"Fossies" - the Fresh Open Source Software Archive

Member "UXP-2019.06.08/dom/indexedDB/ActorsParent.cpp" (8 Jun 2019, 801672 Bytes) of package /linux/www/UXP-2019.06.08.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "ActorsParent.cpp": 2019.03.27_vs_2019.06.08.

    1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
    2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
    3 /* This Source Code Form is subject to the terms of the Mozilla Public
    4  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
    5  * You can obtain one at http://mozilla.org/MPL/2.0/. */
    6 
    7 #include "ActorsParent.h"
    8 
    9 #include <algorithm>
   10 #include <stdint.h> // UINTPTR_MAX, uintptr_t
   11 #include "FileInfo.h"
   12 #include "FileManager.h"
   13 #include "IDBObjectStore.h"
   14 #include "IDBTransaction.h"
   15 #include "IndexedDatabase.h"
   16 #include "IndexedDatabaseInlines.h"
   17 #include "IndexedDatabaseManager.h"
   18 #include "js/StructuredClone.h"
   19 #include "js/Value.h"
   20 #include "jsapi.h"
   21 #include "KeyPath.h"
   22 #include "mozilla/Attributes.h"
   23 #include "mozilla/AppProcessChecker.h"
   24 #include "mozilla/AutoRestore.h"
   25 #include "mozilla/Casting.h"
   26 #include "mozilla/CheckedInt.h"
   27 #include "mozilla/EndianUtils.h"
   28 #include "mozilla/ErrorNames.h"
   29 #include "mozilla/LazyIdleThread.h"
   30 #include "mozilla/Maybe.h"
   31 #include "mozilla/Preferences.h"
   32 #include "mozilla/Services.h"
   33 #include "mozilla/SnappyCompressOutputStream.h"
   34 #include "mozilla/SnappyUncompressInputStream.h"
   35 #include "mozilla/StaticPtr.h"
   36 #include "mozilla/storage.h"
   37 #include "mozilla/Unused.h"
   38 #include "mozilla/UniquePtrExtensions.h"
   39 #include "mozilla/dom/ContentParent.h"
   40 #include "mozilla/dom/File.h"
   41 #include "mozilla/dom/StructuredCloneTags.h"
   42 #include "mozilla/dom/TabParent.h"
   43 #include "mozilla/dom/filehandle/ActorsParent.h"
   44 #include "mozilla/dom/indexedDB/PBackgroundIDBCursorParent.h"
   45 #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseParent.h"
   46 #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseFileParent.h"
   47 #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseRequestParent.h"
   48 #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryParent.h"
   49 #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryRequestParent.h"
   50 #include "mozilla/dom/indexedDB/PBackgroundIDBRequestParent.h"
   51 #include "mozilla/dom/indexedDB/PBackgroundIDBTransactionParent.h"
   52 #include "mozilla/dom/indexedDB/PBackgroundIDBVersionChangeTransactionParent.h"
   53 #include "mozilla/dom/indexedDB/PBackgroundIndexedDBUtilsParent.h"
   54 #include "mozilla/dom/indexedDB/PIndexedDBPermissionRequestParent.h"
   55 #include "mozilla/dom/ipc/BlobParent.h"
   56 #include "mozilla/dom/quota/Client.h"
   57 #include "mozilla/dom/quota/FileStreams.h"
   58 #include "mozilla/dom/quota/OriginScope.h"
   59 #include "mozilla/dom/quota/QuotaManager.h"
   60 #include "mozilla/dom/quota/UsageInfo.h"
   61 #include "mozilla/ipc/BackgroundParent.h"
   62 #include "mozilla/ipc/BackgroundUtils.h"
   63 #include "mozilla/ipc/InputStreamParams.h"
   64 #include "mozilla/ipc/InputStreamUtils.h"
   65 #include "mozilla/ipc/PBackground.h"
   66 #include "mozilla/Scoped.h"
   67 #include "mozilla/storage/Variant.h"
   68 #include "nsAutoPtr.h"
   69 #include "nsCharSeparatedTokenizer.h"
   70 #include "nsClassHashtable.h"
   71 #include "nsCOMPtr.h"
   72 #include "nsDataHashtable.h"
   73 #include "nsEscape.h"
   74 #include "nsHashKeys.h"
   75 #include "nsNetUtil.h"
   76 #include "nsISimpleEnumerator.h"
   77 #include "nsIAppsService.h"
   78 #include "nsIEventTarget.h"
   79 #include "nsIFile.h"
   80 #include "nsIFileURL.h"
   81 #include "nsIFileProtocolHandler.h"
   82 #include "nsIInputStream.h"
   83 #include "nsIInterfaceRequestor.h"
   84 #include "nsInterfaceHashtable.h"
   85 #include "nsIOutputStream.h"
   86 #include "nsIPipe.h"
   87 #include "nsIPrincipal.h"
   88 #include "nsIScriptSecurityManager.h"
   89 #include "nsISupports.h"
   90 #include "nsISupportsImpl.h"
   91 #include "nsISupportsPriority.h"
   92 #include "nsIThread.h"
   93 #include "nsITimer.h"
   94 #include "nsIURI.h"
   95 #include "nsNetUtil.h"
   96 #include "nsPrintfCString.h"
   97 #include "nsQueryObject.h"
   98 #include "nsRefPtrHashtable.h"
   99 #include "nsStreamUtils.h"
  100 #include "nsString.h"
  101 #include "nsStringStream.h"
  102 #include "nsThreadPool.h"
  103 #include "nsThreadUtils.h"
  104 #include "nsXPCOMCID.h"
  105 #include "PermissionRequestBase.h"
  106 #include "ProfilerHelpers.h"
  107 #include "prsystem.h"
  108 #include "prtime.h"
  109 #include "ReportInternalError.h"
  110 #include "snappy/snappy.h"
  111 
  112 #define DISABLE_ASSERTS_FOR_FUZZING 0
  113 
  114 #if DISABLE_ASSERTS_FOR_FUZZING
  115 #define ASSERT_UNLESS_FUZZING(...) do { } while (0)
  116 #else
  117 #define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false, __VA_ARGS__)
  118 #endif
  119 
  120 #define IDB_DEBUG_LOG(_args)                                                   \
  121   MOZ_LOG(IndexedDatabaseManager::GetLoggingModule(),                           \
  122          LogLevel::Debug,                                                         \
  123          _args )
  124 
  125 #if defined(MOZ_WIDGET_ANDROID)
  126 #define IDB_MOBILE
  127 #endif
  128 
  129 #define BLOB_IMPL_STORED_FILE_IID \
  130   {0x6b505c84, 0x2c60, 0x4ffb, {0x8b, 0x91, 0xfe, 0x22, 0xb1, 0xec, 0x75, 0xe2}}
  131 
  132 namespace mozilla {
  133 
  134 MOZ_TYPE_SPECIFIC_SCOPED_POINTER_TEMPLATE(ScopedPRFileDesc,
  135                                           PRFileDesc,
  136                                           PR_Close);
  137 
  138 namespace dom {
  139 namespace indexedDB {
  140 
  141 using namespace mozilla::dom::quota;
  142 using namespace mozilla::ipc;
  143 
  144 namespace {
  145 
  146 class ConnectionPool;
  147 class Cursor;
  148 class Database;
  149 struct DatabaseActorInfo;
  150 class DatabaseFile;
  151 class DatabaseLoggingInfo;
  152 class DatabaseMaintenance;
  153 class Factory;
  154 class Maintenance;
  155 class MutableFile;
  156 class OpenDatabaseOp;
  157 class TransactionBase;
  158 class TransactionDatabaseOperationBase;
  159 class VersionChangeTransaction;
  160 
  161 /*******************************************************************************
  162  * Constants
  163  ******************************************************************************/
  164 
  165 // If JS_STRUCTURED_CLONE_VERSION changes then we need to update our major
  166 // schema version.
  167 static_assert(JS_STRUCTURED_CLONE_VERSION == 8,
  168               "Need to update the major schema version.");
  169 
  170 // Major schema version. Bump for almost everything.
  171 const uint32_t kMajorSchemaVersion = 25;
  172 
  173 // Minor schema version. Should almost always be 0 (maybe bump on release
  174 // branches if we have to).
  175 const uint32_t kMinorSchemaVersion = 0;
  176 
  177 // The schema version we store in the SQLite database is a (signed) 32-bit
  178 // integer. The major version is left-shifted 4 bits so the max value is
  179 // 0xFFFFFFF. The minor version occupies the lower 4 bits and its max is 0xF.
  180 static_assert(kMajorSchemaVersion <= 0xFFFFFFF,
  181               "Major version needs to fit in 28 bits.");
  182 static_assert(kMinorSchemaVersion <= 0xF,
  183               "Minor version needs to fit in 4 bits.");
  184 
  185 const int32_t kSQLiteSchemaVersion =
  186   int32_t((kMajorSchemaVersion << 4) + kMinorSchemaVersion);
  187 
  188 const int32_t kStorageProgressGranularity = 1000;
  189 
  190 // Changing the value here will override the page size of new databases only.
  191 // A journal mode change and VACUUM are needed to change existing databases, so
  192 // the best way to do that is to use the schema version upgrade mechanism.
  193 const uint32_t kSQLitePageSizeOverride =
  194 #ifdef IDB_MOBILE
  195   2048;
  196 #else
  197   4096;
  198 #endif
  199 
  200 static_assert(kSQLitePageSizeOverride == /* mozStorage default */ 0 ||
  201               (kSQLitePageSizeOverride % 2 == 0 &&
  202                kSQLitePageSizeOverride >= 512  &&
  203                kSQLitePageSizeOverride <= 65536),
  204               "Must be 0 (disabled) or a power of 2 between 512 and 65536!");
  205 
  206 // Set to -1 to use SQLite's default, 0 to disable, or some positive number to
  207 // enforce a custom limit.
  208 const int32_t kMaxWALPages = 5000; // 20MB on desktop, 10MB on mobile.
  209 
  210 // Set to some multiple of the page size to grow the database in larger chunks.
  211 const uint32_t kSQLiteGrowthIncrement = kSQLitePageSizeOverride * 2;
  212 
  213 static_assert(kSQLiteGrowthIncrement >= 0 &&
  214               kSQLiteGrowthIncrement % kSQLitePageSizeOverride == 0 &&
  215               kSQLiteGrowthIncrement < uint32_t(INT32_MAX),
  216               "Must be 0 (disabled) or a positive multiple of the page size!");
  217 
  218 // The maximum number of threads that can be used for database activity at a
  219 // single time.
  220 const uint32_t kMaxConnectionThreadCount = 20;
  221 
  222 static_assert(kMaxConnectionThreadCount, "Must have at least one thread!");
  223 
  224 // The maximum number of threads to keep when idle. Threads that become idle in
  225 // excess of this number will be shut down immediately.
  226 const uint32_t kMaxIdleConnectionThreadCount = 2;
  227 
  228 static_assert(kMaxConnectionThreadCount >= kMaxIdleConnectionThreadCount,
  229               "Idle thread limit must be less than total thread limit!");
  230 
  231 // The length of time that database connections will be held open after all
  232 // transactions have completed before doing idle maintenance.
  233 const uint32_t kConnectionIdleMaintenanceMS = 2 * 1000; // 2 seconds
  234 
  235 // The length of time that database connections will be held open after all
  236 // transactions and maintenance have completed.
  237 const uint32_t kConnectionIdleCloseMS = 10 * 1000; // 10 seconds
  238 
  239 // The length of time that idle threads will stay alive before being shut down.
  240 const uint32_t kConnectionThreadIdleMS = 30 * 1000; // 30 seconds
  241 
  242 #define SAVEPOINT_CLAUSE "SAVEPOINT sp;"
  243 
  244 const uint32_t kFileCopyBufferSize = 32768;
  245 
  246 #define JOURNAL_DIRECTORY_NAME "journals"
  247 
  248 const char kFileManagerDirectoryNameSuffix[] = ".files";
  249 const char kSQLiteJournalSuffix[] = ".sqlite-journal";
  250 const char kSQLiteSHMSuffix[] = ".sqlite-shm";
  251 const char kSQLiteWALSuffix[] = ".sqlite-wal";
  252 
  253 const char kPrefFileHandleEnabled[] = "dom.fileHandle.enabled";
  254 
  255 #define IDB_PREFIX "indexedDB"
  256 
  257 #define PERMISSION_STRING_CHROME_BASE IDB_PREFIX "-chrome-"
  258 #define PERMISSION_STRING_CHROME_READ_SUFFIX "-read"
  259 #define PERMISSION_STRING_CHROME_WRITE_SUFFIX "-write"
  260 
  261 #ifdef DEBUG
  262 
  263 const int32_t kDEBUGThreadPriority = nsISupportsPriority::PRIORITY_NORMAL;
  264 const uint32_t kDEBUGThreadSleepMS = 0;
  265 
  266 const int32_t kDEBUGTransactionThreadPriority =
  267   nsISupportsPriority::PRIORITY_NORMAL;
  268 const uint32_t kDEBUGTransactionThreadSleepMS = 0;
  269 
  270 #endif
  271 
  272 template <size_t N>
  273 constexpr size_t
  274 LiteralStringLength(const char (&aArr)[N])
  275 {
  276   static_assert(N, "Zero-length string literal?!");
  277 
  278   // Don't include the null terminator.
  279   return N - 1;
  280 }
  281 
  282 /*******************************************************************************
  283  * Metadata classes
  284  ******************************************************************************/
  285 
  286 struct FullIndexMetadata
  287 {
  288   IndexMetadata mCommonMetadata;
  289 
  290   bool mDeleted;
  291 
  292 public:
  293   FullIndexMetadata()
  294     : mCommonMetadata(0, nsString(), KeyPath(0), nsCString(), false, false, false)
  295     , mDeleted(false)
  296   {
  297     // This can happen either on the QuotaManager IO thread or on a
  298     // versionchange transaction thread. These threads can never race so this is
  299     // totally safe.
  300   }
  301 
  302   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FullIndexMetadata)
  303 
  304 private:
  305   ~FullIndexMetadata()
  306   { }
  307 };
  308 
  309 typedef nsRefPtrHashtable<nsUint64HashKey, FullIndexMetadata> IndexTable;
  310 
  311 struct FullObjectStoreMetadata
  312 {
  313   ObjectStoreMetadata mCommonMetadata;
  314   IndexTable mIndexes;
  315 
  316   // These two members are only ever touched on a transaction thread!
  317   int64_t mNextAutoIncrementId;
  318   int64_t mCommittedAutoIncrementId;
  319 
  320   bool mDeleted;
  321 
  322 public:
  323   FullObjectStoreMetadata()
  324     : mCommonMetadata(0, nsString(), KeyPath(0), false)
  325     , mNextAutoIncrementId(0)
  326     , mCommittedAutoIncrementId(0)
  327     , mDeleted(false)
  328   {
  329     // This can happen either on the QuotaManager IO thread or on a
  330     // versionchange transaction thread. These threads can never race so this is
  331     // totally safe.
  332   }
  333 
  334   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FullObjectStoreMetadata);
  335 
  336   bool
  337   HasLiveIndexes() const;
  338 
  339 private:
  340   ~FullObjectStoreMetadata()
  341   { }
  342 };
  343 
  344 typedef nsRefPtrHashtable<nsUint64HashKey, FullObjectStoreMetadata>
  345   ObjectStoreTable;
  346 
  347 struct FullDatabaseMetadata
  348 {
  349   DatabaseMetadata mCommonMetadata;
  350   nsCString mDatabaseId;
  351   nsString mFilePath;
  352   ObjectStoreTable mObjectStores;
  353 
  354   int64_t mNextObjectStoreId;
  355   int64_t mNextIndexId;
  356 
  357 public:
  358   explicit FullDatabaseMetadata(const DatabaseMetadata& aCommonMetadata)
  359     : mCommonMetadata(aCommonMetadata)
  360     , mNextObjectStoreId(0)
  361     , mNextIndexId(0)
  362   {
  363     AssertIsOnBackgroundThread();
  364   }
  365 
  366   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FullDatabaseMetadata)
  367 
  368   already_AddRefed<FullDatabaseMetadata>
  369   Duplicate() const;
  370 
  371 private:
  372   ~FullDatabaseMetadata()
  373   { }
  374 };
  375 
  376 template <class MetadataType>
  377 class MOZ_STACK_CLASS MetadataNameOrIdMatcher final
  378 {
  379   typedef MetadataNameOrIdMatcher<MetadataType> SelfType;
  380 
  381   const int64_t mId;
  382   const nsString mName;
  383   RefPtr<MetadataType> mMetadata;
  384   bool mCheckName;
  385 
  386 public:
  387   template <class Enumerable>
  388   static MetadataType*
  389   Match(const Enumerable& aEnumerable, uint64_t aId, const nsAString& aName)
  390   {
  391     AssertIsOnBackgroundThread();
  392     MOZ_ASSERT(aId);
  393 
  394     SelfType closure(aId, aName);
  395     MatchHelper(aEnumerable, &closure);
  396 
  397     return closure.mMetadata;
  398   }
  399 
  400   template <class Enumerable>
  401   static MetadataType*
  402   Match(const Enumerable& aEnumerable, uint64_t aId)
  403   {
  404     AssertIsOnBackgroundThread();
  405     MOZ_ASSERT(aId);
  406 
  407     SelfType closure(aId);
  408     MatchHelper(aEnumerable, &closure);
  409 
  410     return closure.mMetadata;
  411   }
  412 
  413 private:
  414   MetadataNameOrIdMatcher(const int64_t& aId, const nsAString& aName)
  415     : mId(aId)
  416     , mName(PromiseFlatString(aName))
  417     , mMetadata(nullptr)
  418     , mCheckName(true)
  419   {
  420     AssertIsOnBackgroundThread();
  421     MOZ_ASSERT(aId);
  422   }
  423 
  424   explicit MetadataNameOrIdMatcher(const int64_t& aId)
  425     : mId(aId)
  426     , mMetadata(nullptr)
  427     , mCheckName(false)
  428   {
  429     AssertIsOnBackgroundThread();
  430     MOZ_ASSERT(aId);
  431   }
  432 
  433   template <class Enumerable>
  434   static void
  435   MatchHelper(const Enumerable& aEnumerable, SelfType* aClosure)
  436   {
  437     AssertIsOnBackgroundThread();
  438     MOZ_ASSERT(aClosure);
  439 
  440     for (auto iter = aEnumerable.ConstIter(); !iter.Done(); iter.Next()) {
  441 #ifdef DEBUG
  442       const uint64_t key = iter.Key();
  443 #endif
  444       MetadataType* value = iter.UserData();
  445       MOZ_ASSERT(key != 0);
  446       MOZ_ASSERT(value);
  447 
  448       if (!value->mDeleted &&
  449           (aClosure->mId == value->mCommonMetadata.id() ||
  450            (aClosure->mCheckName &&
  451             aClosure->mName == value->mCommonMetadata.name()))) {
  452         aClosure->mMetadata = value;
  453         break;
  454       }
  455     }
  456   }
  457 };
  458 
  459 struct IndexDataValue final
  460 {
  461   int64_t mIndexId;
  462   Key mKey;
  463   Key mSortKey;
  464   bool mUnique;
  465 
  466   IndexDataValue()
  467     : mIndexId(0)
  468     , mUnique(false)
  469   {
  470     MOZ_COUNT_CTOR(IndexDataValue);
  471   }
  472 
  473   explicit
  474   IndexDataValue(const IndexDataValue& aOther)
  475     : mIndexId(aOther.mIndexId)
  476     , mKey(aOther.mKey)
  477     , mSortKey(aOther.mSortKey)
  478     , mUnique(aOther.mUnique)
  479   {
  480     MOZ_ASSERT(!aOther.mKey.IsUnset());
  481 
  482     MOZ_COUNT_CTOR(IndexDataValue);
  483   }
  484 
  485   IndexDataValue(int64_t aIndexId, bool aUnique, const Key& aKey)
  486     : mIndexId(aIndexId)
  487     , mKey(aKey)
  488     , mUnique(aUnique)
  489   {
  490     MOZ_ASSERT(!aKey.IsUnset());
  491 
  492     MOZ_COUNT_CTOR(IndexDataValue);
  493   }
  494 
  495   IndexDataValue(int64_t aIndexId, bool aUnique, const Key& aKey,
  496                  const Key& aSortKey)
  497     : mIndexId(aIndexId)
  498     , mKey(aKey)
  499     , mSortKey(aSortKey)
  500     , mUnique(aUnique)
  501   {
  502     MOZ_ASSERT(!aKey.IsUnset());
  503 
  504     MOZ_COUNT_CTOR(IndexDataValue);
  505   }
  506 
  507   ~IndexDataValue()
  508   {
  509     MOZ_COUNT_DTOR(IndexDataValue);
  510   }
  511 
  512   bool
  513   operator==(const IndexDataValue& aOther) const
  514   {
  515     if (mIndexId != aOther.mIndexId) {
  516       return false;
  517     }
  518     if (mSortKey.IsUnset()) {
  519       return mKey == aOther.mKey;
  520     }
  521     return mSortKey == aOther.mSortKey;
  522   }
  523 
  524   bool
  525   operator<(const IndexDataValue& aOther) const
  526   {
  527     if (mIndexId == aOther.mIndexId) {
  528       if (mSortKey.IsUnset()) {
  529         return mKey < aOther.mKey;
  530       }
  531       return mSortKey < aOther.mSortKey;
  532     }
  533 
  534     return mIndexId < aOther.mIndexId;
  535   }
  536 };
  537 
  538 /*******************************************************************************
  539  * SQLite functions
  540  ******************************************************************************/
  541 
  542 int32_t
  543 MakeSchemaVersion(uint32_t aMajorSchemaVersion,
  544                   uint32_t aMinorSchemaVersion)
  545 {
  546   return int32_t((aMajorSchemaVersion << 4) + aMinorSchemaVersion);
  547 }
  548 
  549 uint32_t
  550 HashName(const nsAString& aName)
  551 {
  552   struct Helper
  553   {
  554     static uint32_t
  555     RotateBitsLeft32(uint32_t aValue, uint8_t aBits)
  556     {
  557       MOZ_ASSERT(aBits < 32);
  558       return (aValue << aBits) | (aValue >> (32 - aBits));
  559     }
  560   };
  561 
  562   static const uint32_t kGoldenRatioU32 = 0x9e3779b9u;
  563 
  564   const char16_t* str = aName.BeginReading();
  565   size_t length = aName.Length();
  566 
  567   uint32_t hash = 0;
  568   for (size_t i = 0; i < length; i++) {
  569     hash = kGoldenRatioU32 * (Helper::RotateBitsLeft32(hash, 5) ^ str[i]);
  570   }
  571 
  572   return hash;
  573 }
  574 
  575 nsresult
  576 ClampResultCode(nsresult aResultCode)
  577 {
  578   if (NS_SUCCEEDED(aResultCode) ||
  579       NS_ERROR_GET_MODULE(aResultCode) == NS_ERROR_MODULE_DOM_INDEXEDDB) {
  580     return aResultCode;
  581   }
  582 
  583   switch (aResultCode) {
  584     case NS_ERROR_FILE_NO_DEVICE_SPACE:
  585       return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
  586     case NS_ERROR_STORAGE_CONSTRAINT:
  587       return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
  588     default:
  589 #ifdef DEBUG
  590       nsPrintfCString message("Converting non-IndexedDB error code (0x%X) to "
  591                               "NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR",
  592                               aResultCode);
  593       NS_WARNING(message.get());
  594 #else
  595       ;
  596 #endif
  597   }
  598 
  599   IDB_REPORT_INTERNAL_ERR();
  600   return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
  601 }
  602 
  603 void
  604 GetDatabaseFilename(const nsAString& aName,
  605                     nsAutoString& aDatabaseFilename)
  606 {
  607   MOZ_ASSERT(aDatabaseFilename.IsEmpty());
  608 
  609   aDatabaseFilename.AppendInt(HashName(aName));
  610 
  611   nsCString escapedName;
  612   if (!NS_Escape(NS_ConvertUTF16toUTF8(aName), escapedName, url_XPAlphas)) {
  613     MOZ_CRASH("Can't escape database name!");
  614   }
  615 
  616   const char* forwardIter = escapedName.BeginReading();
  617   const char* backwardIter = escapedName.EndReading() - 1;
  618 
  619   nsAutoCString substring;
  620   while (forwardIter <= backwardIter && substring.Length() < 21) {
  621     if (substring.Length() % 2) {
  622       substring.Append(*backwardIter--);
  623     } else {
  624       substring.Append(*forwardIter++);
  625     }
  626   }
  627 
  628   aDatabaseFilename.AppendASCII(substring.get(), substring.Length());
  629 }
  630 
  631 uint32_t
  632 CompressedByteCountForNumber(uint64_t aNumber)
  633 {
  634   // All bytes have 7 bits available.
  635   uint32_t count = 1;
  636   while ((aNumber >>= 7)) {
  637     count++;
  638   }
  639 
  640   return count;
  641 }
  642 
  643 uint32_t
  644 CompressedByteCountForIndexId(int64_t aIndexId)
  645 {
  646   MOZ_ASSERT(aIndexId);
  647   MOZ_ASSERT(UINT64_MAX - uint64_t(aIndexId) >= uint64_t(aIndexId),
  648               "Overflow!");
  649 
  650   return CompressedByteCountForNumber(uint64_t(aIndexId * 2));
  651 }
  652 
  653 void
  654 WriteCompressedNumber(uint64_t aNumber, uint8_t** aIterator)
  655 {
  656   MOZ_ASSERT(aIterator);
  657   MOZ_ASSERT(*aIterator);
  658 
  659   uint8_t*& buffer = *aIterator;
  660 
  661 #ifdef DEBUG
  662   const uint8_t* bufferStart = buffer;
  663   const uint64_t originalNumber = aNumber;
  664 #endif
  665 
  666   while (true) {
  667     uint64_t shiftedNumber = aNumber >> 7;
  668     if (shiftedNumber) {
  669       *buffer++ = uint8_t(0x80 | (aNumber & 0x7f));
  670       aNumber = shiftedNumber;
  671     } else {
  672       *buffer++ = uint8_t(aNumber);
  673       break;
  674     }
  675   }
  676 
  677   MOZ_ASSERT(buffer > bufferStart);
  678   MOZ_ASSERT(uint32_t(buffer - bufferStart) ==
  679                CompressedByteCountForNumber(originalNumber));
  680 }
  681 
  682 uint64_t
  683 ReadCompressedNumber(const uint8_t** aIterator, const uint8_t* aEnd)
  684 {
  685   MOZ_ASSERT(aIterator);
  686   MOZ_ASSERT(*aIterator);
  687   MOZ_ASSERT(aEnd);
  688   MOZ_ASSERT(*aIterator < aEnd);
  689 
  690   const uint8_t*& buffer = *aIterator;
  691 
  692   uint8_t shiftCounter = 0;
  693   uint64_t result = 0;
  694 
  695   while (true) {
  696     MOZ_ASSERT(shiftCounter <= 56, "Shifted too many bits!");
  697 
  698     result += (uint64_t(*buffer & 0x7f) << shiftCounter);
  699     shiftCounter += 7;
  700 
  701     if (!(*buffer++ & 0x80)) {
  702       break;
  703     }
  704 
  705     if (NS_WARN_IF(buffer == aEnd)) {
  706       MOZ_ASSERT(false);
  707       break;
  708     }
  709   }
  710 
  711   return result;
  712 }
  713 
  714 void
  715 WriteCompressedIndexId(int64_t aIndexId, bool aUnique, uint8_t** aIterator)
  716 {
  717   MOZ_ASSERT(aIndexId);
  718   MOZ_ASSERT(UINT64_MAX - uint64_t(aIndexId) >= uint64_t(aIndexId),
  719              "Overflow!");
  720   MOZ_ASSERT(aIterator);
  721   MOZ_ASSERT(*aIterator);
  722 
  723   const uint64_t indexId = (uint64_t(aIndexId * 2) | (aUnique ? 1 : 0));
  724   WriteCompressedNumber(indexId, aIterator);
  725 }
  726 
  727 void
  728 ReadCompressedIndexId(const uint8_t** aIterator,
  729                       const uint8_t* aEnd,
  730                       int64_t* aIndexId,
  731                       bool* aUnique)
  732 {
  733   MOZ_ASSERT(aIterator);
  734   MOZ_ASSERT(*aIterator);
  735   MOZ_ASSERT(aIndexId);
  736   MOZ_ASSERT(aUnique);
  737 
  738   uint64_t indexId = ReadCompressedNumber(aIterator, aEnd);
  739 
  740   if (indexId % 2) {
  741     *aUnique = true;
  742     indexId--;
  743   } else {
  744     *aUnique = false;
  745   }
  746 
  747   MOZ_ASSERT(UINT64_MAX / 2 >= uint64_t(indexId), "Bad index id!");
  748 
  749   *aIndexId = int64_t(indexId / 2);
  750 }
  751 
  752 // static
  753 nsresult
  754 MakeCompressedIndexDataValues(
  755                              const FallibleTArray<IndexDataValue>& aIndexValues,
  756                              UniqueFreePtr<uint8_t>& aCompressedIndexDataValues,
  757                              uint32_t* aCompressedIndexDataValuesLength)
  758 {
  759   MOZ_ASSERT(!NS_IsMainThread());
  760   MOZ_ASSERT(!IsOnBackgroundThread());
  761   MOZ_ASSERT(!aCompressedIndexDataValues);
  762   MOZ_ASSERT(aCompressedIndexDataValuesLength);
  763 
  764   PROFILER_LABEL("IndexedDB",
  765                  "MakeCompressedIndexDataValues",
  766                  js::ProfileEntry::Category::STORAGE);
  767 
  768   const uint32_t arrayLength = aIndexValues.Length();
  769   if (!arrayLength) {
  770     *aCompressedIndexDataValuesLength = 0;
  771     return NS_OK;
  772   }
  773 
  774   // First calculate the size of the final buffer.
  775   uint32_t blobDataLength = 0;
  776 
  777   for (uint32_t arrayIndex = 0; arrayIndex < arrayLength; arrayIndex++) {
  778     const IndexDataValue& info = aIndexValues[arrayIndex];
  779     const nsCString& keyBuffer = info.mKey.GetBuffer();
  780     const nsCString& sortKeyBuffer = info.mSortKey.GetBuffer();
  781     const uint32_t keyBufferLength = keyBuffer.Length();
  782     const uint32_t sortKeyBufferLength = sortKeyBuffer.Length();
  783 
  784     MOZ_ASSERT(!keyBuffer.IsEmpty());
  785 
  786     const CheckedUint32 infoLength =
  787       CheckedUint32(CompressedByteCountForIndexId(info.mIndexId)) +
  788       CompressedByteCountForNumber(keyBufferLength) +
  789       CompressedByteCountForNumber(sortKeyBufferLength) +
  790       keyBufferLength +
  791       sortKeyBufferLength;
  792     // Don't let |infoLength| overflow.
  793     if (NS_WARN_IF(!infoLength.isValid())) {
  794       IDB_REPORT_INTERNAL_ERR();
  795       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
  796     }
  797 
  798     // Don't let |blobDataLength| overflow.
  799     if (NS_WARN_IF(UINT32_MAX - infoLength.value() < blobDataLength)) {
  800       IDB_REPORT_INTERNAL_ERR();
  801       return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
  802     }
  803 
  804     blobDataLength += infoLength.value();
  805   }
  806 
  807   UniqueFreePtr<uint8_t> blobData(
  808     static_cast<uint8_t*>(malloc(blobDataLength)));
  809   if (NS_WARN_IF(!blobData)) {
  810     IDB_REPORT_INTERNAL_ERR();
  811     return NS_ERROR_OUT_OF_MEMORY;
  812   }
  813 
  814   uint8_t* blobDataIter = blobData.get();
  815 
  816   for (uint32_t arrayIndex = 0; arrayIndex < arrayLength; arrayIndex++) {
  817     const IndexDataValue& info = aIndexValues[arrayIndex];
  818     const nsCString& keyBuffer = info.mKey.GetBuffer();
  819     const nsCString& sortKeyBuffer = info.mSortKey.GetBuffer();
  820     const uint32_t keyBufferLength = keyBuffer.Length();
  821     const uint32_t sortKeyBufferLength = sortKeyBuffer.Length();
  822 
  823     WriteCompressedIndexId(info.mIndexId, info.mUnique, &blobDataIter);
  824     WriteCompressedNumber(keyBufferLength, &blobDataIter);
  825 
  826     memcpy(blobDataIter, keyBuffer.get(), keyBufferLength);
  827     blobDataIter += keyBufferLength;
  828 
  829     WriteCompressedNumber(sortKeyBufferLength, &blobDataIter);
  830 
  831     memcpy(blobDataIter, sortKeyBuffer.get(), sortKeyBufferLength);
  832     blobDataIter += sortKeyBufferLength;
  833   }
  834 
  835   MOZ_ASSERT(blobDataIter == blobData.get() + blobDataLength);
  836 
  837   aCompressedIndexDataValues.swap(blobData);
  838   *aCompressedIndexDataValuesLength = uint32_t(blobDataLength);
  839 
  840   return NS_OK;
  841 }
  842 
  843 nsresult
  844 ReadCompressedIndexDataValuesFromBlob(const uint8_t* aBlobData,
  845                                       uint32_t aBlobDataLength,
  846                                       nsTArray<IndexDataValue>& aIndexValues)
  847 {
  848   MOZ_ASSERT(!NS_IsMainThread());
  849   MOZ_ASSERT(!IsOnBackgroundThread());
  850   MOZ_ASSERT(aBlobData);
  851   MOZ_ASSERT(aBlobDataLength);
  852   MOZ_ASSERT(aIndexValues.IsEmpty());
  853 
  854   PROFILER_LABEL("IndexedDB",
  855                  "ReadCompressedIndexDataValuesFromBlob",
  856                  js::ProfileEntry::Category::STORAGE);
  857 
  858   if (uintptr_t(aBlobData) > UINTPTR_MAX - aBlobDataLength) {
  859     IDB_REPORT_INTERNAL_ERR();
  860     return NS_ERROR_FILE_CORRUPTED;
  861   }
  862 
  863   const uint8_t* blobDataIter = aBlobData;
  864   const uint8_t* blobDataEnd = aBlobData + aBlobDataLength;
  865 
  866   while (blobDataIter < blobDataEnd) {
  867     int64_t indexId;
  868     bool unique;
  869     ReadCompressedIndexId(&blobDataIter, blobDataEnd, &indexId, &unique);
  870 
  871     if (NS_WARN_IF(blobDataIter == blobDataEnd)) {
  872       IDB_REPORT_INTERNAL_ERR();
  873       return NS_ERROR_FILE_CORRUPTED;
  874     }
  875 
  876     // Read key buffer length.
  877     const uint64_t keyBufferLength =
  878       ReadCompressedNumber(&blobDataIter, blobDataEnd);
  879 
  880     if (NS_WARN_IF(blobDataIter == blobDataEnd) ||
  881         NS_WARN_IF(keyBufferLength > uint64_t(UINT32_MAX)) ||
  882         NS_WARN_IF(keyBufferLength > uintptr_t(blobDataEnd)) ||
  883         NS_WARN_IF(blobDataIter > blobDataEnd - keyBufferLength)) {
  884       IDB_REPORT_INTERNAL_ERR();
  885       return NS_ERROR_FILE_CORRUPTED;
  886     }
  887 
  888     nsCString keyBuffer(reinterpret_cast<const char*>(blobDataIter),
  889                         uint32_t(keyBufferLength));
  890     blobDataIter += keyBufferLength;
  891 
  892     IndexDataValue idv(indexId, unique, Key(keyBuffer));
  893 
  894     // Read sort key buffer length.
  895     const uint64_t sortKeyBufferLength =
  896       ReadCompressedNumber(&blobDataIter, blobDataEnd);
  897 
  898     if (sortKeyBufferLength > 0) {
  899       if (NS_WARN_IF(blobDataIter == blobDataEnd) ||
  900           NS_WARN_IF(sortKeyBufferLength > uint64_t(UINT32_MAX)) ||
  901           NS_WARN_IF(sortKeyBufferLength > uintptr_t(blobDataEnd)) ||
  902           NS_WARN_IF(blobDataIter > blobDataEnd - sortKeyBufferLength)) {
  903         IDB_REPORT_INTERNAL_ERR();
  904         return NS_ERROR_FILE_CORRUPTED;
  905       }
  906 
  907       nsCString sortKeyBuffer(reinterpret_cast<const char*>(blobDataIter),
  908                               uint32_t(sortKeyBufferLength));
  909       blobDataIter += sortKeyBufferLength;
  910 
  911       idv.mSortKey = Key(sortKeyBuffer);
  912     }
  913 
  914     if (NS_WARN_IF(!aIndexValues.InsertElementSorted(idv, fallible))) {
  915       IDB_REPORT_INTERNAL_ERR();
  916       return NS_ERROR_OUT_OF_MEMORY;
  917     }
  918   }
  919 
  920   MOZ_ASSERT(blobDataIter == blobDataEnd);
  921 
  922   return NS_OK;
  923 }
  924 
  925 // static
  926 template <typename T>
  927 nsresult
  928 ReadCompressedIndexDataValuesFromSource(T* aSource,
  929                                         uint32_t aColumnIndex,
  930                                         nsTArray<IndexDataValue>& aIndexValues)
  931 {
  932   MOZ_ASSERT(!NS_IsMainThread());
  933   MOZ_ASSERT(!IsOnBackgroundThread());
  934   MOZ_ASSERT(aSource);
  935   MOZ_ASSERT(aIndexValues.IsEmpty());
  936 
  937   int32_t columnType;
  938   nsresult rv = aSource->GetTypeOfIndex(aColumnIndex, &columnType);
  939   if (NS_WARN_IF(NS_FAILED(rv))) {
  940     return rv;
  941   }
  942 
  943   if (columnType == mozIStorageStatement::VALUE_TYPE_NULL) {
  944     return NS_OK;
  945   }
  946 
  947   MOZ_ASSERT(columnType == mozIStorageStatement::VALUE_TYPE_BLOB);
  948 
  949   const uint8_t* blobData;
  950   uint32_t blobDataLength;
  951   rv = aSource->GetSharedBlob(aColumnIndex, &blobDataLength, &blobData);
  952   if (NS_WARN_IF(NS_FAILED(rv))) {
  953     return rv;
  954   }
  955 
  956   if (NS_WARN_IF(!blobDataLength)) {
  957     IDB_REPORT_INTERNAL_ERR();
  958     return NS_ERROR_FILE_CORRUPTED;
  959   }
  960 
  961   rv = ReadCompressedIndexDataValuesFromBlob(blobData,
  962                                              blobDataLength,
  963                                              aIndexValues);
  964   if (NS_WARN_IF(NS_FAILED(rv))) {
  965     return rv;
  966   }
  967 
  968   return NS_OK;
  969 }
  970 
  971 nsresult
  972 ReadCompressedIndexDataValues(mozIStorageStatement* aStatement,
  973                               uint32_t aColumnIndex,
  974                               nsTArray<IndexDataValue>& aIndexValues)
  975 {
  976   return ReadCompressedIndexDataValuesFromSource(aStatement,
  977                                                  aColumnIndex,
  978                                                  aIndexValues);
  979 }
  980 
  981 nsresult
  982 ReadCompressedIndexDataValues(mozIStorageValueArray* aValues,
  983                               uint32_t aColumnIndex,
  984                               nsTArray<IndexDataValue>& aIndexValues)
  985 {
  986   return ReadCompressedIndexDataValuesFromSource(aValues,
  987                                                  aColumnIndex,
  988                                                  aIndexValues);
  989 }
  990 
  991 nsresult
  992 CreateFileTables(mozIStorageConnection* aConnection)
  993 {
  994   AssertIsOnIOThread();
  995   MOZ_ASSERT(aConnection);
  996 
  997   PROFILER_LABEL("IndexedDB",
  998                  "CreateFileTables",
  999                  js::ProfileEntry::Category::STORAGE);
 1000 
 1001   // Table `file`
 1002   nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1003     "CREATE TABLE file ("
 1004       "id INTEGER PRIMARY KEY, "
 1005       "refcount INTEGER NOT NULL"
 1006     ");"
 1007   ));
 1008   if (NS_WARN_IF(NS_FAILED(rv))) {
 1009     return rv;
 1010   }
 1011 
 1012   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1013     "CREATE TRIGGER object_data_insert_trigger "
 1014     "AFTER INSERT ON object_data "
 1015     "FOR EACH ROW "
 1016     "WHEN NEW.file_ids IS NOT NULL "
 1017     "BEGIN "
 1018       "SELECT update_refcount(NULL, NEW.file_ids); "
 1019     "END;"
 1020   ));
 1021   if (NS_WARN_IF(NS_FAILED(rv))) {
 1022     return rv;
 1023   }
 1024 
 1025   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1026     "CREATE TRIGGER object_data_update_trigger "
 1027     "AFTER UPDATE OF file_ids ON object_data "
 1028     "FOR EACH ROW "
 1029     "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL "
 1030     "BEGIN "
 1031       "SELECT update_refcount(OLD.file_ids, NEW.file_ids); "
 1032     "END;"
 1033   ));
 1034   if (NS_WARN_IF(NS_FAILED(rv))) {
 1035     return rv;
 1036   }
 1037 
 1038   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1039     "CREATE TRIGGER object_data_delete_trigger "
 1040     "AFTER DELETE ON object_data "
 1041     "FOR EACH ROW WHEN OLD.file_ids IS NOT NULL "
 1042     "BEGIN "
 1043       "SELECT update_refcount(OLD.file_ids, NULL); "
 1044     "END;"
 1045   ));
 1046   if (NS_WARN_IF(NS_FAILED(rv))) {
 1047     return rv;
 1048   }
 1049 
 1050   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1051     "CREATE TRIGGER file_update_trigger "
 1052     "AFTER UPDATE ON file "
 1053     "FOR EACH ROW WHEN NEW.refcount = 0 "
 1054     "BEGIN "
 1055       "DELETE FROM file WHERE id = OLD.id; "
 1056     "END;"
 1057   ));
 1058   if (NS_WARN_IF(NS_FAILED(rv))) {
 1059     return rv;
 1060   }
 1061 
 1062   return NS_OK;
 1063 }
 1064 
 1065 nsresult
 1066 CreateTables(mozIStorageConnection* aConnection)
 1067 {
 1068   AssertIsOnIOThread();
 1069   MOZ_ASSERT(aConnection);
 1070 
 1071   PROFILER_LABEL("IndexedDB",
 1072                  "CreateTables",
 1073                  js::ProfileEntry::Category::STORAGE);
 1074 
 1075   // Table `database`
 1076 
 1077   // There are two reasons for having the origin column.
 1078   // First, we can ensure that we don't have collisions in the origin hash we
 1079   // use for the path because when we open the db we can make sure that the
 1080   // origins exactly match. Second, chrome code crawling through the idb
 1081   // directory can figure out the origin of every db without having to
 1082   // reverse-engineer our hash scheme.
 1083   nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1084     "CREATE TABLE database"
 1085       "( name TEXT PRIMARY KEY"
 1086       ", origin TEXT NOT NULL"
 1087       ", version INTEGER NOT NULL DEFAULT 0"
 1088       ", last_vacuum_time INTEGER NOT NULL DEFAULT 0"
 1089       ", last_analyze_time INTEGER NOT NULL DEFAULT 0"
 1090       ", last_vacuum_size INTEGER NOT NULL DEFAULT 0"
 1091       ") WITHOUT ROWID;"
 1092   ));
 1093   if (NS_WARN_IF(NS_FAILED(rv))) {
 1094     return rv;
 1095   }
 1096 
 1097   // Table `object_store`
 1098   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1099     "CREATE TABLE object_store"
 1100       "( id INTEGER PRIMARY KEY"
 1101       ", auto_increment INTEGER NOT NULL DEFAULT 0"
 1102       ", name TEXT NOT NULL"
 1103       ", key_path TEXT"
 1104       ");"
 1105   ));
 1106   if (NS_WARN_IF(NS_FAILED(rv))) {
 1107     return rv;
 1108   }
 1109 
 1110   // Table `object_store_index`
 1111   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1112     "CREATE TABLE object_store_index"
 1113       "( id INTEGER PRIMARY KEY"
 1114       ", object_store_id INTEGER NOT NULL"
 1115       ", name TEXT NOT NULL"
 1116       ", key_path TEXT NOT NULL"
 1117       ", unique_index INTEGER NOT NULL"
 1118       ", multientry INTEGER NOT NULL"
 1119       ", locale TEXT"
 1120       ", is_auto_locale BOOLEAN NOT NULL"
 1121       ", FOREIGN KEY (object_store_id) "
 1122           "REFERENCES object_store(id) "
 1123       ");"
 1124   ));
 1125   if (NS_WARN_IF(NS_FAILED(rv))) {
 1126     return rv;
 1127   }
 1128 
 1129   // Table `object_data`
 1130   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1131     "CREATE TABLE object_data"
 1132       "( object_store_id INTEGER NOT NULL"
 1133       ", key BLOB NOT NULL"
 1134       ", index_data_values BLOB DEFAULT NULL"
 1135       ", file_ids TEXT"
 1136       ", data BLOB NOT NULL"
 1137       ", PRIMARY KEY (object_store_id, key)"
 1138       ", FOREIGN KEY (object_store_id) "
 1139           "REFERENCES object_store(id) "
 1140       ") WITHOUT ROWID;"
 1141   ));
 1142   if (NS_WARN_IF(NS_FAILED(rv))) {
 1143     return rv;
 1144   }
 1145 
 1146   // Table `index_data`
 1147   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1148     "CREATE TABLE index_data"
 1149       "( index_id INTEGER NOT NULL"
 1150       ", value BLOB NOT NULL"
 1151       ", object_data_key BLOB NOT NULL"
 1152       ", object_store_id INTEGER NOT NULL"
 1153       ", value_locale BLOB"
 1154       ", PRIMARY KEY (index_id, value, object_data_key)"
 1155       ", FOREIGN KEY (index_id) "
 1156           "REFERENCES object_store_index(id) "
 1157       ", FOREIGN KEY (object_store_id, object_data_key) "
 1158           "REFERENCES object_data(object_store_id, key) "
 1159       ") WITHOUT ROWID;"
 1160   ));
 1161   if (NS_WARN_IF(NS_FAILED(rv))) {
 1162     return rv;
 1163   }
 1164 
 1165   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1166     "CREATE INDEX index_data_value_locale_index "
 1167     "ON index_data (index_id, value_locale, object_data_key, value) "
 1168     "WHERE value_locale IS NOT NULL;"
 1169   ));
 1170   if (NS_WARN_IF(NS_FAILED(rv))) {
 1171     return rv;
 1172   }
 1173 
 1174   // Table `unique_index_data`
 1175   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1176     "CREATE TABLE unique_index_data"
 1177       "( index_id INTEGER NOT NULL"
 1178       ", value BLOB NOT NULL"
 1179       ", object_store_id INTEGER NOT NULL"
 1180       ", object_data_key BLOB NOT NULL"
 1181       ", value_locale BLOB"
 1182       ", PRIMARY KEY (index_id, value)"
 1183       ", FOREIGN KEY (index_id) "
 1184           "REFERENCES object_store_index(id) "
 1185       ", FOREIGN KEY (object_store_id, object_data_key) "
 1186           "REFERENCES object_data(object_store_id, key) "
 1187       ") WITHOUT ROWID;"
 1188   ));
 1189   if (NS_WARN_IF(NS_FAILED(rv))) {
 1190     return rv;
 1191   }
 1192 
 1193   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1194     "CREATE INDEX unique_index_data_value_locale_index "
 1195     "ON unique_index_data (index_id, value_locale, object_data_key, value) "
 1196     "WHERE value_locale IS NOT NULL;"
 1197   ));
 1198   if (NS_WARN_IF(NS_FAILED(rv))) {
 1199     return rv;
 1200   }
 1201 
 1202   rv = CreateFileTables(aConnection);
 1203   if (NS_WARN_IF(NS_FAILED(rv))) {
 1204     return rv;
 1205   }
 1206 
 1207   rv = aConnection->SetSchemaVersion(kSQLiteSchemaVersion);
 1208   if (NS_WARN_IF(NS_FAILED(rv))) {
 1209     return rv;
 1210   }
 1211 
 1212   return NS_OK;
 1213 }
 1214 
 1215 nsresult
 1216 UpgradeSchemaFrom4To5(mozIStorageConnection* aConnection)
 1217 {
 1218   AssertIsOnIOThread();
 1219   MOZ_ASSERT(aConnection);
 1220 
 1221   PROFILER_LABEL("IndexedDB",
 1222                  "UpgradeSchemaFrom4To5",
 1223                  js::ProfileEntry::Category::STORAGE);
 1224 
 1225   nsresult rv;
 1226 
 1227   // All we changed is the type of the version column, so lets try to
 1228   // convert that to an integer, and if we fail, set it to 0.
 1229   nsCOMPtr<mozIStorageStatement> stmt;
 1230   rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
 1231     "SELECT name, version, dataVersion "
 1232     "FROM database"
 1233   ), getter_AddRefs(stmt));
 1234   if (NS_WARN_IF(NS_FAILED(rv))) {
 1235     return rv;
 1236   }
 1237 
 1238   nsString name;
 1239   int32_t intVersion;
 1240   int64_t dataVersion;
 1241 
 1242   {
 1243     mozStorageStatementScoper scoper(stmt);
 1244 
 1245     bool hasResults;
 1246     rv = stmt->ExecuteStep(&hasResults);
 1247     if (NS_WARN_IF(NS_FAILED(rv))) {
 1248       return rv;
 1249     }
 1250     if (NS_WARN_IF(!hasResults)) {
 1251       return NS_ERROR_FAILURE;
 1252     }
 1253 
 1254     nsString version;
 1255     rv = stmt->GetString(1, version);
 1256     if (NS_WARN_IF(NS_FAILED(rv))) {
 1257       return rv;
 1258     }
 1259 
 1260     intVersion = version.ToInteger(&rv);
 1261     if (NS_FAILED(rv)) {
 1262       intVersion = 0;
 1263     }
 1264 
 1265     rv = stmt->GetString(0, name);
 1266     if (NS_WARN_IF(NS_FAILED(rv))) {
 1267       return rv;
 1268     }
 1269 
 1270     rv = stmt->GetInt64(2, &dataVersion);
 1271     if (NS_WARN_IF(NS_FAILED(rv))) {
 1272       return rv;
 1273     }
 1274   }
 1275 
 1276   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1277     "DROP TABLE database"
 1278   ));
 1279   if (NS_WARN_IF(NS_FAILED(rv))) {
 1280     return rv;
 1281   }
 1282 
 1283   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1284     "CREATE TABLE database ("
 1285       "name TEXT NOT NULL, "
 1286       "version INTEGER NOT NULL DEFAULT 0, "
 1287       "dataVersion INTEGER NOT NULL"
 1288     ");"
 1289   ));
 1290   if (NS_WARN_IF(NS_FAILED(rv))) {
 1291     return rv;
 1292   }
 1293 
 1294   rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
 1295     "INSERT INTO database (name, version, dataVersion) "
 1296     "VALUES (:name, :version, :dataVersion)"
 1297   ), getter_AddRefs(stmt));
 1298   if (NS_WARN_IF(NS_FAILED(rv))) {
 1299     return rv;
 1300   }
 1301 
 1302   {
 1303     mozStorageStatementScoper scoper(stmt);
 1304 
 1305     rv = stmt->BindStringParameter(0, name);
 1306     if (NS_WARN_IF(NS_FAILED(rv))) {
 1307       return rv;
 1308     }
 1309 
 1310     rv = stmt->BindInt32Parameter(1, intVersion);
 1311     if (NS_WARN_IF(NS_FAILED(rv))) {
 1312       return rv;
 1313     }
 1314 
 1315     rv = stmt->BindInt64Parameter(2, dataVersion);
 1316     if (NS_WARN_IF(NS_FAILED(rv))) {
 1317       return rv;
 1318     }
 1319 
 1320     rv = stmt->Execute();
 1321     if (NS_WARN_IF(NS_FAILED(rv))) {
 1322       return rv;
 1323     }
 1324   }
 1325 
 1326   rv = aConnection->SetSchemaVersion(5);
 1327   if (NS_WARN_IF(NS_FAILED(rv))) {
 1328     return rv;
 1329   }
 1330 
 1331   return NS_OK;
 1332 }
 1333 
 1334 nsresult
 1335 UpgradeSchemaFrom5To6(mozIStorageConnection* aConnection)
 1336 {
 1337   AssertIsOnIOThread();
 1338   MOZ_ASSERT(aConnection);
 1339 
 1340   PROFILER_LABEL("IndexedDB",
 1341                  "UpgradeSchemaFrom5To6",
 1342                  js::ProfileEntry::Category::STORAGE);
 1343 
 1344   // First, drop all the indexes we're no longer going to use.
 1345   nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1346     "DROP INDEX key_index;"
 1347   ));
 1348   if (NS_WARN_IF(NS_FAILED(rv))) {
 1349     return rv;
 1350   }
 1351 
 1352   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1353     "DROP INDEX ai_key_index;"
 1354   ));
 1355   if (NS_WARN_IF(NS_FAILED(rv))) {
 1356     return rv;
 1357   }
 1358 
 1359   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1360     "DROP INDEX value_index;"
 1361   ));
 1362   if (NS_WARN_IF(NS_FAILED(rv))) {
 1363     return rv;
 1364   }
 1365 
 1366   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1367     "DROP INDEX ai_value_index;"
 1368   ));
 1369   if (NS_WARN_IF(NS_FAILED(rv))) {
 1370     return rv;
 1371   }
 1372 
 1373   // Now, reorder the columns of object_data to put the blob data last. We do
 1374   // this by copying into a temporary table, dropping the original, then copying
 1375   // back into a newly created table.
 1376   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1377     "CREATE TEMPORARY TABLE temp_upgrade ("
 1378       "id INTEGER PRIMARY KEY, "
 1379       "object_store_id, "
 1380       "key_value, "
 1381       "data "
 1382     ");"
 1383   ));
 1384   if (NS_WARN_IF(NS_FAILED(rv))) {
 1385     return rv;
 1386   }
 1387 
 1388   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1389     "INSERT INTO temp_upgrade "
 1390       "SELECT id, object_store_id, key_value, data "
 1391       "FROM object_data;"
 1392   ));
 1393   if (NS_WARN_IF(NS_FAILED(rv))) {
 1394     return rv;
 1395   }
 1396 
 1397   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1398     "DROP TABLE object_data;"
 1399   ));
 1400   if (NS_WARN_IF(NS_FAILED(rv))) {
 1401     return rv;
 1402   }
 1403 
 1404   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1405     "CREATE TABLE object_data ("
 1406       "id INTEGER PRIMARY KEY, "
 1407       "object_store_id INTEGER NOT NULL, "
 1408       "key_value DEFAULT NULL, "
 1409       "data BLOB NOT NULL, "
 1410       "UNIQUE (object_store_id, key_value), "
 1411       "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
 1412         "CASCADE"
 1413     ");"
 1414   ));
 1415   if (NS_WARN_IF(NS_FAILED(rv))) {
 1416     return rv;
 1417   }
 1418 
 1419   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1420     "INSERT INTO object_data "
 1421       "SELECT id, object_store_id, key_value, data "
 1422       "FROM temp_upgrade;"
 1423   ));
 1424   if (NS_WARN_IF(NS_FAILED(rv))) {
 1425     return rv;
 1426   }
 1427 
 1428   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1429     "DROP TABLE temp_upgrade;"
 1430   ));
 1431   if (NS_WARN_IF(NS_FAILED(rv))) {
 1432     return rv;
 1433   }
 1434 
 1435   // We need to add a unique constraint to our ai_object_data table. Copy all
 1436   // the data out of it using a temporary table as before.
 1437   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1438     "CREATE TEMPORARY TABLE temp_upgrade ("
 1439       "id INTEGER PRIMARY KEY, "
 1440       "object_store_id, "
 1441       "data "
 1442     ");"
 1443   ));
 1444   if (NS_WARN_IF(NS_FAILED(rv))) {
 1445     return rv;
 1446   }
 1447 
 1448   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1449     "INSERT INTO temp_upgrade "
 1450       "SELECT id, object_store_id, data "
 1451       "FROM ai_object_data;"
 1452   ));
 1453   if (NS_WARN_IF(NS_FAILED(rv))) {
 1454     return rv;
 1455   }
 1456 
 1457   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1458     "DROP TABLE ai_object_data;"
 1459   ));
 1460   if (NS_WARN_IF(NS_FAILED(rv))) {
 1461     return rv;
 1462   }
 1463 
 1464   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1465     "CREATE TABLE ai_object_data ("
 1466       "id INTEGER PRIMARY KEY AUTOINCREMENT, "
 1467       "object_store_id INTEGER NOT NULL, "
 1468       "data BLOB NOT NULL, "
 1469       "UNIQUE (object_store_id, id), "
 1470       "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
 1471         "CASCADE"
 1472     ");"
 1473   ));
 1474   if (NS_WARN_IF(NS_FAILED(rv))) {
 1475     return rv;
 1476   }
 1477 
 1478   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1479     "INSERT INTO ai_object_data "
 1480       "SELECT id, object_store_id, data "
 1481       "FROM temp_upgrade;"
 1482   ));
 1483   if (NS_WARN_IF(NS_FAILED(rv))) {
 1484     return rv;
 1485   }
 1486 
 1487   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1488     "DROP TABLE temp_upgrade;"
 1489   ));
 1490   if (NS_WARN_IF(NS_FAILED(rv))) {
 1491     return rv;
 1492   }
 1493 
 1494   // Fix up the index_data table. We're reordering the columns as well as
 1495   // changing the primary key from being a simple id to being a composite.
 1496   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1497     "CREATE TEMPORARY TABLE temp_upgrade ("
 1498       "index_id, "
 1499       "value, "
 1500       "object_data_key, "
 1501       "object_data_id "
 1502     ");"
 1503   ));
 1504   if (NS_WARN_IF(NS_FAILED(rv))) {
 1505     return rv;
 1506   }
 1507 
 1508   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1509     "INSERT INTO temp_upgrade "
 1510       "SELECT index_id, value, object_data_key, object_data_id "
 1511       "FROM index_data;"
 1512   ));
 1513   if (NS_WARN_IF(NS_FAILED(rv))) {
 1514     return rv;
 1515   }
 1516 
 1517   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1518     "DROP TABLE index_data;"
 1519   ));
 1520   if (NS_WARN_IF(NS_FAILED(rv))) {
 1521     return rv;
 1522   }
 1523 
 1524   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1525     "CREATE TABLE index_data ("
 1526       "index_id INTEGER NOT NULL, "
 1527       "value NOT NULL, "
 1528       "object_data_key NOT NULL, "
 1529       "object_data_id INTEGER NOT NULL, "
 1530       "PRIMARY KEY (index_id, value, object_data_key), "
 1531       "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
 1532         "CASCADE, "
 1533       "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
 1534         "CASCADE"
 1535     ");"
 1536   ));
 1537   if (NS_WARN_IF(NS_FAILED(rv))) {
 1538     return rv;
 1539   }
 1540 
 1541   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1542     "INSERT OR IGNORE INTO index_data "
 1543       "SELECT index_id, value, object_data_key, object_data_id "
 1544       "FROM temp_upgrade;"
 1545   ));
 1546   if (NS_WARN_IF(NS_FAILED(rv))) {
 1547     return rv;
 1548   }
 1549 
 1550   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1551     "DROP TABLE temp_upgrade;"
 1552   ));
 1553   if (NS_WARN_IF(NS_FAILED(rv))) {
 1554     return rv;
 1555   }
 1556 
 1557   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1558     "CREATE INDEX index_data_object_data_id_index "
 1559     "ON index_data (object_data_id);"
 1560   ));
 1561   if (NS_WARN_IF(NS_FAILED(rv))) {
 1562     return rv;
 1563   }
 1564 
 1565   // Fix up the unique_index_data table. We're reordering the columns as well as
 1566   // changing the primary key from being a simple id to being a composite.
 1567   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1568     "CREATE TEMPORARY TABLE temp_upgrade ("
 1569       "index_id, "
 1570       "value, "
 1571       "object_data_key, "
 1572       "object_data_id "
 1573     ");"
 1574   ));
 1575   if (NS_WARN_IF(NS_FAILED(rv))) {
 1576     return rv;
 1577   }
 1578 
 1579   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1580     "INSERT INTO temp_upgrade "
 1581       "SELECT index_id, value, object_data_key, object_data_id "
 1582       "FROM unique_index_data;"
 1583   ));
 1584   if (NS_WARN_IF(NS_FAILED(rv))) {
 1585     return rv;
 1586   }
 1587 
 1588   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1589     "DROP TABLE unique_index_data;"
 1590   ));
 1591   if (NS_WARN_IF(NS_FAILED(rv))) {
 1592     return rv;
 1593   }
 1594 
 1595   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1596     "CREATE TABLE unique_index_data ("
 1597       "index_id INTEGER NOT NULL, "
 1598       "value NOT NULL, "
 1599       "object_data_key NOT NULL, "
 1600       "object_data_id INTEGER NOT NULL, "
 1601       "PRIMARY KEY (index_id, value, object_data_key), "
 1602       "UNIQUE (index_id, value), "
 1603       "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
 1604         "CASCADE "
 1605       "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
 1606         "CASCADE"
 1607     ");"
 1608   ));
 1609   if (NS_WARN_IF(NS_FAILED(rv))) {
 1610     return rv;
 1611   }
 1612 
 1613   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1614     "INSERT INTO unique_index_data "
 1615       "SELECT index_id, value, object_data_key, object_data_id "
 1616       "FROM temp_upgrade;"
 1617   ));
 1618   if (NS_WARN_IF(NS_FAILED(rv))) {
 1619     return rv;
 1620   }
 1621 
 1622   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1623     "DROP TABLE temp_upgrade;"
 1624   ));
 1625   if (NS_WARN_IF(NS_FAILED(rv))) {
 1626     return rv;
 1627   }
 1628 
 1629   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1630     "CREATE INDEX unique_index_data_object_data_id_index "
 1631     "ON unique_index_data (object_data_id);"
 1632   ));
 1633   if (NS_WARN_IF(NS_FAILED(rv))) {
 1634     return rv;
 1635   }
 1636 
 1637   // Fix up the ai_index_data table. We're reordering the columns as well as
 1638   // changing the primary key from being a simple id to being a composite.
 1639   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1640     "CREATE TEMPORARY TABLE temp_upgrade ("
 1641       "index_id, "
 1642       "value, "
 1643       "ai_object_data_id "
 1644     ");"
 1645   ));
 1646   if (NS_WARN_IF(NS_FAILED(rv))) {
 1647     return rv;
 1648   }
 1649 
 1650   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1651     "INSERT INTO temp_upgrade "
 1652       "SELECT index_id, value, ai_object_data_id "
 1653       "FROM ai_index_data;"
 1654   ));
 1655   if (NS_WARN_IF(NS_FAILED(rv))) {
 1656     return rv;
 1657   }
 1658 
 1659   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1660     "DROP TABLE ai_index_data;"
 1661   ));
 1662   if (NS_WARN_IF(NS_FAILED(rv))) {
 1663     return rv;
 1664   }
 1665 
 1666   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1667     "CREATE TABLE ai_index_data ("
 1668       "index_id INTEGER NOT NULL, "
 1669       "value NOT NULL, "
 1670       "ai_object_data_id INTEGER NOT NULL, "
 1671       "PRIMARY KEY (index_id, value, ai_object_data_id), "
 1672       "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
 1673         "CASCADE, "
 1674       "FOREIGN KEY (ai_object_data_id) REFERENCES ai_object_data(id) ON DELETE "
 1675         "CASCADE"
 1676     ");"
 1677   ));
 1678   if (NS_WARN_IF(NS_FAILED(rv))) {
 1679     return rv;
 1680   }
 1681 
 1682   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1683     "INSERT OR IGNORE INTO ai_index_data "
 1684       "SELECT index_id, value, ai_object_data_id "
 1685       "FROM temp_upgrade;"
 1686   ));
 1687   if (NS_WARN_IF(NS_FAILED(rv))) {
 1688     return rv;
 1689   }
 1690 
 1691   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1692     "DROP TABLE temp_upgrade;"
 1693   ));
 1694   if (NS_WARN_IF(NS_FAILED(rv))) {
 1695     return rv;
 1696   }
 1697 
 1698   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1699     "CREATE INDEX ai_index_data_ai_object_data_id_index "
 1700     "ON ai_index_data (ai_object_data_id);"
 1701   ));
 1702   if (NS_WARN_IF(NS_FAILED(rv))) {
 1703     return rv;
 1704   }
 1705 
 1706   // Fix up the ai_unique_index_data table. We're reordering the columns as well
 1707   // as changing the primary key from being a simple id to being a composite.
 1708   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1709     "CREATE TEMPORARY TABLE temp_upgrade ("
 1710       "index_id, "
 1711       "value, "
 1712       "ai_object_data_id "
 1713     ");"
 1714   ));
 1715   if (NS_WARN_IF(NS_FAILED(rv))) {
 1716     return rv;
 1717   }
 1718 
 1719   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1720     "INSERT INTO temp_upgrade "
 1721       "SELECT index_id, value, ai_object_data_id "
 1722       "FROM ai_unique_index_data;"
 1723   ));
 1724   if (NS_WARN_IF(NS_FAILED(rv))) {
 1725     return rv;
 1726   }
 1727 
 1728   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1729     "DROP TABLE ai_unique_index_data;"
 1730   ));
 1731   if (NS_WARN_IF(NS_FAILED(rv))) {
 1732     return rv;
 1733   }
 1734 
 1735   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1736     "CREATE TABLE ai_unique_index_data ("
 1737       "index_id INTEGER NOT NULL, "
 1738       "value NOT NULL, "
 1739       "ai_object_data_id INTEGER NOT NULL, "
 1740       "UNIQUE (index_id, value), "
 1741       "PRIMARY KEY (index_id, value, ai_object_data_id), "
 1742       "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
 1743         "CASCADE, "
 1744       "FOREIGN KEY (ai_object_data_id) REFERENCES ai_object_data(id) ON DELETE "
 1745         "CASCADE"
 1746     ");"
 1747   ));
 1748   if (NS_WARN_IF(NS_FAILED(rv))) {
 1749     return rv;
 1750   }
 1751 
 1752   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1753     "INSERT INTO ai_unique_index_data "
 1754       "SELECT index_id, value, ai_object_data_id "
 1755       "FROM temp_upgrade;"
 1756   ));
 1757   if (NS_WARN_IF(NS_FAILED(rv))) {
 1758     return rv;
 1759   }
 1760 
 1761   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1762     "DROP TABLE temp_upgrade;"
 1763   ));
 1764   if (NS_WARN_IF(NS_FAILED(rv))) {
 1765     return rv;
 1766   }
 1767 
 1768   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1769     "CREATE INDEX ai_unique_index_data_ai_object_data_id_index "
 1770     "ON ai_unique_index_data (ai_object_data_id);"
 1771   ));
 1772   if (NS_WARN_IF(NS_FAILED(rv))) {
 1773     return rv;
 1774   }
 1775 
 1776   rv = aConnection->SetSchemaVersion(6);
 1777   if (NS_WARN_IF(NS_FAILED(rv))) {
 1778     return rv;
 1779   }
 1780 
 1781   return NS_OK;
 1782 }
 1783 
 1784 nsresult
 1785 UpgradeSchemaFrom6To7(mozIStorageConnection* aConnection)
 1786 {
 1787   AssertIsOnIOThread();
 1788   MOZ_ASSERT(aConnection);
 1789 
 1790   PROFILER_LABEL("IndexedDB",
 1791                  "UpgradeSchemaFrom6To7",
 1792                  js::ProfileEntry::Category::STORAGE);
 1793 
 1794   nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1795     "CREATE TEMPORARY TABLE temp_upgrade ("
 1796       "id, "
 1797       "name, "
 1798       "key_path, "
 1799       "auto_increment"
 1800     ");"
 1801   ));
 1802   if (NS_WARN_IF(NS_FAILED(rv))) {
 1803     return rv;
 1804   }
 1805 
 1806   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1807     "INSERT INTO temp_upgrade "
 1808       "SELECT id, name, key_path, auto_increment "
 1809       "FROM object_store;"
 1810   ));
 1811   if (NS_WARN_IF(NS_FAILED(rv))) {
 1812     return rv;
 1813   }
 1814 
 1815   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1816     "DROP TABLE object_store;"
 1817   ));
 1818   if (NS_WARN_IF(NS_FAILED(rv))) {
 1819     return rv;
 1820   }
 1821 
 1822   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1823     "CREATE TABLE object_store ("
 1824       "id INTEGER PRIMARY KEY, "
 1825       "auto_increment INTEGER NOT NULL DEFAULT 0, "
 1826       "name TEXT NOT NULL, "
 1827       "key_path TEXT, "
 1828       "UNIQUE (name)"
 1829     ");"
 1830   ));
 1831   if (NS_WARN_IF(NS_FAILED(rv))) {
 1832     return rv;
 1833   }
 1834 
 1835   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1836     "INSERT INTO object_store "
 1837       "SELECT id, auto_increment, name, nullif(key_path, '') "
 1838       "FROM temp_upgrade;"
 1839   ));
 1840   if (NS_WARN_IF(NS_FAILED(rv))) {
 1841     return rv;
 1842   }
 1843 
 1844   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1845     "DROP TABLE temp_upgrade;"
 1846   ));
 1847   if (NS_WARN_IF(NS_FAILED(rv))) {
 1848     return rv;
 1849   }
 1850 
 1851   rv = aConnection->SetSchemaVersion(7);
 1852   if (NS_WARN_IF(NS_FAILED(rv))) {
 1853     return rv;
 1854   }
 1855 
 1856   return NS_OK;
 1857 }
 1858 
 1859 nsresult
 1860 UpgradeSchemaFrom7To8(mozIStorageConnection* aConnection)
 1861 {
 1862   AssertIsOnIOThread();
 1863   MOZ_ASSERT(aConnection);
 1864 
 1865   PROFILER_LABEL("IndexedDB",
 1866                  "UpgradeSchemaFrom7To8",
 1867                  js::ProfileEntry::Category::STORAGE);
 1868 
 1869   nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1870     "CREATE TEMPORARY TABLE temp_upgrade ("
 1871       "id, "
 1872       "object_store_id, "
 1873       "name, "
 1874       "key_path, "
 1875       "unique_index, "
 1876       "object_store_autoincrement"
 1877     ");"
 1878   ));
 1879   if (NS_WARN_IF(NS_FAILED(rv))) {
 1880     return rv;
 1881   }
 1882 
 1883   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1884     "INSERT INTO temp_upgrade "
 1885       "SELECT id, object_store_id, name, key_path, "
 1886       "unique_index, object_store_autoincrement "
 1887       "FROM object_store_index;"
 1888   ));
 1889   if (NS_WARN_IF(NS_FAILED(rv))) {
 1890     return rv;
 1891   }
 1892 
 1893   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1894     "DROP TABLE object_store_index;"
 1895   ));
 1896   if (NS_WARN_IF(NS_FAILED(rv))) {
 1897     return rv;
 1898   }
 1899 
 1900   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1901     "CREATE TABLE object_store_index ("
 1902       "id INTEGER, "
 1903       "object_store_id INTEGER NOT NULL, "
 1904       "name TEXT NOT NULL, "
 1905       "key_path TEXT NOT NULL, "
 1906       "unique_index INTEGER NOT NULL, "
 1907       "multientry INTEGER NOT NULL, "
 1908       "object_store_autoincrement INTERGER NOT NULL, "
 1909       "PRIMARY KEY (id), "
 1910       "UNIQUE (object_store_id, name), "
 1911       "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
 1912         "CASCADE"
 1913     ");"
 1914   ));
 1915   if (NS_WARN_IF(NS_FAILED(rv))) {
 1916     return rv;
 1917   }
 1918 
 1919   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1920     "INSERT INTO object_store_index "
 1921       "SELECT id, object_store_id, name, key_path, "
 1922       "unique_index, 0, object_store_autoincrement "
 1923       "FROM temp_upgrade;"
 1924   ));
 1925   if (NS_WARN_IF(NS_FAILED(rv))) {
 1926     return rv;
 1927   }
 1928 
 1929   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 1930     "DROP TABLE temp_upgrade;"
 1931   ));
 1932   if (NS_WARN_IF(NS_FAILED(rv))) {
 1933     return rv;
 1934   }
 1935 
 1936   rv = aConnection->SetSchemaVersion(8);
 1937   if (NS_WARN_IF(NS_FAILED(rv))) {
 1938     return rv;
 1939   }
 1940 
 1941   return NS_OK;
 1942 }
 1943 
 1944 class CompressDataBlobsFunction final
 1945   : public mozIStorageFunction
 1946 {
 1947 public:
 1948   NS_DECL_ISUPPORTS
 1949 
 1950 private:
 1951   ~CompressDataBlobsFunction()
 1952   { }
 1953 
 1954   NS_IMETHOD
 1955   OnFunctionCall(mozIStorageValueArray* aArguments,
 1956                  nsIVariant** aResult) override
 1957   {
 1958     MOZ_ASSERT(aArguments);
 1959     MOZ_ASSERT(aResult);
 1960 
 1961     PROFILER_LABEL("IndexedDB",
 1962                    "CompressDataBlobsFunction::OnFunctionCall",
 1963                    js::ProfileEntry::Category::STORAGE);
 1964 
 1965     uint32_t argc;
 1966     nsresult rv = aArguments->GetNumEntries(&argc);
 1967     if (NS_WARN_IF(NS_FAILED(rv))) {
 1968       return rv;
 1969     }
 1970 
 1971     if (argc != 1) {
 1972       NS_WARNING("Don't call me with the wrong number of arguments!");
 1973       return NS_ERROR_UNEXPECTED;
 1974     }
 1975 
 1976     int32_t type;
 1977     rv = aArguments->GetTypeOfIndex(0, &type);
 1978     if (NS_WARN_IF(NS_FAILED(rv))) {
 1979       return rv;
 1980     }
 1981 
 1982     if (type != mozIStorageStatement::VALUE_TYPE_BLOB) {
 1983       NS_WARNING("Don't call me with the wrong type of arguments!");
 1984       return NS_ERROR_UNEXPECTED;
 1985     }
 1986 
 1987     const uint8_t* uncompressed;
 1988     uint32_t uncompressedLength;
 1989     rv = aArguments->GetSharedBlob(0, &uncompressedLength, &uncompressed);
 1990     if (NS_WARN_IF(NS_FAILED(rv))) {
 1991       return rv;
 1992     }
 1993 
 1994     size_t compressedLength = snappy::MaxCompressedLength(uncompressedLength);
 1995     UniqueFreePtr<uint8_t> compressed(
 1996       static_cast<uint8_t*>(malloc(compressedLength)));
 1997     if (NS_WARN_IF(!compressed)) {
 1998       return NS_ERROR_OUT_OF_MEMORY;
 1999     }
 2000 
 2001     snappy::RawCompress(reinterpret_cast<const char*>(uncompressed),
 2002                         uncompressedLength,
 2003                         reinterpret_cast<char*>(compressed.get()),
 2004                         &compressedLength);
 2005 
 2006     std::pair<uint8_t *, int> data(compressed.release(),
 2007                                    int(compressedLength));
 2008 
 2009     nsCOMPtr<nsIVariant> result = new mozilla::storage::AdoptedBlobVariant(data);
 2010 
 2011     result.forget(aResult);
 2012     return NS_OK;
 2013   }
 2014 };
 2015 
 2016 nsresult
 2017 UpgradeSchemaFrom8To9_0(mozIStorageConnection* aConnection)
 2018 {
 2019   AssertIsOnIOThread();
 2020   MOZ_ASSERT(aConnection);
 2021 
 2022   PROFILER_LABEL("IndexedDB",
 2023                  "UpgradeSchemaFrom8To9_0",
 2024                  js::ProfileEntry::Category::STORAGE);
 2025 
 2026   // We no longer use the dataVersion column.
 2027   nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2028     "UPDATE database SET dataVersion = 0;"
 2029   ));
 2030   if (NS_WARN_IF(NS_FAILED(rv))) {
 2031     return rv;
 2032   }
 2033 
 2034   nsCOMPtr<mozIStorageFunction> compressor = new CompressDataBlobsFunction();
 2035 
 2036   NS_NAMED_LITERAL_CSTRING(compressorName, "compress");
 2037 
 2038   rv = aConnection->CreateFunction(compressorName, 1, compressor);
 2039   if (NS_WARN_IF(NS_FAILED(rv))) {
 2040     return rv;
 2041   }
 2042 
 2043   // Turn off foreign key constraints before we do anything here.
 2044   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2045     "UPDATE object_data SET data = compress(data);"
 2046   ));
 2047   if (NS_WARN_IF(NS_FAILED(rv))) {
 2048     return rv;
 2049   }
 2050 
 2051   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2052     "UPDATE ai_object_data SET data = compress(data);"
 2053   ));
 2054   if (NS_WARN_IF(NS_FAILED(rv))) {
 2055     return rv;
 2056   }
 2057 
 2058   rv = aConnection->RemoveFunction(compressorName);
 2059   if (NS_WARN_IF(NS_FAILED(rv))) {
 2060     return rv;
 2061   }
 2062 
 2063   rv = aConnection->SetSchemaVersion(MakeSchemaVersion(9, 0));
 2064   if (NS_WARN_IF(NS_FAILED(rv))) {
 2065     return rv;
 2066   }
 2067 
 2068   return NS_OK;
 2069 }
 2070 
 2071 nsresult
 2072 UpgradeSchemaFrom9_0To10_0(mozIStorageConnection* aConnection)
 2073 {
 2074   AssertIsOnIOThread();
 2075   MOZ_ASSERT(aConnection);
 2076 
 2077   PROFILER_LABEL("IndexedDB",
 2078                  "UpgradeSchemaFrom9_0To10_0",
 2079                  js::ProfileEntry::Category::STORAGE);
 2080 
 2081   nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2082     "ALTER TABLE object_data ADD COLUMN file_ids TEXT;"
 2083   ));
 2084   if (NS_WARN_IF(NS_FAILED(rv))) {
 2085     return rv;
 2086   }
 2087 
 2088   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2089     "ALTER TABLE ai_object_data ADD COLUMN file_ids TEXT;"
 2090   ));
 2091   if (NS_WARN_IF(NS_FAILED(rv))) {
 2092     return rv;
 2093   }
 2094 
 2095   rv = CreateFileTables(aConnection);
 2096   if (NS_WARN_IF(NS_FAILED(rv))) {
 2097     return rv;
 2098   }
 2099 
 2100   rv = aConnection->SetSchemaVersion(MakeSchemaVersion(10, 0));
 2101   if (NS_WARN_IF(NS_FAILED(rv))) {
 2102     return rv;
 2103   }
 2104 
 2105   return NS_OK;
 2106 }
 2107 
 2108 nsresult
 2109 UpgradeSchemaFrom10_0To11_0(mozIStorageConnection* aConnection)
 2110 {
 2111   AssertIsOnIOThread();
 2112   MOZ_ASSERT(aConnection);
 2113 
 2114   PROFILER_LABEL("IndexedDB",
 2115                  "UpgradeSchemaFrom10_0To11_0",
 2116                  js::ProfileEntry::Category::STORAGE);
 2117 
 2118   nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2119     "CREATE TEMPORARY TABLE temp_upgrade ("
 2120       "id, "
 2121       "object_store_id, "
 2122       "name, "
 2123       "key_path, "
 2124       "unique_index, "
 2125       "multientry"
 2126     ");"
 2127   ));
 2128   if (NS_WARN_IF(NS_FAILED(rv))) {
 2129     return rv;
 2130   }
 2131 
 2132   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2133     "INSERT INTO temp_upgrade "
 2134       "SELECT id, object_store_id, name, key_path, "
 2135       "unique_index, multientry "
 2136       "FROM object_store_index;"
 2137   ));
 2138   if (NS_WARN_IF(NS_FAILED(rv))) {
 2139     return rv;
 2140   }
 2141 
 2142   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2143     "DROP TABLE object_store_index;"
 2144   ));
 2145   if (NS_WARN_IF(NS_FAILED(rv))) {
 2146     return rv;
 2147   }
 2148 
 2149   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2150     "CREATE TABLE object_store_index ("
 2151       "id INTEGER PRIMARY KEY, "
 2152       "object_store_id INTEGER NOT NULL, "
 2153       "name TEXT NOT NULL, "
 2154       "key_path TEXT NOT NULL, "
 2155       "unique_index INTEGER NOT NULL, "
 2156       "multientry INTEGER NOT NULL, "
 2157       "UNIQUE (object_store_id, name), "
 2158       "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
 2159         "CASCADE"
 2160     ");"
 2161   ));
 2162   if (NS_WARN_IF(NS_FAILED(rv))) {
 2163     return rv;
 2164   }
 2165 
 2166   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2167     "INSERT INTO object_store_index "
 2168       "SELECT id, object_store_id, name, key_path, "
 2169       "unique_index, multientry "
 2170       "FROM temp_upgrade;"
 2171   ));
 2172   if (NS_WARN_IF(NS_FAILED(rv))) {
 2173     return rv;
 2174   }
 2175 
 2176   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2177     "DROP TABLE temp_upgrade;"
 2178   ));
 2179   if (NS_WARN_IF(NS_FAILED(rv))) {
 2180     return rv;
 2181   }
 2182 
 2183   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2184     "DROP TRIGGER object_data_insert_trigger;"
 2185   ));
 2186   if (NS_WARN_IF(NS_FAILED(rv))) {
 2187     return rv;
 2188   }
 2189 
 2190   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2191     "INSERT INTO object_data (object_store_id, key_value, data, file_ids) "
 2192       "SELECT object_store_id, id, data, file_ids "
 2193       "FROM ai_object_data;"
 2194   ));
 2195   if (NS_WARN_IF(NS_FAILED(rv))) {
 2196     return rv;
 2197   }
 2198 
 2199   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2200     "CREATE TRIGGER object_data_insert_trigger "
 2201     "AFTER INSERT ON object_data "
 2202     "FOR EACH ROW "
 2203     "WHEN NEW.file_ids IS NOT NULL "
 2204     "BEGIN "
 2205       "SELECT update_refcount(NULL, NEW.file_ids); "
 2206     "END;"
 2207   ));
 2208   if (NS_WARN_IF(NS_FAILED(rv))) {
 2209     return rv;
 2210   }
 2211 
 2212   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2213     "INSERT INTO index_data (index_id, value, object_data_key, object_data_id) "
 2214       "SELECT ai_index_data.index_id, ai_index_data.value, ai_index_data.ai_object_data_id, object_data.id "
 2215       "FROM ai_index_data "
 2216       "INNER JOIN object_store_index ON "
 2217         "object_store_index.id = ai_index_data.index_id "
 2218       "INNER JOIN object_data ON "
 2219         "object_data.object_store_id = object_store_index.object_store_id AND "
 2220         "object_data.key_value = ai_index_data.ai_object_data_id;"
 2221   ));
 2222   if (NS_WARN_IF(NS_FAILED(rv))) {
 2223     return rv;
 2224   }
 2225 
 2226   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2227     "INSERT INTO unique_index_data (index_id, value, object_data_key, object_data_id) "
 2228       "SELECT ai_unique_index_data.index_id, ai_unique_index_data.value, ai_unique_index_data.ai_object_data_id, object_data.id "
 2229       "FROM ai_unique_index_data "
 2230       "INNER JOIN object_store_index ON "
 2231         "object_store_index.id = ai_unique_index_data.index_id "
 2232       "INNER JOIN object_data ON "
 2233         "object_data.object_store_id = object_store_index.object_store_id AND "
 2234         "object_data.key_value = ai_unique_index_data.ai_object_data_id;"
 2235   ));
 2236   if (NS_WARN_IF(NS_FAILED(rv))) {
 2237     return rv;
 2238   }
 2239 
 2240   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2241     "UPDATE object_store "
 2242       "SET auto_increment = (SELECT max(id) FROM ai_object_data) + 1 "
 2243       "WHERE auto_increment;"
 2244   ));
 2245   if (NS_WARN_IF(NS_FAILED(rv))) {
 2246     return rv;
 2247   }
 2248 
 2249   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2250     "DROP TABLE ai_unique_index_data;"
 2251   ));
 2252   if (NS_WARN_IF(NS_FAILED(rv))) {
 2253     return rv;
 2254   }
 2255 
 2256   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2257     "DROP TABLE ai_index_data;"
 2258   ));
 2259   if (NS_WARN_IF(NS_FAILED(rv))) {
 2260     return rv;
 2261   }
 2262 
 2263   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2264     "DROP TABLE ai_object_data;"
 2265   ));
 2266   if (NS_WARN_IF(NS_FAILED(rv))) {
 2267     return rv;
 2268   }
 2269 
 2270   rv = aConnection->SetSchemaVersion(MakeSchemaVersion(11, 0));
 2271   if (NS_WARN_IF(NS_FAILED(rv))) {
 2272     return rv;
 2273   }
 2274 
 2275   return NS_OK;
 2276 }
 2277 
 2278 class EncodeKeysFunction final
 2279   : public mozIStorageFunction
 2280 {
 2281 public:
 2282   NS_DECL_ISUPPORTS
 2283 
 2284 private:
 2285   ~EncodeKeysFunction()
 2286   { }
 2287 
 2288   NS_IMETHOD
 2289   OnFunctionCall(mozIStorageValueArray* aArguments,
 2290                  nsIVariant** aResult) override
 2291   {
 2292     MOZ_ASSERT(aArguments);
 2293     MOZ_ASSERT(aResult);
 2294 
 2295     PROFILER_LABEL("IndexedDB",
 2296                    "EncodeKeysFunction::OnFunctionCall",
 2297                    js::ProfileEntry::Category::STORAGE);
 2298 
 2299     uint32_t argc;
 2300     nsresult rv = aArguments->GetNumEntries(&argc);
 2301     if (NS_WARN_IF(NS_FAILED(rv))) {
 2302       return rv;
 2303     }
 2304 
 2305     if (argc != 1) {
 2306       NS_WARNING("Don't call me with the wrong number of arguments!");
 2307       return NS_ERROR_UNEXPECTED;
 2308     }
 2309 
 2310     int32_t type;
 2311     rv = aArguments->GetTypeOfIndex(0, &type);
 2312     if (NS_WARN_IF(NS_FAILED(rv))) {
 2313       return rv;
 2314     }
 2315 
 2316     Key key;
 2317     if (type == mozIStorageStatement::VALUE_TYPE_INTEGER) {
 2318       int64_t intKey;
 2319       aArguments->GetInt64(0, &intKey);
 2320       key.SetFromInteger(intKey);
 2321     } else if (type == mozIStorageStatement::VALUE_TYPE_TEXT) {
 2322       nsString stringKey;
 2323       aArguments->GetString(0, stringKey);
 2324       key.SetFromString(stringKey);
 2325     } else {
 2326       NS_WARNING("Don't call me with the wrong type of arguments!");
 2327       return NS_ERROR_UNEXPECTED;
 2328     }
 2329 
 2330     const nsCString& buffer = key.GetBuffer();
 2331 
 2332     std::pair<const void *, int> data(static_cast<const void*>(buffer.get()),
 2333                                       int(buffer.Length()));
 2334 
 2335     nsCOMPtr<nsIVariant> result = new mozilla::storage::BlobVariant(data);
 2336 
 2337     result.forget(aResult);
 2338     return NS_OK;
 2339   }
 2340 };
 2341 
 2342 nsresult
 2343 UpgradeSchemaFrom11_0To12_0(mozIStorageConnection* aConnection)
 2344 {
 2345   AssertIsOnIOThread();
 2346   MOZ_ASSERT(aConnection);
 2347 
 2348   PROFILER_LABEL("IndexedDB",
 2349                  "UpgradeSchemaFrom11_0To12_0",
 2350                  js::ProfileEntry::Category::STORAGE);
 2351 
 2352   NS_NAMED_LITERAL_CSTRING(encoderName, "encode");
 2353 
 2354   nsCOMPtr<mozIStorageFunction> encoder = new EncodeKeysFunction();
 2355 
 2356   nsresult rv = aConnection->CreateFunction(encoderName, 1, encoder);
 2357   if (NS_WARN_IF(NS_FAILED(rv))) {
 2358     return rv;
 2359   }
 2360 
 2361   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2362     "CREATE TEMPORARY TABLE temp_upgrade ("
 2363       "id INTEGER PRIMARY KEY, "
 2364       "object_store_id, "
 2365       "key_value, "
 2366       "data, "
 2367       "file_ids "
 2368     ");"
 2369   ));
 2370   if (NS_WARN_IF(NS_FAILED(rv))) {
 2371     return rv;
 2372   }
 2373 
 2374   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2375     "INSERT INTO temp_upgrade "
 2376       "SELECT id, object_store_id, encode(key_value), data, file_ids "
 2377       "FROM object_data;"
 2378   ));
 2379   if (NS_WARN_IF(NS_FAILED(rv))) {
 2380     return rv;
 2381   }
 2382 
 2383   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2384     "DROP TABLE object_data;"
 2385   ));
 2386   if (NS_WARN_IF(NS_FAILED(rv))) {
 2387     return rv;
 2388   }
 2389 
 2390   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2391     "CREATE TABLE object_data ("
 2392       "id INTEGER PRIMARY KEY, "
 2393       "object_store_id INTEGER NOT NULL, "
 2394       "key_value BLOB DEFAULT NULL, "
 2395       "file_ids TEXT, "
 2396       "data BLOB NOT NULL, "
 2397       "UNIQUE (object_store_id, key_value), "
 2398       "FOREIGN KEY (object_store_id) REFERENCES object_store(id) ON DELETE "
 2399         "CASCADE"
 2400     ");"
 2401   ));
 2402   if (NS_WARN_IF(NS_FAILED(rv))) {
 2403     return rv;
 2404   }
 2405 
 2406   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2407     "INSERT INTO object_data "
 2408       "SELECT id, object_store_id, key_value, file_ids, data "
 2409       "FROM temp_upgrade;"
 2410   ));
 2411   if (NS_WARN_IF(NS_FAILED(rv))) {
 2412     return rv;
 2413   }
 2414 
 2415   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2416     "DROP TABLE temp_upgrade;"
 2417   ));
 2418   if (NS_WARN_IF(NS_FAILED(rv))) {
 2419     return rv;
 2420   }
 2421 
 2422   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2423     "CREATE TRIGGER object_data_insert_trigger "
 2424     "AFTER INSERT ON object_data "
 2425     "FOR EACH ROW "
 2426     "WHEN NEW.file_ids IS NOT NULL "
 2427     "BEGIN "
 2428       "SELECT update_refcount(NULL, NEW.file_ids); "
 2429     "END;"
 2430   ));
 2431   if (NS_WARN_IF(NS_FAILED(rv))) {
 2432     return rv;
 2433   }
 2434 
 2435   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2436     "CREATE TRIGGER object_data_update_trigger "
 2437     "AFTER UPDATE OF file_ids ON object_data "
 2438     "FOR EACH ROW "
 2439     "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL "
 2440     "BEGIN "
 2441       "SELECT update_refcount(OLD.file_ids, NEW.file_ids); "
 2442     "END;"
 2443   ));
 2444   if (NS_WARN_IF(NS_FAILED(rv))) {
 2445     return rv;
 2446   }
 2447 
 2448   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2449     "CREATE TRIGGER object_data_delete_trigger "
 2450     "AFTER DELETE ON object_data "
 2451     "FOR EACH ROW WHEN OLD.file_ids IS NOT NULL "
 2452     "BEGIN "
 2453       "SELECT update_refcount(OLD.file_ids, NULL); "
 2454     "END;"
 2455   ));
 2456   if (NS_WARN_IF(NS_FAILED(rv))) {
 2457     return rv;
 2458   }
 2459 
 2460   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2461     "CREATE TEMPORARY TABLE temp_upgrade ("
 2462       "index_id, "
 2463       "value, "
 2464       "object_data_key, "
 2465       "object_data_id "
 2466     ");"
 2467   ));
 2468   if (NS_WARN_IF(NS_FAILED(rv))) {
 2469     return rv;
 2470   }
 2471 
 2472   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2473     "INSERT INTO temp_upgrade "
 2474       "SELECT index_id, encode(value), encode(object_data_key), object_data_id "
 2475       "FROM index_data;"
 2476   ));
 2477   if (NS_WARN_IF(NS_FAILED(rv))) {
 2478     return rv;
 2479   }
 2480 
 2481   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2482     "DROP TABLE index_data;"
 2483   ));
 2484   if (NS_WARN_IF(NS_FAILED(rv))) {
 2485     return rv;
 2486   }
 2487 
 2488   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2489     "CREATE TABLE index_data ("
 2490       "index_id INTEGER NOT NULL, "
 2491       "value BLOB NOT NULL, "
 2492       "object_data_key BLOB NOT NULL, "
 2493       "object_data_id INTEGER NOT NULL, "
 2494       "PRIMARY KEY (index_id, value, object_data_key), "
 2495       "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
 2496         "CASCADE, "
 2497       "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
 2498         "CASCADE"
 2499     ");"
 2500   ));
 2501   if (NS_WARN_IF(NS_FAILED(rv))) {
 2502     return rv;
 2503   }
 2504 
 2505   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2506     "INSERT INTO index_data "
 2507       "SELECT index_id, value, object_data_key, object_data_id "
 2508       "FROM temp_upgrade;"
 2509   ));
 2510   if (NS_WARN_IF(NS_FAILED(rv))) {
 2511     return rv;
 2512   }
 2513 
 2514   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2515     "DROP TABLE temp_upgrade;"
 2516   ));
 2517   if (NS_WARN_IF(NS_FAILED(rv))) {
 2518     return rv;
 2519   }
 2520 
 2521   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2522     "CREATE INDEX index_data_object_data_id_index "
 2523     "ON index_data (object_data_id);"
 2524   ));
 2525   if (NS_WARN_IF(NS_FAILED(rv))) {
 2526     return rv;
 2527   }
 2528 
 2529   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2530     "CREATE TEMPORARY TABLE temp_upgrade ("
 2531       "index_id, "
 2532       "value, "
 2533       "object_data_key, "
 2534       "object_data_id "
 2535     ");"
 2536   ));
 2537   if (NS_WARN_IF(NS_FAILED(rv))) {
 2538     return rv;
 2539   }
 2540 
 2541   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2542     "INSERT INTO temp_upgrade "
 2543       "SELECT index_id, encode(value), encode(object_data_key), object_data_id "
 2544       "FROM unique_index_data;"
 2545   ));
 2546   if (NS_WARN_IF(NS_FAILED(rv))) {
 2547     return rv;
 2548   }
 2549 
 2550   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2551     "DROP TABLE unique_index_data;"
 2552   ));
 2553   if (NS_WARN_IF(NS_FAILED(rv))) {
 2554     return rv;
 2555   }
 2556 
 2557   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2558     "CREATE TABLE unique_index_data ("
 2559       "index_id INTEGER NOT NULL, "
 2560       "value BLOB NOT NULL, "
 2561       "object_data_key BLOB NOT NULL, "
 2562       "object_data_id INTEGER NOT NULL, "
 2563       "PRIMARY KEY (index_id, value, object_data_key), "
 2564       "UNIQUE (index_id, value), "
 2565       "FOREIGN KEY (index_id) REFERENCES object_store_index(id) ON DELETE "
 2566         "CASCADE "
 2567       "FOREIGN KEY (object_data_id) REFERENCES object_data(id) ON DELETE "
 2568         "CASCADE"
 2569     ");"
 2570   ));
 2571   if (NS_WARN_IF(NS_FAILED(rv))) {
 2572     return rv;
 2573   }
 2574 
 2575   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2576     "INSERT INTO unique_index_data "
 2577       "SELECT index_id, value, object_data_key, object_data_id "
 2578       "FROM temp_upgrade;"
 2579   ));
 2580   if (NS_WARN_IF(NS_FAILED(rv))) {
 2581     return rv;
 2582   }
 2583 
 2584   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2585     "DROP TABLE temp_upgrade;"
 2586   ));
 2587   if (NS_WARN_IF(NS_FAILED(rv))) {
 2588     return rv;
 2589   }
 2590 
 2591   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 2592     "CREATE INDEX unique_index_data_object_data_id_index "
 2593     "ON unique_index_data (object_data_id);"
 2594   ));
 2595   if (NS_WARN_IF(NS_FAILED(rv))) {
 2596     return rv;
 2597   }
 2598 
 2599   rv = aConnection->RemoveFunction(encoderName);
 2600   if (NS_WARN_IF(NS_FAILED(rv))) {
 2601     return rv;
 2602   }
 2603 
 2604   rv = aConnection->SetSchemaVersion(MakeSchemaVersion(12, 0));
 2605   if (NS_WARN_IF(NS_FAILED(rv))) {
 2606     return rv;
 2607   }
 2608 
 2609   return NS_OK;
 2610 }
 2611 
 2612 nsresult
 2613 UpgradeSchemaFrom12_0To13_0(mozIStorageConnection* aConnection,
 2614                             bool* aVacuumNeeded)
 2615 {
 2616   AssertIsOnIOThread();
 2617   MOZ_ASSERT(aConnection);
 2618 
 2619   PROFILER_LABEL("IndexedDB",
 2620                  "UpgradeSchemaFrom12_0To13_0",
 2621                  js::ProfileEntry::Category::STORAGE);
 2622 
 2623   nsresult rv;
 2624 
 2625 #ifdef IDB_MOBILE
 2626   int32_t defaultPageSize;
 2627   rv = aConnection->GetDefaultPageSize(&defaultPageSize);
 2628   if (NS_WARN_IF(NS_FAILED(rv))) {
 2629     return rv;
 2630   }
 2631 
 2632   // Enable auto_vacuum mode and update the page size to the platform default.
 2633   nsAutoCString upgradeQuery("PRAGMA auto_vacuum = FULL; PRAGMA page_size = ");
 2634   upgradeQuery.AppendInt(defaultPageSize);
 2635 
 2636   rv = aConnection->ExecuteSimpleSQL(upgradeQuery);
 2637   if (NS_WARN_IF(NS_FAILED(rv))) {
 2638     return rv;
 2639   }
 2640 
 2641   *aVacuumNeeded = true;
 2642 #endif
 2643 
 2644   rv = aConnection->SetSchemaVersion(MakeSchemaVersion(13, 0));
 2645   if (NS_WARN_IF(NS_FAILED(rv))) {
 2646     return rv;
 2647   }
 2648 
 2649   return NS_OK;
 2650 }
 2651 
 2652 nsresult
 2653 UpgradeSchemaFrom13_0To14_0(mozIStorageConnection* aConnection)
 2654 {
 2655   AssertIsOnIOThread();
 2656   MOZ_ASSERT(aConnection);
 2657 
 2658   // The only change between 13 and 14 was a different structured
 2659   // clone format, but it's backwards-compatible.
 2660   nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(14, 0));
 2661   if (NS_WARN_IF(NS_FAILED(rv))) {
 2662     return rv;
 2663   }
 2664 
 2665   return NS_OK;
 2666 }
 2667 
 2668 nsresult
 2669 UpgradeSchemaFrom14_0To15_0(mozIStorageConnection* aConnection)
 2670 {
 2671   // The only change between 14 and 15 was a different structured
 2672   // clone format, but it's backwards-compatible.
 2673   nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(15, 0));
 2674   if (NS_WARN_IF(NS_FAILED(rv))) {
 2675     return rv;
 2676   }
 2677 
 2678   return NS_OK;
 2679 }
 2680 
 2681 nsresult
 2682 UpgradeSchemaFrom15_0To16_0(mozIStorageConnection* aConnection)
 2683 {
 2684   // The only change between 15 and 16 was a different structured
 2685   // clone format, but it's backwards-compatible.
 2686   nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(16, 0));
 2687   if (NS_WARN_IF(NS_FAILED(rv))) {
 2688     return rv;
 2689   }
 2690 
 2691   return NS_OK;
 2692 }
 2693 
 2694 nsresult
 2695 UpgradeSchemaFrom16_0To17_0(mozIStorageConnection* aConnection)
 2696 {
 2697   // The only change between 16 and 17 was a different structured
 2698   // clone format, but it's backwards-compatible.
 2699   nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(17, 0));
 2700   if (NS_WARN_IF(NS_FAILED(rv))) {
 2701     return rv;
 2702   }
 2703 
 2704   return NS_OK;
 2705 }
 2706 
 2707 class UpgradeSchemaFrom17_0To18_0Helper final
 2708 {
 2709   class InsertIndexDataValuesFunction;
 2710   class UpgradeKeyFunction;
 2711 
 2712 public:
 2713   static nsresult
 2714   DoUpgrade(mozIStorageConnection* aConnection, const nsACString& aOrigin);
 2715 
 2716 private:
 2717   static nsresult
 2718   DoUpgradeInternal(mozIStorageConnection* aConnection,
 2719                     const nsACString& aOrigin);
 2720 
 2721   UpgradeSchemaFrom17_0To18_0Helper()
 2722   {
 2723     MOZ_ASSERT_UNREACHABLE("Don't create instances of this class!");
 2724   }
 2725 
 2726   ~UpgradeSchemaFrom17_0To18_0Helper()
 2727   {
 2728     MOZ_ASSERT_UNREACHABLE("Don't create instances of this class!");
 2729   }
 2730 };
 2731 
 2732 class UpgradeSchemaFrom17_0To18_0Helper::InsertIndexDataValuesFunction final
 2733   : public mozIStorageFunction
 2734 {
 2735 public:
 2736   InsertIndexDataValuesFunction()
 2737   { }
 2738 
 2739   NS_DECL_ISUPPORTS
 2740 
 2741 private:
 2742   ~InsertIndexDataValuesFunction()
 2743   { }
 2744 
 2745   NS_DECL_MOZISTORAGEFUNCTION
 2746 };
 2747 
 2748 NS_IMPL_ISUPPORTS(UpgradeSchemaFrom17_0To18_0Helper::
 2749                     InsertIndexDataValuesFunction,
 2750                   mozIStorageFunction);
 2751 
 2752 NS_IMETHODIMP
 2753 UpgradeSchemaFrom17_0To18_0Helper::
 2754 InsertIndexDataValuesFunction::OnFunctionCall(mozIStorageValueArray* aValues,
 2755                                               nsIVariant** _retval)
 2756 {
 2757   MOZ_ASSERT(!NS_IsMainThread());
 2758   MOZ_ASSERT(!IsOnBackgroundThread());
 2759   MOZ_ASSERT(aValues);
 2760   MOZ_ASSERT(_retval);
 2761 
 2762 #ifdef DEBUG
 2763   {
 2764     uint32_t argCount;
 2765     MOZ_ALWAYS_SUCCEEDS(aValues->GetNumEntries(&argCount));
 2766     MOZ_ASSERT(argCount == 4);
 2767 
 2768     int32_t valueType;
 2769     MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(0, &valueType));
 2770     MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_NULL ||
 2771                valueType == mozIStorageValueArray::VALUE_TYPE_BLOB);
 2772 
 2773     MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(1, &valueType));
 2774     MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_INTEGER);
 2775 
 2776     MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(2, &valueType));
 2777     MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_INTEGER);
 2778 
 2779     MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(3, &valueType));
 2780     MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_BLOB);
 2781   }
 2782 #endif
 2783 
 2784   // Read out the previous value. It may be NULL, in which case we'll just end
 2785   // up with an empty array.
 2786   AutoTArray<IndexDataValue, 32> indexValues;
 2787   nsresult rv = ReadCompressedIndexDataValues(aValues, 0, indexValues);
 2788   if (NS_WARN_IF(NS_FAILED(rv))) {
 2789     return rv;
 2790   }
 2791 
 2792   int64_t indexId;
 2793   rv = aValues->GetInt64(1, &indexId);
 2794   if (NS_WARN_IF(NS_FAILED(rv))) {
 2795     return rv;
 2796   }
 2797 
 2798   int32_t unique;
 2799   rv = aValues->GetInt32(2, &unique);
 2800   if (NS_WARN_IF(NS_FAILED(rv))) {
 2801     return rv;
 2802   }
 2803 
 2804   Key value;
 2805   rv = value.SetFromValueArray(aValues, 3);
 2806   if (NS_WARN_IF(NS_FAILED(rv))) {
 2807     return rv;
 2808   }
 2809 
 2810   // Update the array with the new addition.
 2811   if (NS_WARN_IF(!indexValues.SetCapacity(indexValues.Length() + 1,
 2812                                           fallible))) {
 2813     IDB_REPORT_INTERNAL_ERR();
 2814     return NS_ERROR_OUT_OF_MEMORY;
 2815   }
 2816 
 2817   MOZ_ALWAYS_TRUE(
 2818     indexValues.InsertElementSorted(IndexDataValue(indexId, !!unique, value),
 2819                                     fallible));
 2820 
 2821   // Compress the array.
 2822   UniqueFreePtr<uint8_t> indexValuesBlob;
 2823   uint32_t indexValuesBlobLength;
 2824   rv = MakeCompressedIndexDataValues(indexValues,
 2825                                      indexValuesBlob,
 2826                                      &indexValuesBlobLength);
 2827   if (NS_WARN_IF(NS_FAILED(rv))) {
 2828     return rv;
 2829   }
 2830 
 2831   // The compressed blob is the result of this function.
 2832   std::pair<uint8_t *, int> indexValuesBlobPair(indexValuesBlob.release(),
 2833                                                 indexValuesBlobLength);
 2834 
 2835   nsCOMPtr<nsIVariant> result =
 2836     new storage::AdoptedBlobVariant(indexValuesBlobPair);
 2837 
 2838   result.forget(_retval);
 2839   return NS_OK;
 2840 }
 2841 
 2842 class UpgradeSchemaFrom17_0To18_0Helper::UpgradeKeyFunction final
 2843   : public mozIStorageFunction
 2844 {
 2845 public:
 2846   UpgradeKeyFunction()
 2847   { }
 2848 
 2849   static nsresult
 2850   CopyAndUpgradeKeyBuffer(const uint8_t* aSource,
 2851                           const uint8_t* aSourceEnd,
 2852                           uint8_t* aDestination)
 2853   {
 2854     return CopyAndUpgradeKeyBufferInternal(aSource,
 2855                                            aSourceEnd,
 2856                                            aDestination,
 2857                                            0 /* aTagOffset */,
 2858                                            0 /* aRecursionDepth */);
 2859   }
 2860 
 2861   NS_DECL_ISUPPORTS
 2862 
 2863 private:
 2864   ~UpgradeKeyFunction()
 2865   { }
 2866 
 2867   static nsresult
 2868   CopyAndUpgradeKeyBufferInternal(const uint8_t*& aSource,
 2869                                   const uint8_t* aSourceEnd,
 2870                                   uint8_t*& aDestination,
 2871                                   uint8_t aTagOffset,
 2872                                   uint8_t aRecursionDepth);
 2873 
 2874   static uint32_t
 2875   AdjustedSize(uint32_t aMaxSize,
 2876                const uint8_t* aSource,
 2877                const uint8_t* aSourceEnd)
 2878   {
 2879     MOZ_ASSERT(aMaxSize);
 2880     MOZ_ASSERT(aSource);
 2881     MOZ_ASSERT(aSourceEnd);
 2882     MOZ_ASSERT(aSource <= aSourceEnd);
 2883 
 2884     return std::min(aMaxSize, uint32_t(aSourceEnd - aSource));
 2885   }
 2886 
 2887   NS_DECL_MOZISTORAGEFUNCTION
 2888 };
 2889 
 2890 // static
 2891 nsresult
 2892 UpgradeSchemaFrom17_0To18_0Helper::
 2893 UpgradeKeyFunction::CopyAndUpgradeKeyBufferInternal(const uint8_t*& aSource,
 2894                                                     const uint8_t* aSourceEnd,
 2895                                                     uint8_t*& aDestination,
 2896                                                     uint8_t aTagOffset,
 2897                                                     uint8_t aRecursionDepth)
 2898 {
 2899   MOZ_ASSERT(!NS_IsMainThread());
 2900   MOZ_ASSERT(!IsOnBackgroundThread());
 2901   MOZ_ASSERT(aSource);
 2902   MOZ_ASSERT(*aSource);
 2903   MOZ_ASSERT(aSourceEnd);
 2904   MOZ_ASSERT(aSource < aSourceEnd);
 2905   MOZ_ASSERT(aDestination);
 2906   MOZ_ASSERT(aTagOffset <=  Key::kMaxArrayCollapse);
 2907 
 2908   static constexpr uint8_t kOldNumberTag = 0x1;
 2909   static constexpr uint8_t kOldDateTag = 0x2;
 2910   static constexpr uint8_t kOldStringTag = 0x3;
 2911   static constexpr uint8_t kOldArrayTag = 0x4;
 2912   static constexpr uint8_t kOldMaxType = kOldArrayTag;
 2913 
 2914   if (NS_WARN_IF(aRecursionDepth > Key::kMaxRecursionDepth)) {
 2915     IDB_REPORT_INTERNAL_ERR();
 2916     return NS_ERROR_FILE_CORRUPTED;
 2917   }
 2918 
 2919   const uint8_t sourceTag = *aSource - (aTagOffset * kOldMaxType);
 2920   MOZ_ASSERT(sourceTag);
 2921 
 2922   if (NS_WARN_IF(sourceTag > kOldMaxType * Key::kMaxArrayCollapse)) {
 2923     IDB_REPORT_INTERNAL_ERR();
 2924     return NS_ERROR_FILE_CORRUPTED;
 2925   }
 2926 
 2927   if (sourceTag == kOldNumberTag || sourceTag == kOldDateTag) {
 2928     // Write the new tag.
 2929     *aDestination++ =
 2930       (sourceTag == kOldNumberTag ? Key::eFloat : Key::eDate) +
 2931       (aTagOffset * Key::eMaxType);
 2932     aSource++;
 2933 
 2934     // Numbers and Dates are encoded as 64-bit integers, but trailing 0
 2935     // bytes have been removed.
 2936     const uint32_t byteCount =
 2937       AdjustedSize(sizeof(uint64_t), aSource, aSourceEnd);
 2938 
 2939     for (uint32_t count = 0; count < byteCount; count++) {
 2940       *aDestination++ = *aSource++;
 2941     }
 2942 
 2943     return NS_OK;
 2944   }
 2945 
 2946   if (sourceTag == kOldStringTag) {
 2947     // Write the new tag.
 2948     *aDestination++ = Key::eString + (aTagOffset * Key::eMaxType);
 2949     aSource++;
 2950 
 2951     while (aSource < aSourceEnd) {
 2952       const uint8_t byte = *aSource++;
 2953       *aDestination++ = byte;
 2954 
 2955       if (!byte) {
 2956         // Just copied the terminator.
 2957         break;
 2958       }
 2959 
 2960       // Maybe copy one or two extra bytes if the byte is tagged and we have
 2961       // enough source space.
 2962       if (byte & 0x80) {
 2963         const uint32_t byteCount =
 2964           AdjustedSize((byte & 0x40) ? 2 : 1, aSource, aSourceEnd);
 2965 
 2966         for (uint32_t count = 0; count < byteCount; count++) {
 2967           *aDestination++ = *aSource++;
 2968         }
 2969       }
 2970     }
 2971 
 2972     return NS_OK;
 2973   }
 2974 
 2975   if (NS_WARN_IF(sourceTag < kOldArrayTag)) {
 2976     IDB_REPORT_INTERNAL_ERR();
 2977     return NS_ERROR_FILE_CORRUPTED;
 2978   }
 2979 
 2980   aTagOffset++;
 2981 
 2982   if (aTagOffset == Key::kMaxArrayCollapse) {
 2983     MOZ_ASSERT(sourceTag == kOldArrayTag);
 2984 
 2985     *aDestination++ = (aTagOffset * Key::eMaxType);
 2986     aSource++;
 2987 
 2988     aTagOffset = 0;
 2989   }
 2990 
 2991   while (aSource < aSourceEnd &&
 2992          (*aSource - (aTagOffset * kOldMaxType)) != Key::eTerminator) {
 2993     nsresult rv = CopyAndUpgradeKeyBufferInternal(aSource,
 2994                                                   aSourceEnd,
 2995                                                   aDestination,
 2996                                                   aTagOffset,
 2997                                                   aRecursionDepth + 1);
 2998     if (NS_WARN_IF(NS_FAILED(rv))) {
 2999       return rv;
 3000     }
 3001 
 3002     aTagOffset = 0;
 3003   }
 3004 
 3005   if (aSource < aSourceEnd) {
 3006     MOZ_ASSERT((*aSource - (aTagOffset * kOldMaxType)) == Key::eTerminator);
 3007     *aDestination++ = Key::eTerminator + (aTagOffset * Key::eMaxType);
 3008     aSource++;
 3009   }
 3010 
 3011   return NS_OK;
 3012 }
 3013 
 3014 NS_IMPL_ISUPPORTS(UpgradeSchemaFrom17_0To18_0Helper::UpgradeKeyFunction,
 3015                   mozIStorageFunction);
 3016 
 3017 NS_IMETHODIMP
 3018 UpgradeSchemaFrom17_0To18_0Helper::
 3019 UpgradeKeyFunction::OnFunctionCall(mozIStorageValueArray* aValues,
 3020                                    nsIVariant** _retval)
 3021 {
 3022   MOZ_ASSERT(!NS_IsMainThread());
 3023   MOZ_ASSERT(!IsOnBackgroundThread());
 3024   MOZ_ASSERT(aValues);
 3025   MOZ_ASSERT(_retval);
 3026 
 3027 #ifdef DEBUG
 3028   {
 3029     uint32_t argCount;
 3030     MOZ_ALWAYS_SUCCEEDS(aValues->GetNumEntries(&argCount));
 3031     MOZ_ASSERT(argCount == 1);
 3032 
 3033     int32_t valueType;
 3034     MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(0, &valueType));
 3035     MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_BLOB);
 3036   }
 3037 #endif
 3038 
 3039   // Dig the old key out of the values.
 3040   const uint8_t* blobData;
 3041   uint32_t blobDataLength;
 3042   nsresult rv = aValues->GetSharedBlob(0, &blobDataLength, &blobData);
 3043   if (NS_WARN_IF(NS_FAILED(rv))) {
 3044     return rv;
 3045   }
 3046 
 3047   // Upgrading the key doesn't change the amount of space needed to hold it.
 3048   UniqueFreePtr<uint8_t> upgradedBlobData(
 3049     static_cast<uint8_t*>(malloc(blobDataLength)));
 3050   if (NS_WARN_IF(!upgradedBlobData)) {
 3051     IDB_REPORT_INTERNAL_ERR();
 3052     return NS_ERROR_OUT_OF_MEMORY;
 3053   }
 3054 
 3055   rv = CopyAndUpgradeKeyBuffer(blobData,
 3056                                blobData + blobDataLength,
 3057                                upgradedBlobData.get());
 3058   if (NS_WARN_IF(NS_FAILED(rv))) {
 3059     return rv;
 3060   }
 3061 
 3062   // The upgraded key is the result of this function.
 3063   std::pair<uint8_t*, int> data(upgradedBlobData.release(),
 3064                                 int(blobDataLength));
 3065 
 3066   nsCOMPtr<nsIVariant> result = new mozilla::storage::AdoptedBlobVariant(data);
 3067 
 3068   result.forget(_retval);
 3069   return NS_OK;
 3070 }
 3071 
 3072 // static
 3073 nsresult
 3074 UpgradeSchemaFrom17_0To18_0Helper::DoUpgrade(mozIStorageConnection* aConnection,
 3075                                              const nsACString& aOrigin)
 3076 {
 3077   MOZ_ASSERT(aConnection);
 3078   MOZ_ASSERT(!aOrigin.IsEmpty());
 3079 
 3080   // Register the |upgrade_key| function.
 3081   RefPtr<UpgradeKeyFunction> updateFunction = new UpgradeKeyFunction();
 3082 
 3083   NS_NAMED_LITERAL_CSTRING(upgradeKeyFunctionName, "upgrade_key");
 3084 
 3085   nsresult rv =
 3086     aConnection->CreateFunction(upgradeKeyFunctionName, 1, updateFunction);
 3087   if (NS_WARN_IF(NS_FAILED(rv))) {
 3088     return rv;
 3089   }
 3090 
 3091   // Register the |insert_idv| function.
 3092   RefPtr<InsertIndexDataValuesFunction> insertIDVFunction =
 3093     new InsertIndexDataValuesFunction();
 3094 
 3095   NS_NAMED_LITERAL_CSTRING(insertIDVFunctionName, "insert_idv");
 3096 
 3097   rv = aConnection->CreateFunction(insertIDVFunctionName, 4, insertIDVFunction);
 3098   if (NS_WARN_IF(NS_FAILED(rv))) {
 3099     MOZ_ALWAYS_SUCCEEDS(aConnection->RemoveFunction(upgradeKeyFunctionName));
 3100     return rv;
 3101   }
 3102 
 3103   rv = DoUpgradeInternal(aConnection, aOrigin);
 3104 
 3105   MOZ_ALWAYS_SUCCEEDS(aConnection->RemoveFunction(upgradeKeyFunctionName));
 3106   MOZ_ALWAYS_SUCCEEDS(aConnection->RemoveFunction(insertIDVFunctionName));
 3107 
 3108   if (NS_WARN_IF(NS_FAILED(rv))) {
 3109     return rv;
 3110   }
 3111 
 3112   return NS_OK;
 3113 }
 3114 
 3115 // static
 3116 nsresult
 3117 UpgradeSchemaFrom17_0To18_0Helper::DoUpgradeInternal(
 3118                                              mozIStorageConnection* aConnection,
 3119                                              const nsACString& aOrigin)
 3120 {
 3121   MOZ_ASSERT(aConnection);
 3122   MOZ_ASSERT(!aOrigin.IsEmpty());
 3123 
 3124   nsresult rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3125     // Drop these triggers to avoid unnecessary work during the upgrade process.
 3126     "DROP TRIGGER object_data_insert_trigger;"
 3127     "DROP TRIGGER object_data_update_trigger;"
 3128     "DROP TRIGGER object_data_delete_trigger;"
 3129   ));
 3130   if (NS_WARN_IF(NS_FAILED(rv))) {
 3131     return rv;
 3132   }
 3133 
 3134   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3135     // Drop these indexes before we do anything else to free disk space.
 3136     "DROP INDEX index_data_object_data_id_index;"
 3137     "DROP INDEX unique_index_data_object_data_id_index;"
 3138   ));
 3139   if (NS_WARN_IF(NS_FAILED(rv))) {
 3140     return rv;
 3141   }
 3142 
 3143   // Create the new tables and triggers first.
 3144 
 3145   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3146     // This will eventually become the |database| table.
 3147     "CREATE TABLE database_upgrade "
 3148       "( name TEXT PRIMARY KEY"
 3149       ", origin TEXT NOT NULL"
 3150       ", version INTEGER NOT NULL DEFAULT 0"
 3151       ", last_vacuum_time INTEGER NOT NULL DEFAULT 0"
 3152       ", last_analyze_time INTEGER NOT NULL DEFAULT 0"
 3153       ", last_vacuum_size INTEGER NOT NULL DEFAULT 0"
 3154       ") WITHOUT ROWID;"
 3155   ));
 3156   if (NS_WARN_IF(NS_FAILED(rv))) {
 3157     return rv;
 3158   }
 3159 
 3160   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3161      // This will eventually become the |object_store| table.
 3162     "CREATE TABLE object_store_upgrade"
 3163       "( id INTEGER PRIMARY KEY"
 3164       ", auto_increment INTEGER NOT NULL DEFAULT 0"
 3165       ", name TEXT NOT NULL"
 3166       ", key_path TEXT"
 3167       ");"
 3168   ));
 3169   if (NS_WARN_IF(NS_FAILED(rv))) {
 3170     return rv;
 3171   }
 3172 
 3173   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3174     // This will eventually become the |object_store_index| table.
 3175     "CREATE TABLE object_store_index_upgrade"
 3176       "( id INTEGER PRIMARY KEY"
 3177       ", object_store_id INTEGER NOT NULL"
 3178       ", name TEXT NOT NULL"
 3179       ", key_path TEXT NOT NULL"
 3180       ", unique_index INTEGER NOT NULL"
 3181       ", multientry INTEGER NOT NULL"
 3182       ", FOREIGN KEY (object_store_id) "
 3183           "REFERENCES object_store(id) "
 3184       ");"
 3185   ));
 3186   if (NS_WARN_IF(NS_FAILED(rv))) {
 3187     return rv;
 3188   }
 3189 
 3190   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3191     // This will eventually become the |object_data| table.
 3192     "CREATE TABLE object_data_upgrade"
 3193       "( object_store_id INTEGER NOT NULL"
 3194       ", key BLOB NOT NULL"
 3195       ", index_data_values BLOB DEFAULT NULL"
 3196       ", file_ids TEXT"
 3197       ", data BLOB NOT NULL"
 3198       ", PRIMARY KEY (object_store_id, key)"
 3199       ", FOREIGN KEY (object_store_id) "
 3200           "REFERENCES object_store(id) "
 3201       ") WITHOUT ROWID;"
 3202   ));
 3203   if (NS_WARN_IF(NS_FAILED(rv))) {
 3204     return rv;
 3205   }
 3206 
 3207   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3208     // This will eventually become the |index_data| table.
 3209     "CREATE TABLE index_data_upgrade"
 3210       "( index_id INTEGER NOT NULL"
 3211       ", value BLOB NOT NULL"
 3212       ", object_data_key BLOB NOT NULL"
 3213       ", object_store_id INTEGER NOT NULL"
 3214       ", PRIMARY KEY (index_id, value, object_data_key)"
 3215       ", FOREIGN KEY (index_id) "
 3216           "REFERENCES object_store_index(id) "
 3217       ", FOREIGN KEY (object_store_id, object_data_key) "
 3218           "REFERENCES object_data(object_store_id, key) "
 3219       ") WITHOUT ROWID;"
 3220   ));
 3221   if (NS_WARN_IF(NS_FAILED(rv))) {
 3222     return rv;
 3223   }
 3224 
 3225   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3226     // This will eventually become the |unique_index_data| table.
 3227     "CREATE TABLE unique_index_data_upgrade"
 3228       "( index_id INTEGER NOT NULL"
 3229       ", value BLOB NOT NULL"
 3230       ", object_store_id INTEGER NOT NULL"
 3231       ", object_data_key BLOB NOT NULL"
 3232       ", PRIMARY KEY (index_id, value)"
 3233       ", FOREIGN KEY (index_id) "
 3234           "REFERENCES object_store_index(id) "
 3235       ", FOREIGN KEY (object_store_id, object_data_key) "
 3236           "REFERENCES object_data(object_store_id, key) "
 3237       ") WITHOUT ROWID;"
 3238   ));
 3239   if (NS_WARN_IF(NS_FAILED(rv))) {
 3240     return rv;
 3241   }
 3242 
 3243   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3244     // Temporarily store |index_data_values| that we build during the upgrade of
 3245     // the index tables. We will later move this to the |object_data| table.
 3246     "CREATE TEMPORARY TABLE temp_index_data_values "
 3247       "( object_store_id INTEGER NOT NULL"
 3248       ", key BLOB NOT NULL"
 3249       ", index_data_values BLOB DEFAULT NULL"
 3250       ", PRIMARY KEY (object_store_id, key)"
 3251       ") WITHOUT ROWID;"
 3252   ));
 3253   if (NS_WARN_IF(NS_FAILED(rv))) {
 3254     return rv;
 3255   }
 3256 
 3257   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3258     // These two triggers help build the |index_data_values| blobs. The nested
 3259     // SELECT statements help us achieve an "INSERT OR UPDATE"-like behavior.
 3260     "CREATE TEMPORARY TRIGGER unique_index_data_upgrade_insert_trigger "
 3261       "AFTER INSERT ON unique_index_data_upgrade "
 3262       "BEGIN "
 3263         "INSERT OR REPLACE INTO temp_index_data_values "
 3264           "VALUES "
 3265           "( NEW.object_store_id"
 3266           ", NEW.object_data_key"
 3267           ", insert_idv("
 3268               "( SELECT index_data_values "
 3269                   "FROM temp_index_data_values "
 3270                   "WHERE object_store_id = NEW.object_store_id "
 3271                   "AND key = NEW.object_data_key "
 3272               "), NEW.index_id"
 3273                ", 1" /* unique */
 3274                ", NEW.value"
 3275             ")"
 3276           ");"
 3277       "END;"
 3278   ));
 3279   if (NS_WARN_IF(NS_FAILED(rv))) {
 3280     return rv;
 3281   }
 3282 
 3283   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3284     "CREATE TEMPORARY TRIGGER index_data_upgrade_insert_trigger "
 3285       "AFTER INSERT ON index_data_upgrade "
 3286       "BEGIN "
 3287         "INSERT OR REPLACE INTO temp_index_data_values "
 3288           "VALUES "
 3289           "( NEW.object_store_id"
 3290           ", NEW.object_data_key"
 3291           ", insert_idv("
 3292               "("
 3293                 "SELECT index_data_values "
 3294                   "FROM temp_index_data_values "
 3295                   "WHERE object_store_id = NEW.object_store_id "
 3296                   "AND key = NEW.object_data_key "
 3297               "), NEW.index_id"
 3298                ", 0" /* not unique */
 3299                ", NEW.value"
 3300             ")"
 3301           ");"
 3302       "END;"
 3303   ));
 3304   if (NS_WARN_IF(NS_FAILED(rv))) {
 3305     return rv;
 3306   }
 3307 
 3308   // Update the |unique_index_data| table to change the column order, remove the
 3309   // ON DELETE CASCADE clauses, and to apply the WITHOUT ROWID optimization.
 3310   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3311     // Insert all the data.
 3312     "INSERT INTO unique_index_data_upgrade "
 3313       "SELECT "
 3314         "unique_index_data.index_id, "
 3315         "upgrade_key(unique_index_data.value), "
 3316         "object_data.object_store_id, "
 3317         "upgrade_key(unique_index_data.object_data_key) "
 3318         "FROM unique_index_data "
 3319         "JOIN object_data "
 3320         "ON unique_index_data.object_data_id = object_data.id;"
 3321   ));
 3322   if (NS_WARN_IF(NS_FAILED(rv))) {
 3323     return rv;
 3324   }
 3325 
 3326   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3327     // The trigger is no longer needed.
 3328     "DROP TRIGGER unique_index_data_upgrade_insert_trigger;"
 3329   ));
 3330   if (NS_WARN_IF(NS_FAILED(rv))) {
 3331     return rv;
 3332   }
 3333 
 3334   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3335     // The old table is no longer needed.
 3336     "DROP TABLE unique_index_data;"
 3337   ));
 3338   if (NS_WARN_IF(NS_FAILED(rv))) {
 3339     return rv;
 3340   }
 3341 
 3342   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3343     // Rename the table.
 3344     "ALTER TABLE unique_index_data_upgrade "
 3345       "RENAME TO unique_index_data;"
 3346   ));
 3347   if (NS_WARN_IF(NS_FAILED(rv))) {
 3348     return rv;
 3349   }
 3350 
 3351   // Update the |index_data| table to change the column order, remove the ON
 3352   // DELETE CASCADE clauses, and to apply the WITHOUT ROWID optimization.
 3353   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3354     // Insert all the data.
 3355     "INSERT INTO index_data_upgrade "
 3356       "SELECT "
 3357         "index_data.index_id, "
 3358         "upgrade_key(index_data.value), "
 3359         "upgrade_key(index_data.object_data_key), "
 3360         "object_data.object_store_id "
 3361         "FROM index_data "
 3362         "JOIN object_data "
 3363         "ON index_data.object_data_id = object_data.id;"
 3364   ));
 3365   if (NS_WARN_IF(NS_FAILED(rv))) {
 3366     return rv;
 3367   }
 3368 
 3369   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3370     // The trigger is no longer needed.
 3371     "DROP TRIGGER index_data_upgrade_insert_trigger;"
 3372   ));
 3373   if (NS_WARN_IF(NS_FAILED(rv))) {
 3374     return rv;
 3375   }
 3376 
 3377   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3378     // The old table is no longer needed.
 3379     "DROP TABLE index_data;"
 3380   ));
 3381   if (NS_WARN_IF(NS_FAILED(rv))) {
 3382     return rv;
 3383   }
 3384 
 3385   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3386     // Rename the table.
 3387     "ALTER TABLE index_data_upgrade "
 3388       "RENAME TO index_data;"
 3389   ));
 3390   if (NS_WARN_IF(NS_FAILED(rv))) {
 3391     return rv;
 3392   }
 3393 
 3394   // Update the |object_data| table to add the |index_data_values| column,
 3395   // remove the ON DELETE CASCADE clause, and apply the WITHOUT ROWID
 3396   // optimization.
 3397   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3398     // Insert all the data.
 3399     "INSERT INTO object_data_upgrade "
 3400       "SELECT "
 3401         "object_data.object_store_id, "
 3402         "upgrade_key(object_data.key_value), "
 3403         "temp_index_data_values.index_data_values, "
 3404         "object_data.file_ids, "
 3405         "object_data.data "
 3406         "FROM object_data "
 3407         "LEFT JOIN temp_index_data_values "
 3408         "ON object_data.object_store_id = "
 3409           "temp_index_data_values.object_store_id "
 3410         "AND upgrade_key(object_data.key_value) = "
 3411           "temp_index_data_values.key;"
 3412   ));
 3413   if (NS_WARN_IF(NS_FAILED(rv))) {
 3414     return rv;
 3415   }
 3416 
 3417   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3418     // The temporary table is no longer needed.
 3419     "DROP TABLE temp_index_data_values;"
 3420   ));
 3421   if (NS_WARN_IF(NS_FAILED(rv))) {
 3422     return rv;
 3423   }
 3424 
 3425   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3426     // The old table is no longer needed.
 3427     "DROP TABLE object_data;"
 3428   ));
 3429   if (NS_WARN_IF(NS_FAILED(rv))) {
 3430     return rv;
 3431   }
 3432 
 3433   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3434     // Rename the table.
 3435     "ALTER TABLE object_data_upgrade "
 3436       "RENAME TO object_data;"
 3437   ));
 3438   if (NS_WARN_IF(NS_FAILED(rv))) {
 3439     return rv;
 3440   }
 3441 
 3442   // Update the |object_store_index| table to remove the UNIQUE constraint and
 3443   // the ON DELETE CASCADE clause.
 3444   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3445     "INSERT INTO object_store_index_upgrade "
 3446       "SELECT * "
 3447         "FROM object_store_index;"
 3448   ));
 3449   if (NS_WARN_IF(NS_FAILED(rv))) {
 3450     return rv;
 3451   }
 3452 
 3453   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3454     "DROP TABLE object_store_index;"
 3455   ));
 3456   if (NS_WARN_IF(NS_FAILED(rv))) {
 3457     return rv;
 3458   }
 3459 
 3460   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3461     "ALTER TABLE object_store_index_upgrade "
 3462       "RENAME TO object_store_index;"
 3463   ));
 3464   if (NS_WARN_IF(NS_FAILED(rv))) {
 3465     return rv;
 3466   }
 3467 
 3468   // Update the |object_store| table to remove the UNIQUE constraint.
 3469   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3470     "INSERT INTO object_store_upgrade "
 3471       "SELECT * "
 3472         "FROM object_store;"
 3473   ));
 3474   if (NS_WARN_IF(NS_FAILED(rv))) {
 3475     return rv;
 3476   }
 3477 
 3478   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3479     "DROP TABLE object_store;"
 3480   ));
 3481   if (NS_WARN_IF(NS_FAILED(rv))) {
 3482     return rv;
 3483   }
 3484 
 3485   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3486     "ALTER TABLE object_store_upgrade "
 3487       "RENAME TO object_store;"
 3488   ));
 3489   if (NS_WARN_IF(NS_FAILED(rv))) {
 3490     return rv;
 3491   }
 3492 
 3493   // Update the |database| table to include the origin, vacuum information, and
 3494   // apply the WITHOUT ROWID optimization.
 3495   nsCOMPtr<mozIStorageStatement> stmt;
 3496   rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
 3497     "INSERT INTO database_upgrade "
 3498       "SELECT name, :origin, version, 0, 0, 0 "
 3499         "FROM database;"
 3500   ), getter_AddRefs(stmt));
 3501   if (NS_WARN_IF(NS_FAILED(rv))) {
 3502     return rv;
 3503   }
 3504 
 3505   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("origin"), aOrigin);
 3506   if (NS_WARN_IF(NS_FAILED(rv))) {
 3507     return rv;
 3508   }
 3509 
 3510   rv = stmt->Execute();
 3511   if (NS_WARN_IF(NS_FAILED(rv))) {
 3512     return rv;
 3513   }
 3514 
 3515   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3516     "DROP TABLE database;"
 3517   ));
 3518   if (NS_WARN_IF(NS_FAILED(rv))) {
 3519     return rv;
 3520   }
 3521 
 3522   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3523     "ALTER TABLE database_upgrade "
 3524       "RENAME TO database;"
 3525   ));
 3526   if (NS_WARN_IF(NS_FAILED(rv))) {
 3527     return rv;
 3528   }
 3529 
 3530 #ifdef DEBUG
 3531   {
 3532     // Make sure there's only one entry in the |database| table.
 3533     nsCOMPtr<mozIStorageStatement> stmt;
 3534     MOZ_ASSERT(NS_SUCCEEDED(
 3535       aConnection->CreateStatement(
 3536         NS_LITERAL_CSTRING("SELECT COUNT(*) "
 3537                              "FROM database;"),
 3538         getter_AddRefs(stmt))));
 3539 
 3540     bool hasResult;
 3541     MOZ_ASSERT(NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)));
 3542 
 3543     int64_t count;
 3544     MOZ_ASSERT(NS_SUCCEEDED(stmt->GetInt64(0, &count)));
 3545 
 3546     MOZ_ASSERT(count == 1);
 3547   }
 3548 #endif
 3549 
 3550   // Recreate file table triggers.
 3551   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3552     "CREATE TRIGGER object_data_insert_trigger "
 3553       "AFTER INSERT ON object_data "
 3554       "WHEN NEW.file_ids IS NOT NULL "
 3555       "BEGIN "
 3556         "SELECT update_refcount(NULL, NEW.file_ids);"
 3557       "END;"
 3558   ));
 3559   if (NS_WARN_IF(NS_FAILED(rv))) {
 3560     return rv;
 3561   }
 3562 
 3563   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3564     "CREATE TRIGGER object_data_update_trigger "
 3565       "AFTER UPDATE OF file_ids ON object_data "
 3566       "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL "
 3567       "BEGIN "
 3568         "SELECT update_refcount(OLD.file_ids, NEW.file_ids);"
 3569       "END;"
 3570   ));
 3571   if (NS_WARN_IF(NS_FAILED(rv))) {
 3572     return rv;
 3573   }
 3574 
 3575   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3576     "CREATE TRIGGER object_data_delete_trigger "
 3577       "AFTER DELETE ON object_data "
 3578       "WHEN OLD.file_ids IS NOT NULL "
 3579       "BEGIN "
 3580         "SELECT update_refcount(OLD.file_ids, NULL);"
 3581       "END;"
 3582   ));
 3583   if (NS_WARN_IF(NS_FAILED(rv))) {
 3584     return rv;
 3585   }
 3586 
 3587   // Finally, turn on auto_vacuum mode. We use full auto_vacuum mode to reclaim
 3588   // disk space on mobile devices (at the cost of some COMMIT speed), and
 3589   // incremental auto_vacuum mode on desktop builds.
 3590   rv = aConnection->ExecuteSimpleSQL(
 3591 #ifdef IDB_MOBILE
 3592     NS_LITERAL_CSTRING("PRAGMA auto_vacuum = FULL;")
 3593 #else
 3594     NS_LITERAL_CSTRING("PRAGMA auto_vacuum = INCREMENTAL;")
 3595 #endif
 3596   );
 3597   if (NS_WARN_IF(NS_FAILED(rv))) {
 3598     return rv;
 3599   }
 3600 
 3601   rv = aConnection->SetSchemaVersion(MakeSchemaVersion(18, 0));
 3602   if (NS_WARN_IF(NS_FAILED(rv))) {
 3603     return rv;
 3604   }
 3605 
 3606   return NS_OK;
 3607 }
 3608 
 3609 nsresult
 3610 UpgradeSchemaFrom17_0To18_0(mozIStorageConnection* aConnection,
 3611                             const nsACString& aOrigin)
 3612 {
 3613   MOZ_ASSERT(aConnection);
 3614   MOZ_ASSERT(!aOrigin.IsEmpty());
 3615 
 3616   PROFILER_LABEL("IndexedDB",
 3617                  "UpgradeSchemaFrom17_0To18_0",
 3618                  js::ProfileEntry::Category::STORAGE);
 3619 
 3620   return UpgradeSchemaFrom17_0To18_0Helper::DoUpgrade(aConnection, aOrigin);
 3621 }
 3622 
 3623 nsresult
 3624 UpgradeSchemaFrom18_0To19_0(mozIStorageConnection* aConnection)
 3625 {
 3626   AssertIsOnIOThread();
 3627   MOZ_ASSERT(aConnection);
 3628 
 3629   nsresult rv;
 3630   PROFILER_LABEL("IndexedDB",
 3631                  "UpgradeSchemaFrom18_0To19_0",
 3632                  js::ProfileEntry::Category::STORAGE);
 3633 
 3634   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3635     "ALTER TABLE object_store_index "
 3636     "ADD COLUMN locale TEXT;"
 3637   ));
 3638   if (NS_WARN_IF(NS_FAILED(rv))) {
 3639     return rv;
 3640   }
 3641 
 3642   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3643     "ALTER TABLE object_store_index "
 3644     "ADD COLUMN is_auto_locale BOOLEAN;"
 3645   ));
 3646   if (NS_WARN_IF(NS_FAILED(rv))) {
 3647     return rv;
 3648   }
 3649 
 3650   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3651     "ALTER TABLE index_data "
 3652     "ADD COLUMN value_locale BLOB;"
 3653   ));
 3654   if (NS_WARN_IF(NS_FAILED(rv))) {
 3655     return rv;
 3656   }
 3657 
 3658   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3659     "ALTER TABLE unique_index_data "
 3660     "ADD COLUMN value_locale BLOB;"
 3661   ));
 3662   if (NS_WARN_IF(NS_FAILED(rv))) {
 3663     return rv;
 3664   }
 3665 
 3666   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3667     "CREATE INDEX index_data_value_locale_index "
 3668     "ON index_data (index_id, value_locale, object_data_key, value) "
 3669     "WHERE value_locale IS NOT NULL;"
 3670   ));
 3671   if (NS_WARN_IF(NS_FAILED(rv))) {
 3672     return rv;
 3673   }
 3674 
 3675   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3676     "CREATE INDEX unique_index_data_value_locale_index "
 3677     "ON unique_index_data (index_id, value_locale, object_data_key, value) "
 3678     "WHERE value_locale IS NOT NULL;"
 3679   ));
 3680   if (NS_WARN_IF(NS_FAILED(rv))) {
 3681     return rv;
 3682   }
 3683 
 3684   rv = aConnection->SetSchemaVersion(MakeSchemaVersion(19, 0));
 3685   if (NS_WARN_IF(NS_FAILED(rv))) {
 3686     return rv;
 3687   }
 3688 
 3689   return NS_OK;
 3690 }
 3691 
 3692 class NormalJSContext;
 3693 
 3694 class UpgradeFileIdsFunction final
 3695   : public mozIStorageFunction
 3696 {
 3697   RefPtr<FileManager> mFileManager;
 3698   nsAutoPtr<NormalJSContext> mContext;
 3699 
 3700 public:
 3701   UpgradeFileIdsFunction()
 3702   {
 3703     AssertIsOnIOThread();
 3704   }
 3705 
 3706   nsresult
 3707   Init(nsIFile* aFMDirectory,
 3708        mozIStorageConnection* aConnection);
 3709 
 3710   NS_DECL_ISUPPORTS
 3711 
 3712 private:
 3713   ~UpgradeFileIdsFunction()
 3714   {
 3715     AssertIsOnIOThread();
 3716 
 3717     if (mFileManager) {
 3718       mFileManager->Invalidate();
 3719     }
 3720   }
 3721 
 3722   NS_IMETHOD
 3723   OnFunctionCall(mozIStorageValueArray* aArguments,
 3724                  nsIVariant** aResult) override;
 3725 };
 3726 
 3727 nsresult
 3728 UpgradeSchemaFrom19_0To20_0(nsIFile* aFMDirectory,
 3729                             mozIStorageConnection* aConnection)
 3730 {
 3731   AssertIsOnIOThread();
 3732   MOZ_ASSERT(aConnection);
 3733 
 3734   PROFILER_LABEL("IndexedDB",
 3735                  "UpgradeSchemaFrom19_0To20_0",
 3736                  js::ProfileEntry::Category::STORAGE);
 3737 
 3738   nsCOMPtr<mozIStorageStatement> stmt;
 3739   nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
 3740     "SELECT count(*) "
 3741     "FROM object_data "
 3742     "WHERE file_ids IS NOT NULL"
 3743   ), getter_AddRefs(stmt));
 3744   if (NS_WARN_IF(NS_FAILED(rv))) {
 3745     return rv;
 3746   }
 3747 
 3748   int64_t count;
 3749 
 3750   {
 3751     mozStorageStatementScoper scoper(stmt);
 3752 
 3753     bool hasResult;
 3754     rv = stmt->ExecuteStep(&hasResult);
 3755     if (NS_WARN_IF(NS_FAILED(rv))) {
 3756       return rv;
 3757     }
 3758 
 3759     if (NS_WARN_IF(!hasResult)) {
 3760       MOZ_ASSERT(false, "This should never be possible!");
 3761       return NS_ERROR_FAILURE;
 3762     }
 3763 
 3764     count = stmt->AsInt64(0);
 3765     if (NS_WARN_IF(count < 0)) {
 3766       MOZ_ASSERT(false, "This should never be possible!");
 3767       return NS_ERROR_FAILURE;
 3768     }
 3769   }
 3770 
 3771   if (count == 0) {
 3772     // Nothing to upgrade.
 3773     rv = aConnection->SetSchemaVersion(MakeSchemaVersion(20, 0));
 3774     if (NS_WARN_IF(NS_FAILED(rv))) {
 3775       return rv;
 3776     }
 3777 
 3778     return NS_OK;
 3779   }
 3780 
 3781   RefPtr<UpgradeFileIdsFunction> function = new UpgradeFileIdsFunction();
 3782 
 3783   rv = function->Init(aFMDirectory, aConnection);
 3784   if (NS_WARN_IF(NS_FAILED(rv))) {
 3785     return rv;
 3786   }
 3787 
 3788   NS_NAMED_LITERAL_CSTRING(functionName, "upgrade");
 3789 
 3790   rv = aConnection->CreateFunction(functionName, 2, function);
 3791   if (NS_WARN_IF(NS_FAILED(rv))) {
 3792     return rv;
 3793   }
 3794 
 3795   // Disable update trigger.
 3796   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3797     "DROP TRIGGER object_data_update_trigger;"
 3798   ));
 3799   if (NS_WARN_IF(NS_FAILED(rv))) {
 3800     return rv;
 3801   }
 3802 
 3803   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3804     "UPDATE object_data "
 3805       "SET file_ids = upgrade(file_ids, data) "
 3806       "WHERE file_ids IS NOT NULL;"
 3807   ));
 3808   if (NS_WARN_IF(NS_FAILED(rv))) {
 3809     return rv;
 3810   }
 3811 
 3812   // Enable update trigger.
 3813   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 3814     "CREATE TRIGGER object_data_update_trigger "
 3815     "AFTER UPDATE OF file_ids ON object_data "
 3816     "FOR EACH ROW "
 3817     "WHEN OLD.file_ids IS NOT NULL OR NEW.file_ids IS NOT NULL "
 3818     "BEGIN "
 3819       "SELECT update_refcount(OLD.file_ids, NEW.file_ids); "
 3820     "END;"
 3821   ));
 3822   if (NS_WARN_IF(NS_FAILED(rv))) {
 3823     return rv;
 3824   }
 3825 
 3826   rv = aConnection->RemoveFunction(functionName);
 3827   if (NS_WARN_IF(NS_FAILED(rv))) {
 3828     return rv;
 3829   }
 3830 
 3831   rv = aConnection->SetSchemaVersion(MakeSchemaVersion(20, 0));
 3832   if (NS_WARN_IF(NS_FAILED(rv))) {
 3833     return rv;
 3834   }
 3835 
 3836   return NS_OK;
 3837 }
 3838 
 3839 class UpgradeIndexDataValuesFunction final
 3840   : public mozIStorageFunction
 3841 {
 3842 public:
 3843   UpgradeIndexDataValuesFunction()
 3844   {
 3845     AssertIsOnIOThread();
 3846   }
 3847 
 3848   NS_DECL_ISUPPORTS
 3849 
 3850 private:
 3851   ~UpgradeIndexDataValuesFunction()
 3852   {
 3853     AssertIsOnIOThread();
 3854   }
 3855 
 3856   nsresult
 3857   ReadOldCompressedIDVFromBlob(const uint8_t* aBlobData,
 3858                                uint32_t aBlobDataLength,
 3859                                nsTArray<IndexDataValue>& aIndexValues);
 3860 
 3861   NS_IMETHOD
 3862   OnFunctionCall(mozIStorageValueArray* aArguments,
 3863                  nsIVariant** aResult) override;
 3864 };
 3865 
 3866 NS_IMPL_ISUPPORTS(UpgradeIndexDataValuesFunction, mozIStorageFunction)
 3867 
 3868 nsresult
 3869 UpgradeIndexDataValuesFunction::ReadOldCompressedIDVFromBlob(
 3870                                    const uint8_t* aBlobData,
 3871                                    uint32_t aBlobDataLength,
 3872                                    nsTArray<IndexDataValue>& aIndexValues)
 3873 {
 3874   MOZ_ASSERT(!NS_IsMainThread());
 3875   MOZ_ASSERT(!IsOnBackgroundThread());
 3876   MOZ_ASSERT(aBlobData);
 3877   MOZ_ASSERT(aBlobDataLength);
 3878   MOZ_ASSERT(aIndexValues.IsEmpty());
 3879 
 3880   const uint8_t* blobDataIter = aBlobData;
 3881   const uint8_t* blobDataEnd = aBlobData + aBlobDataLength;
 3882 
 3883   int64_t indexId;
 3884   bool unique;
 3885   bool nextIndexIdAlreadyRead = false;
 3886 
 3887   while (blobDataIter < blobDataEnd) {
 3888     if (!nextIndexIdAlreadyRead) {
 3889       ReadCompressedIndexId(&blobDataIter, blobDataEnd, &indexId, &unique);
 3890     }
 3891     nextIndexIdAlreadyRead = false;
 3892 
 3893     if (NS_WARN_IF(blobDataIter == blobDataEnd)) {
 3894       IDB_REPORT_INTERNAL_ERR();
 3895       return NS_ERROR_FILE_CORRUPTED;
 3896     }
 3897 
 3898     // Read key buffer length.
 3899     const uint64_t keyBufferLength =
 3900       ReadCompressedNumber(&blobDataIter, blobDataEnd);
 3901 
 3902     if (NS_WARN_IF(blobDataIter == blobDataEnd) ||
 3903         NS_WARN_IF(keyBufferLength > uint64_t(UINT32_MAX)) ||
 3904         NS_WARN_IF(blobDataIter + keyBufferLength > blobDataEnd)) {
 3905       IDB_REPORT_INTERNAL_ERR();
 3906       return NS_ERROR_FILE_CORRUPTED;
 3907     }
 3908 
 3909     nsCString keyBuffer(reinterpret_cast<const char*>(blobDataIter),
 3910                         uint32_t(keyBufferLength));
 3911     blobDataIter += keyBufferLength;
 3912 
 3913     IndexDataValue idv(indexId, unique, Key(keyBuffer));
 3914 
 3915     if (blobDataIter < blobDataEnd) {
 3916       // Read either a sort key buffer length or an index id.
 3917       uint64_t maybeIndexId = ReadCompressedNumber(&blobDataIter, blobDataEnd);
 3918 
 3919       // Locale-aware indexes haven't been around long enough to have any users,
 3920       // we can safely assume all sort key buffer lengths will be zero.
 3921       if (maybeIndexId != 0) {
 3922         if (maybeIndexId % 2) {
 3923           unique = true;
 3924           maybeIndexId--;
 3925         } else {
 3926           unique = false;
 3927         }
 3928         indexId = maybeIndexId/2;
 3929         nextIndexIdAlreadyRead = true;
 3930       }
 3931     }
 3932 
 3933     if (NS_WARN_IF(!aIndexValues.InsertElementSorted(idv, fallible))) {
 3934       IDB_REPORT_INTERNAL_ERR();
 3935       return NS_ERROR_OUT_OF_MEMORY;
 3936     }
 3937   }
 3938 
 3939   MOZ_ASSERT(blobDataIter == blobDataEnd);
 3940 
 3941   return NS_OK;
 3942 }
 3943 
 3944 NS_IMETHODIMP
 3945 UpgradeIndexDataValuesFunction::OnFunctionCall(mozIStorageValueArray* aArguments,
 3946                                                nsIVariant** aResult)
 3947 {
 3948   MOZ_ASSERT(aArguments);
 3949   MOZ_ASSERT(aResult);
 3950 
 3951   PROFILER_LABEL("IndexedDB",
 3952                  "UpgradeIndexDataValuesFunction::OnFunctionCall",
 3953                  js::ProfileEntry::Category::STORAGE);
 3954 
 3955   uint32_t argc;
 3956   nsresult rv = aArguments->GetNumEntries(&argc);
 3957   if (NS_WARN_IF(NS_FAILED(rv))) {
 3958     return rv;
 3959   }
 3960 
 3961   if (argc != 1) {
 3962     NS_WARNING("Don't call me with the wrong number of arguments!");
 3963     return NS_ERROR_UNEXPECTED;
 3964   }
 3965 
 3966   int32_t type;
 3967   rv = aArguments->GetTypeOfIndex(0, &type);
 3968   if (NS_WARN_IF(NS_FAILED(rv))) {
 3969     return rv;
 3970   }
 3971 
 3972   if (type != mozIStorageStatement::VALUE_TYPE_BLOB) {
 3973     NS_WARNING("Don't call me with the wrong type of arguments!");
 3974     return NS_ERROR_UNEXPECTED;
 3975   }
 3976 
 3977   const uint8_t* oldBlob;
 3978   uint32_t oldBlobLength;
 3979   rv = aArguments->GetSharedBlob(0, &oldBlobLength, &oldBlob);
 3980   if (NS_WARN_IF(NS_FAILED(rv))) {
 3981     return rv;
 3982   }
 3983 
 3984   AutoTArray<IndexDataValue, 32> oldIdv;
 3985   rv = ReadOldCompressedIDVFromBlob(oldBlob, oldBlobLength, oldIdv);
 3986   if (NS_WARN_IF(NS_FAILED(rv))) {
 3987     return rv;
 3988   }
 3989 
 3990   UniqueFreePtr<uint8_t> newIdv;
 3991   uint32_t newIdvLength;
 3992   rv = MakeCompressedIndexDataValues(oldIdv, newIdv, &newIdvLength);
 3993 
 3994   std::pair<uint8_t*, int> data(newIdv.release(), newIdvLength);
 3995 
 3996   nsCOMPtr<nsIVariant> result = new storage::AdoptedBlobVariant(data);
 3997 
 3998   result.forget(aResult);
 3999   return NS_OK;
 4000 }
 4001 
 4002 nsresult
 4003 UpgradeSchemaFrom20_0To21_0(mozIStorageConnection* aConnection)
 4004 {
 4005   // This should have been part of the 18 to 19 upgrade, where we changed the
 4006   // layout of the index_data_values blobs but didn't upgrade the existing data.
 4007   // See bug 1202788.
 4008 
 4009   AssertIsOnIOThread();
 4010   MOZ_ASSERT(aConnection);
 4011 
 4012   PROFILER_LABEL("IndexedDB",
 4013                  "UpgradeSchemaFrom20_0To21_0",
 4014                  js::ProfileEntry::Category::STORAGE);
 4015 
 4016   RefPtr<UpgradeIndexDataValuesFunction> function =
 4017     new UpgradeIndexDataValuesFunction();
 4018 
 4019   NS_NAMED_LITERAL_CSTRING(functionName, "upgrade_idv");
 4020 
 4021   nsresult rv = aConnection->CreateFunction(functionName, 1, function);
 4022   if (NS_WARN_IF(NS_FAILED(rv))) {
 4023     return rv;
 4024   }
 4025 
 4026   rv = aConnection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 4027     "UPDATE object_data "
 4028       "SET index_data_values = upgrade_idv(index_data_values) "
 4029       "WHERE index_data_values IS NOT NULL;"
 4030   ));
 4031   if (NS_WARN_IF(NS_FAILED(rv))) {
 4032     return rv;
 4033   }
 4034 
 4035   rv = aConnection->RemoveFunction(functionName);
 4036   if (NS_WARN_IF(NS_FAILED(rv))) {
 4037     return rv;
 4038   }
 4039 
 4040   rv = aConnection->SetSchemaVersion(MakeSchemaVersion(21, 0));
 4041   if (NS_WARN_IF(NS_FAILED(rv))) {
 4042     return rv;
 4043   }
 4044 
 4045   return NS_OK;
 4046 }
 4047 
 4048 nsresult
 4049 UpgradeSchemaFrom21_0To22_0(mozIStorageConnection* aConnection)
 4050 {
 4051   // The only change between 21 and 22 was a different structured clone format,
 4052   // but it's backwards-compatible.
 4053   nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(22, 0));
 4054   if (NS_WARN_IF(NS_FAILED(rv))) {
 4055     return rv;
 4056   }
 4057 
 4058   return NS_OK;
 4059 }
 4060 
 4061 nsresult
 4062 UpgradeSchemaFrom22_0To23_0(mozIStorageConnection* aConnection,
 4063                             const nsACString& aOrigin)
 4064 {
 4065   AssertIsOnIOThread();
 4066   MOZ_ASSERT(aConnection);
 4067   MOZ_ASSERT(!aOrigin.IsEmpty());
 4068 
 4069   PROFILER_LABEL("IndexedDB",
 4070                  "UpgradeSchemaFrom22_0To23_0",
 4071                  js::ProfileEntry::Category::STORAGE);
 4072 
 4073   nsCOMPtr<mozIStorageStatement> stmt;
 4074   nsresult rv = aConnection->CreateStatement(NS_LITERAL_CSTRING(
 4075     "UPDATE database "
 4076       "SET origin = :origin;"
 4077   ), getter_AddRefs(stmt));
 4078   if (NS_WARN_IF(NS_FAILED(rv))) {
 4079     return rv;
 4080   }
 4081 
 4082   rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("origin"), aOrigin);
 4083   if (NS_WARN_IF(NS_FAILED(rv))) {
 4084     return rv;
 4085   }
 4086 
 4087   rv = stmt->Execute();
 4088   if (NS_WARN_IF(NS_FAILED(rv))) {
 4089     return rv;
 4090   }
 4091 
 4092   rv = aConnection->SetSchemaVersion(MakeSchemaVersion(23, 0));
 4093   if (NS_WARN_IF(NS_FAILED(rv))) {
 4094     return rv;
 4095   }
 4096 
 4097   return NS_OK;
 4098 }
 4099 
 4100 nsresult
 4101 UpgradeSchemaFrom23_0To24_0(mozIStorageConnection* aConnection)
 4102 {
 4103   // The only change between 23 and 24 was a different structured clone format,
 4104   // but it's backwards-compatible.
 4105   nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(24, 0));
 4106   if (NS_WARN_IF(NS_FAILED(rv))) {
 4107     return rv;
 4108   }
 4109 
 4110   return NS_OK;
 4111 }
 4112 
 4113 nsresult
 4114 UpgradeSchemaFrom24_0To25_0(mozIStorageConnection* aConnection)
 4115 {
 4116   // The changes between 24 and 25 were an upgraded snappy library, a different
 4117   // structured clone format and a different file_ds format. But everything is
 4118   // backwards-compatible.
 4119   nsresult rv = aConnection->SetSchemaVersion(MakeSchemaVersion(25, 0));
 4120   if (NS_WARN_IF(NS_FAILED(rv))) {
 4121     return rv;
 4122   }
 4123 
 4124   return NS_OK;
 4125 }
 4126 
 4127 nsresult
 4128 GetDatabaseFileURL(nsIFile* aDatabaseFile,
 4129                    PersistenceType aPersistenceType,
 4130                    const nsACString& aGroup,
 4131                    const nsACString& aOrigin,
 4132                    nsIFileURL** aResult)
 4133 {
 4134   MOZ_ASSERT(aDatabaseFile);
 4135   MOZ_ASSERT(aResult);
 4136 
 4137   nsresult rv;
 4138 
 4139   nsCOMPtr<nsIProtocolHandler> protocolHandler(
 4140     do_GetService(NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "file", &rv));
 4141   if (NS_WARN_IF(NS_FAILED(rv))) {
 4142     return rv;
 4143   }
 4144 
 4145   nsCOMPtr<nsIFileProtocolHandler> fileHandler(
 4146     do_QueryInterface(protocolHandler, &rv));
 4147   if (NS_WARN_IF(NS_FAILED(rv))) {
 4148     return rv;
 4149   }
 4150 
 4151   nsCOMPtr<nsIURI> uri;
 4152   rv = fileHandler->NewFileURI(aDatabaseFile, getter_AddRefs(uri));
 4153   if (NS_WARN_IF(NS_FAILED(rv))) {
 4154     return rv;
 4155   }
 4156 
 4157   nsCOMPtr<nsIFileURL> fileUrl = do_QueryInterface(uri);
 4158   MOZ_ASSERT(fileUrl);
 4159 
 4160   nsAutoCString type;
 4161   PersistenceTypeToText(aPersistenceType, type);
 4162 
 4163   rv = fileUrl->SetQuery(NS_LITERAL_CSTRING("persistenceType=") + type +
 4164                          NS_LITERAL_CSTRING("&group=") + aGroup +
 4165                          NS_LITERAL_CSTRING("&origin=") + aOrigin +
 4166                          NS_LITERAL_CSTRING("&cache=private"));
 4167   if (NS_WARN_IF(NS_FAILED(rv))) {
 4168     return rv;
 4169   }
 4170 
 4171   fileUrl.forget(aResult);
 4172   return NS_OK;
 4173 }
 4174 
 4175 nsresult
 4176 SetDefaultPragmas(mozIStorageConnection* aConnection)
 4177 {
 4178   MOZ_ASSERT(!NS_IsMainThread());
 4179   MOZ_ASSERT(aConnection);
 4180 
 4181   static const char kBuiltInPragmas[] =
 4182     // We use foreign keys in DEBUG builds only because there is a performance
 4183     // cost to using them.
 4184    "PRAGMA foreign_keys = "
 4185 #ifdef DEBUG
 4186      "ON"
 4187 #else
 4188      "OFF"
 4189 #endif
 4190      ";"
 4191 
 4192     // The "INSERT OR REPLACE" statement doesn't fire the update trigger,
 4193     // instead it fires only the insert trigger. This confuses the update
 4194     // refcount function. This behavior changes with enabled recursive triggers,
 4195     // so the statement fires the delete trigger first and then the insert
 4196     // trigger.
 4197     "PRAGMA recursive_triggers = ON;"
 4198 
 4199     // We aggressively truncate the database file when idle so don't bother
 4200     // overwriting the WAL with 0 during active periods.
 4201     "PRAGMA secure_delete = OFF;"
 4202   ;
 4203 
 4204   nsresult rv =
 4205     aConnection->ExecuteSimpleSQL(
 4206       nsDependentCString(kBuiltInPragmas,
 4207                          LiteralStringLength(kBuiltInPragmas)));
 4208   if (NS_WARN_IF(NS_FAILED(rv))) {
 4209     return rv;
 4210   }
 4211 
 4212   nsAutoCString pragmaStmt;
 4213   pragmaStmt.AssignLiteral("PRAGMA synchronous = ");
 4214 
 4215   if (IndexedDatabaseManager::FullSynchronous()) {
 4216     pragmaStmt.AppendLiteral("FULL");
 4217   } else {
 4218     pragmaStmt.AppendLiteral("NORMAL");
 4219   }
 4220   pragmaStmt.Append(';');
 4221 
 4222   rv = aConnection->ExecuteSimpleSQL(pragmaStmt);
 4223   if (NS_WARN_IF(NS_FAILED(rv))) {
 4224     return rv;
 4225   }
 4226 
 4227 #ifndef IDB_MOBILE
 4228   if (kSQLiteGrowthIncrement) {
 4229     // This is just an optimization so ignore the failure if the disk is
 4230     // currently too full.
 4231     rv = aConnection->SetGrowthIncrement(kSQLiteGrowthIncrement,
 4232                                          EmptyCString());
 4233     if (rv != NS_ERROR_FILE_TOO_BIG && NS_WARN_IF(NS_FAILED(rv))) {
 4234       return rv;
 4235     }
 4236   }
 4237 #endif // IDB_MOBILE
 4238 
 4239   return NS_OK;
 4240 }
 4241 
 4242 nsresult
 4243 SetJournalMode(mozIStorageConnection* aConnection)
 4244 {
 4245   MOZ_ASSERT(!NS_IsMainThread());
 4246   MOZ_ASSERT(aConnection);
 4247 
 4248   // Try enabling WAL mode. This can fail in various circumstances so we have to
 4249   // check the results here.
 4250   NS_NAMED_LITERAL_CSTRING(journalModeQueryStart, "PRAGMA journal_mode = ");
 4251   NS_NAMED_LITERAL_CSTRING(journalModeWAL, "wal");
 4252 
 4253   nsCOMPtr<mozIStorageStatement> stmt;
 4254   nsresult rv =
 4255     aConnection->CreateStatement(journalModeQueryStart + journalModeWAL,
 4256                                  getter_AddRefs(stmt));
 4257   if (NS_WARN_IF(NS_FAILED(rv))) {
 4258     return rv;
 4259   }
 4260 
 4261   bool hasResult;
 4262   rv = stmt->ExecuteStep(&hasResult);
 4263   if (NS_WARN_IF(NS_FAILED(rv))) {
 4264     return rv;
 4265   }
 4266 
 4267   MOZ_ASSERT(hasResult);
 4268 
 4269   nsCString journalMode;
 4270   rv = stmt->GetUTF8String(0, journalMode);
 4271   if (NS_WARN_IF(NS_FAILED(rv))) {
 4272     return rv;
 4273   }
 4274 
 4275   if (journalMode.Equals(journalModeWAL)) {
 4276     // WAL mode successfully enabled. Maybe set limits on its size here.
 4277     if (kMaxWALPages >= 0) {
 4278       nsAutoCString pageCount;
 4279       pageCount.AppendInt(kMaxWALPages);
 4280 
 4281       rv = aConnection->ExecuteSimpleSQL(
 4282         NS_LITERAL_CSTRING("PRAGMA wal_autocheckpoint = ") + pageCount);
 4283       if (NS_WARN_IF(NS_FAILED(rv))) {
 4284         return rv;
 4285       }
 4286     }
 4287   } else {
 4288     NS_WARNING("Failed to set WAL mode, falling back to normal journal mode.");
 4289 #ifdef IDB_MOBILE
 4290     rv = aConnection->ExecuteSimpleSQL(journalModeQueryStart +
 4291                                        NS_LITERAL_CSTRING("truncate"));
 4292     if (NS_WARN_IF(NS_FAILED(rv))) {
 4293       return rv;
 4294     }
 4295 #endif
 4296   }
 4297 
 4298   return NS_OK;
 4299 }
 4300 
 4301 template <class FileOrURLType>
 4302 struct StorageOpenTraits;
 4303 
 4304 template <>
 4305 struct StorageOpenTraits<nsIFileURL*>
 4306 {
 4307   static nsresult
 4308   Open(mozIStorageService* aStorageService,
 4309        nsIFileURL* aFileURL,
 4310        mozIStorageConnection** aConnection)
 4311   {
 4312     return aStorageService->OpenDatabaseWithFileURL(aFileURL, aConnection);
 4313   }
 4314 
 4315 #ifdef DEBUG
 4316   static void
 4317   GetPath(nsIFileURL* aFileURL, nsCString& aPath)
 4318   {
 4319     MOZ_ALWAYS_SUCCEEDS(aFileURL->GetFileName(aPath));
 4320   }
 4321 #endif
 4322 };
 4323 
 4324 template <>
 4325 struct StorageOpenTraits<nsIFile*>
 4326 {
 4327   static nsresult
 4328   Open(mozIStorageService* aStorageService,
 4329        nsIFile* aFile,
 4330        mozIStorageConnection** aConnection)
 4331   {
 4332     return aStorageService->OpenUnsharedDatabase(aFile, aConnection);
 4333   }
 4334 
 4335 #ifdef DEBUG
 4336   static void
 4337   GetPath(nsIFile* aFile, nsCString& aPath)
 4338   {
 4339     nsString path;
 4340     MOZ_ALWAYS_SUCCEEDS(aFile->GetPath(path));
 4341 
 4342     aPath.AssignWithConversion(path);
 4343   }
 4344 #endif
 4345 };
 4346 
 4347 template <template <class> class SmartPtr, class FileOrURLType>
 4348 struct StorageOpenTraits<SmartPtr<FileOrURLType>>
 4349   : public StorageOpenTraits<FileOrURLType*>
 4350 { };
 4351 
 4352 template <class FileOrURLType>
 4353 nsresult
 4354 OpenDatabaseAndHandleBusy(mozIStorageService* aStorageService,
 4355                           FileOrURLType aFileOrURL,
 4356                           mozIStorageConnection** aConnection)
 4357 {
 4358   MOZ_ASSERT(!NS_IsMainThread());
 4359   MOZ_ASSERT(!IsOnBackgroundThread());
 4360   MOZ_ASSERT(aStorageService);
 4361   MOZ_ASSERT(aFileOrURL);
 4362   MOZ_ASSERT(aConnection);
 4363 
 4364   nsCOMPtr<mozIStorageConnection> connection;
 4365   nsresult rv =
 4366     StorageOpenTraits<FileOrURLType>::Open(aStorageService,
 4367                                            aFileOrURL,
 4368                                            getter_AddRefs(connection));
 4369 
 4370   if (rv == NS_ERROR_STORAGE_BUSY) {
 4371 #ifdef DEBUG
 4372     {
 4373       nsCString path;
 4374       StorageOpenTraits<FileOrURLType>::GetPath(aFileOrURL, path);
 4375 
 4376       nsPrintfCString message("Received NS_ERROR_STORAGE_BUSY when attempting "
 4377                               "to open database '%s', retrying for up to 10 "
 4378                               "seconds",
 4379                               path.get());
 4380       NS_WARNING(message.get());
 4381     }
 4382 #endif
 4383 
 4384     // Another thread must be checkpointing the WAL. Wait up to 10 seconds for
 4385     // that to complete.
 4386     TimeStamp start = TimeStamp::NowLoRes();
 4387 
 4388     while (true) {
 4389       PR_Sleep(PR_MillisecondsToInterval(100));
 4390 
 4391       rv = StorageOpenTraits<FileOrURLType>::Open(aStorageService,
 4392                                                   aFileOrURL,
 4393                                                   getter_AddRefs(connection));
 4394       if (rv != NS_ERROR_STORAGE_BUSY ||
 4395           TimeStamp::NowLoRes() - start > TimeDuration::FromSeconds(10)) {
 4396         break;
 4397       }
 4398     }
 4399   }
 4400 
 4401   if (NS_WARN_IF(NS_FAILED(rv))) {
 4402     return rv;
 4403   }
 4404 
 4405   connection.forget(aConnection);
 4406   return NS_OK;
 4407 }
 4408 
 4409 nsresult
 4410 CreateStorageConnection(nsIFile* aDBFile,
 4411                         nsIFile* aFMDirectory,
 4412                         const nsAString& aName,
 4413                         PersistenceType aPersistenceType,
 4414                         const nsACString& aGroup,
 4415                         const nsACString& aOrigin,
 4416                         mozIStorageConnection** aConnection)
 4417 {
 4418   AssertIsOnIOThread();
 4419   MOZ_ASSERT(aDBFile);
 4420   MOZ_ASSERT(aFMDirectory);
 4421   MOZ_ASSERT(aConnection);
 4422 
 4423   PROFILER_LABEL("IndexedDB",
 4424                  "CreateStorageConnection",
 4425                  js::ProfileEntry::Category::STORAGE);
 4426 
 4427   nsresult rv;
 4428   bool exists;
 4429 
 4430   if (IndexedDatabaseManager::InLowDiskSpaceMode()) {
 4431     rv = aDBFile->Exists(&exists);
 4432     if (NS_WARN_IF(NS_FAILED(rv))) {
 4433       return rv;
 4434     }
 4435 
 4436     if (!exists) {
 4437       NS_WARNING("Refusing to create database because disk space is low!");
 4438       return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
 4439     }
 4440   }
 4441 
 4442   nsCOMPtr<nsIFileURL> dbFileUrl;
 4443   rv = GetDatabaseFileURL(aDBFile,
 4444                           aPersistenceType,
 4445                           aGroup,
 4446                           aOrigin,
 4447                           getter_AddRefs(dbFileUrl));
 4448   if (NS_WARN_IF(NS_FAILED(rv))) {
 4449     return rv;
 4450   }
 4451 
 4452   nsCOMPtr<mozIStorageService> ss =
 4453     do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
 4454   if (NS_WARN_IF(NS_FAILED(rv))) {
 4455     return rv;
 4456   }
 4457 
 4458   nsCOMPtr<mozIStorageConnection> connection;
 4459   rv = OpenDatabaseAndHandleBusy(ss, dbFileUrl, getter_AddRefs(connection));
 4460   if (rv == NS_ERROR_FILE_CORRUPTED) {
 4461     // If we're just opening the database during origin initialization, then
 4462     // we don't want to erase any files. The failure here will fail origin
 4463     // initialization too.
 4464     if (aName.IsVoid()) {
 4465       return rv;
 4466     }
 4467 
 4468     // Nuke the database file.
 4469     rv = aDBFile->Remove(false);
 4470     if (NS_WARN_IF(NS_FAILED(rv))) {
 4471       return rv;
 4472     }
 4473 
 4474     rv = aFMDirectory->Exists(&exists);
 4475     if (NS_WARN_IF(NS_FAILED(rv))) {
 4476       return rv;
 4477     }
 4478 
 4479     if (exists) {
 4480       bool isDirectory;
 4481       rv = aFMDirectory->IsDirectory(&isDirectory);
 4482       if (NS_WARN_IF(NS_FAILED(rv))) {
 4483         return rv;
 4484       }
 4485       if (NS_WARN_IF(!isDirectory)) {
 4486         IDB_REPORT_INTERNAL_ERR();
 4487         return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
 4488       }
 4489 
 4490       rv = aFMDirectory->Remove(true);
 4491       if (NS_WARN_IF(NS_FAILED(rv))) {
 4492         return rv;
 4493       }
 4494     }
 4495 
 4496     rv = OpenDatabaseAndHandleBusy(ss, dbFileUrl, getter_AddRefs(connection));
 4497   }
 4498 
 4499   if (NS_WARN_IF(NS_FAILED(rv))) {
 4500     return rv;
 4501   }
 4502 
 4503   rv = SetDefaultPragmas(connection);
 4504   if (NS_WARN_IF(NS_FAILED(rv))) {
 4505     return rv;
 4506   }
 4507 
 4508   rv = connection->EnableModule(NS_LITERAL_CSTRING("filesystem"));
 4509   if (NS_WARN_IF(NS_FAILED(rv))) {
 4510     return rv;
 4511   }
 4512 
 4513   // Check to make sure that the database schema is correct.
 4514   int32_t schemaVersion;
 4515   rv = connection->GetSchemaVersion(&schemaVersion);
 4516   if (NS_WARN_IF(NS_FAILED(rv))) {
 4517     return rv;
 4518   }
 4519 
 4520   // Unknown schema will fail origin initialization too.
 4521   if (!schemaVersion && aName.IsVoid()) {
 4522     IDB_WARNING("Unable to open IndexedDB database, schema is not set!");
 4523     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
 4524   }
 4525 
 4526   if (schemaVersion > kSQLiteSchemaVersion) {
 4527     IDB_WARNING("Unable to open IndexedDB database, schema is too high!");
 4528     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
 4529   }
 4530 
 4531   bool journalModeSet = false;
 4532 
 4533   if (schemaVersion != kSQLiteSchemaVersion) {
 4534     const bool newDatabase = !schemaVersion;
 4535 
 4536     if (newDatabase) {
 4537       // Set the page size first.
 4538       if (kSQLitePageSizeOverride) {
 4539         rv = connection->ExecuteSimpleSQL(
 4540           nsPrintfCString("PRAGMA page_size = %lu;", kSQLitePageSizeOverride)
 4541         );
 4542         if (NS_WARN_IF(NS_FAILED(rv))) {
 4543           return rv;
 4544         }
 4545       }
 4546 
 4547       // We have to set the auto_vacuum mode before opening a transaction.
 4548       rv = connection->ExecuteSimpleSQL(
 4549 #ifdef IDB_MOBILE
 4550         // Turn on full auto_vacuum mode to reclaim disk space on mobile
 4551         // devices (at the cost of some COMMIT speed).
 4552         NS_LITERAL_CSTRING("PRAGMA auto_vacuum = FULL;")
 4553 #else
 4554         // Turn on incremental auto_vacuum mode on desktop builds.
 4555         NS_LITERAL_CSTRING("PRAGMA auto_vacuum = INCREMENTAL;")
 4556 #endif
 4557       );
 4558       if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) {
 4559         // mozstorage translates SQLITE_FULL to NS_ERROR_FILE_NO_DEVICE_SPACE,
 4560         // which we know better as NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR.
 4561         rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
 4562       }
 4563       if (NS_WARN_IF(NS_FAILED(rv))) {
 4564         return rv;
 4565       }
 4566 
 4567       rv = SetJournalMode(connection);
 4568       if (NS_WARN_IF(NS_FAILED(rv))) {
 4569         return rv;
 4570       }
 4571 
 4572       journalModeSet = true;
 4573     } else {
 4574 #ifdef DEBUG
 4575     // Disable foreign key support while upgrading. This has to be done before
 4576     // starting a transaction.
 4577     MOZ_ALWAYS_SUCCEEDS(
 4578       connection->ExecuteSimpleSQL(
 4579         NS_LITERAL_CSTRING("PRAGMA foreign_keys = OFF;")));
 4580 #endif
 4581     }
 4582 
 4583     bool vacuumNeeded = false;
 4584 
 4585     mozStorageTransaction transaction(connection, false,
 4586                                   mozIStorageConnection::TRANSACTION_IMMEDIATE);
 4587 
 4588     if (newDatabase) {
 4589       rv = CreateTables(connection);
 4590       if (NS_WARN_IF(NS_FAILED(rv))) {
 4591         return rv;
 4592       }
 4593 
 4594       MOZ_ASSERT(NS_SUCCEEDED(connection->GetSchemaVersion(&schemaVersion)));
 4595       MOZ_ASSERT(schemaVersion == kSQLiteSchemaVersion);
 4596 
 4597       nsCOMPtr<mozIStorageStatement> stmt;
 4598       nsresult rv = connection->CreateStatement(NS_LITERAL_CSTRING(
 4599         "INSERT INTO database (name, origin) "
 4600         "VALUES (:name, :origin)"
 4601       ), getter_AddRefs(stmt));
 4602       if (NS_WARN_IF(NS_FAILED(rv))) {
 4603         return rv;
 4604       }
 4605 
 4606       rv = stmt->BindStringByName(NS_LITERAL_CSTRING("name"), aName);
 4607       if (NS_WARN_IF(NS_FAILED(rv))) {
 4608         return rv;
 4609       }
 4610 
 4611       rv = stmt->BindUTF8StringByName(NS_LITERAL_CSTRING("origin"), aOrigin);
 4612       if (NS_WARN_IF(NS_FAILED(rv))) {
 4613         return rv;
 4614       }
 4615 
 4616       rv = stmt->Execute();
 4617       if (NS_WARN_IF(NS_FAILED(rv))) {
 4618         return rv;
 4619       }
 4620     } else  {
 4621       // This logic needs to change next time we change the schema!
 4622       static_assert(kSQLiteSchemaVersion == int32_t((25 << 4) + 0),
 4623                     "Upgrade function needed due to schema version increase.");
 4624 
 4625       while (schemaVersion != kSQLiteSchemaVersion) {
 4626         if (schemaVersion == 4) {
 4627           rv = UpgradeSchemaFrom4To5(connection);
 4628         } else if (schemaVersion == 5) {
 4629           rv = UpgradeSchemaFrom5To6(connection);
 4630         } else if (schemaVersion == 6) {
 4631           rv = UpgradeSchemaFrom6To7(connection);
 4632         } else if (schemaVersion == 7) {
 4633           rv = UpgradeSchemaFrom7To8(connection);
 4634         } else if (schemaVersion == 8) {
 4635           rv = UpgradeSchemaFrom8To9_0(connection);
 4636           vacuumNeeded = true;
 4637         } else if (schemaVersion == MakeSchemaVersion(9, 0)) {
 4638           rv = UpgradeSchemaFrom9_0To10_0(connection);
 4639         } else if (schemaVersion == MakeSchemaVersion(10, 0)) {
 4640           rv = UpgradeSchemaFrom10_0To11_0(connection);
 4641         } else if (schemaVersion == MakeSchemaVersion(11, 0)) {
 4642           rv = UpgradeSchemaFrom11_0To12_0(connection);
 4643         } else if (schemaVersion == MakeSchemaVersion(12, 0)) {
 4644           rv = UpgradeSchemaFrom12_0To13_0(connection, &vacuumNeeded);
 4645         } else if (schemaVersion == MakeSchemaVersion(13, 0)) {
 4646           rv = UpgradeSchemaFrom13_0To14_0(connection);
 4647         } else if (schemaVersion == MakeSchemaVersion(14, 0)) {
 4648           rv = UpgradeSchemaFrom14_0To15_0(connection);
 4649         } else if (schemaVersion == MakeSchemaVersion(15, 0)) {
 4650           rv = UpgradeSchemaFrom15_0To16_0(connection);
 4651         } else if (schemaVersion == MakeSchemaVersion(16, 0)) {
 4652           rv = UpgradeSchemaFrom16_0To17_0(connection);
 4653         } else if (schemaVersion == MakeSchemaVersion(17, 0)) {
 4654           rv = UpgradeSchemaFrom17_0To18_0(connection, aOrigin);
 4655           vacuumNeeded = true;
 4656         } else if (schemaVersion == MakeSchemaVersion(18, 0)) {
 4657           rv = UpgradeSchemaFrom18_0To19_0(connection);
 4658         } else if (schemaVersion == MakeSchemaVersion(19, 0)) {
 4659           rv = UpgradeSchemaFrom19_0To20_0(aFMDirectory, connection);
 4660         } else if (schemaVersion == MakeSchemaVersion(20, 0)) {
 4661           rv = UpgradeSchemaFrom20_0To21_0(connection);
 4662         } else if (schemaVersion == MakeSchemaVersion(21, 0)) {
 4663           rv = UpgradeSchemaFrom21_0To22_0(connection);
 4664         } else if (schemaVersion == MakeSchemaVersion(22, 0)) {
 4665           rv = UpgradeSchemaFrom22_0To23_0(connection, aOrigin);
 4666         } else if (schemaVersion == MakeSchemaVersion(23, 0)) {
 4667           rv = UpgradeSchemaFrom23_0To24_0(connection);
 4668         } else if (schemaVersion == MakeSchemaVersion(24, 0)) {
 4669           rv = UpgradeSchemaFrom24_0To25_0(connection);
 4670         } else {
 4671           IDB_WARNING("Unable to open IndexedDB database, no upgrade path is "
 4672                       "available!");
 4673           return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
 4674         }
 4675 
 4676         if (NS_WARN_IF(NS_FAILED(rv))) {
 4677           return rv;
 4678         }
 4679 
 4680         rv = connection->GetSchemaVersion(&schemaVersion);
 4681         if (NS_WARN_IF(NS_FAILED(rv))) {
 4682           return rv;
 4683         }
 4684       }
 4685 
 4686       MOZ_ASSERT(schemaVersion == kSQLiteSchemaVersion);
 4687     }
 4688 
 4689     rv = transaction.Commit();
 4690     if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) {
 4691       // mozstorage translates SQLITE_FULL to NS_ERROR_FILE_NO_DEVICE_SPACE,
 4692       // which we know better as NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR.
 4693       rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
 4694     }
 4695     if (NS_WARN_IF(NS_FAILED(rv))) {
 4696       return rv;
 4697     }
 4698 
 4699 #ifdef DEBUG
 4700     if (!newDatabase) {
 4701       // Re-enable foreign key support after doing a foreign key check.
 4702       nsCOMPtr<mozIStorageStatement> checkStmt;
 4703       MOZ_ALWAYS_SUCCEEDS(
 4704         connection->CreateStatement(
 4705           NS_LITERAL_CSTRING("PRAGMA foreign_key_check;"),
 4706           getter_AddRefs(checkStmt)));
 4707 
 4708       bool hasResult;
 4709       MOZ_ALWAYS_SUCCEEDS(checkStmt->ExecuteStep(&hasResult));
 4710       MOZ_ASSERT(!hasResult, "Database has inconsisistent foreign keys!");
 4711 
 4712       MOZ_ALWAYS_SUCCEEDS(
 4713         connection->ExecuteSimpleSQL(
 4714           NS_LITERAL_CSTRING("PRAGMA foreign_keys = OFF;")));
 4715     }
 4716 #endif
 4717 
 4718     if (kSQLitePageSizeOverride && !newDatabase) {
 4719       nsCOMPtr<mozIStorageStatement> stmt;
 4720       rv = connection->CreateStatement(NS_LITERAL_CSTRING(
 4721         "PRAGMA page_size;"
 4722       ), getter_AddRefs(stmt));
 4723       if (NS_WARN_IF(NS_FAILED(rv))) {
 4724         return rv;
 4725       }
 4726 
 4727       bool hasResult;
 4728       rv = stmt->ExecuteStep(&hasResult);
 4729       if (NS_WARN_IF(NS_FAILED(rv))) {
 4730         return rv;
 4731       }
 4732 
 4733       MOZ_ASSERT(hasResult);
 4734 
 4735       int32_t pageSize;
 4736       rv = stmt->GetInt32(0, &pageSize);
 4737       if (NS_WARN_IF(NS_FAILED(rv))) {
 4738         return rv;
 4739       }
 4740 
 4741       MOZ_ASSERT(pageSize >= 512 && pageSize <= 65536);
 4742 
 4743       if (kSQLitePageSizeOverride != uint32_t(pageSize)) {
 4744         // We must not be in WAL journal mode to change the page size.
 4745         rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 4746           "PRAGMA journal_mode = DELETE;"
 4747         ));
 4748         if (NS_WARN_IF(NS_FAILED(rv))) {
 4749           return rv;
 4750         }
 4751 
 4752         rv = connection->CreateStatement(NS_LITERAL_CSTRING(
 4753           "PRAGMA journal_mode;"
 4754         ), getter_AddRefs(stmt));
 4755         if (NS_WARN_IF(NS_FAILED(rv))) {
 4756           return rv;
 4757         }
 4758 
 4759         rv = stmt->ExecuteStep(&hasResult);
 4760         if (NS_WARN_IF(NS_FAILED(rv))) {
 4761           return rv;
 4762         }
 4763 
 4764         MOZ_ASSERT(hasResult);
 4765 
 4766         nsCString journalMode;
 4767         rv = stmt->GetUTF8String(0, journalMode);
 4768         if (NS_WARN_IF(NS_FAILED(rv))) {
 4769           return rv;
 4770         }
 4771 
 4772         if (journalMode.EqualsLiteral("delete")) {
 4773           // Successfully set to rollback journal mode so changing the page size
 4774           // is possible with a VACUUM.
 4775           rv = connection->ExecuteSimpleSQL(
 4776             nsPrintfCString("PRAGMA page_size = %lu;", kSQLitePageSizeOverride)
 4777           );
 4778           if (NS_WARN_IF(NS_FAILED(rv))) {
 4779             return rv;
 4780           }
 4781 
 4782           // We will need to VACUUM in order to change the page size.
 4783           vacuumNeeded = true;
 4784         } else {
 4785           NS_WARNING("Failed to set journal_mode for database, unable to "
 4786                      "change the page size!");
 4787         }
 4788       }
 4789     }
 4790 
 4791     if (vacuumNeeded) {
 4792       rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING("VACUUM;"));
 4793       if (NS_WARN_IF(NS_FAILED(rv))) {
 4794         return rv;
 4795       }
 4796     }
 4797 
 4798     if (newDatabase || vacuumNeeded) {
 4799       if (journalModeSet) {
 4800         // Make sure we checkpoint to get an accurate file size.
 4801         rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
 4802           "PRAGMA wal_checkpoint(FULL);"
 4803         ));
 4804         if (NS_WARN_IF(NS_FAILED(rv))) {
 4805           return rv;
 4806         }
 4807       }
 4808 
 4809       int64_t fileSize;
 4810       rv = aDBFile->GetFileSize(&fileSize);
 4811       if (NS_WARN_IF(NS_FAILED(rv))) {
 4812         return rv;
 4813       }
 4814 
 4815       MOZ_ASSERT(fileSize > 0);
 4816 
 4817       PRTime vacuumTime = PR_Now();
 4818       MOZ_ASSERT(vacuumTime);
 4819 
 4820       nsCOMPtr<mozIStorageStatement> vacuumTimeStmt;
 4821       rv = connection->CreateStatement(NS_LITERAL_CSTRING(
 4822         "UPDATE database "
 4823           "SET last_vacuum_time = :time"
 4824             ", last_vacuum_size = :size;"
 4825       ), getter_AddRefs(vacuumTimeStmt));
 4826       if (NS_WARN_IF(NS_FAILED(rv))) {
 4827         return rv;
 4828       }
 4829 
 4830       rv = vacuumTimeStmt->BindInt64ByName(NS_LITERAL_CSTRING("time"),
 4831                                            vacuumTime);
 4832       if (NS_WARN_IF(NS_FAILED(rv))) {
 4833         return rv;
 4834       }
 4835 
 4836       rv = vacuumTimeStmt->BindInt64ByName(NS_LITERAL_CSTRING("size"),
 4837                                            fileSize);
 4838       if (NS_WARN_IF(NS_FAILED(rv))) {
 4839         return rv;
 4840       }
 4841 
 4842       rv = vacuumTimeStmt->Execute();
 4843       if (NS_WARN_IF(NS_FAILED(rv))) {
 4844         return rv;
 4845       }
 4846     }
 4847   }
 4848 
 4849   if (!journalModeSet) {
 4850     rv = SetJournalMode(connection);
 4851     if (NS_WARN_IF(NS_FAILED(rv))) {
 4852       return rv;
 4853     }
 4854   }
 4855 
 4856   connection.forget(aConnection);
 4857   return NS_OK;
 4858 }
 4859 
 4860 already_AddRefed<nsIFile>
 4861 GetFileForPath(const nsAString& aPath)
 4862 {
 4863   MOZ_ASSERT(!aPath.IsEmpty());
 4864 
 4865   nsCOMPtr<nsIFile> file = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
 4866   if (NS_WARN_IF(!file)) {
 4867     return nullptr;
 4868   }
 4869 
 4870   if (NS_WARN_IF(NS_FAILED(file->InitWithPath(aPath)))) {
 4871     return nullptr;
 4872   }
 4873 
 4874   return file.forget();
 4875 }
 4876 
 4877 nsresult
 4878 GetStorageConnection(nsIFile* aDatabaseFile,
 4879                      PersistenceType aPersistenceType,
 4880                      const nsACString& aGroup,
 4881                      const nsACString& aOrigin,
 4882                      mozIStorageConnection** aConnection)
 4883 {
 4884   MOZ_ASSERT(!NS_IsMainThread());
 4885   MOZ_ASSERT(!IsOnBackgroundThread());
 4886   MOZ_ASSERT(aDatabaseFile);
 4887   MOZ_ASSERT(aConnection);
 4888 
 4889   PROFILER_LABEL("IndexedDB",
 4890                  "GetStorageConnection",
 4891                  js::ProfileEntry::Category::STORAGE);
 4892 
 4893   bool exists;
 4894   nsresult rv = aDatabaseFile->Exists(&exists);
 4895   if (NS_WARN_IF(NS_FAILED(rv))) {
 4896     return rv;
 4897   }
 4898 
 4899   if (NS_WARN_IF(!exists)) {
 4900     IDB_REPORT_INTERNAL_ERR();
 4901     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
 4902   }
 4903 
 4904   nsCOMPtr<nsIFileURL> dbFileUrl;
 4905   rv = GetDatabaseFileURL(aDatabaseFile,
 4906                           aPersistenceType,
 4907                           aGroup,
 4908                           aOrigin,
 4909                           getter_AddRefs(dbFileUrl));
 4910   if (NS_WARN_IF(NS_FAILED(rv))) {
 4911     return rv;
 4912   }
 4913 
 4914   nsCOMPtr<mozIStorageService> ss =
 4915     do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
 4916   if (NS_WARN_IF(NS_FAILED(rv))) {
 4917     return rv;
 4918   }
 4919 
 4920   nsCOMPtr<mozIStorageConnection> connection;
 4921   rv = OpenDatabaseAndHandleBusy(ss, dbFileUrl, getter_AddRefs(connection));
 4922   if (NS_WARN_IF(NS_FAILED(rv))) {
 4923     return rv;
 4924   }
 4925 
 4926   rv = SetDefaultPragmas(connection);
 4927   if (NS_WARN_IF(NS_FAILED(rv))) {
 4928     return rv;
 4929   }
 4930 
 4931   rv = SetJournalMode(connection);
 4932   if (NS_WARN_IF(NS_FAILED(rv))) {
 4933     return rv;
 4934   }
 4935 
 4936   connection.forget(aConnection);
 4937   return NS_OK;
 4938 }
 4939 
 4940 nsresult
 4941 GetStorageConnection(const nsAString& aDatabaseFilePath,
 4942                      PersistenceType aPersistenceType,
 4943                      const nsACString& aGroup,
 4944                      const nsACString& aOrigin,
 4945                      mozIStorageConnection** aConnection)
 4946 {
 4947   MOZ_ASSERT(!NS_IsMainThread());
 4948   MOZ_ASSERT(!IsOnBackgroundThread());
 4949   MOZ_ASSERT(!aDatabaseFilePath.IsEmpty());
 4950   MOZ_ASSERT(StringEndsWith(aDatabaseFilePath, NS_LITERAL_STRING(".sqlite")));
 4951   MOZ_ASSERT(aConnection);
 4952 
 4953   nsCOMPtr<nsIFile> dbFile = GetFileForPath(aDatabaseFilePath);
 4954   if (NS_WARN_IF(!dbFile)) {
 4955     IDB_REPORT_INTERNAL_ERR();
 4956     return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
 4957   }
 4958 
 4959   return GetStorageConnection(dbFile,
 4960                               aPersistenceType,
 4961                               aGroup,
 4962                               aOrigin,
 4963                               aConnection);
 4964 }
 4965 
 4966 /*******************************************************************************
 4967  * ConnectionPool declarations
 4968  ******************************************************************************/
 4969 
 4970 class DatabaseConnection final
 4971 {
 4972   friend class ConnectionPool;
 4973 
 4974   enum class CheckpointMode
 4975   {
 4976     Full,
 4977     Restart,
 4978     Truncate
 4979   };
 4980 
 4981 public:
 4982   class AutoSavepoint;
 4983   class CachedStatement;
 4984   class UpdateRefcountFunction;
 4985 
 4986 private:
 4987   nsCOMPtr<mozIStorageConnection> mStorageConnection;
 4988   RefPtr<FileManager> mFileManager;
 4989   nsInterfaceHashtable<nsCStringHashKey, mozIStorageStatement>
 4990     mCachedStatements;
 4991   RefPtr<UpdateRefcountFunction> mUpdateRefcountFunction;
 4992   RefPtr<QuotaObject> mQuotaObject;
 4993   RefPtr<QuotaObject> mJournalQuotaObject;
 4994   bool mInReadTransaction;
 4995   bool mInWriteTransaction;
 4996 
 4997 #ifdef DEBUG
 4998   uint32_t mDEBUGSavepointCount;
 4999   PRThread* mDEBUGThread;
 5000 #endif
 5001 
 5002 public:
 5003   void
 5004   AssertIsOnConnectionThread() const
 5005   {
 5006     MOZ_ASSERT(mDEBUGThread);
 5007     MOZ_ASSERT(PR_GetCurrentThread() == mDEBUGThread);
 5008   }
 5009 
 5010   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DatabaseConnection)
 5011 
 5012   mozIStorageConnection*
 5013   GetStorageConnection() const
 5014   {
 5015     if (mStorageConnection) {
 5016       AssertIsOnConnectionThread();
 5017       return mStorageConnection;
 5018     }
 5019 
 5020     return nullptr;
 5021   }
 5022 
 5023   UpdateRefcountFunction*
 5024   GetUpdateRefcountFunction() const
 5025   {
 5026     AssertIsOnConnectionThread();
 5027 
 5028     return mUpdateRefcountFunction;
 5029   }
 5030 
 5031   nsresult
 5032   GetCachedStatement(const nsACString& aQuery,
 5033                      CachedStatement* aCachedStatement);
 5034 
 5035   nsresult
 5036   BeginWriteTransaction();
 5037 
 5038   nsresult
 5039   CommitWriteTransaction();
 5040 
 5041   void
 5042   RollbackWriteTransaction();
 5043 
 5044   void
 5045   FinishWriteTransaction();
 5046 
 5047   nsresult
 5048   StartSavepoint();
 5049 
 5050   nsresult
 5051   ReleaseSavepoint();
 5052 
 5053   nsresult
 5054   RollbackSavepoint();
 5055 
 5056   nsresult
 5057   Checkpoint()
 5058   {
 5059     AssertIsOnConnectionThread();
 5060 
 5061     return CheckpointInternal(CheckpointMode::Full);
 5062   }
 5063 
 5064   void
 5065   DoIdleProcessing(bool aNeedsCheckpoint);
 5066 
 5067   void
 5068   Close();
 5069 
 5070   nsresult
 5071   DisableQuotaChecks();
 5072 
 5073   void
 5074   EnableQuotaChecks();
 5075 
 5076 private:
 5077   DatabaseConnection(mozIStorageConnection* aStorageConnection,
 5078                      FileManager* aFileManager);
 5079 
 5080   ~DatabaseConnection();
 5081 
 5082   nsresult
 5083   Init();
 5084 
 5085   nsresult
 5086   CheckpointInternal(CheckpointMode aMode);
 5087 
 5088   nsresult
 5089   GetFreelistCount(CachedStatement& aCachedStatement, uint32_t* aFreelistCount);
 5090 
 5091   nsresult
 5092   ReclaimFreePagesWhileIdle(CachedStatement& aFreelistStatement,
 5093                             CachedStatement& aRollbackStatement,
 5094                             uint32_t aFreelistCount,
 5095                             bool aNeedsCheckpoint,
 5096                             bool* aFreedSomePages);
 5097 
 5098   nsresult
 5099   GetFileSize(const nsAString& aPath, int64_t* aResult);
 5100 };
 5101 
 5102 class MOZ_STACK_CLASS DatabaseConnection::AutoSavepoint final
 5103 {
 5104   DatabaseConnection* mConnection;
 5105 #ifdef DEBUG
 5106   const TransactionBase* mDEBUGTransaction;
 5107 #endif
 5108 
 5109 public:
 5110   AutoSavepoint();
 5111   ~AutoSavepoint();
 5112 
 5113   nsresult
 5114   Start(const TransactionBase* aConnection);
 5115 
 5116   nsresult
 5117   Commit();
 5118 };
 5119 
 5120 class DatabaseConnection::CachedStatement final
 5121 {
 5122   friend class DatabaseConnection;
 5123 
 5124   nsCOMPtr<mozIStorageStatement> mStatement;
 5125   Maybe<mozStorageStatementScoper> mScoper;
 5126 
 5127 #ifdef DEBUG
 5128   DatabaseConnection* mDEBUGConnection;
 5129 #endif
 5130 
 5131 public:
 5132   CachedStatement();
 5133   ~CachedStatement();
 5134 
 5135   void
 5136   AssertIsOnConnectionThread() const
 5137   {
 5138 #ifdef DEBUG
 5139     if (mDEBUGConnection) {
 5140       mDEBUGConnection->AssertIsOnConnectionThread();
 5141     }
 5142     MOZ_ASSERT(!NS_IsMainThread());
 5143     MOZ_ASSERT(!IsOnBackgroundThread());
 5144 #endif
 5145   }
 5146 
 5147   operator mozIStorageStatement*() const;
 5148 
 5149   mozIStorageStatement*
 5150   operator->() const MOZ_NO_ADDREF_RELEASE_ON_RETURN;
 5151 
 5152   void
 5153   Reset();
 5154 
 5155 private:
 5156   // Only called by DatabaseConnection.
 5157   void
 5158   Assign(DatabaseConnection* aConnection,
 5159          already_AddRefed<mozIStorageStatement> aStatement);
 5160 
 5161   // No funny business allowed.
 5162   CachedStatement(const CachedStatement&) = delete;
 5163   CachedStatement& operator=(const CachedStatement&) = delete;
 5164 };
 5165 
 5166 class DatabaseConnection::UpdateRefcountFunction final
 5167   : public mozIStorageFunction
 5168 {
 5169   class DatabaseUpdateFunction;
 5170   class FileInfoEntry;
 5171 
 5172   enum class UpdateType
 5173   {
 5174     Increment,
 5175     Decrement
 5176   };
 5177 
 5178   DatabaseConnection* mConnection;
 5179   FileManager* mFileManager;
 5180   nsClassHashtable<nsUint64HashKey, FileInfoEntry> mFileInfoEntries;
 5181   nsDataHashtable<nsUint64HashKey, FileInfoEntry*> mSavepointEntriesIndex;
 5182 
 5183   nsTArray<int64_t> mJournalsToCreateBeforeCommit;
 5184   nsTArray<int64_t> mJournalsToRemoveAfterCommit;
 5185   nsTArray<int64_t> mJournalsToRemoveAfterAbort;
 5186 
 5187   bool mInSavepoint;
 5188 
 5189 public:
 5190   NS_DECL_ISUPPORTS
 5191   NS_DECL_MOZISTORAGEFUNCTION
 5192 
 5193   UpdateRefcountFunction(DatabaseConnection* aConnection,
 5194                          FileManager* aFileManager);
 5195 
 5196   nsresult
 5197   WillCommit();
 5198 
 5199   void
 5200   DidCommit();
 5201 
 5202   void
 5203   DidAbort();
 5204 
 5205   void
 5206   StartSavepoint();
 5207 
 5208   void
 5209   ReleaseSavepoint();
 5210 
 5211   void
 5212   RollbackSavepoint();
 5213 
 5214   void
 5215   Reset();
 5216 
 5217 private:
 5218   ~UpdateRefcountFunction()
 5219   { }
 5220 
 5221   nsresult
 5222   ProcessValue(mozIStorageValueArray* aValues,
 5223                int32_t aIndex,
 5224                UpdateType aUpdateType);
 5225 
 5226   nsresult
 5227   CreateJournals();
 5228 
 5229   nsresult
 5230   RemoveJournals(const nsTArray<int64_t>& aJournals);
 5231 };
 5232 
 5233 class DatabaseConnection::UpdateRefcountFunction::DatabaseUpdateFunction final
 5234 {
 5235   CachedStatement mUpdateStatement;
 5236   CachedStatement mSelectStatement;
 5237   CachedStatement mInsertStatement;
 5238 
 5239   UpdateRefcountFunction* mFunction;
 5240 
 5241   nsresult mErrorCode;
 5242 
 5243 public:
 5244   explicit
 5245   DatabaseUpdateFunction(UpdateRefcountFunction* aFunction)
 5246     : mFunction(aFunction)
 5247     , mErrorCode(NS_OK)
 5248   {
 5249     MOZ_COUNT_CTOR(
 5250       DatabaseConnection::UpdateRefcountFunction::DatabaseUpdateFunction);
 5251   }
 5252 
 5253   ~DatabaseUpdateFunction()
 5254   {
 5255     MOZ_COUNT_DTOR(
 5256       DatabaseConnection::UpdateRefcountFunction::DatabaseUpdateFunction);
 5257   }
 5258 
 5259   bool
 5260   Update(int64_t aId, int32_t aDelta);
 5261 
 5262   nsresult
 5263   ErrorCode() const
 5264   {
 5265     return mErrorCode;
 5266   }
 5267 
 5268 private:
 5269   nsresult
 5270   UpdateInternal(int64_t aId, int32_t aDelta);
 5271 };
 5272 
 5273 class DatabaseConnection::UpdateRefcountFunction::FileInfoEntry final
 5274 {
 5275   friend class UpdateRefcountFunction;
 5276 
 5277   RefPtr<FileInfo> mFileInfo;
 5278   int32_t mDelta;
 5279   int32_t mSavepointDelta;
 5280 
 5281 public:
 5282   explicit
 5283   FileInfoEntry(FileInfo* aFileInfo)
 5284     : mFileInfo(aFileInfo)
 5285     , mDelta(0)
 5286     , mSavepointDelta(0)
 5287   {
 5288     MOZ_COUNT_CTOR(DatabaseConnection::UpdateRefcountFunction::FileInfoEntry);
 5289   }
 5290 
 5291   ~FileInfoEntry()
 5292   {
 5293     MOZ_COUNT_DTOR(DatabaseConnection::UpdateRefcountFunction::FileInfoEntry);
 5294   }
 5295 };
 5296 
 5297 class ConnectionPool final
 5298 {
 5299 public:
 5300   class FinishCallback;
 5301 
 5302 private:
 5303   class ConnectionRunnable;
 5304   class CloseConnectionRunnable;
 5305   struct DatabaseInfo;
 5306   struct DatabasesCompleteCallback;
 5307   class FinishCallbackWrapper;
 5308   class IdleConnectionRunnable;
 5309   struct IdleDatabaseInfo;
 5310   struct IdleResource;
 5311   struct IdleThreadInfo;
 5312   struct ThreadInfo;
 5313   class ThreadRunnable;
 5314   class TransactionInfo;
 5315   struct TransactionInfoPair;
 5316 
 5317   // This mutex guards mDatabases, see below.
 5318   Mutex mDatabasesMutex;
 5319 
 5320   nsTArray<IdleThreadInfo> mIdleThreads;
 5321   nsTArray<IdleDatabaseInfo> mIdleDatabases;
 5322   nsTArray<DatabaseInfo*> mDatabasesPerformingIdleMaintenance;
 5323   nsCOMPtr<nsITimer> mIdleTimer;
 5324   TimeStamp mTargetIdleTime;
 5325 
 5326   // Only modifed on the owning thread, but read on multiple threads. Therefore
 5327   // all modifications and all reads off the owning thread must be protected by
 5328   // mDatabasesMutex.
 5329   nsClassHashtable<nsCStringHashKey, DatabaseInfo> mDatabases;
 5330 
 5331   nsClassHashtable<nsUint64HashKey, TransactionInfo> mTransactions;
 5332   nsTArray<TransactionInfo*> mQueuedTransactions;
 5333 
 5334   nsTArray<nsAutoPtr<DatabasesCompleteCallback>> mCompleteCallbacks;
 5335 
 5336   uint64_t mNextTransactionId;
 5337   uint32_t mTotalThreadCount;
 5338   bool mShutdownRequested;
 5339   bool mShutdownComplete;
 5340 
 5341 #ifdef DEBUG
 5342   PRThread* mDEBUGOwningThread;
 5343 #endif
 5344 
 5345 public:
 5346   ConnectionPool();
 5347 
 5348   void
 5349   AssertIsOnOwningThread() const
 5350 #ifdef DEBUG
 5351   ;
 5352 #else
 5353   { }
 5354 #endif
 5355 
 5356   nsresult
 5357   GetOrCreateConnection(const Database* aDatabase,
 5358                         DatabaseConnection** aConnection);
 5359 
 5360   uint64_t
 5361   Start(const nsID& aBackgroundChildLoggingId,
 5362         const nsACString& aDatabaseId,
 5363         int64_t aLoggingSerialNumber,
 5364         const nsTArray<nsString>& aObjectStoreNames,
 5365         bool aIsWriteTransaction,
 5366         TransactionDatabaseOperationBase* aTransactionOp);
 5367 
 5368   void
 5369   Dispatch(uint64_t aTransactionId, nsIRunnable* aRunnable);
 5370 
 5371   void
 5372   Finish(uint64_t aTransactionId, FinishCallback* aCallback);
 5373 
 5374   void
 5375   CloseDatabaseWhenIdle(const nsACString& aDatabaseId)
 5376   {
 5377     Unused << CloseDatabaseWhenIdleInternal(aDatabaseId);
 5378   }
 5379 
 5380   void
 5381   WaitForDatabasesToComplete(nsTArray<nsCString>&& aDatabaseIds,
 5382                              nsIRunnable* aCallback);
 5383 
 5384   void
 5385   Shutdown();
 5386 
 5387   NS_INLINE_DECL_REFCOUNTING(ConnectionPool)
 5388 
 5389 private:
 5390   ~ConnectionPool();
 5391 
 5392   static void
 5393   IdleTimerCallback(nsITimer* aTimer, void* aClosure);
 5394 
 5395   void
 5396   Cleanup();
 5397 
 5398   void
 5399   AdjustIdleTimer();
 5400 
 5401   void
 5402   CancelIdleTimer();
 5403 
 5404   void
 5405   ShutdownThread(ThreadInfo& aThreadInfo);
 5406 
 5407   void
 5408   CloseIdleDatabases();
 5409 
 5410   void
 5411   ShutdownIdleThreads();
 5412 
 5413   bool
 5414   ScheduleTransaction(TransactionInfo* aTransactionInfo,
 5415                       bool aFromQueuedTransactions);
 5416 
 5417   void
 5418   NoteFinishedTransaction(uint64_t aTransactionId);
 5419 
 5420   void
 5421   ScheduleQueuedTransactions(ThreadInfo& aThreadInfo);
 5422 
 5423   void
 5424   NoteIdleDatabase(DatabaseInfo* aDatabaseInfo);
 5425 
 5426   void
 5427   NoteClosedDatabase(DatabaseInfo* aDatabaseInfo);
 5428 
 5429   bool
 5430   MaybeFireCallback(DatabasesCompleteCallback* aCallback);
 5431 
 5432   void
 5433   PerformIdleDatabaseMaintenance(DatabaseInfo* aDatabaseInfo);
 5434 
 5435   void
 5436   CloseDatabase(DatabaseInfo* aDatabaseInfo);
 5437 
 5438   bool
 5439   CloseDatabaseWhenIdleInternal(const nsACString& aDatabaseId);
 5440 };
 5441 
 5442 class ConnectionPool::ConnectionRunnable
 5443   : public Runnable
 5444 {
 5445 protected:
 5446   DatabaseInfo* mDatabaseInfo;
 5447   nsCOMPtr<nsIEventTarget> mOwningThread;
 5448 
 5449   explicit
 5450   ConnectionRunnable(DatabaseInfo* aDatabaseInfo);
 5451 
 5452   virtual
 5453   ~ConnectionRunnable()
 5454   { }
 5455 };
 5456 
 5457 class ConnectionPool::IdleConnectionRunnable final
 5458   : public ConnectionRunnable
 5459 {
 5460   bool mNeedsCheckpoint;
 5461 
 5462 public:
 5463   IdleConnectionRunnable(DatabaseInfo* aDatabaseInfo, bool aNeedsCheckpoint)
 5464     : ConnectionRunnable(aDatabaseInfo)
 5465     , mNeedsCheckpoint(aNeedsCheckpoint)
 5466   { }
 5467 
 5468   NS_DECL_ISUPPORTS_INHERITED
 5469 
 5470 private:
 5471   ~IdleConnectionRunnable()
 5472   { }
 5473 
 5474   NS_DECL_NSIRUNNABLE
 5475 };
 5476 
 5477 class ConnectionPool::CloseConnectionRunnable final
 5478   : public ConnectionRunnable
 5479 {
 5480 public:
 5481   explicit
 5482   CloseConnectionRunnable(DatabaseInfo* aDatabaseInfo)
 5483     : ConnectionRunnable(aDatabaseInfo)
 5484   { }
 5485 
 5486   NS_DECL_ISUPPORTS_INHERITED
 5487 
 5488 private:
 5489   ~CloseConnectionRunnable()
 5490   { }
 5491 
 5492   NS_DECL_NSIRUNNABLE
 5493 };
 5494 
 5495 struct ConnectionPool::ThreadInfo
 5496 {
 5497   nsCOMPtr<nsIThread> mThread;
 5498   RefPtr<ThreadRunnable> mRunnable;
 5499 
 5500   ThreadInfo();
 5501 
 5502   explicit
 5503   ThreadInfo(const ThreadInfo& aOther);
 5504 
 5505   ~ThreadInfo();
 5506 };
 5507 
 5508 struct ConnectionPool::DatabaseInfo final
 5509 {
 5510   friend class nsAutoPtr<DatabaseInfo>;
 5511 
 5512   RefPtr<ConnectionPool> mConnectionPool;
 5513   const nsCString mDatabaseId;
 5514   RefPtr<DatabaseConnection> mConnection;
 5515   nsClassHashtable<nsStringHashKey, TransactionInfoPair> mBlockingTransactions;
 5516   nsTArray<TransactionInfo*> mTransactionsScheduledDuringClose;
 5517   nsTArray<TransactionInfo*> mScheduledWriteTransactions;
 5518   TransactionInfo* mRunningWriteTransaction;
 5519   ThreadInfo mThreadInfo;
 5520   uint32_t mReadTransactionCount;
 5521   uint32_t mWriteTransactionCount;
 5522   bool mNeedsCheckpoint;
 5523   bool mIdle;
 5524   bool mCloseOnIdle;
 5525   bool mClosing;
 5526 
 5527 #ifdef DEBUG
 5528   PRThread* mDEBUGConnectionThread;
 5529 #endif
 5530 
 5531   DatabaseInfo(ConnectionPool* aConnectionPool,
 5532                const nsACString& aDatabaseId);
 5533 
 5534   void
 5535   AssertIsOnConnectionThread() const
 5536   {
 5537     MOZ_ASSERT(mDEBUGConnectionThread);
 5538     MOZ_ASSERT(PR_GetCurrentThread() == mDEBUGConnectionThread);
 5539   }
 5540 
 5541   uint64_t
 5542   TotalTransactionCount() const
 5543   {
 5544     return mReadTransactionCount + mWriteTransactionCount;
 5545   }
 5546 
 5547 private:
 5548   ~DatabaseInfo();
 5549 
 5550   DatabaseInfo(const DatabaseInfo&) = delete;
 5551   DatabaseInfo& operator=(const DatabaseInfo&) = delete;
 5552 };
 5553 
 5554 struct ConnectionPool::DatabasesCompleteCallback final
 5555 {
 5556   friend class nsAutoPtr<DatabasesCompleteCallback>;
 5557 
 5558   nsTArray<nsCString> mDatabaseIds;
 5559   nsCOMPtr<nsIRunnable> mCallback;
 5560 
 5561   DatabasesCompleteCallback(nsTArray<nsCString>&& aDatabaseIds,
 5562                             nsIRunnable* aCallback);
 5563 
 5564 private:
 5565   ~DatabasesCompleteCallback();
 5566 };
 5567 
 5568 class NS_NO_VTABLE ConnectionPool::FinishCallback
 5569   : public nsIRunnable
 5570 {
 5571 public:
 5572   // Called on the owning thread before any additional transactions are
 5573   // unblocked.
 5574   virtual void
 5575   TransactionFinishedBeforeUnblock() = 0;
 5576 
 5577   // Called on the owning thread after additional transactions may have been
 5578   // unblocked.
 5579   virtual void
 5580   TransactionFinishedAfterUnblock() = 0;
 5581 
 5582 protected:
 5583   FinishCallback()
 5584   { }
 5585 
 5586   virtual ~FinishCallback()
 5587   { }
 5588 };
 5589 
 5590 class ConnectionPool::FinishCallbackWrapper final
 5591   : public Runnable
 5592 {
 5593   RefPtr<ConnectionPool> mConnectionPool;
 5594   RefPtr<FinishCallback> mCallback;
 5595   nsCOMPtr<nsIEventTarget> mOwningThread;
 5596   uint64_t mTransactionId;
 5597   bool mHasRunOnce;
 5598 
 5599 public:
 5600   FinishCallbackWrapper(ConnectionPool* aConnectionPool,
 5601                         uint64_t aTransactionId,
 5602                         FinishCallback* aCallback);
 5603 
 5604   NS_DECL_ISUPPORTS_INHERITED
 5605 
 5606 private:
 5607   ~FinishCallbackWrapper();
 5608 
 5609   NS_DECL_NSIRUNNABLE
 5610 };
 5611 
 5612 struct ConnectionPool::IdleResource
 5613 {
 5614   TimeStamp mIdleTime;
 5615 
 5616 protected:
 5617   explicit
 5618   IdleResource(const TimeStamp& aIdleTime);
 5619 
 5620   explicit
 5621   IdleResource(const IdleResource& aOther) = delete;
 5622 
 5623   ~IdleResource();
 5624 };
 5625 
 5626 struct ConnectionPool::IdleDatabaseInfo final
 5627   : public IdleResource
 5628 {
 5629   DatabaseInfo* mDatabaseInfo;
 5630 
 5631 public:
 5632   MOZ_IMPLICIT
 5633   IdleDatabaseInfo(DatabaseInfo* aDatabaseInfo);
 5634 
 5635   explicit
 5636   IdleDatabaseInfo(const IdleDatabaseInfo& aOther) = delete;
 5637 
 5638   ~IdleDatabaseInfo();
 5639 
 5640   bool
 5641   operator==(const IdleDatabaseInfo& aOther) const
 5642   {
 5643     return mDatabaseInfo == aOther.mDatabaseInfo;
 5644   }
 5645 
 5646   bool
 5647   operator<(const IdleDatabaseInfo& aOther) const
 5648   {
 5649     return mIdleTime < aOther.mIdleTime;
 5650   }
 5651 };
 5652 
 5653 struct ConnectionPool::IdleThreadInfo final
 5654   : public IdleResource
 5655 {
 5656   ThreadInfo mThreadInfo;
 5657 
 5658 public:
 5659   // Boo, this is needed because nsTArray::InsertElementSorted() doesn't yet
 5660   // work with rvalue references.
 5661   MOZ_IMPLICIT
 5662   IdleThreadInfo(const ThreadInfo& aThreadInfo);
 5663 
 5664   explicit
 5665   IdleThreadInfo(const IdleThreadInfo& aOther) = delete;
 5666 
 5667   ~IdleThreadInfo();
 5668 
 5669   bool
 5670   operator==(const IdleThreadInfo& aOther) const
 5671   {
 5672     return mThreadInfo.mRunnable == aOther.mThreadInfo.mRunnable &&
 5673            mThreadInfo.mThread == aOther.mThreadInfo.mThread;
 5674   }
 5675 
 5676   bool
 5677   operator<(const IdleThreadInfo& aOther) const
 5678   {
 5679     return mIdleTime < aOther.mIdleTime;
 5680   }
 5681 };
 5682 
 5683 class ConnectionPool::ThreadRunnable final
 5684   : public Runnable
 5685 {
 5686   // Only touched on the background thread.
 5687   static uint32_t sNextSerialNumber;
 5688 
 5689   // Set at construction for logging.
 5690   const uint32_t mSerialNumber;
 5691 
 5692   // These two values are only modified on the connection thread.
 5693   bool mFirstRun;
 5694   bool mContinueRunning;
 5695 
 5696 public:
 5697   ThreadRunnable();
 5698 
 5699   NS_DECL_ISUPPORTS_INHERITED
 5700 
 5701   uint32_t
 5702   SerialNumber() const
 5703   {
 5704     return mSerialNumber;
 5705   }
 5706 
 5707 private:
 5708   ~ThreadRunnable();
 5709 
 5710   NS_DECL_NSIRUNNABLE
 5711 };
 5712 
 5713 class ConnectionPool::TransactionInfo final
 5714 {
 5715   friend class nsAutoPtr<TransactionInfo>;
 5716 
 5717   nsTHashtable<nsPtrHashKey<TransactionInfo>> mBlocking;
 5718   nsTArray<TransactionInfo*> mBlockingOrdered;
 5719 
 5720 public:
 5721   DatabaseInfo* mDatabaseInfo;
 5722   const nsID mBackgroundChildLoggingId;
 5723   const nsCString mDatabaseId;
 5724   const uint64_t mTransactionId;
 5725   const int64_t mLoggingSerialNumber;
 5726   const nsTArray<nsString> mObjectStoreNames;
 5727   nsTHashtable<nsPtrHashKey<TransactionInfo>> mBlockedOn;
 5728   nsTArray<nsCOMPtr<nsIRunnable>> mQueuedRunnables;
 5729   const bool mIsWriteTransaction;
 5730   bool mRunning;
 5731 
 5732 #ifdef DEBUG
 5733   bool mFinished;
 5734 #endif
 5735 
 5736   TransactionInfo(DatabaseInfo* aDatabaseInfo,
 5737                   const nsID& aBackgroundChildLoggingId,
 5738                   const nsACString& aDatabaseId,
 5739                   uint64_t aTransactionId,
 5740                   int64_t aLoggingSerialNumber,
 5741                   const nsTArray<nsString>& aObjectStoreNames,
 5742                   bool aIsWriteTransaction,
 5743                   TransactionDatabaseOperationBase* aTransactionOp);
 5744 
 5745   void
 5746   AddBlockingTransaction(TransactionInfo* aTransactionInfo);
 5747 
 5748   void
 5749   RemoveBlockingTransactions();
 5750 
 5751 private:
 5752   ~TransactionInfo();
 5753 
 5754   void
 5755   MaybeUnblock(TransactionInfo* aTransactionInfo);
 5756 };
 5757 
 5758 struct ConnectionPool::TransactionInfoPair final
 5759 {
 5760   friend class nsAutoPtr<TransactionInfoPair>;
 5761 
 5762   // Multiple reading transactions can block future writes.
 5763   nsTArray<TransactionInfo*> mLastBlockingWrites;
 5764   // But only a single writing transaction can block future reads.
 5765   TransactionInfo* mLastBlockingReads;
 5766 
 5767   TransactionInfoPair();
 5768 
 5769 private:
 5770   ~TransactionInfoPair();
 5771 };
 5772 
 5773 /*******************************************************************************
 5774  * Actor class declarations
 5775  ******************************************************************************/
 5776 
 5777 class DatabaseOperationBase
 5778   : public Runnable
 5779   , public mozIStorageProgressHandler
 5780 {
 5781   friend class UpgradeFileIdsFunction;
 5782 
 5783 protected:
 5784   class AutoSetProgressHandler;
 5785 
 5786   typedef nsDataHashtable<nsUint64HashKey, bool> UniqueIndexTable;
 5787 
 5788   nsCOMPtr<nsIEventTarget> mOwningThread;
 5789   const nsID mBackgroundChildLoggingId;
 5790   const uint64_t mLoggingSerialNumber;
 5791   nsresult mResultCode;
 5792 
 5793 private:
 5794   Atomic<bool> mOperationMayProceed;
 5795   bool mActorDestroyed;
 5796 
 5797 public:
 5798   NS_DECL_ISUPPORTS_INHERITED
 5799 
 5800   bool
 5801   IsOnOwningThread() const
 5802   {
 5803     MOZ_ASSERT(mOwningThread);
 5804 
 5805     bool current;
 5806     return NS_SUCCEEDED(mOwningThread->IsOnCurrentThread(&current)) && current;
 5807   }
 5808 
 5809   void
 5810   AssertIsOnOwningThread() const
 5811   {
 5812     MOZ_ASSERT(IsOnBackgroundThread());
 5813     MOZ_ASSERT(IsOnOwningThread());
 5814   }
 5815 
 5816   void
 5817   NoteActorDestroyed()
 5818   {
 5819     AssertIsOnOwningThread();
 5820 
 5821     mActorDestroyed = true;
 5822     mOperationMayProceed = false;
 5823   }
 5824 
 5825   bool
 5826   IsActorDestroyed() const
 5827   {
 5828     AssertIsOnOwningThread();
 5829 
 5830     return mActorDestroyed;
 5831   }
 5832 
 5833   // May be called on any thread, but you should call IsActorDestroyed() if
 5834   // you know you're on the background thread because it is slightly faster.
 5835   bool
 5836   OperationMayProceed() const
 5837   {
 5838     return mOperationMayProceed;
 5839   }
 5840 
 5841   const nsID&
 5842   BackgroundChildLoggingId() const
 5843   {
 5844     return mBackgroundChildLoggingId;
 5845   }
 5846 
 5847   uint64_t
 5848   LoggingSerialNumber() const
 5849   {
 5850     return mLoggingSerialNumber;
 5851   }
 5852 
 5853   nsresult
 5854   ResultCode() const
 5855   {
 5856     return mResultCode;
 5857   }
 5858 
 5859   void
 5860   SetFailureCode(nsresult aErrorCode)
 5861   {
 5862     MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
 5863     MOZ_ASSERT(NS_FAILED(aErrorCode));
 5864 
 5865     mResultCode = aErrorCode;
 5866   }
 5867 
 5868 protected:
 5869   DatabaseOperationBase(const nsID& aBackgroundChildLoggingId,
 5870                         uint64_t aLoggingSerialNumber)
 5871     : mOwningThread(NS_GetCurrentThread())
 5872     , mBackgroundChildLoggingId(aBackgroundChildLoggingId)
 5873     , mLoggingSerialNumber(aLoggingSerialNumber)
 5874     , mResultCode(NS_OK)
 5875     , mOperationMayProceed(true)
 5876     , mActorDestroyed(false)
 5877   {
 5878     AssertIsOnOwningThread();
 5879   }
 5880 
 5881   virtual
 5882   ~DatabaseOperationBase()
 5883   {
 5884     MOZ_ASSERT(mActorDestroyed);
 5885   }
 5886 
 5887   static void
 5888   GetBindingClauseForKeyRange(const SerializedKeyRange& aKeyRange,
 5889                               const nsACString& aKeyColumnName,
 5890                               nsAutoCString& aBindingClause);
 5891 
 5892   static uint64_t
 5893   ReinterpretDoubleAsUInt64(double aDouble);
 5894 
 5895   static nsresult
 5896   GetStructuredCloneReadInfoFromStatement(mozIStorageStatement* aStatement,
 5897                                           uint32_t aDataIndex,
 5898                                           uint32_t aFileIdsIndex,
 5899                                           FileManager* aFileManager,
 5900                                           StructuredCloneReadInfo* aInfo)
 5901   {
 5902     return GetStructuredCloneReadInfoFromSource(aStatement,
 5903                                                 aDataIndex,
 5904                                                 aFileIdsIndex,
 5905                                                 aFileManager,
 5906                                                 aInfo);
 5907   }
 5908 
 5909   static nsresult
 5910   GetStructuredCloneReadInfoFromValueArray(mozIStorageValueArray* aValues,
 5911                                            uint32_t aDataIndex,
 5912                                            uint32_t aFileIdsIndex,
 5913                                            FileManager* aFileManager,
 5914                                            StructuredCloneReadInfo* aInfo)
 5915   {
 5916     return GetStructuredCloneReadInfoFromSource(aValues,
 5917                                                 aDataIndex,
 5918                                                 aFileIdsIndex,
 5919                                                 aFileManager,
 5920                                                 aInfo);
 5921   }
 5922 
 5923   static nsresult
 5924   BindKeyRangeToStatement(const SerializedKeyRange& aKeyRange,
 5925                           mozIStorageStatement* aStatement);
 5926 
 5927   static nsresult
 5928   BindKeyRangeToStatement(const SerializedKeyRange& aKeyRange,
 5929                           mozIStorageStatement* aStatement,
 5930                           const nsCString& aLocale);
 5931 
 5932   static void
 5933   AppendConditionClause(const nsACString& aColumnName,
 5934                         const nsACString& aArgName,
 5935                         bool aLessThan,
 5936                         bool aEquals,
 5937                         nsAutoCString& aResult);
 5938 
 5939   static nsresult
 5940   GetUniqueIndexTableForObjectStore(
 5941                                TransactionBase* aTransaction,
 5942                                int64_t aObjectStoreId,
 5943                                Maybe<UniqueIndexTable>& aMaybeUniqueIndexTable);
 5944 
 5945   static nsresult
 5946   IndexDataValuesFromUpdateInfos(const nsTArray<IndexUpdateInfo>& aUpdateInfos,
 5947                                  const UniqueIndexTable& aUniqueIndexTable,
 5948                                  nsTArray<IndexDataValue>& aIndexValues);
 5949 
 5950   static nsresult
 5951   InsertIndexTableRows(DatabaseConnection* aConnection,
 5952                        const int64_t aObjectStoreId,
 5953                        const Key& aObjectStoreKey,
 5954                        const FallibleTArray<IndexDataValue>& aIndexValues);
 5955 
 5956   static nsresult
 5957   DeleteIndexDataTableRows(DatabaseConnection* aConnection,
 5958                            const Key& aObjectStoreKey,
 5959                            const FallibleTArray<IndexDataValue>& aIndexValues);
 5960 
 5961   static nsresult
 5962   DeleteObjectStoreDataTableRowsWithIndexes(DatabaseConnection* aConnection,
 5963                                             const int64_t aObjectStoreId,
 5964                                             const OptionalKeyRange& aKeyRange);
 5965 
 5966   static nsresult
 5967   UpdateIndexValues(DatabaseConnection* aConnection,
 5968                     const int64_t aObjectStoreId,
 5969                     const Key& aObjectStoreKey,
 5970                     const FallibleTArray<IndexDataValue>& aIndexValues);
 5971 
 5972   static nsresult
 5973   ObjectStoreHasIndexes(DatabaseConnection* aConnection,
 5974                         const int64_t aObjectStoreId,
 5975                         bool* aHasIndexes);
 5976 
 5977 private:
 5978   template <typename T>
 5979   static nsresult
 5980   GetStructuredCloneReadInfoFromSource(T* aSource,
 5981                                        uint32_t aDataIndex,
 5982                                        uint32_t aFileIdsIndex,
 5983                                        FileManager* aFileManager,
 5984                                        StructuredCloneReadInfo* aInfo);
 5985 
 5986   static nsresult
 5987   GetStructuredCloneReadInfoFromBlob(const uint8_t* aBlobData,
 5988                                      uint32_t aBlobDataLength,
 5989                                      FileManager* aFileManager,
 5990                                      const nsAString& aFileIds,
 5991                                      StructuredCloneReadInfo* aInfo);
 5992 
 5993   static nsresult
 5994   GetStructuredCloneReadInfoFromExternalBlob(uint64_t aIntData,
 5995                                              FileManager* aFileManager,
 5996                                              const nsAString& aFileIds,
 5997                                              StructuredCloneReadInfo* aInfo);
 5998 
 5999   // Not to be overridden by subclasses.
 6000   NS_DECL_MOZISTORAGEPROGRESSHANDLER
 6001 };
 6002 
 6003 class MOZ_STACK_CLASS DatabaseOperationBase::AutoSetProgressHandler final
 6004 {
 6005   mozIStorageConnection* mConnection;
 6006 #ifdef DEBUG
 6007   DatabaseOperationBase* mDEBUGDatabaseOp;
 6008 #endif
 6009 
 6010 public:
 6011   AutoSetProgressHandler();
 6012 
 6013   ~AutoSetProgressHandler();
 6014 
 6015   nsresult
 6016   Register(mozIStorageConnection* aConnection,
 6017            DatabaseOperationBase* aDatabaseOp);
 6018 };
 6019 
 6020 class TransactionDatabaseOperationBase
 6021   : public DatabaseOperationBase
 6022 {
 6023   enum class InternalState
 6024   {
 6025     Initial,
 6026     DatabaseWork,
 6027     SendingPreprocess,
 6028     WaitingForContinue,
 6029     SendingResults,
 6030     Completed
 6031   };
 6032 
 6033   RefPtr<TransactionBase> mTransaction;
 6034   const int64_t mTransactionLoggingSerialNumber;
 6035   InternalState mInternalState;
 6036   const bool mTransactionIsAborted;
 6037 
 6038 public:
 6039   void
 6040   AssertIsOnConnectionThread() const
 6041 #ifdef DEBUG
 6042   ;
 6043 #else
 6044   { }
 6045 #endif
 6046 
 6047   uint64_t
 6048   StartOnConnectionPool(const nsID& aBackgroundChildLoggingId,
 6049                         const nsACString& aDatabaseId,
 6050                         int64_t aLoggingSerialNumber,
 6051                         const nsTArray<nsString>& aObjectStoreNames,
 6052                         bool aIsWriteTransaction