tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ActorsParent.cpp (707023B)


      1 /* -*- Mode: C++; tab-width: 8; 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 <inttypes.h>
     10 #include <math.h>
     11 #include <stdlib.h>
     12 #include <string.h>
     13 
     14 #include <algorithm>
     15 #include <cstdint>
     16 #include <functional>
     17 #include <iterator>
     18 #include <new>
     19 #include <numeric>
     20 #include <type_traits>
     21 #include <utility>
     22 
     23 #include "ActorsParentCommon.h"
     24 #include "CrashAnnotations.h"
     25 #include "DBSchema.h"
     26 #include "DatabaseFileInfo.h"
     27 #include "DatabaseFileManager.h"
     28 #include "DatabaseFileManagerImpl.h"
     29 #include "ErrorList.h"
     30 #include "IDBCursorType.h"
     31 #include "IDBObjectStore.h"
     32 #include "IDBTransaction.h"
     33 #include "IndexedDBCipherKeyManager.h"
     34 #include "IndexedDBCommon.h"
     35 #include "IndexedDatabaseInlines.h"
     36 #include "IndexedDatabaseManager.h"
     37 #include "KeyPath.h"
     38 #include "MainThreadUtils.h"
     39 #include "NotifyUtils.h"
     40 #include "ProfilerHelpers.h"
     41 #include "ReportInternalError.h"
     42 #include "SafeRefPtr.h"
     43 #include "SchemaUpgrades.h"
     44 #include "chrome/common/ipc_channel.h"
     45 #include "ipc/IPCMessageUtils.h"
     46 #include "js/RootingAPI.h"
     47 #include "js/StructuredClone.h"
     48 #include "js/Value.h"
     49 #include "jsapi.h"
     50 #include "mozIStorageAsyncConnection.h"
     51 #include "mozIStorageConnection.h"
     52 #include "mozIStorageFunction.h"
     53 #include "mozIStorageProgressHandler.h"
     54 #include "mozIStorageService.h"
     55 #include "mozIStorageStatement.h"
     56 #include "mozIStorageValueArray.h"
     57 #include "mozStorageCID.h"
     58 #include "mozStorageHelper.h"
     59 #include "mozilla/Algorithm.h"
     60 #include "mozilla/ArrayAlgorithm.h"
     61 #include "mozilla/ArrayIterator.h"
     62 #include "mozilla/Assertions.h"
     63 #include "mozilla/Atomics.h"
     64 #include "mozilla/Attributes.h"
     65 #include "mozilla/Casting.h"
     66 #include "mozilla/CondVar.h"
     67 #include "mozilla/DebugOnly.h"
     68 #include "mozilla/EndianUtils.h"
     69 #include "mozilla/ErrorNames.h"
     70 #include "mozilla/ErrorResult.h"
     71 #include "mozilla/GeckoTrace.h"
     72 #include "mozilla/InitializedOnce.h"
     73 #include "mozilla/Logging.h"
     74 #include "mozilla/Maybe.h"
     75 #include "mozilla/Monitor.h"
     76 #include "mozilla/Mutex.h"
     77 #include "mozilla/NotNull.h"
     78 #include "mozilla/Preferences.h"
     79 #include "mozilla/ProfilerLabels.h"
     80 #include "mozilla/RefCountType.h"
     81 #include "mozilla/RefCounted.h"
     82 #include "mozilla/RemoteLazyInputStreamParent.h"
     83 #include "mozilla/RemoteLazyInputStreamStorage.h"
     84 #include "mozilla/Result.h"
     85 #include "mozilla/ResultExtensions.h"
     86 #include "mozilla/SchedulerGroup.h"
     87 #include "mozilla/SnappyCompressOutputStream.h"
     88 #include "mozilla/SpinEventLoopUntil.h"
     89 #include "mozilla/StaticPtr.h"
     90 #include "mozilla/TimeStamp.h"
     91 #include "mozilla/UniquePtr.h"
     92 #include "mozilla/Variant.h"
     93 #include "mozilla/dom/BlobImpl.h"
     94 #include "mozilla/dom/ContentParent.h"
     95 #include "mozilla/dom/FileBlobImpl.h"
     96 #include "mozilla/dom/FlippedOnce.h"
     97 #include "mozilla/dom/IDBCursorBinding.h"
     98 #include "mozilla/dom/IDBFactory.h"
     99 #include "mozilla/dom/IPCBlob.h"
    100 #include "mozilla/dom/IPCBlobUtils.h"
    101 #include "mozilla/dom/IndexedDatabase.h"
    102 #include "mozilla/dom/Nullable.h"
    103 #include "mozilla/dom/PContentParent.h"
    104 #include "mozilla/dom/ScriptSettings.h"
    105 #include "mozilla/dom/indexedDB/IDBResult.h"
    106 #include "mozilla/dom/indexedDB/Key.h"
    107 #include "mozilla/dom/indexedDB/PBackgroundIDBCursor.h"
    108 #include "mozilla/dom/indexedDB/PBackgroundIDBCursorParent.h"
    109 #include "mozilla/dom/indexedDB/PBackgroundIDBDatabase.h"
    110 #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseFileParent.h"
    111 #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseParent.h"
    112 #include "mozilla/dom/indexedDB/PBackgroundIDBFactory.h"
    113 #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryParent.h"
    114 #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryRequestParent.h"
    115 #include "mozilla/dom/indexedDB/PBackgroundIDBRequest.h"
    116 #include "mozilla/dom/indexedDB/PBackgroundIDBRequestParent.h"
    117 #include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h"
    118 #include "mozilla/dom/indexedDB/PBackgroundIDBTransactionParent.h"
    119 #include "mozilla/dom/indexedDB/PBackgroundIDBVersionChangeTransactionParent.h"
    120 #include "mozilla/dom/indexedDB/PBackgroundIndexedDBUtilsParent.h"
    121 #include "mozilla/dom/ipc/IdType.h"
    122 #include "mozilla/dom/quota/Assertions.h"
    123 #include "mozilla/dom/quota/CachingDatabaseConnection.h"
    124 #include "mozilla/dom/quota/Client.h"
    125 #include "mozilla/dom/quota/ClientDirectoryLock.h"
    126 #include "mozilla/dom/quota/ClientDirectoryLockHandle.h"
    127 #include "mozilla/dom/quota/ClientImpl.h"
    128 #include "mozilla/dom/quota/ConditionalCompilation.h"
    129 #include "mozilla/dom/quota/Date.h"
    130 #include "mozilla/dom/quota/DecryptingInputStream_impl.h"
    131 #include "mozilla/dom/quota/DirectoryLock.h"
    132 #include "mozilla/dom/quota/DirectoryLockInlines.h"
    133 #include "mozilla/dom/quota/DirectoryMetadata.h"
    134 #include "mozilla/dom/quota/EncryptingOutputStream_impl.h"
    135 #include "mozilla/dom/quota/ErrorHandling.h"
    136 #include "mozilla/dom/quota/FileStreams.h"
    137 #include "mozilla/dom/quota/OriginScope.h"
    138 #include "mozilla/dom/quota/PersistenceScope.h"
    139 #include "mozilla/dom/quota/PersistenceType.h"
    140 #include "mozilla/dom/quota/PrincipalUtils.h"
    141 #include "mozilla/dom/quota/QuotaCommon.h"
    142 #include "mozilla/dom/quota/QuotaManager.h"
    143 #include "mozilla/dom/quota/QuotaObject.h"
    144 #include "mozilla/dom/quota/ResultExtensions.h"
    145 #include "mozilla/dom/quota/ThreadUtils.h"
    146 #include "mozilla/dom/quota/UniversalDirectoryLock.h"
    147 #include "mozilla/dom/quota/UsageInfo.h"
    148 #include "mozilla/fallible.h"
    149 #include "mozilla/glean/DomIndexedDBMetrics.h"
    150 #include "mozilla/ipc/BackgroundParent.h"
    151 #include "mozilla/ipc/BackgroundUtils.h"
    152 #include "mozilla/ipc/InputStreamParams.h"
    153 #include "mozilla/ipc/PBackgroundParent.h"
    154 #include "mozilla/ipc/PBackgroundSharedTypes.h"
    155 #include "mozilla/ipc/ProtocolUtils.h"
    156 #include "mozilla/mozalloc.h"
    157 #include "mozilla/storage/Variant.h"
    158 #include "nsBaseHashtable.h"
    159 #include "nsCOMPtr.h"
    160 #include "nsClassHashtable.h"
    161 #include "nsContentUtils.h"
    162 #include "nsDebug.h"
    163 #include "nsError.h"
    164 #include "nsEscape.h"
    165 #include "nsHashKeys.h"
    166 #include "nsIAsyncInputStream.h"
    167 #include "nsID.h"
    168 #include "nsIDUtils.h"
    169 #include "nsIDirectoryEnumerator.h"
    170 #include "nsIEventTarget.h"
    171 #include "nsIFile.h"
    172 #include "nsIFileProtocolHandler.h"
    173 #include "nsIFileStreams.h"
    174 #include "nsIFileURL.h"
    175 #include "nsIInputStream.h"
    176 #include "nsIOutputStream.h"
    177 #include "nsIProtocolHandler.h"
    178 #include "nsIRunnable.h"
    179 #include "nsISupports.h"
    180 #include "nsISupportsPriority.h"
    181 #include "nsISupportsUtils.h"
    182 #include "nsIThread.h"
    183 #include "nsIThreadInternal.h"
    184 #include "nsITimer.h"
    185 #include "nsIURIMutator.h"
    186 #include "nsIVariant.h"
    187 #include "nsLiteralString.h"
    188 #include "nsNetCID.h"
    189 #include "nsPrintfCString.h"
    190 #include "nsProxyRelease.h"
    191 #include "nsServiceManagerUtils.h"
    192 #include "nsStreamUtils.h"
    193 #include "nsString.h"
    194 #include "nsStringFlags.h"
    195 #include "nsStringFwd.h"
    196 #include "nsTArray.h"
    197 #include "nsTHashMap.h"
    198 #include "nsTHashSet.h"
    199 #include "nsTHashtable.h"
    200 #include "nsTLiteralString.h"
    201 #include "nsTStringRepr.h"
    202 #include "nsThreadPool.h"
    203 #include "nsThreadUtils.h"
    204 #include "nscore.h"
    205 #include "prinrval.h"
    206 #include "prio.h"
    207 #include "prsystem.h"
    208 #include "prthread.h"
    209 #include "prtime.h"
    210 #include "prtypes.h"
    211 #include "snappy/snappy.h"
    212 
    213 struct JSContext;
    214 class JSObject;
    215 template <class T>
    216 class nsPtrHashKey;
    217 
    218 #define IDB_DEBUG_LOG(_args) \
    219  MOZ_LOG(IndexedDatabaseManager::GetLoggingModule(), LogLevel::Debug, _args)
    220 
    221 #if defined(MOZ_WIDGET_ANDROID)
    222 #  define IDB_MOBILE
    223 #endif
    224 
    225 // Helper macros to reduce assertion verbosity
    226 // AUUF == ASSERT_UNREACHABLE_UNLESS_FUZZING
    227 #ifdef DEBUG
    228 #  ifdef FUZZING
    229 #    define NS_AUUF_OR_WARN(...) NS_WARNING(__VA_ARGS__)
    230 #  else
    231 #    define NS_AUUF_OR_WARN(...) MOZ_ASSERT(false, __VA_ARGS__)
    232 #  endif
    233 #  define NS_AUUF_OR_WARN_IF(cond) \
    234    [](bool aCond) {               \
    235      if (MOZ_UNLIKELY(aCond)) {   \
    236        NS_AUUF_OR_WARN(#cond);    \
    237      }                            \
    238      return aCond;                \
    239    }((cond))
    240 #else
    241 #  define NS_AUUF_OR_WARN(...) \
    242    do {                       \
    243    } while (false)
    244 #  define NS_AUUF_OR_WARN_IF(cond) static_cast<bool>(cond)
    245 #endif
    246 
    247 namespace mozilla {
    248 
    249 namespace dom::indexedDB {
    250 
    251 using namespace mozilla::dom::quota;
    252 using namespace mozilla::ipc;
    253 using mozilla::dom::quota::Client;
    254 
    255 namespace {
    256 
    257 class ConnectionPool;
    258 class Database;
    259 struct DatabaseActorInfo;
    260 class DatabaseFile;
    261 class DatabaseLoggingInfo;
    262 class DatabaseMaintenance;
    263 class Factory;
    264 class Maintenance;
    265 class OpenDatabaseOp;
    266 class TransactionBase;
    267 class TransactionDatabaseOperationBase;
    268 class VersionChangeTransaction;
    269 template <bool StatementHasIndexKeyBindings>
    270 struct ValuePopulateResponseHelper;
    271 
    272 /*******************************************************************************
    273 * Constants
    274 ******************************************************************************/
    275 
    276 const int32_t kStorageProgressGranularity = 1000;
    277 
    278 // Changing the value here will override the page size of new databases only.
    279 // A journal mode change and VACUUM are needed to change existing databases, so
    280 // the best way to do that is to use the schema version upgrade mechanism.
    281 const uint32_t kSQLitePageSizeOverride =
    282 #ifdef IDB_MOBILE
    283    2048;
    284 #else
    285    4096;
    286 #endif
    287 
    288 static_assert(kSQLitePageSizeOverride == /* mozStorage default */ 0 ||
    289                  (kSQLitePageSizeOverride % 2 == 0 &&
    290                   kSQLitePageSizeOverride >= 512 &&
    291                   kSQLitePageSizeOverride <= 65536),
    292              "Must be 0 (disabled) or a power of 2 between 512 and 65536!");
    293 
    294 // Set to -1 to use SQLite's default, 0 to disable, or some positive number to
    295 // enforce a custom limit.
    296 const int32_t kMaxWALPages = 5000;  // 20MB on desktop, 10MB on mobile.
    297 
    298 // Set to some multiple of the page size to grow the database in larger chunks.
    299 const uint32_t kSQLiteGrowthIncrement = kSQLitePageSizeOverride * 2;
    300 
    301 static_assert(kSQLiteGrowthIncrement >= 0 &&
    302                  kSQLiteGrowthIncrement % kSQLitePageSizeOverride == 0 &&
    303                  kSQLiteGrowthIncrement < uint32_t(INT32_MAX),
    304              "Must be 0 (disabled) or a positive multiple of the page size!");
    305 
    306 // The maximum number of threads that can be used for database activity at a
    307 // single time. Please keep in sync with the constants in
    308 // test_connection_idle_maintenance*.js tests
    309 const uint32_t kMaxConnectionThreadCount = 20;
    310 
    311 static_assert(kMaxConnectionThreadCount, "Must have at least one thread!");
    312 
    313 // The maximum number of threads to keep when idle. Until we switch to the STS
    314 // pool, we can reduce the number of idle threads kept around thanks to the
    315 // grace timeout.
    316 const uint32_t kMaxIdleConnectionThreadCount = 1;
    317 
    318 static_assert(kMaxConnectionThreadCount >= kMaxIdleConnectionThreadCount,
    319              "Idle thread limit must be less than total thread limit!");
    320 
    321 // The length of time that wanted idle threads will stay alive before being shut
    322 // down.
    323 const uint32_t kConnectionThreadMaxIdleMS = 30 * 1000;  // 30 seconds
    324 
    325 // The length of time that excess idle threads will stay alive before being shut
    326 // down.
    327 const uint32_t kConnectionThreadGraceIdleMS = 500;  // 0.5 seconds
    328 
    329 // The length of time that database connections will be held open after all
    330 // transactions have completed before doing idle maintenance. Please keep in
    331 // sync with the timeouts in test_connection_idle_maintenance*.js tests
    332 const uint32_t kConnectionIdleMaintenanceMS = 2 * 1000;  // 2 seconds
    333 
    334 // The length of time that database connections will be held open after all
    335 // transactions and maintenance have completed.
    336 const uint32_t kConnectionIdleCloseMS = 10 * 1000;  // 10 seconds
    337 
    338 #define SAVEPOINT_CLAUSE "SAVEPOINT sp;"_ns
    339 
    340 // For efficiency reasons, kEncryptedStreamBlockSize must be a multiple of large
    341 // 4k disk sectors.
    342 static_assert(kEncryptedStreamBlockSize % 4096 == 0);
    343 // Similarly, the file copy buffer size must be a multiple of the encrypted
    344 // block size.
    345 static_assert(kFileCopyBufferSize % kEncryptedStreamBlockSize == 0);
    346 
    347 constexpr auto kFileManagerDirectoryNameSuffix = u".files"_ns;
    348 constexpr auto kSQLiteSuffix = u".sqlite"_ns;
    349 constexpr auto kSQLiteJournalSuffix = u".sqlite-journal"_ns;
    350 constexpr auto kSQLiteSHMSuffix = u".sqlite-shm"_ns;
    351 constexpr auto kSQLiteWALSuffix = u".sqlite-wal"_ns;
    352 
    353 // The following constants define all names of binding parameters in statements,
    354 // where they are bound by name. This should include all parameter names which
    355 // are bound by name. Binding may be done by index when the statement definition
    356 // and binding are done in the same local scope, and no other reasons prevent
    357 // using the indexes (e.g. multiple statement variants with differing number or
    358 // order of parameters). Neither the styles of specifying parameter names
    359 // (literally vs. via these constants) nor the binding styles (by index vs. by
    360 // name) should not be mixed for the same statement. The decision must be made
    361 // for each statement based on the proximity of statement and binding calls.
    362 constexpr auto kStmtParamNameCurrentKey = "current_key"_ns;
    363 constexpr auto kStmtParamNameRangeBound = "range_bound"_ns;
    364 constexpr auto kStmtParamNameObjectStorePosition = "object_store_position"_ns;
    365 constexpr auto kStmtParamNameLowerKey = "lower_key"_ns;
    366 constexpr auto kStmtParamNameUpperKey = "upper_key"_ns;
    367 constexpr auto kStmtParamNameKey = "key"_ns;
    368 constexpr auto kStmtParamNameObjectStoreId = "object_store_id"_ns;
    369 constexpr auto kStmtParamNameIndexId = "index_id"_ns;
    370 // TODO: Maybe the uses of kStmtParamNameId should be replaced by more
    371 // specific constants such as kStmtParamNameObjectStoreId.
    372 constexpr auto kStmtParamNameId = "id"_ns;
    373 constexpr auto kStmtParamNameValue = "value"_ns;
    374 constexpr auto kStmtParamNameObjectDataKey = "object_data_key"_ns;
    375 constexpr auto kStmtParamNameIndexDataValues = "index_data_values"_ns;
    376 constexpr auto kStmtParamNameData = "data"_ns;
    377 constexpr auto kStmtParamNameFileIds = "file_ids"_ns;
    378 constexpr auto kStmtParamNameValueLocale = "value_locale"_ns;
    379 constexpr auto kStmtParamNameLimit = "limit"_ns;
    380 
    381 // The following constants define some names of columns in tables, which are
    382 // referred to in remote locations, e.g. in calls to
    383 // GetBindingClauseForKeyRange.
    384 constexpr auto kColumnNameKey = "key"_ns;
    385 constexpr auto kColumnNameValue = "value"_ns;
    386 constexpr auto kColumnNameAliasSortKey = "sort_column"_ns;
    387 
    388 // SQL fragments used at multiple locations.
    389 constexpr auto kOpenLimit = " LIMIT "_ns;
    390 
    391 // The deletion marker file is created before RemoveDatabaseFilesAndDirectory
    392 // begins deleting a database. It is removed as the last step of deletion. If a
    393 // deletion marker file is found when initializing the origin, the deletion
    394 // routine is run again to ensure that the database and all of its related files
    395 // are removed. The primary goal of this mechanism is to avoid situations where
    396 // a database has been partially deleted, leading to inconsistent state for the
    397 // origin.
    398 constexpr auto kIdbDeletionMarkerFilePrefix = u"idb-deleting-"_ns;
    399 
    400 const uint32_t kDeleteTimeoutMs = 1000;
    401 
    402 #ifdef DEBUG
    403 
    404 const int32_t kDEBUGThreadPriority = nsISupportsPriority::PRIORITY_NORMAL;
    405 const uint32_t kDEBUGThreadSleepMS = 0;
    406 
    407 // Set to a non-zero number to enable debugging of transaction event targets.
    408 // It will cause sleeping after every transaction runnable!
    409 //
    410 // This can be useful for discovering race conditions related to switching to
    411 // another thread. Such races are usually avoided by using MozPromise or
    412 // RunAfterProcessingCurrentEvent. Chaos mode doesn't always help with
    413 // uncovering these issues, and only a precisely targeted sleep call can
    414 // simulate the problem.
    415 const uint32_t kDEBUGTransactionThreadSleepMS = 0;
    416 
    417 // Make sure that we notice if we ever accidentally check in a non-zero value.
    418 #  ifdef MOZILLA_OFFICIAL
    419 static_assert(kDEBUGTransactionThreadSleepMS == 0);
    420 #  endif
    421 
    422 #endif
    423 
    424 /*******************************************************************************
    425 * Metadata classes
    426 ******************************************************************************/
    427 
    428 // Can be instantiated either on the QuotaManager IO thread or on a
    429 // versionchange transaction thread. These threads can never race so this is
    430 // totally safe.
    431 struct FullIndexMetadata {
    432  IndexMetadata mCommonMetadata = {0,     nsString(), KeyPath(0), nsCString(),
    433                                   false, false,      false};
    434 
    435  FlippedOnce<false> mDeleted;
    436 
    437  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FullIndexMetadata)
    438 
    439 private:
    440  ~FullIndexMetadata() = default;
    441 };
    442 
    443 using IndexTable = nsTHashMap<nsUint64HashKey, SafeRefPtr<FullIndexMetadata>>;
    444 
    445 // Can be instantiated either on the QuotaManager IO thread or on a
    446 // versionchange transaction thread. These threads can never race so this is
    447 // totally safe.
    448 struct FullObjectStoreMetadata {
    449  ObjectStoreMetadata mCommonMetadata;
    450  IndexTable mIndexes;
    451 
    452  // The auto increment ids are touched on both the background thread and the
    453  // transaction I/O thread, and they must be kept in sync, so we need a mutex
    454  // to protect them.
    455  struct AutoIncrementIds {
    456    int64_t next;
    457    int64_t committed;
    458  };
    459  DataMutex<AutoIncrementIds> mAutoIncrementIds;
    460 
    461  FlippedOnce<false> mDeleted;
    462 
    463  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FullObjectStoreMetadata);
    464 
    465  bool HasLiveIndexes() const;
    466 
    467  FullObjectStoreMetadata(ObjectStoreMetadata aCommonMetadata,
    468                          const AutoIncrementIds& aAutoIncrementIds)
    469      : mCommonMetadata{std::move(aCommonMetadata)},
    470        mAutoIncrementIds{AutoIncrementIds{aAutoIncrementIds},
    471                          "FullObjectStoreMetadata"} {}
    472 
    473 private:
    474  ~FullObjectStoreMetadata() = default;
    475 };
    476 
    477 using ObjectStoreTable =
    478    nsTHashMap<nsUint64HashKey, SafeRefPtr<FullObjectStoreMetadata>>;
    479 
    480 static_assert(
    481    std::is_same_v<IndexOrObjectStoreId,
    482                   std::remove_cv_t<std::remove_reference_t<
    483                       decltype(std::declval<const ObjectStoreGetParams&>()
    484                                    .objectStoreId())>>>);
    485 static_assert(
    486    std::is_same_v<
    487        IndexOrObjectStoreId,
    488        std::remove_cv_t<std::remove_reference_t<
    489            decltype(std::declval<const IndexGetParams&>().objectStoreId())>>>);
    490 
    491 struct FullDatabaseMetadata final : AtomicSafeRefCounted<FullDatabaseMetadata> {
    492  DatabaseMetadata mCommonMetadata;
    493  nsCString mDatabaseId;
    494  nsString mFilePath;
    495  ObjectStoreTable mObjectStores;
    496 
    497  IndexOrObjectStoreId mNextObjectStoreId = 0;
    498  IndexOrObjectStoreId mNextIndexId = 0;
    499 
    500 public:
    501  explicit FullDatabaseMetadata(const DatabaseMetadata& aCommonMetadata)
    502      : mCommonMetadata(aCommonMetadata) {
    503    AssertIsOnBackgroundThread();
    504  }
    505 
    506  [[nodiscard]] SafeRefPtr<FullDatabaseMetadata> Duplicate() const;
    507 
    508  MOZ_DECLARE_REFCOUNTED_TYPENAME(FullDatabaseMetadata)
    509 };
    510 
    511 template <class Enumerable>
    512 auto MatchMetadataNameOrId(const Enumerable& aEnumerable,
    513                           IndexOrObjectStoreId aId,
    514                           Maybe<const nsAString&> aName = Nothing()) {
    515  AssertIsOnBackgroundThread();
    516  MOZ_ASSERT(aId);
    517 
    518  const auto it = std::find_if(
    519      aEnumerable.cbegin(), aEnumerable.cend(),
    520      [aId, aName](const auto& entry) {
    521        MOZ_ASSERT(entry.GetKey() != 0);
    522 
    523        const auto& value = entry.GetData();
    524        MOZ_ASSERT(value);
    525 
    526        return !value->mDeleted &&
    527               (aId == value->mCommonMetadata.id() ||
    528                (aName && *aName == value->mCommonMetadata.name()));
    529      });
    530 
    531  return ToMaybeRef(it != aEnumerable.cend() ? it->GetData().unsafeGetRawPtr()
    532                                             : nullptr);
    533 }
    534 
    535 /*******************************************************************************
    536 * SQLite functions
    537 ******************************************************************************/
    538 
    539 // WARNING: the hash function used for the database name must not change.
    540 // That's why this function exists separately from mozilla::HashString(), even
    541 // though it is (at the time of writing) equivalent. See bug 780408 and bug
    542 // 940315 for details.
    543 uint32_t HashName(const nsAString& aName) {
    544  struct Helper {
    545    static uint32_t RotateBitsLeft32(uint32_t aValue, uint8_t aBits) {
    546      MOZ_ASSERT(aBits < 32);
    547      return (aValue << aBits) | (aValue >> (32 - aBits));
    548    }
    549  };
    550 
    551  static const uint32_t kGoldenRatioU32 = 0x9e3779b9u;
    552 
    553  return std::accumulate(aName.BeginReading(), aName.EndReading(), uint32_t(0),
    554                         [](uint32_t hash, char16_t ch) {
    555                           return kGoldenRatioU32 *
    556                                  (Helper::RotateBitsLeft32(hash, 5) ^ ch);
    557                         });
    558 }
    559 
    560 nsresult ClampResultCode(nsresult aResultCode) {
    561  if (NS_SUCCEEDED(aResultCode) ||
    562      NS_ERROR_GET_MODULE(aResultCode) == NS_ERROR_MODULE_DOM_INDEXEDDB) {
    563    return aResultCode;
    564  }
    565 
    566  switch (aResultCode) {
    567    case NS_ERROR_FILE_NO_DEVICE_SPACE:
    568      return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
    569    case NS_ERROR_STORAGE_CONSTRAINT:
    570      return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
    571    default:
    572 #ifdef DEBUG
    573      nsPrintfCString message("Converting non-IndexedDB error code (0x%" PRIX32
    574                              ") to "
    575                              "NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR",
    576                              static_cast<uint32_t>(aResultCode));
    577      NS_WARNING(message.get());
    578 #else
    579        ;
    580 #endif
    581  }
    582 
    583  IDB_REPORT_INTERNAL_ERR();
    584  return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
    585 }
    586 
    587 Result<nsCOMPtr<nsIFileURL>, nsresult> GetDatabaseFileURL(
    588    nsIFile& aDatabaseFile, const int64_t aDirectoryLockId,
    589    const Maybe<CipherKey>& aMaybeKey) {
    590  MOZ_ASSERT(aDirectoryLockId >= -1);
    591 
    592  QM_TRY_INSPECT(
    593      const auto& protocolHandler,
    594      MOZ_TO_RESULT_GET_TYPED(nsCOMPtr<nsIProtocolHandler>,
    595                              MOZ_SELECT_OVERLOAD(do_GetService),
    596                              NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "file"));
    597 
    598  QM_TRY_INSPECT(const auto& fileHandler,
    599                 MOZ_TO_RESULT_GET_TYPED(nsCOMPtr<nsIFileProtocolHandler>,
    600                                         MOZ_SELECT_OVERLOAD(do_QueryInterface),
    601                                         protocolHandler));
    602 
    603  QM_TRY_INSPECT(const auto& mutator, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
    604                                          nsCOMPtr<nsIURIMutator>, fileHandler,
    605                                          NewFileURIMutator, &aDatabaseFile));
    606 
    607  // aDirectoryLockId should only be -1 when we are called
    608  // - from DatabaseFileManager::InitDirectory when the temporary storage
    609  //    hasn't been initialized yet. At that time, the in-memory objects (e.g.
    610  //    OriginInfo) are only being created so it doesn't make sense to tunnel
    611  //    quota information to QuotaVFS to get corresponding QuotaObject instances
    612  //    for SQLite files.
    613  // - from DeleteDatabaseOp::LoadPreviousVersion, since this might require
    614  //   temporarily exceeding the quota limit before the database can be
    615  //   deleted.
    616  const nsCString directoryLockIdClause =
    617      "&directoryLockId="_ns + IntToCString(aDirectoryLockId);
    618 
    619  const auto keyClause = [&aMaybeKey] {
    620    nsAutoCString keyClause;
    621    if (aMaybeKey) {
    622      keyClause.AssignLiteral("&key=");
    623      for (uint8_t byte : IndexedDBCipherStrategy::SerializeKey(*aMaybeKey)) {
    624        keyClause.AppendPrintf("%02x", byte);
    625      }
    626    }
    627    return keyClause;
    628  }();
    629 
    630  QM_TRY_UNWRAP(auto result, ([&mutator, &directoryLockIdClause, &keyClause] {
    631                  nsCOMPtr<nsIFileURL> result;
    632                  nsresult rv = NS_MutateURI(mutator)
    633                                    .SetQuery("cache=private"_ns +
    634                                              directoryLockIdClause + keyClause)
    635                                    .Finalize(result);
    636                  return NS_SUCCEEDED(rv)
    637                             ? Result<nsCOMPtr<nsIFileURL>, nsresult>{result}
    638                             : Err(rv);
    639                }()));
    640 
    641  return result;
    642 }
    643 
    644 nsLiteralCString GetDefaultSynchronousMode() {
    645  return IndexedDatabaseManager::FullSynchronous() ? "FULL"_ns : "NORMAL"_ns;
    646 }
    647 
    648 nsresult SetDefaultPragmas(mozIStorageConnection& aConnection) {
    649  MOZ_ASSERT(!NS_IsMainThread());
    650 
    651  static constexpr auto kBuiltInPragmas =
    652      // We use foreign keys in DEBUG builds only because there is a performance
    653      // cost to using them.
    654      "PRAGMA foreign_keys = "
    655 #ifdef DEBUG
    656      "ON"
    657 #else
    658      "OFF"
    659 #endif
    660      ";"
    661 
    662      // The "INSERT OR REPLACE" statement doesn't fire the update trigger,
    663      // instead it fires only the insert trigger. This confuses the update
    664      // refcount function. This behavior changes with enabled recursive
    665      // triggers, so the statement fires the delete trigger first and then the
    666      // insert trigger.
    667      "PRAGMA recursive_triggers = ON;"
    668 
    669      // We aggressively truncate the database file when idle so don't bother
    670      // overwriting the WAL with 0 during active periods.
    671      "PRAGMA secure_delete = OFF;"_ns;
    672 
    673  QM_TRY(MOZ_TO_RESULT(aConnection.ExecuteSimpleSQL(kBuiltInPragmas)));
    674 
    675  QM_TRY(MOZ_TO_RESULT(aConnection.ExecuteSimpleSQL(nsAutoCString{
    676      "PRAGMA synchronous = "_ns + GetDefaultSynchronousMode() + ";"_ns})));
    677 
    678 #ifndef IDB_MOBILE
    679  if (kSQLiteGrowthIncrement) {
    680    // This is just an optimization so ignore the failure if the disk is
    681    // currently too full.
    682    QM_TRY(QM_OR_ELSE_WARN_IF(
    683        // Expression.
    684        MOZ_TO_RESULT(
    685            aConnection.SetGrowthIncrement(kSQLiteGrowthIncrement, ""_ns)),
    686        // Predicate.
    687        IsSpecificError<NS_ERROR_FILE_TOO_BIG>,
    688        // Fallback.
    689        ErrToDefaultOk<>));
    690  }
    691 #endif  // IDB_MOBILE
    692 
    693  return NS_OK;
    694 }
    695 
    696 nsresult SetJournalMode(mozIStorageConnection& aConnection) {
    697  MOZ_ASSERT(!NS_IsMainThread());
    698 
    699  // Try enabling WAL mode. This can fail in various circumstances so we have to
    700  // check the results here.
    701  constexpr auto journalModeQueryStart = "PRAGMA journal_mode = "_ns;
    702  constexpr auto journalModeWAL = "wal"_ns;
    703 
    704  QM_TRY_INSPECT(const auto& stmt,
    705                 CreateAndExecuteSingleStepStatement(
    706                     aConnection, journalModeQueryStart + journalModeWAL));
    707 
    708  QM_TRY_INSPECT(
    709      const auto& journalMode,
    710      MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsCString, *stmt, GetUTF8String, 0));
    711 
    712  if (journalMode.Equals(journalModeWAL)) {
    713    // WAL mode successfully enabled. Maybe set limits on its size here.
    714    if (kMaxWALPages >= 0) {
    715      QM_TRY(MOZ_TO_RESULT(aConnection.ExecuteSimpleSQL(
    716          "PRAGMA wal_autocheckpoint = "_ns + IntToCString(kMaxWALPages))));
    717    }
    718  } else {
    719    NS_WARNING("Failed to set WAL mode, falling back to normal journal mode.");
    720 #ifdef IDB_MOBILE
    721    QM_TRY(MOZ_TO_RESULT(
    722        aConnection.ExecuteSimpleSQL(journalModeQueryStart + "truncate"_ns)));
    723 #endif
    724  }
    725 
    726  return NS_OK;
    727 }
    728 
    729 Result<MovingNotNull<nsCOMPtr<mozIStorageConnection>>, nsresult> OpenDatabase(
    730    mozIStorageService& aStorageService, nsIFileURL& aFileURL,
    731    const uint32_t aTelemetryId = 0) {
    732  const nsAutoCString telemetryFilename =
    733      aTelemetryId ? "indexedDB-"_ns + IntToCString(aTelemetryId) +
    734                         NS_ConvertUTF16toUTF8(kSQLiteSuffix)
    735                   : nsAutoCString();
    736 
    737  QM_TRY_UNWRAP(auto connection,
    738                MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
    739                    nsCOMPtr<mozIStorageConnection>, aStorageService,
    740                    OpenDatabaseWithFileURL, &aFileURL, telemetryFilename,
    741                    mozIStorageService::CONNECTION_INTERRUPTIBLE));
    742 
    743  return WrapMovingNotNull(std::move(connection));
    744 }
    745 
    746 Result<MovingNotNull<nsCOMPtr<mozIStorageConnection>>, nsresult>
    747 OpenDatabaseAndHandleBusy(mozIStorageService& aStorageService,
    748                          nsIFileURL& aFileURL,
    749                          const uint32_t aTelemetryId = 0) {
    750  MOZ_ASSERT(!NS_IsMainThread());
    751  MOZ_ASSERT(!IsOnBackgroundThread());
    752 
    753  using ConnectionType = Maybe<MovingNotNull<nsCOMPtr<mozIStorageConnection>>>;
    754 
    755  QM_TRY_UNWRAP(auto connection,
    756                QM_OR_ELSE_WARN_IF(
    757                    // Expression
    758                    OpenDatabase(aStorageService, aFileURL, aTelemetryId)
    759                        .map([](auto connection) -> ConnectionType {
    760                          return Some(std::move(connection));
    761                        }),
    762                    // Predicate.
    763                    IsSpecificError<NS_ERROR_STORAGE_BUSY>,
    764                    // Fallback.
    765                    ErrToDefaultOk<ConnectionType>));
    766 
    767  if (connection.isNothing()) {
    768 #ifdef DEBUG
    769    {
    770      nsCString path;
    771      MOZ_ALWAYS_SUCCEEDS(aFileURL.GetFileName(path));
    772 
    773      nsPrintfCString message(
    774          "Received NS_ERROR_STORAGE_BUSY when attempting to open database "
    775          "'%s', retrying for up to 10 seconds",
    776          path.get());
    777      NS_WARNING(message.get());
    778    }
    779 #endif
    780 
    781    // Another thread must be checkpointing the WAL. Wait up to 10 seconds for
    782    // that to complete.
    783    const TimeStamp start = TimeStamp::NowLoRes();
    784 
    785    // Use exponential backoff: start at 1ms, double up to 100ms max
    786    uint32_t sleepMs = 1;
    787    constexpr uint32_t kMaxSleepMs = 100;
    788 
    789    do {
    790      PR_Sleep(PR_MillisecondsToInterval(sleepMs));
    791 
    792      // Exponential backoff with cap
    793      sleepMs = std::min(sleepMs * 2, kMaxSleepMs);
    794 
    795      QM_TRY_UNWRAP(connection,
    796                    QM_OR_ELSE_WARN_IF(
    797                        // Expression.
    798                        OpenDatabase(aStorageService, aFileURL, aTelemetryId)
    799                            .map([](auto connection) -> ConnectionType {
    800                              return Some(std::move(connection));
    801                            }),
    802                        // Predicate.
    803                        ([&start](nsresult aValue) {
    804                          return aValue == NS_ERROR_STORAGE_BUSY &&
    805                                 TimeStamp::NowLoRes() - start <=
    806                                     TimeDuration::FromSeconds(10);
    807                        }),
    808                        // Fallback.
    809                        ErrToDefaultOk<ConnectionType>));
    810    } while (connection.isNothing());
    811  }
    812 
    813  return connection.extract();
    814 }
    815 
    816 // Returns true if a given nsIFile exists and is a directory. Returns false if
    817 // it doesn't exist. Returns an error if it exists, but is not a directory, or
    818 // any other error occurs.
    819 Result<bool, nsresult> ExistsAsDirectory(nsIFile& aDirectory) {
    820  QM_TRY_INSPECT(const bool& exists,
    821                 MOZ_TO_RESULT_INVOKE_MEMBER(aDirectory, Exists));
    822 
    823  if (exists) {
    824    QM_TRY_INSPECT(const bool& isDirectory,
    825                   MOZ_TO_RESULT_INVOKE_MEMBER(aDirectory, IsDirectory));
    826 
    827    QM_TRY(OkIf(isDirectory), Err(NS_ERROR_FAILURE));
    828  }
    829 
    830  return exists;
    831 }
    832 
    833 constexpr nsresult mapNoDeviceSpaceError(nsresult aRv) {
    834  if (aRv == NS_ERROR_FILE_NO_DEVICE_SPACE) {
    835    // mozstorage translates SQLITE_FULL to
    836    // NS_ERROR_FILE_NO_DEVICE_SPACE, which we know better as
    837    // NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR.
    838    return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
    839  }
    840  return aRv;
    841 }
    842 
    843 Result<MovingNotNull<nsCOMPtr<mozIStorageConnection>>, nsresult>
    844 CreateStorageConnection(nsIFile& aDBFile, nsIFile& aFMDirectory,
    845                        const nsAString& aName, const nsACString& aOrigin,
    846                        const int64_t aDirectoryLockId,
    847                        const uint32_t aTelemetryId,
    848                        const Maybe<CipherKey>& aMaybeKey) {
    849  AssertIsOnIOThread();
    850  MOZ_ASSERT(aDirectoryLockId >= -1);
    851 
    852  AUTO_PROFILER_LABEL("CreateStorageConnection", DOM);
    853 
    854  QM_TRY_INSPECT(const auto& dbFileUrl,
    855                 GetDatabaseFileURL(aDBFile, aDirectoryLockId, aMaybeKey));
    856 
    857  QM_TRY_INSPECT(const auto& storageService,
    858                 MOZ_TO_RESULT_GET_TYPED(nsCOMPtr<mozIStorageService>,
    859                                         MOZ_SELECT_OVERLOAD(do_GetService),
    860                                         MOZ_STORAGE_SERVICE_CONTRACTID));
    861 
    862  QM_TRY_UNWRAP(
    863      auto connection,
    864      QM_OR_ELSE_WARN_IF(
    865          // Expression.
    866          OpenDatabaseAndHandleBusy(*storageService, *dbFileUrl, aTelemetryId)
    867              .map([](auto connection) -> nsCOMPtr<mozIStorageConnection> {
    868                return std::move(connection).unwrapBasePtr();
    869              }),
    870          // Predicate.
    871          ([&aName](nsresult aValue) {
    872            // If we're just opening the database during origin initialization,
    873            // then we don't want to erase any files. The failure here will fail
    874            // origin initialization too.
    875            return IsDatabaseCorruptionError(aValue) && !aName.IsVoid();
    876          }),
    877          // Fallback.
    878          ErrToDefaultOk<nsCOMPtr<mozIStorageConnection>>));
    879 
    880  if (!connection) {
    881    // XXX Shouldn't we also update quota usage?
    882 
    883    // Nuke the database file.
    884    QM_TRY(MOZ_TO_RESULT(aDBFile.Remove(false)));
    885    QM_TRY_INSPECT(const bool& existsAsDirectory,
    886                   ExistsAsDirectory(aFMDirectory));
    887 
    888    if (existsAsDirectory) {
    889      QM_TRY(MOZ_TO_RESULT(aFMDirectory.Remove(true)));
    890    }
    891 
    892    QM_TRY_UNWRAP(connection, OpenDatabaseAndHandleBusy(
    893                                  *storageService, *dbFileUrl, aTelemetryId));
    894  }
    895 
    896  QM_TRY(MOZ_TO_RESULT(SetDefaultPragmas(*connection)));
    897  QM_TRY(MOZ_TO_RESULT(connection->EnableModule("filesystem"_ns)));
    898 
    899  // Check to make sure that the database schema is correct.
    900  QM_TRY_INSPECT(const int32_t& schemaVersion,
    901                 MOZ_TO_RESULT_INVOKE_MEMBER(connection, GetSchemaVersion));
    902 
    903  // Unknown schema will fail origin initialization too.
    904  QM_TRY(OkIf(schemaVersion || !aName.IsVoid()),
    905         Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR), [](const auto&) {
    906           IDB_WARNING("Unable to open IndexedDB database, schema is not set!");
    907         });
    908 
    909  QM_TRY(
    910      OkIf(schemaVersion <= kSQLiteSchemaVersion),
    911      Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR), [](const auto&) {
    912        IDB_WARNING("Unable to open IndexedDB database, schema is too high!");
    913      });
    914 
    915  bool journalModeSet = false;
    916 
    917  if (schemaVersion != kSQLiteSchemaVersion) {
    918    const bool newDatabase = !schemaVersion;
    919 
    920    if (newDatabase) {
    921      // Set the page size first.
    922      const auto sqlitePageSizeOverride =
    923          aMaybeKey ? 8192 : kSQLitePageSizeOverride;
    924      if (sqlitePageSizeOverride) {
    925        QM_TRY(MOZ_TO_RESULT(connection->ExecuteSimpleSQL(nsPrintfCString(
    926            "PRAGMA page_size = %" PRIu32 ";", sqlitePageSizeOverride))));
    927      }
    928 
    929      // We have to set the auto_vacuum mode before opening a transaction.
    930      QM_TRY((MOZ_TO_RESULT_INVOKE_MEMBER(
    931                  connection, ExecuteSimpleSQL,
    932 #ifdef IDB_MOBILE
    933                  // Turn on full auto_vacuum mode to reclaim disk space on
    934                  // mobile devices (at the cost of some COMMIT speed).
    935                  "PRAGMA auto_vacuum = FULL;"_ns
    936 #else
    937                  // Turn on incremental auto_vacuum mode on desktop builds.
    938                  "PRAGMA auto_vacuum = INCREMENTAL;"_ns
    939 #endif
    940                  )
    941                  .mapErr(mapNoDeviceSpaceError)));
    942 
    943      QM_TRY(MOZ_TO_RESULT(SetJournalMode(*connection)));
    944 
    945      journalModeSet = true;
    946    } else {
    947 #ifdef DEBUG
    948      // Disable foreign key support while upgrading. This has to be done before
    949      // starting a transaction.
    950      MOZ_ALWAYS_SUCCEEDS(
    951          connection->ExecuteSimpleSQL("PRAGMA foreign_keys = OFF;"_ns));
    952 #endif
    953    }
    954 
    955    bool vacuumNeeded = false;
    956 
    957    mozStorageTransaction transaction(
    958        connection.get(), false, mozIStorageConnection::TRANSACTION_IMMEDIATE);
    959 
    960    QM_TRY(MOZ_TO_RESULT(transaction.Start()));
    961 
    962    if (newDatabase) {
    963      QM_TRY(MOZ_TO_RESULT(CreateTables(*connection)));
    964 
    965 #ifdef DEBUG
    966      {
    967        QM_TRY_INSPECT(
    968            const int32_t& schemaVersion,
    969            MOZ_TO_RESULT_INVOKE_MEMBER(connection, GetSchemaVersion),
    970            QM_ASSERT_UNREACHABLE);
    971        MOZ_ASSERT(schemaVersion == kSQLiteSchemaVersion);
    972      }
    973 #endif
    974 
    975      // The parameter names are not used, parameters are bound by index only
    976      // locally in the same function.
    977      QM_TRY_INSPECT(
    978          const auto& stmt,
    979          MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
    980              nsCOMPtr<mozIStorageStatement>, connection, CreateStatement,
    981              "INSERT INTO database (name, origin) "
    982              "VALUES (:name, :origin)"_ns));
    983 
    984      QM_TRY(MOZ_TO_RESULT(stmt->BindStringByIndex(0, aName)));
    985      QM_TRY(MOZ_TO_RESULT(stmt->BindUTF8StringByIndex(1, aOrigin)));
    986      QM_TRY(MOZ_TO_RESULT(stmt->Execute()));
    987    } else {
    988      QM_TRY_UNWRAP(vacuumNeeded, MaybeUpgradeSchema(*connection, schemaVersion,
    989                                                     aFMDirectory, aOrigin));
    990    }
    991 
    992    QM_TRY(MOZ_TO_RESULT_INVOKE_MEMBER(transaction, Commit)
    993               .mapErr(mapNoDeviceSpaceError));
    994 
    995 #ifdef DEBUG
    996    if (!newDatabase) {
    997      // Re-enable foreign key support after doing a foreign key check.
    998      QM_TRY_INSPECT(const bool& foreignKeyError,
    999                     CreateAndExecuteSingleStepStatement<
   1000                         SingleStepResult::ReturnNullIfNoResult>(
   1001                         *connection, "PRAGMA foreign_key_check;"_ns),
   1002                     QM_ASSERT_UNREACHABLE);
   1003 
   1004      MOZ_ASSERT(!foreignKeyError, "Database has inconsisistent foreign keys!");
   1005 
   1006      MOZ_ALWAYS_SUCCEEDS(
   1007          connection->ExecuteSimpleSQL("PRAGMA foreign_keys = OFF;"_ns));
   1008    }
   1009 #endif
   1010 
   1011    if (kSQLitePageSizeOverride && !newDatabase) {
   1012      QM_TRY_INSPECT(const auto& stmt,
   1013                     CreateAndExecuteSingleStepStatement(
   1014                         *connection, "PRAGMA page_size;"_ns));
   1015 
   1016      QM_TRY_INSPECT(const int32_t& pageSize,
   1017                     MOZ_TO_RESULT_INVOKE_MEMBER(*stmt, GetInt32, 0));
   1018      MOZ_ASSERT(pageSize >= 512 && pageSize <= 65536);
   1019 
   1020      if (kSQLitePageSizeOverride != uint32_t(pageSize)) {
   1021        // We must not be in WAL journal mode to change the page size.
   1022        QM_TRY(MOZ_TO_RESULT(
   1023            connection->ExecuteSimpleSQL("PRAGMA journal_mode = DELETE;"_ns)));
   1024 
   1025        QM_TRY_INSPECT(const auto& stmt,
   1026                       CreateAndExecuteSingleStepStatement(
   1027                           *connection, "PRAGMA journal_mode;"_ns));
   1028 
   1029        QM_TRY_INSPECT(const auto& journalMode,
   1030                       MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsCString, *stmt,
   1031                                                         GetUTF8String, 0));
   1032 
   1033        if (journalMode.EqualsLiteral("delete")) {
   1034          // Successfully set to rollback journal mode so changing the page size
   1035          // is possible with a VACUUM.
   1036          QM_TRY(MOZ_TO_RESULT(connection->ExecuteSimpleSQL(nsPrintfCString(
   1037              "PRAGMA page_size = %" PRIu32 ";", kSQLitePageSizeOverride))));
   1038 
   1039          // We will need to VACUUM in order to change the page size.
   1040          vacuumNeeded = true;
   1041        } else {
   1042          NS_WARNING(
   1043              "Failed to set journal_mode for database, unable to "
   1044              "change the page size!");
   1045        }
   1046      }
   1047    }
   1048 
   1049    if (vacuumNeeded) {
   1050      QM_TRY(MOZ_TO_RESULT(connection->ExecuteSimpleSQL("VACUUM;"_ns)));
   1051    }
   1052 
   1053    if (newDatabase || vacuumNeeded) {
   1054      if (journalModeSet) {
   1055        // Make sure we checkpoint to get an accurate file size.
   1056        QM_TRY(MOZ_TO_RESULT(
   1057            connection->ExecuteSimpleSQL("PRAGMA wal_checkpoint(FULL);"_ns)));
   1058      }
   1059 
   1060      QM_TRY_INSPECT(const int64_t& fileSize,
   1061                     MOZ_TO_RESULT_INVOKE_MEMBER(aDBFile, GetFileSize));
   1062      MOZ_ASSERT(fileSize > 0);
   1063 
   1064      PRTime vacuumTime = PR_Now();
   1065      MOZ_ASSERT(vacuumTime);
   1066 
   1067      // The parameter names are not used, parameters are bound by index only
   1068      // locally in the same function.
   1069      QM_TRY_INSPECT(
   1070          const auto& vacuumTimeStmt,
   1071          MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsCOMPtr<mozIStorageStatement>,
   1072                                            connection, CreateStatement,
   1073                                            "UPDATE database "
   1074                                            "SET last_vacuum_time = :time"
   1075                                            ", last_vacuum_size = :size;"_ns));
   1076 
   1077      QM_TRY(MOZ_TO_RESULT(vacuumTimeStmt->BindInt64ByIndex(0, vacuumTime)));
   1078      QM_TRY(MOZ_TO_RESULT(vacuumTimeStmt->BindInt64ByIndex(1, fileSize)));
   1079      QM_TRY(MOZ_TO_RESULT(vacuumTimeStmt->Execute()));
   1080    }
   1081  }
   1082 
   1083  if (!journalModeSet) {
   1084    QM_TRY(MOZ_TO_RESULT(SetJournalMode(*connection)));
   1085  }
   1086 
   1087  return WrapMovingNotNullUnchecked(std::move(connection));
   1088 }
   1089 
   1090 nsCOMPtr<nsIFile> GetFileForPath(const nsAString& aPath) {
   1091  MOZ_ASSERT(!aPath.IsEmpty());
   1092 
   1093  QM_TRY_RETURN(QM_NewLocalFile(aPath), nullptr);
   1094 }
   1095 
   1096 Result<MovingNotNull<nsCOMPtr<mozIStorageConnection>>, nsresult>
   1097 GetStorageConnection(nsIFile& aDatabaseFile, const int64_t aDirectoryLockId,
   1098                     const uint32_t aTelemetryId,
   1099                     const Maybe<CipherKey>& aMaybeKey) {
   1100  MOZ_ASSERT(!NS_IsMainThread());
   1101  MOZ_ASSERT(!IsOnBackgroundThread());
   1102  MOZ_ASSERT(aDirectoryLockId >= 0);
   1103 
   1104  AUTO_PROFILER_LABEL("GetStorageConnection", DOM);
   1105 
   1106  QM_TRY_INSPECT(const bool& exists,
   1107                 MOZ_TO_RESULT_INVOKE_MEMBER(aDatabaseFile, Exists));
   1108 
   1109  QM_TRY(OkIf(exists), Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR),
   1110         IDB_REPORT_INTERNAL_ERR_LAMBDA);
   1111 
   1112  QM_TRY_INSPECT(
   1113      const auto& dbFileUrl,
   1114      GetDatabaseFileURL(aDatabaseFile, aDirectoryLockId, aMaybeKey));
   1115 
   1116  QM_TRY_INSPECT(const auto& storageService,
   1117                 MOZ_TO_RESULT_GET_TYPED(nsCOMPtr<mozIStorageService>,
   1118                                         MOZ_SELECT_OVERLOAD(do_GetService),
   1119                                         MOZ_STORAGE_SERVICE_CONTRACTID));
   1120 
   1121  QM_TRY_UNWRAP(
   1122      nsCOMPtr<mozIStorageConnection> connection,
   1123      OpenDatabaseAndHandleBusy(*storageService, *dbFileUrl, aTelemetryId));
   1124 
   1125  QM_TRY(MOZ_TO_RESULT(SetDefaultPragmas(*connection)));
   1126 
   1127  QM_TRY(MOZ_TO_RESULT(SetJournalMode(*connection)));
   1128 
   1129  return WrapMovingNotNullUnchecked(std::move(connection));
   1130 }
   1131 
   1132 Result<MovingNotNull<nsCOMPtr<mozIStorageConnection>>, nsresult>
   1133 GetStorageConnection(const nsAString& aDatabaseFilePath,
   1134                     const int64_t aDirectoryLockId,
   1135                     const uint32_t aTelemetryId,
   1136                     const Maybe<CipherKey>& aMaybeKey) {
   1137  MOZ_ASSERT(!NS_IsMainThread());
   1138  MOZ_ASSERT(!IsOnBackgroundThread());
   1139  MOZ_ASSERT(!aDatabaseFilePath.IsEmpty());
   1140  MOZ_ASSERT(StringEndsWith(aDatabaseFilePath, kSQLiteSuffix));
   1141  MOZ_ASSERT(aDirectoryLockId >= 0);
   1142 
   1143  nsCOMPtr<nsIFile> dbFile = GetFileForPath(aDatabaseFilePath);
   1144 
   1145  QM_TRY(OkIf(dbFile), Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR),
   1146         IDB_REPORT_INTERNAL_ERR_LAMBDA);
   1147 
   1148  return GetStorageConnection(*dbFile, aDirectoryLockId, aTelemetryId,
   1149                              aMaybeKey);
   1150 }
   1151 
   1152 /*******************************************************************************
   1153 * ConnectionPool declarations
   1154 ******************************************************************************/
   1155 
   1156 class DatabaseConnection final : public CachingDatabaseConnection {
   1157  friend class ConnectionPool;
   1158 
   1159  enum class CheckpointMode { Full, Restart, Truncate };
   1160 
   1161 public:
   1162  class AutoSavepoint;
   1163  class UpdateRefcountFunction;
   1164 
   1165 private:
   1166  InitializedOnce<const NotNull<SafeRefPtr<DatabaseFileManager>>> mFileManager;
   1167  RefPtr<UpdateRefcountFunction> mUpdateRefcountFunction;
   1168  RefPtr<QuotaObject> mQuotaObject;
   1169  RefPtr<QuotaObject> mJournalQuotaObject;
   1170  IDBTransaction::Durability mLastDurability;
   1171  bool mInReadTransaction;
   1172  bool mInWriteTransaction;
   1173 
   1174 #ifdef DEBUG
   1175  uint32_t mDEBUGSavepointCount;
   1176 #endif
   1177 
   1178 public:
   1179  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(DatabaseConnection)
   1180 
   1181  UpdateRefcountFunction* GetUpdateRefcountFunction() const {
   1182    AssertIsOnConnectionThread();
   1183 
   1184    return mUpdateRefcountFunction;
   1185  }
   1186 
   1187  nsresult BeginWriteTransaction(const IDBTransaction::Durability aDurability);
   1188 
   1189  nsresult CommitWriteTransaction();
   1190 
   1191  void RollbackWriteTransaction();
   1192 
   1193  void FinishWriteTransaction();
   1194 
   1195  nsresult StartSavepoint();
   1196 
   1197  nsresult ReleaseSavepoint();
   1198 
   1199  nsresult RollbackSavepoint();
   1200 
   1201  nsresult Checkpoint() {
   1202    AssertIsOnConnectionThread();
   1203 
   1204    return CheckpointInternal(CheckpointMode::Full);
   1205  }
   1206 
   1207  void DoIdleProcessing(bool aNeedsCheckpoint,
   1208                        const Atomic<bool>& aInterrupted);
   1209 
   1210  void Close();
   1211 
   1212  nsresult DisableQuotaChecks();
   1213 
   1214  void EnableQuotaChecks();
   1215 
   1216 private:
   1217  DatabaseConnection(
   1218      MovingNotNull<nsCOMPtr<mozIStorageConnection>> aStorageConnection,
   1219      MovingNotNull<SafeRefPtr<DatabaseFileManager>> aFileManager);
   1220 
   1221  ~DatabaseConnection();
   1222 
   1223  nsresult Init();
   1224 
   1225  nsresult CheckpointInternal(CheckpointMode aMode);
   1226 
   1227  Result<uint32_t, nsresult> GetFreelistCount(
   1228      CachedStatement& aCachedStatement);
   1229 
   1230  /**
   1231   * On success, returns whether some pages were freed.
   1232   */
   1233  Result<bool, nsresult> ReclaimFreePagesWhileIdle(
   1234      CachedStatement& aFreelistStatement, CachedStatement& aRollbackStatement,
   1235      uint32_t aFreelistCount, bool aNeedsCheckpoint,
   1236      const Atomic<bool>& aInterrupted);
   1237 
   1238  Result<int64_t, nsresult> GetFileSize(const nsAString& aPath);
   1239 };
   1240 
   1241 class MOZ_STACK_CLASS DatabaseConnection::AutoSavepoint final {
   1242  DatabaseConnection* mConnection;
   1243 #ifdef DEBUG
   1244  const TransactionBase* mDEBUGTransaction;
   1245 #endif
   1246 
   1247 public:
   1248  AutoSavepoint();
   1249  ~AutoSavepoint();
   1250 
   1251  nsresult Start(const TransactionBase& aTransaction);
   1252 
   1253  nsresult Commit();
   1254 };
   1255 
   1256 class DatabaseConnection::UpdateRefcountFunction final
   1257    : public mozIStorageFunction {
   1258  class FileInfoEntry;
   1259 
   1260  enum class UpdateType { Increment, Decrement };
   1261 
   1262  DatabaseConnection* const mConnection;
   1263  DatabaseFileManager& mFileManager;
   1264  nsClassHashtable<nsUint64HashKey, FileInfoEntry> mFileInfoEntries;
   1265  nsTHashMap<nsUint64HashKey, NotNull<FileInfoEntry*>> mSavepointEntriesIndex;
   1266 
   1267  nsTArray<int64_t> mJournalsToCreateBeforeCommit;
   1268  nsTArray<int64_t> mJournalsToRemoveAfterCommit;
   1269  nsTArray<int64_t> mJournalsToRemoveAfterAbort;
   1270 
   1271  bool mInSavepoint;
   1272 
   1273 public:
   1274  NS_DECL_ISUPPORTS_ONEVENTTARGET
   1275  NS_DECL_MOZISTORAGEFUNCTION
   1276 
   1277  UpdateRefcountFunction(DatabaseConnection* aConnection,
   1278                         DatabaseFileManager& aFileManager);
   1279 
   1280  nsresult WillCommit();
   1281 
   1282  void DidCommit();
   1283 
   1284  void DidAbort();
   1285 
   1286  void StartSavepoint();
   1287 
   1288  void ReleaseSavepoint();
   1289 
   1290  void RollbackSavepoint();
   1291 
   1292  void Reset();
   1293 
   1294 private:
   1295  ~UpdateRefcountFunction() = default;
   1296 
   1297  nsresult ProcessValue(mozIStorageValueArray* aValues, int32_t aIndex,
   1298                        UpdateType aUpdateType);
   1299 
   1300  nsresult CreateJournals();
   1301 
   1302  nsresult RemoveJournals(const nsTArray<int64_t>& aJournals);
   1303 };
   1304 
   1305 class DatabaseConnection::UpdateRefcountFunction::FileInfoEntry final {
   1306  SafeRefPtr<DatabaseFileInfo> mFileInfo;
   1307  int32_t mDelta;
   1308  int32_t mSavepointDelta;
   1309 
   1310 public:
   1311  explicit FileInfoEntry(SafeRefPtr<DatabaseFileInfo> aFileInfo)
   1312      : mFileInfo(std::move(aFileInfo)), mDelta(0), mSavepointDelta(0) {
   1313    MOZ_COUNT_CTOR(DatabaseConnection::UpdateRefcountFunction::FileInfoEntry);
   1314  }
   1315 
   1316  void IncDeltas(bool aUpdateSavepointDelta) {
   1317    ++mDelta;
   1318    if (aUpdateSavepointDelta) {
   1319      ++mSavepointDelta;
   1320    }
   1321  }
   1322  void DecDeltas(bool aUpdateSavepointDelta) {
   1323    --mDelta;
   1324    if (aUpdateSavepointDelta) {
   1325      --mSavepointDelta;
   1326    }
   1327  }
   1328  void DecBySavepointDelta() { mDelta -= mSavepointDelta; }
   1329  SafeRefPtr<DatabaseFileInfo> ReleaseFileInfo() {
   1330    return std::move(mFileInfo);
   1331  }
   1332  void MaybeUpdateDBRefs() {
   1333    if (mDelta) {
   1334      mFileInfo->UpdateDBRefs(mDelta);
   1335    }
   1336  }
   1337 
   1338  int32_t Delta() const { return mDelta; }
   1339  int32_t SavepointDelta() const { return mSavepointDelta; }
   1340 
   1341  ~FileInfoEntry() {
   1342    MOZ_COUNT_DTOR(DatabaseConnection::UpdateRefcountFunction::FileInfoEntry);
   1343  }
   1344 };
   1345 
   1346 class ConnectionPool final {
   1347 public:
   1348  class FinishCallback;
   1349 
   1350 private:
   1351  class ConnectionRunnable;
   1352  class CloseConnectionRunnable;
   1353  struct DatabaseInfo;
   1354  struct DatabaseCompleteCallback;
   1355  class FinishCallbackWrapper;
   1356  class IdleConnectionRunnable;
   1357 
   1358 #ifdef DEBUG
   1359  class TransactionRunnable;
   1360 #endif
   1361  class TransactionInfo;
   1362  struct TransactionInfoPair;
   1363 
   1364  struct IdleResource {
   1365    TimeStamp mIdleTime;
   1366 
   1367    IdleResource(const IdleResource& aOther) = delete;
   1368    IdleResource(IdleResource&& aOther) noexcept
   1369        : IdleResource(aOther.mIdleTime) {}
   1370    IdleResource& operator=(const IdleResource& aOther) = delete;
   1371    IdleResource& operator=(IdleResource&& aOther) = delete;
   1372 
   1373   protected:
   1374    explicit IdleResource(const TimeStamp& aIdleTime);
   1375 
   1376    ~IdleResource();
   1377  };
   1378 
   1379  struct IdleDatabaseInfo final : public IdleResource {
   1380    InitializedOnce<const NotNull<DatabaseInfo*>> mDatabaseInfo;
   1381 
   1382   public:
   1383    explicit IdleDatabaseInfo(DatabaseInfo& aDatabaseInfo);
   1384 
   1385    IdleDatabaseInfo(const IdleDatabaseInfo& aOther) = delete;
   1386    IdleDatabaseInfo(IdleDatabaseInfo&& aOther) noexcept
   1387        : IdleResource(std::move(aOther)),
   1388          mDatabaseInfo{std::move(aOther.mDatabaseInfo)} {
   1389      MOZ_ASSERT(mDatabaseInfo);
   1390 
   1391      MOZ_COUNT_CTOR(ConnectionPool::IdleDatabaseInfo);
   1392    }
   1393    IdleDatabaseInfo& operator=(const IdleDatabaseInfo& aOther) = delete;
   1394    IdleDatabaseInfo& operator=(IdleDatabaseInfo&& aOther) = delete;
   1395 
   1396    ~IdleDatabaseInfo();
   1397 
   1398    bool operator==(const IdleDatabaseInfo& aOther) const {
   1399      return *mDatabaseInfo == *aOther.mDatabaseInfo;
   1400    }
   1401 
   1402    bool operator==(const DatabaseInfo* aDatabaseInfo) const {
   1403      return *mDatabaseInfo == aDatabaseInfo;
   1404    }
   1405 
   1406    bool operator<(const IdleDatabaseInfo& aOther) const {
   1407      return mIdleTime < aOther.mIdleTime;
   1408    }
   1409  };
   1410 
   1411  struct PerformingIdleMaintenanceDatabaseInfo {
   1412    const NotNull<DatabaseInfo*> mDatabaseInfo;
   1413    RefPtr<IdleConnectionRunnable> mIdleConnectionRunnable;
   1414 
   1415    PerformingIdleMaintenanceDatabaseInfo(
   1416        DatabaseInfo& aDatabaseInfo,
   1417        RefPtr<IdleConnectionRunnable> aIdleConnectionRunnable);
   1418 
   1419    PerformingIdleMaintenanceDatabaseInfo(
   1420        const PerformingIdleMaintenanceDatabaseInfo& aOther) = delete;
   1421    PerformingIdleMaintenanceDatabaseInfo(
   1422        PerformingIdleMaintenanceDatabaseInfo&& aOther) noexcept
   1423        : mDatabaseInfo{aOther.mDatabaseInfo},
   1424          mIdleConnectionRunnable{std::move(aOther.mIdleConnectionRunnable)} {
   1425      MOZ_COUNT_CTOR(ConnectionPool::PerformingIdleMaintenanceDatabaseInfo);
   1426    }
   1427    PerformingIdleMaintenanceDatabaseInfo& operator=(
   1428        const PerformingIdleMaintenanceDatabaseInfo& aOther) = delete;
   1429    PerformingIdleMaintenanceDatabaseInfo& operator=(
   1430        PerformingIdleMaintenanceDatabaseInfo&& aOther) = delete;
   1431 
   1432    ~PerformingIdleMaintenanceDatabaseInfo();
   1433 
   1434    bool operator==(const DatabaseInfo* aDatabaseInfo) const {
   1435      return mDatabaseInfo == aDatabaseInfo;
   1436    }
   1437  };
   1438 
   1439  // This mutex guards mDatabases, see below.
   1440  Mutex mDatabasesMutex MOZ_UNANNOTATED;
   1441 
   1442  nsCOMPtr<nsIThreadPool> mIOTarget;
   1443  nsTArray<IdleDatabaseInfo> mIdleDatabases;
   1444  nsTArray<PerformingIdleMaintenanceDatabaseInfo>
   1445      mDatabasesPerformingIdleMaintenance;
   1446  nsCOMPtr<nsITimer> mIdleTimer;
   1447  TimeStamp mTargetIdleTime;
   1448 
   1449  // Only modifed on the owning thread, but read on multiple threads. Therefore
   1450  // all modifications and all reads off the owning thread must be protected by
   1451  // mDatabasesMutex.
   1452  nsClassHashtable<nsCStringHashKey, DatabaseInfo> mDatabases;
   1453 
   1454  nsClassHashtable<nsUint64HashKey, TransactionInfo> mTransactions;
   1455  nsTArray<NotNull<TransactionInfo*>> mQueuedTransactions;
   1456 
   1457  nsTArray<UniquePtr<DatabaseCompleteCallback>> mCompleteCallbacks;
   1458 
   1459  uint64_t mNextTransactionId;
   1460  FlippedOnce<false> mShutdownRequested;
   1461  FlippedOnce<false> mShutdownComplete;
   1462 
   1463 public:
   1464  ConnectionPool();
   1465 
   1466  void AssertIsOnOwningThread() const {
   1467    NS_ASSERT_OWNINGTHREAD(ConnectionPool);
   1468  }
   1469 
   1470  Result<RefPtr<DatabaseConnection>, nsresult> GetOrCreateConnection(
   1471      const Database& aDatabase);
   1472 
   1473  uint64_t Start(const nsID& aBackgroundChildLoggingId,
   1474                 const nsACString& aDatabaseId, int64_t aLoggingSerialNumber,
   1475                 const nsTArray<nsString>& aObjectStoreNames,
   1476                 bool aIsWriteTransaction,
   1477                 TransactionDatabaseOperationBase* aTransactionOp);
   1478 
   1479  /**
   1480   * Starts a new operation associated with the given transaction.
   1481   *
   1482   * This method initiates an operation by:
   1483   * 1. Dispatching the provided runnable to the task queue created on top of
   1484   *    the I/O thread pool if the transaction is currently running.
   1485   * 2. Queuing the runnable for later execution if the transaction is not yet
   1486   *    running.
   1487   *
   1488   * It is mandatory for all operations to call StartOp to ensure proper
   1489   * handling and sequencing within the transaction context.
   1490   *
   1491   * Note:
   1492   * - For more complex operations that involve work on other threads or require
   1493   *   communication with content processes, StartOp should not be called again
   1494   *   to dispatch to the task queue, as this could disrupt proper queuing and
   1495   *   execution.
   1496   */
   1497  void StartOp(uint64_t aTransactionId, nsCOMPtr<nsIRunnable> aRunnable);
   1498 
   1499  /**
   1500   * Marks the completion of an operation associated with the given transaction.
   1501   *
   1502   * This method signals that the current operation has finished, allowing the
   1503   * next queued operation (if any) for the transaction to start.
   1504   */
   1505  void FinishOp(uint64_t aTransactionId);
   1506 
   1507  void Finish(uint64_t aTransactionId, FinishCallback* aCallback);
   1508 
   1509  void CloseDatabaseWhenIdle(const nsACString& aDatabaseId) {
   1510    (void)CloseDatabaseWhenIdleInternal(aDatabaseId);
   1511  }
   1512 
   1513  void WaitForDatabaseToComplete(const nsCString& aDatabaseId,
   1514                                 nsIRunnable* aCallback);
   1515 
   1516  void Shutdown();
   1517 
   1518  NS_INLINE_DECL_REFCOUNTING(ConnectionPool)
   1519 
   1520 private:
   1521  ~ConnectionPool();
   1522 
   1523  static void IdleTimerCallback(nsITimer* aTimer, void* aClosure);
   1524 
   1525  static uint32_t SerialNumber() { return ++sSerialNumber; }
   1526 
   1527  static uint32_t sSerialNumber;
   1528 
   1529  void Cleanup();
   1530 
   1531  void AdjustIdleTimer();
   1532 
   1533  void CancelIdleTimer();
   1534 
   1535  void CloseIdleDatabases();
   1536 
   1537  bool ScheduleTransaction(TransactionInfo& aTransactionInfo,
   1538                           bool aFromQueuedTransactions);
   1539 
   1540  void NoteFinishedTransaction(uint64_t aTransactionId);
   1541 
   1542  void ScheduleQueuedTransactions();
   1543 
   1544  void NoteIdleDatabase(DatabaseInfo& aDatabaseInfo);
   1545 
   1546  void NoteClosedDatabase(DatabaseInfo& aDatabaseInfo);
   1547 
   1548  bool MaybeFireCallback(DatabaseCompleteCallback* aCallback);
   1549 
   1550  void PerformIdleDatabaseMaintenance(DatabaseInfo& aDatabaseInfo);
   1551 
   1552  void CloseDatabase(DatabaseInfo& aDatabaseInfo) const;
   1553 
   1554  bool CloseDatabaseWhenIdleInternal(const nsACString& aDatabaseId);
   1555 };
   1556 
   1557 class ConnectionPool::ConnectionRunnable : public Runnable {
   1558 protected:
   1559  DatabaseInfo& mDatabaseInfo;
   1560  nsCOMPtr<nsIEventTarget> mOwningEventTarget;
   1561 
   1562  explicit ConnectionRunnable(DatabaseInfo& aDatabaseInfo);
   1563 
   1564  ~ConnectionRunnable() override = default;
   1565 };
   1566 
   1567 class ConnectionPool::IdleConnectionRunnable final : public ConnectionRunnable {
   1568  const bool mNeedsCheckpoint;
   1569  Atomic<bool> mInterrupted;
   1570 
   1571 public:
   1572  IdleConnectionRunnable(DatabaseInfo& aDatabaseInfo, bool aNeedsCheckpoint)
   1573      : ConnectionRunnable(aDatabaseInfo), mNeedsCheckpoint(aNeedsCheckpoint) {}
   1574 
   1575  NS_INLINE_DECL_REFCOUNTING_INHERITED(IdleConnectionRunnable,
   1576                                       ConnectionRunnable)
   1577 
   1578  void Interrupt() { mInterrupted = true; }
   1579 
   1580 private:
   1581  ~IdleConnectionRunnable() override = default;
   1582 
   1583  NS_DECL_NSIRUNNABLE
   1584 };
   1585 
   1586 class ConnectionPool::CloseConnectionRunnable final
   1587    : public ConnectionRunnable {
   1588 public:
   1589  explicit CloseConnectionRunnable(DatabaseInfo& aDatabaseInfo)
   1590      : ConnectionRunnable(aDatabaseInfo) {}
   1591 
   1592  NS_INLINE_DECL_REFCOUNTING_INHERITED(CloseConnectionRunnable,
   1593                                       ConnectionRunnable)
   1594 
   1595 private:
   1596  ~CloseConnectionRunnable() override = default;
   1597 
   1598  NS_DECL_NSIRUNNABLE
   1599 };
   1600 
   1601 struct ConnectionPool::DatabaseInfo final {
   1602  friend mozilla::DefaultDelete<DatabaseInfo>;
   1603 
   1604  RefPtr<ConnectionPool> mConnectionPool;
   1605  const nsCString mDatabaseId;
   1606  RefPtr<DatabaseConnection> mConnection;
   1607  nsClassHashtable<nsStringHashKey, TransactionInfoPair> mBlockingTransactions;
   1608  nsTArray<NotNull<TransactionInfo*>> mTransactionsScheduledDuringClose;
   1609  nsTArray<NotNull<TransactionInfo*>> mScheduledWriteTransactions;
   1610  Maybe<TransactionInfo&> mRunningWriteTransaction;
   1611  RefPtr<TaskQueue> mEventTarget;
   1612  uint32_t mReadTransactionCount;
   1613  uint32_t mWriteTransactionCount;
   1614  bool mNeedsCheckpoint;
   1615  bool mIdle;
   1616  FlippedOnce<false> mCloseOnIdle;
   1617  bool mClosing;
   1618 
   1619 #ifdef DEBUG
   1620  nsISerialEventTarget* mDEBUGConnectionEventTarget;
   1621 #endif
   1622 
   1623  DatabaseInfo(ConnectionPool* aConnectionPool, const nsACString& aDatabaseId);
   1624 
   1625  void AssertIsOnConnectionThread() const {
   1626    MOZ_ASSERT(mDEBUGConnectionEventTarget);
   1627    MOZ_ASSERT(GetCurrentSerialEventTarget() == mDEBUGConnectionEventTarget);
   1628  }
   1629 
   1630  uint64_t TotalTransactionCount() const {
   1631    return mReadTransactionCount + mWriteTransactionCount;
   1632  }
   1633 
   1634  nsresult Dispatch(already_AddRefed<nsIRunnable> aRunnable);
   1635 
   1636 private:
   1637  ~DatabaseInfo();
   1638 
   1639  DatabaseInfo(const DatabaseInfo&) = delete;
   1640  DatabaseInfo& operator=(const DatabaseInfo&) = delete;
   1641 };
   1642 
   1643 struct ConnectionPool::DatabaseCompleteCallback final {
   1644  friend DefaultDelete<DatabaseCompleteCallback>;
   1645 
   1646  nsCString mDatabaseId;
   1647  nsCOMPtr<nsIRunnable> mCallback;
   1648 
   1649  DatabaseCompleteCallback(const nsCString& aDatabaseIds,
   1650                           nsIRunnable* aCallback);
   1651 
   1652 private:
   1653  ~DatabaseCompleteCallback();
   1654 };
   1655 
   1656 class NS_NO_VTABLE ConnectionPool::FinishCallback : public nsIRunnable {
   1657 public:
   1658  // Called on the owning thread before any additional transactions are
   1659  // unblocked.
   1660  virtual void TransactionFinishedBeforeUnblock() = 0;
   1661 
   1662  // Called on the owning thread after additional transactions may have been
   1663  // unblocked.
   1664  virtual void TransactionFinishedAfterUnblock() = 0;
   1665 
   1666 protected:
   1667  FinishCallback() = default;
   1668 
   1669  virtual ~FinishCallback() = default;
   1670 };
   1671 
   1672 class ConnectionPool::FinishCallbackWrapper final : public Runnable {
   1673  RefPtr<ConnectionPool> mConnectionPool;
   1674  RefPtr<FinishCallback> mCallback;
   1675  nsCOMPtr<nsIEventTarget> mOwningEventTarget;
   1676  uint64_t mTransactionId;
   1677  bool mHasRunOnce;
   1678 
   1679 public:
   1680  FinishCallbackWrapper(ConnectionPool* aConnectionPool,
   1681                        uint64_t aTransactionId, FinishCallback* aCallback);
   1682 
   1683  NS_INLINE_DECL_REFCOUNTING_INHERITED(FinishCallbackWrapper, Runnable)
   1684 
   1685 private:
   1686  ~FinishCallbackWrapper() override;
   1687 
   1688  NS_DECL_NSIRUNNABLE
   1689 };
   1690 
   1691 #ifdef DEBUG
   1692 
   1693 class ConnectionPool::TransactionRunnable final : public Runnable {
   1694 public:
   1695  explicit TransactionRunnable(nsCOMPtr<nsIRunnable> aRunnable);
   1696 
   1697 private:
   1698  NS_DECL_NSIRUNNABLE
   1699 
   1700  nsCOMPtr<nsIRunnable> mRunnable;
   1701 };
   1702 
   1703 #endif
   1704 
   1705 class ConnectionPool::TransactionInfo final {
   1706  friend mozilla::DefaultDelete<TransactionInfo>;
   1707 
   1708  nsTHashSet<TransactionInfo*> mBlocking;
   1709  nsTArray<NotNull<TransactionInfo*>> mBlockingOrdered;
   1710 
   1711 public:
   1712  DatabaseInfo& mDatabaseInfo;
   1713  const nsID mBackgroundChildLoggingId;
   1714  const nsCString mDatabaseId;
   1715  const uint64_t mTransactionId;
   1716  const int64_t mLoggingSerialNumber;
   1717  const nsTArray<nsString> mObjectStoreNames;
   1718  nsTHashSet<TransactionInfo*> mBlockedOn;
   1719  mozilla::Queue<nsCOMPtr<nsIRunnable>, 16> mQueuedOps;
   1720  const bool mIsWriteTransaction;
   1721  bool mRunning;
   1722  bool mRunningOp;
   1723 
   1724 #ifdef DEBUG
   1725  FlippedOnce<false> mFinished;
   1726 #endif
   1727 
   1728  TransactionInfo(DatabaseInfo& aDatabaseInfo,
   1729                  const nsID& aBackgroundChildLoggingId,
   1730                  const nsACString& aDatabaseId, uint64_t aTransactionId,
   1731                  int64_t aLoggingSerialNumber,
   1732                  const nsTArray<nsString>& aObjectStoreNames,
   1733                  bool aIsWriteTransaction,
   1734                  TransactionDatabaseOperationBase* aTransactionOp);
   1735 
   1736  void AddBlockingTransaction(TransactionInfo& aTransactionInfo);
   1737 
   1738  void RemoveBlockingTransactions();
   1739 
   1740  void SetRunning();
   1741 
   1742  void StartOp(nsCOMPtr<nsIRunnable> aRunnable);
   1743 
   1744  void FinishOp();
   1745 
   1746 private:
   1747  ~TransactionInfo();
   1748 
   1749  void MaybeUnblock(TransactionInfo& aTransactionInfo);
   1750 };
   1751 
   1752 struct ConnectionPool::TransactionInfoPair final {
   1753  // Multiple reading transactions can block future writes.
   1754  nsTArray<NotNull<TransactionInfo*>> mLastBlockingWrites;
   1755  // But only a single writing transaction can block future reads.
   1756  Maybe<TransactionInfo&> mLastBlockingReads;
   1757 
   1758 #if defined(DEBUG) || defined(NS_BUILD_REFCNT_LOGGING)
   1759  TransactionInfoPair();
   1760  ~TransactionInfoPair();
   1761 #endif
   1762 };
   1763 
   1764 /*******************************************************************************
   1765 * Actor class declarations
   1766 ******************************************************************************/
   1767 
   1768 template <IDBCursorType CursorType>
   1769 class CommonOpenOpHelper;
   1770 template <IDBCursorType CursorType>
   1771 class IndexOpenOpHelper;
   1772 template <IDBCursorType CursorType>
   1773 class ObjectStoreOpenOpHelper;
   1774 template <IDBCursorType CursorType>
   1775 class OpenOpHelper;
   1776 
   1777 class DatabaseOperationBase : public Runnable,
   1778                              public mozIStorageProgressHandler {
   1779  template <IDBCursorType CursorType>
   1780  friend class OpenOpHelper;
   1781 
   1782 protected:
   1783  class AutoSetProgressHandler;
   1784 
   1785  using UniqueIndexTable = nsTHashMap<nsUint64HashKey, bool>;
   1786 
   1787  const nsCOMPtr<nsIEventTarget> mOwningEventTarget;
   1788  const nsID mBackgroundChildLoggingId;
   1789  const uint64_t mLoggingSerialNumber;
   1790 
   1791 private:
   1792  nsresult mResultCode = NS_OK;
   1793  Atomic<bool> mOperationMayProceed;
   1794  FlippedOnce<false> mActorDestroyed;
   1795 
   1796 public:
   1797  NS_DECL_ISUPPORTS_INHERITED
   1798 
   1799  bool IsOnOwningThread() const {
   1800    MOZ_ASSERT(mOwningEventTarget);
   1801 
   1802    bool current;
   1803    return NS_SUCCEEDED(mOwningEventTarget->IsOnCurrentThread(&current)) &&
   1804           current;
   1805  }
   1806 
   1807  void AssertIsOnOwningThread() const {
   1808    MOZ_ASSERT(IsOnBackgroundThread());
   1809    MOZ_ASSERT(IsOnOwningThread());
   1810  }
   1811 
   1812  void NoteActorDestroyed() {
   1813    AssertIsOnOwningThread();
   1814 
   1815    mActorDestroyed.EnsureFlipped();
   1816    mOperationMayProceed = false;
   1817  }
   1818 
   1819  bool IsActorDestroyed() const {
   1820    AssertIsOnOwningThread();
   1821 
   1822    return mActorDestroyed;
   1823  }
   1824 
   1825  // May be called on any thread, but you should call IsActorDestroyed() if
   1826  // you know you're on the background thread because it is slightly faster.
   1827  bool OperationMayProceed() const { return mOperationMayProceed; }
   1828 
   1829  const nsID& BackgroundChildLoggingId() const {
   1830    return mBackgroundChildLoggingId;
   1831  }
   1832 
   1833  uint64_t LoggingSerialNumber() const { return mLoggingSerialNumber; }
   1834 
   1835  nsresult ResultCode() const { return mResultCode; }
   1836 
   1837  void SetFailureCode(nsresult aFailureCode) {
   1838    MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
   1839    OverrideFailureCode(aFailureCode);
   1840  }
   1841 
   1842  void SetFailureCodeIfUnset(nsresult aFailureCode) {
   1843    if (NS_SUCCEEDED(mResultCode)) {
   1844      OverrideFailureCode(aFailureCode);
   1845    }
   1846  }
   1847 
   1848  bool HasFailed() const { return NS_FAILED(mResultCode); }
   1849 
   1850 protected:
   1851  DatabaseOperationBase(const nsID& aBackgroundChildLoggingId,
   1852                        uint64_t aLoggingSerialNumber)
   1853      : Runnable("dom::indexedDB::DatabaseOperationBase"),
   1854        mOwningEventTarget(GetCurrentSerialEventTarget()),
   1855        mBackgroundChildLoggingId(aBackgroundChildLoggingId),
   1856        mLoggingSerialNumber(aLoggingSerialNumber),
   1857        mOperationMayProceed(true) {
   1858    AssertIsOnOwningThread();
   1859  }
   1860 
   1861  ~DatabaseOperationBase() override { MOZ_ASSERT(mActorDestroyed); }
   1862 
   1863  void OverrideFailureCode(nsresult aFailureCode) {
   1864    MOZ_ASSERT(NS_FAILED(aFailureCode));
   1865 
   1866    mResultCode = aFailureCode;
   1867  }
   1868 
   1869  static nsAutoCString MaybeGetBindingClauseForKeyRange(
   1870      const Maybe<SerializedKeyRange>& aOptionalKeyRange,
   1871      const nsACString& aKeyColumnName);
   1872 
   1873  static nsAutoCString GetBindingClauseForKeyRange(
   1874      const SerializedKeyRange& aKeyRange, const nsACString& aKeyColumnName);
   1875 
   1876  static uint64_t ReinterpretDoubleAsUInt64(double aDouble);
   1877 
   1878  static nsresult BindKeyRangeToStatement(const SerializedKeyRange& aKeyRange,
   1879                                          mozIStorageStatement* aStatement);
   1880 
   1881  static nsresult BindKeyRangeToStatement(const SerializedKeyRange& aKeyRange,
   1882                                          mozIStorageStatement* aStatement,
   1883                                          const nsCString& aLocale);
   1884 
   1885  static Result<IndexDataValuesAutoArray, nsresult>
   1886  IndexDataValuesFromUpdateInfos(const nsTArray<IndexUpdateInfo>& aUpdateInfos,
   1887                                 const UniqueIndexTable& aUniqueIndexTable);
   1888 
   1889  static nsresult InsertIndexTableRows(
   1890      DatabaseConnection* aConnection, IndexOrObjectStoreId aObjectStoreId,
   1891      const Key& aObjectStoreKey, const nsTArray<IndexDataValue>& aIndexValues);
   1892 
   1893  static nsresult DeleteIndexDataTableRows(
   1894      DatabaseConnection* aConnection, const Key& aObjectStoreKey,
   1895      const nsTArray<IndexDataValue>& aIndexValues);
   1896 
   1897  static nsresult DeleteObjectStoreDataTableRowsWithIndexes(
   1898      DatabaseConnection* aConnection, IndexOrObjectStoreId aObjectStoreId,
   1899      const Maybe<SerializedKeyRange>& aKeyRange);
   1900 
   1901  static nsresult UpdateIndexValues(
   1902      DatabaseConnection* aConnection, IndexOrObjectStoreId aObjectStoreId,
   1903      const Key& aObjectStoreKey, const nsTArray<IndexDataValue>& aIndexValues);
   1904 
   1905  static Result<bool, nsresult> ObjectStoreHasIndexes(
   1906      DatabaseConnection& aConnection, IndexOrObjectStoreId aObjectStoreId);
   1907 
   1908 private:
   1909  template <typename KeyTransformation>
   1910  static nsresult MaybeBindKeyToStatement(
   1911      const Key& aKey, mozIStorageStatement* aStatement,
   1912      const nsACString& aParameterName,
   1913      const KeyTransformation& aKeyTransformation);
   1914 
   1915  template <typename KeyTransformation>
   1916  static nsresult BindTransformedKeyRangeToStatement(
   1917      const SerializedKeyRange& aKeyRange, mozIStorageStatement* aStatement,
   1918      const KeyTransformation& aKeyTransformation);
   1919 
   1920  // Not to be overridden by subclasses.
   1921  NS_DECL_MOZISTORAGEPROGRESSHANDLER
   1922 };
   1923 
   1924 class MOZ_STACK_CLASS DatabaseOperationBase::AutoSetProgressHandler final {
   1925  Maybe<mozIStorageConnection&> mConnection;
   1926 #ifdef DEBUG
   1927  DatabaseOperationBase* mDEBUGDatabaseOp;
   1928 #endif
   1929 
   1930 public:
   1931  AutoSetProgressHandler();
   1932 
   1933  ~AutoSetProgressHandler();
   1934 
   1935  nsresult Register(mozIStorageConnection& aConnection,
   1936                    DatabaseOperationBase* aDatabaseOp);
   1937 
   1938  void Unregister();
   1939 };
   1940 
   1941 class TransactionDatabaseOperationBase : public DatabaseOperationBase {
   1942  enum class InternalState {
   1943    Initial,
   1944    DatabaseWork,
   1945    SendingPreprocess,
   1946    WaitingForContinue,
   1947    SendingResults,
   1948    Completed
   1949  };
   1950 
   1951  InitializedOnce<const NotNull<SafeRefPtr<TransactionBase>>> mTransaction;
   1952  // Unique request id within the context of the transaction, allocated by the
   1953  // transaction in the content process starting from 0. Values less than 0 are
   1954  // impossible and forbidden. Used to support the explicit commit() request.
   1955  const int64_t mRequestId;
   1956  InternalState mInternalState = InternalState::Initial;
   1957  bool mWaitingForContinue = false;
   1958  const bool mTransactionIsAborted;
   1959 
   1960 protected:
   1961  const int64_t mTransactionLoggingSerialNumber;
   1962 
   1963 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
   1964 protected:
   1965  // A check only enables when the diagnostic assert turns on. It assumes the
   1966  // mUpdateRefcountFunction is a nullptr because the previous
   1967  // StartTransactionOp failed on the connection thread and the next write
   1968  // operation (e.g. ObjectstoreAddOrPutRequestOp) doesn't have enough time to
   1969  // catch up the failure information.
   1970  bool mAssumingPreviousOperationFail = false;
   1971 #endif
   1972 
   1973 public:
   1974  void AssertIsOnConnectionThread() const
   1975 #ifdef DEBUG
   1976      ;
   1977 #else
   1978  {
   1979  }
   1980 #endif
   1981 
   1982  uint64_t StartOnConnectionPool(const nsID& aBackgroundChildLoggingId,
   1983                                 const nsACString& aDatabaseId,
   1984                                 int64_t aLoggingSerialNumber,
   1985                                 const nsTArray<nsString>& aObjectStoreNames,
   1986                                 bool aIsWriteTransaction);
   1987 
   1988  void DispatchToConnectionPool();
   1989 
   1990  TransactionBase& Transaction() { return **mTransaction; }
   1991 
   1992  const TransactionBase& Transaction() const { return **mTransaction; }
   1993 
   1994  bool IsWaitingForContinue() const {
   1995    AssertIsOnOwningThread();
   1996 
   1997    return mWaitingForContinue;
   1998  }
   1999 
   2000  void NoteContinueReceived();
   2001 
   2002  int64_t TransactionLoggingSerialNumber() const {
   2003    return mTransactionLoggingSerialNumber;
   2004  }
   2005 
   2006  // May be overridden by subclasses if they need to perform work on the
   2007  // background thread before being dispatched. Returning false will kill the
   2008  // child actors and prevent dispatch.
   2009  virtual bool Init(TransactionBase& aTransaction);
   2010 
   2011  // This callback will be called on the background thread before releasing the
   2012  // final reference to this request object. Subclasses may perform any
   2013  // additional cleanup here but must always call the base class implementation.
   2014  virtual void Cleanup();
   2015 
   2016 protected:
   2017  TransactionDatabaseOperationBase(SafeRefPtr<TransactionBase> aTransaction,
   2018                                   int64_t aRequestId);
   2019 
   2020  TransactionDatabaseOperationBase(SafeRefPtr<TransactionBase> aTransaction,
   2021                                   const int64_t aRequestId,
   2022                                   uint64_t aLoggingSerialNumber);
   2023 
   2024  ~TransactionDatabaseOperationBase() override;
   2025 
   2026  virtual void RunOnConnectionThread();
   2027 
   2028  // Must be overridden in subclasses. Called on the target thread to allow the
   2029  // subclass to perform necessary database or file operations. A successful
   2030  // return value will trigger a SendSuccessResult callback on the background
   2031  // thread while a failure value will trigger a SendFailureResult callback.
   2032  virtual nsresult DoDatabaseWork(DatabaseConnection* aConnection) = 0;
   2033 
   2034  // May be overriden in subclasses. Called on the background thread to decide
   2035  // if the subclass needs to send any preprocess info to the child actor.
   2036  virtual bool HasPreprocessInfo();
   2037 
   2038  // May be overriden in subclasses. Called on the background thread to allow
   2039  // the subclass to serialize its preprocess info and send it to the child
   2040  // actor. A successful return value will trigger a wait for a
   2041  // NoteContinueReceived callback on the background thread while a failure
   2042  // value will trigger a SendFailureResult callback.
   2043  virtual nsresult SendPreprocessInfo();
   2044 
   2045  // Must be overridden in subclasses. Called on the background thread to allow
   2046  // the subclass to serialize its results and send them to the child actor. A
   2047  // failed return value will trigger a SendFailureResult callback.
   2048  virtual nsresult SendSuccessResult() = 0;
   2049 
   2050  // Must be overridden in subclasses. Called on the background thread to allow
   2051  // the subclass to send its failure code. Returning false will cause the
   2052  // transaction to be aborted with aResultCode. Returning true will not cause
   2053  // the transaction to be aborted.
   2054  virtual bool SendFailureResult(nsresult aResultCode) = 0;
   2055 
   2056 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
   2057  auto MakeAutoSavepointCleanupHandler(DatabaseConnection& aConnection) {
   2058    return [this, &aConnection](const auto) {
   2059      if (!aConnection.GetUpdateRefcountFunction()) {
   2060        mAssumingPreviousOperationFail = true;
   2061      }
   2062    };
   2063  }
   2064 #endif
   2065 
   2066 private:
   2067  void SendToConnectionPool();
   2068 
   2069  void SendPreprocess();
   2070 
   2071  void SendResults();
   2072 
   2073  void SendPreprocessInfoOrResults(bool aSendPreprocessInfo);
   2074 
   2075  // Not to be overridden by subclasses.
   2076  NS_DECL_NSIRUNNABLE
   2077 };
   2078 
   2079 class Factory final : public PBackgroundIDBFactoryParent,
   2080                      public AtomicSafeRefCounted<Factory> {
   2081  nsCString mSystemLocale;
   2082  RefPtr<DatabaseLoggingInfo> mLoggingInfo;
   2083 
   2084 #ifdef DEBUG
   2085  bool mActorDestroyed;
   2086 #endif
   2087 
   2088  // Reference counted.
   2089  ~Factory() override;
   2090 
   2091 public:
   2092  [[nodiscard]] static SafeRefPtr<Factory> Create(
   2093      const LoggingInfo& aLoggingInfo, const nsACString& aSystemLocale);
   2094 
   2095  DatabaseLoggingInfo* GetLoggingInfo() const {
   2096    AssertIsOnBackgroundThread();
   2097    MOZ_ASSERT(mLoggingInfo);
   2098 
   2099    return mLoggingInfo;
   2100  }
   2101 
   2102  const nsCString& GetSystemLocale() const { return mSystemLocale; }
   2103 
   2104  MOZ_DECLARE_REFCOUNTED_TYPENAME(mozilla::dom::indexedDB::Factory)
   2105  MOZ_INLINE_DECL_SAFEREFCOUNTING_INHERITED(Factory, AtomicSafeRefCounted)
   2106 
   2107  // Only constructed in Create().
   2108  Factory(RefPtr<DatabaseLoggingInfo> aLoggingInfo,
   2109          const nsACString& aSystemLocale);
   2110 
   2111  // IPDL methods are only called by IPDL.
   2112  void ActorDestroy(ActorDestroyReason aWhy) override;
   2113 
   2114  mozilla::ipc::IPCResult RecvDeleteMe() override;
   2115 
   2116  PBackgroundIDBFactoryRequestParent* AllocPBackgroundIDBFactoryRequestParent(
   2117      const FactoryRequestParams& aParams) override;
   2118 
   2119  mozilla::ipc::IPCResult RecvPBackgroundIDBFactoryRequestConstructor(
   2120      PBackgroundIDBFactoryRequestParent* aActor,
   2121      const FactoryRequestParams& aParams) override;
   2122 
   2123  bool DeallocPBackgroundIDBFactoryRequestParent(
   2124      PBackgroundIDBFactoryRequestParent* aActor) override;
   2125 
   2126  mozilla::ipc::IPCResult RecvGetDatabases(
   2127      const PersistenceType& aPersistenceType,
   2128      const PrincipalInfo& aPrincipalInfo,
   2129      GetDatabasesResolver&& aResolve) override;
   2130 
   2131 private:
   2132  Maybe<ContentParentId> GetContentParentId() const;
   2133 };
   2134 
   2135 class WaitForTransactionsHelper final : public Runnable {
   2136  const nsCString mDatabaseId;
   2137  nsCOMPtr<nsIRunnable> mCallback;
   2138 
   2139  enum class State { Initial = 0, WaitingForTransactions, Complete } mState;
   2140 
   2141 public:
   2142  WaitForTransactionsHelper(const nsACString& aDatabaseId,
   2143                            nsIRunnable* aCallback)
   2144      : Runnable("dom::indexedDB::WaitForTransactionsHelper"),
   2145        mDatabaseId(aDatabaseId),
   2146        mCallback(aCallback),
   2147        mState(State::Initial) {
   2148    AssertIsOnBackgroundThread();
   2149    MOZ_ASSERT(!aDatabaseId.IsEmpty());
   2150    MOZ_ASSERT(aCallback);
   2151  }
   2152 
   2153  void WaitForTransactions();
   2154 
   2155  NS_INLINE_DECL_REFCOUNTING_INHERITED(WaitForTransactionsHelper, Runnable)
   2156 
   2157 private:
   2158  ~WaitForTransactionsHelper() override {
   2159    MOZ_ASSERT(!mCallback);
   2160    MOZ_ASSERT(mState == State::Complete);
   2161  }
   2162 
   2163  void MaybeWaitForTransactions();
   2164 
   2165  void CallCallback();
   2166 
   2167  NS_DECL_NSIRUNNABLE
   2168 };
   2169 
   2170 class Database final : public PBackgroundIDBDatabaseParent,
   2171                       public LinkedListElement<Database>,
   2172                       public AtomicSafeRefCounted<Database> {
   2173  friend class VersionChangeTransaction;
   2174 
   2175  class StartTransactionOp;
   2176  class UnmapBlobCallback;
   2177 
   2178 private:
   2179  SafeRefPtr<Factory> mFactory;
   2180  SafeRefPtr<FullDatabaseMetadata> mMetadata;
   2181  SafeRefPtr<DatabaseFileManager> mFileManager;
   2182  ClientDirectoryLockHandle mDirectoryLockHandle;
   2183  nsTHashSet<TransactionBase*> mTransactions;
   2184  nsTHashMap<nsIDHashKey, SafeRefPtr<DatabaseFileInfo>> mMappedBlobs;
   2185  RefPtr<DatabaseConnection> mConnection;
   2186  const PrincipalInfo mPrincipalInfo;
   2187  const Maybe<ContentParentId> mOptionalContentParentId;
   2188  // XXX Consider changing this to ClientMetadata.
   2189  const quota::OriginMetadata mOriginMetadata;
   2190  const nsCString mId;
   2191  const nsString mFilePath;
   2192  const Maybe<const CipherKey> mKey;
   2193  int64_t mDirectoryLockId;
   2194  const uint32_t mTelemetryId;
   2195  const PersistenceType mPersistenceType;
   2196  const bool mInPrivateBrowsing;
   2197  FlippedOnce<false> mClosed;
   2198  FlippedOnce<false> mInvalidated;
   2199  FlippedOnce<false> mActorWasAlive;
   2200  FlippedOnce<false> mActorDestroyed;
   2201  nsCOMPtr<nsIEventTarget> mBackgroundThread;
   2202 #ifdef DEBUG
   2203  bool mAllBlobsUnmapped;
   2204 #endif
   2205 
   2206 public:
   2207  // Created by OpenDatabaseOp.
   2208  Database(SafeRefPtr<Factory> aFactory, const PrincipalInfo& aPrincipalInfo,
   2209           const Maybe<ContentParentId>& aOptionalContentParentId,
   2210           const quota::OriginMetadata& aOriginMetadata, uint32_t aTelemetryId,
   2211           SafeRefPtr<FullDatabaseMetadata> aMetadata,
   2212           SafeRefPtr<DatabaseFileManager> aFileManager,
   2213           ClientDirectoryLockHandle aDirectoryLockHandle,
   2214           bool aInPrivateBrowsing, const Maybe<const CipherKey>& aMaybeKey);
   2215 
   2216  void AssertIsOnConnectionThread() const {
   2217 #ifdef DEBUG
   2218    // mConnection is used to cache the result from ConnectionPool's
   2219    // GetOrCreateConnection method (potentially avoiding a lock and a hash
   2220    // lookup). However, once the connection is closed, the task queue for the
   2221    // given database is also destroyed, so the connection, which caches the
   2222    // event target it was created on, is no longer reliable for asserting that
   2223    // the current thread is the connection thread (mConnection might be reset
   2224    // when EnsureConnection is called again, but in the meantime, we have to
   2225    // fallback to just checking the main thread and the PBackgroud thread).
   2226    if (mConnection && !mConnection->Closed()) {
   2227      mConnection->AssertIsOnConnectionThread();
   2228    } else {
   2229      MOZ_ASSERT(!NS_IsMainThread());
   2230      MOZ_ASSERT(!IsOnBackgroundThread());
   2231      MOZ_ASSERT(mInvalidated);
   2232    }
   2233 #endif
   2234  }
   2235 
   2236  NS_IMETHOD_(MozExternalRefCountType) AddRef() override {
   2237    return AtomicSafeRefCounted<Database>::AddRef();
   2238  }
   2239  NS_IMETHOD_(MozExternalRefCountType) Release() override {
   2240    return AtomicSafeRefCounted<Database>::Release();
   2241  }
   2242 
   2243  MOZ_DECLARE_REFCOUNTED_TYPENAME(mozilla::dom::indexedDB::Database)
   2244 
   2245  void Invalidate();
   2246 
   2247  bool IsOwnedByProcess(ContentParentId aContentParentId) const {
   2248    return mOptionalContentParentId &&
   2249           mOptionalContentParentId.value() == aContentParentId;
   2250  }
   2251 
   2252  const quota::OriginMetadata& OriginMetadata() const {
   2253    return mOriginMetadata;
   2254  }
   2255 
   2256  const nsCString& Id() const { return mId; }
   2257 
   2258  Maybe<ClientDirectoryLock&> MaybeDirectoryLockRef() const {
   2259    AssertIsOnBackgroundThread();
   2260 
   2261    return ToMaybeRef(mDirectoryLockHandle.get());
   2262  }
   2263 
   2264  int64_t DirectoryLockId() const { return mDirectoryLockId; }
   2265 
   2266  uint32_t TelemetryId() const { return mTelemetryId; }
   2267 
   2268  PersistenceType Type() const { return mPersistenceType; }
   2269 
   2270  const nsString& FilePath() const { return mFilePath; }
   2271 
   2272  DatabaseFileManager& GetFileManager() const { return *mFileManager; }
   2273 
   2274  MovingNotNull<SafeRefPtr<DatabaseFileManager>> GetFileManagerPtr() const {
   2275    return WrapMovingNotNull(mFileManager.clonePtr());
   2276  }
   2277 
   2278  const FullDatabaseMetadata& Metadata() const {
   2279    MOZ_ASSERT(mMetadata);
   2280    return *mMetadata;
   2281  }
   2282 
   2283  SafeRefPtr<FullDatabaseMetadata> MetadataPtr() const {
   2284    MOZ_ASSERT(mMetadata);
   2285    return mMetadata.clonePtr();
   2286  }
   2287 
   2288  PBackgroundParent* GetBackgroundParent() const {
   2289    AssertIsOnBackgroundThread();
   2290    MOZ_ASSERT(!IsActorDestroyed());
   2291 
   2292    return Manager()->Manager();
   2293  }
   2294 
   2295  DatabaseLoggingInfo* GetLoggingInfo() const {
   2296    AssertIsOnBackgroundThread();
   2297    MOZ_ASSERT(mFactory);
   2298 
   2299    return mFactory->GetLoggingInfo();
   2300  }
   2301 
   2302  bool RegisterTransaction(TransactionBase& aTransaction);
   2303 
   2304  void UnregisterTransaction(TransactionBase& aTransaction);
   2305 
   2306  void SetActorAlive();
   2307 
   2308  void MapBlob(const IPCBlob& aIPCBlob, SafeRefPtr<DatabaseFileInfo> aFileInfo);
   2309 
   2310  bool IsActorAlive() const {
   2311    AssertIsOnBackgroundThread();
   2312 
   2313    return mActorWasAlive && !mActorDestroyed;
   2314  }
   2315 
   2316  bool IsActorDestroyed() const {
   2317    AssertIsOnBackgroundThread();
   2318 
   2319    return mActorWasAlive && mActorDestroyed;
   2320  }
   2321 
   2322  bool IsClosed() const {
   2323    AssertIsOnBackgroundThread();
   2324 
   2325    return mClosed;
   2326  }
   2327 
   2328  bool IsInvalidated() const {
   2329    AssertIsOnBackgroundThread();
   2330 
   2331    return mInvalidated;
   2332  }
   2333 
   2334  nsresult EnsureConnection();
   2335 
   2336  DatabaseConnection* GetConnection() const {
   2337 #ifdef DEBUG
   2338    if (mConnection) {
   2339      mConnection->AssertIsOnConnectionThread();
   2340    }
   2341 #endif
   2342 
   2343    return mConnection;
   2344  }
   2345 
   2346  void Stringify(nsACString& aResult) const;
   2347 
   2348  bool IsInPrivateBrowsing() const {
   2349    AssertIsOnBackgroundThread();
   2350    return mInPrivateBrowsing;
   2351  }
   2352 
   2353  const Maybe<const CipherKey>& MaybeKeyRef() const {
   2354    // This can be called on any thread, as it is const.
   2355    MOZ_ASSERT(mKey.isSome() == mInPrivateBrowsing);
   2356    return mKey;
   2357  }
   2358 
   2359  ~Database() override {
   2360    MOZ_ASSERT(mClosed);
   2361    MOZ_ASSERT_IF(mActorWasAlive, mActorDestroyed);
   2362    MOZ_DIAGNOSTIC_ASSERT(!isInList());
   2363 
   2364    NS_ProxyRelease("ReleaseIDBFactory", mBackgroundThread.get(),
   2365                    mFactory.forget());
   2366  }
   2367 
   2368 private:
   2369  [[nodiscard]] SafeRefPtr<DatabaseFileInfo> GetBlob(const IPCBlob& aIPCBlob);
   2370 
   2371  void UnmapBlob(const nsID& aID);
   2372 
   2373  void UnmapAllBlobs();
   2374 
   2375  bool CloseInternal();
   2376 
   2377  void MaybeCloseConnection();
   2378 
   2379  void ConnectionClosedCallback();
   2380 
   2381  void CleanupMetadata();
   2382 
   2383  // IPDL methods are only called by IPDL.
   2384  void ActorDestroy(ActorDestroyReason aWhy) override;
   2385 
   2386  PBackgroundIDBDatabaseFileParent* AllocPBackgroundIDBDatabaseFileParent(
   2387      const IPCBlob& aIPCBlob) override;
   2388 
   2389  bool DeallocPBackgroundIDBDatabaseFileParent(
   2390      PBackgroundIDBDatabaseFileParent* aActor) override;
   2391 
   2392  already_AddRefed<PBackgroundIDBTransactionParent>
   2393  AllocPBackgroundIDBTransactionParent(
   2394      const nsTArray<nsString>& aObjectStoreNames, const Mode& aMode,
   2395      const Durability& aDurability) override;
   2396 
   2397  mozilla::ipc::IPCResult RecvPBackgroundIDBTransactionConstructor(
   2398      PBackgroundIDBTransactionParent* aActor,
   2399      nsTArray<nsString>&& aObjectStoreNames, const Mode& aMode,
   2400      const Durability& aDurability) override;
   2401 
   2402  mozilla::ipc::IPCResult RecvDeleteMe() override;
   2403 
   2404  mozilla::ipc::IPCResult RecvBlocked() override;
   2405 
   2406  mozilla::ipc::IPCResult RecvClose() override;
   2407 
   2408  template <typename T>
   2409  static bool InvalidateAll(const nsTBaseHashSet<nsPtrHashKey<T>>& aTable);
   2410 };
   2411 
   2412 class Database::StartTransactionOp final
   2413    : public TransactionDatabaseOperationBase {
   2414  friend class Database;
   2415 
   2416 private:
   2417  explicit StartTransactionOp(SafeRefPtr<TransactionBase> aTransaction)
   2418      : TransactionDatabaseOperationBase(std::move(aTransaction),
   2419                                         /* aRequestId */ 0,
   2420                                         /* aLoggingSerialNumber */ 0) {}
   2421 
   2422  ~StartTransactionOp() override = default;
   2423 
   2424  void RunOnConnectionThread() override;
   2425 
   2426  nsresult DoDatabaseWork(DatabaseConnection* aConnection) override;
   2427 
   2428  nsresult SendSuccessResult() override;
   2429 
   2430  bool SendFailureResult(nsresult aResultCode) override;
   2431 
   2432  void Cleanup() override;
   2433 };
   2434 
   2435 class Database::UnmapBlobCallback final
   2436    : public RemoteLazyInputStreamParentCallback {
   2437  SafeRefPtr<Database> mDatabase;
   2438  nsCOMPtr<nsISerialEventTarget> mBackgroundThread;
   2439 
   2440 public:
   2441  explicit UnmapBlobCallback(SafeRefPtr<Database> aDatabase)
   2442      : mDatabase(std::move(aDatabase)),
   2443        mBackgroundThread(GetCurrentSerialEventTarget()) {
   2444    AssertIsOnBackgroundThread();
   2445  }
   2446 
   2447  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(Database::UnmapBlobCallback, override)
   2448 
   2449  void ActorDestroyed(const nsID& aID) override {
   2450    MOZ_ASSERT(mDatabase);
   2451    mBackgroundThread->Dispatch(NS_NewRunnableFunction(
   2452        "UnmapBlobCallback", [aID, database = std::move(mDatabase)] {
   2453          AssertIsOnBackgroundThread();
   2454          database->UnmapBlob(aID);
   2455        }));
   2456  }
   2457 
   2458 private:
   2459  ~UnmapBlobCallback() = default;
   2460 };
   2461 
   2462 /**
   2463 * In coordination with IDBDatabase's mFileActors weak-map on the child side, a
   2464 * long-lived mapping from a child process's live Blobs to their corresponding
   2465 * DatabaseFileInfo in our owning database.  Assists in avoiding redundant IPC
   2466 * traffic and disk storage.  This includes both:
   2467 * - Blobs retrieved from this database and sent to the child that do not need
   2468 *   to be written to disk because they already exist on disk in this database's
   2469 *   files directory.
   2470 * - Blobs retrieved from other databases or from anywhere else that will need
   2471 *   to be written to this database's files directory.  In this case we will
   2472 *   hold a reference to its BlobImpl in mBlobImpl until we have successfully
   2473 *   written the Blob to disk.
   2474 *
   2475 * Relevant Blob context: Blobs sent from the parent process to child processes
   2476 * are automatically linked back to their source BlobImpl when the child process
   2477 * references the Blob via IPC. This is done using the internal IPCBlob
   2478 * inputStream actor ID to DatabaseFileInfo mapping. However, when getting an
   2479 * actor in the child process for sending an in-child-created Blob to the
   2480 * parent process, there is (currently) no Blob machinery to automatically
   2481 * establish and reuse a long-lived Actor.  As a result, without IDB's weak-map
   2482 * cleverness, a memory-backed Blob repeatedly sent from the child to the parent
   2483 * would appear as a different Blob each time, requiring the Blob data to be
   2484 * sent over IPC each time as well as potentially needing to be written to disk
   2485 * each time.
   2486 *
   2487 * This object remains alive as long as there is an active child actor or an
   2488 * ObjectStoreAddOrPutRequestOp::StoredFileInfo for a queued or active add/put
   2489 * op is holding a reference to us.
   2490 */
   2491 class DatabaseFile final : public PBackgroundIDBDatabaseFileParent {
   2492  // mBlobImpl's ownership lifecycle:
   2493  // - Initialized on the background thread at creation time.  Then
   2494  //   responsibility is handed off to the connection thread.
   2495  // - Checked and used by the connection thread to generate a stream to write
   2496  //   the blob to disk by an add/put operation.
   2497  // - Cleared on the connection thread once the file has successfully been
   2498  //   written to disk.
   2499  InitializedOnce<const RefPtr<BlobImpl>> mBlobImpl;
   2500  const SafeRefPtr<DatabaseFileInfo> mFileInfo;
   2501 
   2502 public:
   2503  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::DatabaseFile);
   2504 
   2505  const DatabaseFileInfo& GetFileInfo() const {
   2506    AssertIsOnBackgroundThread();
   2507 
   2508    return *mFileInfo;
   2509  }
   2510 
   2511  SafeRefPtr<DatabaseFileInfo> GetFileInfoPtr() const {
   2512    AssertIsOnBackgroundThread();
   2513 
   2514    return mFileInfo.clonePtr();
   2515  }
   2516 
   2517  /**
   2518   * If mBlobImpl is non-null (implying the contents of this file have not yet
   2519   * been written to disk), then return an input stream. Otherwise, if mBlobImpl
   2520   * is null (because the contents have been written to disk), returns null.
   2521   */
   2522  [[nodiscard]] nsCOMPtr<nsIInputStream> GetInputStream(ErrorResult& rv) const;
   2523 
   2524  /**
   2525   * To be called upon successful copying of the stream GetInputStream()
   2526   * returned so that we won't try and redundantly write the file to disk in the
   2527   * future.  This is a separate step from GetInputStream() because
   2528   * the write could fail due to quota errors that happen now but that might
   2529   * not happen in a future attempt.
   2530   */
   2531  void WriteSucceededClearBlobImpl() {
   2532    MOZ_ASSERT(!IsOnBackgroundThread());
   2533 
   2534    MOZ_ASSERT(*mBlobImpl);
   2535    mBlobImpl.destroy();
   2536  }
   2537 
   2538 public:
   2539  // Called when sending to the child.
   2540  explicit DatabaseFile(SafeRefPtr<DatabaseFileInfo> aFileInfo)
   2541      : mBlobImpl{nullptr}, mFileInfo(std::move(aFileInfo)) {
   2542    AssertIsOnBackgroundThread();
   2543    MOZ_ASSERT(mFileInfo);
   2544  }
   2545 
   2546  // Called when receiving from the child.
   2547  DatabaseFile(RefPtr<BlobImpl> aBlobImpl,
   2548               SafeRefPtr<DatabaseFileInfo> aFileInfo)
   2549      : mBlobImpl(std::move(aBlobImpl)), mFileInfo(std::move(aFileInfo)) {
   2550    AssertIsOnBackgroundThread();
   2551    MOZ_ASSERT(*mBlobImpl);
   2552    MOZ_ASSERT(mFileInfo);
   2553  }
   2554 
   2555 private:
   2556  ~DatabaseFile() override = default;
   2557 
   2558  void ActorDestroy(ActorDestroyReason aWhy) override {
   2559    AssertIsOnBackgroundThread();
   2560  }
   2561 };
   2562 
   2563 nsCOMPtr<nsIInputStream> DatabaseFile::GetInputStream(ErrorResult& rv) const {
   2564  // We should only be called from our DB connection thread, not the background
   2565  // thread.
   2566  MOZ_ASSERT(!IsOnBackgroundThread());
   2567 
   2568  // If we were constructed without a BlobImpl, or WriteSucceededClearBlobImpl
   2569  // was already called, return nullptr.
   2570  if (!mBlobImpl || !*mBlobImpl) {
   2571    return nullptr;
   2572  }
   2573 
   2574  nsCOMPtr<nsIInputStream> inputStream;
   2575  (*mBlobImpl)->CreateInputStream(getter_AddRefs(inputStream), rv);
   2576  if (rv.Failed()) {
   2577    return nullptr;
   2578  }
   2579 
   2580  return inputStream;
   2581 }
   2582 
   2583 class TransactionBase : public AtomicSafeRefCounted<TransactionBase> {
   2584  friend class CursorBase;
   2585 
   2586  template <IDBCursorType CursorType>
   2587  friend class Cursor;
   2588 
   2589  class CommitOp;
   2590 
   2591 protected:
   2592  using Mode = IDBTransaction::Mode;
   2593  using Durability = IDBTransaction::Durability;
   2594 
   2595 private:
   2596  const SafeRefPtr<Database> mDatabase;
   2597  nsTArray<SafeRefPtr<FullObjectStoreMetadata>>
   2598      mModifiedAutoIncrementObjectStoreMetadataArray;
   2599  LazyInitializedOnceNotNull<const uint64_t> mTransactionId;
   2600  const nsCString mDatabaseId;
   2601  const int64_t mLoggingSerialNumber;
   2602  uint64_t mActiveRequestCount;
   2603  Atomic<bool> mInvalidatedOnAnyThread;
   2604  const Mode mMode;
   2605  const Durability mDurability;
   2606  FlippedOnce<false> mInitialized;
   2607  FlippedOnce<false> mHasBeenActiveOnConnectionThread;
   2608  FlippedOnce<false> mActorDestroyed;
   2609  FlippedOnce<false> mInvalidated;
   2610 
   2611 protected:
   2612  nsresult mResultCode;
   2613  FlippedOnce<false> mCommitOrAbortReceived;
   2614  FlippedOnce<false> mCommittedOrAborted;
   2615  FlippedOnce<false> mForceAborted;
   2616  LazyInitializedOnce<const Maybe<int64_t>> mLastRequestBeforeCommit;
   2617  Maybe<int64_t> mLastFailedRequest;
   2618 
   2619 public:
   2620  void AssertIsOnConnectionThread() const {
   2621    MOZ_ASSERT(mDatabase);
   2622    mDatabase->AssertIsOnConnectionThread();
   2623  }
   2624 
   2625  bool IsActorDestroyed() const {
   2626    AssertIsOnBackgroundThread();
   2627 
   2628    return mActorDestroyed;
   2629  }
   2630 
   2631  // Must be called on the background thread.
   2632  bool IsInvalidated() const {
   2633    MOZ_ASSERT(IsOnBackgroundThread(), "Use IsInvalidatedOnAnyThread()");
   2634    MOZ_ASSERT_IF(mInvalidated, NS_FAILED(mResultCode));
   2635 
   2636    return mInvalidated;
   2637  }
   2638 
   2639  // May be called on any thread, but is more expensive than IsInvalidated().
   2640  bool IsInvalidatedOnAnyThread() const { return mInvalidatedOnAnyThread; }
   2641 
   2642  void Init(const uint64_t aTransactionId) {
   2643    AssertIsOnBackgroundThread();
   2644    MOZ_ASSERT(aTransactionId);
   2645 
   2646    mTransactionId.init(aTransactionId);
   2647    mInitialized.Flip();
   2648  }
   2649 
   2650  void SetActiveOnConnectionThread() {
   2651    AssertIsOnConnectionThread();
   2652    mHasBeenActiveOnConnectionThread.Flip();
   2653  }
   2654 
   2655  MOZ_DECLARE_REFCOUNTED_TYPENAME(mozilla::dom::indexedDB::TransactionBase)
   2656 
   2657  void Abort(nsresult aResultCode, bool aForce);
   2658 
   2659  uint64_t TransactionId() const { return *mTransactionId; }
   2660 
   2661  const nsACString& DatabaseId() const { return mDatabaseId; }
   2662 
   2663  Mode GetMode() const { return mMode; }
   2664 
   2665  Durability GetDurability() const { return mDurability; }
   2666 
   2667  const Database& GetDatabase() const {
   2668    MOZ_ASSERT(mDatabase);
   2669 
   2670    return *mDatabase;
   2671  }
   2672 
   2673  Database& GetMutableDatabase() const {
   2674    MOZ_ASSERT(mDatabase);
   2675 
   2676    return *mDatabase;
   2677  }
   2678 
   2679  SafeRefPtr<Database> GetDatabasePtr() const {
   2680    MOZ_ASSERT(mDatabase);
   2681 
   2682    return mDatabase.clonePtr();
   2683  }
   2684 
   2685  DatabaseLoggingInfo* GetLoggingInfo() const {
   2686    AssertIsOnBackgroundThread();
   2687    MOZ_ASSERT(mDatabase);
   2688 
   2689    return mDatabase->GetLoggingInfo();
   2690  }
   2691 
   2692  int64_t LoggingSerialNumber() const { return mLoggingSerialNumber; }
   2693 
   2694  bool IsAborted() const {
   2695    AssertIsOnBackgroundThread();
   2696 
   2697    return NS_FAILED(mResultCode);
   2698  }
   2699 
   2700  [[nodiscard]] SafeRefPtr<FullObjectStoreMetadata> GetMetadataForObjectStoreId(
   2701      IndexOrObjectStoreId aObjectStoreId) const;
   2702 
   2703  [[nodiscard]] SafeRefPtr<FullIndexMetadata> GetMetadataForIndexId(
   2704      FullObjectStoreMetadata& aObjectStoreMetadata,
   2705      IndexOrObjectStoreId aIndexId) const;
   2706 
   2707  PBackgroundParent* GetBackgroundParent() const {
   2708    AssertIsOnBackgroundThread();
   2709    MOZ_ASSERT(!IsActorDestroyed());
   2710 
   2711    return GetDatabase().GetBackgroundParent();
   2712  }
   2713 
   2714  void NoteModifiedAutoIncrementObjectStore(
   2715      const SafeRefPtr<FullObjectStoreMetadata>& aMetadata);
   2716 
   2717  void ForgetModifiedAutoIncrementObjectStore(
   2718      FullObjectStoreMetadata& aMetadata);
   2719 
   2720  void NoteActiveRequest();
   2721 
   2722  void NoteFinishedRequest(int64_t aRequestId, nsresult aResultCode);
   2723 
   2724  void Invalidate();
   2725 
   2726  virtual ~TransactionBase();
   2727 
   2728 protected:
   2729  TransactionBase(SafeRefPtr<Database> aDatabase, Mode aMode,
   2730                  Durability aDurability);
   2731 
   2732  void NoteActorDestroyed() {
   2733    AssertIsOnBackgroundThread();
   2734 
   2735    mActorDestroyed.Flip();
   2736  }
   2737 
   2738 #ifdef DEBUG
   2739  // Only called by VersionChangeTransaction.
   2740  void FakeActorDestroyed() { mActorDestroyed.EnsureFlipped(); }
   2741 #endif
   2742 
   2743  mozilla::ipc::IPCResult RecvCommit(IProtocol* aActor,
   2744                                     const Maybe<int64_t> aLastRequest);
   2745 
   2746  mozilla::ipc::IPCResult RecvAbort(IProtocol* aActor, nsresult aResultCode);
   2747 
   2748  void MaybeCommitOrAbort() {
   2749    AssertIsOnBackgroundThread();
   2750 
   2751    // If we've already committed or aborted then there's nothing else to do.
   2752    if (mCommittedOrAborted) {
   2753      return;
   2754    }
   2755 
   2756    // If there are active requests then we have to wait for those requests to
   2757    // complete (see NoteFinishedRequest).
   2758    if (mActiveRequestCount) {
   2759      return;
   2760    }
   2761 
   2762    // If we haven't yet received a commit or abort message then there could be
   2763    // additional requests coming so we should wait unless we're being forced to
   2764    // abort.
   2765    if (!mCommitOrAbortReceived && !mForceAborted) {
   2766      return;
   2767    }
   2768 
   2769    CommitOrAbort();
   2770  }
   2771 
   2772  PBackgroundIDBRequestParent* AllocRequest(const int64_t aRequestId,
   2773                                            RequestParams&& aParams,
   2774                                            bool aTrustParams);
   2775 
   2776  bool StartRequest(PBackgroundIDBRequestParent* aActor);
   2777 
   2778  bool DeallocRequest(PBackgroundIDBRequestParent* aActor);
   2779 
   2780  already_AddRefed<PBackgroundIDBCursorParent> AllocCursor(
   2781      const OpenCursorParams& aParams, bool aTrustParams);
   2782 
   2783  bool StartCursor(PBackgroundIDBCursorParent* aActor, const int64_t aRequestId,
   2784                   const OpenCursorParams& aParams);
   2785 
   2786  virtual void UpdateMetadata(nsresult aResult) {}
   2787 
   2788  virtual void SendCompleteNotification(nsresult aResult) = 0;
   2789 
   2790 private:
   2791  bool VerifyRequestParams(const RequestParams& aParams) const;
   2792 
   2793  bool VerifyRequestParams(const SerializedKeyRange& aParams) const;
   2794 
   2795  bool VerifyRequestParams(const ObjectStoreAddPutParams& aParams) const;
   2796 
   2797  bool VerifyRequestParams(const Maybe<SerializedKeyRange>& aParams) const;
   2798 
   2799  void CommitOrAbort();
   2800 };
   2801 
   2802 class TransactionBase::CommitOp final : public DatabaseOperationBase,
   2803                                        public ConnectionPool::FinishCallback {
   2804  friend class TransactionBase;
   2805 
   2806  SafeRefPtr<TransactionBase> mTransaction;
   2807  nsresult mResultCode;  ///< TODO: There is also a mResultCode in
   2808                         ///< DatabaseOperationBase. Is there a reason not to
   2809                         ///< use that? At least a more specific name should be
   2810                         ///< given to this one.
   2811 
   2812 private:
   2813  CommitOp(SafeRefPtr<TransactionBase> aTransaction, nsresult aResultCode);
   2814 
   2815  ~CommitOp() override = default;
   2816 
   2817  // Writes new autoIncrement counts to database.
   2818  nsresult WriteAutoIncrementCounts();
   2819 
   2820  // Updates counts after a database activity has finished.
   2821  void CommitOrRollbackAutoIncrementCounts();
   2822 
   2823  void AssertForeignKeyConsistency(DatabaseConnection* aConnection)
   2824 #ifdef DEBUG
   2825      ;
   2826 #else
   2827  {
   2828  }
   2829 #endif
   2830 
   2831  NS_DECL_NSIRUNNABLE
   2832 
   2833  void TransactionFinishedBeforeUnblock() override;
   2834 
   2835  void TransactionFinishedAfterUnblock() override;
   2836 
   2837 public:
   2838  // We need to declare all of nsISupports, because FinishCallback has
   2839  // a pure-virtual nsISupports declaration.
   2840  NS_DECL_ISUPPORTS_INHERITED
   2841 };
   2842 
   2843 class NormalTransaction final : public TransactionBase,
   2844                                public PBackgroundIDBTransactionParent {
   2845  nsTArray<SafeRefPtr<FullObjectStoreMetadata>> mObjectStores;
   2846 
   2847  // Reference counted.
   2848  ~NormalTransaction() override = default;
   2849 
   2850  bool IsSameProcessActor();
   2851 
   2852  // Only called by TransactionBase.
   2853  void SendCompleteNotification(nsresult aResult) override;
   2854 
   2855  // IPDL methods are only called by IPDL.
   2856  void ActorDestroy(ActorDestroyReason aWhy) override;
   2857 
   2858  mozilla::ipc::IPCResult RecvDeleteMe() override;
   2859 
   2860  mozilla::ipc::IPCResult RecvCommit(
   2861      const Maybe<int64_t>& aLastRequest) override;
   2862 
   2863  mozilla::ipc::IPCResult RecvAbort(const nsresult& aResultCode) override;
   2864 
   2865  PBackgroundIDBRequestParent* AllocPBackgroundIDBRequestParent(
   2866      const int64_t& aRequestId, const RequestParams& aParams) override;
   2867 
   2868  mozilla::ipc::IPCResult RecvPBackgroundIDBRequestConstructor(
   2869      PBackgroundIDBRequestParent* aActor, const int64_t& aRequestId,
   2870      const RequestParams& aParams) override;
   2871 
   2872  bool DeallocPBackgroundIDBRequestParent(
   2873      PBackgroundIDBRequestParent* aActor) override;
   2874 
   2875  already_AddRefed<PBackgroundIDBCursorParent> AllocPBackgroundIDBCursorParent(
   2876      const int64_t& aRequestId, const OpenCursorParams& aParams) override;
   2877 
   2878  mozilla::ipc::IPCResult RecvPBackgroundIDBCursorConstructor(
   2879      PBackgroundIDBCursorParent* aActor, const int64_t& aRequestId,
   2880      const OpenCursorParams& aParams) override;
   2881 
   2882 public:
   2883  // This constructor is only called by Database.
   2884  NormalTransaction(
   2885      SafeRefPtr<Database> aDatabase, TransactionBase::Mode aMode,
   2886      TransactionBase::Durability aDurability,
   2887      nsTArray<SafeRefPtr<FullObjectStoreMetadata>>&& aObjectStores);
   2888 
   2889  MOZ_INLINE_DECL_SAFEREFCOUNTING_INHERITED(NormalTransaction, TransactionBase)
   2890 };
   2891 
   2892 class VersionChangeTransaction final
   2893    : public TransactionBase,
   2894      public PBackgroundIDBVersionChangeTransactionParent {
   2895  friend class OpenDatabaseOp;
   2896 
   2897  RefPtr<OpenDatabaseOp> mOpenDatabaseOp;
   2898  SafeRefPtr<FullDatabaseMetadata> mOldMetadata;
   2899 
   2900  FlippedOnce<false> mActorWasAlive;
   2901 
   2902 public:
   2903  // Only called by OpenDatabaseOp.
   2904  explicit VersionChangeTransaction(OpenDatabaseOp* aOpenDatabaseOp);
   2905 
   2906  MOZ_INLINE_DECL_SAFEREFCOUNTING_INHERITED(VersionChangeTransaction,
   2907                                            TransactionBase)
   2908 
   2909 private:
   2910  // Reference counted.
   2911  ~VersionChangeTransaction() override;
   2912 
   2913  bool IsSameProcessActor();
   2914 
   2915  // Only called by OpenDatabaseOp.
   2916  bool CopyDatabaseMetadata();
   2917 
   2918  void SetActorAlive();
   2919 
   2920  // Only called by TransactionBase.
   2921  void UpdateMetadata(nsresult aResult) override;
   2922 
   2923  // Only called by TransactionBase.
   2924  void SendCompleteNotification(nsresult aResult) override;
   2925 
   2926  // IPDL methods are only called by IPDL.
   2927  void ActorDestroy(ActorDestroyReason aWhy) override;
   2928 
   2929  mozilla::ipc::IPCResult RecvDeleteMe() override;
   2930 
   2931  mozilla::ipc::IPCResult RecvCommit(
   2932      const Maybe<int64_t>& aLastRequest) override;
   2933 
   2934  mozilla::ipc::IPCResult RecvAbort(const nsresult& aResultCode) override;
   2935 
   2936  mozilla::ipc::IPCResult RecvCreateObjectStore(
   2937      const ObjectStoreMetadata& aMetadata) override;
   2938 
   2939  mozilla::ipc::IPCResult RecvDeleteObjectStore(
   2940      const IndexOrObjectStoreId& aObjectStoreId) override;
   2941 
   2942  mozilla::ipc::IPCResult RecvRenameObjectStore(
   2943      const IndexOrObjectStoreId& aObjectStoreId,
   2944      const nsAString& aName) override;
   2945 
   2946  mozilla::ipc::IPCResult RecvCreateIndex(
   2947      const IndexOrObjectStoreId& aObjectStoreId,
   2948      const IndexMetadata& aMetadata) override;
   2949 
   2950  mozilla::ipc::IPCResult RecvDeleteIndex(
   2951      const IndexOrObjectStoreId& aObjectStoreId,
   2952      const IndexOrObjectStoreId& aIndexId) override;
   2953 
   2954  mozilla::ipc::IPCResult RecvRenameIndex(
   2955      const IndexOrObjectStoreId& aObjectStoreId,
   2956      const IndexOrObjectStoreId& aIndexId, const nsAString& aName) override;
   2957 
   2958  PBackgroundIDBRequestParent* AllocPBackgroundIDBRequestParent(
   2959      const int64_t& aRequestId, const RequestParams& aParams) override;
   2960 
   2961  mozilla::ipc::IPCResult RecvPBackgroundIDBRequestConstructor(
   2962      PBackgroundIDBRequestParent* aActor, const int64_t& aRequestId,
   2963      const RequestParams& aParams) override;
   2964 
   2965  bool DeallocPBackgroundIDBRequestParent(
   2966      PBackgroundIDBRequestParent* aActor) override;
   2967 
   2968  already_AddRefed<PBackgroundIDBCursorParent> AllocPBackgroundIDBCursorParent(
   2969      const int64_t& aRequestId, const OpenCursorParams& aParams) override;
   2970 
   2971  mozilla::ipc::IPCResult RecvPBackgroundIDBCursorConstructor(
   2972      PBackgroundIDBCursorParent* aActor, const int64_t& aRequestId,
   2973      const OpenCursorParams& aParams) override;
   2974 };
   2975 
   2976 class FactoryOp : public DatabaseOperationBase,
   2977                  public LinkedListElement<FactoryOp> {
   2978 public:
   2979  struct MaybeBlockedDatabaseInfo final {
   2980    SafeRefPtr<Database> mDatabase;
   2981    bool mBlocked;
   2982 
   2983    MaybeBlockedDatabaseInfo(MaybeBlockedDatabaseInfo&&) = default;
   2984    MaybeBlockedDatabaseInfo& operator=(MaybeBlockedDatabaseInfo&&) = default;
   2985 
   2986    MOZ_IMPLICIT MaybeBlockedDatabaseInfo(SafeRefPtr<Database> aDatabase)
   2987        : mDatabase(std::move(aDatabase)), mBlocked(false) {
   2988      MOZ_ASSERT(mDatabase);
   2989 
   2990      MOZ_COUNT_CTOR(FactoryOp::MaybeBlockedDatabaseInfo);
   2991    }
   2992 
   2993    ~MaybeBlockedDatabaseInfo() {
   2994      MOZ_COUNT_DTOR(FactoryOp::MaybeBlockedDatabaseInfo);
   2995    }
   2996 
   2997    bool operator==(const Database* aOther) const {
   2998      return mDatabase == aOther;
   2999    }
   3000 
   3001    Database* operator->() const& MOZ_NO_ADDREF_RELEASE_ON_RETURN {
   3002      return mDatabase.unsafeGetRawPtr();
   3003    }
   3004  };
   3005 
   3006 protected:
   3007  enum class State {
   3008    // Just created on the PBackground thread, dispatched to the current thread.
   3009    // Next step is either SendingResults if opening initialization failed, or
   3010    // DirectoryOpenPending if the opening initialization succeeded.
   3011    Initial,
   3012 
   3013    // Waiting for directory open allowed on the PBackground thread. The next
   3014    // step is either SendingResults if directory lock failed to acquire, or
   3015    // DirectoryWorkOpen if the factory operation is not tied up to a specific
   3016    // database, or DatabaseOpenPending otherwise.
   3017    DirectoryOpenPending,
   3018 
   3019    // Waiting to do/doing directory work on the QuotaManager IO thread. Its
   3020    // next step is DirectoryWorkDone if directory work was successful or
   3021    // SendingResults if directory work failed.
   3022    DirectoryWorkOpen,
   3023 
   3024    // Checking if database work can be started. If the database is not blocked
   3025    // by other factory operations then the next step is DatabaseWorkOpen.
   3026    // Otherwise the next step is DatabaseOpenPending.
   3027    DirectoryWorkDone,
   3028 
   3029    // Waiting for database open allowed on the PBackground thread. The next
   3030    // step is DatabaseWorkOpen.
   3031    DatabaseOpenPending,
   3032 
   3033    // Waiting to do/doing work on the QuotaManager IO thread. Its next step is
   3034    // either BeginVersionChange if the requested version doesn't match the
   3035    // existing database version or SendingResults if the versions match.
   3036    DatabaseWorkOpen,
   3037 
   3038    // Starting a version change transaction or deleting a database on the
   3039    // PBackground thread. We need to notify other databases that a version
   3040    // change is about to happen, and maybe tell the request that a version
   3041    // change has been blocked. If databases are notified then the next step is
   3042    // WaitingForOtherDatabasesToClose. Otherwise the next step is
   3043    // WaitingForTransactionsToComplete.
   3044    BeginVersionChange,
   3045 
   3046    // Waiting for other databases to close on the PBackground thread. This
   3047    // state may persist until all databases are closed. The next state is
   3048    // WaitingForTransactionsToComplete.
   3049    WaitingForOtherDatabasesToClose,
   3050 
   3051    // Waiting for all transactions that could interfere with this operation to
   3052    // complete on the PBackground thread. Next state is
   3053    // DatabaseWorkVersionChange.
   3054    WaitingForTransactionsToComplete,
   3055 
   3056    // Waiting to do/doing work on the "work thread". This involves waiting for
   3057    // the VersionChangeOp (OpenDatabaseOp and DeleteDatabaseOp each have a
   3058    // different implementation) to do its work. If the VersionChangeOp is
   3059    // OpenDatabaseOp and it succeeded then the next state is
   3060    // DatabaseWorkVersionUpdate. Otherwise the next step is SendingResults.
   3061    DatabaseWorkVersionChange,
   3062 
   3063    // Waiting to do/doing finalization work on the QuotaManager IO thread.
   3064    // Eventually the state will transition to SendingResults.
   3065    DatabaseWorkVersionUpdate,
   3066 
   3067    // Waiting to send/sending results on the PBackground thread. Next step is
   3068    // Completed.
   3069    SendingResults,
   3070 
   3071    // All done.
   3072    Completed
   3073  };
   3074 
   3075  // Must be released on the background thread!
   3076  SafeRefPtr<Factory> mFactory;
   3077 
   3078  Maybe<ContentParentId> mContentParentId;
   3079 
   3080  // Must be released on the main thread!
   3081  ClientDirectoryLockHandle mDirectoryLockHandle;
   3082 
   3083  nsTArray<NotNull<RefPtr<FactoryOp>>> mBlocking;
   3084  nsTHashSet<RefPtr<FactoryOp>> mBlockedOn;
   3085 
   3086  nsTArray<MaybeBlockedDatabaseInfo> mMaybeBlockedDatabases;
   3087 
   3088  const PrincipalInfo mPrincipalInfo;
   3089  OriginMetadata mOriginMetadata;
   3090  Maybe<nsString> mDatabaseName;
   3091  Maybe<nsCString> mDatabaseId;
   3092  Maybe<nsString> mDatabaseFilePath;
   3093  int64_t mDirectoryLockId;
   3094  const PersistenceType mPersistenceType;
   3095  State mState;
   3096  bool mWaitingForPermissionRetry;
   3097  bool mEnforcingQuota;
   3098  const bool mDeleting;
   3099  FlippedOnce<false> mInPrivateBrowsing;
   3100 
   3101 public:
   3102  const nsACString& Origin() const {
   3103    AssertIsOnOwningThread();
   3104 
   3105    return mOriginMetadata.mOrigin;
   3106  }
   3107 
   3108  const Maybe<nsString>& DatabaseNameRef() const {
   3109    AssertIsOnOwningThread();
   3110 
   3111    return mDatabaseName;
   3112  }
   3113 
   3114  bool DatabaseFilePathIsKnown() const {
   3115    AssertIsOnOwningThread();
   3116 
   3117    return mDatabaseFilePath.isSome();
   3118  }
   3119 
   3120  const nsAString& DatabaseFilePath() const {
   3121    AssertIsOnOwningThread();
   3122    MOZ_ASSERT(mDatabaseFilePath);
   3123 
   3124    return mDatabaseFilePath.ref();
   3125  }
   3126 
   3127  nsresult DispatchThisAfterProcessingCurrentEvent(
   3128      nsCOMPtr<nsIEventTarget> aEventTarget);
   3129 
   3130  void NoteDatabaseBlocked(Database* aDatabase);
   3131 
   3132  void NoteDatabaseClosed(Database* aDatabase);
   3133 
   3134 #ifdef DEBUG
   3135  bool HasBlockedDatabases() const { return !mMaybeBlockedDatabases.IsEmpty(); }
   3136 #endif
   3137 
   3138  void StringifyState(nsACString& aResult) const;
   3139 
   3140  void Stringify(nsACString& aResult) const;
   3141 
   3142 protected:
   3143  FactoryOp(SafeRefPtr<Factory> aFactory,
   3144            const Maybe<ContentParentId>& aContentParentId,
   3145            const PersistenceType aPersistenceType,
   3146            const PrincipalInfo& aPrincipalInfo,
   3147            const Maybe<nsString>& aDatabaseName, bool aDeleting);
   3148 
   3149  ~FactoryOp() override {
   3150    // Normally this would be out-of-line since it is a virtual function but
   3151    // MSVC 2010 fails to link for some reason if it is not inlined here...
   3152    MOZ_ASSERT_IF(OperationMayProceed(),
   3153                  mState == State::Initial || mState == State::Completed);
   3154    MOZ_DIAGNOSTIC_ASSERT(!isInList());
   3155  }
   3156 
   3157  nsresult Open();
   3158 
   3159  nsresult DirectoryOpen();
   3160 
   3161  nsresult DirectoryWorkDone();
   3162 
   3163  nsresult SendToIOThread();
   3164 
   3165  void WaitForTransactions();
   3166 
   3167  void CleanupMetadata();
   3168 
   3169  void FinishSendResults();
   3170 
   3171  nsresult SendVersionChangeMessages(DatabaseActorInfo* aDatabaseActorInfo,
   3172                                     Maybe<Database&> aOpeningDatabase,
   3173                                     uint64_t aOldVersion,
   3174                                     const Maybe<uint64_t>& aNewVersion);
   3175 
   3176  // Methods that subclasses must implement.
   3177  virtual nsresult DoDirectoryWork() = 0;
   3178 
   3179  virtual nsresult DatabaseOpen() = 0;
   3180 
   3181  virtual nsresult DoDatabaseWork() = 0;
   3182 
   3183  virtual nsresult BeginVersionChange() = 0;
   3184 
   3185  virtual bool AreActorsAlive() = 0;
   3186 
   3187  virtual nsresult DispatchToWorkThread() = 0;
   3188 
   3189  virtual nsresult DoVersionUpdate() = 0;
   3190 
   3191  // Should only be called by Run().
   3192  virtual void SendResults() = 0;
   3193 
   3194  // Common nsIRunnable implementation that subclasses may not override.
   3195  NS_IMETHOD
   3196  Run() final;
   3197 
   3198  void DirectoryLockAcquired(ClientDirectoryLockHandle aLockHandle);
   3199 
   3200  void DirectoryLockFailed();
   3201 
   3202  virtual void SendBlockedNotification() = 0;
   3203 
   3204 private:
   3205  // Test whether this FactoryOp needs to wait for the given op.
   3206  bool MustWaitFor(const FactoryOp& aExistingOp);
   3207 
   3208  void AddBlockingOp(FactoryOp& aOp) {
   3209    AssertIsOnOwningThread();
   3210 
   3211    mBlocking.AppendElement(WrapNotNull(&aOp));
   3212  }
   3213 
   3214  void AddBlockedOnOp(FactoryOp& aOp) {
   3215    AssertIsOnOwningThread();
   3216 
   3217    mBlockedOn.Insert(&aOp);
   3218  }
   3219 
   3220  void MaybeUnblock(FactoryOp& aOp) {
   3221    AssertIsOnOwningThread();
   3222 
   3223    mBlockedOn.Remove(&aOp);
   3224    if (mBlockedOn.IsEmpty()) {
   3225      MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(this));
   3226    }
   3227  }
   3228 };
   3229 
   3230 class FactoryRequestOp : public FactoryOp,
   3231                         public PBackgroundIDBFactoryRequestParent {
   3232 protected:
   3233  const CommonFactoryRequestParams mCommonParams;
   3234 
   3235  FactoryRequestOp(SafeRefPtr<Factory> aFactory,
   3236                   const Maybe<ContentParentId>& aContentParentId,
   3237                   const CommonFactoryRequestParams& aCommonParams,
   3238                   bool aDeleting)
   3239      : FactoryOp(std::move(aFactory), aContentParentId,
   3240                  aCommonParams.metadata().persistenceType(),
   3241                  aCommonParams.principalInfo(),
   3242                  Some(aCommonParams.metadata().name()), aDeleting),
   3243        mCommonParams(aCommonParams) {}
   3244 
   3245  nsresult DoDirectoryWork() override;
   3246 
   3247  // IPDL methods.
   3248  void ActorDestroy(ActorDestroyReason aWhy) override;
   3249 };
   3250 
   3251 class OpenDatabaseOp final : public FactoryRequestOp {
   3252  friend class Database;
   3253  friend class VersionChangeTransaction;
   3254 
   3255  class VersionChangeOp;
   3256 
   3257  SafeRefPtr<FullDatabaseMetadata> mMetadata;
   3258 
   3259  uint64_t mRequestedVersion;
   3260  SafeRefPtr<DatabaseFileManager> mFileManager;
   3261 
   3262  SafeRefPtr<Database> mDatabase;
   3263  SafeRefPtr<VersionChangeTransaction> mVersionChangeTransaction;
   3264 
   3265  // This is only set while a VersionChangeOp is live. It holds a strong
   3266  // reference to its OpenDatabaseOp object so this is a weak pointer to avoid
   3267  // cycles.
   3268  VersionChangeOp* mVersionChangeOp;
   3269 
   3270  MoveOnlyFunction<void()> mCompleteCallback;
   3271 
   3272  uint32_t mTelemetryId;
   3273 
   3274 public:
   3275  OpenDatabaseOp(SafeRefPtr<Factory> aFactory,
   3276                 const Maybe<ContentParentId>& aContentParentId,
   3277                 const CommonFactoryRequestParams& aParams);
   3278 
   3279 private:
   3280  ~OpenDatabaseOp() override { MOZ_ASSERT(!mVersionChangeOp); }
   3281 
   3282  nsresult LoadDatabaseInformation(mozIStorageConnection& aConnection);
   3283 
   3284  nsresult SendUpgradeNeeded();
   3285 
   3286  void EnsureDatabaseActor();
   3287 
   3288  nsresult EnsureDatabaseActorIsAlive();
   3289 
   3290  mozilla::Result<DatabaseSpec, nsresult> MetadataToSpec() const;
   3291 
   3292  void AssertMetadataConsistency(const FullDatabaseMetadata& aMetadata)
   3293 #ifdef DEBUG
   3294      ;
   3295 #else
   3296  {
   3297  }
   3298 #endif
   3299 
   3300  void ConnectionClosedCallback();
   3301 
   3302  void ActorDestroy(ActorDestroyReason aWhy) override;
   3303 
   3304  nsresult DatabaseOpen() override;
   3305 
   3306  nsresult DoDatabaseWork() override;
   3307 
   3308  nsresult BeginVersionChange() override;
   3309 
   3310  bool AreActorsAlive() override;
   3311 
   3312  void SendBlockedNotification() override;
   3313 
   3314  nsresult DispatchToWorkThread() override;
   3315 
   3316  nsresult DoVersionUpdate() override;
   3317 
   3318  void SendResults() override;
   3319 
   3320  static nsresult UpdateLocaleAwareIndex(mozIStorageConnection& aConnection,
   3321                                         const IndexMetadata& aIndexMetadata,
   3322                                         const nsCString& aLocale);
   3323 };
   3324 
   3325 class OpenDatabaseOp::VersionChangeOp final
   3326    : public TransactionDatabaseOperationBase {
   3327  friend class OpenDatabaseOp;
   3328 
   3329  RefPtr<OpenDatabaseOp> mOpenDatabaseOp;
   3330  const uint64_t mRequestedVersion;
   3331  uint64_t mPreviousVersion;
   3332 
   3333 private:
   3334  explicit VersionChangeOp(OpenDatabaseOp* aOpenDatabaseOp)
   3335      : TransactionDatabaseOperationBase(
   3336            aOpenDatabaseOp->mVersionChangeTransaction.clonePtr(),
   3337            /* aRequestId */ 0, aOpenDatabaseOp->LoggingSerialNumber()),
   3338        mOpenDatabaseOp(aOpenDatabaseOp),
   3339        mRequestedVersion(aOpenDatabaseOp->mRequestedVersion),
   3340        mPreviousVersion(
   3341            aOpenDatabaseOp->mMetadata->mCommonMetadata.version()) {
   3342    MOZ_ASSERT(aOpenDatabaseOp);
   3343    MOZ_ASSERT(mRequestedVersion);
   3344  }
   3345 
   3346  ~VersionChangeOp() override { MOZ_ASSERT(!mOpenDatabaseOp); }
   3347 
   3348  nsresult DoDatabaseWork(DatabaseConnection* aConnection) override;
   3349 
   3350  nsresult SendSuccessResult() override;
   3351 
   3352  bool SendFailureResult(nsresult aResultCode) override;
   3353 
   3354  void Cleanup() override;
   3355 };
   3356 
   3357 class DeleteDatabaseOp final : public FactoryRequestOp {
   3358  class VersionChangeOp;
   3359 
   3360  nsString mDatabaseDirectoryPath;
   3361  nsString mDatabaseFilenameBase;
   3362  uint64_t mPreviousVersion;
   3363 
   3364 public:
   3365  DeleteDatabaseOp(SafeRefPtr<Factory> aFactory,
   3366                   const Maybe<ContentParentId>& aContentParentId,
   3367                   const CommonFactoryRequestParams& aParams)
   3368      : FactoryRequestOp(std::move(aFactory), aContentParentId, aParams,
   3369                         /* aDeleting */ true),
   3370        mPreviousVersion(0) {}
   3371 
   3372 private:
   3373  ~DeleteDatabaseOp() override = default;
   3374 
   3375  void LoadPreviousVersion(nsIFile& aDatabaseFile);
   3376 
   3377  nsresult DatabaseOpen() override;
   3378 
   3379  nsresult DoDatabaseWork() override;
   3380 
   3381  nsresult BeginVersionChange() override;
   3382 
   3383  bool AreActorsAlive() override;
   3384 
   3385  void SendBlockedNotification() override;
   3386 
   3387  nsresult DispatchToWorkThread() override;
   3388 
   3389  nsresult DoVersionUpdate() override;
   3390 
   3391  void SendResults() override;
   3392 };
   3393 
   3394 class DeleteDatabaseOp::VersionChangeOp final : public DatabaseOperationBase {
   3395  friend class DeleteDatabaseOp;
   3396 
   3397  RefPtr<DeleteDatabaseOp> mDeleteDatabaseOp;
   3398 
   3399 private:
   3400  explicit VersionChangeOp(DeleteDatabaseOp* aDeleteDatabaseOp)
   3401      : DatabaseOperationBase(aDeleteDatabaseOp->BackgroundChildLoggingId(),
   3402                              aDeleteDatabaseOp->LoggingSerialNumber()),
   3403        mDeleteDatabaseOp(aDeleteDatabaseOp) {
   3404    MOZ_ASSERT(aDeleteDatabaseOp);
   3405    MOZ_ASSERT(!aDeleteDatabaseOp->mDatabaseDirectoryPath.IsEmpty());
   3406  }
   3407 
   3408  ~VersionChangeOp() override = default;
   3409 
   3410  nsresult RunOnIOThread();
   3411 
   3412  void RunOnOwningThread();
   3413 
   3414  NS_DECL_NSIRUNNABLE
   3415 };
   3416 
   3417 class GetDatabasesOp final : public FactoryOp {
   3418  nsTHashMap<nsStringHashKey, DatabaseMetadata> mDatabaseMetadataTable;
   3419  nsTArray<DatabaseMetadata> mDatabaseMetadataArray;
   3420  Factory::GetDatabasesResolver mResolver;
   3421 
   3422 public:
   3423  GetDatabasesOp(SafeRefPtr<Factory> aFactory,
   3424                 const Maybe<ContentParentId>& aContentParentId,
   3425                 const PersistenceType aPersistenceType,
   3426                 const PrincipalInfo& aPrincipalInfo,
   3427                 Factory::GetDatabasesResolver&& aResolver)
   3428      : FactoryOp(std::move(aFactory), aContentParentId, aPersistenceType,
   3429                  aPrincipalInfo, Nothing(), /* aDeleting */ false),
   3430        mResolver(std::move(aResolver)) {}
   3431 
   3432 private:
   3433  ~GetDatabasesOp() override = default;
   3434 
   3435  nsresult DatabasesNotAvailable();
   3436 
   3437  nsresult DoDirectoryWork() override;
   3438 
   3439  nsresult DatabaseOpen() override;
   3440 
   3441  nsresult DoDatabaseWork() override;
   3442 
   3443  nsresult BeginVersionChange() override;
   3444 
   3445  bool AreActorsAlive() override;
   3446 
   3447  void SendBlockedNotification() override;
   3448 
   3449  nsresult DispatchToWorkThread() override;
   3450 
   3451  nsresult DoVersionUpdate() override;
   3452 
   3453  void SendResults() override;
   3454 };
   3455 
   3456 class VersionChangeTransactionOp : public TransactionDatabaseOperationBase {
   3457 public:
   3458  void Cleanup() override;
   3459 
   3460 protected:
   3461  explicit VersionChangeTransactionOp(
   3462      SafeRefPtr<VersionChangeTransaction> aTransaction)
   3463      : TransactionDatabaseOperationBase(std::move(aTransaction),
   3464                                         /* aRequestId */ 0) {}
   3465 
   3466  ~VersionChangeTransactionOp() override = default;
   3467 
   3468 private:
   3469  nsresult SendSuccessResult() override;
   3470 
   3471  bool SendFailureResult(nsresult aResultCode) override;
   3472 };
   3473 
   3474 class CreateObjectStoreOp final : public VersionChangeTransactionOp {
   3475  friend class VersionChangeTransaction;
   3476 
   3477  const ObjectStoreMetadata mMetadata;
   3478 
   3479 private:
   3480  // Only created by VersionChangeTransaction.
   3481  CreateObjectStoreOp(SafeRefPtr<VersionChangeTransaction> aTransaction,
   3482                      const ObjectStoreMetadata& aMetadata)
   3483      : VersionChangeTransactionOp(std::move(aTransaction)),
   3484        mMetadata(aMetadata) {
   3485    MOZ_ASSERT(aMetadata.id());
   3486  }
   3487 
   3488  ~CreateObjectStoreOp() override = default;
   3489 
   3490  nsresult DoDatabaseWork(DatabaseConnection* aConnection) override;
   3491 };
   3492 
   3493 class DeleteObjectStoreOp final : public VersionChangeTransactionOp {
   3494  friend class VersionChangeTransaction;
   3495 
   3496  const SafeRefPtr<FullObjectStoreMetadata> mMetadata;
   3497  const bool mIsLastObjectStore;
   3498 
   3499 private:
   3500  // Only created by VersionChangeTransaction.
   3501  DeleteObjectStoreOp(SafeRefPtr<VersionChangeTransaction> aTransaction,
   3502                      SafeRefPtr<FullObjectStoreMetadata> aMetadata,
   3503                      const bool aIsLastObjectStore)
   3504      : VersionChangeTransactionOp(std::move(aTransaction)),
   3505        mMetadata(std::move(aMetadata)),
   3506        mIsLastObjectStore(aIsLastObjectStore) {
   3507    MOZ_ASSERT(mMetadata->mCommonMetadata.id());
   3508  }
   3509 
   3510  ~DeleteObjectStoreOp() override = default;
   3511 
   3512  nsresult DoDatabaseWork(DatabaseConnection* aConnection) override;
   3513 };
   3514 
   3515 class RenameObjectStoreOp final : public VersionChangeTransactionOp {
   3516  friend class VersionChangeTransaction;
   3517 
   3518  const int64_t mId;
   3519  const nsString mNewName;
   3520 
   3521 private:
   3522  // Only created by VersionChangeTransaction.
   3523  RenameObjectStoreOp(SafeRefPtr<VersionChangeTransaction> aTransaction,
   3524                      FullObjectStoreMetadata& aMetadata)
   3525      : VersionChangeTransactionOp(std::move(aTransaction)),
   3526        mId(aMetadata.mCommonMetadata.id()),
   3527        mNewName(aMetadata.mCommonMetadata.name()) {
   3528    MOZ_ASSERT(mId);
   3529  }
   3530 
   3531  ~RenameObjectStoreOp() override = default;
   3532 
   3533  nsresult DoDatabaseWork(DatabaseConnection* aConnection) override;
   3534 };
   3535 
   3536 class CreateIndexOp final : public VersionChangeTransactionOp {
   3537  friend class VersionChangeTransaction;
   3538 
   3539  class UpdateIndexDataValuesFunction;
   3540 
   3541  const IndexMetadata mMetadata;
   3542  Maybe<UniqueIndexTable> mMaybeUniqueIndexTable;
   3543  const SafeRefPtr<DatabaseFileManager> mFileManager;
   3544  const nsCString mDatabaseId;
   3545  const IndexOrObjectStoreId mObjectStoreId;
   3546 
   3547 private:
   3548  // Only created by VersionChangeTransaction.
   3549  CreateIndexOp(SafeRefPtr<VersionChangeTransaction> aTransaction,
   3550                IndexOrObjectStoreId aObjectStoreId,
   3551                const IndexMetadata& aMetadata);
   3552 
   3553  ~CreateIndexOp() override = default;
   3554 
   3555  nsresult InsertDataFromObjectStore(DatabaseConnection* aConnection);
   3556 
   3557  nsresult InsertDataFromObjectStoreInternal(
   3558      DatabaseConnection* aConnection) const;
   3559 
   3560  bool Init(TransactionBase& aTransaction) override;
   3561 
   3562  nsresult DoDatabaseWork(DatabaseConnection* aConnection) override;
   3563 };
   3564 
   3565 class CreateIndexOp::UpdateIndexDataValuesFunction final
   3566    : public mozIStorageFunction {
   3567  RefPtr<CreateIndexOp> mOp;
   3568  RefPtr<DatabaseConnection> mConnection;
   3569  const NotNull<SafeRefPtr<Database>> mDatabase;
   3570 
   3571 public:
   3572  UpdateIndexDataValuesFunction(CreateIndexOp* aOp,
   3573                                DatabaseConnection* aConnection,
   3574                                SafeRefPtr<Database> aDatabase)
   3575      : mOp(aOp),
   3576        mConnection(aConnection),
   3577        mDatabase(WrapNotNull(std::move(aDatabase))) {
   3578    MOZ_ASSERT(aOp);
   3579    MOZ_ASSERT(aConnection);
   3580    aConnection->AssertIsOnConnectionThread();
   3581  }
   3582 
   3583  NS_DECL_ISUPPORTS
   3584 
   3585 private:
   3586  ~UpdateIndexDataValuesFunction() = default;
   3587 
   3588  NS_DECL_MOZISTORAGEFUNCTION
   3589 };
   3590 
   3591 class DeleteIndexOp final : public VersionChangeTransactionOp {
   3592  friend class VersionChangeTransaction;
   3593 
   3594  const IndexOrObjectStoreId mObjectStoreId;
   3595  const IndexOrObjectStoreId mIndexId;
   3596  const bool mUnique;
   3597  const bool mIsLastIndex;
   3598 
   3599 private:
   3600  // Only created by VersionChangeTransaction.
   3601  DeleteIndexOp(SafeRefPtr<VersionChangeTransaction> aTransaction,
   3602                IndexOrObjectStoreId aObjectStoreId,
   3603                IndexOrObjectStoreId aIndexId, const bool aUnique,
   3604                const bool aIsLastIndex);
   3605 
   3606  ~DeleteIndexOp() override = default;
   3607 
   3608  nsresult RemoveReferencesToIndex(
   3609      DatabaseConnection* aConnection, const Key& aObjectDataKey,
   3610      nsTArray<IndexDataValue>& aIndexValues) const;
   3611 
   3612  nsresult DoDatabaseWork(DatabaseConnection* aConnection) override;
   3613 };
   3614 
   3615 class RenameIndexOp final : public VersionChangeTransactionOp {
   3616  friend class VersionChangeTransaction;
   3617 
   3618  const IndexOrObjectStoreId mObjectStoreId;
   3619  const IndexOrObjectStoreId mIndexId;
   3620  const nsString mNewName;
   3621 
   3622 private:
   3623  // Only created by VersionChangeTransaction.
   3624  RenameIndexOp(SafeRefPtr<VersionChangeTransaction> aTransaction,
   3625                FullIndexMetadata& aMetadata,
   3626                IndexOrObjectStoreId aObjectStoreId)
   3627      : VersionChangeTransactionOp(std::move(aTransaction)),
   3628        mObjectStoreId(aObjectStoreId),
   3629        mIndexId(aMetadata.mCommonMetadata.id()),
   3630        mNewName(aMetadata.mCommonMetadata.name()) {
   3631    MOZ_ASSERT(mIndexId);
   3632  }
   3633 
   3634  ~RenameIndexOp() override = default;
   3635 
   3636  nsresult DoDatabaseWork(DatabaseConnection* aConnection) override;
   3637 };
   3638 
   3639 class NormalTransactionOp : public TransactionDatabaseOperationBase,
   3640                            public PBackgroundIDBRequestParent {
   3641 #ifdef DEBUG
   3642  bool mResponseSent;
   3643 #endif
   3644 
   3645 public:
   3646  void Cleanup() override;
   3647 
   3648 protected:
   3649  NormalTransactionOp(SafeRefPtr<TransactionBase> aTransaction,
   3650                      const int64_t aRequestId)
   3651      : TransactionDatabaseOperationBase(std::move(aTransaction), aRequestId)
   3652 #ifdef DEBUG
   3653        ,
   3654        mResponseSent(false)
   3655 #endif
   3656  {
   3657  }
   3658 
   3659  ~NormalTransactionOp() override = default;
   3660 
   3661  // An overload of DatabaseOperationBase's function that can avoid doing extra
   3662  // work on non-versionchange transactions.
   3663  mozilla::Result<bool, nsresult> ObjectStoreHasIndexes(
   3664      DatabaseConnection& aConnection, IndexOrObjectStoreId aObjectStoreId,
   3665      bool aMayHaveIndexes);
   3666 
   3667  virtual mozilla::Result<PreprocessParams, nsresult> GetPreprocessParams();
   3668 
   3669  // Subclasses use this override to set the IPDL response value.
   3670  virtual void GetResponse(RequestResponse& aResponse,
   3671                           size_t* aResponseSize) = 0;
   3672 
   3673 private:
   3674  nsresult SendPreprocessInfo() override;
   3675 
   3676  nsresult SendSuccessResult() override;
   3677 
   3678  bool SendFailureResult(nsresult aResultCode) override;
   3679 
   3680  // IPDL methods.
   3681  void ActorDestroy(ActorDestroyReason aWhy) override;
   3682 
   3683  mozilla::ipc::IPCResult RecvContinue(
   3684      const PreprocessResponse& aResponse) final;
   3685 };
   3686 
   3687 class ObjectStoreAddOrPutRequestOp final : public NormalTransactionOp {
   3688  friend class TransactionBase;
   3689 
   3690  using PersistenceType = mozilla::dom::quota::PersistenceType;
   3691 
   3692  class StoredFileInfo final {
   3693    InitializedOnce<const NotNull<SafeRefPtr<DatabaseFileInfo>>> mFileInfo;
   3694    // Either nothing, a file actor or a non-Blob-backed inputstream to write to
   3695    // disk.
   3696    using FileActorOrInputStream =
   3697        Variant<Nothing, RefPtr<DatabaseFile>, nsCOMPtr<nsIInputStream>>;
   3698    InitializedOnce<const FileActorOrInputStream> mFileActorOrInputStream;
   3699 #ifdef DEBUG
   3700    const StructuredCloneFileBase::FileType mType;
   3701 #endif
   3702    void EnsureCipherKey();
   3703    void AssertInvariants() const;
   3704 
   3705    StoredFileInfo(SafeRefPtr<DatabaseFileInfo> aFileInfo,
   3706                   RefPtr<DatabaseFile> aFileActor);
   3707 
   3708    StoredFileInfo(SafeRefPtr<DatabaseFileInfo> aFileInfo,
   3709                   nsCOMPtr<nsIInputStream> aInputStream);
   3710 
   3711   public:
   3712 #if defined(NS_BUILD_REFCNT_LOGGING)
   3713    // Only for MOZ_COUNT_CTOR.
   3714    StoredFileInfo(StoredFileInfo&& aOther)
   3715        : mFileInfo{std::move(aOther.mFileInfo)},
   3716          mFileActorOrInputStream{std::move(aOther.mFileActorOrInputStream)}
   3717 #  ifdef DEBUG
   3718          ,
   3719          mType{aOther.mType}
   3720 #  endif
   3721    {
   3722      MOZ_COUNT_CTOR(ObjectStoreAddOrPutRequestOp::StoredFileInfo);
   3723    }
   3724 #else
   3725    StoredFileInfo(StoredFileInfo&&) = default;
   3726 #endif
   3727 
   3728    static StoredFileInfo CreateForBlob(SafeRefPtr<DatabaseFileInfo> aFileInfo,
   3729                                        RefPtr<DatabaseFile> aFileActor);
   3730    static StoredFileInfo CreateForStructuredClone(
   3731        SafeRefPtr<DatabaseFileInfo> aFileInfo,
   3732        nsCOMPtr<nsIInputStream> aInputStream);
   3733 
   3734 #if defined(DEBUG) || defined(NS_BUILD_REFCNT_LOGGING)
   3735    ~StoredFileInfo() {
   3736      AssertIsOnBackgroundThread();
   3737      AssertInvariants();
   3738 
   3739      MOZ_COUNT_DTOR(ObjectStoreAddOrPutRequestOp::StoredFileInfo);
   3740    }
   3741 #endif
   3742 
   3743    bool IsValid() const { return static_cast<bool>(mFileInfo); }
   3744 
   3745    const DatabaseFileInfo& GetFileInfo() const { return **mFileInfo; }
   3746 
   3747    bool ShouldCompress() const;
   3748 
   3749    void NotifyWriteSucceeded() const;
   3750 
   3751    using InputStreamResult =
   3752        mozilla::Result<nsCOMPtr<nsIInputStream>, nsresult>;
   3753    InputStreamResult GetInputStream();
   3754 
   3755    void Serialize(nsString& aText) const;
   3756  };
   3757  class SCInputStream;
   3758 
   3759  ObjectStoreAddPutParams mParams;
   3760  Maybe<UniqueIndexTable> mUniqueIndexTable;
   3761 
   3762  // This must be non-const so that we can update the mNextAutoIncrementId field
   3763  // if we are modifying an autoIncrement objectStore.
   3764  SafeRefPtr<FullObjectStoreMetadata> mMetadata;
   3765 
   3766  nsTArray<StoredFileInfo> mStoredFileInfos;
   3767 
   3768  Key mResponse;
   3769  const OriginMetadata mOriginMetadata;
   3770  const PersistenceType mPersistenceType;
   3771  const bool mOverwrite;
   3772  bool mObjectStoreMayHaveIndexes;
   3773  bool mDataOverThreshold;
   3774 
   3775 private:
   3776  // Only created by TransactionBase.
   3777  ObjectStoreAddOrPutRequestOp(SafeRefPtr<TransactionBase> aTransaction,
   3778                               const int64_t aRequestId,
   3779                               RequestParams&& aParams);
   3780 
   3781  ~ObjectStoreAddOrPutRequestOp() override = default;
   3782 
   3783  nsresult RemoveOldIndexDataValues(DatabaseConnection* aConnection);
   3784 
   3785  bool Init(TransactionBase& aTransaction) override;
   3786 
   3787  nsresult DoDatabaseWork(DatabaseConnection* aConnection) override;
   3788 
   3789  void GetResponse(RequestResponse& aResponse, size_t* aResponseSize) override;
   3790 
   3791  void Cleanup() override;
   3792 };
   3793 
   3794 void ObjectStoreAddOrPutRequestOp::StoredFileInfo::AssertInvariants() const {
   3795  // The only allowed types are eStructuredClone, eBlob and eMutableFile.
   3796  MOZ_ASSERT(StructuredCloneFileBase::eStructuredClone == mType ||
   3797             StructuredCloneFileBase::eBlob == mType ||
   3798             StructuredCloneFileBase::eMutableFile == mType);
   3799 
   3800  // mFileInfo and a file actor in mFileActorOrInputStream are present until
   3801  // the object is moved away, but an inputStream in mFileActorOrInputStream
   3802  // can be released early.
   3803  MOZ_ASSERT_IF(static_cast<bool>(mFileActorOrInputStream) &&
   3804                    mFileActorOrInputStream->is<RefPtr<DatabaseFile>>(),
   3805                static_cast<bool>(mFileInfo));
   3806 
   3807  if (mFileInfo) {
   3808    // In a non-moved StoredFileInfo, one of the following is true:
   3809    // - This was an overflow structured clone (eStructuredClone) and
   3810    //   storedFileInfo.mFileActorOrInputStream CAN be a non-nullptr input
   3811    //   stream (but that might have been release by ReleaseInputStream).
   3812    MOZ_ASSERT_IF(
   3813        StructuredCloneFileBase::eStructuredClone == mType,
   3814        !mFileActorOrInputStream ||
   3815            (mFileActorOrInputStream->is<nsCOMPtr<nsIInputStream>>() &&
   3816             mFileActorOrInputStream->as<nsCOMPtr<nsIInputStream>>()));
   3817 
   3818    // - This is a reference to a Blob (eBlob) that may or may not have
   3819    //   already been written to disk.  storedFileInfo.mFileActorOrInputStream
   3820    //   MUST be a non-null file actor, but its GetInputStream may return
   3821    //   nullptr (so don't assert on that).
   3822    MOZ_ASSERT_IF(StructuredCloneFileBase::eBlob == mType,
   3823                  mFileActorOrInputStream->is<RefPtr<DatabaseFile>>() &&
   3824                      mFileActorOrInputStream->as<RefPtr<DatabaseFile>>());
   3825 
   3826    // - It's a mutable file (eMutableFile).  No writing will be performed,
   3827    // and storedFileInfo.mFileActorOrInputStream is Nothing.
   3828    MOZ_ASSERT_IF(StructuredCloneFileBase::eMutableFile == mType,
   3829                  mFileActorOrInputStream->is<Nothing>());
   3830  }
   3831 }
   3832 
   3833 void ObjectStoreAddOrPutRequestOp::StoredFileInfo::EnsureCipherKey() {
   3834  const auto& fileInfo = GetFileInfo();
   3835  const auto& fileManager = fileInfo.Manager();
   3836 
   3837  // No need to generate cipher keys if we are not in PBM
   3838  if (!fileManager.IsInPrivateBrowsingMode()) {
   3839    return;
   3840  }
   3841 
   3842  nsCString keyId;
   3843  keyId.AppendInt(fileInfo.Id());
   3844 
   3845  fileManager.MutableCipherKeyManagerRef().Ensure(keyId);
   3846 }
   3847 
   3848 ObjectStoreAddOrPutRequestOp::StoredFileInfo::StoredFileInfo(
   3849    SafeRefPtr<DatabaseFileInfo> aFileInfo, RefPtr<DatabaseFile> aFileActor)
   3850    : mFileInfo{WrapNotNull(std::move(aFileInfo))},
   3851      mFileActorOrInputStream{std::move(aFileActor)}
   3852 #ifdef DEBUG
   3853      ,
   3854      mType{StructuredCloneFileBase::eBlob}
   3855 #endif
   3856 {
   3857  AssertIsOnBackgroundThread();
   3858  AssertInvariants();
   3859 
   3860  EnsureCipherKey();
   3861  MOZ_COUNT_CTOR(ObjectStoreAddOrPutRequestOp::StoredFileInfo);
   3862 }
   3863 
   3864 ObjectStoreAddOrPutRequestOp::StoredFileInfo::StoredFileInfo(
   3865    SafeRefPtr<DatabaseFileInfo> aFileInfo,
   3866    nsCOMPtr<nsIInputStream> aInputStream)
   3867    : mFileInfo{WrapNotNull(std::move(aFileInfo))},
   3868      mFileActorOrInputStream{std::move(aInputStream)}
   3869 #ifdef DEBUG
   3870      ,
   3871      mType{StructuredCloneFileBase::eStructuredClone}
   3872 #endif
   3873 {
   3874  AssertIsOnBackgroundThread();
   3875  AssertInvariants();
   3876 
   3877  EnsureCipherKey();
   3878  MOZ_COUNT_CTOR(ObjectStoreAddOrPutRequestOp::StoredFileInfo);
   3879 }
   3880 
   3881 ObjectStoreAddOrPutRequestOp::StoredFileInfo
   3882 ObjectStoreAddOrPutRequestOp::StoredFileInfo::CreateForBlob(
   3883    SafeRefPtr<DatabaseFileInfo> aFileInfo, RefPtr<DatabaseFile> aFileActor) {
   3884  return {std::move(aFileInfo), std::move(aFileActor)};
   3885 }
   3886 
   3887 ObjectStoreAddOrPutRequestOp::StoredFileInfo
   3888 ObjectStoreAddOrPutRequestOp::StoredFileInfo::CreateForStructuredClone(
   3889    SafeRefPtr<DatabaseFileInfo> aFileInfo,
   3890    nsCOMPtr<nsIInputStream> aInputStream) {
   3891  return {std::move(aFileInfo), std::move(aInputStream)};
   3892 }
   3893 
   3894 bool ObjectStoreAddOrPutRequestOp::StoredFileInfo::ShouldCompress() const {
   3895  // Must not be called after moving.
   3896  MOZ_ASSERT(IsValid());
   3897 
   3898  // Compression is only necessary for eStructuredClone, i.e. when
   3899  // mFileActorOrInputStream stored an input stream. However, this is only
   3900  // called after GetInputStream, when mFileActorOrInputStream has been
   3901  // cleared, which is only possible for this type.
   3902  const bool res = !mFileActorOrInputStream;
   3903  MOZ_ASSERT(res == (StructuredCloneFileBase::eStructuredClone == mType));
   3904  return res;
   3905 }
   3906 
   3907 void ObjectStoreAddOrPutRequestOp::StoredFileInfo::NotifyWriteSucceeded()
   3908    const {
   3909  MOZ_ASSERT(IsValid());
   3910 
   3911  // For eBlob, clear the blob implementation.
   3912  if (mFileActorOrInputStream &&
   3913      mFileActorOrInputStream->is<RefPtr<DatabaseFile>>()) {
   3914    mFileActorOrInputStream->as<RefPtr<DatabaseFile>>()
   3915        ->WriteSucceededClearBlobImpl();
   3916  }
   3917 
   3918  // For the other types, no action is necessary.
   3919 }
   3920 
   3921 ObjectStoreAddOrPutRequestOp::StoredFileInfo::InputStreamResult
   3922 ObjectStoreAddOrPutRequestOp::StoredFileInfo::GetInputStream() {
   3923  if (!mFileActorOrInputStream) {
   3924    MOZ_ASSERT(StructuredCloneFileBase::eStructuredClone == mType);
   3925    return nsCOMPtr<nsIInputStream>{};
   3926  }
   3927 
   3928  // For the different cases, see also the comments in AssertInvariants.
   3929  return mFileActorOrInputStream->match(
   3930      [](const Nothing&) -> InputStreamResult {
   3931        return nsCOMPtr<nsIInputStream>{};
   3932      },
   3933      [](const RefPtr<DatabaseFile>& databaseActor) -> InputStreamResult {
   3934        ErrorResult rv;
   3935        auto inputStream = databaseActor->GetInputStream(rv);
   3936        if (NS_WARN_IF(rv.Failed())) {
   3937          return Err(rv.StealNSResult());
   3938        }
   3939 
   3940        return inputStream;
   3941      },
   3942      [this](const nsCOMPtr<nsIInputStream>& inputStream) -> InputStreamResult {
   3943        auto res = inputStream;
   3944        // destroy() clears the inputStream parameter, so we needed to make a
   3945        // copy before
   3946        mFileActorOrInputStream.destroy();
   3947        AssertInvariants();
   3948        return res;
   3949      });
   3950 }
   3951 
   3952 void ObjectStoreAddOrPutRequestOp::StoredFileInfo::Serialize(
   3953    nsString& aText) const {
   3954  AssertInvariants();
   3955  MOZ_ASSERT(IsValid());
   3956 
   3957  const int64_t id = (*mFileInfo)->Id();
   3958 
   3959  auto structuredCloneHandler = [&aText, id](const nsCOMPtr<nsIInputStream>&) {
   3960    // eStructuredClone
   3961    aText.Append('.');
   3962    aText.AppendInt(id);
   3963  };
   3964 
   3965  // If mFileActorOrInputStream was moved, we had an inputStream before.
   3966  if (!mFileActorOrInputStream) {
   3967    structuredCloneHandler(nullptr);
   3968    return;
   3969  }
   3970 
   3971  // This encoding is parsed in DeserializeStructuredCloneFile.
   3972  mFileActorOrInputStream->match(
   3973      [&aText, id](const Nothing&) {
   3974        // eMutableFile
   3975        aText.AppendInt(-id);
   3976      },
   3977      [&aText, id](const RefPtr<DatabaseFile>&) {
   3978        // eBlob
   3979        aText.AppendInt(id);
   3980      },
   3981      structuredCloneHandler);
   3982 }
   3983 
   3984 class ObjectStoreAddOrPutRequestOp::SCInputStream final
   3985    : public nsIInputStream {
   3986  const JSStructuredCloneData& mData;
   3987  JSStructuredCloneData::Iterator mIter;
   3988 
   3989 public:
   3990  explicit SCInputStream(const JSStructuredCloneData& aData)
   3991      : mData(aData), mIter(aData.Start()) {}
   3992 
   3993 private:
   3994  virtual ~SCInputStream() = default;
   3995 
   3996  NS_DECL_THREADSAFE_ISUPPORTS
   3997  NS_DECL_NSIINPUTSTREAM
   3998 };
   3999 
   4000 class ObjectStoreGetRequestOp final : public NormalTransactionOp {
   4001  friend class TransactionBase;
   4002 
   4003  const IndexOrObjectStoreId mObjectStoreId;
   4004  SafeRefPtr<Database> mDatabase;
   4005  const Maybe<SerializedKeyRange> mOptionalKeyRange;
   4006  AutoTArray<StructuredCloneReadInfoParent, 1> mResponse;
   4007  PBackgroundParent* mBackgroundParent;
   4008  uint32_t mPreprocessInfoCount;
   4009  const uint32_t mLimit;
   4010  const bool mGetAll;
   4011 
   4012 private:
   4013  // Only created by TransactionBase.
   4014  ObjectStoreGetRequestOp(SafeRefPtr<TransactionBase> aTransaction,
   4015                          const int64_t aRequestId,
   4016                          const RequestParams& aParams, bool aGetAll);
   4017 
   4018  ~ObjectStoreGetRequestOp() override = default;
   4019 
   4020  template <typename T>
   4021  mozilla::Result<T, nsresult> ConvertResponse(
   4022      StructuredCloneReadInfoParent&& aInfo);
   4023 
   4024  nsresult DoDatabaseWork(DatabaseConnection* aConnection) override;
   4025 
   4026  bool HasPreprocessInfo() override;
   4027 
   4028  mozilla::Result<PreprocessParams, nsresult> GetPreprocessParams() override;
   4029 
   4030  void GetResponse(RequestResponse& aResponse, size_t* aResponseSize) override;
   4031 };
   4032 
   4033 class ObjectStoreGetKeyRequestOp final : public NormalTransactionOp {
   4034  friend class TransactionBase;
   4035 
   4036  const IndexOrObjectStoreId mObjectStoreId;
   4037  const Maybe<SerializedKeyRange> mOptionalKeyRange;
   4038  const uint32_t mLimit;
   4039  const bool mGetAll;
   4040  nsTArray<Key> mResponse;
   4041 
   4042 private:
   4043  // Only created by TransactionBase.
   4044  ObjectStoreGetKeyRequestOp(SafeRefPtr<TransactionBase> aTransaction,
   4045                             const int64_t aRequestId,
   4046                             const RequestParams& aParams, bool aGetAll);
   4047 
   4048  ~ObjectStoreGetKeyRequestOp() override = default;
   4049 
   4050  nsresult DoDatabaseWork(DatabaseConnection* aConnection) override;
   4051 
   4052  void GetResponse(RequestResponse& aResponse, size_t* aResponseSize) override;
   4053 };
   4054 
   4055 class ObjectStoreDeleteRequestOp final : public NormalTransactionOp {
   4056  friend class TransactionBase;
   4057 
   4058  const ObjectStoreDeleteParams mParams;
   4059  ObjectStoreDeleteResponse mResponse;
   4060  bool mObjectStoreMayHaveIndexes;
   4061 
   4062 private:
   4063  ObjectStoreDeleteRequestOp(SafeRefPtr<TransactionBase> aTransaction,
   4064                             const int64_t aRequestId,
   4065                             const ObjectStoreDeleteParams& aParams);
   4066 
   4067  ~ObjectStoreDeleteRequestOp() override = default;
   4068 
   4069  nsresult DoDatabaseWork(DatabaseConnection* aConnection) override;
   4070 
   4071  void GetResponse(RequestResponse& aResponse, size_t* aResponseSize) override {
   4072    aResponse = std::move(mResponse);
   4073    *aResponseSize = 0;
   4074  }
   4075 };
   4076 
   4077 class ObjectStoreClearRequestOp final : public NormalTransactionOp {
   4078  friend class TransactionBase;
   4079 
   4080  const ObjectStoreClearParams mParams;
   4081  ObjectStoreClearResponse mResponse;
   4082  bool mObjectStoreMayHaveIndexes;
   4083 
   4084 private:
   4085  ObjectStoreClearRequestOp(SafeRefPtr<TransactionBase> aTransaction,
   4086                            const int64_t aRequestId,
   4087                            const ObjectStoreClearParams& aParams);
   4088 
   4089  ~ObjectStoreClearRequestOp() override = default;
   4090 
   4091  nsresult DoDatabaseWork(DatabaseConnection* aConnection) override;
   4092 
   4093  void GetResponse(RequestResponse& aResponse, size_t* aResponseSize) override {
   4094    aResponse = std::move(mResponse);
   4095    *aResponseSize = 0;
   4096  }
   4097 };
   4098 
   4099 class ObjectStoreCountRequestOp final : public NormalTransactionOp {
   4100  friend class TransactionBase;
   4101 
   4102  const ObjectStoreCountParams mParams;
   4103  ObjectStoreCountResponse mResponse;
   4104 
   4105 private:
   4106  ObjectStoreCountRequestOp(SafeRefPtr<TransactionBase> aTransaction,
   4107                            const int64_t aRequestId,
   4108                            const ObjectStoreCountParams& aParams)
   4109      : NormalTransactionOp(std::move(aTransaction), aRequestId),
   4110        mParams(aParams) {}
   4111 
   4112  ~ObjectStoreCountRequestOp() override = default;
   4113 
   4114  nsresult DoDatabaseWork(DatabaseConnection* aConnection) override;
   4115 
   4116  void GetResponse(RequestResponse& aResponse, size_t* aResponseSize) override {
   4117    aResponse = std::move(mResponse);
   4118    *aResponseSize = sizeof(uint64_t);
   4119  }
   4120 };
   4121 
   4122 class IndexRequestOpBase : public NormalTransactionOp {
   4123 protected:
   4124  const SafeRefPtr<FullIndexMetadata> mMetadata;
   4125 
   4126 protected:
   4127  IndexRequestOpBase(SafeRefPtr<TransactionBase> aTransaction,
   4128                     const int64_t aRequestId, const RequestParams& aParams)
   4129      : NormalTransactionOp(std::move(aTransaction), aRequestId),
   4130        mMetadata(IndexMetadataForParams(Transaction(), aParams)) {}
   4131 
   4132  ~IndexRequestOpBase() override = default;
   4133 
   4134 private:
   4135  static SafeRefPtr<FullIndexMetadata> IndexMetadataForParams(
   4136      const TransactionBase& aTransaction, const RequestParams& aParams);
   4137 };
   4138 
   4139 class IndexGetRequestOp final : public IndexRequestOpBase {
   4140  friend class TransactionBase;
   4141 
   4142  SafeRefPtr<Database> mDatabase;
   4143  const Maybe<SerializedKeyRange> mOptionalKeyRange;
   4144  AutoTArray<StructuredCloneReadInfoParent, 1> mResponse;
   4145  PBackgroundParent* mBackgroundParent;
   4146  const uint32_t mLimit;
   4147  const bool mGetAll;
   4148 
   4149 private:
   4150  // Only created by TransactionBase.
   4151  IndexGetRequestOp(SafeRefPtr<TransactionBase> aTransaction,
   4152                    const int64_t aRequestId, const RequestParams& aParams,
   4153                    bool aGetAll);
   4154 
   4155  ~IndexGetRequestOp() override = default;
   4156 
   4157  nsresult DoDatabaseWork(DatabaseConnection* aConnection) override;
   4158 
   4159  void GetResponse(RequestResponse& aResponse, size_t* aResponseSize) override;
   4160 };
   4161 
   4162 class IndexGetKeyRequestOp final : public IndexRequestOpBase {
   4163  friend class TransactionBase;
   4164 
   4165  const Maybe<SerializedKeyRange> mOptionalKeyRange;
   4166  AutoTArray<Key, 1> mResponse;
   4167  const uint32_t mLimit;
   4168  const bool mGetAll;
   4169 
   4170 private:
   4171  // Only created by TransactionBase.
   4172  IndexGetKeyRequestOp(SafeRefPtr<TransactionBase> aTransaction,
   4173                       const int64_t aRequestId, const RequestParams& aParams,
   4174                       bool aGetAll);
   4175 
   4176  ~IndexGetKeyRequestOp() override = default;
   4177 
   4178  nsresult DoDatabaseWork(DatabaseConnection* aConnection) override;
   4179 
   4180  void GetResponse(RequestResponse& aResponse, size_t* aResponseSize) override;
   4181 };
   4182 
   4183 class IndexCountRequestOp final : public IndexRequestOpBase {
   4184  friend class TransactionBase;
   4185 
   4186  const IndexCountParams mParams;
   4187  IndexCountResponse mResponse;
   4188 
   4189 private:
   4190  // Only created by TransactionBase.
   4191  IndexCountRequestOp(SafeRefPtr<TransactionBase> aTransaction,
   4192                      const int64_t aRequestId, const RequestParams& aParams)
   4193      : IndexRequestOpBase(std::move(aTransaction), aRequestId, aParams),
   4194        mParams(aParams.get_IndexCountParams()) {}
   4195 
   4196  ~IndexCountRequestOp() override = default;
   4197 
   4198  nsresult DoDatabaseWork(DatabaseConnection* aConnection) override;
   4199 
   4200  void GetResponse(RequestResponse& aResponse, size_t* aResponseSize) override {
   4201    aResponse = std::move(mResponse);
   4202    *aResponseSize = sizeof(uint64_t);
   4203  }
   4204 };
   4205 
   4206 template <IDBCursorType CursorType>
   4207 class Cursor;
   4208 
   4209 constexpr IDBCursorType ToKeyOnlyType(const IDBCursorType aType) {
   4210  MOZ_ASSERT(aType == IDBCursorType::ObjectStore ||
   4211             aType == IDBCursorType::ObjectStoreKey ||
   4212             aType == IDBCursorType::Index || aType == IDBCursorType::IndexKey);
   4213  switch (aType) {
   4214    case IDBCursorType::ObjectStore:
   4215      [[fallthrough]];
   4216    case IDBCursorType::ObjectStoreKey:
   4217      return IDBCursorType::ObjectStoreKey;
   4218    case IDBCursorType::Index:
   4219      [[fallthrough]];
   4220    case IDBCursorType::IndexKey:
   4221      return IDBCursorType::IndexKey;
   4222  }
   4223 }
   4224 
   4225 template <IDBCursorType CursorType>
   4226 using CursorPosition = CursorData<ToKeyOnlyType(CursorType)>;
   4227 
   4228 #ifdef DEBUG
   4229 constexpr indexedDB::OpenCursorParams::Type ToOpenCursorParamsType(
   4230    const IDBCursorType aType) {
   4231  MOZ_ASSERT(aType == IDBCursorType::ObjectStore ||
   4232             aType == IDBCursorType::ObjectStoreKey ||
   4233             aType == IDBCursorType::Index || aType == IDBCursorType::IndexKey);
   4234  switch (aType) {
   4235    case IDBCursorType::ObjectStore:
   4236      return indexedDB::OpenCursorParams::TObjectStoreOpenCursorParams;
   4237    case IDBCursorType::ObjectStoreKey:
   4238      return indexedDB::OpenCursorParams::TObjectStoreOpenKeyCursorParams;
   4239    case IDBCursorType::Index:
   4240      return indexedDB::OpenCursorParams::TIndexOpenCursorParams;
   4241    case IDBCursorType::IndexKey:
   4242      return indexedDB::OpenCursorParams::TIndexOpenKeyCursorParams;
   4243  }
   4244 }
   4245 #endif
   4246 
   4247 class CursorBase : public PBackgroundIDBCursorParent {
   4248  friend class TransactionBase;
   4249  template <IDBCursorType CursorType>
   4250  friend class CommonOpenOpHelper;
   4251 
   4252 protected:
   4253  const SafeRefPtr<TransactionBase> mTransaction;
   4254 
   4255  // This should only be touched on the PBackground thread to check whether
   4256  // the objectStore has been deleted. Holding these saves a hash lookup for
   4257  // every call to continue()/advance().
   4258  InitializedOnce<const NotNull<SafeRefPtr<FullObjectStoreMetadata>>>
   4259      mObjectStoreMetadata;
   4260 
   4261  const IndexOrObjectStoreId mObjectStoreId;
   4262 
   4263  LazyInitializedOnce<const Key>
   4264      mLocaleAwareRangeBound;  ///< If the cursor is based on a key range, the
   4265                               ///< bound in the direction of iteration (e.g.
   4266                               ///< the upper bound in case of mDirection ==
   4267                               ///< NEXT). If the cursor is based on a key, it
   4268                               ///< is unset. If mLocale is set, this was
   4269                               ///< converted to mLocale.
   4270 
   4271  const Direction mDirection;
   4272 
   4273  const int32_t mMaxExtraCount;
   4274 
   4275  const bool mIsSameProcessActor;
   4276 
   4277  struct ConstructFromTransactionBase {};
   4278 
   4279 public:
   4280  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::CursorBase,
   4281                                        final)
   4282 
   4283  CursorBase(SafeRefPtr<TransactionBase> aTransaction,
   4284             SafeRefPtr<FullObjectStoreMetadata> aObjectStoreMetadata,
   4285             Direction aDirection,
   4286             ConstructFromTransactionBase aConstructionTag);
   4287 
   4288 protected:
   4289  // Reference counted.
   4290  ~CursorBase() override { MOZ_ASSERT(!mObjectStoreMetadata); }
   4291 
   4292 private:
   4293  virtual bool Start(const int64_t aRequestId,
   4294                     const OpenCursorParams& aParams) = 0;
   4295 };
   4296 
   4297 class IndexCursorBase : public CursorBase {
   4298 public:
   4299  bool IsLocaleAware() const { return !mLocale.IsEmpty(); }
   4300 
   4301  IndexCursorBase(SafeRefPtr<TransactionBase> aTransaction,
   4302                  SafeRefPtr<FullObjectStoreMetadata> aObjectStoreMetadata,
   4303                  SafeRefPtr<FullIndexMetadata> aIndexMetadata,
   4304                  Direction aDirection,
   4305                  ConstructFromTransactionBase aConstructionTag)
   4306      : CursorBase{std::move(aTransaction), std::move(aObjectStoreMetadata),
   4307                   aDirection, aConstructionTag},
   4308        mIndexMetadata(WrapNotNull(std::move(aIndexMetadata))),
   4309        mIndexId((*mIndexMetadata)->mCommonMetadata.id()),
   4310        mUniqueIndex((*mIndexMetadata)->mCommonMetadata.unique()),
   4311        mLocale((*mIndexMetadata)->mCommonMetadata.locale()) {}
   4312 
   4313 protected:
   4314  IndexOrObjectStoreId Id() const { return mIndexId; }
   4315 
   4316  // This should only be touched on the PBackground thread to check whether
   4317  // the index has been deleted. Holding these saves a hash lookup for every
   4318  // call to continue()/advance().
   4319  InitializedOnce<const NotNull<SafeRefPtr<FullIndexMetadata>>> mIndexMetadata;
   4320  const IndexOrObjectStoreId mIndexId;
   4321  const bool mUniqueIndex;
   4322  const nsCString
   4323      mLocale;  ///< The locale if the cursor is locale-aware, otherwise empty.
   4324 
   4325  struct ContinueQueries {
   4326    nsCString mContinueQuery;
   4327    nsCString mContinueToQuery;
   4328    nsCString mContinuePrimaryKeyQuery;
   4329 
   4330    const nsACString& GetContinueQuery(const bool hasContinueKey,
   4331                                       const bool hasContinuePrimaryKey) const {
   4332      return hasContinuePrimaryKey ? mContinuePrimaryKeyQuery
   4333             : hasContinueKey      ? mContinueToQuery
   4334                                   : mContinueQuery;
   4335    }
   4336  };
   4337 };
   4338 
   4339 class ObjectStoreCursorBase : public CursorBase {
   4340 public:
   4341  using CursorBase::CursorBase;
   4342 
   4343  static constexpr bool IsLocaleAware() { return false; }
   4344 
   4345 protected:
   4346  IndexOrObjectStoreId Id() const { return mObjectStoreId; }
   4347 
   4348  struct ContinueQueries {
   4349    nsCString mContinueQuery;
   4350    nsCString mContinueToQuery;
   4351 
   4352    const nsACString& GetContinueQuery(const bool hasContinueKey,
   4353                                       const bool hasContinuePrimaryKey) const {
   4354      MOZ_ASSERT(!hasContinuePrimaryKey);
   4355      return hasContinueKey ? mContinueToQuery : mContinueQuery;
   4356    }
   4357  };
   4358 };
   4359 
   4360 using FilesArray = nsTArray<nsTArray<StructuredCloneFileParent>>;
   4361 
   4362 struct PseudoFilesArray {
   4363  static constexpr bool IsEmpty() { return true; }
   4364 
   4365  static constexpr void Clear() {}
   4366 };
   4367 
   4368 template <IDBCursorType CursorType>
   4369 using FilesArrayT =
   4370    std::conditional_t<!CursorTypeTraits<CursorType>::IsKeyOnlyCursor,
   4371                       FilesArray, PseudoFilesArray>;
   4372 
   4373 class ValueCursorBase {
   4374  friend struct ValuePopulateResponseHelper<true>;
   4375  friend struct ValuePopulateResponseHelper<false>;
   4376 
   4377 protected:
   4378  explicit ValueCursorBase(TransactionBase* const aTransaction)
   4379      : mDatabase(aTransaction->GetDatabasePtr()),
   4380        mFileManager(mDatabase->GetFileManagerPtr()),
   4381        mBackgroundParent(WrapNotNull(aTransaction->GetBackgroundParent())) {
   4382    MOZ_ASSERT(mDatabase);
   4383  }
   4384 
   4385  void ProcessFiles(CursorResponse& aResponse, const FilesArray& aFiles);
   4386 
   4387  ~ValueCursorBase() { MOZ_ASSERT(!mBackgroundParent); }
   4388 
   4389  const SafeRefPtr<Database> mDatabase;
   4390  const NotNull<SafeRefPtr<DatabaseFileManager>> mFileManager;
   4391 
   4392  InitializedOnce<const NotNull<PBackgroundParent*>> mBackgroundParent;
   4393 };
   4394 
   4395 class KeyCursorBase {
   4396 protected:
   4397  explicit KeyCursorBase(TransactionBase* const /*aTransaction*/) {}
   4398 
   4399  static constexpr void ProcessFiles(CursorResponse& aResponse,
   4400                                     const PseudoFilesArray& aFiles) {}
   4401 };
   4402 
   4403 template <IDBCursorType CursorType>
   4404 class CursorOpBaseHelperBase;
   4405 
   4406 template <IDBCursorType CursorType>
   4407 class Cursor final
   4408    : public std::conditional_t<
   4409          CursorTypeTraits<CursorType>::IsObjectStoreCursor,
   4410          ObjectStoreCursorBase, IndexCursorBase>,
   4411      public std::conditional_t<CursorTypeTraits<CursorType>::IsKeyOnlyCursor,
   4412                                KeyCursorBase, ValueCursorBase> {
   4413  using Base =
   4414      std::conditional_t<CursorTypeTraits<CursorType>::IsObjectStoreCursor,
   4415                         ObjectStoreCursorBase, IndexCursorBase>;
   4416 
   4417  using KeyValueBase =
   4418      std::conditional_t<CursorTypeTraits<CursorType>::IsKeyOnlyCursor,
   4419                         KeyCursorBase, ValueCursorBase>;
   4420 
   4421  static constexpr bool IsIndexCursor =
   4422      !CursorTypeTraits<CursorType>::IsObjectStoreCursor;
   4423 
   4424  static constexpr bool IsValueCursor =
   4425      !CursorTypeTraits<CursorType>::IsKeyOnlyCursor;
   4426 
   4427  class CursorOpBase;
   4428  class OpenOp;
   4429  class ContinueOp;
   4430 
   4431  using Base::Id;
   4432  using CursorBase::Manager;
   4433  using CursorBase::mDirection;
   4434  using CursorBase::mObjectStoreId;
   4435  using CursorBase::mTransaction;
   4436  using typename CursorBase::ActorDestroyReason;
   4437 
   4438  using TypedOpenOpHelper =
   4439      std::conditional_t<IsIndexCursor, IndexOpenOpHelper<CursorType>,
   4440                         ObjectStoreOpenOpHelper<CursorType>>;
   4441 
   4442  friend class CursorOpBaseHelperBase<CursorType>;
   4443  friend class CommonOpenOpHelper<CursorType>;
   4444  friend TypedOpenOpHelper;
   4445  friend class OpenOpHelper<CursorType>;
   4446 
   4447  CursorOpBase* mCurrentlyRunningOp = nullptr;
   4448 
   4449  LazyInitializedOnce<const typename Base::ContinueQueries> mContinueQueries;
   4450 
   4451  // Only called by TransactionBase.
   4452  bool Start(const int64_t aRequestId, const OpenCursorParams& aParams) final;
   4453 
   4454  void SendResponseInternal(CursorResponse& aResponse,
   4455                            const FilesArrayT<CursorType>& aFiles);
   4456 
   4457  // Must call SendResponseInternal!
   4458  bool SendResponse(const CursorResponse& aResponse) = delete;
   4459 
   4460  // IPDL methods.
   4461  void ActorDestroy(ActorDestroyReason aWhy) override;
   4462 
   4463  mozilla::ipc::IPCResult RecvDeleteMe() override;
   4464 
   4465  mozilla::ipc::IPCResult RecvContinue(
   4466      const int64_t& aRequestId, const CursorRequestParams& aParams,
   4467      const Key& aCurrentKey, const Key& aCurrentObjectStoreKey) override;
   4468 
   4469 public:
   4470  Cursor(SafeRefPtr<TransactionBase> aTransaction,
   4471         SafeRefPtr<FullObjectStoreMetadata> aObjectStoreMetadata,
   4472         SafeRefPtr<FullIndexMetadata> aIndexMetadata,
   4473         typename Base::Direction aDirection,
   4474         typename Base::ConstructFromTransactionBase aConstructionTag)
   4475      : Base{std::move(aTransaction), std::move(aObjectStoreMetadata),
   4476             std::move(aIndexMetadata), aDirection, aConstructionTag},
   4477        KeyValueBase{this->mTransaction.unsafeGetRawPtr()} {}
   4478 
   4479  Cursor(SafeRefPtr<TransactionBase> aTransaction,
   4480         SafeRefPtr<FullObjectStoreMetadata> aObjectStoreMetadata,
   4481         typename Base::Direction aDirection,
   4482         typename Base::ConstructFromTransactionBase aConstructionTag)
   4483      : Base{std::move(aTransaction), std::move(aObjectStoreMetadata),
   4484             aDirection, aConstructionTag},
   4485        KeyValueBase{this->mTransaction.unsafeGetRawPtr()} {}
   4486 
   4487 private:
   4488  void SetOptionalKeyRange(const Maybe<SerializedKeyRange>& aOptionalKeyRange,
   4489                           bool* aOpen);
   4490 
   4491  bool VerifyRequestParams(const CursorRequestParams& aParams,
   4492                           const CursorPosition<CursorType>& aPosition) const;
   4493 
   4494  ~Cursor() final = default;
   4495 };
   4496 
   4497 template <IDBCursorType CursorType>
   4498 class Cursor<CursorType>::CursorOpBase
   4499    : public TransactionDatabaseOperationBase {
   4500  friend class CursorOpBaseHelperBase<CursorType>;
   4501 
   4502 protected:
   4503  RefPtr<Cursor> mCursor;
   4504  FilesArrayT<CursorType> mFiles;  // TODO: Consider removing this member
   4505                                   // entirely if we are no value cursor.
   4506 
   4507  CursorResponse mResponse;
   4508 
   4509 #ifdef DEBUG
   4510  bool mResponseSent;
   4511 #endif
   4512 
   4513 protected:
   4514  explicit CursorOpBase(Cursor* aCursor, const int64_t aRequestId)
   4515      : TransactionDatabaseOperationBase(aCursor->mTransaction.clonePtr(),
   4516                                         /* aRequestId */ aRequestId),
   4517        mCursor(aCursor)
   4518 #ifdef DEBUG
   4519        ,
   4520        mResponseSent(false)
   4521 #endif
   4522  {
   4523    AssertIsOnBackgroundThread();
   4524    MOZ_ASSERT(aCursor);
   4525  }
   4526 
   4527  ~CursorOpBase() override = default;
   4528 
   4529  bool SendFailureResult(nsresult aResultCode) final;
   4530  nsresult SendSuccessResult() final;
   4531 
   4532  void Cleanup() override;
   4533 };
   4534 
   4535 template <IDBCursorType CursorType>
   4536 class OpenOpHelper;
   4537 
   4538 using ResponseSizeOrError = Result<size_t, nsresult>;
   4539 
   4540 template <IDBCursorType CursorType>
   4541 class CursorOpBaseHelperBase {
   4542 public:
   4543  explicit CursorOpBaseHelperBase(
   4544      typename Cursor<CursorType>::CursorOpBase& aOp)
   4545      : mOp{aOp} {}
   4546 
   4547  ResponseSizeOrError PopulateResponseFromStatement(mozIStorageStatement* aStmt,
   4548                                                    bool aInitializeResponse,
   4549                                                    Key* const aOptOutSortKey);
   4550 
   4551  void PopulateExtraResponses(mozIStorageStatement* aStmt,
   4552                              uint32_t aMaxExtraCount,
   4553                              const size_t aInitialResponseSize,
   4554                              const nsACString& aOperation,
   4555                              Key* const aOptPreviousSortKey);
   4556 
   4557 protected:
   4558  Cursor<CursorType>& GetCursor() {
   4559    MOZ_ASSERT(mOp.mCursor);
   4560    return *mOp.mCursor;
   4561  }
   4562 
   4563  void SetResponse(CursorResponse aResponse) {
   4564    mOp.mResponse = std::move(aResponse);
   4565  }
   4566 
   4567 protected:
   4568  typename Cursor<CursorType>::CursorOpBase& mOp;
   4569 };
   4570 
   4571 class CommonOpenOpHelperBase {
   4572 protected:
   4573  static void AppendConditionClause(const nsACString& aColumnName,
   4574                                    const nsACString& aStatementParameterName,
   4575                                    bool aLessThan, bool aEquals,
   4576                                    nsCString& aResult);
   4577 };
   4578 
   4579 template <IDBCursorType CursorType>
   4580 class CommonOpenOpHelper : public CursorOpBaseHelperBase<CursorType>,
   4581                           protected CommonOpenOpHelperBase {
   4582 public:
   4583  explicit CommonOpenOpHelper(typename Cursor<CursorType>::OpenOp& aOp)
   4584      : CursorOpBaseHelperBase<CursorType>{aOp} {}
   4585 
   4586 protected:
   4587  using CursorOpBaseHelperBase<CursorType>::GetCursor;
   4588  using CursorOpBaseHelperBase<CursorType>::PopulateExtraResponses;
   4589  using CursorOpBaseHelperBase<CursorType>::PopulateResponseFromStatement;
   4590  using CursorOpBaseHelperBase<CursorType>::SetResponse;
   4591 
   4592  const Maybe<SerializedKeyRange>& GetOptionalKeyRange() const {
   4593    // This downcast is safe, since we initialized mOp from an OpenOp in the
   4594    // ctor.
   4595    return static_cast<typename Cursor<CursorType>::OpenOp&>(this->mOp)
   4596        .mOptionalKeyRange;
   4597  }
   4598 
   4599  nsresult ProcessStatementSteps(mozIStorageStatement* aStmt);
   4600 };
   4601 
   4602 template <IDBCursorType CursorType>
   4603 class ObjectStoreOpenOpHelper : protected CommonOpenOpHelper<CursorType> {
   4604 public:
   4605  using CommonOpenOpHelper<CursorType>::CommonOpenOpHelper;
   4606 
   4607 protected:
   4608  using CommonOpenOpHelper<CursorType>::GetCursor;
   4609  using CommonOpenOpHelper<CursorType>::GetOptionalKeyRange;
   4610  using CommonOpenOpHelper<CursorType>::AppendConditionClause;
   4611 
   4612  void PrepareKeyConditionClauses(const nsACString& aDirectionClause,
   4613                                  const nsACString& aQueryStart);
   4614 };
   4615 
   4616 template <IDBCursorType CursorType>
   4617 class IndexOpenOpHelper : protected CommonOpenOpHelper<CursorType> {
   4618 public:
   4619  using CommonOpenOpHelper<CursorType>::CommonOpenOpHelper;
   4620 
   4621 protected:
   4622  using CommonOpenOpHelper<CursorType>::GetCursor;
   4623  using CommonOpenOpHelper<CursorType>::GetOptionalKeyRange;
   4624  using CommonOpenOpHelper<CursorType>::AppendConditionClause;
   4625 
   4626  void PrepareIndexKeyConditionClause(
   4627      const nsACString& aDirectionClause,
   4628      const nsLiteralCString& aObjectDataKeyPrefix, nsAutoCString aQueryStart);
   4629 };
   4630 
   4631 template <>
   4632 class OpenOpHelper<IDBCursorType::ObjectStore>
   4633    : public ObjectStoreOpenOpHelper<IDBCursorType::ObjectStore> {
   4634 public:
   4635  using ObjectStoreOpenOpHelper<
   4636      IDBCursorType::ObjectStore>::ObjectStoreOpenOpHelper;
   4637 
   4638  nsresult DoDatabaseWork(DatabaseConnection* aConnection);
   4639 };
   4640 
   4641 template <>
   4642 class OpenOpHelper<IDBCursorType::ObjectStoreKey>
   4643    : public ObjectStoreOpenOpHelper<IDBCursorType::ObjectStoreKey> {
   4644 public:
   4645  using ObjectStoreOpenOpHelper<
   4646      IDBCursorType::ObjectStoreKey>::ObjectStoreOpenOpHelper;
   4647 
   4648  nsresult DoDatabaseWork(DatabaseConnection* aConnection);
   4649 };
   4650 
   4651 template <>
   4652 class OpenOpHelper<IDBCursorType::Index>
   4653    : IndexOpenOpHelper<IDBCursorType::Index> {
   4654 private:
   4655  void PrepareKeyConditionClauses(const nsACString& aDirectionClause,
   4656                                  nsAutoCString aQueryStart) {
   4657    PrepareIndexKeyConditionClause(aDirectionClause, "index_table."_ns,
   4658                                   std::move(aQueryStart));
   4659  }
   4660 
   4661 public:
   4662  using IndexOpenOpHelper<IDBCursorType::Index>::IndexOpenOpHelper;
   4663 
   4664  nsresult DoDatabaseWork(DatabaseConnection* aConnection);
   4665 };
   4666 
   4667 template <>
   4668 class OpenOpHelper<IDBCursorType::IndexKey>
   4669    : IndexOpenOpHelper<IDBCursorType::IndexKey> {
   4670 private:
   4671  void PrepareKeyConditionClauses(const nsACString& aDirectionClause,
   4672                                  nsAutoCString aQueryStart) {
   4673    PrepareIndexKeyConditionClause(aDirectionClause, ""_ns,
   4674                                   std::move(aQueryStart));
   4675  }
   4676 
   4677 public:
   4678  using IndexOpenOpHelper<IDBCursorType::IndexKey>::IndexOpenOpHelper;
   4679 
   4680  nsresult DoDatabaseWork(DatabaseConnection* aConnection);
   4681 };
   4682 
   4683 template <IDBCursorType CursorType>
   4684 class Cursor<CursorType>::OpenOp final : public CursorOpBase {
   4685  friend class Cursor<CursorType>;
   4686  friend class CommonOpenOpHelper<CursorType>;
   4687 
   4688  const Maybe<SerializedKeyRange> mOptionalKeyRange;
   4689 
   4690  using CursorOpBase::mCursor;
   4691  using CursorOpBase::mResponse;
   4692 
   4693  // Only created by Cursor.
   4694  OpenOp(Cursor* const aCursor, const int64_t aRequestId,
   4695         const Maybe<SerializedKeyRange>& aOptionalKeyRange)
   4696      : CursorOpBase(aCursor, aRequestId),
   4697        mOptionalKeyRange(aOptionalKeyRange) {}
   4698 
   4699  // Reference counted.
   4700  ~OpenOp() override = default;
   4701 
   4702  nsresult DoDatabaseWork(DatabaseConnection* aConnection) override;
   4703 };
   4704 
   4705 template <IDBCursorType CursorType>
   4706 class Cursor<CursorType>::ContinueOp final
   4707    : public Cursor<CursorType>::CursorOpBase {
   4708  friend class Cursor<CursorType>;
   4709 
   4710  using CursorOpBase::mCursor;
   4711  using CursorOpBase::mResponse;
   4712  const CursorRequestParams mParams;
   4713 
   4714  // Only created by Cursor.
   4715  ContinueOp(Cursor* const aCursor, int64_t aRequestId,
   4716             CursorRequestParams aParams, CursorPosition<CursorType> aPosition)
   4717      : CursorOpBase(aCursor, aRequestId),
   4718        mParams(std::move(aParams)),
   4719        mCurrentPosition{std::move(aPosition)} {
   4720    MOZ_ASSERT(mParams.type() != CursorRequestParams::T__None);
   4721  }
   4722 
   4723  // Reference counted.
   4724  ~ContinueOp() override = default;
   4725 
   4726  nsresult DoDatabaseWork(DatabaseConnection* aConnection) override;
   4727 
   4728  const CursorPosition<CursorType> mCurrentPosition;
   4729 };
   4730 
   4731 class Utils final : public PBackgroundIndexedDBUtilsParent {
   4732 #ifdef DEBUG
   4733  bool mActorDestroyed;
   4734 #endif
   4735 
   4736 public:
   4737  Utils();
   4738 
   4739  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::Utils)
   4740 
   4741 private:
   4742  // Reference counted.
   4743  ~Utils() override;
   4744 
   4745  // IPDL methods are only called by IPDL.
   4746  void ActorDestroy(ActorDestroyReason aWhy) override;
   4747 
   4748  mozilla::ipc::IPCResult RecvDeleteMe() override;
   4749 
   4750  mozilla::ipc::IPCResult RecvGetFileReferences(
   4751      const PersistenceType& aPersistenceType, const nsACString& aOrigin,
   4752      const nsAString& aDatabaseName, const int64_t& aFileId, int32_t* aRefCnt,
   4753      int32_t* aDBRefCnt, bool* aResult) override;
   4754 
   4755  mozilla::ipc::IPCResult RecvDoMaintenance(
   4756      DoMaintenanceResolver&& aResolver) override;
   4757 };
   4758 
   4759 /*******************************************************************************
   4760 * Other class declarations
   4761 ******************************************************************************/
   4762 
   4763 struct DatabaseActorInfo final {
   4764  friend mozilla::DefaultDelete<DatabaseActorInfo>;
   4765 
   4766  SafeRefPtr<FullDatabaseMetadata> mMetadata;
   4767  // We don't use LinkedList<CheckedUnsafePtr<Database>> because
   4768  // CheckedUnsafePtr is not suitable for use within LinkedList. While it's
   4769  // theoretically possible to adapt LinkedList to support it, doing so would
   4770  // introduce unnecessary overhead. Instead, we use a simpler and more
   4771  // efficient approach. Each Database instance asserts !isInList() in its
   4772  // destructor to catch dangling pointer issues.
   4773  LinkedList<Database> mLiveDatabases;
   4774  RefPtr<FactoryOp> mWaitingFactoryOp;
   4775 
   4776  DatabaseActorInfo(SafeRefPtr<FullDatabaseMetadata> aMetadata,
   4777                    NotNull<Database*> aDatabase)
   4778      : mMetadata(std::move(aMetadata)) {
   4779    MOZ_COUNT_CTOR(DatabaseActorInfo);
   4780 
   4781    mLiveDatabases.insertBack(aDatabase);
   4782  }
   4783 
   4784 private:
   4785  ~DatabaseActorInfo() {
   4786    MOZ_ASSERT(mLiveDatabases.isEmpty());
   4787    MOZ_ASSERT(!mWaitingFactoryOp || !mWaitingFactoryOp->HasBlockedDatabases());
   4788 
   4789    MOZ_COUNT_DTOR(DatabaseActorInfo);
   4790  }
   4791 };
   4792 
   4793 class DatabaseLoggingInfo final {
   4794 #ifdef DEBUG
   4795  // Just for potential warnings.
   4796  friend class Factory;
   4797 #endif
   4798 
   4799  LoggingInfo mLoggingInfo;
   4800 
   4801 public:
   4802  explicit DatabaseLoggingInfo(const LoggingInfo& aLoggingInfo)
   4803      : mLoggingInfo(aLoggingInfo) {
   4804    AssertIsOnBackgroundThread();
   4805    MOZ_ASSERT(aLoggingInfo.nextTransactionSerialNumber());
   4806    MOZ_ASSERT(aLoggingInfo.nextVersionChangeTransactionSerialNumber());
   4807    MOZ_ASSERT(aLoggingInfo.nextRequestSerialNumber());
   4808  }
   4809 
   4810  const nsID& Id() const {
   4811    AssertIsOnBackgroundThread();
   4812 
   4813    return mLoggingInfo.backgroundChildLoggingId();
   4814  }
   4815 
   4816  int64_t NextTransactionSN(IDBTransaction::Mode aMode) {
   4817    AssertIsOnBackgroundThread();
   4818    MOZ_ASSERT(mLoggingInfo.nextTransactionSerialNumber() < INT64_MAX);
   4819    MOZ_ASSERT(mLoggingInfo.nextVersionChangeTransactionSerialNumber() >
   4820               INT64_MIN);
   4821 
   4822    if (aMode == IDBTransaction::Mode::VersionChange) {
   4823      return mLoggingInfo.nextVersionChangeTransactionSerialNumber()--;
   4824    }
   4825 
   4826    return mLoggingInfo.nextTransactionSerialNumber()++;
   4827  }
   4828 
   4829  uint64_t NextRequestSN() {
   4830    AssertIsOnBackgroundThread();
   4831    MOZ_ASSERT(mLoggingInfo.nextRequestSerialNumber() < UINT64_MAX);
   4832 
   4833    return mLoggingInfo.nextRequestSerialNumber()++;
   4834  }
   4835 
   4836  NS_INLINE_DECL_REFCOUNTING(DatabaseLoggingInfo)
   4837 
   4838 private:
   4839  ~DatabaseLoggingInfo();
   4840 };
   4841 
   4842 class QuotaClient final : public mozilla::dom::quota::Client {
   4843  friend class GetDatabasesOp;
   4844 
   4845  static QuotaClient* sInstance;
   4846 
   4847  nsCOMPtr<nsIEventTarget> mBackgroundThread;
   4848  nsCOMPtr<nsITimer> mDeleteTimer;
   4849  nsTArray<RefPtr<Maintenance>> mMaintenanceQueue;
   4850  RefPtr<Maintenance> mCurrentMaintenance;
   4851  RefPtr<nsThreadPool> mMaintenanceThreadPool;
   4852  nsClassHashtable<nsRefPtrHashKey<DatabaseFileManager>, nsTArray<int64_t>>
   4853      mPendingDeleteInfos;
   4854 
   4855 public:
   4856  QuotaClient();
   4857 
   4858  static QuotaClient* GetInstance() {
   4859    AssertIsOnBackgroundThread();
   4860 
   4861    return sInstance;
   4862  }
   4863 
   4864  nsIEventTarget* BackgroundThread() const {
   4865    MOZ_ASSERT(mBackgroundThread);
   4866    return mBackgroundThread;
   4867  }
   4868 
   4869  nsresult AsyncDeleteFile(DatabaseFileManager* aFileManager, int64_t aFileId);
   4870 
   4871  nsresult FlushPendingFileDeletions();
   4872 
   4873  RefPtr<BoolPromise> DoMaintenance();
   4874 
   4875  RefPtr<Maintenance> GetCurrentMaintenance() const {
   4876    return mCurrentMaintenance;
   4877  }
   4878 
   4879  void NoteFinishedMaintenance(Maintenance* aMaintenance) {
   4880    AssertIsOnBackgroundThread();
   4881    MOZ_ASSERT(aMaintenance);
   4882    MOZ_ASSERT(mCurrentMaintenance == aMaintenance);
   4883 
   4884    mCurrentMaintenance = nullptr;
   4885 
   4886    QuotaManager::MaybeRecordQuotaClientShutdownStep(quota::Client::IDB,
   4887                                                     "Maintenance finished"_ns);
   4888 
   4889    ProcessMaintenanceQueue();
   4890  }
   4891 
   4892  nsThreadPool* GetOrCreateThreadPool();
   4893 
   4894  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::indexedDB::QuotaClient,
   4895                                        override)
   4896 
   4897  mozilla::dom::quota::Client::Type GetType() override;
   4898 
   4899  nsresult UpgradeStorageFrom1_0To2_0(nsIFile* aDirectory) override;
   4900 
   4901  nsresult UpgradeStorageFrom2_1To2_2(nsIFile* aDirectory) override;
   4902 
   4903  Result<UsageInfo, nsresult> InitOrigin(PersistenceType aPersistenceType,
   4904                                         const OriginMetadata& aOriginMetadata,
   4905                                         const AtomicBool& aCanceled) override;
   4906 
   4907  nsresult InitOriginWithoutTracking(PersistenceType aPersistenceType,
   4908                                     const OriginMetadata& aOriginMetadata,
   4909                                     const AtomicBool& aCanceled) override;
   4910 
   4911  Result<UsageInfo, nsresult> GetUsageForOrigin(
   4912      PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata,
   4913      const AtomicBool& aCanceled) override;
   4914 
   4915  void OnOriginClearCompleted(const OriginMetadata& aOriginMetadata) override;
   4916 
   4917  void OnRepositoryClearCompleted(PersistenceType aPersistenceType) override;
   4918 
   4919  void ReleaseIOThreadObjects() override;
   4920 
   4921  void AbortOperationsForLocks(
   4922      const DirectoryLockIdTable& aDirectoryLockIds) override;
   4923 
   4924  void AbortOperationsForProcess(ContentParentId aContentParentId) override;
   4925 
   4926  void AbortAllOperations() override;
   4927 
   4928  void StartIdleMaintenance() override;
   4929 
   4930  void StopIdleMaintenance() override;
   4931 
   4932 private:
   4933  ~QuotaClient() override;
   4934 
   4935  void InitiateShutdown() override;
   4936  bool IsShutdownCompleted() const override;
   4937  nsCString GetShutdownStatus() const override;
   4938  void ForceKillActors() override;
   4939  void FinalizeShutdown() override;
   4940 
   4941  static void DeleteTimerCallback(nsITimer* aTimer, void* aClosure);
   4942 
   4943  void AbortAllMaintenances();
   4944 
   4945  Result<nsCOMPtr<nsIFile>, nsresult> GetDirectory(
   4946      const OriginMetadata& aOriginMetadata);
   4947 
   4948  struct SubdirectoriesToProcessAndDatabaseFilenames {
   4949    AutoTArray<nsString, 20> subdirsToProcess;
   4950    nsTHashSet<nsString> databaseFilenames{20};
   4951  };
   4952 
   4953  struct SubdirectoriesToProcessAndDatabaseFilenamesAndObsoleteFilenames {
   4954    AutoTArray<nsString, 20> subdirsToProcess;
   4955    nsTHashSet<nsString> databaseFilenames{20};
   4956    nsTHashSet<nsString> obsoleteFilenames{20};
   4957  };
   4958 
   4959  enum class ObsoleteFilenamesHandling { Include, Omit };
   4960 
   4961  template <ObsoleteFilenamesHandling ObsoleteFilenames>
   4962  using GetDatabaseFilenamesResult = std::conditional_t<
   4963      ObsoleteFilenames == ObsoleteFilenamesHandling::Include,
   4964      SubdirectoriesToProcessAndDatabaseFilenamesAndObsoleteFilenames,
   4965      SubdirectoriesToProcessAndDatabaseFilenames>;
   4966 
   4967  // Returns a two-part or three-part structure:
   4968  //
   4969  // The first part is an array of subdirectories to process.
   4970  //
   4971  // The second part is a hashtable of database filenames.
   4972  //
   4973  // When ObsoleteFilenames is ObsoleteFilenamesHandling::Include, will also
   4974  // collect files based on the marker files. For now,
   4975  // GetUsageForOriginInternal() is the only consumer of this result because it
   4976  // checks those unfinished deletion and clean them up after that.
   4977  template <ObsoleteFilenamesHandling ObsoleteFilenames =
   4978                ObsoleteFilenamesHandling::Omit>
   4979  Result<GetDatabaseFilenamesResult<ObsoleteFilenames>,
   4980         nsresult> static GetDatabaseFilenames(nsIFile& aDirectory,
   4981                                               const AtomicBool& aCanceled);
   4982 
   4983  nsresult GetUsageForOriginInternal(PersistenceType aPersistenceType,
   4984                                     const OriginMetadata& aOriginMetadata,
   4985                                     const AtomicBool& aCanceled,
   4986                                     bool aInitializing, UsageInfo* aUsageInfo);
   4987 
   4988  // Runs on the PBackground thread. Checks to see if there's a queued
   4989  // Maintenance to run.
   4990  void ProcessMaintenanceQueue();
   4991 };
   4992 
   4993 class DeleteFilesRunnable final : public Runnable {
   4994  using ClientDirectoryLock = mozilla::dom::quota::ClientDirectoryLock;
   4995 
   4996  enum State {
   4997    // Just created on the PBackground thread. Next step is
   4998    // State_DirectoryOpenPending.
   4999    State_Initial,
   5000 
   5001    // Waiting for directory open allowed on the main thread. The next step is
   5002    // State_DatabaseWorkOpen.
   5003    State_DirectoryOpenPending,
   5004 
   5005    // Waiting to do/doing work on the QuotaManager IO thread. The next step is
   5006    // State_UnblockingOpen.
   5007    State_DatabaseWorkOpen,
   5008 
   5009    // Notifying the QuotaManager that it can proceed to the next operation on
   5010    // the main thread. Next step is State_Completed.
   5011    State_UnblockingOpen,
   5012 
   5013    // All done.
   5014    State_Completed
   5015  };
   5016 
   5017  nsCOMPtr<nsIEventTarget> mOwningEventTarget;
   5018  SafeRefPtr<DatabaseFileManager> mFileManager;
   5019  ClientDirectoryLockHandle mDirectoryLockHandle;
   5020  nsTArray<int64_t> mFileIds;
   5021  State mState;
   5022  DEBUGONLY(bool mDEBUGCountsAsPending = false);
   5023 
   5024  static uint64_t sPendingRunnables;
   5025 
   5026 public:
   5027  DeleteFilesRunnable(SafeRefPtr<DatabaseFileManager> aFileManager,
   5028                      nsTArray<int64_t>&& aFileIds);
   5029 
   5030  void RunImmediately();
   5031 
   5032  static bool IsDeletionPending() { return sPendingRunnables > 0; }
   5033 
   5034 private:
   5035 #ifdef DEBUG
   5036  ~DeleteFilesRunnable();
   5037 #else
   5038  ~DeleteFilesRunnable() = default;
   5039 #endif
   5040 
   5041  void Open();
   5042 
   5043  void DoDatabaseWork();
   5044 
   5045  void Finish();
   5046 
   5047  void UnblockOpen();
   5048 
   5049  NS_DECL_NSIRUNNABLE
   5050 
   5051  void DirectoryLockAcquired(ClientDirectoryLockHandle aLockHandle);
   5052 
   5053  void DirectoryLockFailed();
   5054 };
   5055 
   5056 class Maintenance final : public Runnable {
   5057  struct DirectoryInfo final {
   5058    InitializedOnce<const OriginMetadata> mOriginMetadata;
   5059    InitializedOnce<const nsTArray<nsString>> mDatabasePaths;
   5060    const PersistenceType mPersistenceType;
   5061 
   5062    DirectoryInfo(PersistenceType aPersistenceType,
   5063                  OriginMetadata aOriginMetadata,
   5064                  nsTArray<nsString>&& aDatabasePaths);
   5065 
   5066    DirectoryInfo(const DirectoryInfo& aOther) = delete;
   5067    DirectoryInfo(DirectoryInfo&& aOther) = delete;
   5068 
   5069    ~DirectoryInfo() { MOZ_COUNT_DTOR(Maintenance::DirectoryInfo); }
   5070  };
   5071 
   5072  enum class State {
   5073    // Newly created on the PBackground thread. Will proceed immediately or be
   5074    // added to the maintenance queue. The next step is either
   5075    // DirectoryOpenPending if IndexedDatabaseManager is running, or
   5076    // CreateIndexedDatabaseManager if not.
   5077    Initial = 0,
   5078 
   5079    // Create IndexedDatabaseManager on the main thread. The next step is either
   5080    // Finishing if IndexedDatabaseManager initialization fails, or
   5081    // IndexedDatabaseManagerOpen if initialization succeeds.
   5082    CreateIndexedDatabaseManager,
   5083 
   5084    // Call OpenDirectory() on the PBackground thread. The next step is
   5085    // DirectoryOpenPending.
   5086    IndexedDatabaseManagerOpen,
   5087 
   5088    // Waiting for directory open allowed on the PBackground thread. The next
   5089    // step is either Finishing if directory lock failed to acquire, or
   5090    // DirectoryWorkOpen if directory lock is acquired.
   5091    DirectoryOpenPending,
   5092 
   5093    // Waiting to do/doing work on the QuotaManager IO thread. The next step is
   5094    // BeginDatabaseMaintenance.
   5095    DirectoryWorkOpen,
   5096 
   5097    // Dispatching a runnable for each database on the PBackground thread. The
   5098    // next state is either WaitingForDatabaseMaintenancesToComplete if at least
   5099    // one runnable has been dispatched, or Finishing otherwise.
   5100    BeginDatabaseMaintenance,
   5101 
   5102    // Waiting for DatabaseMaintenance to finish on maintenance thread pool.
   5103    // The next state is Finishing if the last runnable has finished.
   5104    WaitingForDatabaseMaintenancesToComplete,
   5105 
   5106    // Waiting to finish/finishing on the PBackground thread. The next step is
   5107    // Completed.
   5108    Finishing,
   5109 
   5110    // All done.
   5111    Complete
   5112  };
   5113 
   5114  RefPtr<QuotaClient> mQuotaClient;
   5115  MozPromiseHolder<BoolPromise> mPromiseHolder;
   5116  PRTime mStartTime;
   5117  RefPtr<UniversalDirectoryLock> mPendingDirectoryLock;
   5118  // The directory lock is normally dropped by BeginDatabaseMaintenance, but if
   5119  // something fails (in any method), the Finish method will do the cleanup.
   5120  RefPtr<UniversalDirectoryLock> mDirectoryLock;
   5121  nsTArray<nsCOMPtr<nsIRunnable>> mCompleteCallbacks;
   5122  nsTArray<DirectoryInfo> mDirectoryInfos;
   5123  nsTHashMap<nsStringHashKey, DatabaseMaintenance*> mDatabaseMaintenances;
   5124  nsresult mResultCode;
   5125  Atomic<bool> mAborted;
   5126  bool mOpenStorageForAllRepositoriesFailed;
   5127  State mState;
   5128 
   5129 public:
   5130  explicit Maintenance(QuotaClient* aQuotaClient)
   5131      : Runnable("dom::indexedDB::Maintenance"),
   5132        mQuotaClient(aQuotaClient),
   5133        mStartTime(PR_Now()),
   5134        mResultCode(NS_OK),
   5135        mAborted(false),
   5136        mOpenStorageForAllRepositoriesFailed(false),
   5137        mState(State::Initial) {
   5138    AssertIsOnBackgroundThread();
   5139    MOZ_ASSERT(aQuotaClient);
   5140    MOZ_ASSERT(QuotaClient::GetInstance() == aQuotaClient);
   5141    MOZ_ASSERT(mStartTime);
   5142  }
   5143 
   5144  nsIEventTarget* BackgroundThread() const {
   5145    MOZ_ASSERT(mQuotaClient);
   5146    return mQuotaClient->BackgroundThread();
   5147  }
   5148 
   5149  PRTime StartTime() const { return mStartTime; }
   5150 
   5151  bool IsAborted() const { return mAborted; }
   5152 
   5153  void RunImmediately() {
   5154    MOZ_ASSERT(mState == State::Initial);
   5155 
   5156    (void)this->Run();
   5157  }
   5158 
   5159  RefPtr<BoolPromise> OnResults() {
   5160    AssertIsOnBackgroundThread();
   5161 
   5162    return mPromiseHolder.Ensure(__func__);
   5163  }
   5164 
   5165  void Abort();
   5166 
   5167  void RegisterDatabaseMaintenance(DatabaseMaintenance* aDatabaseMaintenance);
   5168 
   5169  void UnregisterDatabaseMaintenance(DatabaseMaintenance* aDatabaseMaintenance);
   5170 
   5171  bool HasDatabaseMaintenances() const { return mDatabaseMaintenances.Count(); }
   5172 
   5173  RefPtr<DatabaseMaintenance> GetDatabaseMaintenance(
   5174      const nsAString& aDatabasePath) const {
   5175    AssertIsOnBackgroundThread();
   5176 
   5177    return mDatabaseMaintenances.Get(aDatabasePath);
   5178  }
   5179 
   5180  void WaitForCompletion(nsIRunnable* aCallback) {
   5181    AssertIsOnBackgroundThread();
   5182    MOZ_ASSERT(mDatabaseMaintenances.Count());
   5183 
   5184    mCompleteCallbacks.AppendElement(aCallback);
   5185  }
   5186 
   5187  void Stringify(nsACString& aResult) const;
   5188 
   5189 private:
   5190  ~Maintenance() override {
   5191    MOZ_ASSERT(mState == State::Complete);
   5192    MOZ_ASSERT(!mDatabaseMaintenances.Count());
   5193  }
   5194 
   5195  // Runs on the PBackground thread. Checks if IndexedDatabaseManager is
   5196  // running. Calls OpenDirectory() or dispatches to the main thread on which
   5197  // CreateIndexedDatabaseManager() is called.
   5198  nsresult Start();
   5199 
   5200  // Runs on the main thread. Once IndexedDatabaseManager is created it will
   5201  // dispatch to the PBackground thread on which OpenDirectory() is called.
   5202  nsresult CreateIndexedDatabaseManager();
   5203 
   5204  RefPtr<UniversalDirectoryLockPromise> OpenStorageDirectory(
   5205      const PersistenceScope& aPersistenceScope, bool aInitializeOrigins);
   5206 
   5207  // Runs on the PBackground thread. Once QuotaManager has given a lock it will
   5208  // call DirectoryOpen().
   5209  nsresult OpenDirectory();
   5210 
   5211  // Runs on the PBackground thread. Dispatches to the QuotaManager I/O thread.
   5212  nsresult DirectoryOpen();
   5213 
   5214  // Runs on the QuotaManager I/O thread. Once it finds databases it will
   5215  // dispatch to the PBackground thread on which BeginDatabaseMaintenance()
   5216  // is called.
   5217  nsresult DirectoryWork();
   5218 
   5219  // Runs on the PBackground thread. It dispatches a runnable for each database.
   5220  nsresult BeginDatabaseMaintenance();
   5221 
   5222  // Runs on the PBackground thread. Called when the maintenance is finished or
   5223  // if any of above methods fails.
   5224  void Finish();
   5225 
   5226  NS_DECL_NSIRUNNABLE
   5227 
   5228  void DirectoryLockAcquired(UniversalDirectoryLock* aLock);
   5229 
   5230  void DirectoryLockFailed();
   5231 };
   5232 
   5233 Maintenance::DirectoryInfo::DirectoryInfo(PersistenceType aPersistenceType,
   5234                                          OriginMetadata aOriginMetadata,
   5235                                          nsTArray<nsString>&& aDatabasePaths)
   5236    : mOriginMetadata(std::move(aOriginMetadata)),
   5237      mDatabasePaths(std::move(aDatabasePaths)),
   5238      mPersistenceType(aPersistenceType) {
   5239  MOZ_ASSERT(aPersistenceType != PERSISTENCE_TYPE_INVALID);
   5240  MOZ_ASSERT(!mOriginMetadata->mGroup.IsEmpty());
   5241  MOZ_ASSERT(!mOriginMetadata->mOrigin.IsEmpty());
   5242 #ifdef DEBUG
   5243  MOZ_ASSERT(!mDatabasePaths->IsEmpty());
   5244  for (const nsAString& databasePath : *mDatabasePaths) {
   5245    MOZ_ASSERT(!databasePath.IsEmpty());
   5246  }
   5247 #endif
   5248 
   5249  MOZ_COUNT_CTOR(Maintenance::DirectoryInfo);
   5250 }
   5251 
   5252 class DatabaseMaintenance final : public Runnable {
   5253  // The minimum amount of time that has passed since the last vacuum before we
   5254  // will attempt to analyze the database for fragmentation.
   5255  static const PRTime kMinVacuumAge =
   5256      PRTime(PR_USEC_PER_SEC) * 60 * 60 * 24 * 7;
   5257 
   5258  // If the percent of database pages that are not in contiguous order is higher
   5259  // than this percentage we will attempt a vacuum.
   5260  static const int32_t kPercentUnorderedThreshold = 30;
   5261 
   5262  // If the percent of file size growth since the last vacuum is higher than
   5263  // this percentage we will attempt a vacuum.
   5264  static const int32_t kPercentFileSizeGrowthThreshold = 10;
   5265 
   5266  // The number of freelist pages beyond which we will favor an incremental
   5267  // vacuum over a full vacuum.
   5268  static const int32_t kMaxFreelistThreshold = 5;
   5269 
   5270  // If the percent of unused file bytes in the database exceeds this percentage
   5271  // then we will attempt a full vacuum.
   5272  static const int32_t kPercentUnusedThreshold = 20;
   5273 
   5274  enum class MaintenanceAction { Nothing = 0, IncrementalVacuum, FullVacuum };
   5275 
   5276  RefPtr<Maintenance> mMaintenance;
   5277  // The directory lock is dropped in RunOnOwningThread which serves as a
   5278  // cleanup method and is always called.
   5279  RefPtr<ClientDirectoryLock> mDirectoryLock;
   5280  const OriginMetadata mOriginMetadata;
   5281  const nsString mDatabasePath;
   5282  int64_t mDirectoryLockId;
   5283  nsCOMPtr<nsIRunnable> mCompleteCallback;
   5284  const PersistenceType mPersistenceType;
   5285  const Maybe<CipherKey> mMaybeKey;
   5286  Atomic<bool> mAborted;
   5287  DataMutex<nsCOMPtr<mozIStorageConnection>> mSharedStorageConnection;
   5288 
   5289 public:
   5290  DatabaseMaintenance(Maintenance* aMaintenance,
   5291                      RefPtr<ClientDirectoryLock> aDirectoryLock,
   5292                      PersistenceType aPersistenceType,
   5293                      const OriginMetadata& aOriginMetadata,
   5294                      const nsAString& aDatabasePath,
   5295                      const Maybe<CipherKey>& aMaybeKey)
   5296      : Runnable("dom::indexedDB::DatabaseMaintenance"),
   5297        mMaintenance(aMaintenance),
   5298        mDirectoryLock(std::move(aDirectoryLock)),
   5299        mOriginMetadata(aOriginMetadata),
   5300        mDatabasePath(aDatabasePath),
   5301        mPersistenceType(aPersistenceType),
   5302        mMaybeKey{aMaybeKey},
   5303        mAborted(false),
   5304        mSharedStorageConnection("sharedStorageConnection") {
   5305    MOZ_ASSERT(mDirectoryLock);
   5306 
   5307    MOZ_ASSERT(mDirectoryLock->Id() >= 0);
   5308    mDirectoryLockId = mDirectoryLock->Id();
   5309  }
   5310 
   5311  const nsAString& DatabasePath() const { return mDatabasePath; }
   5312 
   5313  void WaitForCompletion(nsIRunnable* aCallback) {
   5314    AssertIsOnBackgroundThread();
   5315    MOZ_ASSERT(!mCompleteCallback);
   5316 
   5317    mCompleteCallback = aCallback;
   5318  }
   5319 
   5320  void Stringify(nsACString& aResult) const;
   5321 
   5322  nsresult Abort();
   5323 
   5324 private:
   5325  ~DatabaseMaintenance() override = default;
   5326 
   5327  // Runs on maintenance thread pool. Does maintenance on the database.
   5328  void PerformMaintenanceOnDatabase();
   5329 
   5330  // Runs on maintenance thread pool as part of PerformMaintenanceOnDatabase.
   5331  nsresult CheckIntegrity(mozIStorageConnection& aConnection, bool* aOk);
   5332 
   5333  // Runs on maintenance thread pool as part of PerformMaintenanceOnDatabase.
   5334  nsresult DetermineMaintenanceAction(mozIStorageConnection& aConnection,
   5335                                      nsIFile* aDatabaseFile,
   5336                                      MaintenanceAction* aMaintenanceAction);
   5337 
   5338  // Runs on maintenance thread pool as part of PerformMaintenanceOnDatabase.
   5339  void IncrementalVacuum(mozIStorageConnection& aConnection);
   5340 
   5341  // Runs on maintenance thread pool as part of PerformMaintenanceOnDatabase.
   5342  void FullVacuum(mozIStorageConnection& aConnection, nsIFile* aDatabaseFile);
   5343 
   5344  // Runs on the PBackground thread. It dispatches a complete callback and
   5345  // unregisters from Maintenance.
   5346  void RunOnOwningThread();
   5347 
   5348  // Runs on maintenance thread pool. Once it performs database maintenance
   5349  // it will dispatch to the PBackground thread on which RunOnOwningThread()
   5350  // is called.
   5351  void RunOnConnectionThread();
   5352 
   5353  // TODO: Could QuotaClient::IsShuttingDownOnNonBackgroundThread() call
   5354  // be part of mMaintenance::IsAborted() ?
   5355  inline bool IsAborted() const {
   5356    return mMaintenance->IsAborted() || mAborted ||
   5357           NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread());
   5358  }
   5359 
   5360  NS_DECL_NSIRUNNABLE
   5361 };
   5362 
   5363 #ifdef DEBUG
   5364 
   5365 class DEBUGThreadSlower final : public nsIThreadObserver {
   5366 public:
   5367  DEBUGThreadSlower() {
   5368    AssertIsOnBackgroundThread();
   5369    MOZ_ASSERT(kDEBUGThreadSleepMS);
   5370  }
   5371 
   5372  NS_DECL_ISUPPORTS
   5373 
   5374 private:
   5375  ~DEBUGThreadSlower() { AssertIsOnBackgroundThread(); }
   5376 
   5377  NS_DECL_NSITHREADOBSERVER
   5378 };
   5379 
   5380 #endif  // DEBUG
   5381 
   5382 /*******************************************************************************
   5383 * Helper classes
   5384 ******************************************************************************/
   5385 
   5386 // XXX Get rid of FileHelper and move the functions into DatabaseFileManager.
   5387 // Then, DatabaseFileManager::Get(Journal)Directory and
   5388 // DatabaseFileManager::GetFileForId might eventually be made private.
   5389 class MOZ_STACK_CLASS FileHelper final {
   5390  const SafeRefPtr<DatabaseFileManager> mFileManager;
   5391 
   5392  LazyInitializedOnce<const NotNull<nsCOMPtr<nsIFile>>> mFileDirectory;
   5393  LazyInitializedOnce<const NotNull<nsCOMPtr<nsIFile>>> mJournalDirectory;
   5394 
   5395  class ReadCallback;
   5396  LazyInitializedOnce<const NotNull<RefPtr<ReadCallback>>> mReadCallback;
   5397 
   5398 public:
   5399  explicit FileHelper(SafeRefPtr<DatabaseFileManager>&& aFileManager)
   5400      : mFileManager(std::move(aFileManager)) {
   5401    MOZ_ASSERT(mFileManager);
   5402  }
   5403 
   5404  nsresult Init();
   5405 
   5406  [[nodiscard]] nsCOMPtr<nsIFile> GetFile(const DatabaseFileInfo& aFileInfo);
   5407 
   5408  [[nodiscard]] nsCOMPtr<nsIFile> GetJournalFile(
   5409      const DatabaseFileInfo& aFileInfo);
   5410 
   5411  nsresult CreateFileFromStream(nsIFile& aFile, nsIFile& aJournalFile,
   5412                                nsIInputStream& aInputStream, bool aCompress,
   5413                                const Maybe<CipherKey>& aMaybeKey);
   5414 
   5415 private:
   5416  nsresult SyncCopy(nsIInputStream& aInputStream,
   5417                    nsIOutputStream& aOutputStream, char* aBuffer,
   5418                    uint32_t aBufferSize);
   5419 
   5420  nsresult SyncRead(nsIInputStream& aInputStream, char* aBuffer,
   5421                    uint32_t aBufferSize, uint32_t* aRead);
   5422 };
   5423 
   5424 /*******************************************************************************
   5425 * Helper Functions
   5426 ******************************************************************************/
   5427 
   5428 bool GetFilenameBase(const nsAString& aFilename, const nsAString& aSuffix,
   5429                     nsDependentSubstring& aFilenameBase) {
   5430  MOZ_ASSERT(!aFilename.IsEmpty());
   5431  MOZ_ASSERT(aFilenameBase.IsEmpty());
   5432 
   5433  if (!StringEndsWith(aFilename, aSuffix) ||
   5434      aFilename.Length() == aSuffix.Length()) {
   5435    return false;
   5436  }
   5437 
   5438  MOZ_ASSERT(aFilename.Length() > aSuffix.Length());
   5439 
   5440  aFilenameBase.Rebind(aFilename, 0, aFilename.Length() - aSuffix.Length());
   5441  return true;
   5442 }
   5443 
   5444 class EncryptedFileBlobImpl final : public FileBlobImpl {
   5445 public:
   5446  EncryptedFileBlobImpl(const nsCOMPtr<nsIFile>& aNativeFile,
   5447                        const DatabaseFileInfo::IdType aId,
   5448                        const CipherKey& aKey)
   5449      : FileBlobImpl{aNativeFile}, mKey{aKey} {
   5450    SetFileId(aId);
   5451  }
   5452 
   5453  uint64_t GetSize(ErrorResult& aRv) override {
   5454    nsCOMPtr<nsIInputStream> inputStream;
   5455    CreateInputStream(getter_AddRefs(inputStream), aRv);
   5456 
   5457    if (aRv.Failed()) {
   5458      return 0;
   5459    }
   5460 
   5461    MOZ_ASSERT(inputStream);
   5462 
   5463    QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_MEMBER(inputStream, Available), 0,
   5464                  [&aRv](const nsresult rv) { aRv = rv; });
   5465  }
   5466 
   5467  void CreateInputStream(nsIInputStream** aInputStream,
   5468                         ErrorResult& aRv) const override {
   5469    nsCOMPtr<nsIInputStream> baseInputStream;
   5470    FileBlobImpl::CreateInputStream(getter_AddRefs(baseInputStream), aRv);
   5471    if (NS_WARN_IF(aRv.Failed())) {
   5472      return;
   5473    }
   5474 
   5475    *aInputStream =
   5476        MakeAndAddRef<DecryptingInputStream<IndexedDBCipherStrategy>>(
   5477            WrapNotNull(std::move(baseInputStream)), kEncryptedStreamBlockSize,
   5478            mKey)
   5479            .take();
   5480  }
   5481 
   5482  void GetBlobImplType(nsAString& aBlobImplType) const override {
   5483    aBlobImplType = u"EncryptedFileBlobImpl"_ns;
   5484  }
   5485 
   5486  already_AddRefed<BlobImpl> CreateSlice(uint64_t aStart, uint64_t aLength,
   5487                                         const nsAString& aContentType,
   5488                                         ErrorResult& aRv) const override {
   5489    MOZ_CRASH("Not implemented because this should be unreachable.");
   5490  }
   5491 
   5492 private:
   5493  const CipherKey mKey;
   5494 };
   5495 
   5496 RefPtr<BlobImpl> CreateFileBlobImpl(const Database& aDatabase,
   5497                                    const nsCOMPtr<nsIFile>& aNativeFile,
   5498                                    const DatabaseFileInfo::IdType aId) {
   5499  if (aDatabase.IsInPrivateBrowsing()) {
   5500    nsCString keyId;
   5501    keyId.AppendInt(aId);
   5502 
   5503    const auto& key =
   5504        aDatabase.GetFileManager().MutableCipherKeyManagerRef().Get(keyId);
   5505 
   5506    MOZ_RELEASE_ASSERT(key.isSome());
   5507    return MakeRefPtr<EncryptedFileBlobImpl>(aNativeFile, aId, *key);
   5508  }
   5509 
   5510  auto impl = MakeRefPtr<FileBlobImpl>(aNativeFile);
   5511  impl->SetFileId(aId);
   5512 
   5513  return impl;
   5514 }
   5515 
   5516 Result<nsTArray<SerializedStructuredCloneFile>, nsresult>
   5517 SerializeStructuredCloneFiles(const SafeRefPtr<Database>& aDatabase,
   5518                              const nsTArray<StructuredCloneFileParent>& aFiles,
   5519                              bool aForPreprocess) {
   5520  AssertIsOnBackgroundThread();
   5521  MOZ_ASSERT(aDatabase);
   5522 
   5523  if (aFiles.IsEmpty()) {
   5524    return nsTArray<SerializedStructuredCloneFile>{};
   5525  }
   5526 
   5527  const nsCOMPtr<nsIFile> directory =
   5528      aDatabase->GetFileManager().GetCheckedDirectory();
   5529  QM_TRY(OkIf(directory), Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR),
   5530         IDB_REPORT_INTERNAL_ERR_LAMBDA);
   5531 
   5532  nsTArray<SerializedStructuredCloneFile> serializedStructuredCloneFiles;
   5533  QM_TRY(OkIf(serializedStructuredCloneFiles.SetCapacity(aFiles.Length(),
   5534                                                         fallible)),
   5535         Err(NS_ERROR_OUT_OF_MEMORY));
   5536 
   5537  QM_TRY(TransformIfAbortOnErr(
   5538      aFiles, MakeBackInserter(serializedStructuredCloneFiles),
   5539      [aForPreprocess](const auto& file) {
   5540        return !aForPreprocess ||
   5541               file.Type() == StructuredCloneFileBase::eStructuredClone;
   5542      },
   5543      [&directory, &aDatabase, aForPreprocess](
   5544          const auto& file) -> Result<SerializedStructuredCloneFile, nsresult> {
   5545        const int64_t fileId = file.FileInfo().Id();
   5546        MOZ_ASSERT(fileId > 0);
   5547 
   5548        const nsCOMPtr<nsIFile> nativeFile =
   5549            mozilla::dom::indexedDB::DatabaseFileManager::GetCheckedFileForId(
   5550                directory, fileId);
   5551        QM_TRY(OkIf(nativeFile), Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR),
   5552               IDB_REPORT_INTERNAL_ERR_LAMBDA);
   5553 
   5554        switch (file.Type()) {
   5555          case StructuredCloneFileBase::eStructuredClone:
   5556            if (!aForPreprocess) {
   5557              return SerializedStructuredCloneFile{
   5558                  null_t(), StructuredCloneFileBase::eStructuredClone};
   5559            }
   5560 
   5561            [[fallthrough]];
   5562 
   5563          case StructuredCloneFileBase::eBlob: {
   5564            const auto impl = CreateFileBlobImpl(*aDatabase, nativeFile,
   5565                                                 file.FileInfo().Id());
   5566 
   5567            IPCBlob ipcBlob;
   5568 
   5569            // This can only fail if the child has crashed.
   5570            QM_TRY(MOZ_TO_RESULT(IPCBlobUtils::Serialize(impl, ipcBlob)),
   5571                   Err(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR),
   5572                   IDB_REPORT_INTERNAL_ERR_LAMBDA);
   5573 
   5574            aDatabase->MapBlob(ipcBlob, file.FileInfoPtr());
   5575 
   5576            return SerializedStructuredCloneFile{ipcBlob, file.Type()};
   5577          }
   5578 
   5579          case StructuredCloneFileBase::eMutableFile:
   5580          case StructuredCloneFileBase::eWasmBytecode:
   5581          case StructuredCloneFileBase::eWasmCompiled: {
   5582            // Set file() to null, support for storing WebAssembly.Modules has
   5583            // been removed in bug 1469395. Support for de-serialization of
   5584            // WebAssembly.Modules modules has been removed in bug 1561876.
   5585            // Support for MutableFile has been removed in bug 1500343. Full
   5586            // removal is tracked in bug 1487479.
   5587 
   5588            return SerializedStructuredCloneFile{null_t(), file.Type()};
   5589          }
   5590 
   5591          default:
   5592            MOZ_CRASH("Should never get here!");
   5593        }
   5594      }));
   5595 
   5596  return std::move(serializedStructuredCloneFiles);
   5597 }
   5598 
   5599 bool IsFileNotFoundError(const nsresult aRv) {
   5600  return aRv == NS_ERROR_FILE_NOT_FOUND;
   5601 }
   5602 
   5603 enum struct Idempotency { Yes, No };
   5604 
   5605 // Delete a file, decreasing the quota usage as appropriate. If the file no
   5606 // longer exists but aIdempotency is Idempotency::Yes, success is returned,
   5607 // although quota usage can't be decreased. (With the assumption being that the
   5608 // file was already deleted prior to this logic running, and the non-existent
   5609 // file was no longer tracked by quota because it didn't exist at
   5610 // initialization time or a previous deletion call updated the usage.)
   5611 nsresult DeleteFile(nsIFile& aFile, QuotaManager* const aQuotaManager,
   5612                    const PersistenceType aPersistenceType,
   5613                    const OriginMetadata& aOriginMetadata,
   5614                    const Idempotency aIdempotency) {
   5615  MOZ_ASSERT(!NS_IsMainThread());
   5616  MOZ_ASSERT(!IsOnBackgroundThread());
   5617 
   5618  // Callers which pass Idempotency::Yes call this function without checking if
   5619  // the file already exists (idempotent usage). QM_OR_ELSE_WARN_IF is not used
   5620  // here since we just want to log NS_ERROR_FILE_NOT_FOUND results and not spam
   5621  // the reports.
   5622  // Theoretically, there should be no QM_OR_ELSE_(WARN|LOG_VERBOSE)_IF when a
   5623  // caller passes Idempotency::No, but it's simpler when the predicate just
   5624  // always returns false in that case.
   5625 
   5626  const auto isIgnorableError = [&aIdempotency]() -> bool (*)(nsresult) {
   5627    if (aIdempotency == Idempotency::Yes) {
   5628      return IsFileNotFoundError;
   5629    }
   5630 
   5631    return [](const nsresult rv) { return false; };
   5632  }();
   5633 
   5634  QM_TRY_INSPECT(
   5635      const auto& fileSize,
   5636      ([aQuotaManager, &aFile,
   5637        isIgnorableError]() -> Result<Maybe<int64_t>, nsresult> {
   5638        if (aQuotaManager) {
   5639          QM_TRY_INSPECT(
   5640              const Maybe<int64_t>& fileSize,
   5641              QM_OR_ELSE_LOG_VERBOSE_IF(
   5642                  // Expression.
   5643                  MOZ_TO_RESULT_INVOKE_MEMBER(aFile, GetFileSize)
   5644                      .map([](const int64_t val) { return Some(val); }),
   5645                  // Predicate.
   5646                  isIgnorableError,
   5647                  // Fallback.
   5648                  ErrToDefaultOk<Maybe<int64_t>>));
   5649 
   5650          // XXX Can we really assert that the file size is not 0 if
   5651          // it existed? This might be violated by external
   5652          // influences.
   5653          MOZ_ASSERT(!fileSize || fileSize.value() >= 0);
   5654 
   5655          return fileSize;
   5656        }
   5657 
   5658        return Some(int64_t(0));
   5659      }()));
   5660 
   5661  if (!fileSize) {
   5662    return NS_OK;
   5663  }
   5664 
   5665  QM_TRY_INSPECT(const auto& didExist,
   5666                 QM_OR_ELSE_LOG_VERBOSE_IF(
   5667                     // Expression.
   5668                     MOZ_TO_RESULT(aFile.Remove(false)).map(Some<Ok>),
   5669                     // Predicate.
   5670                     isIgnorableError,
   5671                     // Fallback.
   5672                     ErrToDefaultOk<Maybe<Ok>>));
   5673 
   5674  if (!didExist) {
   5675    // XXX If we get here, this means that the file still existed when we
   5676    // queried its size, but no longer when we tried to remove it. Not sure if
   5677    // this should really be silently accepted in idempotent mode.
   5678    return NS_OK;
   5679  }
   5680 
   5681  if (fileSize.value() > 0) {
   5682    MOZ_ASSERT(aQuotaManager);
   5683 
   5684    aQuotaManager->DecreaseUsageForClient(
   5685        ClientMetadata{aOriginMetadata, Client::IDB}, fileSize.value());
   5686  }
   5687 
   5688  return NS_OK;
   5689 }
   5690 
   5691 nsresult DeleteFile(nsIFile& aDirectory, const nsAString& aFilename,
   5692                    QuotaManager* const aQuotaManager,
   5693                    const PersistenceType aPersistenceType,
   5694                    const OriginMetadata& aOriginMetadata,
   5695                    const Idempotency aIdempotent) {
   5696  AssertIsOnIOThread();
   5697  MOZ_ASSERT(!aFilename.IsEmpty());
   5698 
   5699  QM_TRY_INSPECT(const auto& file, CloneFileAndAppend(aDirectory, aFilename));
   5700 
   5701  return DeleteFile(*file, aQuotaManager, aPersistenceType, aOriginMetadata,
   5702                    aIdempotent);
   5703 }
   5704 
   5705 // Delete files in a directory that you think exists. If the directory doesn't
   5706 // exist, an error will not be returned, but warning telemetry will be
   5707 // generated! So only call this on directories that you know exist (idempotent
   5708 // usage, but it's not recommended).
   5709 nsresult DeleteFilesNoQuota(nsIFile& aFile) {
   5710  AssertIsOnIOThread();
   5711 
   5712  QM_TRY_INSPECT(const auto& didExist,
   5713                 QM_OR_ELSE_WARN_IF(
   5714                     // Expression.
   5715                     MOZ_TO_RESULT(aFile.Remove(true)).map(Some<Ok>),
   5716                     // Predicate.
   5717                     IsFileNotFoundError,
   5718                     // Fallback.
   5719                     ErrToDefaultOk<Maybe<Ok>>));
   5720 
   5721  (void)didExist;
   5722 
   5723  return NS_OK;
   5724 }
   5725 
   5726 nsresult DeleteFilesNoQuota(nsIFile* aDirectory, const nsAString& aFilename) {
   5727  AssertIsOnIOThread();
   5728  MOZ_ASSERT(aDirectory);
   5729  MOZ_ASSERT(!aFilename.IsEmpty());
   5730 
   5731  // The current using function hasn't initialized the origin, so in here we
   5732  // don't update the size of origin. Adding this assertion for preventing from
   5733  // misusing.
   5734  DebugOnly<QuotaManager*> quotaManager = QuotaManager::Get();
   5735  MOZ_ASSERT(!quotaManager->IsTemporaryStorageInitializedInternal());
   5736 
   5737  QM_TRY_INSPECT(const auto& file, CloneFileAndAppend(*aDirectory, aFilename));
   5738 
   5739  QM_TRY(MOZ_TO_RESULT(DeleteFilesNoQuota(*file)));
   5740 
   5741  return NS_OK;
   5742 }
   5743 
   5744 // CreateMarkerFile and RemoveMarkerFile are a pair of functions to indicate
   5745 // whether having removed all the files successfully. The marker file should
   5746 // be checked before executing the next operation or initialization.
   5747 Result<nsCOMPtr<nsIFile>, nsresult> CreateMarkerFile(
   5748    nsIFile& aBaseDirectory, const nsAString& aDatabaseNameBase) {
   5749  AssertIsOnIOThread();
   5750  MOZ_ASSERT(!aDatabaseNameBase.IsEmpty());
   5751 
   5752  QM_TRY_INSPECT(
   5753      const auto& markerFile,
   5754      CloneFileAndAppend(aBaseDirectory,
   5755                         kIdbDeletionMarkerFilePrefix + aDatabaseNameBase));
   5756 
   5757  // Callers call this function without checking if the file already exists
   5758  // (idempotent usage). QM_OR_ELSE_WARN_IF is not used here since we just want
   5759  // to log NS_ERROR_FILE_ALREADY_EXISTS result and not spam the reports.
   5760  //
   5761  // TODO: In theory if this file exists, then RemoveDatabaseFilesAndDirectory
   5762  // should have cleaned it up, but obviously we can crash and not clean it up,
   5763  // which is the whole point of the marker file. In that case, we'll realize
   5764  // the marker file exists in OpenDatabaseOp::DoDatabaseWork or
   5765  // GetUsageForOriginInternal and resume the removal by calling
   5766  // RemoveDatabaseFilesAndDirectory again, but we will also try to create the
   5767  // marker file again, so if we see this marker file, it is part
   5768  // of our standard operating procedure to redundantly try and create the
   5769  // marker here. We currently treat this as idempotent usage, but we could
   5770  // add an additional argument to RemoveDatabaseFilesAndDirectory which would
   5771  // indicate that we are resuming an unfinished removal, so the marker already
   5772  // exists and doesn't have to be created, and change
   5773  // QM_OR_ELSE_LOG_VERBOSE_IF to QM_OR_ELSE_WARN_IF in the end.
   5774  QM_TRY(QM_OR_ELSE_LOG_VERBOSE_IF(
   5775      // Expression.
   5776      MOZ_TO_RESULT(markerFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644)),
   5777      // Predicate.
   5778      IsSpecificError<NS_ERROR_FILE_ALREADY_EXISTS>,
   5779      // Fallback.
   5780      ErrToDefaultOk<>));
   5781 
   5782  return markerFile;
   5783 }
   5784 
   5785 nsresult RemoveMarkerFile(nsIFile* aMarkerFile) {
   5786  AssertIsOnIOThread();
   5787  MOZ_ASSERT(aMarkerFile);
   5788 
   5789  DebugOnly<bool> exists;
   5790  MOZ_ASSERT(NS_SUCCEEDED(aMarkerFile->Exists(&exists)));
   5791  MOZ_ASSERT(exists);
   5792 
   5793  QM_TRY(MOZ_TO_RESULT(aMarkerFile->Remove(false)));
   5794 
   5795  return NS_OK;
   5796 }
   5797 
   5798 Result<Ok, nsresult> DeleteFileManagerDirectory(
   5799    nsIFile& aFileManagerDirectory, QuotaManager* aQuotaManager,
   5800    const PersistenceType aPersistenceType,
   5801    const OriginMetadata& aOriginMetadata) {
   5802  GECKO_TRACE_SCOPE("dom::indexedDB", "DeleteFileManagerDirectory");
   5803 
   5804  // XXX In theory, deleting can continue for other files in case of a failure,
   5805  // leaving only those files behind that cause the problem actually. However,
   5806  // the current architecture doesn't allow having more databases (for the same
   5807  // name) on disk, so trying to delete as much as possible won't help much
   5808  // because we need to delete entire .files directory in the end anyway.
   5809  QM_TRY(DatabaseFileManager::TraverseFiles(
   5810      aFileManagerDirectory,
   5811      // KnownDirEntryOp
   5812      [&aQuotaManager, aPersistenceType, &aOriginMetadata](
   5813          nsIFile& file, const bool isDirectory) -> Result<Ok, nsresult> {
   5814        if (isDirectory) {
   5815          // The journal directory doesn't count towards quota.
   5816          QM_TRY_RETURN(MOZ_TO_RESULT(DeleteFilesNoQuota(file)));
   5817        }
   5818 
   5819        // Stored files do count towards quota.
   5820        QM_TRY_RETURN(
   5821            MOZ_TO_RESULT(DeleteFile(file, aQuotaManager, aPersistenceType,
   5822                                     aOriginMetadata, Idempotency::Yes)));
   5823      },
   5824      // UnknownDirEntryOp
   5825      [aPersistenceType, &aOriginMetadata](
   5826          nsIFile& file, const bool isDirectory) -> Result<Ok, nsresult> {
   5827        // Unknown files and directories don't count towards quota.
   5828 
   5829        if (isDirectory) {
   5830          QM_TRY_RETURN(MOZ_TO_RESULT(DeleteFilesNoQuota(file)));
   5831        }
   5832 
   5833        QM_TRY_RETURN(MOZ_TO_RESULT(
   5834            DeleteFile(file, /* doesn't count */ nullptr, aPersistenceType,
   5835                       aOriginMetadata, Idempotency::Yes)));
   5836      }));
   5837 
   5838  QM_TRY_RETURN(MOZ_TO_RESULT(aFileManagerDirectory.Remove(false)));
   5839 }
   5840 
   5841 // Idempotently delete all the parts of an IndexedDB database including its
   5842 // SQLite database file, its WAL journal, it's shared-memory file, and its
   5843 // Blob/Files sub-directory. A marker file is created prior to performing the
   5844 // deletion so that in the event we crash or fail to successfully delete the
   5845 // database and its files, we will re-attempt the deletion the next time the
   5846 // origin is initialized using this method. Because this means the method may be
   5847 // called on a partially deleted database, this method uses DeleteFile which
   5848 // succeeds when the file we ask it to delete does not actually exist. The
   5849 // marker file is removed once deletion has successfully completed.
   5850 nsresult RemoveDatabaseFilesAndDirectory(nsIFile& aBaseDirectory,
   5851                                         const nsAString& aDatabaseFilenameBase,
   5852                                         QuotaManager* aQuotaManager,
   5853                                         const PersistenceType aPersistenceType,
   5854                                         const OriginMetadata& aOriginMetadata,
   5855                                         const nsAString& aDatabaseName) {
   5856  GECKO_TRACE_SCOPE("dom::indexedDB", "RemoveDatabaseFilesAndDirectory");
   5857 
   5858  AssertIsOnIOThread();
   5859  MOZ_ASSERT(!aDatabaseFilenameBase.IsEmpty());
   5860 
   5861  AUTO_PROFILER_LABEL("RemoveDatabaseFilesAndDirectory", DOM);
   5862 
   5863  QM_TRY_UNWRAP(auto markerFile,
   5864                CreateMarkerFile(aBaseDirectory, aDatabaseFilenameBase));
   5865 
   5866  // The database file counts towards quota.
   5867  QM_TRY(MOZ_TO_RESULT(DeleteFile(
   5868      aBaseDirectory, aDatabaseFilenameBase + kSQLiteSuffix, aQuotaManager,
   5869      aPersistenceType, aOriginMetadata, Idempotency::Yes)));
   5870 
   5871  // .sqlite-journal files don't count towards quota.
   5872  QM_TRY(MOZ_TO_RESULT(DeleteFile(aBaseDirectory,
   5873                                  aDatabaseFilenameBase + kSQLiteJournalSuffix,
   5874                                  /* doesn't count */ nullptr, aPersistenceType,
   5875                                  aOriginMetadata, Idempotency::Yes)));
   5876 
   5877  // .sqlite-shm files don't count towards quota.
   5878  QM_TRY(MOZ_TO_RESULT(DeleteFile(aBaseDirectory,
   5879                                  aDatabaseFilenameBase + kSQLiteSHMSuffix,
   5880                                  /* doesn't count */ nullptr, aPersistenceType,
   5881                                  aOriginMetadata, Idempotency::Yes)));
   5882 
   5883  // .sqlite-wal files do count towards quota.
   5884  QM_TRY(MOZ_TO_RESULT(DeleteFile(
   5885      aBaseDirectory, aDatabaseFilenameBase + kSQLiteWALSuffix, aQuotaManager,
   5886      aPersistenceType, aOriginMetadata, Idempotency::Yes)));
   5887 
   5888  // The files directory counts towards quota.
   5889  QM_TRY_INSPECT(
   5890      const auto& fmDirectory,
   5891      CloneFileAndAppend(aBaseDirectory, aDatabaseFilenameBase +
   5892                                             kFileManagerDirectoryNameSuffix));
   5893 
   5894  QM_TRY_INSPECT(const bool& exists,
   5895                 MOZ_TO_RESULT_INVOKE_MEMBER(fmDirectory, Exists));
   5896 
   5897  if (exists) {
   5898    QM_TRY_INSPECT(const bool& isDirectory,
   5899                   MOZ_TO_RESULT_INVOKE_MEMBER(fmDirectory, IsDirectory));
   5900 
   5901    QM_TRY(OkIf(isDirectory), NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
   5902 
   5903    QM_TRY(DeleteFileManagerDirectory(*fmDirectory, aQuotaManager,
   5904                                      aPersistenceType, aOriginMetadata));
   5905  }
   5906 
   5907  IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get();
   5908  MOZ_ASSERT_IF(aQuotaManager, mgr);
   5909 
   5910  if (mgr) {
   5911    mgr->InvalidateFileManager(aPersistenceType, aOriginMetadata.mOrigin,
   5912                               aDatabaseName);
   5913  }
   5914 
   5915  QM_TRY(MOZ_TO_RESULT(RemoveMarkerFile(markerFile)));
   5916 
   5917  return NS_OK;
   5918 }
   5919 
   5920 /*******************************************************************************
   5921 * Globals
   5922 ******************************************************************************/
   5923 
   5924 // Counts the number of "live" Factory, FactoryOp and Database instances.
   5925 uint64_t gBusyCount = 0;
   5926 
   5927 // We don't use LinkedList<CheckedUnsafePtr<FactoryOp>> because
   5928 // CheckedUnsafePtr is not suitable for use within LinkedList. While it's
   5929 // theoretically possible to adapt LinkedList to support it, doing so would
   5930 // introduce unnecessary overhead. Instead, we use a simpler and more
   5931 // efficient approach. Each FactoryOp instance asserts !isInList() in its
   5932 // destructor to catch dangling pointer issues.
   5933 using FactoryOpArray = LinkedList<FactoryOp>;
   5934 
   5935 StaticAutoPtr<FactoryOpArray> gFactoryOps;
   5936 
   5937 // Maps a database id to information about live database actors.
   5938 using DatabaseActorHashtable =
   5939    nsClassHashtable<nsCStringHashKey, DatabaseActorInfo>;
   5940 
   5941 StaticAutoPtr<DatabaseActorHashtable> gLiveDatabaseHashtable;
   5942 
   5943 StaticRefPtr<ConnectionPool> gConnectionPool;
   5944 
   5945 using DatabaseLoggingInfoHashtable =
   5946    nsTHashMap<nsIDHashKey, DatabaseLoggingInfo*>;
   5947 
   5948 StaticAutoPtr<DatabaseLoggingInfoHashtable> gLoggingInfoHashtable;
   5949 
   5950 using TelemetryIdHashtable = nsTHashMap<nsUint32HashKey, uint32_t>;
   5951 
   5952 StaticAutoPtr<TelemetryIdHashtable> gTelemetryIdHashtable;
   5953 
   5954 // Protects all reads and writes to gTelemetryIdHashtable.
   5955 StaticAutoPtr<Mutex> gTelemetryIdMutex;
   5956 
   5957 // For private browsing, maps the raw database names provided by content to a
   5958 // replacement UUID in order to avoid exposing the name of the database on
   5959 // disk or a directly derived value, such as the non-private-browsing
   5960 // representation. This mapping will be the same for all databases with the
   5961 // same name across all storage keys/origins for the lifetime of the IDB
   5962 // QuotaClient. In tests, the QuotaClient may be created and destroyed multiple
   5963 // times, but for normal browser use the QuotaClient will last until the
   5964 // browser shuts down. Bug 1831835 will improve this implementation to avoid
   5965 // using the same mapping across storage keys and to deal with the resulting
   5966 // lifecycle issues of the additional memory use.
   5967 using StorageDatabaseNameHashtable = nsTHashMap<nsString, nsString>;
   5968 
   5969 StaticAutoPtr<StorageDatabaseNameHashtable> gStorageDatabaseNameHashtable;
   5970 
   5971 // Protects all reads and writes to gStorageDatabaseNameHashtable.
   5972 StaticAutoPtr<Mutex> gStorageDatabaseNameMutex;
   5973 
   5974 #ifdef DEBUG
   5975 
   5976 StaticRefPtr<DEBUGThreadSlower> gDEBUGThreadSlower;
   5977 
   5978 #endif  // DEBUG
   5979 
   5980 void IncreaseBusyCount() {
   5981  AssertIsOnBackgroundThread();
   5982 
   5983  // If this is the first instance then we need to do some initialization.
   5984  if (!gBusyCount) {
   5985    MOZ_ASSERT(!gFactoryOps);
   5986    gFactoryOps = new FactoryOpArray();
   5987 
   5988    MOZ_ASSERT(!gLiveDatabaseHashtable);
   5989    gLiveDatabaseHashtable = new DatabaseActorHashtable();
   5990 
   5991    MOZ_ASSERT(!gLoggingInfoHashtable);
   5992    gLoggingInfoHashtable = new DatabaseLoggingInfoHashtable();
   5993 
   5994 #ifdef DEBUG
   5995    if (kDEBUGThreadPriority != nsISupportsPriority::PRIORITY_NORMAL) {
   5996      NS_WARNING(
   5997          "PBackground thread debugging enabled, priority has been "
   5998          "modified!");
   5999      nsCOMPtr<nsISupportsPriority> thread =
   6000          do_QueryInterface(NS_GetCurrentThread());
   6001      MOZ_ASSERT(thread);
   6002 
   6003      MOZ_ALWAYS_SUCCEEDS(thread->SetPriority(kDEBUGThreadPriority));
   6004    }
   6005 
   6006    if (kDEBUGThreadSleepMS) {
   6007      NS_WARNING(
   6008          "PBackground thread debugging enabled, sleeping after every "
   6009          "event!");
   6010      nsCOMPtr<nsIThreadInternal> thread =
   6011          do_QueryInterface(NS_GetCurrentThread());
   6012      MOZ_ASSERT(thread);
   6013 
   6014      gDEBUGThreadSlower = new DEBUGThreadSlower();
   6015 
   6016      MOZ_ALWAYS_SUCCEEDS(thread->AddObserver(gDEBUGThreadSlower));
   6017    }
   6018 #endif  // DEBUG
   6019  }
   6020 
   6021  gBusyCount++;
   6022 }
   6023 
   6024 void DecreaseBusyCount() {
   6025  AssertIsOnBackgroundThread();
   6026  MOZ_ASSERT(gBusyCount);
   6027 
   6028  // Clean up if there are no more instances.
   6029  if (--gBusyCount == 0) {
   6030    MOZ_ASSERT(gLoggingInfoHashtable);
   6031    gLoggingInfoHashtable = nullptr;
   6032 
   6033    MOZ_ASSERT(gLiveDatabaseHashtable);
   6034    MOZ_ASSERT(!gLiveDatabaseHashtable->Count());
   6035    gLiveDatabaseHashtable = nullptr;
   6036 
   6037    MOZ_ASSERT(gFactoryOps);
   6038    MOZ_ASSERT(gFactoryOps->isEmpty());
   6039    gFactoryOps = nullptr;
   6040 
   6041 #ifdef DEBUG
   6042    if (kDEBUGThreadPriority != nsISupportsPriority::PRIORITY_NORMAL) {
   6043      nsCOMPtr<nsISupportsPriority> thread =
   6044          do_QueryInterface(NS_GetCurrentThread());
   6045      MOZ_ASSERT(thread);
   6046 
   6047      MOZ_ALWAYS_SUCCEEDS(
   6048          thread->SetPriority(nsISupportsPriority::PRIORITY_NORMAL));
   6049    }
   6050 
   6051    if (kDEBUGThreadSleepMS) {
   6052      MOZ_ASSERT(gDEBUGThreadSlower);
   6053 
   6054      nsCOMPtr<nsIThreadInternal> thread =
   6055          do_QueryInterface(NS_GetCurrentThread());
   6056      MOZ_ASSERT(thread);
   6057 
   6058      MOZ_ALWAYS_SUCCEEDS(thread->RemoveObserver(gDEBUGThreadSlower));
   6059 
   6060      gDEBUGThreadSlower = nullptr;
   6061    }
   6062 #endif  // DEBUG
   6063  }
   6064 }
   6065 
   6066 template <typename Condition>
   6067 void InvalidateLiveDatabasesMatching(const Condition& aCondition) {
   6068  AssertIsOnBackgroundThread();
   6069 
   6070  if (!gLiveDatabaseHashtable) {
   6071    return;
   6072  }
   6073 
   6074  // Invalidating a Database will cause it to be removed from the
   6075  // gLiveDatabaseHashtable entries' mLiveDatabases, and, if it was the last
   6076  // element in mLiveDatabases, to remove the whole hashtable entry. Therefore,
   6077  // we need to make a temporary list of the databases to invalidate to avoid
   6078  // iterator invalidation.
   6079 
   6080  nsTArray<SafeRefPtr<Database>> databases;
   6081 
   6082  for (const auto& liveDatabasesEntry : gLiveDatabaseHashtable->Values()) {
   6083    for (Database* const database : liveDatabasesEntry->mLiveDatabases) {
   6084      if (aCondition(*database)) {
   6085        databases.AppendElement(
   6086            SafeRefPtr{database, AcquireStrongRefFromRawPtr{}});
   6087      }
   6088    }
   6089  }
   6090 
   6091  for (const auto& database : databases) {
   6092    database->Invalidate();
   6093  }
   6094 }
   6095 
   6096 uint32_t TelemetryIdForFile(nsIFile* aFile) {
   6097  // May be called on any thread!
   6098 
   6099  MOZ_ASSERT(aFile);
   6100  MOZ_ASSERT(gTelemetryIdMutex);
   6101 
   6102  // The storage directory is structured like this:
   6103  //
   6104  //   <profile>/storage/<persistence>/<origin>/idb/<filename>.sqlite
   6105  //
   6106  // For the purposes of this function we're only concerned with the
   6107  // <persistence>, <origin>, and <filename> pieces.
   6108 
   6109  nsString filename;
   6110  MOZ_ALWAYS_SUCCEEDS(aFile->GetLeafName(filename));
   6111 
   6112  // Make sure we were given a database file.
   6113  MOZ_ASSERT(StringEndsWith(filename, kSQLiteSuffix));
   6114 
   6115  filename.Truncate(filename.Length() - kSQLiteSuffix.Length());
   6116 
   6117  // Get the "idb" directory.
   6118  nsCOMPtr<nsIFile> idbDirectory;
   6119  MOZ_ALWAYS_SUCCEEDS(aFile->GetParent(getter_AddRefs(idbDirectory)));
   6120 
   6121  DebugOnly<nsString> idbLeafName;
   6122  MOZ_ASSERT(NS_SUCCEEDED(idbDirectory->GetLeafName(idbLeafName)));
   6123  MOZ_ASSERT(static_cast<nsString&>(idbLeafName).EqualsLiteral("idb"));
   6124 
   6125  // Get the <origin> directory.
   6126  nsCOMPtr<nsIFile> originDirectory;
   6127  MOZ_ALWAYS_SUCCEEDS(idbDirectory->GetParent(getter_AddRefs(originDirectory)));
   6128 
   6129  nsString origin;
   6130  MOZ_ALWAYS_SUCCEEDS(originDirectory->GetLeafName(origin));
   6131 
   6132  // Any databases in these directories are owned by the application and should
   6133  // not have their filenames masked. Hopefully they also appear in the
   6134  // Telemetry.cpp whitelist.
   6135  if (origin.EqualsLiteral("chrome") ||
   6136      origin.EqualsLiteral("moz-safe-about+home")) {
   6137    return 0;
   6138  }
   6139 
   6140  // Get the <persistence> directory.
   6141  nsCOMPtr<nsIFile> persistenceDirectory;
   6142  MOZ_ALWAYS_SUCCEEDS(
   6143      originDirectory->GetParent(getter_AddRefs(persistenceDirectory)));
   6144 
   6145  nsString persistence;
   6146  MOZ_ALWAYS_SUCCEEDS(persistenceDirectory->GetLeafName(persistence));
   6147 
   6148  constexpr auto separator = u"*"_ns;
   6149 
   6150  uint32_t hashValue =
   6151      HashString(persistence + separator + origin + separator + filename);
   6152 
   6153  MutexAutoLock lock(*gTelemetryIdMutex);
   6154 
   6155  if (!gTelemetryIdHashtable) {
   6156    gTelemetryIdHashtable = new TelemetryIdHashtable();
   6157  }
   6158 
   6159  return gTelemetryIdHashtable->LookupOrInsertWith(hashValue, [] {
   6160    static uint32_t sNextId = 1;
   6161 
   6162    // We're locked, no need for atomics.
   6163    return sNextId++;
   6164  });
   6165 }
   6166 
   6167 nsAutoString GetDatabaseFilenameBase(const nsAString& aDatabaseName,
   6168                                     bool aIsPrivate) {
   6169  nsAutoString databaseFilenameBase;
   6170 
   6171  if (aIsPrivate) {
   6172    MOZ_DIAGNOSTIC_ASSERT(gStorageDatabaseNameMutex);
   6173 
   6174    MutexAutoLock lock(*gStorageDatabaseNameMutex);
   6175 
   6176    if (!gStorageDatabaseNameHashtable) {
   6177      gStorageDatabaseNameHashtable = new StorageDatabaseNameHashtable();
   6178    }
   6179 
   6180    databaseFilenameBase.Append(
   6181        gStorageDatabaseNameHashtable->LookupOrInsertWith(aDatabaseName, []() {
   6182          return NSID_TrimBracketsUTF16(nsID::GenerateUUID());
   6183        }));
   6184 
   6185    return databaseFilenameBase;
   6186  }
   6187 
   6188  // WARNING: do not change this hash function. See the comment in HashName()
   6189  // for details.
   6190  databaseFilenameBase.AppendInt(HashName(aDatabaseName));
   6191 
   6192  nsAutoCString escapedName;
   6193  if (!NS_Escape(NS_ConvertUTF16toUTF8(aDatabaseName), escapedName,
   6194                 url_XPAlphas)) {
   6195    MOZ_CRASH("Can't escape database name!");
   6196  }
   6197 
   6198  const char* forwardIter = escapedName.BeginReading();
   6199  const char* backwardIter = escapedName.EndReading() - 1;
   6200 
   6201  nsAutoCString substring;
   6202  while (forwardIter <= backwardIter && substring.Length() < 21) {
   6203    if (substring.Length() % 2) {
   6204      substring.Append(*backwardIter--);
   6205    } else {
   6206      substring.Append(*forwardIter++);
   6207    }
   6208  }
   6209 
   6210  databaseFilenameBase.AppendASCII(substring.get(), substring.Length());
   6211 
   6212  return databaseFilenameBase;
   6213 }
   6214 
   6215 const CommonIndexOpenCursorParams& GetCommonIndexOpenCursorParams(
   6216    const OpenCursorParams& aParams) {
   6217  switch (aParams.type()) {
   6218    case OpenCursorParams::TIndexOpenCursorParams:
   6219      return aParams.get_IndexOpenCursorParams().commonIndexParams();
   6220    case OpenCursorParams::TIndexOpenKeyCursorParams:
   6221      return aParams.get_IndexOpenKeyCursorParams().commonIndexParams();
   6222    default:
   6223      MOZ_CRASH("Should never get here!");
   6224  }
   6225 }
   6226 
   6227 const CommonOpenCursorParams& GetCommonOpenCursorParams(
   6228    const OpenCursorParams& aParams) {
   6229  switch (aParams.type()) {
   6230    case OpenCursorParams::TObjectStoreOpenCursorParams:
   6231      return aParams.get_ObjectStoreOpenCursorParams().commonParams();
   6232    case OpenCursorParams::TObjectStoreOpenKeyCursorParams:
   6233      return aParams.get_ObjectStoreOpenKeyCursorParams().commonParams();
   6234    case OpenCursorParams::TIndexOpenCursorParams:
   6235    case OpenCursorParams::TIndexOpenKeyCursorParams:
   6236      return GetCommonIndexOpenCursorParams(aParams).commonParams();
   6237    default:
   6238      MOZ_CRASH("Should never get here!");
   6239  }
   6240 }
   6241 
   6242 // TODO: Using nsCString as a return type here seems to lead to a dependency on
   6243 // some temporaries, which I did not expect. Is it a good idea that the default
   6244 // operator+ behaviour constructs such strings? It is certainly useful as an
   6245 // optimization, but this should be better done via an appropriately named
   6246 // function rather than an operator.
   6247 nsAutoCString MakeColumnPairSelectionList(
   6248    const nsLiteralCString& aPlainColumnName,
   6249    const nsLiteralCString& aLocaleAwareColumnName,
   6250    const nsLiteralCString& aSortColumnAlias, const bool aIsLocaleAware) {
   6251  return aPlainColumnName +
   6252         (aIsLocaleAware ? EmptyCString() : " as "_ns + aSortColumnAlias) +
   6253         ", "_ns + aLocaleAwareColumnName +
   6254         (aIsLocaleAware ? " as "_ns + aSortColumnAlias : EmptyCString());
   6255 }
   6256 
   6257 constexpr bool IsIncreasingOrder(const IDBCursorDirection aDirection) {
   6258  MOZ_ASSERT(aDirection == IDBCursorDirection::Next ||
   6259             aDirection == IDBCursorDirection::Nextunique ||
   6260             aDirection == IDBCursorDirection::Prev ||
   6261             aDirection == IDBCursorDirection::Prevunique);
   6262 
   6263  return aDirection == IDBCursorDirection::Next ||
   6264         aDirection == IDBCursorDirection::Nextunique;
   6265 }
   6266 
   6267 constexpr bool IsUnique(const IDBCursorDirection aDirection) {
   6268  MOZ_ASSERT(aDirection == IDBCursorDirection::Next ||
   6269             aDirection == IDBCursorDirection::Nextunique ||
   6270             aDirection == IDBCursorDirection::Prev ||
   6271             aDirection == IDBCursorDirection::Prevunique);
   6272 
   6273  return aDirection == IDBCursorDirection::Nextunique ||
   6274         aDirection == IDBCursorDirection::Prevunique;
   6275 }
   6276 
   6277 // TODO: In principle, this could be constexpr, if operator+(nsLiteralCString,
   6278 // nsLiteralCString) were constexpr and returned a literal type.
   6279 nsAutoCString MakeDirectionClause(const IDBCursorDirection aDirection) {
   6280  return " ORDER BY "_ns + kColumnNameKey +
   6281         (IsIncreasingOrder(aDirection) ? " ASC"_ns : " DESC"_ns);
   6282 }
   6283 
   6284 enum struct ComparisonOperator {
   6285  LessThan,
   6286  LessOrEquals,
   6287  Equals,
   6288  GreaterThan,
   6289  GreaterOrEquals,
   6290 };
   6291 
   6292 constexpr nsLiteralCString GetComparisonOperatorString(
   6293    const ComparisonOperator aComparisonOperator) {
   6294  switch (aComparisonOperator) {
   6295    case ComparisonOperator::LessThan:
   6296      return "<"_ns;
   6297    case ComparisonOperator::LessOrEquals:
   6298      return "<="_ns;
   6299    case ComparisonOperator::Equals:
   6300      return "=="_ns;
   6301    case ComparisonOperator::GreaterThan:
   6302      return ">"_ns;
   6303    case ComparisonOperator::GreaterOrEquals:
   6304      return ">="_ns;
   6305  }
   6306 
   6307  // TODO: This is just to silence the "control reaches end of non-void
   6308  // function" warning. Cannot use MOZ_CRASH in a constexpr function,
   6309  // unfortunately.
   6310  return ""_ns;
   6311 }
   6312 
   6313 nsAutoCString GetKeyClause(const nsACString& aColumnName,
   6314                           const ComparisonOperator aComparisonOperator,
   6315                           const nsLiteralCString& aStmtParamName) {
   6316  return aColumnName + " "_ns +
   6317         GetComparisonOperatorString(aComparisonOperator) + " :"_ns +
   6318         aStmtParamName;
   6319 }
   6320 
   6321 nsAutoCString GetSortKeyClause(const ComparisonOperator aComparisonOperator,
   6322                               const nsLiteralCString& aStmtParamName) {
   6323  return GetKeyClause(kColumnNameAliasSortKey, aComparisonOperator,
   6324                      aStmtParamName);
   6325 }
   6326 
   6327 template <IDBCursorType CursorType>
   6328 struct PopulateResponseHelper;
   6329 
   6330 struct CommonPopulateResponseHelper {
   6331  explicit CommonPopulateResponseHelper(
   6332      const TransactionDatabaseOperationBase& aOp)
   6333      : mOp{aOp} {}
   6334 
   6335  nsresult GetKeys(mozIStorageStatement* const aStmt,
   6336                   Key* const aOptOutSortKey) {
   6337    QM_TRY(MOZ_TO_RESULT(GetCommonKeys(aStmt)));
   6338 
   6339    if (aOptOutSortKey) {
   6340      *aOptOutSortKey = mPosition;
   6341    }
   6342 
   6343    return NS_OK;
   6344  }
   6345 
   6346  nsresult GetCommonKeys(mozIStorageStatement* const aStmt) {
   6347    MOZ_ASSERT(mPosition.IsUnset());
   6348 
   6349    QM_TRY(MOZ_TO_RESULT(mPosition.SetFromStatement(aStmt, 0)));
   6350 
   6351    IDB_LOG_MARK_PARENT_TRANSACTION_REQUEST(
   6352        "PRELOAD: Populating response with key %s", "Populating%.0s",
   6353        IDB_LOG_ID_STRING(mOp.BackgroundChildLoggingId()),
   6354        mOp.TransactionLoggingSerialNumber(), mOp.LoggingSerialNumber(),
   6355        mPosition.GetBuffer().get());
   6356 
   6357    return NS_OK;
   6358  }
   6359 
   6360  template <typename Response>
   6361  void FillKeys(Response& aResponse) {
   6362    MOZ_ASSERT(!mPosition.IsUnset());
   6363    aResponse.key() = std::move(mPosition);
   6364  }
   6365 
   6366  template <typename Response>
   6367  static size_t GetKeySize(const Response& aResponse) {
   6368    return aResponse.key().GetBuffer().Length();
   6369  }
   6370 
   6371 protected:
   6372  const Key& GetPosition() const { return mPosition; }
   6373 
   6374 private:
   6375  const TransactionDatabaseOperationBase& mOp;
   6376  Key mPosition;
   6377 };
   6378 
   6379 struct IndexPopulateResponseHelper : CommonPopulateResponseHelper {
   6380  using CommonPopulateResponseHelper::CommonPopulateResponseHelper;
   6381 
   6382  nsresult GetKeys(mozIStorageStatement* const aStmt,
   6383                   Key* const aOptOutSortKey) {
   6384    MOZ_ASSERT(mLocaleAwarePosition.IsUnset());
   6385    MOZ_ASSERT(mObjectStorePosition.IsUnset());
   6386 
   6387    QM_TRY(MOZ_TO_RESULT(CommonPopulateResponseHelper::GetCommonKeys(aStmt)));
   6388 
   6389    QM_TRY(MOZ_TO_RESULT(mLocaleAwarePosition.SetFromStatement(aStmt, 1)));
   6390 
   6391    QM_TRY(MOZ_TO_RESULT(mObjectStorePosition.SetFromStatement(aStmt, 2)));
   6392 
   6393    if (aOptOutSortKey) {
   6394      *aOptOutSortKey =
   6395          mLocaleAwarePosition.IsUnset() ? GetPosition() : mLocaleAwarePosition;
   6396    }
   6397 
   6398    return NS_OK;
   6399  }
   6400 
   6401  template <typename Response>
   6402  void FillKeys(Response& aResponse) {
   6403    MOZ_ASSERT(!mLocaleAwarePosition.IsUnset());
   6404    MOZ_ASSERT(!mObjectStorePosition.IsUnset());
   6405 
   6406    CommonPopulateResponseHelper::FillKeys(aResponse);
   6407    aResponse.sortKey() = std::move(mLocaleAwarePosition);
   6408    aResponse.objectKey() = std::move(mObjectStorePosition);
   6409  }
   6410 
   6411  template <typename Response>
   6412  static size_t GetKeySize(Response& aResponse) {
   6413    return CommonPopulateResponseHelper::GetKeySize(aResponse) +
   6414           aResponse.sortKey().GetBuffer().Length() +
   6415           aResponse.objectKey().GetBuffer().Length();
   6416  }
   6417 
   6418 private:
   6419  Key mLocaleAwarePosition, mObjectStorePosition;
   6420 };
   6421 
   6422 struct KeyPopulateResponseHelper {
   6423  static constexpr nsresult MaybeGetCloneInfo(
   6424      mozIStorageStatement* const /*aStmt*/, const CursorBase& /*aCursor*/) {
   6425    return NS_OK;
   6426  }
   6427 
   6428  template <typename Response>
   6429  static constexpr void MaybeFillCloneInfo(Response& /*aResponse*/,
   6430                                           FilesArray* const /*aFiles*/) {}
   6431 
   6432  template <typename Response>
   6433  static constexpr size_t MaybeGetCloneInfoSize(const Response& /*aResponse*/) {
   6434    return 0;
   6435  }
   6436 };
   6437 
   6438 template <bool StatementHasIndexKeyBindings>
   6439 struct ValuePopulateResponseHelper {
   6440  nsresult MaybeGetCloneInfo(mozIStorageStatement* const aStmt,
   6441                             const ValueCursorBase& aCursor) {
   6442    constexpr auto offset = StatementHasIndexKeyBindings ? 2 : 0;
   6443 
   6444    QM_TRY_UNWRAP(auto cloneInfo,
   6445                  GetStructuredCloneReadInfoFromStatement(
   6446                      aStmt, 2 + offset, 1 + offset, *aCursor.mFileManager));
   6447 
   6448    mCloneInfo.init(std::move(cloneInfo));
   6449 
   6450    if (mCloneInfo->HasPreprocessInfo()) {
   6451      IDB_WARNING("Preprocessing for cursors not yet implemented!");
   6452      return NS_ERROR_NOT_IMPLEMENTED;
   6453    }
   6454 
   6455    return NS_OK;
   6456  }
   6457 
   6458  template <typename Response>
   6459  void MaybeFillCloneInfo(Response& aResponse, FilesArray* const aFiles) {
   6460    auto cloneInfo = mCloneInfo.release();
   6461    aResponse.cloneInfo().data().data = cloneInfo.ReleaseData();
   6462    aFiles->AppendElement(cloneInfo.ReleaseFiles());
   6463  }
   6464 
   6465  template <typename Response>
   6466  static size_t MaybeGetCloneInfoSize(const Response& aResponse) {
   6467    return aResponse.cloneInfo().data().data.Size();
   6468  }
   6469 
   6470 private:
   6471  LazyInitializedOnceEarlyDestructible<const StructuredCloneReadInfoParent>
   6472      mCloneInfo;
   6473 };
   6474 
   6475 template <>
   6476 struct PopulateResponseHelper<IDBCursorType::ObjectStore>
   6477    : ValuePopulateResponseHelper<false>, CommonPopulateResponseHelper {
   6478  using CommonPopulateResponseHelper::CommonPopulateResponseHelper;
   6479 
   6480  static auto& GetTypedResponse(CursorResponse* const aResponse) {
   6481    return aResponse->get_ArrayOfObjectStoreCursorResponse();
   6482  }
   6483 };
   6484 
   6485 template <>
   6486 struct PopulateResponseHelper<IDBCursorType::ObjectStoreKey>
   6487    : KeyPopulateResponseHelper, CommonPopulateResponseHelper {
   6488  using CommonPopulateResponseHelper::CommonPopulateResponseHelper;
   6489 
   6490  static auto& GetTypedResponse(CursorResponse* const aResponse) {
   6491    return aResponse->get_ArrayOfObjectStoreKeyCursorResponse();
   6492  }
   6493 };
   6494 
   6495 template <>
   6496 struct PopulateResponseHelper<IDBCursorType::Index>
   6497    : ValuePopulateResponseHelper<true>, IndexPopulateResponseHelper {
   6498  using IndexPopulateResponseHelper::IndexPopulateResponseHelper;
   6499 
   6500  static auto& GetTypedResponse(CursorResponse* const aResponse) {
   6501    return aResponse->get_ArrayOfIndexCursorResponse();
   6502  }
   6503 };
   6504 
   6505 template <>
   6506 struct PopulateResponseHelper<IDBCursorType::IndexKey>
   6507    : KeyPopulateResponseHelper, IndexPopulateResponseHelper {
   6508  using IndexPopulateResponseHelper::IndexPopulateResponseHelper;
   6509 
   6510  static auto& GetTypedResponse(CursorResponse* const aResponse) {
   6511    return aResponse->get_ArrayOfIndexKeyCursorResponse();
   6512  }
   6513 };
   6514 
   6515 nsresult DispatchAndReturnFileReferences(
   6516    PersistenceType aPersistenceType, const nsACString& aOrigin,
   6517    const nsAString& aDatabaseName, const int64_t aFileId,
   6518    int32_t* const aMemRefCnt, int32_t* const aDBRefCnt, bool* const aResult) {
   6519  AssertIsOnBackgroundThread();
   6520  MOZ_ASSERT(aMemRefCnt);
   6521  MOZ_ASSERT(aDBRefCnt);
   6522  MOZ_ASSERT(aResult);
   6523 
   6524  *aResult = false;
   6525  *aMemRefCnt = -1;
   6526  *aDBRefCnt = -1;
   6527 
   6528  mozilla::Monitor monitor MOZ_ANNOTATED(__func__);
   6529  bool waiting = true;
   6530 
   6531  auto lambda = [&] {
   6532    AssertIsOnIOThread();
   6533 
   6534    {
   6535      IndexedDatabaseManager* const mgr = IndexedDatabaseManager::Get();
   6536      MOZ_ASSERT(mgr);
   6537 
   6538      const SafeRefPtr<DatabaseFileManager> fileManager =
   6539          mgr->GetFileManager(aPersistenceType, aOrigin, aDatabaseName);
   6540 
   6541      if (fileManager) {
   6542        const SafeRefPtr<DatabaseFileInfo> fileInfo =
   6543            fileManager->GetFileInfo(aFileId);
   6544 
   6545        if (fileInfo) {
   6546          fileInfo->GetReferences(aMemRefCnt, aDBRefCnt);
   6547 
   6548          if (*aMemRefCnt != -1) {
   6549            // We added an extra temp ref, so account for that accordingly.
   6550            (*aMemRefCnt)--;
   6551          }
   6552 
   6553          *aResult = true;
   6554        }
   6555      }
   6556    }
   6557 
   6558    mozilla::MonitorAutoLock lock(monitor);
   6559    MOZ_ASSERT(waiting);
   6560 
   6561    waiting = false;
   6562    lock.Notify();
   6563  };
   6564 
   6565  QuotaManager* const quotaManager = QuotaManager::Get();
   6566  MOZ_ASSERT(quotaManager);
   6567 
   6568  // XXX can't we simply use NS_DispatchAndSpinEventLoopUntilComplete instead of
   6569  // using a monitor?
   6570  QM_TRY(MOZ_TO_RESULT(quotaManager->IOThread()->Dispatch(
   6571      NS_NewRunnableFunction("GetFileReferences", std::move(lambda)),
   6572      NS_DISPATCH_NORMAL)));
   6573 
   6574  mozilla::MonitorAutoLock autolock(monitor);
   6575  while (waiting) {
   6576    autolock.Wait();
   6577  }
   6578 
   6579  return NS_OK;
   6580 }
   6581 
   6582 class DeserializeIndexValueHelper final : public Runnable {
   6583 public:
   6584  DeserializeIndexValueHelper(int64_t aIndexID, const KeyPath& aKeyPath,
   6585                              bool aMultiEntry, const nsACString& aLocale,
   6586                              StructuredCloneReadInfoParent& aCloneReadInfo,
   6587                              nsTArray<IndexUpdateInfo>& aUpdateInfoArray)
   6588      : Runnable("DeserializeIndexValueHelper"),
   6589        mMonitor("DeserializeIndexValueHelper::mMonitor"),
   6590        mIndexID(aIndexID),
   6591        mKeyPath(aKeyPath),
   6592        mMultiEntry(aMultiEntry),
   6593        mLocale(aLocale),
   6594        mCloneReadInfo(aCloneReadInfo),
   6595        mUpdateInfoArray(aUpdateInfoArray),
   6596        mStatus(NS_ERROR_FAILURE) {}
   6597 
   6598  nsresult DispatchAndWait() {
   6599    // FIXME(Bug 1637530) Re-enable optimization using a non-system-principaled
   6600    // JS context
   6601 #if 0
   6602    // We don't need to go to the main-thread and use the sandbox. Let's create
   6603    // the updateInfo data here.
   6604    if (!mCloneReadInfo.Data().Size()) {
   6605      AutoJSAPI jsapi;
   6606      jsapi.Init();
   6607 
   6608      JS::Rooted<JS::Value> value(jsapi.cx());
   6609      value.setUndefined();
   6610 
   6611      ErrorResult rv;
   6612      IDBObjectStore::AppendIndexUpdateInfo(
   6613          mIndexID, mKeyPath, mMultiEntry, &mUpdateInfoArray,
   6614          /* aAutoIncrementedObjectStoreKeyPath */ VoidString(), &rv);
   6615      return rv.Failed() ? rv.StealNSResult() : NS_OK;
   6616    }
   6617 #endif
   6618 
   6619    // The operation will continue on the main-thread.
   6620 
   6621    MOZ_ASSERT(!(mCloneReadInfo.Data().Size() % sizeof(uint64_t)));
   6622 
   6623    MonitorAutoLock lock(mMonitor);
   6624 
   6625    RefPtr<Runnable> self = this;
   6626    QM_TRY(MOZ_TO_RESULT(SchedulerGroup::Dispatch(self.forget())));
   6627 
   6628    lock.Wait();
   6629    return mStatus;
   6630  }
   6631 
   6632  NS_IMETHOD
   6633  Run() override {
   6634    MOZ_ASSERT(NS_IsMainThread());
   6635 
   6636    AutoJSAPI jsapi;
   6637    jsapi.Init();
   6638    JSContext* const cx = jsapi.cx();
   6639 
   6640    JS::Rooted<JSObject*> global(cx, GetSandbox(cx));
   6641 
   6642    QM_TRY(OkIf(global), NS_OK,
   6643           [this](const NotOk) { OperationCompleted(NS_ERROR_FAILURE); });
   6644 
   6645    const JSAutoRealm ar(cx, global);
   6646 
   6647    JS::Rooted<JS::Value> value(cx);
   6648    QM_TRY(MOZ_TO_RESULT(DeserializeIndexValue(cx, &value)), NS_OK,
   6649           [this](const nsresult rv) { OperationCompleted(rv); });
   6650 
   6651    ErrorResult errorResult;
   6652    IDBObjectStore::AppendIndexUpdateInfo(
   6653        mIndexID, mKeyPath, mMultiEntry, mLocale, cx, value, &mUpdateInfoArray,
   6654        /* aAutoIncrementedObjectStoreKeyPath */ VoidString(), &errorResult);
   6655    QM_TRY(OkIf(!errorResult.Failed()), NS_OK,
   6656           ([this, &errorResult](const NotOk) {
   6657             OperationCompleted(errorResult.StealNSResult());
   6658           }));
   6659 
   6660    OperationCompleted(NS_OK);
   6661    return NS_OK;
   6662  }
   6663 
   6664 private:
   6665  nsresult DeserializeIndexValue(JSContext* aCx,
   6666                                 JS::MutableHandle<JS::Value> aValue) {
   6667    static const JSStructuredCloneCallbacks callbacks = {
   6668        StructuredCloneReadCallback<StructuredCloneReadInfoParent>,
   6669        nullptr,
   6670        nullptr,
   6671        nullptr,
   6672        nullptr,
   6673        nullptr,
   6674        nullptr,
   6675        nullptr};
   6676 
   6677    if (!JS_ReadStructuredClone(
   6678            aCx, mCloneReadInfo.Data(), JS_STRUCTURED_CLONE_VERSION,
   6679            JS::StructuredCloneScope::DifferentProcessForIndexedDB, aValue,
   6680            JS::CloneDataPolicy(), &callbacks, &mCloneReadInfo)) {
   6681      return NS_ERROR_DOM_DATA_CLONE_ERR;
   6682    }
   6683 
   6684    return NS_OK;
   6685  }
   6686 
   6687  void OperationCompleted(nsresult aStatus) {
   6688    mStatus = aStatus;
   6689 
   6690    MonitorAutoLock lock(mMonitor);
   6691    lock.Notify();
   6692  }
   6693 
   6694  Monitor mMonitor MOZ_UNANNOTATED;
   6695 
   6696  const int64_t mIndexID;
   6697  const KeyPath& mKeyPath;
   6698  const bool mMultiEntry;
   6699  const nsCString mLocale;
   6700  StructuredCloneReadInfoParent& mCloneReadInfo;
   6701  nsTArray<IndexUpdateInfo>& mUpdateInfoArray;
   6702  nsresult mStatus;
   6703 };
   6704 
   6705 auto DeserializeIndexValueToUpdateInfos(
   6706    int64_t aIndexID, const KeyPath& aKeyPath, bool aMultiEntry,
   6707    const nsACString& aLocale, StructuredCloneReadInfoParent& aCloneReadInfo) {
   6708  MOZ_ASSERT(!NS_IsMainThread());
   6709 
   6710  using ArrayType = AutoTArray<IndexUpdateInfo, 32>;
   6711  using ResultType = Result<ArrayType, nsresult>;
   6712 
   6713  ArrayType updateInfoArray;
   6714  const auto helper = MakeRefPtr<DeserializeIndexValueHelper>(
   6715      aIndexID, aKeyPath, aMultiEntry, aLocale, aCloneReadInfo,
   6716      updateInfoArray);
   6717  const nsresult rv = helper->DispatchAndWait();
   6718  return NS_FAILED(rv) ? Err(rv) : ResultType{std::move(updateInfoArray)};
   6719 }
   6720 
   6721 bool IsSome(
   6722    const Maybe<CachingDatabaseConnection::BorrowedStatement>& aMaybeStmt) {
   6723  return aMaybeStmt.isSome();
   6724 }
   6725 
   6726 already_AddRefed<nsIThreadPool> MakeConnectionIOTarget() {
   6727  nsCOMPtr<nsIThreadPool> threadPool = new nsThreadPool();
   6728 
   6729  MOZ_ALWAYS_SUCCEEDS(threadPool->SetThreadLimit(kMaxConnectionThreadCount));
   6730 
   6731  MOZ_ALWAYS_SUCCEEDS(
   6732      threadPool->SetIdleThreadLimit(kMaxIdleConnectionThreadCount));
   6733 
   6734  MOZ_ALWAYS_SUCCEEDS(
   6735      threadPool->SetIdleThreadMaximumTimeout(kConnectionThreadMaxIdleMS));
   6736 
   6737  MOZ_ALWAYS_SUCCEEDS(
   6738      threadPool->SetIdleThreadGraceTimeout(kConnectionThreadGraceIdleMS));
   6739 
   6740  MOZ_ALWAYS_SUCCEEDS(threadPool->SetName("IndexedDB IO"_ns));
   6741 
   6742  return threadPool.forget();
   6743 }
   6744 
   6745 }  // namespace
   6746 
   6747 /*******************************************************************************
   6748 * Exported functions
   6749 ******************************************************************************/
   6750 
   6751 already_AddRefed<PBackgroundIDBFactoryParent> AllocPBackgroundIDBFactoryParent(
   6752    const LoggingInfo& aLoggingInfo, const nsACString& aSystemLocale) {
   6753  AssertIsOnBackgroundThread();
   6754 
   6755  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) {
   6756    return nullptr;
   6757  }
   6758 
   6759  if (NS_AUUF_OR_WARN_IF(!aLoggingInfo.nextTransactionSerialNumber()) ||
   6760      NS_AUUF_OR_WARN_IF(
   6761          !aLoggingInfo.nextVersionChangeTransactionSerialNumber()) ||
   6762      NS_AUUF_OR_WARN_IF(!aLoggingInfo.nextRequestSerialNumber())) {
   6763    return nullptr;
   6764  }
   6765 
   6766  SafeRefPtr<Factory> actor = Factory::Create(aLoggingInfo, aSystemLocale);
   6767  MOZ_ASSERT(actor);
   6768 
   6769  return actor.forget();
   6770 }
   6771 
   6772 bool RecvPBackgroundIDBFactoryConstructor(
   6773    PBackgroundIDBFactoryParent* aActor, const LoggingInfo& /* aLoggingInfo */,
   6774    const nsACString& /* aSystemLocale */) {
   6775  AssertIsOnBackgroundThread();
   6776  MOZ_ASSERT(aActor);
   6777  MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
   6778 
   6779  return true;
   6780 }
   6781 
   6782 PBackgroundIndexedDBUtilsParent* AllocPBackgroundIndexedDBUtilsParent() {
   6783  AssertIsOnBackgroundThread();
   6784 
   6785  RefPtr<Utils> actor = new Utils();
   6786 
   6787  return actor.forget().take();
   6788 }
   6789 
   6790 bool DeallocPBackgroundIndexedDBUtilsParent(
   6791    PBackgroundIndexedDBUtilsParent* aActor) {
   6792  AssertIsOnBackgroundThread();
   6793  MOZ_ASSERT(aActor);
   6794 
   6795  RefPtr<Utils> actor = dont_AddRef(static_cast<Utils*>(aActor));
   6796  return true;
   6797 }
   6798 
   6799 bool RecvFlushPendingFileDeletions() {
   6800  AssertIsOnBackgroundThread();
   6801 
   6802  if (QuotaClient* quotaClient = QuotaClient::GetInstance()) {
   6803    QM_WARNONLY_TRY(QM_TO_RESULT(quotaClient->FlushPendingFileDeletions()));
   6804  }
   6805 
   6806  return true;
   6807 }
   6808 
   6809 RefPtr<mozilla::dom::quota::Client> CreateQuotaClient() {
   6810  AssertIsOnBackgroundThread();
   6811 
   6812  return MakeRefPtr<QuotaClient>();
   6813 }
   6814 
   6815 nsresult DatabaseFileManager::AsyncDeleteFile(int64_t aFileId) {
   6816  AssertIsOnBackgroundThread();
   6817  MOZ_ASSERT(!ContainsFileInfo(aFileId));
   6818 
   6819  QuotaClient* quotaClient = QuotaClient::GetInstance();
   6820  if (quotaClient) {
   6821    QM_TRY(MOZ_TO_RESULT(quotaClient->AsyncDeleteFile(this, aFileId)));
   6822  }
   6823 
   6824  return NS_OK;
   6825 }
   6826 
   6827 /*******************************************************************************
   6828 * DatabaseConnection implementation
   6829 ******************************************************************************/
   6830 
   6831 DatabaseConnection::DatabaseConnection(
   6832    MovingNotNull<nsCOMPtr<mozIStorageConnection>> aStorageConnection,
   6833    MovingNotNull<SafeRefPtr<DatabaseFileManager>> aFileManager)
   6834    : CachingDatabaseConnection(std::move(aStorageConnection)),
   6835      mFileManager(std::move(aFileManager)),
   6836      mLastDurability(IDBTransaction::Durability::Default),
   6837      mInReadTransaction(false),
   6838      mInWriteTransaction(false)
   6839 #ifdef DEBUG
   6840      ,
   6841      mDEBUGSavepointCount(0)
   6842 #endif
   6843 {
   6844  AssertIsOnConnectionThread();
   6845  MOZ_ASSERT(mFileManager);
   6846 }
   6847 
   6848 DatabaseConnection::~DatabaseConnection() {
   6849  MOZ_ASSERT(!mFileManager);
   6850  MOZ_ASSERT(!mUpdateRefcountFunction);
   6851  MOZ_DIAGNOSTIC_ASSERT(!mInWriteTransaction);
   6852  MOZ_ASSERT(!mDEBUGSavepointCount);
   6853 }
   6854 
   6855 nsresult DatabaseConnection::Init() {
   6856  AssertIsOnConnectionThread();
   6857  MOZ_ASSERT(!mInReadTransaction);
   6858  MOZ_ASSERT(!mInWriteTransaction);
   6859 
   6860  QM_TRY(MOZ_TO_RESULT(ExecuteCachedStatement("BEGIN;"_ns)));
   6861 
   6862  mInReadTransaction = true;
   6863 
   6864  return NS_OK;
   6865 }
   6866 
   6867 nsresult DatabaseConnection::BeginWriteTransaction(
   6868    const IDBTransaction::Durability aDurability) {
   6869  AssertIsOnConnectionThread();
   6870  MOZ_ASSERT(HasStorageConnection());
   6871  MOZ_ASSERT(mInReadTransaction);
   6872  MOZ_ASSERT(!mInWriteTransaction);
   6873 
   6874  AUTO_PROFILER_LABEL("DatabaseConnection::BeginWriteTransaction", DOM);
   6875 
   6876  // Release our read locks.
   6877  QM_TRY(MOZ_TO_RESULT(ExecuteCachedStatement("ROLLBACK;"_ns)));
   6878 
   6879  mInReadTransaction = false;
   6880 
   6881  if (mLastDurability != aDurability) {
   6882    auto synchronousMode = [aDurability]() -> nsLiteralCString {
   6883      switch (aDurability) {
   6884        case IDBTransaction::Durability::Default:
   6885          return GetDefaultSynchronousMode();
   6886 
   6887        case IDBTransaction::Durability::Strict:
   6888          return "EXTRA"_ns;
   6889 
   6890        case IDBTransaction::Durability::Relaxed:
   6891          return "OFF"_ns;
   6892 
   6893        default:
   6894          MOZ_CRASH("Unknown CheckpointMode!");
   6895      }
   6896    }();
   6897 
   6898    QM_TRY(MOZ_TO_RESULT(ExecuteCachedStatement("PRAGMA synchronous = "_ns +
   6899                                                synchronousMode + ";"_ns)));
   6900 
   6901    mLastDurability = aDurability;
   6902  }
   6903 
   6904  if (!mUpdateRefcountFunction) {
   6905    MOZ_ASSERT(mFileManager);
   6906 
   6907    RefPtr<UpdateRefcountFunction> function =
   6908        new UpdateRefcountFunction(this, **mFileManager);
   6909 
   6910    QM_TRY(MOZ_TO_RESULT(MutableStorageConnection().CreateFunction(
   6911        "update_refcount"_ns,
   6912        /* aNumArguments */ 2, function)));
   6913 
   6914    mUpdateRefcountFunction = std::move(function);
   6915  }
   6916 
   6917  // This one cannot obviously use ExecuteCachedStatement because of the custom
   6918  // error handling for Execute only. If only Execute can produce
   6919  // NS_ERROR_STORAGE_BUSY, we could actually use ExecuteCachedStatement and
   6920  // simplify this.
   6921  QM_TRY_INSPECT(const auto& beginStmt,
   6922                 BorrowCachedStatement("BEGIN IMMEDIATE;"_ns));
   6923 
   6924  QM_TRY(QM_OR_ELSE_WARN_IF(
   6925      // Expression.
   6926      MOZ_TO_RESULT(beginStmt->Execute()),
   6927      // Predicate.
   6928      IsSpecificError<NS_ERROR_STORAGE_BUSY>,
   6929      // Fallback.
   6930      ([&beginStmt](nsresult rv) {
   6931        NS_WARNING(
   6932            "Received NS_ERROR_STORAGE_BUSY when attempting to start write "
   6933            "transaction, retrying for up to 10 seconds");
   6934 
   6935        // Another thread must be using the database. Wait up to 10 seconds
   6936        // for that to complete.
   6937        const TimeStamp start = TimeStamp::NowLoRes();
   6938 
   6939        while (true) {
   6940          PR_Sleep(PR_MillisecondsToInterval(100));
   6941 
   6942          rv = beginStmt->Execute();
   6943          if (rv != NS_ERROR_STORAGE_BUSY ||
   6944              TimeStamp::NowLoRes() - start > TimeDuration::FromSeconds(10)) {
   6945            break;
   6946          }
   6947        }
   6948 
   6949        return MOZ_TO_RESULT(rv);
   6950      })));
   6951 
   6952  mInWriteTransaction = true;
   6953 
   6954  return NS_OK;
   6955 }
   6956 
   6957 nsresult DatabaseConnection::CommitWriteTransaction() {
   6958  AssertIsOnConnectionThread();
   6959  MOZ_ASSERT(HasStorageConnection());
   6960  MOZ_ASSERT(!mInReadTransaction);
   6961  MOZ_ASSERT(mInWriteTransaction);
   6962 
   6963  AUTO_PROFILER_LABEL("DatabaseConnection::CommitWriteTransaction", DOM);
   6964 
   6965  QM_TRY(MOZ_TO_RESULT(ExecuteCachedStatement("COMMIT;"_ns)));
   6966 
   6967  mInWriteTransaction = false;
   6968  return NS_OK;
   6969 }
   6970 
   6971 void DatabaseConnection::RollbackWriteTransaction() {
   6972  AssertIsOnConnectionThread();
   6973  MOZ_ASSERT(!mInReadTransaction);
   6974  MOZ_DIAGNOSTIC_ASSERT(HasStorageConnection());
   6975 
   6976  AUTO_PROFILER_LABEL("DatabaseConnection::RollbackWriteTransaction", DOM);
   6977 
   6978  if (!mInWriteTransaction) {
   6979    return;
   6980  }
   6981 
   6982  QM_WARNONLY_TRY(
   6983      BorrowCachedStatement("ROLLBACK;"_ns)
   6984          .andThen([&self = *this](const auto& stmt) -> Result<Ok, nsresult> {
   6985            // This may fail if SQLite already rolled back the transaction
   6986            // so ignore any errors.
   6987 
   6988            // XXX ROLLBACK can fail quite normmally if a previous statement
   6989            // failed to execute successfully so SQLite rolled back the
   6990            // transaction already. However, if it failed because of some other
   6991            // reason, we could try to close the connection.
   6992            (void)stmt->Execute();
   6993 
   6994            self.mInWriteTransaction = false;
   6995            return Ok{};
   6996          }));
   6997 }
   6998 
   6999 void DatabaseConnection::FinishWriteTransaction() {
   7000  AssertIsOnConnectionThread();
   7001  MOZ_ASSERT(HasStorageConnection());
   7002  MOZ_ASSERT(!mInReadTransaction);
   7003  MOZ_ASSERT(!mInWriteTransaction);
   7004 
   7005  AUTO_PROFILER_LABEL("DatabaseConnection::FinishWriteTransaction", DOM);
   7006 
   7007  if (mUpdateRefcountFunction) {
   7008    mUpdateRefcountFunction->Reset();
   7009  }
   7010 
   7011  QM_WARNONLY_TRY(MOZ_TO_RESULT(ExecuteCachedStatement("BEGIN;"_ns))
   7012                      .andThen([&](const auto) -> Result<Ok, nsresult> {
   7013                        mInReadTransaction = true;
   7014                        return Ok{};
   7015                      }));
   7016 }
   7017 
   7018 nsresult DatabaseConnection::StartSavepoint() {
   7019  AssertIsOnConnectionThread();
   7020  MOZ_ASSERT(HasStorageConnection());
   7021  MOZ_ASSERT(mUpdateRefcountFunction);
   7022  MOZ_ASSERT(mInWriteTransaction);
   7023 
   7024  AUTO_PROFILER_LABEL("DatabaseConnection::StartSavepoint", DOM);
   7025 
   7026  QM_TRY(MOZ_TO_RESULT(ExecuteCachedStatement(SAVEPOINT_CLAUSE)));
   7027 
   7028  mUpdateRefcountFunction->StartSavepoint();
   7029 
   7030 #ifdef DEBUG
   7031  MOZ_ASSERT(mDEBUGSavepointCount < UINT32_MAX);
   7032  mDEBUGSavepointCount++;
   7033 #endif
   7034 
   7035  return NS_OK;
   7036 }
   7037 
   7038 nsresult DatabaseConnection::ReleaseSavepoint() {
   7039  AssertIsOnConnectionThread();
   7040  MOZ_ASSERT(HasStorageConnection());
   7041  MOZ_ASSERT(mUpdateRefcountFunction);
   7042  MOZ_ASSERT(mInWriteTransaction);
   7043 
   7044  AUTO_PROFILER_LABEL("DatabaseConnection::ReleaseSavepoint", DOM);
   7045 
   7046  QM_TRY(MOZ_TO_RESULT(ExecuteCachedStatement("RELEASE "_ns SAVEPOINT_CLAUSE)));
   7047 
   7048  mUpdateRefcountFunction->ReleaseSavepoint();
   7049 
   7050 #ifdef DEBUG
   7051  MOZ_ASSERT(mDEBUGSavepointCount);
   7052  mDEBUGSavepointCount--;
   7053 #endif
   7054 
   7055  return NS_OK;
   7056 }
   7057 
   7058 nsresult DatabaseConnection::RollbackSavepoint() {
   7059  AssertIsOnConnectionThread();
   7060  MOZ_ASSERT(HasStorageConnection());
   7061  MOZ_ASSERT(mUpdateRefcountFunction);
   7062  MOZ_ASSERT(mInWriteTransaction);
   7063 
   7064  AUTO_PROFILER_LABEL("DatabaseConnection::RollbackSavepoint", DOM);
   7065 
   7066 #ifdef DEBUG
   7067  MOZ_ASSERT(mDEBUGSavepointCount);
   7068  mDEBUGSavepointCount--;
   7069 #endif
   7070 
   7071  mUpdateRefcountFunction->RollbackSavepoint();
   7072 
   7073  QM_TRY_INSPECT(const auto& stmt,
   7074                 BorrowCachedStatement("ROLLBACK TO "_ns SAVEPOINT_CLAUSE));
   7075 
   7076  // This may fail if SQLite already rolled back the savepoint so ignore any
   7077  // errors.
   7078  (void)stmt->Execute();
   7079 
   7080  return NS_OK;
   7081 }
   7082 
   7083 nsresult DatabaseConnection::CheckpointInternal(CheckpointMode aMode) {
   7084  AssertIsOnConnectionThread();
   7085  MOZ_ASSERT(!mInReadTransaction);
   7086  MOZ_ASSERT(!mInWriteTransaction);
   7087 
   7088  AUTO_PROFILER_LABEL("DatabaseConnection::CheckpointInternal", DOM);
   7089 
   7090  nsAutoCString stmtString;
   7091  stmtString.AssignLiteral("PRAGMA wal_checkpoint(");
   7092 
   7093  switch (aMode) {
   7094    case CheckpointMode::Full:
   7095      // Ensures that the database is completely checkpointed and flushed to
   7096      // disk.
   7097      stmtString.AppendLiteral("FULL");
   7098      break;
   7099 
   7100    case CheckpointMode::Restart:
   7101      // Like Full, but also ensures that the next write will start overwriting
   7102      // the existing WAL file rather than letting the WAL file grow.
   7103      stmtString.AppendLiteral("RESTART");
   7104      break;
   7105 
   7106    case CheckpointMode::Truncate:
   7107      // Like Restart but also truncates the existing WAL file.
   7108      stmtString.AppendLiteral("TRUNCATE");
   7109      break;
   7110 
   7111    default:
   7112      MOZ_CRASH("Unknown CheckpointMode!");
   7113  }
   7114 
   7115  stmtString.AppendLiteral(");");
   7116 
   7117  QM_TRY(MOZ_TO_RESULT(ExecuteCachedStatement(stmtString)));
   7118 
   7119  return NS_OK;
   7120 }
   7121 
   7122 void DatabaseConnection::DoIdleProcessing(bool aNeedsCheckpoint,
   7123                                          const Atomic<bool>& aInterrupted) {
   7124  AssertIsOnConnectionThread();
   7125  MOZ_ASSERT(mInReadTransaction);
   7126  MOZ_ASSERT(!mInWriteTransaction);
   7127 
   7128  AUTO_PROFILER_LABEL("DatabaseConnection::DoIdleProcessing", DOM);
   7129 
   7130  CachingDatabaseConnection::CachedStatement freelistStmt;
   7131  const uint32_t freelistCount = [this, &freelistStmt] {
   7132    QM_TRY_RETURN(GetFreelistCount(freelistStmt), 0u);
   7133  }();
   7134 
   7135  CachedStatement rollbackStmt;
   7136  CachedStatement beginStmt;
   7137  if (aNeedsCheckpoint || freelistCount) {
   7138    QM_TRY_UNWRAP(rollbackStmt, GetCachedStatement("ROLLBACK;"_ns), QM_VOID);
   7139    QM_TRY_UNWRAP(beginStmt, GetCachedStatement("BEGIN;"_ns), QM_VOID);
   7140 
   7141    // Release the connection's normal transaction. It's possible that it could
   7142    // fail, but that isn't a problem here.
   7143    (void)rollbackStmt.Borrow()->Execute();
   7144 
   7145    mInReadTransaction = false;
   7146  }
   7147 
   7148  const bool freedSomePages =
   7149      freelistCount && [this, &freelistStmt, &rollbackStmt, freelistCount,
   7150                        aNeedsCheckpoint, &aInterrupted] {
   7151        // Warn in case of an error, but do not propagate it. Just indicate we
   7152        // didn't free any pages.
   7153        QM_TRY_INSPECT(
   7154            const bool& res,
   7155            ReclaimFreePagesWhileIdle(freelistStmt, rollbackStmt, freelistCount,
   7156                                      aNeedsCheckpoint, aInterrupted),
   7157            false);
   7158 
   7159        // Make sure we didn't leave a transaction running.
   7160        MOZ_ASSERT(!mInReadTransaction);
   7161        MOZ_ASSERT(!mInWriteTransaction);
   7162 
   7163        return res;
   7164      }();
   7165 
   7166  // Truncate the WAL if we were asked to or if we managed to free some space.
   7167  if (aNeedsCheckpoint || freedSomePages) {
   7168    QM_WARNONLY_TRY(QM_TO_RESULT(CheckpointInternal(CheckpointMode::Truncate)));
   7169  }
   7170 
   7171  // Finally try to restart the read transaction if we rolled it back earlier.
   7172  if (beginStmt) {
   7173    QM_WARNONLY_TRY(
   7174        MOZ_TO_RESULT(beginStmt.Borrow()->Execute())
   7175            .andThen([&self = *this](const Ok) -> Result<Ok, nsresult> {
   7176              self.mInReadTransaction = true;
   7177              return Ok{};
   7178            }));
   7179  }
   7180 }
   7181 
   7182 Result<bool, nsresult> DatabaseConnection::ReclaimFreePagesWhileIdle(
   7183    CachedStatement& aFreelistStatement, CachedStatement& aRollbackStatement,
   7184    uint32_t aFreelistCount, bool aNeedsCheckpoint,
   7185    const Atomic<bool>& aInterrupted) {
   7186  AssertIsOnConnectionThread();
   7187  MOZ_ASSERT(aFreelistStatement);
   7188  MOZ_ASSERT(aRollbackStatement);
   7189  MOZ_ASSERT(aFreelistCount);
   7190  MOZ_ASSERT(!mInReadTransaction);
   7191  MOZ_ASSERT(!mInWriteTransaction);
   7192 
   7193  AUTO_PROFILER_LABEL("DatabaseConnection::ReclaimFreePagesWhileIdle", DOM);
   7194 
   7195  uint32_t pauseOnConnectionThreadMs = StaticPrefs::
   7196      dom_indexedDB_connectionIdleMaintenance_pauseOnConnectionThreadMs();
   7197  if (pauseOnConnectionThreadMs > 0) {
   7198    PR_Sleep(PR_MillisecondsToInterval(pauseOnConnectionThreadMs));
   7199  }
   7200 
   7201  // Make sure we don't keep working if anything else needs this thread.
   7202  if (aInterrupted) {
   7203    return false;
   7204  }
   7205 
   7206  // Make all the statements we'll need up front.
   7207 
   7208  // Only try to free 10% at a time so that we can bail out if this connection
   7209  // suddenly becomes active or if the thread is needed otherwise.
   7210  QM_TRY_INSPECT(
   7211      const auto& incrementalVacuumStmt,
   7212      GetCachedStatement(
   7213          "PRAGMA incremental_vacuum("_ns +
   7214          IntToCString(std::max(uint64_t(1), uint64_t(aFreelistCount / 10))) +
   7215          ");"_ns));
   7216 
   7217  QM_TRY_INSPECT(const auto& beginImmediateStmt,
   7218                 GetCachedStatement("BEGIN IMMEDIATE;"_ns));
   7219 
   7220  QM_TRY_INSPECT(const auto& commitStmt, GetCachedStatement("COMMIT;"_ns));
   7221 
   7222  if (aNeedsCheckpoint) {
   7223    // Freeing pages is a journaled operation, so it will require additional WAL
   7224    // space. However, we're idle and are about to checkpoint anyway, so doing a
   7225    // RESTART checkpoint here should allow us to reuse any existing space.
   7226    QM_TRY(MOZ_TO_RESULT(CheckpointInternal(CheckpointMode::Restart)));
   7227  }
   7228 
   7229  // Start the write transaction.
   7230  QM_TRY(MOZ_TO_RESULT(beginImmediateStmt.Borrow()->Execute()));
   7231 
   7232  mInWriteTransaction = true;
   7233 
   7234  bool freedSomePages = false;
   7235 
   7236  const auto rollback = [&aRollbackStatement, this](const auto&) {
   7237    MOZ_ASSERT(mInWriteTransaction);
   7238 
   7239    // Something failed, make sure we roll everything back.
   7240    (void)aRollbackStatement.Borrow()->Execute();
   7241 
   7242    // XXX Is rollback infallible? Shouldn't we check the result?
   7243 
   7244    mInWriteTransaction = false;
   7245  };
   7246 
   7247  uint64_t previousFreelistCount = (uint64_t)aFreelistCount + 1;
   7248 
   7249  QM_TRY(CollectWhile(
   7250             [&aFreelistCount, &previousFreelistCount,
   7251              &aInterrupted]() -> Result<bool, nsresult> {
   7252               if (aInterrupted) {
   7253                 // On interrupt, abort and roll back this transaction. It's ok
   7254                 // if we never make progress here because the idle service
   7255                 // should eventually reclaim this space.
   7256                 return false;
   7257               }
   7258               // If we were not able to free anything, we might either see
   7259               // a DB that has no auto-vacuum support at all or some other
   7260               // (hopefully temporary) condition that prevents vacuum from
   7261               // working. Just carry on in non-DEBUG.
   7262               bool madeProgress = previousFreelistCount != aFreelistCount;
   7263               previousFreelistCount = aFreelistCount;
   7264               MOZ_ASSERT(madeProgress);
   7265               QM_WARNONLY_TRY(MOZ_TO_RESULT(madeProgress));
   7266               return madeProgress && (aFreelistCount != 0);
   7267             },
   7268             [&aFreelistStatement, &aFreelistCount, &incrementalVacuumStmt,
   7269              &freedSomePages, this]() -> mozilla::Result<Ok, nsresult> {
   7270               QM_TRY(MOZ_TO_RESULT(incrementalVacuumStmt.Borrow()->Execute()));
   7271 
   7272               freedSomePages = true;
   7273 
   7274               QM_TRY_UNWRAP(aFreelistCount,
   7275                             GetFreelistCount(aFreelistStatement));
   7276 
   7277               return Ok{};
   7278             })
   7279             .andThen([&commitStmt, &freedSomePages, &aInterrupted, &rollback,
   7280                       this](Ok) -> Result<Ok, nsresult> {
   7281               if (aInterrupted) {
   7282                 rollback(Ok{});
   7283                 freedSomePages = false;
   7284               }
   7285 
   7286               if (freedSomePages) {
   7287                 // Commit the write transaction.
   7288                 QM_TRY(MOZ_TO_RESULT(commitStmt.Borrow()->Execute()),
   7289                        QM_PROPAGATE,
   7290                        [](const auto&) { NS_WARNING("Failed to commit!"); });
   7291 
   7292                 mInWriteTransaction = false;
   7293               }
   7294 
   7295               return Ok{};
   7296             }),
   7297         QM_PROPAGATE, rollback);
   7298 
   7299  return freedSomePages;
   7300 }
   7301 
   7302 Result<uint32_t, nsresult> DatabaseConnection::GetFreelistCount(
   7303    CachedStatement& aCachedStatement) {
   7304  AssertIsOnConnectionThread();
   7305 
   7306  AUTO_PROFILER_LABEL("DatabaseConnection::GetFreelistCount", DOM);
   7307 
   7308  if (!aCachedStatement) {
   7309    QM_TRY_UNWRAP(aCachedStatement,
   7310                  GetCachedStatement("PRAGMA freelist_count;"_ns));
   7311  }
   7312 
   7313  const auto borrowedStatement = aCachedStatement.Borrow();
   7314 
   7315  QM_TRY_UNWRAP(const DebugOnly<bool> hasResult,
   7316                MOZ_TO_RESULT_INVOKE_MEMBER(&*borrowedStatement, ExecuteStep));
   7317 
   7318  MOZ_ASSERT(hasResult);
   7319 
   7320  QM_TRY_INSPECT(const int32_t& freelistCount,
   7321                 MOZ_TO_RESULT_INVOKE_MEMBER(*borrowedStatement, GetInt32, 0));
   7322 
   7323  MOZ_ASSERT(freelistCount >= 0);
   7324 
   7325  return uint32_t(freelistCount);
   7326 }
   7327 
   7328 void DatabaseConnection::Close() {
   7329  AssertIsOnConnectionThread();
   7330  MOZ_ASSERT(!mDEBUGSavepointCount);
   7331  MOZ_DIAGNOSTIC_ASSERT(!mInWriteTransaction);
   7332 
   7333  AUTO_PROFILER_LABEL("DatabaseConnection::Close", DOM);
   7334 
   7335  if (mUpdateRefcountFunction) {
   7336    MOZ_ALWAYS_SUCCEEDS(
   7337        MutableStorageConnection().RemoveFunction("update_refcount"_ns));
   7338    mUpdateRefcountFunction = nullptr;
   7339  }
   7340 
   7341  CachingDatabaseConnection::Close();
   7342 
   7343  mFileManager.destroy();
   7344 }
   7345 
   7346 nsresult DatabaseConnection::DisableQuotaChecks() {
   7347  AssertIsOnConnectionThread();
   7348  MOZ_ASSERT(HasStorageConnection());
   7349 
   7350  if (!mQuotaObject) {
   7351    MOZ_ASSERT(!mJournalQuotaObject);
   7352 
   7353    QM_TRY(MOZ_TO_RESULT(MutableStorageConnection().GetQuotaObjects(
   7354        getter_AddRefs(mQuotaObject), getter_AddRefs(mJournalQuotaObject))));
   7355 
   7356    MOZ_ASSERT(mQuotaObject);
   7357    MOZ_ASSERT(mJournalQuotaObject);
   7358  }
   7359 
   7360  mQuotaObject->DisableQuotaCheck();
   7361  mJournalQuotaObject->DisableQuotaCheck();
   7362 
   7363  return NS_OK;
   7364 }
   7365 
   7366 void DatabaseConnection::EnableQuotaChecks() {
   7367  AssertIsOnConnectionThread();
   7368  if (!mQuotaObject) {
   7369    MOZ_ASSERT(!mJournalQuotaObject);
   7370 
   7371    // DisableQuotaChecks failed earlier, so we don't need to enable quota
   7372    // checks again.
   7373    return;
   7374  }
   7375 
   7376  MOZ_ASSERT(mJournalQuotaObject);
   7377 
   7378  const RefPtr<QuotaObject> quotaObject = std::move(mQuotaObject);
   7379  const RefPtr<QuotaObject> journalQuotaObject = std::move(mJournalQuotaObject);
   7380 
   7381  quotaObject->EnableQuotaCheck();
   7382  journalQuotaObject->EnableQuotaCheck();
   7383 
   7384  QM_TRY_INSPECT(const int64_t& fileSize, GetFileSize(quotaObject->Path()),
   7385                 QM_VOID);
   7386  QM_TRY_INSPECT(const int64_t& journalFileSize,
   7387                 GetFileSize(journalQuotaObject->Path()), QM_VOID);
   7388 
   7389  DebugOnly<bool> result = journalQuotaObject->MaybeUpdateSize(
   7390      journalFileSize, /* aTruncate */ true);
   7391  MOZ_ASSERT(result);
   7392 
   7393  result = quotaObject->MaybeUpdateSize(fileSize, /* aTruncate */ true);
   7394  MOZ_ASSERT(result);
   7395 }
   7396 
   7397 Result<int64_t, nsresult> DatabaseConnection::GetFileSize(
   7398    const nsAString& aPath) {
   7399  MOZ_ASSERT(!aPath.IsEmpty());
   7400 
   7401  QM_TRY_INSPECT(const auto& file, QM_NewLocalFile(aPath));
   7402  QM_TRY_INSPECT(const bool& exists, MOZ_TO_RESULT_INVOKE_MEMBER(file, Exists));
   7403 
   7404  if (exists) {
   7405    QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_MEMBER(file, GetFileSize));
   7406  }
   7407 
   7408  return 0;
   7409 }
   7410 
   7411 DatabaseConnection::AutoSavepoint::AutoSavepoint()
   7412    : mConnection(nullptr)
   7413 #ifdef DEBUG
   7414      ,
   7415      mDEBUGTransaction(nullptr)
   7416 #endif
   7417 {
   7418  MOZ_COUNT_CTOR(DatabaseConnection::AutoSavepoint);
   7419 }
   7420 
   7421 DatabaseConnection::AutoSavepoint::~AutoSavepoint() {
   7422  MOZ_COUNT_DTOR(DatabaseConnection::AutoSavepoint);
   7423 
   7424  if (mConnection) {
   7425    mConnection->AssertIsOnConnectionThread();
   7426    MOZ_ASSERT(mDEBUGTransaction);
   7427    MOZ_ASSERT(
   7428        mDEBUGTransaction->GetMode() == IDBTransaction::Mode::ReadWrite ||
   7429        mDEBUGTransaction->GetMode() == IDBTransaction::Mode::ReadWriteFlush ||
   7430        mDEBUGTransaction->GetMode() == IDBTransaction::Mode::Cleanup ||
   7431        mDEBUGTransaction->GetMode() == IDBTransaction::Mode::VersionChange);
   7432 
   7433    QM_WARNONLY_TRY(QM_TO_RESULT(mConnection->RollbackSavepoint()));
   7434  }
   7435 }
   7436 
   7437 nsresult DatabaseConnection::AutoSavepoint::Start(
   7438    const TransactionBase& aTransaction) {
   7439  MOZ_ASSERT(aTransaction.GetMode() == IDBTransaction::Mode::ReadWrite ||
   7440             aTransaction.GetMode() == IDBTransaction::Mode::ReadWriteFlush ||
   7441             aTransaction.GetMode() == IDBTransaction::Mode::Cleanup ||
   7442             aTransaction.GetMode() == IDBTransaction::Mode::VersionChange);
   7443 
   7444  DatabaseConnection* connection = aTransaction.GetDatabase().GetConnection();
   7445  MOZ_ASSERT(connection);
   7446  connection->AssertIsOnConnectionThread();
   7447 
   7448  // The previous operation failed to begin a write transaction and the
   7449  // following opertion jumped to the connection thread before the previous
   7450  // operation has updated its failure to the transaction.
   7451  if (!connection->GetUpdateRefcountFunction()) {
   7452    NS_WARNING(
   7453        "The connection was closed because the previous operation "
   7454        "failed!");
   7455    return NS_ERROR_DOM_INDEXEDDB_ABORT_ERR;
   7456  }
   7457 
   7458  MOZ_ASSERT(!mConnection);
   7459  MOZ_ASSERT(!mDEBUGTransaction);
   7460 
   7461  QM_TRY(MOZ_TO_RESULT(connection->StartSavepoint()));
   7462 
   7463  mConnection = connection;
   7464 #ifdef DEBUG
   7465  mDEBUGTransaction = &aTransaction;
   7466 #endif
   7467 
   7468  return NS_OK;
   7469 }
   7470 
   7471 nsresult DatabaseConnection::AutoSavepoint::Commit() {
   7472  MOZ_ASSERT(mConnection);
   7473  mConnection->AssertIsOnConnectionThread();
   7474  MOZ_ASSERT(mDEBUGTransaction);
   7475 
   7476  QM_TRY(MOZ_TO_RESULT(mConnection->ReleaseSavepoint()));
   7477 
   7478  mConnection = nullptr;
   7479 #ifdef DEBUG
   7480  mDEBUGTransaction = nullptr;
   7481 #endif
   7482 
   7483  return NS_OK;
   7484 }
   7485 
   7486 DatabaseConnection::UpdateRefcountFunction::UpdateRefcountFunction(
   7487    DatabaseConnection* const aConnection, DatabaseFileManager& aFileManager)
   7488    : mConnection(aConnection),
   7489      mFileManager(aFileManager),
   7490      mInSavepoint(false) {
   7491  MOZ_ASSERT(aConnection);
   7492  aConnection->AssertIsOnConnectionThread();
   7493 }
   7494 
   7495 nsresult DatabaseConnection::UpdateRefcountFunction::WillCommit() {
   7496  MOZ_ASSERT(mConnection);
   7497  mConnection->AssertIsOnConnectionThread();
   7498  MOZ_ASSERT(mConnection->HasStorageConnection());
   7499 
   7500  AUTO_PROFILER_LABEL("DatabaseConnection::UpdateRefcountFunction::WillCommit",
   7501                      DOM);
   7502 
   7503  // The parameter names are not used, parameters are bound by index
   7504  // only locally in the same function.
   7505  auto update =
   7506      [updateStatement = LazyStatement{*mConnection,
   7507                                       "UPDATE file "
   7508                                       "SET refcount = refcount + :delta "
   7509                                       "WHERE id = :id"_ns},
   7510       selectStatement = LazyStatement{*mConnection,
   7511                                       "SELECT id "
   7512                                       "FROM file "
   7513                                       "WHERE id = :id"_ns},
   7514       insertStatement =
   7515           LazyStatement{
   7516               *mConnection,
   7517               "INSERT INTO file (id, refcount) VALUES(:id, :delta)"_ns},
   7518       this](int64_t aId, int32_t aDelta) mutable -> Result<Ok, nsresult> {
   7519    AUTO_PROFILER_LABEL(
   7520        "DatabaseConnection::UpdateRefcountFunction::WillCommit::Update", DOM);
   7521    {
   7522      QM_TRY_INSPECT(const auto& borrowedUpdateStatement,
   7523                     updateStatement.Borrow());
   7524 
   7525      QM_TRY(
   7526          MOZ_TO_RESULT(borrowedUpdateStatement->BindInt32ByIndex(0, aDelta)));
   7527      QM_TRY(MOZ_TO_RESULT(borrowedUpdateStatement->BindInt64ByIndex(1, aId)));
   7528      QM_TRY(MOZ_TO_RESULT(borrowedUpdateStatement->Execute()));
   7529    }
   7530 
   7531    QM_TRY_INSPECT(
   7532        const int32_t& rows,
   7533        MOZ_TO_RESULT_INVOKE_MEMBER(mConnection->MutableStorageConnection(),
   7534                                    GetAffectedRows));
   7535 
   7536    if (rows > 0) {
   7537      QM_TRY_INSPECT(
   7538          const bool& hasResult,
   7539          selectStatement
   7540              .BorrowAndExecuteSingleStep(
   7541                  [aId](auto& stmt) -> Result<Ok, nsresult> {
   7542                    QM_TRY(MOZ_TO_RESULT(stmt.BindInt64ByIndex(0, aId)));
   7543                    return Ok{};
   7544                  })
   7545              .map(IsSome));
   7546 
   7547      if (!hasResult) {
   7548        // Don't have to create the journal here, we can create all at once,
   7549        // just before commit
   7550        mJournalsToCreateBeforeCommit.AppendElement(aId);
   7551      }
   7552 
   7553      return Ok{};
   7554    }
   7555 
   7556    QM_TRY_INSPECT(const auto& borrowedInsertStatement,
   7557                   insertStatement.Borrow());
   7558 
   7559    QM_TRY(MOZ_TO_RESULT(borrowedInsertStatement->BindInt64ByIndex(0, aId)));
   7560    QM_TRY(MOZ_TO_RESULT(borrowedInsertStatement->BindInt32ByIndex(1, aDelta)));
   7561    QM_TRY(MOZ_TO_RESULT(borrowedInsertStatement->Execute()));
   7562 
   7563    mJournalsToRemoveAfterCommit.AppendElement(aId);
   7564 
   7565    return Ok{};
   7566  };
   7567 
   7568  QM_TRY(CollectEachInRange(
   7569      mFileInfoEntries, [&update](const auto& entry) -> Result<Ok, nsresult> {
   7570        const auto delta = entry.GetData()->Delta();
   7571        if (delta) {
   7572          QM_TRY(update(entry.GetKey(), delta));
   7573        }
   7574 
   7575        return Ok{};
   7576      }));
   7577 
   7578  QM_TRY(MOZ_TO_RESULT(CreateJournals()));
   7579 
   7580  return NS_OK;
   7581 }
   7582 
   7583 void DatabaseConnection::UpdateRefcountFunction::DidCommit() {
   7584  MOZ_ASSERT(mConnection);
   7585  mConnection->AssertIsOnConnectionThread();
   7586 
   7587  AUTO_PROFILER_LABEL("DatabaseConnection::UpdateRefcountFunction::DidCommit",
   7588                      DOM);
   7589 
   7590  for (const auto& entry : mFileInfoEntries.Values()) {
   7591    entry->MaybeUpdateDBRefs();
   7592  }
   7593 
   7594  QM_WARNONLY_TRY(QM_TO_RESULT(RemoveJournals(mJournalsToRemoveAfterCommit)));
   7595 }
   7596 
   7597 void DatabaseConnection::UpdateRefcountFunction::DidAbort() {
   7598  MOZ_ASSERT(mConnection);
   7599  mConnection->AssertIsOnConnectionThread();
   7600 
   7601  AUTO_PROFILER_LABEL("DatabaseConnection::UpdateRefcountFunction::DidAbort",
   7602                      DOM);
   7603 
   7604  QM_WARNONLY_TRY(QM_TO_RESULT(RemoveJournals(mJournalsToRemoveAfterAbort)));
   7605 }
   7606 
   7607 void DatabaseConnection::UpdateRefcountFunction::StartSavepoint() {
   7608  MOZ_ASSERT(mConnection);
   7609  mConnection->AssertIsOnConnectionThread();
   7610  MOZ_ASSERT(!mInSavepoint);
   7611  MOZ_ASSERT(!mSavepointEntriesIndex.Count());
   7612 
   7613  mInSavepoint = true;
   7614 }
   7615 
   7616 void DatabaseConnection::UpdateRefcountFunction::ReleaseSavepoint() {
   7617  MOZ_ASSERT(mConnection);
   7618  mConnection->AssertIsOnConnectionThread();
   7619  MOZ_ASSERT(mInSavepoint);
   7620 
   7621  mSavepointEntriesIndex.Clear();
   7622  mInSavepoint = false;
   7623 }
   7624 
   7625 void DatabaseConnection::UpdateRefcountFunction::RollbackSavepoint() {
   7626  MOZ_ASSERT(mConnection);
   7627  mConnection->AssertIsOnConnectionThread();
   7628  MOZ_ASSERT(!IsOnBackgroundThread());
   7629  MOZ_ASSERT(mInSavepoint);
   7630 
   7631  for (const auto& entry : mSavepointEntriesIndex.Values()) {
   7632    entry->DecBySavepointDelta();
   7633  }
   7634 
   7635  mInSavepoint = false;
   7636  mSavepointEntriesIndex.Clear();
   7637 }
   7638 
   7639 void DatabaseConnection::UpdateRefcountFunction::Reset() {
   7640  MOZ_ASSERT(mConnection);
   7641  mConnection->AssertIsOnConnectionThread();
   7642  MOZ_ASSERT(!mSavepointEntriesIndex.Count());
   7643  MOZ_ASSERT(!mInSavepoint);
   7644 
   7645  mJournalsToCreateBeforeCommit.Clear();
   7646  mJournalsToRemoveAfterCommit.Clear();
   7647  mJournalsToRemoveAfterAbort.Clear();
   7648 
   7649  // DatabaseFileInfo implementation automatically removes unreferenced files,
   7650  // but it's done asynchronously and with a delay. We want to remove them (and
   7651  // decrease quota usage) before we fire the commit event.
   7652  for (const auto& entry : mFileInfoEntries.Values()) {
   7653    // We need to move mFileInfo into a raw pointer in order to release it
   7654    // explicitly with aSyncDeleteFile == true.
   7655    DatabaseFileInfo* const fileInfo = entry->ReleaseFileInfo().forget().take();
   7656    MOZ_ASSERT(fileInfo);
   7657 
   7658    fileInfo->Release(/* aSyncDeleteFile */ true);
   7659  }
   7660 
   7661  mFileInfoEntries.Clear();
   7662 }
   7663 
   7664 nsresult DatabaseConnection::UpdateRefcountFunction::ProcessValue(
   7665    mozIStorageValueArray* aValues, int32_t aIndex, UpdateType aUpdateType) {
   7666  MOZ_ASSERT(mConnection);
   7667  mConnection->AssertIsOnConnectionThread();
   7668  MOZ_ASSERT(aValues);
   7669 
   7670  AUTO_PROFILER_LABEL(
   7671      "DatabaseConnection::UpdateRefcountFunction::ProcessValue", DOM);
   7672 
   7673  QM_TRY_INSPECT(const int32_t& type,
   7674                 MOZ_TO_RESULT_INVOKE_MEMBER(aValues, GetTypeOfIndex, aIndex));
   7675 
   7676  if (type == mozIStorageValueArray::VALUE_TYPE_NULL) {
   7677    return NS_OK;
   7678  }
   7679 
   7680  QM_TRY_INSPECT(const auto& ids, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
   7681                                      nsString, aValues, GetString, aIndex));
   7682 
   7683  QM_TRY_INSPECT(const auto& files,
   7684                 DeserializeStructuredCloneFiles(mFileManager, ids));
   7685 
   7686  for (const StructuredCloneFileParent& file : files) {
   7687    const int64_t id = file.FileInfo().Id();
   7688    MOZ_ASSERT(id > 0);
   7689 
   7690    const auto entry =
   7691        WrapNotNull(mFileInfoEntries.GetOrInsertNew(id, file.FileInfoPtr()));
   7692 
   7693    if (mInSavepoint) {
   7694      mSavepointEntriesIndex.InsertOrUpdate(id, entry);
   7695    }
   7696 
   7697    switch (aUpdateType) {
   7698      case UpdateType::Increment:
   7699        entry->IncDeltas(mInSavepoint);
   7700        break;
   7701      case UpdateType::Decrement:
   7702        entry->DecDeltas(mInSavepoint);
   7703        break;
   7704      default:
   7705        MOZ_CRASH("Unknown update type!");
   7706    }
   7707  }
   7708 
   7709  return NS_OK;
   7710 }
   7711 
   7712 nsresult DatabaseConnection::UpdateRefcountFunction::CreateJournals() {
   7713  MOZ_ASSERT(mConnection);
   7714  mConnection->AssertIsOnConnectionThread();
   7715 
   7716  AUTO_PROFILER_LABEL(
   7717      "DatabaseConnection::UpdateRefcountFunction::CreateJournals", DOM);
   7718 
   7719  const nsCOMPtr<nsIFile> journalDirectory = mFileManager.GetJournalDirectory();
   7720  QM_TRY(OkIf(journalDirectory), NS_ERROR_FAILURE);
   7721 
   7722  for (const int64_t id : mJournalsToCreateBeforeCommit) {
   7723    const nsCOMPtr<nsIFile> file =
   7724        DatabaseFileManager::GetFileForId(journalDirectory, id);
   7725    QM_TRY(OkIf(file), NS_ERROR_FAILURE);
   7726 
   7727    QM_TRY(MOZ_TO_RESULT(file->Create(nsIFile::NORMAL_FILE_TYPE, 0644)));
   7728 
   7729    mJournalsToRemoveAfterAbort.AppendElement(id);
   7730  }
   7731 
   7732  return NS_OK;
   7733 }
   7734 
   7735 nsresult DatabaseConnection::UpdateRefcountFunction::RemoveJournals(
   7736    const nsTArray<int64_t>& aJournals) {
   7737  MOZ_ASSERT(mConnection);
   7738  mConnection->AssertIsOnConnectionThread();
   7739 
   7740  AUTO_PROFILER_LABEL(
   7741      "DatabaseConnection::UpdateRefcountFunction::RemoveJournals", DOM);
   7742 
   7743  nsCOMPtr<nsIFile> journalDirectory = mFileManager.GetJournalDirectory();
   7744  QM_TRY(OkIf(journalDirectory), NS_ERROR_FAILURE);
   7745 
   7746  for (const auto& journal : aJournals) {
   7747    nsCOMPtr<nsIFile> file =
   7748        DatabaseFileManager::GetFileForId(journalDirectory, journal);
   7749    QM_TRY(OkIf(file), NS_ERROR_FAILURE);
   7750 
   7751    QM_WARNONLY_TRY(QM_TO_RESULT(file->Remove(false)));
   7752  }
   7753 
   7754  return NS_OK;
   7755 }
   7756 
   7757 NS_IMPL_ISUPPORTS(DatabaseConnection::UpdateRefcountFunction,
   7758                  mozIStorageFunction)
   7759 
   7760 NS_IMETHODIMP
   7761 DatabaseConnection::UpdateRefcountFunction::OnFunctionCall(
   7762    mozIStorageValueArray* aValues, nsIVariant** _retval) {
   7763  MOZ_ASSERT(aValues);
   7764  MOZ_ASSERT(_retval);
   7765 
   7766  AUTO_PROFILER_LABEL(
   7767      "DatabaseConnection::UpdateRefcountFunction::OnFunctionCall", DOM);
   7768 
   7769 #ifdef DEBUG
   7770  {
   7771    QM_TRY_INSPECT(const uint32_t& numEntries,
   7772                   MOZ_TO_RESULT_INVOKE_MEMBER(aValues, GetNumEntries),
   7773                   QM_ASSERT_UNREACHABLE);
   7774 
   7775    MOZ_ASSERT(numEntries == 2);
   7776 
   7777    QM_TRY_INSPECT(const int32_t& type1,
   7778                   MOZ_TO_RESULT_INVOKE_MEMBER(aValues, GetTypeOfIndex, 0),
   7779                   QM_ASSERT_UNREACHABLE);
   7780 
   7781    QM_TRY_INSPECT(const int32_t& type2,
   7782                   MOZ_TO_RESULT_INVOKE_MEMBER(aValues, GetTypeOfIndex, 1),
   7783                   QM_ASSERT_UNREACHABLE);
   7784 
   7785    MOZ_ASSERT(!(type1 == mozIStorageValueArray::VALUE_TYPE_NULL &&
   7786                 type2 == mozIStorageValueArray::VALUE_TYPE_NULL));
   7787  }
   7788 #endif
   7789 
   7790  QM_TRY(MOZ_TO_RESULT(ProcessValue(aValues, 0, UpdateType::Decrement)));
   7791 
   7792  QM_TRY(MOZ_TO_RESULT(ProcessValue(aValues, 1, UpdateType::Increment)));
   7793 
   7794  return NS_OK;
   7795 }
   7796 
   7797 /*******************************************************************************
   7798 * ConnectionPool implementation
   7799 ******************************************************************************/
   7800 
   7801 ConnectionPool::ConnectionPool()
   7802    : mDatabasesMutex("ConnectionPool::mDatabasesMutex"),
   7803      mIOTarget(MakeConnectionIOTarget()),
   7804      mIdleTimer(NS_NewTimer()),
   7805      mNextTransactionId(0) {
   7806  AssertIsOnOwningThread();
   7807  AssertIsOnBackgroundThread();
   7808  MOZ_ASSERT(mIdleTimer);
   7809 }
   7810 
   7811 ConnectionPool::~ConnectionPool() {
   7812  AssertIsOnOwningThread();
   7813  MOZ_ASSERT(mIdleDatabases.IsEmpty());
   7814  MOZ_ASSERT(!mIdleTimer);
   7815  MOZ_ASSERT(mTargetIdleTime.IsNull());
   7816  MOZ_ASSERT(!mDatabases.Count());
   7817  MOZ_ASSERT(!mTransactions.Count());
   7818  MOZ_ASSERT(mQueuedTransactions.IsEmpty());
   7819  MOZ_ASSERT(mCompleteCallbacks.IsEmpty());
   7820  MOZ_ASSERT(mShutdownRequested);
   7821  MOZ_ASSERT(mShutdownComplete);
   7822 }
   7823 
   7824 // static
   7825 void ConnectionPool::IdleTimerCallback(nsITimer* aTimer, void* aClosure) {
   7826  MOZ_ASSERT(aTimer);
   7827  MOZ_ASSERT(aClosure);
   7828 
   7829  AUTO_PROFILER_LABEL("ConnectionPool::IdleTimerCallback", DOM);
   7830 
   7831  auto& self = *static_cast<ConnectionPool*>(aClosure);
   7832  MOZ_ASSERT(self.mIdleTimer);
   7833  MOZ_ASSERT(SameCOMIdentity(self.mIdleTimer, aTimer));
   7834  MOZ_ASSERT(!self.mTargetIdleTime.IsNull());
   7835 
   7836  self.mTargetIdleTime = TimeStamp();
   7837 
   7838  // Cheat a little.
   7839  const TimeStamp now =
   7840      TimeStamp::NowLoRes() + TimeDuration::FromMilliseconds(500);
   7841 
   7842  // XXX Move this to ArrayAlgorithm.h?
   7843  const auto removeUntil = [](auto& array, auto&& cond) {
   7844    const auto begin = array.begin(), end = array.end();
   7845    array.RemoveElementsRange(
   7846        begin, std::find_if(begin, end, std::forward<decltype(cond)>(cond)));
   7847  };
   7848 
   7849  removeUntil(self.mIdleDatabases, [now, &self](const auto& info) {
   7850    if (now >= info.mIdleTime) {
   7851      if ((*info.mDatabaseInfo)->mIdle) {
   7852        self.PerformIdleDatabaseMaintenance(*info.mDatabaseInfo.ref());
   7853      } else {
   7854        self.CloseDatabase(*info.mDatabaseInfo.ref());
   7855      }
   7856 
   7857      return false;
   7858    }
   7859 
   7860    return true;
   7861  });
   7862 
   7863  self.AdjustIdleTimer();
   7864 }
   7865 
   7866 Result<RefPtr<DatabaseConnection>, nsresult>
   7867 ConnectionPool::GetOrCreateConnection(const Database& aDatabase) {
   7868  MOZ_ASSERT(!NS_IsMainThread());
   7869  MOZ_ASSERT(!IsOnBackgroundThread());
   7870 
   7871  AUTO_PROFILER_LABEL("ConnectionPool::GetOrCreateConnection", DOM);
   7872 
   7873  DatabaseInfo* dbInfo;
   7874  {
   7875    MutexAutoLock lock(mDatabasesMutex);
   7876 
   7877    dbInfo = mDatabases.Get(aDatabase.Id());
   7878  }
   7879 
   7880  MOZ_ASSERT(dbInfo);
   7881 
   7882  if (dbInfo->mConnection) {
   7883    dbInfo->AssertIsOnConnectionThread();
   7884 
   7885    return dbInfo->mConnection;
   7886  }
   7887 
   7888  MOZ_ASSERT(!dbInfo->mDEBUGConnectionEventTarget);
   7889 
   7890  QM_TRY_UNWRAP(
   7891      MovingNotNull<nsCOMPtr<mozIStorageConnection>> storageConnection,
   7892      GetStorageConnection(aDatabase.FilePath(), aDatabase.DirectoryLockId(),
   7893                           aDatabase.TelemetryId(), aDatabase.MaybeKeyRef()));
   7894 
   7895  RefPtr<DatabaseConnection> connection = new DatabaseConnection(
   7896      std::move(storageConnection), aDatabase.GetFileManagerPtr());
   7897 
   7898  QM_TRY(MOZ_TO_RESULT(connection->Init()));
   7899 
   7900  dbInfo->mConnection = connection;
   7901 
   7902  IDB_DEBUG_LOG(("ConnectionPool created connection 0x%p for '%s'",
   7903                 dbInfo->mConnection.get(),
   7904                 NS_ConvertUTF16toUTF8(aDatabase.FilePath()).get()));
   7905 
   7906 #ifdef DEBUG
   7907  dbInfo->mDEBUGConnectionEventTarget = GetCurrentSerialEventTarget();
   7908 #endif
   7909 
   7910  return connection;
   7911 }
   7912 
   7913 uint64_t ConnectionPool::Start(
   7914    const nsID& aBackgroundChildLoggingId, const nsACString& aDatabaseId,
   7915    int64_t aLoggingSerialNumber, const nsTArray<nsString>& aObjectStoreNames,
   7916    bool aIsWriteTransaction,
   7917    TransactionDatabaseOperationBase* aTransactionOp) {
   7918  AssertIsOnOwningThread();
   7919  MOZ_ASSERT(!aDatabaseId.IsEmpty());
   7920  MOZ_ASSERT(mNextTransactionId < UINT64_MAX);
   7921  MOZ_ASSERT(!mShutdownRequested);
   7922 
   7923  AUTO_PROFILER_LABEL("ConnectionPool::Start", DOM);
   7924 
   7925  const uint64_t transactionId = ++mNextTransactionId;
   7926 
   7927  // To avoid always acquiring a lock, we don't use WithEntryHandle here, which
   7928  // would require a lock in any case.
   7929  DatabaseInfo* dbInfo = mDatabases.Get(aDatabaseId);
   7930 
   7931  const bool databaseInfoIsNew = !dbInfo;
   7932 
   7933  if (databaseInfoIsNew) {
   7934    MutexAutoLock lock(mDatabasesMutex);
   7935 
   7936    dbInfo = mDatabases
   7937                 .InsertOrUpdate(aDatabaseId,
   7938                                 MakeUnique<DatabaseInfo>(this, aDatabaseId))
   7939                 .get();
   7940  }
   7941 
   7942  MOZ_ASSERT(!mTransactions.Contains(transactionId));
   7943  auto& transactionInfo = *mTransactions.InsertOrUpdate(
   7944      transactionId, MakeUnique<TransactionInfo>(
   7945                         *dbInfo, aBackgroundChildLoggingId, aDatabaseId,
   7946                         transactionId, aLoggingSerialNumber, aObjectStoreNames,
   7947                         aIsWriteTransaction, aTransactionOp));
   7948 
   7949  if (aIsWriteTransaction) {
   7950    MOZ_ASSERT(dbInfo->mWriteTransactionCount < UINT32_MAX);
   7951    dbInfo->mWriteTransactionCount++;
   7952  } else {
   7953    MOZ_ASSERT(dbInfo->mReadTransactionCount < UINT32_MAX);
   7954    dbInfo->mReadTransactionCount++;
   7955  }
   7956 
   7957  auto& blockingTransactions = dbInfo->mBlockingTransactions;
   7958 
   7959  for (const nsAString& objectStoreName : aObjectStoreNames) {
   7960    TransactionInfoPair* blockInfo =
   7961        blockingTransactions.GetOrInsertNew(objectStoreName);
   7962 
   7963    // Mark what we are blocking on.
   7964    if (const auto maybeBlockingRead = blockInfo->mLastBlockingReads) {
   7965      transactionInfo.mBlockedOn.Insert(&maybeBlockingRead.ref());
   7966      maybeBlockingRead->AddBlockingTransaction(transactionInfo);
   7967    }
   7968 
   7969    if (aIsWriteTransaction) {
   7970      for (const auto blockingWrite : blockInfo->mLastBlockingWrites) {
   7971        transactionInfo.mBlockedOn.Insert(blockingWrite);
   7972        blockingWrite->AddBlockingTransaction(transactionInfo);
   7973      }
   7974 
   7975      blockInfo->mLastBlockingReads = SomeRef(transactionInfo);
   7976      blockInfo->mLastBlockingWrites.Clear();
   7977    } else {
   7978      blockInfo->mLastBlockingWrites.AppendElement(
   7979          WrapNotNullUnchecked(&transactionInfo));
   7980    }
   7981  }
   7982 
   7983  if (!transactionInfo.mBlockedOn.Count()) {
   7984    (void)ScheduleTransaction(transactionInfo,
   7985                              /* aFromQueuedTransactions */ false);
   7986  }
   7987 
   7988  if (!databaseInfoIsNew &&
   7989      (mIdleDatabases.RemoveElement(dbInfo) ||
   7990       mDatabasesPerformingIdleMaintenance.RemoveElement(dbInfo))) {
   7991    AdjustIdleTimer();
   7992  }
   7993 
   7994  return transactionId;
   7995 }
   7996 
   7997 void ConnectionPool::StartOp(uint64_t aTransactionId,
   7998                             nsCOMPtr<nsIRunnable> aRunnable) {
   7999  AssertIsOnOwningThread();
   8000 
   8001  AUTO_PROFILER_LABEL("ConnectionPool::StartOp", DOM);
   8002 
   8003  auto* const transactionInfo = mTransactions.Get(aTransactionId);
   8004  MOZ_ASSERT(transactionInfo);
   8005 
   8006  transactionInfo->StartOp(std::move(aRunnable));
   8007 }
   8008 
   8009 void ConnectionPool::FinishOp(uint64_t aTransactionId) {
   8010  AssertIsOnOwningThread();
   8011 
   8012  AUTO_PROFILER_LABEL("ConnectionPool::FinishOp", DOM);
   8013 
   8014  auto* const transactionInfo = mTransactions.Get(aTransactionId);
   8015  MOZ_ASSERT(transactionInfo);
   8016 
   8017  transactionInfo->FinishOp();
   8018 }
   8019 
   8020 void ConnectionPool::Finish(uint64_t aTransactionId,
   8021                            FinishCallback* aCallback) {
   8022  AssertIsOnOwningThread();
   8023 
   8024 #ifdef DEBUG
   8025  auto* const transactionInfo = mTransactions.Get(aTransactionId);
   8026  MOZ_ASSERT(transactionInfo);
   8027  MOZ_ASSERT(!transactionInfo->mFinished);
   8028 #endif
   8029 
   8030  AUTO_PROFILER_LABEL("ConnectionPool::Finish", DOM);
   8031 
   8032  nsCOMPtr<nsIRunnable> wrapper =
   8033      new FinishCallbackWrapper(this, aTransactionId, aCallback);
   8034 
   8035  StartOp(aTransactionId, std::move(wrapper));
   8036 
   8037 #ifdef DEBUG
   8038  transactionInfo->mFinished.Flip();
   8039 #endif
   8040 }
   8041 
   8042 void ConnectionPool::WaitForDatabaseToComplete(const nsCString& aDatabaseId,
   8043                                               nsIRunnable* aCallback) {
   8044  AssertIsOnOwningThread();
   8045  MOZ_ASSERT(!aDatabaseId.IsEmpty());
   8046  MOZ_ASSERT(aCallback);
   8047 
   8048  AUTO_PROFILER_LABEL("ConnectionPool::WaitForDatabaseToComplete", DOM);
   8049 
   8050  if (!CloseDatabaseWhenIdleInternal(aDatabaseId)) {
   8051    (void)aCallback->Run();
   8052    return;
   8053  }
   8054 
   8055  mCompleteCallbacks.EmplaceBack(
   8056      MakeUnique<DatabaseCompleteCallback>(aDatabaseId, aCallback));
   8057 }
   8058 
   8059 void ConnectionPool::Shutdown() {
   8060  AssertIsOnOwningThread();
   8061  MOZ_ASSERT(!mShutdownComplete);
   8062 
   8063  AUTO_PROFILER_LABEL("ConnectionPool::Shutdown", DOM);
   8064 
   8065  mShutdownRequested.Flip();
   8066 
   8067  CancelIdleTimer();
   8068  MOZ_ASSERT(mTargetIdleTime.IsNull());
   8069 
   8070  mIdleTimer = nullptr;
   8071 
   8072  CloseIdleDatabases();
   8073 
   8074  if (!mDatabases.Count()) {
   8075    MOZ_ASSERT(!mTransactions.Count());
   8076 
   8077    Cleanup();
   8078 
   8079    MOZ_ASSERT(mShutdownComplete);
   8080 
   8081    mIOTarget->Shutdown();
   8082 
   8083    return;
   8084  }
   8085 
   8086  MOZ_ALWAYS_TRUE(SpinEventLoopUntil("ConnectionPool::Shutdown"_ns, [&]() {
   8087    return static_cast<bool>(mShutdownComplete);
   8088  }));
   8089 
   8090  mIOTarget->Shutdown();
   8091 }
   8092 
   8093 void ConnectionPool::Cleanup() {
   8094  AssertIsOnOwningThread();
   8095  MOZ_ASSERT(mShutdownRequested);
   8096  MOZ_ASSERT(!mShutdownComplete);
   8097  MOZ_ASSERT(!mDatabases.Count());
   8098  MOZ_ASSERT(!mTransactions.Count());
   8099 
   8100  AUTO_PROFILER_LABEL("ConnectionPool::Cleanup", DOM);
   8101 
   8102  if (!mCompleteCallbacks.IsEmpty()) {
   8103    // Run all callbacks manually now.
   8104 
   8105    {
   8106      auto completeCallbacks = std::move(mCompleteCallbacks);
   8107      for (const auto& completeCallback : completeCallbacks) {
   8108        MOZ_ASSERT(completeCallback);
   8109        MOZ_ASSERT(completeCallback->mCallback);
   8110 
   8111        (void)completeCallback->mCallback->Run();
   8112      }
   8113 
   8114      // We expect no new callbacks being completed by running the existing
   8115      // ones.
   8116      MOZ_ASSERT(mCompleteCallbacks.IsEmpty());
   8117    }
   8118 
   8119    // And make sure they get processed.
   8120    nsIThread* currentThread = NS_GetCurrentThread();
   8121    MOZ_ASSERT(currentThread);
   8122 
   8123    MOZ_ALWAYS_SUCCEEDS(NS_ProcessPendingEvents(currentThread));
   8124  }
   8125 
   8126  mShutdownComplete.Flip();
   8127 }
   8128 
   8129 void ConnectionPool::AdjustIdleTimer() {
   8130  AssertIsOnOwningThread();
   8131  MOZ_ASSERT(mIdleTimer);
   8132 
   8133  AUTO_PROFILER_LABEL("ConnectionPool::AdjustIdleTimer", DOM);
   8134 
   8135  // Figure out the next time at which we should release idle resources. This
   8136  // includes both databases and threads.
   8137  TimeStamp newTargetIdleTime;
   8138  MOZ_ASSERT(newTargetIdleTime.IsNull());
   8139 
   8140  if (!mIdleDatabases.IsEmpty()) {
   8141    newTargetIdleTime = mIdleDatabases[0].mIdleTime;
   8142  }
   8143 
   8144  MOZ_ASSERT_IF(newTargetIdleTime.IsNull(), mIdleDatabases.IsEmpty());
   8145 
   8146  // Cancel the timer if it was running and the new target time is different.
   8147  if (!mTargetIdleTime.IsNull() &&
   8148      (newTargetIdleTime.IsNull() || mTargetIdleTime != newTargetIdleTime)) {
   8149    CancelIdleTimer();
   8150 
   8151    MOZ_ASSERT(mTargetIdleTime.IsNull());
   8152  }
   8153 
   8154  // Schedule the timer if we have a target time different than before.
   8155  if (!newTargetIdleTime.IsNull() &&
   8156      (mTargetIdleTime.IsNull() || mTargetIdleTime != newTargetIdleTime)) {
   8157    double delta = (newTargetIdleTime - TimeStamp::NowLoRes()).ToMilliseconds();
   8158 
   8159    uint32_t delay;
   8160    if (delta > 0) {
   8161      delay = uint32_t(std::min(delta, double(UINT32_MAX)));
   8162    } else {
   8163      delay = 0;
   8164    }
   8165 
   8166    MOZ_ALWAYS_SUCCEEDS(mIdleTimer->InitWithNamedFuncCallback(
   8167        IdleTimerCallback, this, delay, nsITimer::TYPE_ONE_SHOT,
   8168        "ConnectionPool::IdleTimerCallback"_ns));
   8169 
   8170    mTargetIdleTime = newTargetIdleTime;
   8171  }
   8172 }
   8173 
   8174 void ConnectionPool::CancelIdleTimer() {
   8175  AssertIsOnOwningThread();
   8176  MOZ_ASSERT(mIdleTimer);
   8177 
   8178  if (!mTargetIdleTime.IsNull()) {
   8179    MOZ_ALWAYS_SUCCEEDS(mIdleTimer->Cancel());
   8180 
   8181    mTargetIdleTime = TimeStamp();
   8182    MOZ_ASSERT(mTargetIdleTime.IsNull());
   8183  }
   8184 }
   8185 
   8186 void ConnectionPool::CloseIdleDatabases() {
   8187  AssertIsOnOwningThread();
   8188  MOZ_ASSERT(mShutdownRequested);
   8189 
   8190  AUTO_PROFILER_LABEL("ConnectionPool::CloseIdleDatabases", DOM);
   8191 
   8192  if (!mIdleDatabases.IsEmpty()) {
   8193    for (IdleDatabaseInfo& idleInfo : mIdleDatabases) {
   8194      CloseDatabase(*idleInfo.mDatabaseInfo.ref());
   8195    }
   8196    mIdleDatabases.Clear();
   8197  }
   8198 
   8199  if (!mDatabasesPerformingIdleMaintenance.IsEmpty()) {
   8200    for (PerformingIdleMaintenanceDatabaseInfo& performingIdleMaintenanceInfo :
   8201         mDatabasesPerformingIdleMaintenance) {
   8202      CloseDatabase(*performingIdleMaintenanceInfo.mDatabaseInfo);
   8203    }
   8204    mDatabasesPerformingIdleMaintenance.Clear();
   8205  }
   8206 }
   8207 
   8208 bool ConnectionPool::ScheduleTransaction(TransactionInfo& aTransactionInfo,
   8209                                         bool aFromQueuedTransactions) {
   8210  AssertIsOnOwningThread();
   8211 
   8212  AUTO_PROFILER_LABEL("ConnectionPool::ScheduleTransaction", DOM);
   8213 
   8214  DatabaseInfo& dbInfo = aTransactionInfo.mDatabaseInfo;
   8215 
   8216  dbInfo.mIdle = false;
   8217 
   8218  if (dbInfo.mClosing) {
   8219    MOZ_ASSERT(!mIdleDatabases.Contains(&dbInfo));
   8220    MOZ_ASSERT(
   8221        !dbInfo.mTransactionsScheduledDuringClose.Contains(&aTransactionInfo));
   8222 
   8223    dbInfo.mTransactionsScheduledDuringClose.AppendElement(
   8224        WrapNotNullUnchecked(&aTransactionInfo));
   8225    return true;
   8226  }
   8227 
   8228  if (!dbInfo.mEventTarget) {
   8229    const uint32_t serialNumber = SerialNumber();
   8230    const nsCString serialName =
   8231        nsPrintfCString("IndexedDB #%" PRIu32, serialNumber);
   8232 
   8233    dbInfo.mEventTarget =
   8234        TaskQueue::Create(do_AddRef(mIOTarget), serialName.get());
   8235    MOZ_ASSERT(dbInfo.mEventTarget);
   8236    IDB_DEBUG_LOG(("ConnectionPool created task queue %" PRIu32, serialNumber));
   8237  }
   8238 
   8239  // The number of active operations equals the number of databases minus idle
   8240  // databases. The maximum number of database operations which can make
   8241  // progress at the same time is kMaxConnectionThreadCount. If we are at this
   8242  // limit, all idle processing is interrupted to make room for user
   8243  // transactions.
   8244  if (mDatabases.Count() >=
   8245          (mIdleDatabases.Length() + kMaxConnectionThreadCount) &&
   8246      !mDatabasesPerformingIdleMaintenance.IsEmpty()) {
   8247    const auto& busyDbs = mDatabasesPerformingIdleMaintenance;
   8248    for (auto dbInfo = busyDbs.rbegin(); dbInfo != busyDbs.rend(); ++dbInfo) {
   8249      (*dbInfo).mIdleConnectionRunnable->Interrupt();
   8250    }
   8251  }
   8252 
   8253  if (aTransactionInfo.mIsWriteTransaction) {
   8254    if (dbInfo.mRunningWriteTransaction) {
   8255      // SQLite only allows one write transaction at a time so queue this
   8256      // transaction for later.
   8257      MOZ_ASSERT(
   8258          !dbInfo.mScheduledWriteTransactions.Contains(&aTransactionInfo));
   8259 
   8260      dbInfo.mScheduledWriteTransactions.AppendElement(
   8261          WrapNotNullUnchecked(&aTransactionInfo));
   8262      return true;
   8263    }
   8264 
   8265    dbInfo.mRunningWriteTransaction = SomeRef(aTransactionInfo);
   8266    dbInfo.mNeedsCheckpoint = true;
   8267  }
   8268 
   8269  aTransactionInfo.SetRunning();
   8270 
   8271  return true;
   8272 }
   8273 
   8274 void ConnectionPool::NoteFinishedTransaction(uint64_t aTransactionId) {
   8275  AssertIsOnOwningThread();
   8276 
   8277  AUTO_PROFILER_LABEL("ConnectionPool::NoteFinishedTransaction", DOM);
   8278 
   8279  auto* const transactionInfo = mTransactions.Get(aTransactionId);
   8280  MOZ_ASSERT(transactionInfo);
   8281  MOZ_ASSERT(transactionInfo->mRunning);
   8282  MOZ_ASSERT(transactionInfo->mFinished);
   8283 
   8284  transactionInfo->mRunning = false;
   8285 
   8286  DatabaseInfo& dbInfo = transactionInfo->mDatabaseInfo;
   8287  MOZ_ASSERT(mDatabases.Get(transactionInfo->mDatabaseId) == &dbInfo);
   8288  MOZ_ASSERT(dbInfo.mEventTarget);
   8289 
   8290  // Schedule the next write transaction if there are any queued.
   8291  if (dbInfo.mRunningWriteTransaction &&
   8292      dbInfo.mRunningWriteTransaction.refEquals(*transactionInfo)) {
   8293    MOZ_ASSERT(transactionInfo->mIsWriteTransaction);
   8294    MOZ_ASSERT(dbInfo.mNeedsCheckpoint);
   8295 
   8296    dbInfo.mRunningWriteTransaction = Nothing();
   8297 
   8298    if (!dbInfo.mScheduledWriteTransactions.IsEmpty()) {
   8299      const auto nextWriteTransaction = dbInfo.mScheduledWriteTransactions[0];
   8300 
   8301      dbInfo.mScheduledWriteTransactions.RemoveElementAt(0);
   8302 
   8303      MOZ_ALWAYS_TRUE(ScheduleTransaction(*nextWriteTransaction,
   8304                                          /* aFromQueuedTransactions */ false));
   8305    }
   8306  }
   8307 
   8308  for (const auto& objectStoreName : transactionInfo->mObjectStoreNames) {
   8309    TransactionInfoPair* blockInfo =
   8310        dbInfo.mBlockingTransactions.Get(objectStoreName);
   8311    MOZ_ASSERT(blockInfo);
   8312 
   8313    if (transactionInfo->mIsWriteTransaction && blockInfo->mLastBlockingReads &&
   8314        blockInfo->mLastBlockingReads.refEquals(*transactionInfo)) {
   8315      blockInfo->mLastBlockingReads = Nothing();
   8316    }
   8317 
   8318    blockInfo->mLastBlockingWrites.RemoveElement(transactionInfo);
   8319  }
   8320 
   8321  transactionInfo->RemoveBlockingTransactions();
   8322 
   8323  if (transactionInfo->mIsWriteTransaction) {
   8324    MOZ_ASSERT(dbInfo.mWriteTransactionCount);
   8325    dbInfo.mWriteTransactionCount--;
   8326  } else {
   8327    MOZ_ASSERT(dbInfo.mReadTransactionCount);
   8328    dbInfo.mReadTransactionCount--;
   8329  }
   8330 
   8331  mTransactions.Remove(aTransactionId);
   8332 
   8333  if (!dbInfo.TotalTransactionCount()) {
   8334    MOZ_ASSERT(!dbInfo.mIdle);
   8335    dbInfo.mIdle = true;
   8336 
   8337    NoteIdleDatabase(dbInfo);
   8338  }
   8339 }
   8340 
   8341 void ConnectionPool::ScheduleQueuedTransactions() {
   8342  AssertIsOnOwningThread();
   8343  MOZ_ASSERT(!mQueuedTransactions.IsEmpty());
   8344 
   8345  AUTO_PROFILER_LABEL("ConnectionPool::ScheduleQueuedTransactions", DOM);
   8346 
   8347  const auto foundIt = std::find_if(
   8348      mQueuedTransactions.begin(), mQueuedTransactions.end(),
   8349      [&me = *this](const auto& queuedTransaction) {
   8350        return !me.ScheduleTransaction(*queuedTransaction,
   8351                                       /* aFromQueuedTransactions */ true);
   8352      });
   8353 
   8354  mQueuedTransactions.RemoveElementsRange(mQueuedTransactions.begin(), foundIt);
   8355 
   8356  AdjustIdleTimer();
   8357 }
   8358 
   8359 void ConnectionPool::NoteIdleDatabase(DatabaseInfo& aDatabaseInfo) {
   8360  AssertIsOnOwningThread();
   8361  MOZ_ASSERT(!aDatabaseInfo.TotalTransactionCount());
   8362  MOZ_ASSERT(aDatabaseInfo.mEventTarget);
   8363  MOZ_ASSERT(!mIdleDatabases.Contains(&aDatabaseInfo));
   8364 
   8365  AUTO_PROFILER_LABEL("ConnectionPool::NoteIdleDatabase", DOM);
   8366 
   8367  const bool otherDatabasesWaiting = !mQueuedTransactions.IsEmpty();
   8368 
   8369  // We check mShutdownRequested because when it is true, mIdleTimer is null.
   8370  if (mShutdownRequested || otherDatabasesWaiting ||
   8371      aDatabaseInfo.mCloseOnIdle) {
   8372    // Make sure we close the connection if we're shutting down or giving the
   8373    // thread to another database.
   8374    CloseDatabase(aDatabaseInfo);
   8375 
   8376    if (otherDatabasesWaiting) {
   8377      ScheduleQueuedTransactions();
   8378    }
   8379 
   8380    return;
   8381  }
   8382 
   8383  mIdleDatabases.InsertElementSorted(IdleDatabaseInfo{aDatabaseInfo});
   8384 
   8385  AdjustIdleTimer();
   8386 }
   8387 
   8388 void ConnectionPool::NoteClosedDatabase(DatabaseInfo& aDatabaseInfo) {
   8389  AssertIsOnOwningThread();
   8390  MOZ_ASSERT(aDatabaseInfo.mClosing);
   8391  MOZ_ASSERT(!mIdleDatabases.Contains(&aDatabaseInfo));
   8392 
   8393  AUTO_PROFILER_LABEL("ConnectionPool::NoteClosedDatabase", DOM);
   8394 
   8395  aDatabaseInfo.mClosing = false;
   8396 
   8397  // Schedule any transactions that were started while we were closing the
   8398  // connection.
   8399  if (!mQueuedTransactions.IsEmpty()) {
   8400    ScheduleQueuedTransactions();
   8401  } else if (!aDatabaseInfo.TotalTransactionCount() && !mShutdownRequested) {
   8402    AdjustIdleTimer();
   8403  }
   8404 
   8405  // Schedule any transactions that were started while we were closing the
   8406  // connection.
   8407  if (aDatabaseInfo.TotalTransactionCount()) {
   8408    auto& scheduledTransactions =
   8409        aDatabaseInfo.mTransactionsScheduledDuringClose;
   8410 
   8411    MOZ_ASSERT(!scheduledTransactions.IsEmpty());
   8412 
   8413    for (const auto& scheduledTransaction : scheduledTransactions) {
   8414      (void)ScheduleTransaction(*scheduledTransaction,
   8415                                /* aFromQueuedTransactions */ false);
   8416    }
   8417 
   8418    scheduledTransactions.Clear();
   8419 
   8420    return;
   8421  }
   8422 
   8423  // There are no more transactions and the connection has been closed. We're
   8424  // done with this database.
   8425  {
   8426    MutexAutoLock lock(mDatabasesMutex);
   8427 
   8428    mDatabases.Remove(aDatabaseInfo.mDatabaseId);
   8429  }
   8430 
   8431  // That just deleted |aDatabaseInfo|, we must not access that below.
   8432 
   8433  // See if we need to fire any complete callbacks now that the database is
   8434  // finished.
   8435  mCompleteCallbacks.RemoveLastElements(
   8436      mCompleteCallbacks.end() -
   8437      std::remove_if(mCompleteCallbacks.begin(), mCompleteCallbacks.end(),
   8438                     [&me = *this](const auto& completeCallback) {
   8439                       return me.MaybeFireCallback(completeCallback.get());
   8440                     }));
   8441 
   8442  // If that was the last database and we're supposed to be shutting down then
   8443  // we are finished.
   8444  if (mShutdownRequested && !mDatabases.Count()) {
   8445    MOZ_ASSERT(!mTransactions.Count());
   8446    Cleanup();
   8447  }
   8448 }
   8449 
   8450 bool ConnectionPool::MaybeFireCallback(DatabaseCompleteCallback* aCallback) {
   8451  AssertIsOnOwningThread();
   8452  MOZ_ASSERT(aCallback);
   8453  MOZ_ASSERT(!aCallback->mDatabaseId.IsEmpty());
   8454  MOZ_ASSERT(aCallback->mCallback);
   8455 
   8456  AUTO_PROFILER_LABEL("ConnectionPool::MaybeFireCallback", DOM);
   8457 
   8458  if (mDatabases.Get(aCallback->mDatabaseId)) {
   8459    return false;
   8460  }
   8461 
   8462  (void)aCallback->mCallback->Run();
   8463  return true;
   8464 }
   8465 
   8466 void ConnectionPool::PerformIdleDatabaseMaintenance(
   8467    DatabaseInfo& aDatabaseInfo) {
   8468  AssertIsOnOwningThread();
   8469  MOZ_ASSERT(!aDatabaseInfo.TotalTransactionCount());
   8470  MOZ_ASSERT(aDatabaseInfo.mEventTarget);
   8471  MOZ_ASSERT(aDatabaseInfo.mIdle);
   8472  MOZ_ASSERT(!aDatabaseInfo.mCloseOnIdle);
   8473  MOZ_ASSERT(!aDatabaseInfo.mClosing);
   8474  MOZ_ASSERT(mIdleDatabases.Contains(&aDatabaseInfo));
   8475  MOZ_ASSERT(!mDatabasesPerformingIdleMaintenance.Contains(&aDatabaseInfo));
   8476 
   8477  const bool neededCheckpoint = aDatabaseInfo.mNeedsCheckpoint;
   8478 
   8479  aDatabaseInfo.mNeedsCheckpoint = false;
   8480  aDatabaseInfo.mIdle = false;
   8481 
   8482  auto idleConnectionRunnable =
   8483      MakeRefPtr<IdleConnectionRunnable>(aDatabaseInfo, neededCheckpoint);
   8484 
   8485  mDatabasesPerformingIdleMaintenance.AppendElement(
   8486      PerformingIdleMaintenanceDatabaseInfo{aDatabaseInfo,
   8487                                            idleConnectionRunnable});
   8488 
   8489  MOZ_ALWAYS_SUCCEEDS(aDatabaseInfo.mEventTarget->Dispatch(
   8490      idleConnectionRunnable.forget(), NS_DISPATCH_NORMAL));
   8491 }
   8492 
   8493 void ConnectionPool::CloseDatabase(DatabaseInfo& aDatabaseInfo) const {
   8494  AssertIsOnOwningThread();
   8495  MOZ_DIAGNOSTIC_ASSERT(!aDatabaseInfo.TotalTransactionCount());
   8496  MOZ_ASSERT(aDatabaseInfo.mEventTarget);
   8497  MOZ_ASSERT(!aDatabaseInfo.mClosing);
   8498 
   8499  aDatabaseInfo.mIdle = false;
   8500  aDatabaseInfo.mNeedsCheckpoint = false;
   8501  aDatabaseInfo.mClosing = true;
   8502 
   8503  MOZ_ALWAYS_SUCCEEDS(aDatabaseInfo.Dispatch(
   8504      MakeAndAddRef<CloseConnectionRunnable>(aDatabaseInfo)));
   8505 }
   8506 
   8507 bool ConnectionPool::CloseDatabaseWhenIdleInternal(
   8508    const nsACString& aDatabaseId) {
   8509  AssertIsOnOwningThread();
   8510  MOZ_ASSERT(!aDatabaseId.IsEmpty());
   8511 
   8512  AUTO_PROFILER_LABEL("ConnectionPool::CloseDatabaseWhenIdleInternal", DOM);
   8513 
   8514  if (DatabaseInfo* dbInfo = mDatabases.Get(aDatabaseId)) {
   8515    if (mIdleDatabases.RemoveElement(dbInfo) ||
   8516        mDatabasesPerformingIdleMaintenance.RemoveElement(dbInfo)) {
   8517      CloseDatabase(*dbInfo);
   8518      AdjustIdleTimer();
   8519    } else {
   8520      dbInfo->mCloseOnIdle.EnsureFlipped();
   8521    }
   8522 
   8523    return true;
   8524  }
   8525 
   8526  return false;
   8527 }
   8528 
   8529 ConnectionPool::ConnectionRunnable::ConnectionRunnable(
   8530    DatabaseInfo& aDatabaseInfo)
   8531    : Runnable("dom::indexedDB::ConnectionPool::ConnectionRunnable"),
   8532      mDatabaseInfo(aDatabaseInfo),
   8533      mOwningEventTarget(GetCurrentSerialEventTarget()) {
   8534  AssertIsOnBackgroundThread();
   8535  MOZ_ASSERT(aDatabaseInfo.mConnectionPool);
   8536  aDatabaseInfo.mConnectionPool->AssertIsOnOwningThread();
   8537  MOZ_ASSERT(mOwningEventTarget);
   8538 }
   8539 
   8540 NS_IMETHODIMP
   8541 ConnectionPool::IdleConnectionRunnable::Run() {
   8542  MOZ_ASSERT(!mDatabaseInfo.mIdle);
   8543 
   8544  const nsCOMPtr<nsIEventTarget> owningThread = std::move(mOwningEventTarget);
   8545 
   8546  if (owningThread) {
   8547    mDatabaseInfo.AssertIsOnConnectionThread();
   8548 
   8549    // The connection could be null if EnsureConnection() didn't run or was not
   8550    // successful in TransactionDatabaseOperationBase::RunOnConnectionThread().
   8551    if (mDatabaseInfo.mConnection) {
   8552      mDatabaseInfo.mConnection->DoIdleProcessing(mNeedsCheckpoint,
   8553                                                  mInterrupted);
   8554    }
   8555 
   8556    MOZ_ALWAYS_SUCCEEDS(owningThread->Dispatch(this, NS_DISPATCH_NORMAL));
   8557    return NS_OK;
   8558  }
   8559 
   8560  AssertIsOnBackgroundThread();
   8561 
   8562  RefPtr<ConnectionPool> connectionPool = mDatabaseInfo.mConnectionPool;
   8563  MOZ_ASSERT(connectionPool);
   8564 
   8565  if (mDatabaseInfo.mClosing || mDatabaseInfo.TotalTransactionCount()) {
   8566    MOZ_ASSERT(!connectionPool->mDatabasesPerformingIdleMaintenance.Contains(
   8567        &mDatabaseInfo));
   8568  } else {
   8569    MOZ_ALWAYS_TRUE(
   8570        connectionPool->mDatabasesPerformingIdleMaintenance.RemoveElement(
   8571            &mDatabaseInfo));
   8572 
   8573    connectionPool->NoteIdleDatabase(mDatabaseInfo);
   8574  }
   8575 
   8576  return NS_OK;
   8577 }
   8578 
   8579 NS_IMETHODIMP
   8580 ConnectionPool::CloseConnectionRunnable::Run() {
   8581  AUTO_PROFILER_LABEL("ConnectionPool::CloseConnectionRunnable::Run", DOM);
   8582 
   8583  if (mOwningEventTarget) {
   8584    MOZ_ASSERT(mDatabaseInfo.mClosing);
   8585 
   8586    const nsCOMPtr<nsIEventTarget> owningThread = std::move(mOwningEventTarget);
   8587 
   8588    // The connection could be null if EnsureConnection() didn't run or was not
   8589    // successful in TransactionDatabaseOperationBase::RunOnConnectionThread().
   8590    if (mDatabaseInfo.mConnection) {
   8591      mDatabaseInfo.AssertIsOnConnectionThread();
   8592 
   8593      mDatabaseInfo.mConnection->Close();
   8594 
   8595      IDB_DEBUG_LOG(("ConnectionPool closed connection 0x%p",
   8596                     mDatabaseInfo.mConnection.get()));
   8597 
   8598      mDatabaseInfo.mConnection = nullptr;
   8599 
   8600 #ifdef DEBUG
   8601      mDatabaseInfo.mDEBUGConnectionEventTarget = nullptr;
   8602 #endif
   8603    }
   8604 
   8605    MOZ_ALWAYS_SUCCEEDS(owningThread->Dispatch(this, NS_DISPATCH_NORMAL));
   8606    return NS_OK;
   8607  }
   8608 
   8609  RefPtr<ConnectionPool> connectionPool = mDatabaseInfo.mConnectionPool;
   8610  MOZ_ASSERT(connectionPool);
   8611 
   8612  connectionPool->NoteClosedDatabase(mDatabaseInfo);
   8613  return NS_OK;
   8614 }
   8615 
   8616 ConnectionPool::DatabaseInfo::DatabaseInfo(ConnectionPool* aConnectionPool,
   8617                                           const nsACString& aDatabaseId)
   8618    : mConnectionPool(aConnectionPool),
   8619      mDatabaseId(aDatabaseId),
   8620      mReadTransactionCount(0),
   8621      mWriteTransactionCount(0),
   8622      mNeedsCheckpoint(false),
   8623      mIdle(false),
   8624      mClosing(false)
   8625 #ifdef DEBUG
   8626      ,
   8627      mDEBUGConnectionEventTarget(nullptr)
   8628 #endif
   8629 {
   8630  AssertIsOnBackgroundThread();
   8631  MOZ_ASSERT(aConnectionPool);
   8632  aConnectionPool->AssertIsOnOwningThread();
   8633  MOZ_ASSERT(!aDatabaseId.IsEmpty());
   8634 
   8635  MOZ_COUNT_CTOR(ConnectionPool::DatabaseInfo);
   8636 }
   8637 
   8638 ConnectionPool::DatabaseInfo::~DatabaseInfo() {
   8639  AssertIsOnBackgroundThread();
   8640  MOZ_ASSERT(!mConnection);
   8641  MOZ_ASSERT(mScheduledWriteTransactions.IsEmpty());
   8642  MOZ_ASSERT(!mRunningWriteTransaction);
   8643  MOZ_ASSERT(!TotalTransactionCount());
   8644 
   8645  MOZ_COUNT_DTOR(ConnectionPool::DatabaseInfo);
   8646 }
   8647 
   8648 nsresult ConnectionPool::DatabaseInfo::Dispatch(
   8649    already_AddRefed<nsIRunnable> aRunnable) {
   8650  nsCOMPtr<nsIRunnable> runnable = aRunnable;
   8651 
   8652 #ifdef DEBUG
   8653  if (kDEBUGTransactionThreadSleepMS) {
   8654    runnable = MakeRefPtr<TransactionRunnable>(std::move(runnable));
   8655  }
   8656 #endif
   8657 
   8658  return mEventTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
   8659 }
   8660 
   8661 ConnectionPool::DatabaseCompleteCallback::DatabaseCompleteCallback(
   8662    const nsCString& aDatabaseId, nsIRunnable* aCallback)
   8663    : mDatabaseId(aDatabaseId), mCallback(aCallback) {
   8664  AssertIsOnBackgroundThread();
   8665  MOZ_ASSERT(!mDatabaseId.IsEmpty());
   8666  MOZ_ASSERT(aCallback);
   8667 
   8668  MOZ_COUNT_CTOR(ConnectionPool::DatabaseCompleteCallback);
   8669 }
   8670 
   8671 ConnectionPool::DatabaseCompleteCallback::~DatabaseCompleteCallback() {
   8672  AssertIsOnBackgroundThread();
   8673 
   8674  MOZ_COUNT_DTOR(ConnectionPool::DatabaseCompleteCallback);
   8675 }
   8676 
   8677 ConnectionPool::FinishCallbackWrapper::FinishCallbackWrapper(
   8678    ConnectionPool* aConnectionPool, uint64_t aTransactionId,
   8679    FinishCallback* aCallback)
   8680    : Runnable("dom::indexedDB::ConnectionPool::FinishCallbackWrapper"),
   8681      mConnectionPool(aConnectionPool),
   8682      mCallback(aCallback),
   8683      mOwningEventTarget(GetCurrentSerialEventTarget()),
   8684      mTransactionId(aTransactionId),
   8685      mHasRunOnce(false) {
   8686  AssertIsOnBackgroundThread();
   8687  MOZ_ASSERT(aConnectionPool);
   8688  MOZ_ASSERT(aCallback);
   8689  MOZ_ASSERT(mOwningEventTarget);
   8690 }
   8691 
   8692 ConnectionPool::FinishCallbackWrapper::~FinishCallbackWrapper() {
   8693  MOZ_ASSERT(!mConnectionPool);
   8694  MOZ_ASSERT(!mCallback);
   8695 }
   8696 
   8697 nsresult ConnectionPool::FinishCallbackWrapper::Run() {
   8698  MOZ_ASSERT(mConnectionPool);
   8699  MOZ_ASSERT(mCallback);
   8700  MOZ_ASSERT(mOwningEventTarget);
   8701 
   8702  AUTO_PROFILER_LABEL("ConnectionPool::FinishCallbackWrapper::Run", DOM);
   8703 
   8704  if (!mHasRunOnce) {
   8705    MOZ_ASSERT(!IsOnBackgroundThread());
   8706 
   8707    mHasRunOnce = true;
   8708 
   8709    (void)mCallback->Run();
   8710 
   8711    MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
   8712 
   8713    return NS_OK;
   8714  }
   8715 
   8716  mConnectionPool->AssertIsOnOwningThread();
   8717  MOZ_ASSERT(mHasRunOnce);
   8718 
   8719  RefPtr<ConnectionPool> connectionPool = std::move(mConnectionPool);
   8720 
   8721  connectionPool->FinishOp(mTransactionId);
   8722 
   8723  RefPtr<FinishCallback> callback = std::move(mCallback);
   8724 
   8725  callback->TransactionFinishedBeforeUnblock();
   8726 
   8727  connectionPool->NoteFinishedTransaction(mTransactionId);
   8728 
   8729  callback->TransactionFinishedAfterUnblock();
   8730 
   8731  return NS_OK;
   8732 }
   8733 
   8734 uint32_t ConnectionPool::sSerialNumber = 0u;
   8735 
   8736 #ifdef DEBUG
   8737 
   8738 ConnectionPool::TransactionRunnable::TransactionRunnable(
   8739    nsCOMPtr<nsIRunnable> aRunnable)
   8740    : Runnable("dom::indexedDB::ConnectionPool::TransactionRunnable"),
   8741      mRunnable(std::move(aRunnable)) {
   8742  AssertIsOnBackgroundThread();
   8743  MOZ_ASSERT(kDEBUGTransactionThreadSleepMS);
   8744 }
   8745 
   8746 nsresult ConnectionPool::TransactionRunnable::Run() {
   8747  MOZ_ASSERT(!IsOnBackgroundThread());
   8748 
   8749  QM_TRY(MOZ_TO_RESULT(mRunnable->Run()));
   8750 
   8751  MOZ_ALWAYS_TRUE(PR_Sleep(PR_MillisecondsToInterval(
   8752                      kDEBUGTransactionThreadSleepMS)) == PR_SUCCESS);
   8753 
   8754  return NS_OK;
   8755 }
   8756 
   8757 #endif
   8758 
   8759 ConnectionPool::IdleResource::IdleResource(const TimeStamp& aIdleTime)
   8760    : mIdleTime(aIdleTime) {
   8761  AssertIsOnBackgroundThread();
   8762  MOZ_ASSERT(!aIdleTime.IsNull());
   8763 
   8764  MOZ_COUNT_CTOR(ConnectionPool::IdleResource);
   8765 }
   8766 
   8767 ConnectionPool::IdleResource::~IdleResource() {
   8768  AssertIsOnBackgroundThread();
   8769 
   8770  MOZ_COUNT_DTOR(ConnectionPool::IdleResource);
   8771 }
   8772 
   8773 ConnectionPool::IdleDatabaseInfo::IdleDatabaseInfo(DatabaseInfo& aDatabaseInfo)
   8774    : IdleResource(
   8775          TimeStamp::NowLoRes() +
   8776          (aDatabaseInfo.mIdle
   8777               ? TimeDuration::FromMilliseconds(kConnectionIdleMaintenanceMS)
   8778               : TimeDuration::FromMilliseconds(kConnectionIdleCloseMS))),
   8779      mDatabaseInfo(WrapNotNullUnchecked(&aDatabaseInfo)) {
   8780  AssertIsOnBackgroundThread();
   8781 
   8782  MOZ_COUNT_CTOR(ConnectionPool::IdleDatabaseInfo);
   8783 }
   8784 
   8785 ConnectionPool::IdleDatabaseInfo::~IdleDatabaseInfo() {
   8786  AssertIsOnBackgroundThread();
   8787 
   8788  MOZ_COUNT_DTOR(ConnectionPool::IdleDatabaseInfo);
   8789 }
   8790 
   8791 ConnectionPool::PerformingIdleMaintenanceDatabaseInfo::
   8792    PerformingIdleMaintenanceDatabaseInfo(
   8793        DatabaseInfo& aDatabaseInfo,
   8794        RefPtr<IdleConnectionRunnable> aIdleConnectionRunnable)
   8795    : mDatabaseInfo(WrapNotNullUnchecked(&aDatabaseInfo)),
   8796      mIdleConnectionRunnable(std::move(aIdleConnectionRunnable)) {
   8797  AssertIsOnBackgroundThread();
   8798  MOZ_ASSERT(mIdleConnectionRunnable);
   8799 
   8800  MOZ_COUNT_CTOR(ConnectionPool::PerformingIdleMaintenanceDatabaseInfo);
   8801 }
   8802 
   8803 ConnectionPool::PerformingIdleMaintenanceDatabaseInfo::
   8804    ~PerformingIdleMaintenanceDatabaseInfo() {
   8805  AssertIsOnBackgroundThread();
   8806 
   8807  MOZ_COUNT_DTOR(ConnectionPool::PerformingIdleMaintenanceDatabaseInfo);
   8808 }
   8809 
   8810 ConnectionPool::TransactionInfo::TransactionInfo(
   8811    DatabaseInfo& aDatabaseInfo, const nsID& aBackgroundChildLoggingId,
   8812    const nsACString& aDatabaseId, uint64_t aTransactionId,
   8813    int64_t aLoggingSerialNumber, const nsTArray<nsString>& aObjectStoreNames,
   8814    bool aIsWriteTransaction, TransactionDatabaseOperationBase* aTransactionOp)
   8815    : mDatabaseInfo(aDatabaseInfo),
   8816      mBackgroundChildLoggingId(aBackgroundChildLoggingId),
   8817      mDatabaseId(aDatabaseId),
   8818      mTransactionId(aTransactionId),
   8819      mLoggingSerialNumber(aLoggingSerialNumber),
   8820      mObjectStoreNames(aObjectStoreNames.Clone()),
   8821      mIsWriteTransaction(aIsWriteTransaction),
   8822      mRunning(false),
   8823      mRunningOp(false) {
   8824  AssertIsOnBackgroundThread();
   8825  aDatabaseInfo.mConnectionPool->AssertIsOnOwningThread();
   8826 
   8827  MOZ_COUNT_CTOR(ConnectionPool::TransactionInfo);
   8828 
   8829  if (aTransactionOp) {
   8830    mQueuedOps.Push(aTransactionOp);
   8831  }
   8832 }
   8833 
   8834 ConnectionPool::TransactionInfo::~TransactionInfo() {
   8835  AssertIsOnBackgroundThread();
   8836  MOZ_ASSERT(!mBlockedOn.Count());
   8837  MOZ_ASSERT(mQueuedOps.IsEmpty());
   8838  MOZ_ASSERT(!mRunning);
   8839  MOZ_ASSERT(!mRunningOp);
   8840  MOZ_ASSERT(mFinished);
   8841 
   8842  MOZ_COUNT_DTOR(ConnectionPool::TransactionInfo);
   8843 }
   8844 
   8845 void ConnectionPool::TransactionInfo::AddBlockingTransaction(
   8846    TransactionInfo& aTransactionInfo) {
   8847  AssertIsOnBackgroundThread();
   8848 
   8849  // XXX Does it really make sense to have both mBlocking and mBlockingOrdered,
   8850  // just to reduce the algorithmic complexity of this Contains check? This was
   8851  // mentioned in the context of Bug 1290853, but no real justification was
   8852  // given. There was the suggestion of encapsulating this in an
   8853  // insertion-ordered hashtable implementation, which seems like a good idea.
   8854  // If we had that, this would be the appropriate data structure to use here.
   8855  if (mBlocking.EnsureInserted(&aTransactionInfo)) {
   8856    mBlockingOrdered.AppendElement(WrapNotNullUnchecked(&aTransactionInfo));
   8857  }
   8858 }
   8859 
   8860 void ConnectionPool::TransactionInfo::RemoveBlockingTransactions() {
   8861  AssertIsOnBackgroundThread();
   8862 
   8863  for (const auto blockedInfo : mBlockingOrdered) {
   8864    blockedInfo->MaybeUnblock(*this);
   8865  }
   8866 
   8867  mBlocking.Clear();
   8868  mBlockingOrdered.Clear();
   8869 }
   8870 
   8871 void ConnectionPool::TransactionInfo::SetRunning() {
   8872  AssertIsOnBackgroundThread();
   8873  MOZ_ASSERT(!mRunning);
   8874 
   8875  AUTO_PROFILER_LABEL("ConnectionPool::FinishOp", DOM);
   8876 
   8877  mRunning = true;
   8878 
   8879  if (!mQueuedOps.IsEmpty()) {
   8880    mRunningOp = true;
   8881 
   8882    nsCOMPtr<nsIRunnable> runnable = mQueuedOps.Pop();
   8883 
   8884    MOZ_ALWAYS_SUCCEEDS(mDatabaseInfo.Dispatch(runnable.forget()));
   8885  }
   8886 }
   8887 
   8888 void ConnectionPool::TransactionInfo::StartOp(nsCOMPtr<nsIRunnable> aRunnable) {
   8889  AssertIsOnBackgroundThread();
   8890 
   8891  if (mRunning) {
   8892    MOZ_ASSERT(mDatabaseInfo.mEventTarget);
   8893    MOZ_ASSERT(!mDatabaseInfo.mClosing);
   8894    MOZ_ASSERT_IF(mIsWriteTransaction,
   8895                  mDatabaseInfo.mRunningWriteTransaction &&
   8896                      mDatabaseInfo.mRunningWriteTransaction.refEquals(*this));
   8897 
   8898    if (!mRunningOp) {
   8899      mRunningOp = true;
   8900 
   8901      MOZ_ALWAYS_SUCCEEDS(mDatabaseInfo.Dispatch(aRunnable.forget()));
   8902    } else {
   8903      mQueuedOps.Push(std::move(aRunnable));
   8904    }
   8905  } else {
   8906    mQueuedOps.Push(std::move(aRunnable));
   8907  }
   8908 }
   8909 
   8910 void ConnectionPool::TransactionInfo::FinishOp() {
   8911  AssertIsOnBackgroundThread();
   8912  MOZ_ASSERT(mRunning);
   8913  MOZ_ASSERT(mRunningOp);
   8914 
   8915  if (mQueuedOps.IsEmpty()) {
   8916    mRunningOp = false;
   8917  } else {
   8918    nsCOMPtr<nsIRunnable> runnable = mQueuedOps.Pop();
   8919 
   8920    MOZ_ALWAYS_SUCCEEDS(mDatabaseInfo.Dispatch(runnable.forget()));
   8921  }
   8922 }
   8923 
   8924 void ConnectionPool::TransactionInfo::MaybeUnblock(
   8925    TransactionInfo& aTransactionInfo) {
   8926  AssertIsOnBackgroundThread();
   8927  MOZ_ASSERT(mBlockedOn.Contains(&aTransactionInfo));
   8928 
   8929  mBlockedOn.Remove(&aTransactionInfo);
   8930  if (mBlockedOn.IsEmpty()) {
   8931    ConnectionPool* connectionPool = mDatabaseInfo.mConnectionPool;
   8932    MOZ_ASSERT(connectionPool);
   8933    connectionPool->AssertIsOnOwningThread();
   8934 
   8935    (void)connectionPool->ScheduleTransaction(
   8936        *this,
   8937        /* aFromQueuedTransactions */ false);
   8938  }
   8939 }
   8940 
   8941 #if defined(DEBUG) || defined(NS_BUILD_REFCNT_LOGGING)
   8942 ConnectionPool::TransactionInfoPair::TransactionInfoPair() {
   8943  AssertIsOnBackgroundThread();
   8944 
   8945  MOZ_COUNT_CTOR(ConnectionPool::TransactionInfoPair);
   8946 }
   8947 
   8948 ConnectionPool::TransactionInfoPair::~TransactionInfoPair() {
   8949  AssertIsOnBackgroundThread();
   8950 
   8951  MOZ_COUNT_DTOR(ConnectionPool::TransactionInfoPair);
   8952 }
   8953 #endif
   8954 
   8955 /*******************************************************************************
   8956 * Metadata classes
   8957 ******************************************************************************/
   8958 
   8959 bool FullObjectStoreMetadata::HasLiveIndexes() const {
   8960  AssertIsOnBackgroundThread();
   8961 
   8962  return std::any_of(mIndexes.Values().cbegin(), mIndexes.Values().cend(),
   8963                     [](const auto& entry) { return !entry->mDeleted; });
   8964 }
   8965 
   8966 SafeRefPtr<FullDatabaseMetadata> FullDatabaseMetadata::Duplicate() const {
   8967  AssertIsOnBackgroundThread();
   8968 
   8969  // FullDatabaseMetadata contains two hash tables of pointers that we need to
   8970  // duplicate so we can't just use the copy constructor.
   8971  auto newMetadata = MakeSafeRefPtr<FullDatabaseMetadata>(mCommonMetadata);
   8972 
   8973  newMetadata->mDatabaseId = mDatabaseId;
   8974  newMetadata->mFilePath = mFilePath;
   8975  newMetadata->mNextObjectStoreId = mNextObjectStoreId;
   8976  newMetadata->mNextIndexId = mNextIndexId;
   8977 
   8978  for (const auto& objectStoreEntry : mObjectStores) {
   8979    const auto& objectStoreValue = objectStoreEntry.GetData();
   8980 
   8981    auto newOSMetadata = MakeSafeRefPtr<FullObjectStoreMetadata>(
   8982        objectStoreValue->mCommonMetadata, [&objectStoreValue] {
   8983          const auto&& srcLocked = objectStoreValue->mAutoIncrementIds.Lock();
   8984          return *srcLocked;
   8985        }());
   8986 
   8987    for (const auto& indexEntry : objectStoreValue->mIndexes) {
   8988      const auto& value = indexEntry.GetData();
   8989 
   8990      auto newIndexMetadata = MakeSafeRefPtr<FullIndexMetadata>();
   8991 
   8992      newIndexMetadata->mCommonMetadata = value->mCommonMetadata;
   8993 
   8994      if (NS_WARN_IF(!newOSMetadata->mIndexes.InsertOrUpdate(
   8995              indexEntry.GetKey(), std::move(newIndexMetadata), fallible))) {
   8996        return nullptr;
   8997      }
   8998    }
   8999 
   9000    MOZ_ASSERT(objectStoreValue->mIndexes.Count() ==
   9001               newOSMetadata->mIndexes.Count());
   9002 
   9003    if (NS_WARN_IF(!newMetadata->mObjectStores.InsertOrUpdate(
   9004            objectStoreEntry.GetKey(), std::move(newOSMetadata), fallible))) {
   9005      return nullptr;
   9006    }
   9007  }
   9008 
   9009  MOZ_ASSERT(mObjectStores.Count() == newMetadata->mObjectStores.Count());
   9010 
   9011  return newMetadata;
   9012 }
   9013 
   9014 DatabaseLoggingInfo::~DatabaseLoggingInfo() {
   9015  AssertIsOnBackgroundThread();
   9016 
   9017  if (gLoggingInfoHashtable) {
   9018    const nsID& backgroundChildLoggingId =
   9019        mLoggingInfo.backgroundChildLoggingId();
   9020 
   9021    MOZ_ASSERT(gLoggingInfoHashtable->Get(backgroundChildLoggingId) == this);
   9022 
   9023    gLoggingInfoHashtable->Remove(backgroundChildLoggingId);
   9024  }
   9025 }
   9026 
   9027 /*******************************************************************************
   9028 * Factory
   9029 ******************************************************************************/
   9030 
   9031 Factory::Factory(RefPtr<DatabaseLoggingInfo> aLoggingInfo,
   9032                 const nsACString& aSystemLocale)
   9033    : mSystemLocale(aSystemLocale),
   9034      mLoggingInfo(std::move(aLoggingInfo))
   9035 #ifdef DEBUG
   9036      ,
   9037      mActorDestroyed(false)
   9038 #endif
   9039 {
   9040  AssertIsOnBackgroundThread();
   9041  MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
   9042 }
   9043 
   9044 Factory::~Factory() { MOZ_ASSERT(mActorDestroyed); }
   9045 
   9046 // static
   9047 SafeRefPtr<Factory> Factory::Create(const LoggingInfo& aLoggingInfo,
   9048                                    const nsACString& aSystemLocale) {
   9049  AssertIsOnBackgroundThread();
   9050  MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
   9051 
   9052  // Balanced in ActoryDestroy().
   9053  IncreaseBusyCount();
   9054 
   9055  MOZ_ASSERT(gLoggingInfoHashtable);
   9056  RefPtr<DatabaseLoggingInfo> loggingInfo =
   9057      gLoggingInfoHashtable->WithEntryHandle(
   9058          aLoggingInfo.backgroundChildLoggingId(), [&](auto&& entry) {
   9059            if (entry) {
   9060              [[maybe_unused]] const auto& loggingInfo = entry.Data();
   9061              MOZ_ASSERT(aLoggingInfo.backgroundChildLoggingId() ==
   9062                         loggingInfo->Id());
   9063 #if !FUZZING
   9064              NS_WARNING_ASSERTION(
   9065                  aLoggingInfo.nextTransactionSerialNumber() ==
   9066                      loggingInfo->mLoggingInfo.nextTransactionSerialNumber(),
   9067                  "NextTransactionSerialNumber doesn't match!");
   9068              NS_WARNING_ASSERTION(
   9069                  aLoggingInfo.nextVersionChangeTransactionSerialNumber() ==
   9070                      loggingInfo->mLoggingInfo
   9071                          .nextVersionChangeTransactionSerialNumber(),
   9072                  "NextVersionChangeTransactionSerialNumber doesn't match!");
   9073              NS_WARNING_ASSERTION(
   9074                  aLoggingInfo.nextRequestSerialNumber() ==
   9075                      loggingInfo->mLoggingInfo.nextRequestSerialNumber(),
   9076                  "NextRequestSerialNumber doesn't match!");
   9077 #endif  // !FUZZING
   9078            } else {
   9079              entry.Insert(new DatabaseLoggingInfo(aLoggingInfo));
   9080            }
   9081 
   9082            return do_AddRef(entry.Data());
   9083          });
   9084 
   9085  return MakeSafeRefPtr<Factory>(std::move(loggingInfo), aSystemLocale);
   9086 }
   9087 
   9088 void Factory::ActorDestroy(ActorDestroyReason aWhy) {
   9089  AssertIsOnBackgroundThread();
   9090  MOZ_ASSERT(!mActorDestroyed);
   9091 
   9092 #ifdef DEBUG
   9093  mActorDestroyed = true;
   9094 #endif
   9095 
   9096  // Match the IncreaseBusyCount in Create().
   9097  DecreaseBusyCount();
   9098 }
   9099 
   9100 mozilla::ipc::IPCResult Factory::RecvDeleteMe() {
   9101  AssertIsOnBackgroundThread();
   9102  MOZ_ASSERT(!mActorDestroyed);
   9103 
   9104  QM_WARNONLY_TRY(OkIf(PBackgroundIDBFactoryParent::Send__delete__(this)));
   9105 
   9106  return IPC_OK();
   9107 }
   9108 
   9109 PBackgroundIDBFactoryRequestParent*
   9110 Factory::AllocPBackgroundIDBFactoryRequestParent(
   9111    const FactoryRequestParams& aParams) {
   9112  AssertIsOnBackgroundThread();
   9113  MOZ_ASSERT(aParams.type() != FactoryRequestParams::T__None);
   9114 
   9115  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) {
   9116    return nullptr;
   9117  }
   9118 
   9119  const CommonFactoryRequestParams* commonParams;
   9120 
   9121  switch (aParams.type()) {
   9122    case FactoryRequestParams::TOpenDatabaseRequestParams: {
   9123      const OpenDatabaseRequestParams& params =
   9124          aParams.get_OpenDatabaseRequestParams();
   9125      commonParams = &params.commonParams();
   9126      break;
   9127    }
   9128 
   9129    case FactoryRequestParams::TDeleteDatabaseRequestParams: {
   9130      const DeleteDatabaseRequestParams& params =
   9131          aParams.get_DeleteDatabaseRequestParams();
   9132      commonParams = &params.commonParams();
   9133      break;
   9134    }
   9135 
   9136    default:
   9137      MOZ_CRASH("Should never get here!");
   9138  }
   9139 
   9140  MOZ_ASSERT(commonParams);
   9141 
   9142  const DatabaseMetadata& metadata = commonParams->metadata();
   9143 
   9144  if (NS_AUUF_OR_WARN_IF(!IsValidPersistenceType(metadata.persistenceType()))) {
   9145    return nullptr;
   9146  }
   9147 
   9148  const PrincipalInfo& principalInfo = commonParams->principalInfo();
   9149 
   9150  if (NS_AUUF_OR_WARN_IF(!quota::IsPrincipalInfoValid(principalInfo))) {
   9151    IPC_FAIL(this, "Invalid principal!");
   9152    return nullptr;
   9153  }
   9154 
   9155  MOZ_ASSERT(principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo ||
   9156             principalInfo.type() == PrincipalInfo::TContentPrincipalInfo);
   9157 
   9158  if (NS_AUUF_OR_WARN_IF(
   9159          principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo &&
   9160          metadata.persistenceType() != PERSISTENCE_TYPE_PERSISTENT)) {
   9161    return nullptr;
   9162  }
   9163 
   9164  if (NS_AUUF_OR_WARN_IF(
   9165          principalInfo.type() == PrincipalInfo::TContentPrincipalInfo &&
   9166          QuotaManager::IsOriginInternal(
   9167              principalInfo.get_ContentPrincipalInfo().originNoSuffix()) &&
   9168          metadata.persistenceType() != PERSISTENCE_TYPE_PERSISTENT)) {
   9169    return nullptr;
   9170  }
   9171 
   9172  Maybe<ContentParentId> contentParentId = GetContentParentId();
   9173 
   9174  auto actor = [&]() -> RefPtr<FactoryRequestOp> {
   9175    if (aParams.type() == FactoryRequestParams::TOpenDatabaseRequestParams) {
   9176      return MakeRefPtr<OpenDatabaseOp>(SafeRefPtrFromThis(), contentParentId,
   9177                                        *commonParams);
   9178    } else {
   9179      return MakeRefPtr<DeleteDatabaseOp>(SafeRefPtrFromThis(), contentParentId,
   9180                                          *commonParams);
   9181    }
   9182  }();
   9183 
   9184  gFactoryOps->insertBack(actor);
   9185 
   9186  // Balanced in CleanupMetadata() which is/must always called by SendResults().
   9187  IncreaseBusyCount();
   9188 
   9189  // Transfer ownership to IPDL.
   9190  return actor.forget().take();
   9191 }
   9192 
   9193 mozilla::ipc::IPCResult Factory::RecvPBackgroundIDBFactoryRequestConstructor(
   9194    PBackgroundIDBFactoryRequestParent* aActor,
   9195    const FactoryRequestParams& aParams) {
   9196  AssertIsOnBackgroundThread();
   9197  MOZ_ASSERT(aActor);
   9198  MOZ_ASSERT(aParams.type() != FactoryRequestParams::T__None);
   9199  MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
   9200 
   9201  auto* op = static_cast<FactoryRequestOp*>(aActor);
   9202 
   9203  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(op));
   9204  return IPC_OK();
   9205 }
   9206 
   9207 bool Factory::DeallocPBackgroundIDBFactoryRequestParent(
   9208    PBackgroundIDBFactoryRequestParent* aActor) {
   9209  AssertIsOnBackgroundThread();
   9210  MOZ_ASSERT(aActor);
   9211 
   9212  // Transfer ownership back from IPDL.
   9213  RefPtr<FactoryRequestOp> op =
   9214      dont_AddRef(static_cast<FactoryRequestOp*>(aActor));
   9215  return true;
   9216 }
   9217 
   9218 mozilla::ipc::IPCResult Factory::RecvGetDatabases(
   9219    const PersistenceType& aPersistenceType,
   9220    const PrincipalInfo& aPrincipalInfo, GetDatabasesResolver&& aResolve) {
   9221  AssertIsOnBackgroundThread();
   9222 
   9223  auto ResolveGetDatabasesAndReturn = [&aResolve](const nsresult rv) {
   9224    aResolve(rv);
   9225    return IPC_OK();
   9226  };
   9227 
   9228  QM_TRY(MOZ_TO_RESULT(!QuotaClient::IsShuttingDownOnBackgroundThread()),
   9229         ResolveGetDatabasesAndReturn);
   9230 
   9231  QM_TRY(MOZ_TO_RESULT(IsValidPersistenceType(aPersistenceType)),
   9232         QM_IPC_FAIL(this));
   9233 
   9234  QM_TRY(MOZ_TO_RESULT(quota::IsPrincipalInfoValid(aPrincipalInfo)),
   9235         QM_IPC_FAIL(this));
   9236 
   9237  MOZ_ASSERT(aPrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo ||
   9238             aPrincipalInfo.type() == PrincipalInfo::TContentPrincipalInfo);
   9239 
   9240  PersistenceType persistenceType =
   9241      IDBFactory::GetPersistenceType(aPrincipalInfo);
   9242 
   9243  QM_TRY(MOZ_TO_RESULT(aPersistenceType == persistenceType), QM_IPC_FAIL(this));
   9244 
   9245  Maybe<ContentParentId> contentParentId = GetContentParentId();
   9246 
   9247  auto op = MakeRefPtr<GetDatabasesOp>(SafeRefPtrFromThis(), contentParentId,
   9248                                       aPersistenceType, aPrincipalInfo,
   9249                                       std::move(aResolve));
   9250 
   9251  gFactoryOps->insertBack(op);
   9252 
   9253  // Balanced in CleanupMetadata() which is/must always called by SendResults().
   9254  IncreaseBusyCount();
   9255 
   9256  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(op));
   9257 
   9258  return IPC_OK();
   9259 }
   9260 
   9261 Maybe<ContentParentId> Factory::GetContentParentId() const {
   9262  uint64_t childID = BackgroundParent::GetChildID(Manager());
   9263  if (childID) {
   9264    // If childID is not zero we are dealing with an other-process actor. We
   9265    // want to initialize OpenDatabaseOp/DeleteDatabaseOp here with the ID
   9266    // (and later also Database) in that case, so Database::IsOwnedByProcess
   9267    // can find Databases belonging to a particular content process when
   9268    // QuotaClient::AbortOperationsForProcess is called which is currently used
   9269    // to abort operations for content processes only.
   9270    return Some(ContentParentId(childID));
   9271  }
   9272 
   9273  return Nothing();
   9274 }
   9275 
   9276 /*******************************************************************************
   9277 * WaitForTransactionsHelper
   9278 ******************************************************************************/
   9279 
   9280 void WaitForTransactionsHelper::WaitForTransactions() {
   9281  MOZ_ASSERT(mState == State::Initial);
   9282 
   9283  (void)this->Run();
   9284 }
   9285 
   9286 void WaitForTransactionsHelper::MaybeWaitForTransactions() {
   9287  AssertIsOnBackgroundThread();
   9288  MOZ_ASSERT(mState == State::Initial);
   9289 
   9290  RefPtr<ConnectionPool> connectionPool = gConnectionPool.get();
   9291  if (connectionPool) {
   9292    mState = State::WaitingForTransactions;
   9293 
   9294    connectionPool->WaitForDatabaseToComplete(mDatabaseId, this);
   9295 
   9296    return;
   9297  }
   9298 
   9299  CallCallback();
   9300 }
   9301 
   9302 void WaitForTransactionsHelper::CallCallback() {
   9303  AssertIsOnBackgroundThread();
   9304  MOZ_ASSERT(mState == State::Initial ||
   9305             mState == State::WaitingForTransactions);
   9306 
   9307  const nsCOMPtr<nsIRunnable> callback = std::move(mCallback);
   9308 
   9309  callback->Run();
   9310 
   9311  mState = State::Complete;
   9312 }
   9313 
   9314 NS_IMETHODIMP
   9315 WaitForTransactionsHelper::Run() {
   9316  MOZ_ASSERT(mState != State::Complete);
   9317  MOZ_ASSERT(mCallback);
   9318 
   9319  switch (mState) {
   9320    case State::Initial:
   9321      MaybeWaitForTransactions();
   9322      break;
   9323 
   9324    case State::WaitingForTransactions:
   9325      CallCallback();
   9326      break;
   9327 
   9328    default:
   9329      MOZ_CRASH("Should never get here!");
   9330  }
   9331 
   9332  return NS_OK;
   9333 }
   9334 
   9335 /*******************************************************************************
   9336 * Database
   9337 ******************************************************************************/
   9338 
   9339 Database::Database(SafeRefPtr<Factory> aFactory,
   9340                   const PrincipalInfo& aPrincipalInfo,
   9341                   const Maybe<ContentParentId>& aOptionalContentParentId,
   9342                   const quota::OriginMetadata& aOriginMetadata,
   9343                   uint32_t aTelemetryId,
   9344                   SafeRefPtr<FullDatabaseMetadata> aMetadata,
   9345                   SafeRefPtr<DatabaseFileManager> aFileManager,
   9346                   ClientDirectoryLockHandle aDirectoryLockHandle,
   9347                   bool aInPrivateBrowsing,
   9348                   const Maybe<const CipherKey>& aMaybeKey)
   9349    : mFactory(std::move(aFactory)),
   9350      mMetadata(std::move(aMetadata)),
   9351      mFileManager(std::move(aFileManager)),
   9352      mDirectoryLockHandle(std::move(aDirectoryLockHandle)),
   9353      mPrincipalInfo(aPrincipalInfo),
   9354      mOptionalContentParentId(aOptionalContentParentId),
   9355      mOriginMetadata(aOriginMetadata),
   9356      mId(mMetadata->mDatabaseId),
   9357      mFilePath(mMetadata->mFilePath),
   9358      mKey(aMaybeKey),
   9359      mTelemetryId(aTelemetryId),
   9360      mPersistenceType(mMetadata->mCommonMetadata.persistenceType()),
   9361      mInPrivateBrowsing(aInPrivateBrowsing),
   9362      mBackgroundThread(GetCurrentSerialEventTarget())
   9363 #ifdef DEBUG
   9364      ,
   9365      mAllBlobsUnmapped(false)
   9366 #endif
   9367 {
   9368  AssertIsOnBackgroundThread();
   9369  MOZ_ASSERT(mFactory);
   9370  MOZ_ASSERT(mMetadata);
   9371  MOZ_ASSERT(mFileManager);
   9372 
   9373  MOZ_ASSERT(mDirectoryLockHandle);
   9374  MOZ_ASSERT(mDirectoryLockHandle->Id() >= 0);
   9375  mDirectoryLockId = mDirectoryLockHandle->Id();
   9376 }
   9377 
   9378 template <typename T>
   9379 bool Database::InvalidateAll(const nsTBaseHashSet<nsPtrHashKey<T>>& aTable) {
   9380  AssertIsOnBackgroundThread();
   9381 
   9382  const uint32_t count = aTable.Count();
   9383  if (!count) {
   9384    return true;
   9385  }
   9386 
   9387  // XXX Does this really need to be fallible?
   9388  QM_TRY_INSPECT(const auto& elementsToInvalidate,
   9389                 TransformIntoNewArray(
   9390                     aTable, [](const auto& entry) { return entry; }, fallible),
   9391                 false);
   9392 
   9393  IDB_REPORT_INTERNAL_ERR();
   9394 
   9395  for (const auto& elementToInvalidate : elementsToInvalidate) {
   9396    MOZ_ASSERT(elementToInvalidate);
   9397 
   9398    elementToInvalidate->Invalidate();
   9399  }
   9400 
   9401  return true;
   9402 }
   9403 
   9404 void Database::Invalidate() {
   9405  AssertIsOnBackgroundThread();
   9406 
   9407  if (mInvalidated) {
   9408    return;
   9409  }
   9410 
   9411  mInvalidated.Flip();
   9412 
   9413  if (mActorWasAlive && !mActorDestroyed) {
   9414    (void)SendInvalidate();
   9415  }
   9416 
   9417  QM_WARNONLY_TRY(OkIf(InvalidateAll(mTransactions)));
   9418 
   9419  MOZ_ALWAYS_TRUE(CloseInternal());
   9420 }
   9421 
   9422 nsresult Database::EnsureConnection() {
   9423  MOZ_ASSERT(!NS_IsMainThread());
   9424  MOZ_ASSERT(!IsOnBackgroundThread());
   9425 
   9426  AUTO_PROFILER_LABEL("Database::EnsureConnection", DOM);
   9427 
   9428  if (!mConnection || !mConnection->HasStorageConnection()) {
   9429    QM_TRY_UNWRAP(mConnection, gConnectionPool->GetOrCreateConnection(*this));
   9430  }
   9431 
   9432  AssertIsOnConnectionThread();
   9433 
   9434  return NS_OK;
   9435 }
   9436 
   9437 bool Database::RegisterTransaction(TransactionBase& aTransaction) {
   9438  AssertIsOnBackgroundThread();
   9439  MOZ_ASSERT(!mTransactions.Contains(&aTransaction));
   9440  MOZ_ASSERT(mDirectoryLockHandle);
   9441  MOZ_ASSERT(!mInvalidated);
   9442  MOZ_ASSERT(!mClosed);
   9443 
   9444  if (NS_WARN_IF(!mTransactions.Insert(&aTransaction, fallible))) {
   9445    return false;
   9446  }
   9447 
   9448  return true;
   9449 }
   9450 
   9451 void Database::UnregisterTransaction(TransactionBase& aTransaction) {
   9452  AssertIsOnBackgroundThread();
   9453  MOZ_ASSERT(mTransactions.Contains(&aTransaction));
   9454 
   9455  mTransactions.Remove(&aTransaction);
   9456 
   9457  MaybeCloseConnection();
   9458 }
   9459 
   9460 void Database::SetActorAlive() {
   9461  AssertIsOnBackgroundThread();
   9462  MOZ_ASSERT(!mActorDestroyed);
   9463 
   9464  mActorWasAlive.Flip();
   9465 }
   9466 
   9467 void Database::MapBlob(const IPCBlob& aIPCBlob,
   9468                       SafeRefPtr<DatabaseFileInfo> aFileInfo) {
   9469  AssertIsOnBackgroundThread();
   9470 
   9471  const RemoteLazyStream& stream = aIPCBlob.inputStream();
   9472  MOZ_ASSERT(stream.type() == RemoteLazyStream::TRemoteLazyInputStream);
   9473 
   9474  nsID id{};
   9475  MOZ_ALWAYS_SUCCEEDS(
   9476      stream.get_RemoteLazyInputStream()->GetInternalStreamID(id));
   9477 
   9478  MOZ_ASSERT(!mMappedBlobs.Contains(id));
   9479  mMappedBlobs.InsertOrUpdate(id, std::move(aFileInfo));
   9480 
   9481  RefPtr<UnmapBlobCallback> callback =
   9482      new UnmapBlobCallback(SafeRefPtrFromThis());
   9483 
   9484  auto storage = RemoteLazyInputStreamStorage::Get();
   9485  MOZ_ASSERT(storage.isOk());
   9486  storage.inspect()->StoreCallback(id, callback);
   9487 }
   9488 
   9489 void Database::Stringify(nsACString& aResult) const {
   9490  AssertIsOnBackgroundThread();
   9491 
   9492  constexpr auto kQuotaGenericDelimiterString = "|"_ns;
   9493 
   9494  aResult.Append(
   9495      "DirectoryLock:"_ns + IntToCString(!!mDirectoryLockHandle) +
   9496      kQuotaGenericDelimiterString +
   9497      //
   9498      "Transactions:"_ns + IntToCString(mTransactions.Count()) +
   9499      kQuotaGenericDelimiterString +
   9500      //
   9501      "OtherProcessActor:"_ns +
   9502      IntToCString(
   9503          BackgroundParent::IsOtherProcessActor(GetBackgroundParent())) +
   9504      kQuotaGenericDelimiterString +
   9505      //
   9506      "Origin:"_ns + AnonymizedOriginString(mOriginMetadata.mOrigin) +
   9507      kQuotaGenericDelimiterString +
   9508      //
   9509      "PersistenceType:"_ns + PersistenceTypeToString(mPersistenceType) +
   9510      kQuotaGenericDelimiterString +
   9511      //
   9512      "Closed:"_ns + IntToCString(static_cast<bool>(mClosed)) +
   9513      kQuotaGenericDelimiterString +
   9514      //
   9515      "Invalidated:"_ns + IntToCString(static_cast<bool>(mInvalidated)) +
   9516      kQuotaGenericDelimiterString +
   9517      //
   9518      "ActorWasAlive:"_ns + IntToCString(static_cast<bool>(mActorWasAlive)) +
   9519      kQuotaGenericDelimiterString +
   9520      //
   9521      "ActorDestroyed:"_ns + IntToCString(static_cast<bool>(mActorDestroyed)));
   9522 }
   9523 
   9524 SafeRefPtr<DatabaseFileInfo> Database::GetBlob(const IPCBlob& aIPCBlob) {
   9525  AssertIsOnBackgroundThread();
   9526 
   9527  RefPtr<RemoteLazyInputStream> lazyStream;
   9528  switch (aIPCBlob.inputStream().type()) {
   9529    case RemoteLazyStream::TIPCStream: {
   9530      const InputStreamParams& inputStreamParams =
   9531          aIPCBlob.inputStream().get_IPCStream().stream();
   9532      if (inputStreamParams.type() !=
   9533          InputStreamParams::TRemoteLazyInputStreamParams) {
   9534        return nullptr;
   9535      }
   9536      lazyStream = inputStreamParams.get_RemoteLazyInputStreamParams().stream();
   9537      break;
   9538    }
   9539    case RemoteLazyStream::TRemoteLazyInputStream:
   9540      lazyStream = aIPCBlob.inputStream().get_RemoteLazyInputStream();
   9541      break;
   9542    default:
   9543      MOZ_ASSERT_UNREACHABLE("Unknown RemoteLazyStream type");
   9544      return nullptr;
   9545  }
   9546 
   9547  if (!lazyStream) {
   9548    MOZ_ASSERT_UNREACHABLE("Unexpected null stream");
   9549    return nullptr;
   9550  }
   9551 
   9552  nsID id{};
   9553  nsresult rv = lazyStream->GetInternalStreamID(id);
   9554  if (NS_FAILED(rv)) {
   9555    MOZ_ASSERT_UNREACHABLE(
   9556        "Received RemoteLazyInputStream doesn't have an actor connection");
   9557    return nullptr;
   9558  }
   9559 
   9560  const auto fileInfo = mMappedBlobs.Lookup(id);
   9561  return fileInfo ? fileInfo->clonePtr() : nullptr;
   9562 }
   9563 
   9564 void Database::UnmapBlob(const nsID& aID) {
   9565  AssertIsOnBackgroundThread();
   9566 
   9567  MOZ_ASSERT_IF(!mAllBlobsUnmapped, mMappedBlobs.Contains(aID));
   9568  mMappedBlobs.Remove(aID);
   9569 }
   9570 
   9571 void Database::UnmapAllBlobs() {
   9572  AssertIsOnBackgroundThread();
   9573 
   9574 #ifdef DEBUG
   9575  mAllBlobsUnmapped = true;
   9576 #endif
   9577 
   9578  mMappedBlobs.Clear();
   9579 }
   9580 
   9581 bool Database::CloseInternal() {
   9582  AssertIsOnBackgroundThread();
   9583 
   9584  if (mClosed) {
   9585    if (NS_WARN_IF(!IsInvalidated())) {
   9586      // Signal misbehaving child for sending the close message twice.
   9587      return false;
   9588    }
   9589 
   9590    // Ignore harmless race when we just invalidated the database.
   9591    return true;
   9592  }
   9593 
   9594  mClosed.Flip();
   9595 
   9596  if (gConnectionPool) {
   9597    gConnectionPool->CloseDatabaseWhenIdle(Id());
   9598  }
   9599 
   9600  DatabaseActorInfo* info;
   9601  MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(Id(), &info));
   9602 
   9603  MOZ_ASSERT(info->mLiveDatabases.contains(this));
   9604 
   9605  if (info->mWaitingFactoryOp) {
   9606    info->mWaitingFactoryOp->NoteDatabaseClosed(this);
   9607  }
   9608 
   9609  MaybeCloseConnection();
   9610 
   9611  return true;
   9612 }
   9613 
   9614 void Database::MaybeCloseConnection() {
   9615  AssertIsOnBackgroundThread();
   9616 
   9617  if (!mTransactions.Count() && IsClosed() && mDirectoryLockHandle) {
   9618    nsCOMPtr<nsIRunnable> callback =
   9619        NewRunnableMethod("dom::indexedDB::Database::ConnectionClosedCallback",
   9620                          this, &Database::ConnectionClosedCallback);
   9621 
   9622    RefPtr<WaitForTransactionsHelper> helper =
   9623        new WaitForTransactionsHelper(Id(), callback);
   9624    helper->WaitForTransactions();
   9625  }
   9626 }
   9627 
   9628 void Database::ConnectionClosedCallback() {
   9629  AssertIsOnBackgroundThread();
   9630  MOZ_ASSERT(mClosed);
   9631  MOZ_ASSERT(!mTransactions.Count());
   9632 
   9633  {
   9634    auto destroyingDirectoryLockHandle = std::move(mDirectoryLockHandle);
   9635  }
   9636 
   9637  CleanupMetadata();
   9638 
   9639  UnmapAllBlobs();
   9640 
   9641  if (IsInvalidated() && IsActorAlive()) {
   9642    // Step 3 and 4 of "5.2 Closing a Database":
   9643    // 1. Wait for all transactions to complete.
   9644    // 2. Fire a close event if forced flag is set, i.e., IsInvalidated() in our
   9645    //    implementation.
   9646    (void)SendCloseAfterInvalidationComplete();
   9647  }
   9648 }
   9649 
   9650 void Database::CleanupMetadata() {
   9651  AssertIsOnBackgroundThread();
   9652 
   9653  DatabaseActorInfo* info;
   9654  MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(Id(), &info));
   9655  removeFrom(info->mLiveDatabases);
   9656 
   9657  QuotaManager::MaybeRecordQuotaClientShutdownStep(
   9658      quota::Client::IDB, "Live database entry removed"_ns);
   9659 
   9660  if (info->mLiveDatabases.isEmpty()) {
   9661    MOZ_ASSERT(!info->mWaitingFactoryOp ||
   9662               !info->mWaitingFactoryOp->HasBlockedDatabases());
   9663    gLiveDatabaseHashtable->Remove(Id());
   9664 
   9665    QuotaManager::MaybeRecordQuotaClientShutdownStep(
   9666        quota::Client::IDB, "gLiveDatabaseHashtable entry removed"_ns);
   9667  }
   9668 
   9669  // Match the IncreaseBusyCount in OpenDatabaseOp::EnsureDatabaseActor().
   9670  DecreaseBusyCount();
   9671 }
   9672 
   9673 void Database::ActorDestroy(ActorDestroyReason aWhy) {
   9674  AssertIsOnBackgroundThread();
   9675 
   9676  mActorDestroyed.Flip();
   9677 
   9678  if (!IsInvalidated()) {
   9679    Invalidate();
   9680  }
   9681 }
   9682 
   9683 PBackgroundIDBDatabaseFileParent*
   9684 Database::AllocPBackgroundIDBDatabaseFileParent(const IPCBlob& aIPCBlob) {
   9685  AssertIsOnBackgroundThread();
   9686 
   9687  SafeRefPtr<DatabaseFileInfo> fileInfo = GetBlob(aIPCBlob);
   9688  RefPtr<DatabaseFile> actor;
   9689 
   9690  if (fileInfo) {
   9691    actor = new DatabaseFile(std::move(fileInfo));
   9692  } else {
   9693    // This is a blob we haven't seen before.
   9694    fileInfo = mFileManager->CreateFileInfo();
   9695    if (NS_WARN_IF(!fileInfo)) {
   9696      return nullptr;
   9697    }
   9698 
   9699    actor = new DatabaseFile(IPCBlobUtils::Deserialize(aIPCBlob),
   9700                             std::move(fileInfo));
   9701  }
   9702 
   9703  MOZ_ASSERT(actor);
   9704 
   9705  return actor.forget().take();
   9706 }
   9707 
   9708 bool Database::DeallocPBackgroundIDBDatabaseFileParent(
   9709    PBackgroundIDBDatabaseFileParent* aActor) {
   9710  AssertIsOnBackgroundThread();
   9711  MOZ_ASSERT(aActor);
   9712 
   9713  RefPtr<DatabaseFile> actor = dont_AddRef(static_cast<DatabaseFile*>(aActor));
   9714  return true;
   9715 }
   9716 
   9717 already_AddRefed<PBackgroundIDBTransactionParent>
   9718 Database::AllocPBackgroundIDBTransactionParent(
   9719    const nsTArray<nsString>& aObjectStoreNames, const Mode& aMode,
   9720    const Durability& aDurability) {
   9721  AssertIsOnBackgroundThread();
   9722 
   9723  // Once a database is closed it must not try to open new transactions.
   9724  if (NS_WARN_IF(mClosed)) {
   9725    MOZ_ASSERT_UNLESS_FUZZING(mInvalidated);
   9726    return nullptr;
   9727  }
   9728 
   9729  if (NS_AUUF_OR_WARN_IF(aObjectStoreNames.IsEmpty())) {
   9730    return nullptr;
   9731  }
   9732 
   9733  if (NS_AUUF_OR_WARN_IF(aMode != IDBTransaction::Mode::ReadOnly &&
   9734                         aMode != IDBTransaction::Mode::ReadWrite &&
   9735                         aMode != IDBTransaction::Mode::ReadWriteFlush &&
   9736                         aMode != IDBTransaction::Mode::Cleanup)) {
   9737    return nullptr;
   9738  }
   9739 
   9740  if (NS_AUUF_OR_WARN_IF(aDurability != IDBTransaction::Durability::Default &&
   9741                         aDurability != IDBTransaction::Durability::Strict &&
   9742                         aDurability != IDBTransaction::Durability::Relaxed)) {
   9743    return nullptr;
   9744  }
   9745 
   9746  const ObjectStoreTable& objectStores = mMetadata->mObjectStores;
   9747  const uint32_t nameCount = aObjectStoreNames.Length();
   9748 
   9749  if (NS_AUUF_OR_WARN_IF(nameCount > objectStores.Count())) {
   9750    return nullptr;
   9751  }
   9752 
   9753  QM_TRY_UNWRAP(
   9754      auto objectStoreMetadatas,
   9755      TransformIntoNewArrayAbortOnErr(
   9756          aObjectStoreNames,
   9757          [lastName = Maybe<const nsString&>{},
   9758           &objectStores](const nsString& name) mutable
   9759              -> mozilla::Result<SafeRefPtr<FullObjectStoreMetadata>,
   9760                                 nsresult> {
   9761            if (lastName) {
   9762              // Make sure that this name is sorted properly and not a
   9763              // duplicate.
   9764              if (NS_AUUF_OR_WARN_IF(name <= lastName.ref())) {
   9765                return Err(NS_ERROR_FAILURE);
   9766              }
   9767            }
   9768            lastName = SomeRef(name);
   9769 
   9770            const auto foundIt =
   9771                std::find_if(objectStores.cbegin(), objectStores.cend(),
   9772                             [&name](const auto& entry) {
   9773                               const auto& value = entry.GetData();
   9774                               MOZ_ASSERT(entry.GetKey());
   9775                               return name == value->mCommonMetadata.name() &&
   9776                                      !value->mDeleted;
   9777                             });
   9778            if (foundIt == objectStores.cend()) {
   9779              MOZ_ASSERT_UNLESS_FUZZING(false, "ObjectStore not found.");
   9780              return Err(NS_ERROR_FAILURE);
   9781            }
   9782 
   9783            return foundIt->GetData().clonePtr();
   9784          },
   9785          fallible),
   9786      nullptr);
   9787 
   9788  return MakeSafeRefPtr<NormalTransaction>(SafeRefPtrFromThis(), aMode,
   9789                                           aDurability,
   9790                                           std::move(objectStoreMetadatas))
   9791      .forget();
   9792 }
   9793 
   9794 mozilla::ipc::IPCResult Database::RecvPBackgroundIDBTransactionConstructor(
   9795    PBackgroundIDBTransactionParent* aActor,
   9796    nsTArray<nsString>&& aObjectStoreNames, const Mode& aMode,
   9797    const Durability& aDurability) {
   9798  AssertIsOnBackgroundThread();
   9799  MOZ_ASSERT(aActor);
   9800  MOZ_ASSERT(!aObjectStoreNames.IsEmpty());
   9801  MOZ_ASSERT(aMode == IDBTransaction::Mode::ReadOnly ||
   9802             aMode == IDBTransaction::Mode::ReadWrite ||
   9803             aMode == IDBTransaction::Mode::ReadWriteFlush ||
   9804             aMode == IDBTransaction::Mode::Cleanup);
   9805  MOZ_ASSERT(aDurability == IDBTransaction::Durability::Default ||
   9806             aDurability == IDBTransaction::Durability::Strict ||
   9807             aDurability == IDBTransaction::Durability::Relaxed);
   9808  MOZ_ASSERT(!mClosed);
   9809 
   9810  if (IsInvalidated()) {
   9811    // This is an expected race. We don't want the child to die here, just don't
   9812    // actually do any work.
   9813    return IPC_OK();
   9814  }
   9815 
   9816  if (!gConnectionPool) {
   9817    gConnectionPool = new ConnectionPool();
   9818  }
   9819 
   9820  auto* transaction = static_cast<NormalTransaction*>(aActor);
   9821 
   9822  RefPtr<StartTransactionOp> startOp = new StartTransactionOp(
   9823      SafeRefPtr{transaction, AcquireStrongRefFromRawPtr{}});
   9824 
   9825  uint64_t transactionId = startOp->StartOnConnectionPool(
   9826      GetLoggingInfo()->Id(), mMetadata->mDatabaseId,
   9827      transaction->LoggingSerialNumber(), aObjectStoreNames,
   9828      aMode != IDBTransaction::Mode::ReadOnly);
   9829 
   9830  transaction->Init(transactionId);
   9831 
   9832  if (NS_WARN_IF(!RegisterTransaction(*transaction))) {
   9833    IDB_REPORT_INTERNAL_ERR();
   9834    transaction->Abort(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR, /* aForce */ false);
   9835    return IPC_OK();
   9836  }
   9837 
   9838  return IPC_OK();
   9839 }
   9840 
   9841 mozilla::ipc::IPCResult Database::RecvDeleteMe() {
   9842  AssertIsOnBackgroundThread();
   9843  MOZ_ASSERT(!mActorDestroyed);
   9844 
   9845  QM_WARNONLY_TRY(OkIf(PBackgroundIDBDatabaseParent::Send__delete__(this)));
   9846 
   9847  return IPC_OK();
   9848 }
   9849 
   9850 mozilla::ipc::IPCResult Database::RecvBlocked() {
   9851  AssertIsOnBackgroundThread();
   9852 
   9853  if (NS_WARN_IF(mClosed)) {
   9854    // Even though the sender checks the DB for not being closed, too,
   9855    // there is a potential race with an ongoing origin clearing which
   9856    // might have invalidated the DB in the meantime. Just ignore.
   9857    return IPC_OK();
   9858  }
   9859 
   9860  DatabaseActorInfo* info;
   9861  MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(Id(), &info));
   9862  MOZ_ASSERT(info->mLiveDatabases.contains(this));
   9863 
   9864  if (NS_WARN_IF(!info->mWaitingFactoryOp)) {
   9865    return IPC_FAIL(this, "Database info has no mWaitingFactoryOp!");
   9866  }
   9867 
   9868  info->mWaitingFactoryOp->NoteDatabaseBlocked(this);
   9869 
   9870  return IPC_OK();
   9871 }
   9872 
   9873 mozilla::ipc::IPCResult Database::RecvClose() {
   9874  AssertIsOnBackgroundThread();
   9875 
   9876  if (NS_WARN_IF(!CloseInternal())) {
   9877    return IPC_FAIL(this, "CloseInternal failed!");
   9878  }
   9879 
   9880  return IPC_OK();
   9881 }
   9882 
   9883 void Database::StartTransactionOp::RunOnConnectionThread() {
   9884  MOZ_ASSERT(!IsOnBackgroundThread());
   9885  MOZ_ASSERT(!HasFailed());
   9886 
   9887  IDB_LOG_MARK_PARENT_TRANSACTION("Beginning database work", "DB Start",
   9888                                  IDB_LOG_ID_STRING(mBackgroundChildLoggingId),
   9889                                  mTransactionLoggingSerialNumber);
   9890 
   9891  TransactionDatabaseOperationBase::RunOnConnectionThread();
   9892 }
   9893 
   9894 nsresult Database::StartTransactionOp::DoDatabaseWork(
   9895    DatabaseConnection* aConnection) {
   9896  MOZ_ASSERT(aConnection);
   9897  aConnection->AssertIsOnConnectionThread();
   9898 
   9899  Transaction().SetActiveOnConnectionThread();
   9900 
   9901  if (Transaction().GetMode() == IDBTransaction::Mode::Cleanup) {
   9902    DebugOnly<nsresult> rv = aConnection->DisableQuotaChecks();
   9903    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   9904                         "DisableQuotaChecks failed, trying to continue "
   9905                         "cleanup transaction with quota checks enabled");
   9906  }
   9907 
   9908  if (Transaction().GetMode() != IDBTransaction::Mode::ReadOnly) {
   9909    QM_TRY(MOZ_TO_RESULT(
   9910        aConnection->BeginWriteTransaction(Transaction().GetDurability())));
   9911  }
   9912 
   9913  return NS_OK;
   9914 }
   9915 
   9916 nsresult Database::StartTransactionOp::SendSuccessResult() {
   9917  // We don't need to do anything here.
   9918  return NS_OK;
   9919 }
   9920 
   9921 bool Database::StartTransactionOp::SendFailureResult(
   9922    nsresult /* aResultCode */) {
   9923  IDB_REPORT_INTERNAL_ERR();
   9924 
   9925  // Abort the transaction.
   9926  return false;
   9927 }
   9928 
   9929 void Database::StartTransactionOp::Cleanup() {
   9930 #ifdef DEBUG
   9931  // StartTransactionOp is not a normal database operation that is tied to an
   9932  // actor. Do this to make our assertions happy.
   9933  NoteActorDestroyed();
   9934 #endif
   9935 
   9936  TransactionDatabaseOperationBase::Cleanup();
   9937 }
   9938 
   9939 /*******************************************************************************
   9940 * TransactionBase
   9941 ******************************************************************************/
   9942 
   9943 TransactionBase::TransactionBase(SafeRefPtr<Database> aDatabase, Mode aMode,
   9944                                 Durability aDurability)
   9945    : mDatabase(std::move(aDatabase)),
   9946      mDatabaseId(mDatabase->Id()),
   9947      mLoggingSerialNumber(
   9948          mDatabase->GetLoggingInfo()->NextTransactionSN(aMode)),
   9949      mActiveRequestCount(0),
   9950      mInvalidatedOnAnyThread(false),
   9951      mMode(aMode),
   9952      mDurability(aDurability),
   9953      mResultCode(NS_OK) {
   9954  AssertIsOnBackgroundThread();
   9955  MOZ_ASSERT(mDatabase);
   9956  MOZ_ASSERT(mLoggingSerialNumber);
   9957 }
   9958 
   9959 TransactionBase::~TransactionBase() {
   9960  MOZ_ASSERT(!mActiveRequestCount);
   9961  MOZ_ASSERT(mActorDestroyed);
   9962  MOZ_ASSERT_IF(mInitialized, mCommittedOrAborted);
   9963 }
   9964 
   9965 void TransactionBase::Abort(nsresult aResultCode, bool aForce) {
   9966  AssertIsOnBackgroundThread();
   9967  MOZ_ASSERT(NS_FAILED(aResultCode));
   9968 
   9969  if (NS_SUCCEEDED(mResultCode)) {
   9970    mResultCode = aResultCode;
   9971  }
   9972 
   9973  if (aForce) {
   9974    mForceAborted.EnsureFlipped();
   9975  }
   9976 
   9977  MaybeCommitOrAbort();
   9978 }
   9979 
   9980 mozilla::ipc::IPCResult TransactionBase::RecvCommit(
   9981    IProtocol* aActor, const Maybe<int64_t> aLastRequest) {
   9982  AssertIsOnBackgroundThread();
   9983 
   9984  if (NS_WARN_IF(mCommitOrAbortReceived)) {
   9985    return IPC_FAIL(
   9986        aActor, "Attempt to commit an already comitted/aborted transaction!");
   9987  }
   9988 
   9989  mCommitOrAbortReceived.Flip();
   9990  mLastRequestBeforeCommit.init(aLastRequest);
   9991  MaybeCommitOrAbort();
   9992 
   9993  return IPC_OK();
   9994 }
   9995 
   9996 mozilla::ipc::IPCResult TransactionBase::RecvAbort(IProtocol* aActor,
   9997                                                   nsresult aResultCode) {
   9998  AssertIsOnBackgroundThread();
   9999 
  10000  if (NS_WARN_IF(NS_SUCCEEDED(aResultCode))) {
  10001    return IPC_FAIL(aActor, "aResultCode must not be a success code!");
  10002  }
  10003 
  10004  if (NS_WARN_IF(NS_ERROR_GET_MODULE(aResultCode) !=
  10005                 NS_ERROR_MODULE_DOM_INDEXEDDB)) {
  10006    return IPC_FAIL(aActor, "aResultCode does not refer to IndexedDB!");
  10007  }
  10008 
  10009  if (NS_WARN_IF(mCommitOrAbortReceived)) {
  10010    return IPC_FAIL(
  10011        aActor, "Attempt to abort an already comitted/aborted transaction!");
  10012  }
  10013 
  10014  mCommitOrAbortReceived.Flip();
  10015  Abort(aResultCode, /* aForce */ false);
  10016 
  10017  return IPC_OK();
  10018 }
  10019 
  10020 void TransactionBase::CommitOrAbort() {
  10021  AssertIsOnBackgroundThread();
  10022 
  10023  mCommittedOrAborted.Flip();
  10024 
  10025  if (!mInitialized) {
  10026    return;
  10027  }
  10028 
  10029  // In case of a failed request and explicitly committed transaction, abort
  10030  // (cf. https://w3c.github.io/IndexedDB/#async-execute-request step 5.3
  10031  // vs. 5.4). It's worth emphasizing this can only happen here when we are
  10032  // committing explicitly, otherwise the decision is made by the child.
  10033  if (NS_SUCCEEDED(mResultCode) && mLastFailedRequest &&
  10034      *mLastRequestBeforeCommit &&
  10035      *mLastFailedRequest == **mLastRequestBeforeCommit) {
  10036    mResultCode = NS_ERROR_DOM_INDEXEDDB_ABORT_ERR;
  10037  }
  10038 
  10039  RefPtr<CommitOp> commitOp =
  10040      new CommitOp(SafeRefPtrFromThis(), ClampResultCode(mResultCode));
  10041 
  10042  gConnectionPool->Finish(TransactionId(), commitOp);
  10043 }
  10044 
  10045 SafeRefPtr<FullObjectStoreMetadata>
  10046 TransactionBase::GetMetadataForObjectStoreId(
  10047    IndexOrObjectStoreId aObjectStoreId) const {
  10048  AssertIsOnBackgroundThread();
  10049  MOZ_ASSERT(aObjectStoreId);
  10050 
  10051  if (!aObjectStoreId) {
  10052    return nullptr;
  10053  }
  10054 
  10055  auto metadata = mDatabase->Metadata().mObjectStores.Lookup(aObjectStoreId);
  10056  if (!metadata || (*metadata)->mDeleted) {
  10057    return nullptr;
  10058  }
  10059 
  10060  MOZ_ASSERT((*metadata)->mCommonMetadata.id() == aObjectStoreId);
  10061 
  10062  return metadata->clonePtr();
  10063 }
  10064 
  10065 SafeRefPtr<FullIndexMetadata> TransactionBase::GetMetadataForIndexId(
  10066    FullObjectStoreMetadata& aObjectStoreMetadata,
  10067    IndexOrObjectStoreId aIndexId) const {
  10068  AssertIsOnBackgroundThread();
  10069  MOZ_ASSERT(aIndexId);
  10070 
  10071  if (!aIndexId) {
  10072    return nullptr;
  10073  }
  10074 
  10075  auto metadata = aObjectStoreMetadata.mIndexes.Lookup(aIndexId);
  10076  if (!metadata || (*metadata)->mDeleted) {
  10077    return nullptr;
  10078  }
  10079 
  10080  MOZ_ASSERT((*metadata)->mCommonMetadata.id() == aIndexId);
  10081 
  10082  return metadata->clonePtr();
  10083 }
  10084 
  10085 void TransactionBase::NoteModifiedAutoIncrementObjectStore(
  10086    const SafeRefPtr<FullObjectStoreMetadata>& aMetadata) {
  10087  AssertIsOnConnectionThread();
  10088 
  10089  if (!mModifiedAutoIncrementObjectStoreMetadataArray.Contains(aMetadata)) {
  10090    mModifiedAutoIncrementObjectStoreMetadataArray.AppendElement(
  10091        aMetadata.clonePtr());
  10092  }
  10093 }
  10094 
  10095 void TransactionBase::ForgetModifiedAutoIncrementObjectStore(
  10096    FullObjectStoreMetadata& aMetadata) {
  10097  AssertIsOnConnectionThread();
  10098 
  10099  mModifiedAutoIncrementObjectStoreMetadataArray.RemoveElement(&aMetadata);
  10100 }
  10101 
  10102 bool TransactionBase::VerifyRequestParams(const RequestParams& aParams) const {
  10103  AssertIsOnBackgroundThread();
  10104  MOZ_ASSERT(aParams.type() != RequestParams::T__None);
  10105 
  10106  switch (aParams.type()) {
  10107    case RequestParams::TObjectStoreAddParams: {
  10108      const ObjectStoreAddPutParams& params =
  10109          aParams.get_ObjectStoreAddParams().commonParams();
  10110      if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params))) {
  10111        return false;
  10112      }
  10113      break;
  10114    }
  10115 
  10116    case RequestParams::TObjectStorePutParams: {
  10117      const ObjectStoreAddPutParams& params =
  10118          aParams.get_ObjectStorePutParams().commonParams();
  10119      if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params))) {
  10120        return false;
  10121      }
  10122      break;
  10123    }
  10124 
  10125    case RequestParams::TObjectStoreGetParams: {
  10126      const ObjectStoreGetParams& params = aParams.get_ObjectStoreGetParams();
  10127      const SafeRefPtr<FullObjectStoreMetadata> objectStoreMetadata =
  10128          GetMetadataForObjectStoreId(params.objectStoreId());
  10129      if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata)) {
  10130        return false;
  10131      }
  10132      if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params.keyRange()))) {
  10133        return false;
  10134      }
  10135      break;
  10136    }
  10137 
  10138    case RequestParams::TObjectStoreGetKeyParams: {
  10139      const ObjectStoreGetKeyParams& params =
  10140          aParams.get_ObjectStoreGetKeyParams();
  10141      const SafeRefPtr<FullObjectStoreMetadata> objectStoreMetadata =
  10142          GetMetadataForObjectStoreId(params.objectStoreId());
  10143      if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata)) {
  10144        return false;
  10145      }
  10146      if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params.keyRange()))) {
  10147        return false;
  10148      }
  10149      break;
  10150    }
  10151 
  10152    case RequestParams::TObjectStoreGetAllParams: {
  10153      const ObjectStoreGetAllParams& params =
  10154          aParams.get_ObjectStoreGetAllParams();
  10155      const SafeRefPtr<FullObjectStoreMetadata> objectStoreMetadata =
  10156          GetMetadataForObjectStoreId(params.objectStoreId());
  10157      if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata)) {
  10158        return false;
  10159      }
  10160      if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) {
  10161        return false;
  10162      }
  10163      break;
  10164    }
  10165 
  10166    case RequestParams::TObjectStoreGetAllKeysParams: {
  10167      const ObjectStoreGetAllKeysParams& params =
  10168          aParams.get_ObjectStoreGetAllKeysParams();
  10169      const SafeRefPtr<FullObjectStoreMetadata> objectStoreMetadata =
  10170          GetMetadataForObjectStoreId(params.objectStoreId());
  10171      if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata)) {
  10172        return false;
  10173      }
  10174      if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) {
  10175        return false;
  10176      }
  10177      break;
  10178    }
  10179 
  10180    case RequestParams::TObjectStoreDeleteParams: {
  10181      if (NS_AUUF_OR_WARN_IF(mMode != IDBTransaction::Mode::ReadWrite &&
  10182                             mMode != IDBTransaction::Mode::ReadWriteFlush &&
  10183                             mMode != IDBTransaction::Mode::Cleanup &&
  10184                             mMode != IDBTransaction::Mode::VersionChange)) {
  10185        return false;
  10186      }
  10187 
  10188      const ObjectStoreDeleteParams& params =
  10189          aParams.get_ObjectStoreDeleteParams();
  10190      const SafeRefPtr<FullObjectStoreMetadata> objectStoreMetadata =
  10191          GetMetadataForObjectStoreId(params.objectStoreId());
  10192      if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata)) {
  10193        return false;
  10194      }
  10195      if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params.keyRange()))) {
  10196        return false;
  10197      }
  10198      break;
  10199    }
  10200 
  10201    case RequestParams::TObjectStoreClearParams: {
  10202      if (NS_AUUF_OR_WARN_IF(mMode != IDBTransaction::Mode::ReadWrite &&
  10203                             mMode != IDBTransaction::Mode::ReadWriteFlush &&
  10204                             mMode != IDBTransaction::Mode::Cleanup &&
  10205                             mMode != IDBTransaction::Mode::VersionChange)) {
  10206        return false;
  10207      }
  10208 
  10209      const ObjectStoreClearParams& params =
  10210          aParams.get_ObjectStoreClearParams();
  10211      const SafeRefPtr<FullObjectStoreMetadata> objectStoreMetadata =
  10212          GetMetadataForObjectStoreId(params.objectStoreId());
  10213      if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata)) {
  10214        return false;
  10215      }
  10216      break;
  10217    }
  10218 
  10219    case RequestParams::TObjectStoreCountParams: {
  10220      const ObjectStoreCountParams& params =
  10221          aParams.get_ObjectStoreCountParams();
  10222      const SafeRefPtr<FullObjectStoreMetadata> objectStoreMetadata =
  10223          GetMetadataForObjectStoreId(params.objectStoreId());
  10224      if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata)) {
  10225        return false;
  10226      }
  10227      if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) {
  10228        return false;
  10229      }
  10230      break;
  10231    }
  10232 
  10233    case RequestParams::TIndexGetParams: {
  10234      const IndexGetParams& params = aParams.get_IndexGetParams();
  10235      const SafeRefPtr<FullObjectStoreMetadata> objectStoreMetadata =
  10236          GetMetadataForObjectStoreId(params.objectStoreId());
  10237      if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata)) {
  10238        return false;
  10239      }
  10240      const SafeRefPtr<FullIndexMetadata> indexMetadata =
  10241          GetMetadataForIndexId(*objectStoreMetadata, params.indexId());
  10242      if (NS_AUUF_OR_WARN_IF(!indexMetadata)) {
  10243        return false;
  10244      }
  10245      if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params.keyRange()))) {
  10246        return false;
  10247      }
  10248      break;
  10249    }
  10250 
  10251    case RequestParams::TIndexGetKeyParams: {
  10252      const IndexGetKeyParams& params = aParams.get_IndexGetKeyParams();
  10253      const SafeRefPtr<FullObjectStoreMetadata> objectStoreMetadata =
  10254          GetMetadataForObjectStoreId(params.objectStoreId());
  10255      if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata)) {
  10256        return false;
  10257      }
  10258      const SafeRefPtr<FullIndexMetadata> indexMetadata =
  10259          GetMetadataForIndexId(*objectStoreMetadata, params.indexId());
  10260      if (NS_AUUF_OR_WARN_IF(!indexMetadata)) {
  10261        return false;
  10262      }
  10263      if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params.keyRange()))) {
  10264        return false;
  10265      }
  10266      break;
  10267    }
  10268 
  10269    case RequestParams::TIndexGetAllParams: {
  10270      const IndexGetAllParams& params = aParams.get_IndexGetAllParams();
  10271      const SafeRefPtr<FullObjectStoreMetadata> objectStoreMetadata =
  10272          GetMetadataForObjectStoreId(params.objectStoreId());
  10273      if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata)) {
  10274        return false;
  10275      }
  10276      const SafeRefPtr<FullIndexMetadata> indexMetadata =
  10277          GetMetadataForIndexId(*objectStoreMetadata, params.indexId());
  10278      if (NS_AUUF_OR_WARN_IF(!indexMetadata)) {
  10279        return false;
  10280      }
  10281      if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) {
  10282        return false;
  10283      }
  10284      break;
  10285    }
  10286 
  10287    case RequestParams::TIndexGetAllKeysParams: {
  10288      const IndexGetAllKeysParams& params = aParams.get_IndexGetAllKeysParams();
  10289      const SafeRefPtr<FullObjectStoreMetadata> objectStoreMetadata =
  10290          GetMetadataForObjectStoreId(params.objectStoreId());
  10291      if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata)) {
  10292        return false;
  10293      }
  10294      const SafeRefPtr<FullIndexMetadata> indexMetadata =
  10295          GetMetadataForIndexId(*objectStoreMetadata, params.indexId());
  10296      if (NS_AUUF_OR_WARN_IF(!indexMetadata)) {
  10297        return false;
  10298      }
  10299      if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) {
  10300        return false;
  10301      }
  10302      break;
  10303    }
  10304 
  10305    case RequestParams::TIndexCountParams: {
  10306      const IndexCountParams& params = aParams.get_IndexCountParams();
  10307      const SafeRefPtr<FullObjectStoreMetadata> objectStoreMetadata =
  10308          GetMetadataForObjectStoreId(params.objectStoreId());
  10309      if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata)) {
  10310        return false;
  10311      }
  10312      const SafeRefPtr<FullIndexMetadata> indexMetadata =
  10313          GetMetadataForIndexId(*objectStoreMetadata, params.indexId());
  10314      if (NS_AUUF_OR_WARN_IF(!indexMetadata)) {
  10315        return false;
  10316      }
  10317      if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(params.optionalKeyRange()))) {
  10318        return false;
  10319      }
  10320      break;
  10321    }
  10322 
  10323    default:
  10324      MOZ_CRASH("Should never get here!");
  10325  }
  10326 
  10327  return true;
  10328 }
  10329 
  10330 bool TransactionBase::VerifyRequestParams(
  10331    const SerializedKeyRange& aParams) const {
  10332  AssertIsOnBackgroundThread();
  10333 
  10334  // XXX Check more here?
  10335 
  10336  if (aParams.isOnly()) {
  10337    if (NS_AUUF_OR_WARN_IF(aParams.lower().IsUnset())) {
  10338      return false;
  10339    }
  10340    if (NS_AUUF_OR_WARN_IF(!aParams.upper().IsUnset())) {
  10341      return false;
  10342    }
  10343    if (NS_AUUF_OR_WARN_IF(aParams.lowerOpen())) {
  10344      return false;
  10345    }
  10346    if (NS_AUUF_OR_WARN_IF(aParams.upperOpen())) {
  10347      return false;
  10348    }
  10349  } else if (NS_AUUF_OR_WARN_IF(aParams.lower().IsUnset() &&
  10350                                aParams.upper().IsUnset())) {
  10351    return false;
  10352  }
  10353 
  10354  return true;
  10355 }
  10356 
  10357 bool TransactionBase::VerifyRequestParams(
  10358    const ObjectStoreAddPutParams& aParams) const {
  10359  AssertIsOnBackgroundThread();
  10360 
  10361  if (NS_AUUF_OR_WARN_IF(mMode != IDBTransaction::Mode::ReadWrite &&
  10362                         mMode != IDBTransaction::Mode::ReadWriteFlush &&
  10363                         mMode != IDBTransaction::Mode::VersionChange)) {
  10364    return false;
  10365  }
  10366 
  10367  SafeRefPtr<FullObjectStoreMetadata> objMetadata =
  10368      GetMetadataForObjectStoreId(aParams.objectStoreId());
  10369  if (NS_AUUF_OR_WARN_IF(!objMetadata)) {
  10370    return false;
  10371  }
  10372 
  10373  if (NS_AUUF_OR_WARN_IF(!aParams.cloneInfo().data().data.Size())) {
  10374    return false;
  10375  }
  10376 
  10377  if (objMetadata->mCommonMetadata.autoIncrement() &&
  10378      objMetadata->mCommonMetadata.keyPath().IsValid() &&
  10379      aParams.key().IsUnset()) {
  10380    const SerializedStructuredCloneWriteInfo& cloneInfo = aParams.cloneInfo();
  10381 
  10382    if (NS_AUUF_OR_WARN_IF(!cloneInfo.offsetToKeyProp())) {
  10383      return false;
  10384    }
  10385 
  10386    if (NS_AUUF_OR_WARN_IF(cloneInfo.data().data.Size() < sizeof(uint64_t))) {
  10387      return false;
  10388    }
  10389 
  10390    if (NS_AUUF_OR_WARN_IF(cloneInfo.offsetToKeyProp() >
  10391                           (cloneInfo.data().data.Size() - sizeof(uint64_t)))) {
  10392      return false;
  10393    }
  10394  } else if (NS_AUUF_OR_WARN_IF(aParams.cloneInfo().offsetToKeyProp())) {
  10395    return false;
  10396  }
  10397 
  10398  for (const auto& updateInfo : aParams.indexUpdateInfos()) {
  10399    SafeRefPtr<FullIndexMetadata> indexMetadata =
  10400        GetMetadataForIndexId(*objMetadata, updateInfo.indexId());
  10401    if (NS_AUUF_OR_WARN_IF(!indexMetadata)) {
  10402      return false;
  10403    }
  10404 
  10405    if (NS_AUUF_OR_WARN_IF(updateInfo.value().IsUnset())) {
  10406      return false;
  10407    }
  10408 
  10409    MOZ_ASSERT(!updateInfo.value().GetBuffer().IsEmpty());
  10410  }
  10411 
  10412  for (const FileAddInfo& fileAddInfo : aParams.fileAddInfos()) {
  10413    const PBackgroundIDBDatabaseFileParent* file =
  10414        fileAddInfo.file().AsParent();
  10415 
  10416    switch (fileAddInfo.type()) {
  10417      case StructuredCloneFileBase::eBlob:
  10418        if (NS_AUUF_OR_WARN_IF(!file)) {
  10419          return false;
  10420        }
  10421        break;
  10422 
  10423      case StructuredCloneFileBase::eMutableFile: {
  10424        return false;
  10425      }
  10426 
  10427      case StructuredCloneFileBase::eStructuredClone:
  10428      case StructuredCloneFileBase::eWasmBytecode:
  10429      case StructuredCloneFileBase::eWasmCompiled:
  10430      case StructuredCloneFileBase::eEndGuard:
  10431        MOZ_ASSERT_UNLESS_FUZZING(false, "Unsupported.");
  10432        return false;
  10433 
  10434      default:
  10435        MOZ_CRASH("Should never get here!");
  10436    }
  10437  }
  10438 
  10439  return true;
  10440 }
  10441 
  10442 bool TransactionBase::VerifyRequestParams(
  10443    const Maybe<SerializedKeyRange>& aParams) const {
  10444  AssertIsOnBackgroundThread();
  10445 
  10446  if (aParams.isSome()) {
  10447    if (NS_AUUF_OR_WARN_IF(!VerifyRequestParams(aParams.ref()))) {
  10448      return false;
  10449    }
  10450  }
  10451 
  10452  return true;
  10453 }
  10454 
  10455 void TransactionBase::NoteActiveRequest() {
  10456  AssertIsOnBackgroundThread();
  10457  MOZ_ASSERT(mActiveRequestCount < UINT64_MAX);
  10458 
  10459  mActiveRequestCount++;
  10460 }
  10461 
  10462 void TransactionBase::NoteFinishedRequest(const int64_t aRequestId,
  10463                                          const nsresult aResultCode) {
  10464  AssertIsOnBackgroundThread();
  10465  MOZ_ASSERT(mActiveRequestCount);
  10466 
  10467  mActiveRequestCount--;
  10468 
  10469  if (NS_FAILED(aResultCode)) {
  10470    mLastFailedRequest = Some(aRequestId);
  10471  }
  10472 
  10473  MaybeCommitOrAbort();
  10474 }
  10475 
  10476 void TransactionBase::Invalidate() {
  10477  AssertIsOnBackgroundThread();
  10478  MOZ_ASSERT(mInvalidated == mInvalidatedOnAnyThread);
  10479 
  10480  if (!mInvalidated) {
  10481    mInvalidated.Flip();
  10482    mInvalidatedOnAnyThread = true;
  10483 
  10484    Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR, /* aForce */ false);
  10485  }
  10486 }
  10487 
  10488 PBackgroundIDBRequestParent* TransactionBase::AllocRequest(
  10489    const int64_t aRequestId, RequestParams&& aParams, bool aTrustParams) {
  10490  AssertIsOnBackgroundThread();
  10491  MOZ_ASSERT(aParams.type() != RequestParams::T__None);
  10492 
  10493 #ifdef DEBUG
  10494  // Always verify parameters in DEBUG builds!
  10495  aTrustParams = false;
  10496 #endif
  10497 
  10498  if (NS_AUUF_OR_WARN_IF(!aTrustParams && !VerifyRequestParams(aParams))) {
  10499    return nullptr;
  10500  }
  10501 
  10502  if (NS_AUUF_OR_WARN_IF(mCommitOrAbortReceived)) {
  10503    return nullptr;
  10504  }
  10505 
  10506  RefPtr<NormalTransactionOp> actor;
  10507 
  10508  switch (aParams.type()) {
  10509    case RequestParams::TObjectStoreAddParams:
  10510    case RequestParams::TObjectStorePutParams:
  10511      actor = new ObjectStoreAddOrPutRequestOp(SafeRefPtrFromThis(), aRequestId,
  10512                                               std::move(aParams));
  10513      break;
  10514 
  10515    case RequestParams::TObjectStoreGetParams:
  10516      actor =
  10517          new ObjectStoreGetRequestOp(SafeRefPtrFromThis(), aRequestId, aParams,
  10518                                      /* aGetAll */ false);
  10519      break;
  10520 
  10521    case RequestParams::TObjectStoreGetAllParams:
  10522      actor =
  10523          new ObjectStoreGetRequestOp(SafeRefPtrFromThis(), aRequestId, aParams,
  10524                                      /* aGetAll */ true);
  10525      break;
  10526 
  10527    case RequestParams::TObjectStoreGetKeyParams:
  10528      actor = new ObjectStoreGetKeyRequestOp(SafeRefPtrFromThis(), aRequestId,
  10529                                             aParams,
  10530                                             /* aGetAll */ false);
  10531      break;
  10532 
  10533    case RequestParams::TObjectStoreGetAllKeysParams:
  10534      actor = new ObjectStoreGetKeyRequestOp(SafeRefPtrFromThis(), aRequestId,
  10535                                             aParams,
  10536                                             /* aGetAll */ true);
  10537      break;
  10538 
  10539    case RequestParams::TObjectStoreDeleteParams:
  10540      actor =
  10541          new ObjectStoreDeleteRequestOp(SafeRefPtrFromThis(), aRequestId,
  10542                                         aParams.get_ObjectStoreDeleteParams());
  10543      break;
  10544 
  10545    case RequestParams::TObjectStoreClearParams:
  10546      actor =
  10547          new ObjectStoreClearRequestOp(SafeRefPtrFromThis(), aRequestId,
  10548                                        aParams.get_ObjectStoreClearParams());
  10549      break;
  10550 
  10551    case RequestParams::TObjectStoreCountParams:
  10552      actor =
  10553          new ObjectStoreCountRequestOp(SafeRefPtrFromThis(), aRequestId,
  10554                                        aParams.get_ObjectStoreCountParams());
  10555      break;
  10556 
  10557    case RequestParams::TIndexGetParams:
  10558      actor = new IndexGetRequestOp(SafeRefPtrFromThis(), aRequestId, aParams,
  10559                                    /* aGetAll */ false);
  10560      break;
  10561 
  10562    case RequestParams::TIndexGetKeyParams:
  10563      actor =
  10564          new IndexGetKeyRequestOp(SafeRefPtrFromThis(), aRequestId, aParams,
  10565                                   /* aGetAll */ false);
  10566      break;
  10567 
  10568    case RequestParams::TIndexGetAllParams:
  10569      actor = new IndexGetRequestOp(SafeRefPtrFromThis(), aRequestId, aParams,
  10570                                    /* aGetAll */ true);
  10571      break;
  10572 
  10573    case RequestParams::TIndexGetAllKeysParams:
  10574      actor =
  10575          new IndexGetKeyRequestOp(SafeRefPtrFromThis(), aRequestId, aParams,
  10576                                   /* aGetAll */ true);
  10577      break;
  10578 
  10579    case RequestParams::TIndexCountParams:
  10580      actor =
  10581          new IndexCountRequestOp(SafeRefPtrFromThis(), aRequestId, aParams);
  10582      break;
  10583 
  10584    default:
  10585      MOZ_CRASH("Should never get here!");
  10586  }
  10587 
  10588  MOZ_ASSERT(actor);
  10589 
  10590  // Transfer ownership to IPDL.
  10591  return actor.forget().take();
  10592 }
  10593 
  10594 bool TransactionBase::StartRequest(PBackgroundIDBRequestParent* aActor) {
  10595  AssertIsOnBackgroundThread();
  10596  MOZ_ASSERT(aActor);
  10597 
  10598  auto* op = static_cast<NormalTransactionOp*>(aActor);
  10599 
  10600  if (NS_WARN_IF(!op->Init(*this))) {
  10601    op->Cleanup();
  10602    return false;
  10603  }
  10604 
  10605  op->DispatchToConnectionPool();
  10606  return true;
  10607 }
  10608 
  10609 bool TransactionBase::DeallocRequest(
  10610    PBackgroundIDBRequestParent* const aActor) {
  10611  AssertIsOnBackgroundThread();
  10612  MOZ_ASSERT(aActor);
  10613 
  10614  // Transfer ownership back from IPDL.
  10615  const RefPtr<NormalTransactionOp> actor =
  10616      dont_AddRef(static_cast<NormalTransactionOp*>(aActor));
  10617  return true;
  10618 }
  10619 
  10620 already_AddRefed<PBackgroundIDBCursorParent> TransactionBase::AllocCursor(
  10621    const OpenCursorParams& aParams, bool aTrustParams) {
  10622  AssertIsOnBackgroundThread();
  10623  MOZ_ASSERT(aParams.type() != OpenCursorParams::T__None);
  10624 
  10625 #ifdef DEBUG
  10626  // Always verify parameters in DEBUG builds!
  10627  aTrustParams = false;
  10628 #endif
  10629 
  10630  const OpenCursorParams::Type type = aParams.type();
  10631  SafeRefPtr<FullObjectStoreMetadata> objectStoreMetadata;
  10632  SafeRefPtr<FullIndexMetadata> indexMetadata;
  10633  CursorBase::Direction direction;
  10634 
  10635  // First extract the parameters common to all open cursor variants.
  10636  const auto& commonParams = GetCommonOpenCursorParams(aParams);
  10637  objectStoreMetadata =
  10638      GetMetadataForObjectStoreId(commonParams.objectStoreId());
  10639  if (NS_AUUF_OR_WARN_IF(!objectStoreMetadata)) {
  10640    return nullptr;
  10641  }
  10642  if (aTrustParams && NS_AUUF_OR_WARN_IF(!VerifyRequestParams(
  10643                          commonParams.optionalKeyRange()))) {
  10644    return nullptr;
  10645  }
  10646  direction = commonParams.direction();
  10647 
  10648  // Now, for the index open cursor variants, extract the additional parameter.
  10649  if (type == OpenCursorParams::TIndexOpenCursorParams ||
  10650      type == OpenCursorParams::TIndexOpenKeyCursorParams) {
  10651    const auto& commonIndexParams = GetCommonIndexOpenCursorParams(aParams);
  10652    indexMetadata = GetMetadataForIndexId(*objectStoreMetadata,
  10653                                          commonIndexParams.indexId());
  10654    if (NS_AUUF_OR_WARN_IF(!indexMetadata)) {
  10655      return nullptr;
  10656    }
  10657  }
  10658 
  10659  if (NS_AUUF_OR_WARN_IF(mCommitOrAbortReceived)) {
  10660    return nullptr;
  10661  }
  10662 
  10663  // Create Cursor and transfer ownership to IPDL.
  10664  switch (type) {
  10665    case OpenCursorParams::TObjectStoreOpenCursorParams:
  10666      MOZ_ASSERT(!indexMetadata);
  10667      return MakeAndAddRef<Cursor<IDBCursorType::ObjectStore>>(
  10668          SafeRefPtrFromThis(), std::move(objectStoreMetadata), direction,
  10669          CursorBase::ConstructFromTransactionBase{});
  10670    case OpenCursorParams::TObjectStoreOpenKeyCursorParams:
  10671      MOZ_ASSERT(!indexMetadata);
  10672      return MakeAndAddRef<Cursor<IDBCursorType::ObjectStoreKey>>(
  10673          SafeRefPtrFromThis(), std::move(objectStoreMetadata), direction,
  10674          CursorBase::ConstructFromTransactionBase{});
  10675    case OpenCursorParams::TIndexOpenCursorParams:
  10676      return MakeAndAddRef<Cursor<IDBCursorType::Index>>(
  10677          SafeRefPtrFromThis(), std::move(objectStoreMetadata),
  10678          std::move(indexMetadata), direction,
  10679          CursorBase::ConstructFromTransactionBase{});
  10680    case OpenCursorParams::TIndexOpenKeyCursorParams:
  10681      return MakeAndAddRef<Cursor<IDBCursorType::IndexKey>>(
  10682          SafeRefPtrFromThis(), std::move(objectStoreMetadata),
  10683          std::move(indexMetadata), direction,
  10684          CursorBase::ConstructFromTransactionBase{});
  10685    default:
  10686      MOZ_CRASH("Cannot get here.");
  10687  }
  10688 }
  10689 
  10690 bool TransactionBase::StartCursor(PBackgroundIDBCursorParent* const aActor,
  10691                                  const int64_t aRequestId,
  10692                                  const OpenCursorParams& aParams) {
  10693  AssertIsOnBackgroundThread();
  10694  MOZ_ASSERT(aActor);
  10695  MOZ_ASSERT(aParams.type() != OpenCursorParams::T__None);
  10696 
  10697  auto* const op = static_cast<CursorBase*>(aActor);
  10698 
  10699  if (NS_WARN_IF(!op->Start(aRequestId, aParams))) {
  10700    return false;
  10701  }
  10702 
  10703  return true;
  10704 }
  10705 
  10706 /*******************************************************************************
  10707 * NormalTransaction
  10708 ******************************************************************************/
  10709 
  10710 NormalTransaction::NormalTransaction(
  10711    SafeRefPtr<Database> aDatabase, TransactionBase::Mode aMode,
  10712    TransactionBase::Durability aDurability,
  10713    nsTArray<SafeRefPtr<FullObjectStoreMetadata>>&& aObjectStores)
  10714    : TransactionBase(std::move(aDatabase), aMode, aDurability),
  10715      mObjectStores{std::move(aObjectStores)} {
  10716  AssertIsOnBackgroundThread();
  10717  MOZ_ASSERT(!mObjectStores.IsEmpty());
  10718 }
  10719 
  10720 bool NormalTransaction::IsSameProcessActor() {
  10721  AssertIsOnBackgroundThread();
  10722 
  10723  PBackgroundParent* const actor = Manager()->Manager()->Manager();
  10724  MOZ_ASSERT(actor);
  10725 
  10726  return !BackgroundParent::IsOtherProcessActor(actor);
  10727 }
  10728 
  10729 void NormalTransaction::SendCompleteNotification(nsresult aResult) {
  10730  AssertIsOnBackgroundThread();
  10731 
  10732  if (!IsActorDestroyed()) {
  10733    (void)SendComplete(aResult);
  10734  }
  10735 }
  10736 
  10737 void NormalTransaction::ActorDestroy(ActorDestroyReason aWhy) {
  10738  AssertIsOnBackgroundThread();
  10739 
  10740  NoteActorDestroyed();
  10741 
  10742  if (!mCommittedOrAborted) {
  10743    if (NS_SUCCEEDED(mResultCode)) {
  10744      IDB_REPORT_INTERNAL_ERR();
  10745      mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
  10746    }
  10747 
  10748    mForceAborted.EnsureFlipped();
  10749 
  10750    MaybeCommitOrAbort();
  10751  }
  10752 }
  10753 
  10754 mozilla::ipc::IPCResult NormalTransaction::RecvDeleteMe() {
  10755  AssertIsOnBackgroundThread();
  10756  MOZ_ASSERT(!IsActorDestroyed());
  10757 
  10758  QM_WARNONLY_TRY(OkIf(PBackgroundIDBTransactionParent::Send__delete__(this)));
  10759 
  10760  return IPC_OK();
  10761 }
  10762 
  10763 mozilla::ipc::IPCResult NormalTransaction::RecvCommit(
  10764    const Maybe<int64_t>& aLastRequest) {
  10765  AssertIsOnBackgroundThread();
  10766 
  10767  return TransactionBase::RecvCommit(this, aLastRequest);
  10768 }
  10769 
  10770 mozilla::ipc::IPCResult NormalTransaction::RecvAbort(
  10771    const nsresult& aResultCode) {
  10772  AssertIsOnBackgroundThread();
  10773 
  10774  return TransactionBase::RecvAbort(this, aResultCode);
  10775 }
  10776 
  10777 PBackgroundIDBRequestParent*
  10778 NormalTransaction::AllocPBackgroundIDBRequestParent(
  10779    const int64_t& aRequestId, const RequestParams& aParams) {
  10780  AssertIsOnBackgroundThread();
  10781  MOZ_ASSERT(aParams.type() != RequestParams::T__None);
  10782 
  10783  return AllocRequest(aRequestId,
  10784                      std::move(const_cast<RequestParams&>(aParams)),
  10785                      IsSameProcessActor());
  10786 }
  10787 
  10788 mozilla::ipc::IPCResult NormalTransaction::RecvPBackgroundIDBRequestConstructor(
  10789    PBackgroundIDBRequestParent* const aActor, const int64_t& aRequestId,
  10790    const RequestParams& aParams) {
  10791  AssertIsOnBackgroundThread();
  10792  MOZ_ASSERT(aActor);
  10793  MOZ_ASSERT(aParams.type() != RequestParams::T__None);
  10794 
  10795  if (!StartRequest(aActor)) {
  10796    return IPC_FAIL(this, "StartRequest failed!");
  10797  }
  10798  return IPC_OK();
  10799 }
  10800 
  10801 bool NormalTransaction::DeallocPBackgroundIDBRequestParent(
  10802    PBackgroundIDBRequestParent* const aActor) {
  10803  AssertIsOnBackgroundThread();
  10804  MOZ_ASSERT(aActor);
  10805 
  10806  return DeallocRequest(aActor);
  10807 }
  10808 
  10809 already_AddRefed<PBackgroundIDBCursorParent>
  10810 NormalTransaction::AllocPBackgroundIDBCursorParent(
  10811    const int64_t& aRequestId, const OpenCursorParams& aParams) {
  10812  AssertIsOnBackgroundThread();
  10813 
  10814  return AllocCursor(aParams, IsSameProcessActor());
  10815 }
  10816 
  10817 mozilla::ipc::IPCResult NormalTransaction::RecvPBackgroundIDBCursorConstructor(
  10818    PBackgroundIDBCursorParent* const aActor, const int64_t& aRequestId,
  10819    const OpenCursorParams& aParams) {
  10820  AssertIsOnBackgroundThread();
  10821  MOZ_ASSERT(aActor);
  10822  MOZ_ASSERT(aParams.type() != OpenCursorParams::T__None);
  10823 
  10824  if (!StartCursor(aActor, aRequestId, aParams)) {
  10825    return IPC_FAIL(this, "StartCursor failed!");
  10826  }
  10827  return IPC_OK();
  10828 }
  10829 
  10830 /*******************************************************************************
  10831 * VersionChangeTransaction
  10832 ******************************************************************************/
  10833 
  10834 VersionChangeTransaction::VersionChangeTransaction(
  10835    OpenDatabaseOp* aOpenDatabaseOp)
  10836    : TransactionBase(aOpenDatabaseOp->mDatabase.clonePtr(),
  10837                      IDBTransaction::Mode::VersionChange,
  10838                      // VersionChange must not change durability.
  10839                      IDBTransaction::Durability::Default),  // Not used.
  10840      mOpenDatabaseOp(aOpenDatabaseOp) {
  10841  AssertIsOnBackgroundThread();
  10842  MOZ_ASSERT(aOpenDatabaseOp);
  10843 }
  10844 
  10845 VersionChangeTransaction::~VersionChangeTransaction() {
  10846 #ifdef DEBUG
  10847  // Silence the base class' destructor assertion if we never made this actor
  10848  // live.
  10849  FakeActorDestroyed();
  10850 #endif
  10851 }
  10852 
  10853 bool VersionChangeTransaction::IsSameProcessActor() {
  10854  AssertIsOnBackgroundThread();
  10855 
  10856  PBackgroundParent* actor = Manager()->Manager()->Manager();
  10857  MOZ_ASSERT(actor);
  10858 
  10859  return !BackgroundParent::IsOtherProcessActor(actor);
  10860 }
  10861 
  10862 void VersionChangeTransaction::SetActorAlive() {
  10863  AssertIsOnBackgroundThread();
  10864  MOZ_ASSERT(!IsActorDestroyed());
  10865 
  10866  mActorWasAlive.Flip();
  10867 }
  10868 
  10869 bool VersionChangeTransaction::CopyDatabaseMetadata() {
  10870  AssertIsOnBackgroundThread();
  10871  MOZ_ASSERT(!mOldMetadata);
  10872 
  10873  const auto& origMetadata = GetDatabase().Metadata();
  10874 
  10875  SafeRefPtr<FullDatabaseMetadata> newMetadata = origMetadata.Duplicate();
  10876  if (NS_WARN_IF(!newMetadata)) {
  10877    return false;
  10878  }
  10879 
  10880  // Replace the live metadata with the new mutable copy.
  10881  DatabaseActorInfo* info;
  10882  MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(origMetadata.mDatabaseId, &info));
  10883  MOZ_ASSERT(!info->mLiveDatabases.isEmpty());
  10884  MOZ_ASSERT(info->mMetadata == &origMetadata);
  10885 
  10886  mOldMetadata = std::move(info->mMetadata);
  10887  info->mMetadata = std::move(newMetadata);
  10888 
  10889  // Replace metadata pointers for all live databases.
  10890  for (Database* const liveDatabase : info->mLiveDatabases) {
  10891    liveDatabase->mMetadata = info->mMetadata.clonePtr();
  10892  }
  10893 
  10894  return true;
  10895 }
  10896 
  10897 void VersionChangeTransaction::UpdateMetadata(nsresult aResult) {
  10898  AssertIsOnBackgroundThread();
  10899  MOZ_ASSERT(mOpenDatabaseOp);
  10900  MOZ_ASSERT(!!mActorWasAlive == !!mOpenDatabaseOp->mDatabase);
  10901  MOZ_ASSERT_IF(mActorWasAlive, !mOpenDatabaseOp->mDatabaseId.ref().IsEmpty());
  10902 
  10903  if (IsActorDestroyed() || !mActorWasAlive) {
  10904    return;
  10905  }
  10906 
  10907  SafeRefPtr<FullDatabaseMetadata> oldMetadata = std::move(mOldMetadata);
  10908 
  10909  DatabaseActorInfo* info;
  10910  if (!gLiveDatabaseHashtable->Get(oldMetadata->mDatabaseId, &info)) {
  10911    return;
  10912  }
  10913 
  10914  MOZ_ASSERT(!info->mLiveDatabases.isEmpty());
  10915 
  10916  if (NS_SUCCEEDED(aResult)) {
  10917    // Remove all deleted objectStores and indexes, then mark immutable.
  10918    info->mMetadata->mObjectStores.RemoveIf([](const auto& objectStoreIter) {
  10919      MOZ_ASSERT(objectStoreIter.Key());
  10920      const SafeRefPtr<FullObjectStoreMetadata>& metadata =
  10921          objectStoreIter.Data();
  10922      MOZ_ASSERT(metadata);
  10923 
  10924      if (metadata->mDeleted) {
  10925        return true;
  10926      }
  10927 
  10928      metadata->mIndexes.RemoveIf([](const auto& indexIter) -> bool {
  10929        MOZ_ASSERT(indexIter.Key());
  10930        const SafeRefPtr<FullIndexMetadata>& index = indexIter.Data();
  10931        MOZ_ASSERT(index);
  10932 
  10933        return index->mDeleted;
  10934      });
  10935      metadata->mIndexes.MarkImmutable();
  10936 
  10937      return false;
  10938    });
  10939 
  10940    info->mMetadata->mObjectStores.MarkImmutable();
  10941  } else {
  10942    // Replace metadata pointers for all live databases.
  10943    info->mMetadata = std::move(oldMetadata);
  10944 
  10945    for (Database* const liveDatabase : info->mLiveDatabases) {
  10946      liveDatabase->mMetadata = info->mMetadata.clonePtr();
  10947    }
  10948  }
  10949 }
  10950 
  10951 void VersionChangeTransaction::SendCompleteNotification(nsresult aResult) {
  10952  AssertIsOnBackgroundThread();
  10953  MOZ_ASSERT(mOpenDatabaseOp);
  10954  MOZ_ASSERT(!mOpenDatabaseOp->mCompleteCallback);
  10955  MOZ_ASSERT_IF(!mActorWasAlive, mOpenDatabaseOp->HasFailed());
  10956  MOZ_ASSERT_IF(!mActorWasAlive, mOpenDatabaseOp->mState >
  10957                                     OpenDatabaseOp::State::SendingResults);
  10958 
  10959  const RefPtr<OpenDatabaseOp> openDatabaseOp = std::move(mOpenDatabaseOp);
  10960 
  10961  if (!mActorWasAlive) {
  10962    return;
  10963  }
  10964 
  10965  openDatabaseOp->mCompleteCallback =
  10966      [self = SafeRefPtr{this, AcquireStrongRefFromRawPtr{}}, aResult]() {
  10967        if (!self->IsActorDestroyed()) {
  10968          (void)self->SendComplete(aResult);
  10969        }
  10970      };
  10971 
  10972  auto handleError = [openDatabaseOp](const nsresult rv) {
  10973    openDatabaseOp->SetFailureCodeIfUnset(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
  10974 
  10975    openDatabaseOp->mState = OpenDatabaseOp::State::SendingResults;
  10976 
  10977    MOZ_ALWAYS_SUCCEEDS(openDatabaseOp->Run());
  10978  };
  10979 
  10980  if (NS_FAILED(aResult)) {
  10981    // 3.3.1 Opening a database:
  10982    // "If the upgrade transaction was aborted, run the steps for closing a
  10983    //  database connection with connection, create and return a new AbortError
  10984    //  exception and abort these steps."
  10985    handleError(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
  10986    return;
  10987  }
  10988 
  10989  openDatabaseOp->mState = OpenDatabaseOp::State::DatabaseWorkVersionUpdate;
  10990 
  10991  QuotaManager* const quotaManager = QuotaManager::Get();
  10992  MOZ_ASSERT(quotaManager);
  10993 
  10994  QM_TRY(MOZ_TO_RESULT(quotaManager->IOThread()->Dispatch(openDatabaseOp,
  10995                                                          NS_DISPATCH_NORMAL))
  10996             .mapErr(
  10997                 [](const auto) { return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; }),
  10998         QM_VOID, handleError);
  10999 }
  11000 
  11001 void VersionChangeTransaction::ActorDestroy(ActorDestroyReason aWhy) {
  11002  AssertIsOnBackgroundThread();
  11003 
  11004  NoteActorDestroyed();
  11005 
  11006  if (!mCommittedOrAborted) {
  11007    if (NS_SUCCEEDED(mResultCode)) {
  11008      IDB_REPORT_INTERNAL_ERR();
  11009      mResultCode = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
  11010    }
  11011 
  11012    mForceAborted.EnsureFlipped();
  11013 
  11014    MaybeCommitOrAbort();
  11015  }
  11016 }
  11017 
  11018 mozilla::ipc::IPCResult VersionChangeTransaction::RecvDeleteMe() {
  11019  AssertIsOnBackgroundThread();
  11020  MOZ_ASSERT(!IsActorDestroyed());
  11021 
  11022  QM_WARNONLY_TRY(
  11023      OkIf(PBackgroundIDBVersionChangeTransactionParent::Send__delete__(this)));
  11024 
  11025  return IPC_OK();
  11026 }
  11027 
  11028 mozilla::ipc::IPCResult VersionChangeTransaction::RecvCommit(
  11029    const Maybe<int64_t>& aLastRequest) {
  11030  AssertIsOnBackgroundThread();
  11031 
  11032  return TransactionBase::RecvCommit(this, aLastRequest);
  11033 }
  11034 
  11035 mozilla::ipc::IPCResult VersionChangeTransaction::RecvAbort(
  11036    const nsresult& aResultCode) {
  11037  AssertIsOnBackgroundThread();
  11038 
  11039  return TransactionBase::RecvAbort(this, aResultCode);
  11040 }
  11041 
  11042 mozilla::ipc::IPCResult VersionChangeTransaction::RecvCreateObjectStore(
  11043    const ObjectStoreMetadata& aMetadata) {
  11044  AssertIsOnBackgroundThread();
  11045 
  11046  if (NS_WARN_IF(!aMetadata.id())) {
  11047    return IPC_FAIL(this, "No metadata ID!");
  11048  }
  11049 
  11050  const SafeRefPtr<FullDatabaseMetadata> dbMetadata =
  11051      GetDatabase().MetadataPtr();
  11052 
  11053  if (NS_WARN_IF(aMetadata.id() != dbMetadata->mNextObjectStoreId)) {
  11054    return IPC_FAIL(this, "Requested metadata ID does not match next ID!");
  11055  }
  11056 
  11057  if (NS_WARN_IF(
  11058          MatchMetadataNameOrId(dbMetadata->mObjectStores, aMetadata.id(),
  11059                                SomeRef<const nsAString&>(aMetadata.name()))
  11060              .isSome())) {
  11061    return IPC_FAIL(this, "MatchMetadataNameOrId failed!");
  11062  }
  11063 
  11064  if (NS_WARN_IF(mCommitOrAbortReceived)) {
  11065    return IPC_FAIL(this, "Transaction is already committed/aborted!");
  11066  }
  11067 
  11068  const int64_t initialAutoIncrementId = aMetadata.autoIncrement() ? 1 : 0;
  11069  auto newMetadata = MakeSafeRefPtr<FullObjectStoreMetadata>(
  11070      aMetadata, FullObjectStoreMetadata::AutoIncrementIds{
  11071                     initialAutoIncrementId, initialAutoIncrementId});
  11072 
  11073  if (NS_WARN_IF(!dbMetadata->mObjectStores.InsertOrUpdate(
  11074          aMetadata.id(), std::move(newMetadata), fallible))) {
  11075    return IPC_FAIL(this, "mObjectStores.InsertOrUpdate failed!");
  11076  }
  11077 
  11078  dbMetadata->mNextObjectStoreId++;
  11079 
  11080  RefPtr<CreateObjectStoreOp> op = new CreateObjectStoreOp(
  11081      SafeRefPtrFromThis().downcast<VersionChangeTransaction>(), aMetadata);
  11082 
  11083  if (NS_WARN_IF(!op->Init(*this))) {
  11084    op->Cleanup();
  11085    return IPC_FAIL(this, "ObjectStoreOp initialization failed!");
  11086  }
  11087 
  11088  op->DispatchToConnectionPool();
  11089 
  11090  return IPC_OK();
  11091 }
  11092 
  11093 mozilla::ipc::IPCResult VersionChangeTransaction::RecvDeleteObjectStore(
  11094    const IndexOrObjectStoreId& aObjectStoreId) {
  11095  AssertIsOnBackgroundThread();
  11096 
  11097  if (NS_WARN_IF(!aObjectStoreId)) {
  11098    return IPC_FAIL(this, "No ObjectStoreId!");
  11099  }
  11100 
  11101  const auto& dbMetadata = GetDatabase().Metadata();
  11102  MOZ_ASSERT(dbMetadata.mNextObjectStoreId > 0);
  11103 
  11104  if (NS_WARN_IF(aObjectStoreId >= dbMetadata.mNextObjectStoreId)) {
  11105    return IPC_FAIL(this, "Invalid ObjectStoreId!");
  11106  }
  11107 
  11108  SafeRefPtr<FullObjectStoreMetadata> foundMetadata =
  11109      GetMetadataForObjectStoreId(aObjectStoreId);
  11110 
  11111  if (NS_WARN_IF(!foundMetadata)) {
  11112    return IPC_FAIL(this, "No metadata found for ObjectStoreId!");
  11113  }
  11114 
  11115  if (NS_WARN_IF(mCommitOrAbortReceived)) {
  11116    return IPC_FAIL(this, "Transaction is already committed/aborted!");
  11117  }
  11118 
  11119  foundMetadata->mDeleted.Flip();
  11120 
  11121  DebugOnly<bool> foundTargetId = false;
  11122  const bool isLastObjectStore = std::all_of(
  11123      dbMetadata.mObjectStores.begin(), dbMetadata.mObjectStores.end(),
  11124      [&foundTargetId, aObjectStoreId](const auto& objectStoreEntry) -> bool {
  11125        if (uint64_t(aObjectStoreId) == objectStoreEntry.GetKey()) {
  11126          foundTargetId = true;
  11127          return true;
  11128        }
  11129 
  11130        return objectStoreEntry.GetData()->mDeleted;
  11131      });
  11132  MOZ_ASSERT_IF(isLastObjectStore, foundTargetId);
  11133 
  11134  RefPtr<DeleteObjectStoreOp> op = new DeleteObjectStoreOp(
  11135      SafeRefPtrFromThis().downcast<VersionChangeTransaction>(),
  11136      std::move(foundMetadata), isLastObjectStore);
  11137 
  11138  if (NS_WARN_IF(!op->Init(*this))) {
  11139    op->Cleanup();
  11140    return IPC_FAIL(this, "ObjectStoreOp initialization failed!");
  11141  }
  11142 
  11143  op->DispatchToConnectionPool();
  11144 
  11145  return IPC_OK();
  11146 }
  11147 
  11148 mozilla::ipc::IPCResult VersionChangeTransaction::RecvRenameObjectStore(
  11149    const IndexOrObjectStoreId& aObjectStoreId, const nsAString& aName) {
  11150  AssertIsOnBackgroundThread();
  11151 
  11152  if (NS_WARN_IF(!aObjectStoreId)) {
  11153    return IPC_FAIL(this, "No ObjectStoreId!");
  11154  }
  11155 
  11156  {
  11157    const auto& dbMetadata = GetDatabase().Metadata();
  11158    MOZ_ASSERT(dbMetadata.mNextObjectStoreId > 0);
  11159 
  11160    if (NS_WARN_IF(aObjectStoreId >= dbMetadata.mNextObjectStoreId)) {
  11161      return IPC_FAIL(this, "Invalid ObjectStoreId!");
  11162    }
  11163  }
  11164 
  11165  SafeRefPtr<FullObjectStoreMetadata> foundMetadata =
  11166      GetMetadataForObjectStoreId(aObjectStoreId);
  11167 
  11168  if (NS_WARN_IF(!foundMetadata)) {
  11169    return IPC_FAIL(this, "No metadata found for ObjectStoreId!");
  11170  }
  11171 
  11172  if (NS_WARN_IF(mCommitOrAbortReceived)) {
  11173    return IPC_FAIL(this, "Transaction is already committed/aborted!");
  11174  }
  11175 
  11176  foundMetadata->mCommonMetadata.name() = aName;
  11177 
  11178  RefPtr<RenameObjectStoreOp> renameOp = new RenameObjectStoreOp(
  11179      SafeRefPtrFromThis().downcast<VersionChangeTransaction>(),
  11180      *foundMetadata);
  11181 
  11182  if (NS_WARN_IF(!renameOp->Init(*this))) {
  11183    renameOp->Cleanup();
  11184    return IPC_FAIL(this, "ObjectStoreOp initialization failed!");
  11185  }
  11186 
  11187  renameOp->DispatchToConnectionPool();
  11188 
  11189  return IPC_OK();
  11190 }
  11191 
  11192 mozilla::ipc::IPCResult VersionChangeTransaction::RecvCreateIndex(
  11193    const IndexOrObjectStoreId& aObjectStoreId,
  11194    const IndexMetadata& aMetadata) {
  11195  AssertIsOnBackgroundThread();
  11196 
  11197  if (NS_WARN_IF(!aObjectStoreId)) {
  11198    return IPC_FAIL(this, "No ObjectStoreId!");
  11199  }
  11200 
  11201  if (NS_WARN_IF(!aMetadata.id())) {
  11202    return IPC_FAIL(this, "No Metadata id!");
  11203  }
  11204 
  11205  const auto dbMetadata = GetDatabase().MetadataPtr();
  11206 
  11207  if (NS_WARN_IF(aMetadata.id() != dbMetadata->mNextIndexId)) {
  11208    return IPC_FAIL(this, "Requested metadata ID does not match next ID!");
  11209  }
  11210 
  11211  SafeRefPtr<FullObjectStoreMetadata> foundObjectStoreMetadata =
  11212      GetMetadataForObjectStoreId(aObjectStoreId);
  11213 
  11214  if (NS_WARN_IF(!foundObjectStoreMetadata)) {
  11215    return IPC_FAIL(this, "GetMetadataForObjectStoreId failed!");
  11216  }
  11217 
  11218  if (NS_WARN_IF(MatchMetadataNameOrId(
  11219                     foundObjectStoreMetadata->mIndexes, aMetadata.id(),
  11220                     SomeRef<const nsAString&>(aMetadata.name()))
  11221                     .isSome())) {
  11222    return IPC_FAIL(this, "MatchMetadataNameOrId failed!");
  11223  }
  11224 
  11225  if (NS_WARN_IF(mCommitOrAbortReceived)) {
  11226    return IPC_FAIL(this, "Transaction is already committed/aborted!");
  11227  }
  11228 
  11229  auto newMetadata = MakeSafeRefPtr<FullIndexMetadata>();
  11230  newMetadata->mCommonMetadata = aMetadata;
  11231 
  11232  if (NS_WARN_IF(!foundObjectStoreMetadata->mIndexes.InsertOrUpdate(
  11233          aMetadata.id(), std::move(newMetadata), fallible))) {
  11234    return IPC_FAIL(this, "mIndexes.InsertOrUpdate failed!");
  11235  }
  11236 
  11237  dbMetadata->mNextIndexId++;
  11238 
  11239  RefPtr<CreateIndexOp> op = new CreateIndexOp(
  11240      SafeRefPtrFromThis().downcast<VersionChangeTransaction>(), aObjectStoreId,
  11241      aMetadata);
  11242 
  11243  if (NS_WARN_IF(!op->Init(*this))) {
  11244    op->Cleanup();
  11245    return IPC_FAIL(this, "ObjectStoreOp initialization failed!");
  11246  }
  11247 
  11248  op->DispatchToConnectionPool();
  11249 
  11250  return IPC_OK();
  11251 }
  11252 
  11253 mozilla::ipc::IPCResult VersionChangeTransaction::RecvDeleteIndex(
  11254    const IndexOrObjectStoreId& aObjectStoreId,
  11255    const IndexOrObjectStoreId& aIndexId) {
  11256  AssertIsOnBackgroundThread();
  11257 
  11258  if (NS_WARN_IF(!aObjectStoreId)) {
  11259    return IPC_FAIL(this, "No ObjectStoreId!");
  11260  }
  11261 
  11262  if (NS_WARN_IF(!aIndexId)) {
  11263    return IPC_FAIL(this, "No Index id!");
  11264  }
  11265  {
  11266    const auto& dbMetadata = GetDatabase().Metadata();
  11267    MOZ_ASSERT(dbMetadata.mNextObjectStoreId > 0);
  11268    MOZ_ASSERT(dbMetadata.mNextIndexId > 0);
  11269 
  11270    if (NS_WARN_IF(aObjectStoreId >= dbMetadata.mNextObjectStoreId)) {
  11271      return IPC_FAIL(this, "Requested ObjectStoreId does not match next ID!");
  11272    }
  11273 
  11274    if (NS_WARN_IF(aIndexId >= dbMetadata.mNextIndexId)) {
  11275      return IPC_FAIL(this, "Requested IndexId does not match next ID!");
  11276    }
  11277  }
  11278 
  11279  SafeRefPtr<FullObjectStoreMetadata> foundObjectStoreMetadata =
  11280      GetMetadataForObjectStoreId(aObjectStoreId);
  11281 
  11282  if (NS_WARN_IF(!foundObjectStoreMetadata)) {
  11283    return IPC_FAIL(this, "GetMetadataForObjectStoreId failed!");
  11284  }
  11285 
  11286  SafeRefPtr<FullIndexMetadata> foundIndexMetadata =
  11287      GetMetadataForIndexId(*foundObjectStoreMetadata, aIndexId);
  11288 
  11289  if (NS_WARN_IF(!foundIndexMetadata)) {
  11290    return IPC_FAIL(this, "GetMetadataForIndexId failed!");
  11291  }
  11292 
  11293  if (NS_WARN_IF(mCommitOrAbortReceived)) {
  11294    return IPC_FAIL(this, "Transaction is already committed/aborted!");
  11295  }
  11296 
  11297  foundIndexMetadata->mDeleted.Flip();
  11298 
  11299  DebugOnly<bool> foundTargetId = false;
  11300  const bool isLastIndex =
  11301      std::all_of(foundObjectStoreMetadata->mIndexes.cbegin(),
  11302                  foundObjectStoreMetadata->mIndexes.cend(),
  11303                  [&foundTargetId, aIndexId](const auto& indexEntry) -> bool {
  11304                    if (uint64_t(aIndexId) == indexEntry.GetKey()) {
  11305                      foundTargetId = true;
  11306                      return true;
  11307                    }
  11308 
  11309                    return indexEntry.GetData()->mDeleted;
  11310                  });
  11311  MOZ_ASSERT_IF(isLastIndex, foundTargetId);
  11312 
  11313  RefPtr<DeleteIndexOp> op = new DeleteIndexOp(
  11314      SafeRefPtrFromThis().downcast<VersionChangeTransaction>(), aObjectStoreId,
  11315      aIndexId, foundIndexMetadata->mCommonMetadata.unique(), isLastIndex);
  11316 
  11317  if (NS_WARN_IF(!op->Init(*this))) {
  11318    op->Cleanup();
  11319    return IPC_FAIL(this, "ObjectStoreOp initialization failed!");
  11320  }
  11321 
  11322  op->DispatchToConnectionPool();
  11323 
  11324  return IPC_OK();
  11325 }
  11326 
  11327 mozilla::ipc::IPCResult VersionChangeTransaction::RecvRenameIndex(
  11328    const IndexOrObjectStoreId& aObjectStoreId,
  11329    const IndexOrObjectStoreId& aIndexId, const nsAString& aName) {
  11330  AssertIsOnBackgroundThread();
  11331 
  11332  if (NS_WARN_IF(!aObjectStoreId)) {
  11333    return IPC_FAIL(this, "No ObjectStoreId!");
  11334  }
  11335 
  11336  if (NS_WARN_IF(!aIndexId)) {
  11337    return IPC_FAIL(this, "No Index id!");
  11338  }
  11339 
  11340  const SafeRefPtr<FullDatabaseMetadata> dbMetadata =
  11341      GetDatabase().MetadataPtr();
  11342  MOZ_ASSERT(dbMetadata);
  11343  MOZ_ASSERT(dbMetadata->mNextObjectStoreId > 0);
  11344  MOZ_ASSERT(dbMetadata->mNextIndexId > 0);
  11345 
  11346  if (NS_WARN_IF(aObjectStoreId >= dbMetadata->mNextObjectStoreId)) {
  11347    return IPC_FAIL(this, "Requested ObjectStoreId does not match next ID!");
  11348  }
  11349 
  11350  if (NS_WARN_IF(aIndexId >= dbMetadata->mNextIndexId)) {
  11351    return IPC_FAIL(this, "Requested IndexId does not match next ID!");
  11352  }
  11353 
  11354  SafeRefPtr<FullObjectStoreMetadata> foundObjectStoreMetadata =
  11355      GetMetadataForObjectStoreId(aObjectStoreId);
  11356 
  11357  if (NS_WARN_IF(!foundObjectStoreMetadata)) {
  11358    return IPC_FAIL(this, "GetMetadataForObjectStoreId failed!");
  11359  }
  11360 
  11361  SafeRefPtr<FullIndexMetadata> foundIndexMetadata =
  11362      GetMetadataForIndexId(*foundObjectStoreMetadata, aIndexId);
  11363 
  11364  if (NS_WARN_IF(!foundIndexMetadata)) {
  11365    return IPC_FAIL(this, "GetMetadataForIndexId failed!");
  11366  }
  11367 
  11368  if (NS_WARN_IF(mCommitOrAbortReceived)) {
  11369    return IPC_FAIL(this, "Transaction is already committed/aborted!");
  11370  }
  11371 
  11372  foundIndexMetadata->mCommonMetadata.name() = aName;
  11373 
  11374  RefPtr<RenameIndexOp> renameOp = new RenameIndexOp(
  11375      SafeRefPtrFromThis().downcast<VersionChangeTransaction>(),
  11376      *foundIndexMetadata, aObjectStoreId);
  11377 
  11378  if (NS_WARN_IF(!renameOp->Init(*this))) {
  11379    renameOp->Cleanup();
  11380    return IPC_FAIL(this, "ObjectStoreOp initialization failed!");
  11381  }
  11382 
  11383  renameOp->DispatchToConnectionPool();
  11384 
  11385  return IPC_OK();
  11386 }
  11387 
  11388 PBackgroundIDBRequestParent*
  11389 VersionChangeTransaction::AllocPBackgroundIDBRequestParent(
  11390    const int64_t& aRequestId, const RequestParams& aParams) {
  11391  AssertIsOnBackgroundThread();
  11392  MOZ_ASSERT(aParams.type() != RequestParams::T__None);
  11393 
  11394  return AllocRequest(aRequestId,
  11395                      std::move(const_cast<RequestParams&>(aParams)),
  11396                      IsSameProcessActor());
  11397 }
  11398 
  11399 mozilla::ipc::IPCResult
  11400 VersionChangeTransaction::RecvPBackgroundIDBRequestConstructor(
  11401    PBackgroundIDBRequestParent* aActor, const int64_t& aRequestId,
  11402    const RequestParams& aParams) {
  11403  AssertIsOnBackgroundThread();
  11404  MOZ_ASSERT(aActor);
  11405  MOZ_ASSERT(aParams.type() != RequestParams::T__None);
  11406 
  11407  if (!StartRequest(aActor)) {
  11408    return IPC_FAIL(this, "StartRequest failed!");
  11409  }
  11410  return IPC_OK();
  11411 }
  11412 
  11413 bool VersionChangeTransaction::DeallocPBackgroundIDBRequestParent(
  11414    PBackgroundIDBRequestParent* aActor) {
  11415  AssertIsOnBackgroundThread();
  11416  MOZ_ASSERT(aActor);
  11417 
  11418  return DeallocRequest(aActor);
  11419 }
  11420 
  11421 already_AddRefed<PBackgroundIDBCursorParent>
  11422 VersionChangeTransaction::AllocPBackgroundIDBCursorParent(
  11423    const int64_t& aRequestId, const OpenCursorParams& aParams) {
  11424  AssertIsOnBackgroundThread();
  11425 
  11426  return AllocCursor(aParams, IsSameProcessActor());
  11427 }
  11428 
  11429 mozilla::ipc::IPCResult
  11430 VersionChangeTransaction::RecvPBackgroundIDBCursorConstructor(
  11431    PBackgroundIDBCursorParent* aActor, const int64_t& aRequestId,
  11432    const OpenCursorParams& aParams) {
  11433  AssertIsOnBackgroundThread();
  11434  MOZ_ASSERT(aActor);
  11435  MOZ_ASSERT(aParams.type() != OpenCursorParams::T__None);
  11436 
  11437  if (!StartCursor(aActor, aRequestId, aParams)) {
  11438    return IPC_FAIL(this, "StartCursor failed!");
  11439  }
  11440  return IPC_OK();
  11441 }
  11442 
  11443 /*******************************************************************************
  11444 * CursorBase
  11445 ******************************************************************************/
  11446 
  11447 CursorBase::CursorBase(SafeRefPtr<TransactionBase> aTransaction,
  11448                       SafeRefPtr<FullObjectStoreMetadata> aObjectStoreMetadata,
  11449                       const Direction aDirection,
  11450                       const ConstructFromTransactionBase /*aConstructionTag*/)
  11451    : mTransaction(std::move(aTransaction)),
  11452      mObjectStoreMetadata(WrapNotNull(std::move(aObjectStoreMetadata))),
  11453      mObjectStoreId((*mObjectStoreMetadata)->mCommonMetadata.id()),
  11454      mDirection(aDirection),
  11455      mMaxExtraCount(IndexedDatabaseManager::MaxPreloadExtraRecords()),
  11456      mIsSameProcessActor(!BackgroundParent::IsOtherProcessActor(
  11457          mTransaction->GetBackgroundParent())) {
  11458  AssertIsOnBackgroundThread();
  11459  MOZ_ASSERT(mTransaction);
  11460 
  11461  static_assert(
  11462      OpenCursorParams::T__None == 0 && OpenCursorParams::T__Last == 4,
  11463      "Lots of code here assumes only four types of cursors!");
  11464 }
  11465 
  11466 template <IDBCursorType CursorType>
  11467 bool Cursor<CursorType>::VerifyRequestParams(
  11468    const CursorRequestParams& aParams,
  11469    const CursorPosition<CursorType>& aPosition) const {
  11470  AssertIsOnBackgroundThread();
  11471  MOZ_ASSERT(aParams.type() != CursorRequestParams::T__None);
  11472  MOZ_ASSERT(this->mObjectStoreMetadata);
  11473  if constexpr (IsIndexCursor) {
  11474    MOZ_ASSERT(this->mIndexMetadata);
  11475  }
  11476 
  11477 #ifdef DEBUG
  11478  {
  11479    const SafeRefPtr<FullObjectStoreMetadata> objectStoreMetadata =
  11480        mTransaction->GetMetadataForObjectStoreId(mObjectStoreId);
  11481    if (objectStoreMetadata) {
  11482      MOZ_ASSERT(objectStoreMetadata == (*this->mObjectStoreMetadata));
  11483    } else {
  11484      MOZ_ASSERT((*this->mObjectStoreMetadata)->mDeleted);
  11485    }
  11486 
  11487    if constexpr (IsIndexCursor) {
  11488      if (objectStoreMetadata) {
  11489        const SafeRefPtr<FullIndexMetadata> indexMetadata =
  11490            mTransaction->GetMetadataForIndexId(*objectStoreMetadata,
  11491                                                this->mIndexId);
  11492        if (indexMetadata) {
  11493          MOZ_ASSERT(indexMetadata == *this->mIndexMetadata);
  11494        } else {
  11495          MOZ_ASSERT((*this->mIndexMetadata)->mDeleted);
  11496        }
  11497      }
  11498    }
  11499  }
  11500 #endif
  11501 
  11502  if (NS_AUUF_OR_WARN_IF((*this->mObjectStoreMetadata)->mDeleted)) {
  11503    return false;
  11504  }
  11505 
  11506  if constexpr (IsIndexCursor) {
  11507    if (NS_AUUF_OR_WARN_IF(this->mIndexMetadata &&
  11508                           (*this->mIndexMetadata)->mDeleted)) {
  11509      return false;
  11510    }
  11511  }
  11512 
  11513  const Key& sortKey = aPosition.GetSortKey(this->IsLocaleAware());
  11514 
  11515  switch (aParams.type()) {
  11516    case CursorRequestParams::TContinueParams: {
  11517      const Key& key = aParams.get_ContinueParams().key();
  11518      if (!key.IsUnset()) {
  11519        switch (mDirection) {
  11520          case IDBCursorDirection::Next:
  11521          case IDBCursorDirection::Nextunique:
  11522            if (NS_AUUF_OR_WARN_IF(key <= sortKey)) {
  11523              return false;
  11524            }
  11525            break;
  11526 
  11527          case IDBCursorDirection::Prev:
  11528          case IDBCursorDirection::Prevunique:
  11529            if (NS_AUUF_OR_WARN_IF(key >= sortKey)) {
  11530              return false;
  11531            }
  11532            break;
  11533 
  11534          default:
  11535            MOZ_CRASH("Should never get here!");
  11536        }
  11537      }
  11538      break;
  11539    }
  11540 
  11541    case CursorRequestParams::TContinuePrimaryKeyParams: {
  11542      if constexpr (IsIndexCursor) {
  11543        const Key& key = aParams.get_ContinuePrimaryKeyParams().key();
  11544        const Key& primaryKey =
  11545            aParams.get_ContinuePrimaryKeyParams().primaryKey();
  11546        MOZ_ASSERT(!key.IsUnset());
  11547        MOZ_ASSERT(!primaryKey.IsUnset());
  11548        switch (mDirection) {
  11549          case IDBCursorDirection::Next:
  11550            if (NS_AUUF_OR_WARN_IF(key < sortKey ||
  11551                                   (key == sortKey &&
  11552                                    primaryKey <= aPosition.mObjectStoreKey))) {
  11553              return false;
  11554            }
  11555            break;
  11556 
  11557          case IDBCursorDirection::Prev:
  11558            if (NS_AUUF_OR_WARN_IF(key > sortKey ||
  11559                                   (key == sortKey &&
  11560                                    primaryKey >= aPosition.mObjectStoreKey))) {
  11561              return false;
  11562            }
  11563            break;
  11564 
  11565          default:
  11566            MOZ_CRASH("Should never get here!");
  11567        }
  11568      }
  11569      break;
  11570    }
  11571 
  11572    case CursorRequestParams::TAdvanceParams:
  11573      if (NS_AUUF_OR_WARN_IF(!aParams.get_AdvanceParams().count())) {
  11574        return false;
  11575      }
  11576      break;
  11577 
  11578    default:
  11579      MOZ_CRASH("Should never get here!");
  11580  }
  11581 
  11582  return true;
  11583 }
  11584 
  11585 template <IDBCursorType CursorType>
  11586 bool Cursor<CursorType>::Start(const int64_t aRequestId,
  11587                               const OpenCursorParams& aParams) {
  11588  AssertIsOnBackgroundThread();
  11589  MOZ_ASSERT(aParams.type() == ToOpenCursorParamsType(CursorType));
  11590  MOZ_ASSERT(this->mObjectStoreMetadata);
  11591 
  11592  if (NS_AUUF_OR_WARN_IF(mCurrentlyRunningOp)) {
  11593    return false;
  11594  }
  11595 
  11596  const Maybe<SerializedKeyRange>& optionalKeyRange =
  11597      GetCommonOpenCursorParams(aParams).optionalKeyRange();
  11598 
  11599  const RefPtr<OpenOp> openOp = new OpenOp(this, aRequestId, optionalKeyRange);
  11600 
  11601  if (NS_WARN_IF(!openOp->Init(*mTransaction))) {
  11602    openOp->Cleanup();
  11603    return false;
  11604  }
  11605 
  11606  openOp->DispatchToConnectionPool();
  11607  mCurrentlyRunningOp = openOp;
  11608 
  11609  return true;
  11610 }
  11611 
  11612 void ValueCursorBase::ProcessFiles(CursorResponse& aResponse,
  11613                                   const FilesArray& aFiles) {
  11614  MOZ_ASSERT_IF(
  11615      aResponse.type() == CursorResponse::Tnsresult ||
  11616          aResponse.type() == CursorResponse::Tvoid_t ||
  11617          aResponse.type() ==
  11618              CursorResponse::TArrayOfObjectStoreKeyCursorResponse ||
  11619          aResponse.type() == CursorResponse::TArrayOfIndexKeyCursorResponse,
  11620      aFiles.IsEmpty());
  11621 
  11622  for (size_t i = 0; i < aFiles.Length(); ++i) {
  11623    const auto& files = aFiles[i];
  11624    if (!files.IsEmpty()) {
  11625      // TODO: Replace this assertion by one that checks if the response type
  11626      // matches the cursor type, at a more generic location.
  11627      MOZ_ASSERT(aResponse.type() ==
  11628                     CursorResponse::TArrayOfObjectStoreCursorResponse ||
  11629                 aResponse.type() ==
  11630                     CursorResponse::TArrayOfIndexCursorResponse);
  11631 
  11632      SerializedStructuredCloneReadInfo* serializedInfo = nullptr;
  11633      switch (aResponse.type()) {
  11634        case CursorResponse::TArrayOfObjectStoreCursorResponse: {
  11635          auto& responses = aResponse.get_ArrayOfObjectStoreCursorResponse();
  11636          MOZ_ASSERT(i < responses.Length());
  11637          serializedInfo = &responses[i].cloneInfo();
  11638          break;
  11639        }
  11640 
  11641        case CursorResponse::TArrayOfIndexCursorResponse: {
  11642          auto& responses = aResponse.get_ArrayOfIndexCursorResponse();
  11643          MOZ_ASSERT(i < responses.Length());
  11644          serializedInfo = &responses[i].cloneInfo();
  11645          break;
  11646        }
  11647 
  11648        default:
  11649          MOZ_CRASH("Should never get here!");
  11650      }
  11651 
  11652      MOZ_ASSERT(serializedInfo);
  11653      MOZ_ASSERT(serializedInfo->files().IsEmpty());
  11654      MOZ_ASSERT(this->mDatabase);
  11655 
  11656      QM_TRY_UNWRAP(serializedInfo->files(),
  11657                    SerializeStructuredCloneFiles(this->mDatabase, files,
  11658                                                  /* aForPreprocess */ false),
  11659                    QM_VOID, [&aResponse](const nsresult result) {
  11660                      aResponse = ClampResultCode(result);
  11661                    });
  11662    }
  11663  }
  11664 }
  11665 
  11666 template <IDBCursorType CursorType>
  11667 void Cursor<CursorType>::SendResponseInternal(
  11668    CursorResponse& aResponse, const FilesArrayT<CursorType>& aFiles) {
  11669  AssertIsOnBackgroundThread();
  11670  MOZ_ASSERT(aResponse.type() != CursorResponse::T__None);
  11671  MOZ_ASSERT_IF(aResponse.type() == CursorResponse::Tnsresult,
  11672                NS_FAILED(aResponse.get_nsresult()));
  11673  MOZ_ASSERT_IF(aResponse.type() == CursorResponse::Tnsresult,
  11674                NS_ERROR_GET_MODULE(aResponse.get_nsresult()) ==
  11675                    NS_ERROR_MODULE_DOM_INDEXEDDB);
  11676  MOZ_ASSERT(this->mObjectStoreMetadata);
  11677  MOZ_ASSERT(mCurrentlyRunningOp);
  11678 
  11679  KeyValueBase::ProcessFiles(aResponse, aFiles);
  11680 
  11681  // Work around the deleted function by casting to the base class.
  11682  QM_WARNONLY_TRY(OkIf(
  11683      static_cast<PBackgroundIDBCursorParent*>(this)->SendResponse(aResponse)));
  11684 
  11685  mCurrentlyRunningOp = nullptr;
  11686 }
  11687 
  11688 template <IDBCursorType CursorType>
  11689 void Cursor<CursorType>::ActorDestroy(ActorDestroyReason aWhy) {
  11690  AssertIsOnBackgroundThread();
  11691 
  11692  if (mCurrentlyRunningOp) {
  11693    mCurrentlyRunningOp->NoteActorDestroyed();
  11694  }
  11695 
  11696  if constexpr (IsValueCursor) {
  11697    this->mBackgroundParent.destroy();
  11698  }
  11699  this->mObjectStoreMetadata.destroy();
  11700  if constexpr (IsIndexCursor) {
  11701    this->mIndexMetadata.destroy();
  11702  }
  11703 }
  11704 
  11705 template <IDBCursorType CursorType>
  11706 mozilla::ipc::IPCResult Cursor<CursorType>::RecvDeleteMe() {
  11707  AssertIsOnBackgroundThread();
  11708  MOZ_ASSERT(this->mObjectStoreMetadata);
  11709 
  11710  if (NS_WARN_IF(mCurrentlyRunningOp)) {
  11711    return IPC_FAIL(
  11712        this,
  11713        "Attempt to delete a cursor with a non-null mCurrentlyRunningOp!");
  11714  }
  11715 
  11716  QM_WARNONLY_TRY(OkIf(PBackgroundIDBCursorParent::Send__delete__(this)));
  11717 
  11718  return IPC_OK();
  11719 }
  11720 
  11721 template <IDBCursorType CursorType>
  11722 mozilla::ipc::IPCResult Cursor<CursorType>::RecvContinue(
  11723    const int64_t& aRequestId, const CursorRequestParams& aParams,
  11724    const Key& aCurrentKey, const Key& aCurrentObjectStoreKey) {
  11725  AssertIsOnBackgroundThread();
  11726  MOZ_ASSERT(aParams.type() != CursorRequestParams::T__None);
  11727  MOZ_ASSERT(this->mObjectStoreMetadata);
  11728  if constexpr (IsIndexCursor) {
  11729    MOZ_ASSERT(this->mIndexMetadata);
  11730  }
  11731 
  11732  const bool trustParams =
  11733 #ifdef DEBUG
  11734      // Always verify parameters in DEBUG builds!
  11735      false
  11736 #else
  11737      this->mIsSameProcessActor
  11738 #endif
  11739      ;
  11740 
  11741  MOZ_ASSERT(!aCurrentKey.IsUnset());
  11742 
  11743  QM_TRY_UNWRAP(
  11744      auto position,
  11745      ([&]() -> Result<CursorPosition<CursorType>, mozilla::ipc::IPCResult> {
  11746        if constexpr (IsIndexCursor) {
  11747          auto localeAwarePosition = Key{};
  11748          if (this->IsLocaleAware()) {
  11749            QM_TRY_UNWRAP(
  11750                localeAwarePosition,
  11751                aCurrentKey.ToLocaleAwareKey(this->mLocale),
  11752                Err(IPC_FAIL(this, "aCurrentKey.ToLocaleAwareKey failed!")));
  11753          }
  11754          return CursorPosition<CursorType>{aCurrentKey, localeAwarePosition,
  11755                                            aCurrentObjectStoreKey};
  11756        } else {
  11757          return CursorPosition<CursorType>{aCurrentKey};
  11758        }
  11759      }()));
  11760 
  11761  if (!trustParams && !VerifyRequestParams(aParams, position)) {
  11762    return IPC_FAIL(this, "VerifyRequestParams failed!");
  11763  }
  11764 
  11765  if (NS_WARN_IF(mCurrentlyRunningOp)) {
  11766    return IPC_FAIL(this, "Cursor is CurrentlyRunningOp!");
  11767  }
  11768 
  11769  if (NS_WARN_IF(mTransaction->mCommitOrAbortReceived)) {
  11770    return IPC_FAIL(this, "Transaction is already committed/aborted!");
  11771  }
  11772 
  11773  const RefPtr<ContinueOp> continueOp =
  11774      new ContinueOp(this, aRequestId, aParams, std::move(position));
  11775  if (NS_WARN_IF(!continueOp->Init(*mTransaction))) {
  11776    continueOp->Cleanup();
  11777    return IPC_FAIL(this, "ContinueOp initialization failed!");
  11778  }
  11779 
  11780  continueOp->DispatchToConnectionPool();
  11781  mCurrentlyRunningOp = continueOp;
  11782 
  11783  return IPC_OK();
  11784 }
  11785 
  11786 /*******************************************************************************
  11787 * DatabaseFileManager
  11788 ******************************************************************************/
  11789 
  11790 DatabaseFileManager::MutexType DatabaseFileManager::sMutex;
  11791 
  11792 DatabaseFileManager::DatabaseFileManager(
  11793    PersistenceType aPersistenceType,
  11794    const quota::OriginMetadata& aOriginMetadata,
  11795    const nsAString& aDatabaseName, const nsCString& aDatabaseID,
  11796    const nsAString& aDatabaseFilePath, bool aEnforcingQuota,
  11797    bool aIsInPrivateBrowsingMode)
  11798    : mPersistenceType(aPersistenceType),
  11799      mOriginMetadata(aOriginMetadata),
  11800      mDatabaseName(aDatabaseName),
  11801      mDatabaseID(aDatabaseID),
  11802      mDatabaseFilePath(aDatabaseFilePath),
  11803      mCipherKeyManager(
  11804          aIsInPrivateBrowsingMode
  11805              ? new IndexedDBCipherKeyManager("IndexedDBCipherKeyManager")
  11806              : nullptr),
  11807      mDatabaseVersion(0),
  11808      mEnforcingQuota(aEnforcingQuota),
  11809      mIsInPrivateBrowsingMode(aIsInPrivateBrowsingMode) {}
  11810 
  11811 uint64_t DatabaseFileManager::DatabaseVersion() const {
  11812  AssertIsOnIOThread();
  11813 
  11814  return mDatabaseVersion;
  11815 }
  11816 
  11817 void DatabaseFileManager::UpdateDatabaseVersion(uint64_t aDatabaseVersion) {
  11818  AssertIsOnIOThread();
  11819 
  11820  mDatabaseVersion = aDatabaseVersion;
  11821 }
  11822 
  11823 nsresult DatabaseFileManager::Init(nsIFile* aDirectory,
  11824                                   const uint64_t aDatabaseVersion,
  11825                                   mozIStorageConnection& aConnection) {
  11826  AssertIsOnIOThread();
  11827  MOZ_ASSERT(aDirectory);
  11828 
  11829  {
  11830    QM_TRY_INSPECT(const bool& existsAsDirectory,
  11831                   ExistsAsDirectory(*aDirectory));
  11832 
  11833    if (!existsAsDirectory) {
  11834      QM_TRY(MOZ_TO_RESULT(aDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755)));
  11835    }
  11836 
  11837    QM_TRY_UNWRAP(auto path, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
  11838                                 nsString, aDirectory, GetPath));
  11839 
  11840    mDirectoryPath.init(std::move(path));
  11841  }
  11842 
  11843  QM_TRY_INSPECT(const auto& journalDirectory,
  11844                 CloneFileAndAppend(*aDirectory, kJournalDirectoryName));
  11845 
  11846  // We don't care if it doesn't exist at all, but if it does exist, make sure
  11847  // it's a directory.
  11848  QM_TRY_INSPECT(const bool& existsAsDirectory,
  11849                 ExistsAsDirectory(*journalDirectory));
  11850  (void)existsAsDirectory;
  11851 
  11852  {
  11853    QM_TRY_UNWRAP(auto path, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
  11854                                 nsString, journalDirectory, GetPath));
  11855 
  11856    mJournalDirectoryPath.init(std::move(path));
  11857  }
  11858 
  11859  mDatabaseVersion = aDatabaseVersion;
  11860 
  11861  QM_TRY_INSPECT(const auto& stmt,
  11862                 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
  11863                     nsCOMPtr<mozIStorageStatement>, aConnection,
  11864                     CreateStatement, "SELECT id, refcount FROM file"_ns));
  11865 
  11866  QM_TRY(
  11867      CollectWhileHasResult(*stmt, [this](auto& stmt) -> Result<Ok, nsresult> {
  11868        QM_TRY_INSPECT(const int64_t& id,
  11869                       MOZ_TO_RESULT_INVOKE_MEMBER(stmt, GetInt64, 0));
  11870        QM_TRY_INSPECT(const int32_t& dbRefCnt,
  11871                       MOZ_TO_RESULT_INVOKE_MEMBER(stmt, GetInt32, 1));
  11872 
  11873        // We put a raw pointer into the hash table, so the memory refcount will
  11874        // be 0, but the dbRefCnt is non-zero, which will keep the
  11875        // DatabaseFileInfo object alive.
  11876        MOZ_ASSERT(dbRefCnt > 0);
  11877        DebugOnly ok = static_cast<bool>(CreateFileInfo(Some(id), dbRefCnt));
  11878        MOZ_ASSERT(ok);
  11879 
  11880        return Ok{};
  11881      }));
  11882 
  11883  mInitialized.Flip();
  11884 
  11885  return NS_OK;
  11886 }
  11887 
  11888 nsCOMPtr<nsIFile> DatabaseFileManager::GetDirectory() {
  11889  if (!this->AssertValid()) {
  11890    return nullptr;
  11891  }
  11892 
  11893  return GetFileForPath(*mDirectoryPath);
  11894 }
  11895 
  11896 nsCOMPtr<nsIFile> DatabaseFileManager::GetCheckedDirectory() {
  11897  auto directory = GetDirectory();
  11898  if (NS_WARN_IF(!directory)) {
  11899    return nullptr;
  11900  }
  11901 
  11902  DebugOnly<bool> exists;
  11903  MOZ_ASSERT(NS_SUCCEEDED(directory->Exists(&exists)));
  11904  MOZ_ASSERT(exists);
  11905 
  11906  DebugOnly<bool> isDirectory;
  11907  MOZ_ASSERT(NS_SUCCEEDED(directory->IsDirectory(&isDirectory)));
  11908  MOZ_ASSERT(isDirectory);
  11909 
  11910  return directory;
  11911 }
  11912 
  11913 nsCOMPtr<nsIFile> DatabaseFileManager::GetJournalDirectory() {
  11914  if (!this->AssertValid()) {
  11915    return nullptr;
  11916  }
  11917 
  11918  return GetFileForPath(*mJournalDirectoryPath);
  11919 }
  11920 
  11921 nsCOMPtr<nsIFile> DatabaseFileManager::EnsureJournalDirectory() {
  11922  // This can happen on the IO or on a transaction thread.
  11923  MOZ_ASSERT(!NS_IsMainThread());
  11924 
  11925  auto journalDirectory = GetFileForPath(*mJournalDirectoryPath);
  11926  QM_TRY(OkIf(journalDirectory), nullptr);
  11927 
  11928  QM_TRY_INSPECT(const bool& exists,
  11929                 MOZ_TO_RESULT_INVOKE_MEMBER(journalDirectory, Exists),
  11930                 nullptr);
  11931 
  11932  if (exists) {
  11933    QM_TRY_INSPECT(const bool& isDirectory,
  11934                   MOZ_TO_RESULT_INVOKE_MEMBER(journalDirectory, IsDirectory),
  11935                   nullptr);
  11936 
  11937    QM_TRY(OkIf(isDirectory), nullptr);
  11938  } else {
  11939    QM_TRY(
  11940        MOZ_TO_RESULT(journalDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755)),
  11941        nullptr);
  11942  }
  11943 
  11944  return journalDirectory;
  11945 }
  11946 
  11947 // static
  11948 nsCOMPtr<nsIFile> DatabaseFileManager::GetFileForId(nsIFile* aDirectory,
  11949                                                    int64_t aId) {
  11950  MOZ_ASSERT(aDirectory);
  11951  MOZ_ASSERT(aId > 0);
  11952 
  11953  QM_TRY_RETURN(CloneFileAndAppend(*aDirectory, IntToString(aId)), nullptr);
  11954 }
  11955 
  11956 // static
  11957 nsCOMPtr<nsIFile> DatabaseFileManager::GetCheckedFileForId(nsIFile* aDirectory,
  11958                                                           int64_t aId) {
  11959  auto file = GetFileForId(aDirectory, aId);
  11960  if (NS_WARN_IF(!file)) {
  11961    return nullptr;
  11962  }
  11963 
  11964  DebugOnly<bool> exists;
  11965  MOZ_ASSERT(NS_SUCCEEDED(file->Exists(&exists)));
  11966  MOZ_ASSERT(exists);
  11967 
  11968  DebugOnly<bool> isFile;
  11969  MOZ_ASSERT(NS_SUCCEEDED(file->IsFile(&isFile)));
  11970  MOZ_ASSERT(isFile);
  11971 
  11972  return file;
  11973 }
  11974 
  11975 // static
  11976 nsresult DatabaseFileManager::InitDirectory(nsIFile& aDirectory,
  11977                                            nsIFile& aDatabaseFile,
  11978                                            const nsACString& aOrigin,
  11979                                            uint32_t aTelemetryId) {
  11980  AssertIsOnIOThread();
  11981 
  11982  {
  11983    QM_TRY_INSPECT(const bool& exists,
  11984                   MOZ_TO_RESULT_INVOKE_MEMBER(aDirectory, Exists));
  11985 
  11986    if (!exists) {
  11987      return NS_OK;
  11988    }
  11989 
  11990    QM_TRY_INSPECT(const bool& isDirectory,
  11991                   MOZ_TO_RESULT_INVOKE_MEMBER(aDirectory, IsDirectory));
  11992    QM_TRY(OkIf(isDirectory), NS_ERROR_FAILURE);
  11993  }
  11994 
  11995  QM_TRY_INSPECT(const auto& journalDirectory,
  11996                 CloneFileAndAppend(aDirectory, kJournalDirectoryName));
  11997 
  11998  QM_TRY_INSPECT(const bool& exists,
  11999                 MOZ_TO_RESULT_INVOKE_MEMBER(journalDirectory, Exists));
  12000 
  12001  if (exists) {
  12002    QM_TRY_INSPECT(const bool& isDirectory,
  12003                   MOZ_TO_RESULT_INVOKE_MEMBER(journalDirectory, IsDirectory));
  12004    QM_TRY(OkIf(isDirectory), NS_ERROR_FAILURE);
  12005 
  12006    bool hasJournals = false;
  12007 
  12008    QM_TRY(CollectEachFile(
  12009        *journalDirectory,
  12010        [&hasJournals](const nsCOMPtr<nsIFile>& file) -> Result<Ok, nsresult> {
  12011          QM_TRY_INSPECT(
  12012              const auto& leafName,
  12013              MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsString, file, GetLeafName));
  12014 
  12015          nsresult rv;
  12016          leafName.ToInteger64(&rv);
  12017          if (NS_SUCCEEDED(rv)) {
  12018            hasJournals = true;
  12019          } else {
  12020            UNKNOWN_FILE_WARNING(leafName);
  12021          }
  12022 
  12023          return Ok{};
  12024        }));
  12025 
  12026    if (hasJournals) {
  12027      QM_TRY_UNWRAP(const NotNull<nsCOMPtr<mozIStorageConnection>> connection,
  12028                    CreateStorageConnection(
  12029                        aDatabaseFile, aDirectory, VoidString(), aOrigin,
  12030                        /* aDirectoryLockId */ -1, aTelemetryId, Nothing{}));
  12031 
  12032      mozStorageTransaction transaction(connection.get(), false);
  12033 
  12034      QM_TRY(MOZ_TO_RESULT(transaction.Start()))
  12035 
  12036      QM_TRY(MOZ_TO_RESULT(connection->ExecuteSimpleSQL(
  12037          "CREATE VIRTUAL TABLE fs USING filesystem;"_ns)));
  12038 
  12039      // The parameter names are not used, parameters are bound by index only
  12040      // locally in the same function.
  12041      QM_TRY_INSPECT(
  12042          const auto& stmt,
  12043          MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
  12044              nsCOMPtr<mozIStorageStatement>, *connection, CreateStatement,
  12045              "SELECT name, (name IN (SELECT id FROM file)) FROM fs WHERE path = :path"_ns));
  12046 
  12047      QM_TRY_INSPECT(const auto& path,
  12048                     MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
  12049                         nsString, journalDirectory, GetPath));
  12050 
  12051      QM_TRY(MOZ_TO_RESULT(stmt->BindStringByIndex(0, path)));
  12052 
  12053      QM_TRY(CollectWhileHasResult(
  12054          *stmt,
  12055          [&aDirectory, &journalDirectory](auto& stmt) -> Result<Ok, nsresult> {
  12056            nsString name;
  12057            QM_TRY(MOZ_TO_RESULT(stmt.GetString(0, name)));
  12058 
  12059            nsresult rv;
  12060            name.ToInteger64(&rv);
  12061            if (NS_FAILED(rv)) {
  12062              return Ok{};
  12063            }
  12064 
  12065            int32_t flag = stmt.AsInt32(1);
  12066 
  12067            if (!flag) {
  12068              QM_TRY_INSPECT(const auto& file,
  12069                             CloneFileAndAppend(aDirectory, name));
  12070 
  12071              if (NS_FAILED(file->Remove(false))) {
  12072                NS_WARNING("Failed to remove orphaned file!");
  12073              }
  12074            }
  12075 
  12076            QM_TRY_INSPECT(const auto& journalFile,
  12077                           CloneFileAndAppend(*journalDirectory, name));
  12078 
  12079            if (NS_FAILED(journalFile->Remove(false))) {
  12080              NS_WARNING("Failed to remove journal file!");
  12081            }
  12082 
  12083            return Ok{};
  12084          }));
  12085 
  12086      QM_TRY(MOZ_TO_RESULT(connection->ExecuteSimpleSQL("DROP TABLE fs;"_ns)));
  12087      QM_TRY(MOZ_TO_RESULT(transaction.Commit()));
  12088    }
  12089  }
  12090 
  12091  return NS_OK;
  12092 }
  12093 
  12094 // static
  12095 Result<FileUsageType, nsresult> DatabaseFileManager::GetUsage(
  12096    nsIFile* aDirectory) {
  12097  AssertIsOnIOThread();
  12098  MOZ_ASSERT(aDirectory);
  12099 
  12100  FileUsageType usage;
  12101 
  12102  QM_TRY(TraverseFiles(
  12103      *aDirectory,
  12104      // KnownDirEntryOp
  12105      [&usage](nsIFile& file, const bool isDirectory) -> Result<Ok, nsresult> {
  12106        if (isDirectory) {
  12107          return Ok{};
  12108        }
  12109 
  12110        // Usually we only use QM_OR_ELSE_LOG_VERBOSE(_IF) with Remove and
  12111        // NS_ERROR_FILE_NOT_FOUND check, but the file was found by a directory
  12112        // traversal and ToInteger on the name succeeded, so it should be our
  12113        // file and if the file disappears, the use of QM_OR_ELSE_WARN_IF is ok
  12114        // here.
  12115        QM_TRY_INSPECT(const auto& thisUsage,
  12116                       QM_OR_ELSE_WARN_IF(
  12117                           // Expression.
  12118                           MOZ_TO_RESULT_INVOKE_MEMBER(file, GetFileSize)
  12119                               .map([](const int64_t fileSize) {
  12120                                 return FileUsageType(Some(uint64_t(fileSize)));
  12121                               }),
  12122                           // Predicate.
  12123                           ([](const nsresult rv) {
  12124                             return rv == NS_ERROR_FILE_NOT_FOUND;
  12125                           }),
  12126                           // Fallback. If the file does no longer exist, treat
  12127                           // it as 0-sized.
  12128                           ErrToDefaultOk<FileUsageType>));
  12129 
  12130        usage += thisUsage;
  12131 
  12132        return Ok{};
  12133      },
  12134      // UnknownDirEntryOp
  12135      [](nsIFile&, const bool) -> Result<Ok, nsresult> { return Ok{}; }));
  12136 
  12137  return usage;
  12138 }
  12139 
  12140 nsresult DatabaseFileManager::SyncDeleteFile(const int64_t aId) {
  12141  MOZ_ASSERT(!ContainsFileInfo(aId));
  12142 
  12143  if (!this->AssertValid()) {
  12144    return NS_ERROR_UNEXPECTED;
  12145  }
  12146 
  12147  const auto directory = GetDirectory();
  12148  QM_TRY(OkIf(directory), NS_ERROR_FAILURE);
  12149 
  12150  const auto journalDirectory = GetJournalDirectory();
  12151  QM_TRY(OkIf(journalDirectory), NS_ERROR_FAILURE);
  12152 
  12153  const nsCOMPtr<nsIFile> file = GetFileForId(directory, aId);
  12154  QM_TRY(OkIf(file), NS_ERROR_FAILURE);
  12155 
  12156  const nsCOMPtr<nsIFile> journalFile = GetFileForId(journalDirectory, aId);
  12157  QM_TRY(OkIf(journalFile), NS_ERROR_FAILURE);
  12158 
  12159  return SyncDeleteFile(*file, *journalFile);
  12160 }
  12161 
  12162 nsresult DatabaseFileManager::SyncDeleteFile(nsIFile& aFile,
  12163                                             nsIFile& aJournalFile) const {
  12164  QuotaManager* const quotaManager =
  12165      EnforcingQuota() ? QuotaManager::Get() : nullptr;
  12166  MOZ_ASSERT_IF(EnforcingQuota(), quotaManager);
  12167 
  12168  QM_TRY(MOZ_TO_RESULT(DeleteFile(aFile, quotaManager, Type(), OriginMetadata(),
  12169                                  Idempotency::No)));
  12170 
  12171  QM_TRY(MOZ_TO_RESULT(aJournalFile.Remove(false)));
  12172 
  12173  return NS_OK;
  12174 }
  12175 
  12176 nsresult DatabaseFileManager::Invalidate() {
  12177  if (mCipherKeyManager) {
  12178    mCipherKeyManager->Invalidate();
  12179  }
  12180 
  12181  QM_TRY(MOZ_TO_RESULT(FileInfoManager::Invalidate()));
  12182 
  12183  return NS_OK;
  12184 }
  12185 
  12186 /*******************************************************************************
  12187 * QuotaClient
  12188 ******************************************************************************/
  12189 
  12190 QuotaClient* QuotaClient::sInstance = nullptr;
  12191 
  12192 QuotaClient::QuotaClient() : mDeleteTimer(NS_NewTimer()) {
  12193  AssertIsOnBackgroundThread();
  12194  MOZ_ASSERT(!sInstance, "We expect this to be a singleton!");
  12195  MOZ_ASSERT(!gTelemetryIdMutex);
  12196 
  12197  // Always create this so that later access to gTelemetryIdHashtable can be
  12198  // properly synchronized.
  12199  gTelemetryIdMutex = new Mutex("IndexedDB gTelemetryIdMutex");
  12200 
  12201  gStorageDatabaseNameMutex = new Mutex("IndexedDB gStorageDatabaseNameMutex");
  12202 
  12203  sInstance = this;
  12204 }
  12205 
  12206 QuotaClient::~QuotaClient() {
  12207  AssertIsOnBackgroundThread();
  12208  MOZ_ASSERT(sInstance == this, "We expect this to be a singleton!");
  12209  MOZ_ASSERT(gTelemetryIdMutex);
  12210  MOZ_ASSERT(!mMaintenanceThreadPool);
  12211 
  12212  // No one else should be able to touch gTelemetryIdHashtable now that the
  12213  // QuotaClient has gone away.
  12214  gTelemetryIdHashtable = nullptr;
  12215  gTelemetryIdMutex = nullptr;
  12216 
  12217  gStorageDatabaseNameHashtable = nullptr;
  12218  gStorageDatabaseNameMutex = nullptr;
  12219 
  12220  sInstance = nullptr;
  12221 }
  12222 
  12223 nsresult QuotaClient::AsyncDeleteFile(DatabaseFileManager* aFileManager,
  12224                                      int64_t aFileId) {
  12225  AssertIsOnBackgroundThread();
  12226 
  12227  if (IsShuttingDownOnBackgroundThread()) {
  12228    // Whoops! We want to delete an IndexedDB disk-backed File but it's too late
  12229    // to actually delete the file! This means we're going to "leak" the file
  12230    // and leave it around when we shouldn't! (The file will stay around until
  12231    // next storage initialization is triggered when the app is started again).
  12232    // Fixing this is tracked by bug 1539377.
  12233 
  12234    return NS_OK;
  12235  }
  12236 
  12237  MOZ_ASSERT(mDeleteTimer);
  12238  MOZ_ALWAYS_SUCCEEDS(mDeleteTimer->Cancel());
  12239 
  12240  QM_TRY(MOZ_TO_RESULT(mDeleteTimer->InitWithNamedFuncCallback(
  12241      DeleteTimerCallback, this, kDeleteTimeoutMs, nsITimer::TYPE_ONE_SHOT,
  12242      "dom::indexeddb::QuotaClient::AsyncDeleteFile"_ns)));
  12243 
  12244  mPendingDeleteInfos.GetOrInsertNew(aFileManager)->AppendElement(aFileId);
  12245 
  12246  return NS_OK;
  12247 }
  12248 
  12249 nsresult QuotaClient::FlushPendingFileDeletions() {
  12250  AssertIsOnBackgroundThread();
  12251 
  12252  QM_TRY(MOZ_TO_RESULT(mDeleteTimer->Cancel()));
  12253 
  12254  DeleteTimerCallback(mDeleteTimer, this);
  12255 
  12256  return NS_OK;
  12257 }
  12258 
  12259 RefPtr<BoolPromise> QuotaClient::DoMaintenance() {
  12260  AssertIsOnBackgroundThread();
  12261  MOZ_ASSERT(!IsShuttingDownOnBackgroundThread());
  12262 
  12263  if (!mBackgroundThread) {
  12264    mBackgroundThread = GetCurrentSerialEventTarget();
  12265  }
  12266 
  12267  auto maintenance = MakeRefPtr<Maintenance>(this);
  12268 
  12269  mMaintenanceQueue.AppendElement(maintenance);
  12270  ProcessMaintenanceQueue();
  12271 
  12272  return maintenance->OnResults();
  12273 }
  12274 
  12275 nsThreadPool* QuotaClient::GetOrCreateThreadPool() {
  12276  AssertIsOnBackgroundThread();
  12277  MOZ_ASSERT(!IsShuttingDownOnBackgroundThread());
  12278 
  12279  if (!mMaintenanceThreadPool) {
  12280    RefPtr<nsThreadPool> threadPool = new nsThreadPool();
  12281 
  12282    // PR_GetNumberOfProcessors() can return -1 on error, so make sure we
  12283    // don't set some huge number here. We add 2 in case some threads block on
  12284    // the disk I/O.
  12285    const uint32_t threadCount =
  12286        std::max(int32_t(PR_GetNumberOfProcessors()), int32_t(1)) + 2;
  12287 
  12288    MOZ_ALWAYS_SUCCEEDS(threadPool->SetThreadLimit(threadCount));
  12289 
  12290    // Don't keep more than one idle thread.
  12291    MOZ_ALWAYS_SUCCEEDS(threadPool->SetIdleThreadLimit(1));
  12292 
  12293    // Don't keep idle threads alive very long.
  12294    MOZ_ALWAYS_SUCCEEDS(
  12295        threadPool->SetIdleThreadMaximumTimeout(5 * PR_MSEC_PER_SEC));
  12296 
  12297    MOZ_ALWAYS_SUCCEEDS(threadPool->SetName("IndexedDB Mnt"_ns));
  12298 
  12299    mMaintenanceThreadPool = std::move(threadPool);
  12300  }
  12301 
  12302  return mMaintenanceThreadPool;
  12303 }
  12304 
  12305 mozilla::dom::quota::Client::Type QuotaClient::GetType() {
  12306  return QuotaClient::IDB;
  12307 }
  12308 
  12309 nsresult QuotaClient::UpgradeStorageFrom1_0To2_0(nsIFile* aDirectory) {
  12310  AssertIsOnIOThread();
  12311  MOZ_ASSERT(aDirectory);
  12312 
  12313  QM_TRY_INSPECT(const auto& databaseFilenamesInfo,
  12314                 GetDatabaseFilenames(*aDirectory,
  12315                                      /* aCanceled */ AtomicBool{false}));
  12316  // FIXME: use structural binding once we support c++20.
  12317  const auto& subdirsToProcess = databaseFilenamesInfo.subdirsToProcess;
  12318  const auto& databaseFilenames = databaseFilenamesInfo.databaseFilenames;
  12319 
  12320  QM_TRY(CollectEachInRange(
  12321      subdirsToProcess,
  12322      [&databaseFilenames = databaseFilenames,
  12323       aDirectory](const nsAString& subdirName) -> Result<Ok, nsresult> {
  12324        // If the directory has the correct suffix then it should exist in
  12325        // databaseFilenames.
  12326        nsDependentSubstring subdirNameBase;
  12327        if (GetFilenameBase(subdirName, kFileManagerDirectoryNameSuffix,
  12328                            subdirNameBase)) {
  12329          QM_WARNONLY_TRY(OkIf(databaseFilenames.Contains(subdirNameBase)));
  12330          return Ok{};
  12331        }
  12332 
  12333        // The directory didn't have the right suffix but we might need to
  12334        // rename it. Check to see if we have a database that references this
  12335        // directory.
  12336        QM_TRY_INSPECT(
  12337            const auto& subdirNameWithSuffix,
  12338            ([&databaseFilenames,
  12339              &subdirName]() -> Result<nsAutoString, NotOk> {
  12340              if (databaseFilenames.Contains(subdirName)) {
  12341                return nsAutoString{subdirName +
  12342                                    kFileManagerDirectoryNameSuffix};
  12343              }
  12344 
  12345              // Windows doesn't allow a directory to end with a dot ('.'), so
  12346              // we have to check that possibility here too. We do this on all
  12347              // platforms, because the origin directory may have been created
  12348              // on Windows and now accessed on different OS.
  12349              const nsAutoString subdirNameWithDot = subdirName + u"."_ns;
  12350              QM_TRY(OkIf(databaseFilenames.Contains(subdirNameWithDot)),
  12351                     Err(NotOk{}));
  12352 
  12353              return nsAutoString{subdirNameWithDot +
  12354                                  kFileManagerDirectoryNameSuffix};
  12355            }()),
  12356            Ok{});
  12357 
  12358        // We do have a database that uses this subdir so we should rename it
  12359        // now.
  12360        QM_TRY_INSPECT(const auto& subdir,
  12361                       CloneFileAndAppend(*aDirectory, subdirName));
  12362 
  12363        DebugOnly<bool> isDirectory;
  12364        MOZ_ASSERT(NS_SUCCEEDED(subdir->IsDirectory(&isDirectory)));
  12365        MOZ_ASSERT(isDirectory);
  12366 
  12367        // Check if the subdir with suffix already exists before renaming.
  12368        QM_TRY_INSPECT(const auto& subdirWithSuffix,
  12369                       CloneFileAndAppend(*aDirectory, subdirNameWithSuffix));
  12370 
  12371        QM_TRY_INSPECT(const bool& exists,
  12372                       MOZ_TO_RESULT_INVOKE_MEMBER(subdirWithSuffix, Exists));
  12373 
  12374        if (exists) {
  12375          IDB_WARNING("Deleting old %s files directory!",
  12376                      NS_ConvertUTF16toUTF8(subdirName).get());
  12377 
  12378          QM_TRY(MOZ_TO_RESULT(subdir->Remove(/* aRecursive */ true)));
  12379 
  12380          return Ok{};
  12381        }
  12382 
  12383        // Finally, rename the subdir.
  12384        QM_TRY(MOZ_TO_RESULT(subdir->RenameTo(nullptr, subdirNameWithSuffix)));
  12385 
  12386        return Ok{};
  12387      }));
  12388 
  12389  return NS_OK;
  12390 }
  12391 
  12392 nsresult QuotaClient::UpgradeStorageFrom2_1To2_2(nsIFile* aDirectory) {
  12393  AssertIsOnIOThread();
  12394  MOZ_ASSERT(aDirectory);
  12395 
  12396  QM_TRY(CollectEachFile(
  12397      *aDirectory, [](const nsCOMPtr<nsIFile>& file) -> Result<Ok, nsresult> {
  12398        QM_TRY_INSPECT(const auto& dirEntryKind, GetDirEntryKind(*file));
  12399 
  12400        switch (dirEntryKind) {
  12401          case nsIFileKind::ExistsAsDirectory:
  12402            break;
  12403 
  12404          case nsIFileKind::ExistsAsFile: {
  12405            QM_TRY_INSPECT(
  12406                const auto& leafName,
  12407                MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsString, file, GetLeafName));
  12408 
  12409            // It's reported that files ending with ".tmp" somehow live in the
  12410            // indexedDB directories in Bug 1503883. Such files shouldn't exist
  12411            // in the indexedDB directory so remove them in this upgrade.
  12412            if (StringEndsWith(leafName, u".tmp"_ns)) {
  12413              IDB_WARNING("Deleting unknown temporary file!");
  12414 
  12415              QM_TRY(MOZ_TO_RESULT(file->Remove(false)));
  12416            }
  12417 
  12418            break;
  12419          }
  12420 
  12421          case nsIFileKind::DoesNotExist:
  12422            // Ignore files that got removed externally while iterating.
  12423            break;
  12424        }
  12425 
  12426        return Ok{};
  12427      }));
  12428 
  12429  return NS_OK;
  12430 }
  12431 
  12432 Result<UsageInfo, nsresult> QuotaClient::InitOrigin(
  12433    PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata,
  12434    const AtomicBool& aCanceled) {
  12435  AssertIsOnIOThread();
  12436 
  12437  QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_MEMBER(this, GetUsageForOriginInternal,
  12438                                            aPersistenceType, aOriginMetadata,
  12439                                            aCanceled,
  12440                                            /* aInitializing*/ true));
  12441 }
  12442 
  12443 nsresult QuotaClient::InitOriginWithoutTracking(
  12444    PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata,
  12445    const AtomicBool& aCanceled) {
  12446  AssertIsOnIOThread();
  12447 
  12448  return GetUsageForOriginInternal(aPersistenceType, aOriginMetadata, aCanceled,
  12449                                   /* aInitializing*/ true, nullptr);
  12450 }
  12451 
  12452 Result<UsageInfo, nsresult> QuotaClient::GetUsageForOrigin(
  12453    PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata,
  12454    const AtomicBool& aCanceled) {
  12455  AssertIsOnIOThread();
  12456 
  12457  QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_MEMBER(this, GetUsageForOriginInternal,
  12458                                            aPersistenceType, aOriginMetadata,
  12459                                            aCanceled,
  12460                                            /* aInitializing*/ false));
  12461 }
  12462 
  12463 nsresult QuotaClient::GetUsageForOriginInternal(
  12464    PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata,
  12465    const AtomicBool& aCanceled, const bool aInitializing,
  12466    UsageInfo* aUsageInfo) {
  12467  GECKO_TRACE_SCOPE("dom::indexedDB", "QuotaClient::GetUsageForOriginInternal");
  12468 
  12469  AssertIsOnIOThread();
  12470  MOZ_ASSERT(aOriginMetadata.mPersistenceType == aPersistenceType);
  12471 
  12472  QM_TRY_INSPECT(const nsCOMPtr<nsIFile>& directory,
  12473                 GetDirectory(aOriginMetadata));
  12474 
  12475  // We need to see if there are any files in the directory already. If they
  12476  // are database files then we need to cleanup stored files (if it's needed)
  12477  // and also get the usage.
  12478 
  12479  // XXX Can we avoid unwrapping into non-const variables here? (Only
  12480  // databaseFilenames is currently modified below)
  12481  QM_TRY_UNWRAP((auto [subdirsToProcess, databaseFilenames, obsoleteFilenames]),
  12482                GetDatabaseFilenames<ObsoleteFilenamesHandling::Include>(
  12483                    *directory, aCanceled));
  12484 
  12485  if (aInitializing) {
  12486    QM_TRY(CollectEachInRange(
  12487        subdirsToProcess,
  12488        [&directory, &obsoleteFilenames = obsoleteFilenames,
  12489         &databaseFilenames = databaseFilenames, aPersistenceType,
  12490         &aOriginMetadata](
  12491            const nsAString& subdirName) -> Result<Ok, nsresult> {
  12492          // The directory must have the correct suffix.
  12493          nsDependentSubstring subdirNameBase;
  12494          QM_TRY(QM_OR_ELSE_WARN(
  12495                     // Expression.
  12496                     ([&subdirName, &subdirNameBase] {
  12497                       QM_TRY_RETURN(OkIf(GetFilenameBase(
  12498                           subdirName, kFileManagerDirectoryNameSuffix,
  12499                           subdirNameBase)));
  12500                     }()),
  12501                     // Fallback.
  12502                     ([&directory,
  12503                       &subdirName](const NotOk) -> Result<Ok, nsresult> {
  12504                       // If there is an unexpected directory in the idb
  12505                       // directory, trying to delete at first instead of
  12506                       // breaking the whole initialization.
  12507                       QM_TRY(MOZ_TO_RESULT(
  12508                                  DeleteFilesNoQuota(directory, subdirName)),
  12509                              Err(NS_ERROR_UNEXPECTED));
  12510 
  12511                       return Ok{};
  12512                     })),
  12513                 Ok{});
  12514 
  12515          if (obsoleteFilenames.Contains(subdirNameBase)) {
  12516            // If this fails, it probably means we are in a serious situation.
  12517            // e.g. Filesystem corruption. Will handle this in bug 1521541.
  12518            QM_TRY(MOZ_TO_RESULT(RemoveDatabaseFilesAndDirectory(
  12519                       *directory, subdirNameBase, /* aQuotaManager */ nullptr,
  12520                       aPersistenceType, aOriginMetadata,
  12521                       /* aDatabaseName */ u""_ns)),
  12522                   Err(NS_ERROR_UNEXPECTED));
  12523 
  12524            databaseFilenames.Remove(subdirNameBase);
  12525            return Ok{};
  12526          }
  12527 
  12528          // The directory base must exist in databaseFilenames.
  12529          // If there is an unexpected directory in the idb directory, trying to
  12530          // delete at first instead of breaking the whole initialization.
  12531 
  12532          // XXX This is still somewhat quirky. It would be nice to make it
  12533          // clear that the warning handler is infallible, which would also
  12534          // remove the need for the error type conversion.
  12535          QM_WARNONLY_TRY(QM_OR_ELSE_WARN(
  12536              // Expression.
  12537              OkIf(databaseFilenames.Contains(subdirNameBase))
  12538                  .mapErr([](const NotOk) { return NS_ERROR_FAILURE; }),
  12539              // Fallback.
  12540              ([&directory,
  12541                &subdirName](const nsresult) -> Result<Ok, nsresult> {
  12542                // XXX It seems if we really got here, we can fail the
  12543                // MOZ_ASSERT(!quotaManager->IsTemporaryStorageInitializedInternal());
  12544                // assertion in DeleteFilesNoQuota.
  12545                QM_TRY(MOZ_TO_RESULT(DeleteFilesNoQuota(directory, subdirName)),
  12546                       Err(NS_ERROR_UNEXPECTED));
  12547 
  12548                return Ok{};
  12549              })));
  12550 
  12551          return Ok{};
  12552        }));
  12553  }
  12554 
  12555  for (const auto& databaseFilename : databaseFilenames) {
  12556    if (aCanceled) {
  12557      break;
  12558    }
  12559 
  12560    QM_TRY_INSPECT(
  12561        const auto& fmDirectory,
  12562        CloneFileAndAppend(*directory,
  12563                           databaseFilename + kFileManagerDirectoryNameSuffix));
  12564 
  12565    QM_TRY_INSPECT(
  12566        const auto& databaseFile,
  12567        CloneFileAndAppend(*directory, databaseFilename + kSQLiteSuffix));
  12568 
  12569    if (aInitializing) {
  12570      QM_TRY(MOZ_TO_RESULT(DatabaseFileManager::InitDirectory(
  12571          *fmDirectory, *databaseFile, aOriginMetadata.mOrigin,
  12572          TelemetryIdForFile(databaseFile))));
  12573    }
  12574 
  12575    if (aUsageInfo) {
  12576      {
  12577        QM_TRY_INSPECT(const int64_t& fileSize,
  12578                       MOZ_TO_RESULT_INVOKE_MEMBER(databaseFile, GetFileSize));
  12579 
  12580        MOZ_ASSERT(fileSize >= 0);
  12581 
  12582        *aUsageInfo += DatabaseUsageType(Some(uint64_t(fileSize)));
  12583      }
  12584 
  12585      {
  12586        QM_TRY_INSPECT(const auto& walFile,
  12587                       CloneFileAndAppend(*directory,
  12588                                          databaseFilename + kSQLiteWALSuffix));
  12589 
  12590        // QM_OR_ELSE_WARN_IF is not used here since we just want to log
  12591        // NS_ERROR_FILE_NOT_FOUND result and not spam the reports (the -wal
  12592        // file doesn't have to exist).
  12593        QM_TRY_INSPECT(const int64_t& walFileSize,
  12594                       QM_OR_ELSE_LOG_VERBOSE_IF(
  12595                           // Expression.
  12596                           MOZ_TO_RESULT_INVOKE_MEMBER(walFile, GetFileSize),
  12597                           // Predicate.
  12598                           ([](const nsresult rv) {
  12599                             return rv == NS_ERROR_FILE_NOT_FOUND;
  12600                           }),
  12601                           // Fallback.
  12602                           (ErrToOk<0, int64_t>)));
  12603        MOZ_ASSERT(walFileSize >= 0);
  12604        *aUsageInfo += DatabaseUsageType(Some(uint64_t(walFileSize)));
  12605      }
  12606 
  12607      {
  12608        QM_TRY_INSPECT(const auto& fileUsage,
  12609                       DatabaseFileManager::GetUsage(fmDirectory));
  12610 
  12611        *aUsageInfo += fileUsage;
  12612      }
  12613    }
  12614  }
  12615 
  12616  return NS_OK;
  12617 }
  12618 
  12619 void QuotaClient::OnOriginClearCompleted(
  12620    const OriginMetadata& aOriginMetadata) {
  12621  AssertIsOnIOThread();
  12622 
  12623  if (IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get()) {
  12624    mgr->InvalidateFileManagers(aOriginMetadata.mPersistenceType,
  12625                                aOriginMetadata.mOrigin);
  12626  }
  12627 }
  12628 
  12629 void QuotaClient::OnRepositoryClearCompleted(PersistenceType aPersistenceType) {
  12630  AssertIsOnIOThread();
  12631 
  12632  if (IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get()) {
  12633    mgr->InvalidateFileManagers(aPersistenceType);
  12634  }
  12635 }
  12636 
  12637 void QuotaClient::ReleaseIOThreadObjects() {
  12638  AssertIsOnIOThread();
  12639 
  12640  if (IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get()) {
  12641    mgr->InvalidateAllFileManagers();
  12642  }
  12643 }
  12644 
  12645 void QuotaClient::AbortOperationsForLocks(
  12646    const DirectoryLockIdTable& aDirectoryLockIds) {
  12647  AssertIsOnBackgroundThread();
  12648 
  12649  InvalidateLiveDatabasesMatching([&aDirectoryLockIds](const auto& database) {
  12650    // If the database is registered in gLiveDatabaseHashtable then it must have
  12651    // a directory lock.
  12652    return IsLockForObjectContainedInLockTable(database, aDirectoryLockIds);
  12653  });
  12654 }
  12655 
  12656 void QuotaClient::AbortOperationsForProcess(ContentParentId aContentParentId) {
  12657  AssertIsOnBackgroundThread();
  12658 
  12659  InvalidateLiveDatabasesMatching([&aContentParentId](const auto& database) {
  12660    return database.IsOwnedByProcess(aContentParentId);
  12661  });
  12662 }
  12663 
  12664 void QuotaClient::AbortAllOperations() {
  12665  AssertIsOnBackgroundThread();
  12666 
  12667  AbortAllMaintenances();
  12668 
  12669  InvalidateLiveDatabasesMatching([](const auto&) { return true; });
  12670 }
  12671 
  12672 void QuotaClient::StartIdleMaintenance() {
  12673  AssertIsOnBackgroundThread();
  12674  if (IsShuttingDownOnBackgroundThread()) {
  12675    MOZ_ASSERT(false, "!IsShuttingDownOnBackgroundThread()");
  12676    return;
  12677  }
  12678 
  12679  DoMaintenance();
  12680 }
  12681 
  12682 void QuotaClient::StopIdleMaintenance() {
  12683  AssertIsOnBackgroundThread();
  12684 
  12685  AbortAllMaintenances();
  12686 }
  12687 
  12688 void QuotaClient::InitiateShutdown() {
  12689  AssertIsOnBackgroundThread();
  12690  MOZ_ASSERT(IsShuttingDownOnBackgroundThread());
  12691 
  12692  if (mDeleteTimer) {
  12693    // QuotaClient::AsyncDeleteFile will not schedule new timers beyond
  12694    // shutdown. And we expect all critical (PBM) deletions to have been
  12695    // triggered before this point via ClearPrivateRepository (w/out using
  12696    // DeleteFilesRunnable at all).
  12697    mDeleteTimer->Cancel();
  12698    mDeleteTimer = nullptr;
  12699    mPendingDeleteInfos.Clear();
  12700  }
  12701 
  12702  AbortAllOperations();
  12703 }
  12704 
  12705 bool QuotaClient::IsShutdownCompleted() const {
  12706  return (!gFactoryOps || gFactoryOps->isEmpty()) &&
  12707         (!gLiveDatabaseHashtable || !gLiveDatabaseHashtable->Count()) &&
  12708         !mCurrentMaintenance && !DeleteFilesRunnable::IsDeletionPending();
  12709 }
  12710 
  12711 void QuotaClient::ForceKillActors() {
  12712  // Currently we don't implement force killing actors.
  12713 }
  12714 
  12715 nsCString QuotaClient::GetShutdownStatus() const {
  12716  AssertIsOnBackgroundThread();
  12717 
  12718  nsCString data;
  12719 
  12720  if (gFactoryOps && !gFactoryOps->isEmpty()) {
  12721    data.Append("FactoryOperations: "_ns +
  12722                IntToCString(static_cast<uint32_t>(gFactoryOps->length())) +
  12723                " ("_ns);
  12724 
  12725    // XXX It might be confusing to remove duplicates here, as the actual list
  12726    // won't match the count then.
  12727    nsTHashSet<nsCString> ids;
  12728 
  12729    std::transform(gFactoryOps->begin(), gFactoryOps->end(), MakeInserter(ids),
  12730                   [](const FactoryOp* const factoryOp) {
  12731                     MOZ_ASSERT(factoryOp);
  12732 
  12733                     nsCString id;
  12734                     factoryOp->Stringify(id);
  12735                     return id;
  12736                   });
  12737 
  12738    StringJoinAppend(data, ", "_ns, ids);
  12739 
  12740    data.Append(")\n");
  12741  }
  12742 
  12743  if (gLiveDatabaseHashtable && gLiveDatabaseHashtable->Count()) {
  12744    data.Append("LiveDatabases: "_ns +
  12745                IntToCString(gLiveDatabaseHashtable->Count()) + " ("_ns);
  12746 
  12747    // XXX It might be confusing to remove duplicates here, as the actual list
  12748    // won't match the count then.
  12749    nsTHashSet<nsCString> ids;
  12750 
  12751    for (const auto& entry : gLiveDatabaseHashtable->Values()) {
  12752      MOZ_ASSERT(entry);
  12753 
  12754      std::transform(entry->mLiveDatabases.begin(), entry->mLiveDatabases.end(),
  12755                     MakeInserter(ids), [](const Database* const database) {
  12756                       nsCString id;
  12757                       database->Stringify(id);
  12758                       return id;
  12759                     });
  12760    }
  12761 
  12762    StringJoinAppend(data, ", "_ns, ids);
  12763 
  12764    data.Append(")\n");
  12765  }
  12766 
  12767  if (mCurrentMaintenance) {
  12768    data.Append("IdleMaintenance: 1 (");
  12769    mCurrentMaintenance->Stringify(data);
  12770    data.Append(")\n");
  12771  }
  12772 
  12773  return data;
  12774 }
  12775 
  12776 void QuotaClient::FinalizeShutdown() {
  12777  RefPtr<ConnectionPool> connectionPool = gConnectionPool.get();
  12778  if (connectionPool) {
  12779    connectionPool->Shutdown();
  12780 
  12781    gConnectionPool = nullptr;
  12782  }
  12783 
  12784  if (mMaintenanceThreadPool) {
  12785    mMaintenanceThreadPool->Shutdown();
  12786    mMaintenanceThreadPool = nullptr;
  12787  }
  12788 }
  12789 
  12790 void QuotaClient::DeleteTimerCallback(nsITimer* aTimer, void* aClosure) {
  12791  AssertIsOnBackgroundThread();
  12792  MOZ_ASSERT(aTimer);
  12793 
  12794  // Even though we do not schedule new timers after shutdown has started,
  12795  // an already existing one might fire afterwards (actually we think it
  12796  // shouldn't, but there is no reason to enforce this invariant). We can
  12797  // just ignore it, the cleanup work is done in InitiateShutdown.
  12798  if (NS_WARN_IF(IsShuttingDownOnBackgroundThread())) {
  12799    return;
  12800  }
  12801 
  12802  auto* const self = static_cast<QuotaClient*>(aClosure);
  12803  MOZ_ASSERT(self);
  12804  MOZ_ASSERT(self->mDeleteTimer);
  12805  MOZ_ASSERT(SameCOMIdentity(self->mDeleteTimer, aTimer));
  12806 
  12807  for (const auto& pendingDeleteInfoEntry : self->mPendingDeleteInfos) {
  12808    const auto& key = pendingDeleteInfoEntry.GetKey();
  12809    const auto& value = pendingDeleteInfoEntry.GetData();
  12810    MOZ_ASSERT(!value->IsEmpty());
  12811 
  12812    RefPtr<DeleteFilesRunnable> runnable = new DeleteFilesRunnable(
  12813        SafeRefPtr{key, AcquireStrongRefFromRawPtr{}}, std::move(*value));
  12814 
  12815    MOZ_ASSERT(value->IsEmpty());
  12816 
  12817    runnable->RunImmediately();
  12818  }
  12819 
  12820  self->mPendingDeleteInfos.Clear();
  12821 }
  12822 
  12823 void QuotaClient::AbortAllMaintenances() {
  12824  if (mCurrentMaintenance) {
  12825    mCurrentMaintenance->Abort();
  12826  }
  12827 
  12828  for (const auto& maintenance : mMaintenanceQueue) {
  12829    maintenance->Abort();
  12830  }
  12831 }
  12832 
  12833 Result<nsCOMPtr<nsIFile>, nsresult> QuotaClient::GetDirectory(
  12834    const OriginMetadata& aOriginMetadata) {
  12835  QuotaManager* const quotaManager = QuotaManager::Get();
  12836  NS_ASSERTION(quotaManager, "This should never fail!");
  12837 
  12838  QM_TRY_INSPECT(const auto& directory,
  12839                 quotaManager->GetOriginDirectory(aOriginMetadata));
  12840 
  12841  MOZ_ASSERT(directory);
  12842 
  12843  QM_TRY(MOZ_TO_RESULT(
  12844      directory->Append(NS_LITERAL_STRING_FROM_CSTRING(IDB_DIRECTORY_NAME))));
  12845 
  12846  return directory;
  12847 }
  12848 
  12849 template <QuotaClient::ObsoleteFilenamesHandling ObsoleteFilenames>
  12850 Result<QuotaClient::GetDatabaseFilenamesResult<ObsoleteFilenames>, nsresult>
  12851 QuotaClient::GetDatabaseFilenames(nsIFile& aDirectory,
  12852                                  const AtomicBool& aCanceled) {
  12853  AssertIsOnIOThread();
  12854 
  12855  GetDatabaseFilenamesResult<ObsoleteFilenames> result;
  12856 
  12857  QM_TRY(CollectEachFileAtomicCancelable(
  12858      aDirectory, aCanceled,
  12859      [&result](const nsCOMPtr<nsIFile>& file) -> Result<Ok, nsresult> {
  12860        QM_TRY_INSPECT(const auto& leafName, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
  12861                                                 nsString, file, GetLeafName));
  12862 
  12863        QM_TRY_INSPECT(const auto& dirEntryKind, GetDirEntryKind(*file));
  12864 
  12865        switch (dirEntryKind) {
  12866          case nsIFileKind::ExistsAsDirectory:
  12867            result.subdirsToProcess.AppendElement(leafName);
  12868            break;
  12869 
  12870          case nsIFileKind::ExistsAsFile: {
  12871            if constexpr (ObsoleteFilenames ==
  12872                          ObsoleteFilenamesHandling::Include) {
  12873              if (StringBeginsWith(leafName, kIdbDeletionMarkerFilePrefix)) {
  12874                result.obsoleteFilenames.Insert(
  12875                    Substring(leafName, kIdbDeletionMarkerFilePrefix.Length()));
  12876                break;
  12877              }
  12878            }
  12879 
  12880            // Skip OS metadata files. These files are only used in different
  12881            // platforms, but the profile can be shared across different
  12882            // operating systems, so we check it on all platforms.
  12883            if (QuotaManager::IsOSMetadata(leafName)) {
  12884              break;
  12885            }
  12886 
  12887            // Skip files starting with ".".
  12888            if (QuotaManager::IsDotFile(leafName)) {
  12889              break;
  12890            }
  12891 
  12892            // Skip SQLite temporary files. These files take up space on disk
  12893            // but will be deleted as soon as the database is opened, so we
  12894            // don't count them towards quota.
  12895            if (StringEndsWith(leafName, kSQLiteJournalSuffix) ||
  12896                StringEndsWith(leafName, kSQLiteSHMSuffix)) {
  12897              break;
  12898            }
  12899 
  12900            // The SQLite WAL file does count towards quota, but it is handled
  12901            // below once we find the actual database file.
  12902            if (StringEndsWith(leafName, kSQLiteWALSuffix)) {
  12903              break;
  12904            }
  12905 
  12906            nsDependentSubstring leafNameBase;
  12907            if (!GetFilenameBase(leafName, kSQLiteSuffix, leafNameBase)) {
  12908              UNKNOWN_FILE_WARNING(leafName);
  12909              break;
  12910            }
  12911 
  12912            result.databaseFilenames.Insert(leafNameBase);
  12913            break;
  12914          }
  12915 
  12916          case nsIFileKind::DoesNotExist:
  12917            // Ignore files that got removed externally while iterating.
  12918            break;
  12919        }
  12920 
  12921        return Ok{};
  12922      }));
  12923 
  12924  return result;
  12925 }
  12926 
  12927 void QuotaClient::ProcessMaintenanceQueue() {
  12928  AssertIsOnBackgroundThread();
  12929 
  12930  if (mCurrentMaintenance || mMaintenanceQueue.IsEmpty()) {
  12931    return;
  12932  }
  12933 
  12934  mCurrentMaintenance = mMaintenanceQueue[0];
  12935  mMaintenanceQueue.RemoveElementAt(0);
  12936 
  12937  mCurrentMaintenance->RunImmediately();
  12938 }
  12939 
  12940 /*******************************************************************************
  12941 * DeleteFilesRunnable
  12942 ******************************************************************************/
  12943 
  12944 uint64_t DeleteFilesRunnable::sPendingRunnables = 0;
  12945 
  12946 DeleteFilesRunnable::DeleteFilesRunnable(
  12947    SafeRefPtr<DatabaseFileManager> aFileManager, nsTArray<int64_t>&& aFileIds)
  12948    : Runnable("dom::indexeddb::DeleteFilesRunnable"),
  12949      mOwningEventTarget(GetCurrentSerialEventTarget()),
  12950      mFileManager(std::move(aFileManager)),
  12951      mFileIds(std::move(aFileIds)),
  12952      mState(State_Initial) {}
  12953 
  12954 #ifdef DEBUG
  12955 DeleteFilesRunnable::~DeleteFilesRunnable() {
  12956  MOZ_ASSERT(!mDEBUGCountsAsPending);
  12957 }
  12958 #endif
  12959 
  12960 void DeleteFilesRunnable::RunImmediately() {
  12961  AssertIsOnBackgroundThread();
  12962  MOZ_ASSERT(mState == State_Initial);
  12963 
  12964  (void)this->Run();
  12965 }
  12966 
  12967 void DeleteFilesRunnable::Open() {
  12968  AssertIsOnBackgroundThread();
  12969  MOZ_ASSERT(mState == State_Initial);
  12970 
  12971  MOZ_ASSERT(!mDEBUGCountsAsPending);
  12972  sPendingRunnables++;
  12973  DEBUGONLY(mDEBUGCountsAsPending = true);
  12974 
  12975  QuotaManager* const quotaManager = QuotaManager::Get();
  12976  if (NS_WARN_IF(!quotaManager)) {
  12977    Finish();
  12978    return;
  12979  }
  12980 
  12981  mState = State_DirectoryOpenPending;
  12982 
  12983  quotaManager
  12984      ->OpenClientDirectory(
  12985          {mFileManager->OriginMetadata(), quota::Client::IDB})
  12986      ->Then(
  12987          GetCurrentSerialEventTarget(), __func__,
  12988          [self = RefPtr(this)](QuotaManager::ClientDirectoryLockHandlePromise::
  12989                                    ResolveOrRejectValue&& aValue) {
  12990            if (aValue.IsResolve()) {
  12991              self->DirectoryLockAcquired(std::move(aValue.ResolveValue()));
  12992            } else {
  12993              self->DirectoryLockFailed();
  12994            }
  12995          });
  12996 }
  12997 
  12998 void DeleteFilesRunnable::DoDatabaseWork() {
  12999  AssertIsOnIOThread();
  13000  MOZ_ASSERT(mState == State_DatabaseWorkOpen);
  13001 
  13002  if (!mFileManager->Invalidated()) {
  13003    for (int64_t fileId : mFileIds) {
  13004      if (NS_FAILED(mFileManager->SyncDeleteFile(fileId))) {
  13005        NS_WARNING("Failed to delete file!");
  13006      }
  13007    }
  13008  }
  13009 
  13010  Finish();
  13011 }
  13012 
  13013 void DeleteFilesRunnable::Finish() {
  13014  MOZ_ASSERT(mState != State_UnblockingOpen);
  13015 
  13016  // Must set mState before dispatching otherwise we will race with the main
  13017  // thread.
  13018  mState = State_UnblockingOpen;
  13019 
  13020  MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
  13021 }
  13022 
  13023 void DeleteFilesRunnable::UnblockOpen() {
  13024  AssertIsOnBackgroundThread();
  13025  MOZ_ASSERT(mState == State_UnblockingOpen);
  13026 
  13027  {
  13028    auto destroyingDirectoryLockHandle = std::move(mDirectoryLockHandle);
  13029  }
  13030 
  13031  MOZ_ASSERT(mDEBUGCountsAsPending);
  13032  sPendingRunnables--;
  13033  DEBUGONLY(mDEBUGCountsAsPending = false);
  13034 
  13035  mState = State_Completed;
  13036 }
  13037 
  13038 NS_IMETHODIMP
  13039 DeleteFilesRunnable::Run() {
  13040  switch (mState) {
  13041    case State_Initial:
  13042      Open();
  13043      break;
  13044 
  13045    case State_DatabaseWorkOpen:
  13046      DoDatabaseWork();
  13047      break;
  13048 
  13049    case State_UnblockingOpen:
  13050      UnblockOpen();
  13051      break;
  13052 
  13053    case State_DirectoryOpenPending:
  13054    default:
  13055      MOZ_CRASH("Should never get here!");
  13056  }
  13057 
  13058  return NS_OK;
  13059 }
  13060 
  13061 void DeleteFilesRunnable::DirectoryLockAcquired(
  13062    ClientDirectoryLockHandle aLockHandle) {
  13063  AssertIsOnBackgroundThread();
  13064  MOZ_ASSERT(mState == State_DirectoryOpenPending);
  13065  MOZ_ASSERT(!mDirectoryLockHandle);
  13066 
  13067  mDirectoryLockHandle = std::move(aLockHandle);
  13068 
  13069  QuotaManager* const quotaManager = QuotaManager::Get();
  13070  MOZ_ASSERT(quotaManager);
  13071 
  13072  // Must set this before dispatching otherwise we will race with the IO thread
  13073  mState = State_DatabaseWorkOpen;
  13074 
  13075  QM_TRY(MOZ_TO_RESULT(
  13076             quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL)),
  13077         QM_VOID, [this](const nsresult) { Finish(); });
  13078 }
  13079 
  13080 void DeleteFilesRunnable::DirectoryLockFailed() {
  13081  AssertIsOnBackgroundThread();
  13082  MOZ_ASSERT(mState == State_DirectoryOpenPending);
  13083  MOZ_ASSERT(!mDirectoryLockHandle);
  13084 
  13085  Finish();
  13086 }
  13087 
  13088 void Maintenance::Abort() {
  13089  AssertIsOnBackgroundThread();
  13090 
  13091  // Safe because mDatabaseMaintenances is modified
  13092  // only in the background thread
  13093  for (const auto& aDatabaseMaintenance : mDatabaseMaintenances) {
  13094    aDatabaseMaintenance.GetData()->Abort();
  13095  }
  13096 
  13097  mAborted = true;
  13098 }
  13099 
  13100 void Maintenance::RegisterDatabaseMaintenance(
  13101    DatabaseMaintenance* aDatabaseMaintenance) {
  13102  AssertIsOnBackgroundThread();
  13103  MOZ_ASSERT(aDatabaseMaintenance);
  13104  MOZ_ASSERT(mState == State::BeginDatabaseMaintenance);
  13105  MOZ_ASSERT(
  13106      !mDatabaseMaintenances.Contains(aDatabaseMaintenance->DatabasePath()));
  13107 
  13108  mDatabaseMaintenances.InsertOrUpdate(aDatabaseMaintenance->DatabasePath(),
  13109                                       aDatabaseMaintenance);
  13110 }
  13111 
  13112 void Maintenance::UnregisterDatabaseMaintenance(
  13113    DatabaseMaintenance* aDatabaseMaintenance) {
  13114  AssertIsOnBackgroundThread();
  13115  MOZ_ASSERT(aDatabaseMaintenance);
  13116  MOZ_ASSERT(mState == State::WaitingForDatabaseMaintenancesToComplete);
  13117  MOZ_ASSERT(mDatabaseMaintenances.Get(aDatabaseMaintenance->DatabasePath()));
  13118 
  13119  mDatabaseMaintenances.Remove(aDatabaseMaintenance->DatabasePath());
  13120 
  13121  if (mDatabaseMaintenances.Count()) {
  13122    return;
  13123  }
  13124 
  13125  for (const auto& completeCallback : mCompleteCallbacks) {
  13126    MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(completeCallback));
  13127  }
  13128  mCompleteCallbacks.Clear();
  13129 
  13130  mState = State::Finishing;
  13131  Finish();
  13132 }
  13133 
  13134 void Maintenance::Stringify(nsACString& aResult) const {
  13135  AssertIsOnBackgroundThread();
  13136 
  13137  aResult.Append("DatabaseMaintenances: "_ns +
  13138                 IntToCString(mDatabaseMaintenances.Count()) + " ("_ns);
  13139 
  13140  // XXX It might be confusing to remove duplicates here, as the actual list
  13141  // won't match the count then.
  13142  nsTHashSet<nsCString> ids;
  13143  std::transform(mDatabaseMaintenances.Values().cbegin(),
  13144                 mDatabaseMaintenances.Values().cend(), MakeInserter(ids),
  13145                 [](const auto& entry) {
  13146                   MOZ_ASSERT(entry);
  13147 
  13148                   nsCString id;
  13149                   entry->Stringify(id);
  13150 
  13151                   return id;
  13152                 });
  13153 
  13154  StringJoinAppend(aResult, ", "_ns, ids);
  13155 
  13156  aResult.Append(")");
  13157 }
  13158 
  13159 nsresult Maintenance::Start() {
  13160  AssertIsOnBackgroundThread();
  13161  MOZ_ASSERT(mState == State::Initial);
  13162 
  13163  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
  13164      IsAborted()) {
  13165    return NS_ERROR_ABORT;
  13166  }
  13167 
  13168  // Make sure that the IndexedDatabaseManager is running so that we can check
  13169  // for low disk space mode.
  13170 
  13171  if (IndexedDatabaseManager::Get()) {
  13172    OpenDirectory();
  13173    return NS_OK;
  13174  }
  13175 
  13176  mState = State::CreateIndexedDatabaseManager;
  13177  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
  13178 
  13179  return NS_OK;
  13180 }
  13181 
  13182 nsresult Maintenance::CreateIndexedDatabaseManager() {
  13183  MOZ_ASSERT(NS_IsMainThread());
  13184  MOZ_ASSERT(mState == State::CreateIndexedDatabaseManager);
  13185 
  13186  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
  13187      IsAborted()) {
  13188    return NS_ERROR_ABORT;
  13189  }
  13190 
  13191  IndexedDatabaseManager* const mgr = IndexedDatabaseManager::GetOrCreate();
  13192  if (NS_WARN_IF(!mgr)) {
  13193    return NS_ERROR_FAILURE;
  13194  }
  13195 
  13196  mState = State::IndexedDatabaseManagerOpen;
  13197  MOZ_ALWAYS_SUCCEEDS(
  13198      mQuotaClient->BackgroundThread()->Dispatch(this, NS_DISPATCH_NORMAL));
  13199 
  13200  return NS_OK;
  13201 }
  13202 
  13203 RefPtr<UniversalDirectoryLockPromise> Maintenance::OpenStorageDirectory(
  13204    const PersistenceScope& aPersistenceScope, bool aInitializeOrigins) {
  13205  AssertIsOnBackgroundThread();
  13206  MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
  13207  MOZ_ASSERT(!mDirectoryLock);
  13208  MOZ_ASSERT(!mAborted);
  13209  MOZ_ASSERT(mState == State::DirectoryOpenPending);
  13210 
  13211  QuotaManager* quotaManager = QuotaManager::Get();
  13212  MOZ_ASSERT(quotaManager);
  13213 
  13214  // Return a shared lock for <profile>/storage/repo(aPersistenceScope)/*/idb
  13215  return quotaManager->OpenStorageDirectory(
  13216      aPersistenceScope, OriginScope::FromNull(),
  13217      ClientStorageScope::CreateFromClient(Client::IDB),
  13218      /* aExclusive */ false, aInitializeOrigins, DirectoryLockCategory::None,
  13219      SomeRef(mPendingDirectoryLock));
  13220 }
  13221 
  13222 nsresult Maintenance::OpenDirectory() {
  13223  AssertIsOnBackgroundThread();
  13224  MOZ_ASSERT(mState == State::Initial ||
  13225             mState == State::IndexedDatabaseManagerOpen);
  13226  MOZ_ASSERT(!mDirectoryLock);
  13227  MOZ_ASSERT(QuotaManager::Get());
  13228 
  13229  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
  13230      IsAborted()) {
  13231    return NS_ERROR_ABORT;
  13232  }
  13233 
  13234  mState = State::DirectoryOpenPending;
  13235 
  13236  // Since idle maintenance may occur before persistent or temporary storage is
  13237  // initialized, make sure it's initialized here (all persistent and
  13238  // non-persistent origins need to be cleaned up and quota info needs to be
  13239  // loaded for non-persistent origins).
  13240 
  13241  OpenStorageDirectory(PersistenceScope::CreateFromNull(),
  13242                       /* aInitializeOrigins */ true)
  13243      ->Then(
  13244          GetCurrentSerialEventTarget(), __func__,
  13245          [self = RefPtr(this)](
  13246              const UniversalDirectoryLockPromise::ResolveOrRejectValue&
  13247                  aValue) {
  13248            if (aValue.IsResolve()) {
  13249              self->DirectoryLockAcquired(aValue.ResolveValue());
  13250              return;
  13251            }
  13252 
  13253            // Don't fail whole idle maintenance in case of an error, the
  13254            // persistent repository can still be processed.
  13255 
  13256            self->mPendingDirectoryLock = nullptr;
  13257            self->mOpenStorageForAllRepositoriesFailed = true;
  13258 
  13259            if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
  13260                self->IsAborted()) {
  13261              self->DirectoryLockFailed();
  13262              return;
  13263            }
  13264 
  13265            self->OpenStorageDirectory(PersistenceScope::CreateFromValue(
  13266                                           PERSISTENCE_TYPE_PERSISTENT),
  13267                                       /* aInitializeOrigins */ true)
  13268                ->Then(GetCurrentSerialEventTarget(), __func__,
  13269                       [self](const UniversalDirectoryLockPromise::
  13270                                  ResolveOrRejectValue& aValue) {
  13271                         if (aValue.IsResolve()) {
  13272                           self->DirectoryLockAcquired(aValue.ResolveValue());
  13273                         } else {
  13274                           self->DirectoryLockFailed();
  13275                         }
  13276                       });
  13277          });
  13278 
  13279  return NS_OK;
  13280 }
  13281 
  13282 nsresult Maintenance::DirectoryOpen() {
  13283  AssertIsOnBackgroundThread();
  13284  MOZ_ASSERT(mState == State::DirectoryOpenPending);
  13285  MOZ_ASSERT(mDirectoryLock);
  13286 
  13287  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
  13288      IsAborted()) {
  13289    return NS_ERROR_ABORT;
  13290  }
  13291 
  13292  QuotaManager* const quotaManager = QuotaManager::Get();
  13293  MOZ_ASSERT(quotaManager);
  13294 
  13295  mState = State::DirectoryWorkOpen;
  13296 
  13297  QM_TRY(MOZ_TO_RESULT(
  13298             quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL)),
  13299         NS_ERROR_FAILURE);
  13300 
  13301  return NS_OK;
  13302 }
  13303 
  13304 nsresult Maintenance::DirectoryWork() {
  13305  AssertIsOnIOThread();
  13306  MOZ_ASSERT(mState == State::DirectoryWorkOpen);
  13307 
  13308  // The storage directory is structured like this:
  13309  //
  13310  //   <profile>/storage/<persistence>/<origin>/idb/*.sqlite
  13311  //
  13312  // We have to find all database files that match any persistence type and any
  13313  // origin. We ignore anything out of the ordinary for now.
  13314 
  13315  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
  13316      IsAborted()) {
  13317    return NS_ERROR_ABORT;
  13318  }
  13319 
  13320  QuotaManager* const quotaManager = QuotaManager::Get();
  13321  MOZ_ASSERT(quotaManager);
  13322 
  13323  const nsCOMPtr<nsIFile> storageDir =
  13324      GetFileForPath(quotaManager->GetStoragePath());
  13325  QM_TRY(OkIf(storageDir), NS_ERROR_FAILURE);
  13326 
  13327  {
  13328    QM_TRY_INSPECT(const bool& exists,
  13329                   MOZ_TO_RESULT_INVOKE_MEMBER(storageDir, Exists));
  13330 
  13331    // XXX No warning here?
  13332    if (!exists) {
  13333      return NS_ERROR_NOT_AVAILABLE;
  13334    }
  13335  }
  13336 
  13337  {
  13338    QM_TRY_INSPECT(const bool& isDirectory,
  13339                   MOZ_TO_RESULT_INVOKE_MEMBER(storageDir, IsDirectory));
  13340 
  13341    QM_TRY(OkIf(isDirectory), NS_ERROR_FAILURE);
  13342  }
  13343 
  13344  // There are currently only 4 persistence types, and we want to iterate them
  13345  // in this order:
  13346  static const PersistenceType kPersistenceTypes[] = {
  13347      PERSISTENCE_TYPE_PERSISTENT, PERSISTENCE_TYPE_DEFAULT,
  13348      PERSISTENCE_TYPE_TEMPORARY, PERSISTENCE_TYPE_PRIVATE};
  13349 
  13350  static_assert(
  13351      std::size(kPersistenceTypes) == size_t(PERSISTENCE_TYPE_INVALID),
  13352      "Something changed with available persistence types!");
  13353 
  13354  constexpr auto idbDirName =
  13355      NS_LITERAL_STRING_FROM_CSTRING(IDB_DIRECTORY_NAME);
  13356 
  13357  for (const PersistenceType persistenceType : kPersistenceTypes) {
  13358    // Loop over "<persistence>" directories.
  13359    if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
  13360        IsAborted()) {
  13361      return NS_ERROR_ABORT;
  13362    }
  13363 
  13364    // Don't do any maintenance for private browsing databases, which are only
  13365    // temporary.
  13366    if (persistenceType == PERSISTENCE_TYPE_PRIVATE) {
  13367      continue;
  13368    }
  13369 
  13370    const bool persistent = persistenceType == PERSISTENCE_TYPE_PERSISTENT;
  13371 
  13372    if (!persistent && mOpenStorageForAllRepositoriesFailed) {
  13373      // Non-persistent (best effort) repositories can't be processed if
  13374      // temporary storage initialization failed.
  13375      continue;
  13376    }
  13377 
  13378    // For all non-persistent types, temporary storage must already be
  13379    // initialized
  13380    MOZ_DIAGNOSTIC_ASSERT(
  13381        persistent || quotaManager->IsTemporaryStorageInitializedInternal());
  13382 
  13383    // XXX persistenceType == PERSISTENCE_TYPE_PERSISTENT shouldn't be a special
  13384    // case...
  13385    const auto persistenceTypeString =
  13386        persistenceType == PERSISTENCE_TYPE_PERSISTENT
  13387            ? "permanent"_ns
  13388            : PersistenceTypeToString(persistenceType);
  13389 
  13390    QM_TRY_INSPECT(const auto& persistenceDir,
  13391                   CloneFileAndAppend(*storageDir, NS_ConvertASCIItoUTF16(
  13392                                                       persistenceTypeString)));
  13393 
  13394    {
  13395      QM_TRY_INSPECT(const bool& exists,
  13396                     MOZ_TO_RESULT_INVOKE_MEMBER(persistenceDir, Exists));
  13397 
  13398      if (!exists) {
  13399        continue;
  13400      }
  13401 
  13402      QM_TRY_INSPECT(const bool& isDirectory,
  13403                     MOZ_TO_RESULT_INVOKE_MEMBER(persistenceDir, IsDirectory));
  13404 
  13405      if (NS_WARN_IF(!isDirectory)) {
  13406        continue;
  13407      }
  13408    }
  13409 
  13410    // Loop over "<origin>/idb" directories.
  13411    QM_TRY(CollectEachFile(
  13412        *persistenceDir,
  13413        [this, &quotaManager, persistenceType, persistent, &idbDirName](
  13414            const nsCOMPtr<nsIFile>& originDir) -> Result<Ok, nsresult> {
  13415          if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
  13416              IsAborted()) {
  13417            return Err(NS_ERROR_ABORT);
  13418          }
  13419 
  13420          QM_TRY_INSPECT(const auto& dirEntryKind, GetDirEntryKind(*originDir));
  13421 
  13422          switch (dirEntryKind) {
  13423            case nsIFileKind::ExistsAsFile:
  13424              break;
  13425 
  13426            case nsIFileKind::ExistsAsDirectory: {
  13427              // Get the necessary information about the origin
  13428              // (GetOriginMetadata also checks if it's a valid origin).
  13429 
  13430              QM_TRY_UNWRAP(auto metadata,
  13431                            quotaManager->GetOriginMetadata(originDir),
  13432                            // Not much we can do here...
  13433                            Ok{});
  13434 
  13435              if (!persistent &&
  13436                  !quotaManager->IsTemporaryOriginInitializedInternal(
  13437                      metadata)) {
  13438                glean::idb_maintenance::fallback_fullrestore_metadata.Add();
  13439 
  13440                // XXX GetOriginMetadata, which skips loading the metadata file
  13441                // and instead relies on parsing the origin directory name and
  13442                // reconstructing the principal, may produce a different origin
  13443                // string than the one originally used to create the origin
  13444                // directory.
  13445                //
  13446                // For now, if this mismatch occurs, we fall back to the slower
  13447                // LoadFullOriginMetadataWithRestore.
  13448                //
  13449                // In the future, it would be useful to report anonymized
  13450                // origin strings via telemetry to help investigate and
  13451                // eventually fix this mismatch.
  13452 
  13453                QM_TRY_INSPECT(
  13454                    const auto& fullmetadataAndStatus,
  13455                    quotaManager->LoadFullOriginMetadataWithRestoreAndStatus(
  13456                        originDir),
  13457                    Ok{});
  13458 
  13459                metadata = fullmetadataAndStatus.first;
  13460                if (fullmetadataAndStatus.second) {
  13461                  /* metadata was restored */
  13462                  glean::idb_maintenance::metadata_restored.Add();
  13463                }
  13464 
  13465                QM_TRY(OkIf(quotaManager->IsTemporaryOriginInitializedInternal(
  13466                           metadata)),
  13467                       /* unexpected but still fine to just skip it */ Ok{},
  13468                       /* increment telemetry in case of failure */
  13469                       [](const auto&) {
  13470                         glean::idb_maintenance::unknown_metadata.Add();
  13471                       });
  13472              }
  13473 
  13474              // We now use a dedicated repository for private browsing
  13475              // databases, but there could be some forgotten private browsing
  13476              // databases in other repositories, so it's better to check for
  13477              // that and don't do any maintenance for such databases.
  13478              if (metadata.mIsPrivate) {
  13479                return Ok{};
  13480              }
  13481 
  13482              QM_TRY_INSPECT(const auto& idbDir,
  13483                             CloneFileAndAppend(*originDir, idbDirName));
  13484 
  13485              QM_TRY_INSPECT(const bool& exists,
  13486                             MOZ_TO_RESULT_INVOKE_MEMBER(idbDir, Exists));
  13487 
  13488              if (!exists) {
  13489                return Ok{};
  13490              }
  13491 
  13492              QM_TRY_INSPECT(const bool& isDirectory,
  13493                             MOZ_TO_RESULT_INVOKE_MEMBER(idbDir, IsDirectory));
  13494 
  13495              QM_TRY(OkIf(isDirectory), Ok{});
  13496 
  13497              nsTArray<nsString> databasePaths;
  13498 
  13499              // Loop over files in the "idb" directory.
  13500              QM_TRY(CollectEachFile(
  13501                  *idbDir,
  13502                  [this, &databasePaths](const nsCOMPtr<nsIFile>& idbDirFile)
  13503                      -> Result<Ok, nsresult> {
  13504                    if (NS_WARN_IF(QuotaClient::
  13505                                       IsShuttingDownOnNonBackgroundThread()) ||
  13506                        IsAborted()) {
  13507                      return Err(NS_ERROR_ABORT);
  13508                    }
  13509 
  13510                    QM_TRY_UNWRAP(auto idbFilePath,
  13511                                  MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
  13512                                      nsString, idbDirFile, GetPath));
  13513 
  13514                    if (!StringEndsWith(idbFilePath, kSQLiteSuffix)) {
  13515                      return Ok{};
  13516                    }
  13517 
  13518                    QM_TRY_INSPECT(const auto& dirEntryKind,
  13519                                   GetDirEntryKind(*idbDirFile));
  13520 
  13521                    switch (dirEntryKind) {
  13522                      case nsIFileKind::ExistsAsDirectory:
  13523                        break;
  13524 
  13525                      case nsIFileKind::ExistsAsFile:
  13526                        // Found a database.
  13527 
  13528                        MOZ_ASSERT(!databasePaths.Contains(idbFilePath));
  13529 
  13530                        databasePaths.AppendElement(std::move(idbFilePath));
  13531                        break;
  13532 
  13533                      case nsIFileKind::DoesNotExist:
  13534                        // Ignore files that got removed externally while
  13535                        // iterating.
  13536                        break;
  13537                    }
  13538 
  13539                    return Ok{};
  13540                  }));
  13541 
  13542              if (!databasePaths.IsEmpty()) {
  13543                if (!persistent) {
  13544                  auto maybeOriginStateMetadata =
  13545                      quotaManager->GetOriginStateMetadata(metadata);
  13546                  auto originStateMetadata = maybeOriginStateMetadata.extract();
  13547 
  13548                  // Skip origin maintenance if the origin hasn't been accessed
  13549                  // since its last recorded maintenance. This avoids
  13550                  // unnecessary I/O and prevents updating the accessed flag in
  13551                  // metadata, which helps preserve the effectiveness of the L2
  13552                  // quota info cache.
  13553                  //
  13554                  // This early-out is safe because maintenance is only needed
  13555                  // when something has changed (e.g., new access or activity).
  13556                  const Date accessDate =
  13557                      Date::FromTimestamp(originStateMetadata.mLastAccessTime);
  13558                  const Date maintenanceDate =
  13559                      Date::FromDays(originStateMetadata.mLastMaintenanceDate);
  13560 
  13561                  if (accessDate <= maintenanceDate) {
  13562                    return Ok{};
  13563                  }
  13564 
  13565                  originStateMetadata.mLastMaintenanceDate =
  13566                      Date::Today().ToDays();
  13567                  originStateMetadata.mAccessed = true;
  13568 
  13569                  QM_TRY(MOZ_TO_RESULT(SaveDirectoryMetadataHeader(
  13570                      *originDir, originStateMetadata)));
  13571 
  13572                  quotaManager->UpdateOriginMaintenanceDate(
  13573                      metadata, originStateMetadata.mLastMaintenanceDate);
  13574                  quotaManager->UpdateOriginAccessed(metadata);
  13575                }
  13576 
  13577                mDirectoryInfos.EmplaceBack(persistenceType, metadata,
  13578                                            std::move(databasePaths));
  13579              }
  13580 
  13581              break;
  13582            }
  13583 
  13584            case nsIFileKind::DoesNotExist:
  13585              // Ignore files that got removed externally while iterating.
  13586              break;
  13587          }
  13588 
  13589          return Ok{};
  13590        }));
  13591  }
  13592 
  13593  mState = State::BeginDatabaseMaintenance;
  13594 
  13595  MOZ_ALWAYS_SUCCEEDS(
  13596      mQuotaClient->BackgroundThread()->Dispatch(this, NS_DISPATCH_NORMAL));
  13597 
  13598  return NS_OK;
  13599 }
  13600 
  13601 nsresult Maintenance::BeginDatabaseMaintenance() {
  13602  AssertIsOnBackgroundThread();
  13603  MOZ_ASSERT(mState == State::BeginDatabaseMaintenance);
  13604 
  13605  class MOZ_STACK_CLASS Helper final {
  13606   public:
  13607    static bool IsSafeToRunMaintenance(const nsAString& aDatabasePath) {
  13608      if (gFactoryOps) {
  13609        // XXX LinkedList should support reverse iteration via rbegin() and
  13610        // rend(), see bug 1964967.
  13611        for (const FactoryOp* existingOp = gFactoryOps->getLast(); existingOp;
  13612             existingOp = existingOp->getPrevious()) {
  13613          if (existingOp->DatabaseNameRef().isNothing()) {
  13614            return false;
  13615          }
  13616 
  13617          if (!existingOp->DatabaseFilePathIsKnown()) {
  13618            continue;
  13619          }
  13620 
  13621          if (existingOp->DatabaseFilePath() == aDatabasePath) {
  13622            return false;
  13623          }
  13624        }
  13625      }
  13626 
  13627      if (gLiveDatabaseHashtable) {
  13628        return std::all_of(gLiveDatabaseHashtable->Values().cbegin(),
  13629                           gLiveDatabaseHashtable->Values().cend(),
  13630                           [&aDatabasePath](const auto& liveDatabasesEntry) {
  13631                             // XXX std::all_of currently doesn't work with
  13632                             // LinkedList's iterator. See bug 1964969.
  13633                             for (const Database* const database :
  13634                                  liveDatabasesEntry->mLiveDatabases) {
  13635                               if (database->FilePath() == aDatabasePath) {
  13636                                 return false;
  13637                               }
  13638                             }
  13639                             return true;
  13640                           });
  13641      }
  13642 
  13643      return true;
  13644    }
  13645  };
  13646 
  13647  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
  13648      IsAborted()) {
  13649    return NS_ERROR_ABORT;
  13650  }
  13651 
  13652  RefPtr<nsThreadPool> threadPool;
  13653 
  13654  for (DirectoryInfo& directoryInfo : mDirectoryInfos) {
  13655    for (const nsAString& databasePath : *directoryInfo.mDatabasePaths) {
  13656      if (Helper::IsSafeToRunMaintenance(databasePath)) {
  13657        RefPtr<ClientDirectoryLock> directoryLock =
  13658            mDirectoryLock->SpecializeForClient(directoryInfo.mPersistenceType,
  13659                                                *directoryInfo.mOriginMetadata,
  13660                                                Client::IDB);
  13661        MOZ_ASSERT(directoryLock);
  13662 
  13663        // No key needs to be passed here, because we skip encrypted databases
  13664        // in DoDirectoryWork as long as they are only used in private browsing
  13665        // mode.
  13666        const auto databaseMaintenance = MakeRefPtr<DatabaseMaintenance>(
  13667            this, std::move(directoryLock), directoryInfo.mPersistenceType,
  13668            *directoryInfo.mOriginMetadata, databasePath, Nothing{});
  13669 
  13670        if (!threadPool) {
  13671          threadPool = mQuotaClient->GetOrCreateThreadPool();
  13672          MOZ_ASSERT(threadPool);
  13673        }
  13674 
  13675        // Perform database maintenance on a TaskQueue, as database connections
  13676        // require a serial event target when being opened in order to allow
  13677        // memory pressure notifications to clear caches (bug 1806751).
  13678        const auto taskQueue = TaskQueue::Create(
  13679            do_AddRef(threadPool), "IndexedDB Database Maintenance");
  13680 
  13681        MOZ_ALWAYS_SUCCEEDS(
  13682            taskQueue->Dispatch(databaseMaintenance, NS_DISPATCH_NORMAL));
  13683 
  13684        RegisterDatabaseMaintenance(databaseMaintenance);
  13685      }
  13686    }
  13687  }
  13688 
  13689  mDirectoryInfos.Clear();
  13690 
  13691  DropDirectoryLock(mDirectoryLock);
  13692 
  13693  if (mDatabaseMaintenances.Count()) {
  13694    mState = State::WaitingForDatabaseMaintenancesToComplete;
  13695  } else {
  13696    mState = State::Finishing;
  13697    Finish();
  13698  }
  13699 
  13700  return NS_OK;
  13701 }
  13702 
  13703 void Maintenance::Finish() {
  13704  AssertIsOnBackgroundThread();
  13705  MOZ_ASSERT(mState == State::Finishing);
  13706 
  13707  if (NS_SUCCEEDED(mResultCode)) {
  13708    mPromiseHolder.ResolveIfExists(true, __func__);
  13709  } else {
  13710    mPromiseHolder.RejectIfExists(mResultCode, __func__);
  13711 
  13712    nsCString errorName;
  13713    GetErrorName(mResultCode, errorName);
  13714 
  13715    IDB_WARNING("Maintenance finished with error: %s", errorName.get());
  13716  }
  13717 
  13718  SafeDropDirectoryLock(mDirectoryLock);
  13719 
  13720  // It can happen that we are only referenced by mCurrentMaintenance which is
  13721  // cleared in NoteFinishedMaintenance()
  13722  const RefPtr<Maintenance> kungFuDeathGrip = this;
  13723 
  13724  mQuotaClient->NoteFinishedMaintenance(this);
  13725 
  13726  mState = State::Complete;
  13727 }
  13728 
  13729 NS_IMETHODIMP
  13730 Maintenance::Run() {
  13731  MOZ_ASSERT(mState != State::Complete);
  13732 
  13733  const auto handleError = [this](const nsresult rv) {
  13734    if (mState != State::Finishing) {
  13735      if (NS_SUCCEEDED(mResultCode)) {
  13736        mResultCode = rv;
  13737      }
  13738 
  13739      // Must set mState before dispatching otherwise we will race with the
  13740      // owning thread.
  13741      mState = State::Finishing;
  13742 
  13743      if (IsOnBackgroundThread()) {
  13744        Finish();
  13745      } else {
  13746        MOZ_ALWAYS_SUCCEEDS(mQuotaClient->BackgroundThread()->Dispatch(
  13747            this, NS_DISPATCH_NORMAL));
  13748      }
  13749    }
  13750  };
  13751 
  13752  switch (mState) {
  13753    case State::Initial:
  13754      QM_TRY(MOZ_TO_RESULT(Start()), NS_OK, handleError);
  13755      break;
  13756 
  13757    case State::CreateIndexedDatabaseManager:
  13758      QM_TRY(MOZ_TO_RESULT(CreateIndexedDatabaseManager()), NS_OK, handleError);
  13759      break;
  13760 
  13761    case State::IndexedDatabaseManagerOpen:
  13762      QM_TRY(MOZ_TO_RESULT(OpenDirectory()), NS_OK, handleError);
  13763      break;
  13764 
  13765    case State::DirectoryWorkOpen:
  13766      QM_TRY(MOZ_TO_RESULT(DirectoryWork()), NS_OK, handleError);
  13767      break;
  13768 
  13769    case State::BeginDatabaseMaintenance:
  13770      QM_TRY(MOZ_TO_RESULT(BeginDatabaseMaintenance()), NS_OK, handleError);
  13771      break;
  13772 
  13773    case State::Finishing:
  13774      Finish();
  13775      break;
  13776 
  13777    default:
  13778      MOZ_CRASH("Bad state!");
  13779  }
  13780 
  13781  return NS_OK;
  13782 }
  13783 
  13784 void Maintenance::DirectoryLockAcquired(UniversalDirectoryLock* aLock) {
  13785  AssertIsOnBackgroundThread();
  13786  MOZ_ASSERT(mState == State::DirectoryOpenPending);
  13787  MOZ_ASSERT(!mDirectoryLock);
  13788 
  13789  mDirectoryLock = std::exchange(mPendingDirectoryLock, nullptr);
  13790 
  13791  nsresult rv = DirectoryOpen();
  13792  if (NS_WARN_IF(NS_FAILED(rv))) {
  13793    if (NS_SUCCEEDED(mResultCode)) {
  13794      mResultCode = rv;
  13795    }
  13796 
  13797    mState = State::Finishing;
  13798    Finish();
  13799 
  13800    return;
  13801  }
  13802 }
  13803 
  13804 void Maintenance::DirectoryLockFailed() {
  13805  AssertIsOnBackgroundThread();
  13806  MOZ_ASSERT(mState == State::DirectoryOpenPending);
  13807  MOZ_ASSERT(!mDirectoryLock);
  13808 
  13809  mPendingDirectoryLock = nullptr;
  13810 
  13811  if (NS_SUCCEEDED(mResultCode)) {
  13812    mResultCode = NS_ERROR_FAILURE;
  13813  }
  13814 
  13815  mState = State::Finishing;
  13816  Finish();
  13817 }
  13818 
  13819 void DatabaseMaintenance::Stringify(nsACString& aResult) const {
  13820  AssertIsOnBackgroundThread();
  13821 
  13822  aResult.AppendLiteral("Origin:");
  13823  aResult.Append(AnonymizedOriginString(mOriginMetadata.mOrigin));
  13824  aResult.Append(kQuotaGenericDelimiter);
  13825 
  13826  aResult.AppendLiteral("PersistenceType:");
  13827  aResult.Append(PersistenceTypeToString(mPersistenceType));
  13828  aResult.Append(kQuotaGenericDelimiter);
  13829 
  13830  aResult.AppendLiteral("Duration:");
  13831  aResult.AppendInt((PR_Now() - mMaintenance->StartTime()) / PR_USEC_PER_MSEC);
  13832 }
  13833 
  13834 nsresult DatabaseMaintenance::Abort() {
  13835  AssertIsOnBackgroundThread();
  13836 
  13837  // StopIdleMaintenance and AbortAllOperations may request abort independently
  13838  if (!mAborted.compareExchange(false, true)) {
  13839    return NS_OK;
  13840  }
  13841 
  13842  {
  13843    auto shardStorageConnectionLocked = mSharedStorageConnection.Lock();
  13844    if (nsCOMPtr<mozIStorageConnection> connection =
  13845            *shardStorageConnectionLocked) {
  13846      QM_TRY(MOZ_TO_RESULT(connection->Interrupt()));
  13847    }
  13848  }
  13849 
  13850  return NS_OK;
  13851 }
  13852 
  13853 void DatabaseMaintenance::PerformMaintenanceOnDatabase() {
  13854  MOZ_ASSERT(!NS_IsMainThread());
  13855  MOZ_ASSERT(!IsOnBackgroundThread());
  13856  MOZ_ASSERT(mMaintenance);
  13857  MOZ_ASSERT(mMaintenance->StartTime());
  13858  MOZ_ASSERT(mDirectoryLock);
  13859  MOZ_ASSERT(!mDatabasePath.IsEmpty());
  13860  MOZ_ASSERT(!mOriginMetadata.mGroup.IsEmpty());
  13861  MOZ_ASSERT(!mOriginMetadata.mOrigin.IsEmpty());
  13862 
  13863  if (NS_WARN_IF(IsAborted())) {
  13864    return;
  13865  }
  13866 
  13867  const nsCOMPtr<nsIFile> databaseFile = GetFileForPath(mDatabasePath);
  13868  MOZ_ASSERT(databaseFile);
  13869 
  13870  QM_TRY_UNWRAP(
  13871      const NotNull<nsCOMPtr<mozIStorageConnection>> connection,
  13872      GetStorageConnection(*databaseFile, mDirectoryLockId,
  13873                           TelemetryIdForFile(databaseFile), mMaybeKey),
  13874      QM_VOID);
  13875 
  13876  auto autoClearConnection = MakeScopeExit([&]() {
  13877    auto sharedStorageConnectionLocked = mSharedStorageConnection.Lock();
  13878    sharedStorageConnectionLocked.ref() = nullptr;
  13879    connection->Close();
  13880  });
  13881 
  13882  {
  13883    auto sharedStorageConnectionLocked = mSharedStorageConnection.Lock();
  13884    sharedStorageConnectionLocked.ref() = connection;
  13885  }
  13886 
  13887  auto databaseIsOk = false;
  13888  QM_TRY(MOZ_TO_RESULT(CheckIntegrity(*connection, &databaseIsOk)), QM_VOID);
  13889 
  13890  QM_TRY(OkIf(databaseIsOk), QM_VOID, [](auto result) {
  13891    // XXX Handle this somehow! Probably need to clear all storage for the
  13892    //     origin. See Bug 1760612.
  13893    MOZ_ASSERT(false, "Database corruption detected!");
  13894  });
  13895 
  13896  MaintenanceAction maintenanceAction;
  13897  QM_TRY(MOZ_TO_RESULT(DetermineMaintenanceAction(*connection, databaseFile,
  13898                                                  &maintenanceAction)),
  13899         QM_VOID);
  13900 
  13901  switch (maintenanceAction) {
  13902    case MaintenanceAction::Nothing:
  13903      break;
  13904 
  13905    case MaintenanceAction::IncrementalVacuum:
  13906      IncrementalVacuum(*connection);
  13907      break;
  13908 
  13909    case MaintenanceAction::FullVacuum:
  13910      FullVacuum(*connection, databaseFile);
  13911      break;
  13912 
  13913    default:
  13914      MOZ_CRASH("Unknown MaintenanceAction!");
  13915  }
  13916 }
  13917 
  13918 nsresult DatabaseMaintenance::CheckIntegrity(mozIStorageConnection& aConnection,
  13919                                             bool* aOk) {
  13920  MOZ_ASSERT(!NS_IsMainThread());
  13921  MOZ_ASSERT(!IsOnBackgroundThread());
  13922  MOZ_ASSERT(aOk);
  13923 
  13924  if (NS_WARN_IF(IsAborted())) {
  13925    return NS_ERROR_ABORT;
  13926  }
  13927 
  13928  // First do a full integrity_check. Scope statements tightly here because
  13929  // later operations require zero live statements.
  13930  {
  13931    QM_TRY_INSPECT(const auto& stmt,
  13932                   CreateAndExecuteSingleStepStatement(
  13933                       aConnection, "PRAGMA integrity_check(1);"_ns));
  13934 
  13935    QM_TRY_INSPECT(const auto& result, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
  13936                                           nsString, *stmt, GetString, 0));
  13937 
  13938    QM_TRY(OkIf(result.EqualsLiteral("ok")), NS_OK,
  13939           [&aOk](const auto) { *aOk = false; });
  13940  }
  13941 
  13942  // Now enable and check for foreign key constraints.
  13943  {
  13944    QM_TRY_INSPECT(
  13945        const int32_t& foreignKeysWereEnabled,
  13946        ([&aConnection]() -> Result<int32_t, nsresult> {
  13947          QM_TRY_INSPECT(const auto& stmt,
  13948                         CreateAndExecuteSingleStepStatement(
  13949                             aConnection, "PRAGMA foreign_keys;"_ns));
  13950 
  13951          QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_MEMBER(*stmt, GetInt32, 0));
  13952        }()));
  13953 
  13954    if (!foreignKeysWereEnabled) {
  13955      QM_TRY(MOZ_TO_RESULT(
  13956          aConnection.ExecuteSimpleSQL("PRAGMA foreign_keys = ON;"_ns)));
  13957    }
  13958 
  13959    QM_TRY_INSPECT(const bool& foreignKeyError,
  13960                   CreateAndExecuteSingleStepStatement<
  13961                       SingleStepResult::ReturnNullIfNoResult>(
  13962                       aConnection, "PRAGMA foreign_key_check;"_ns));
  13963 
  13964    if (!foreignKeysWereEnabled) {
  13965      QM_TRY(MOZ_TO_RESULT(
  13966          aConnection.ExecuteSimpleSQL("PRAGMA foreign_keys = OFF;"_ns)));
  13967    }
  13968 
  13969    if (foreignKeyError) {
  13970      *aOk = false;
  13971      return NS_OK;
  13972    }
  13973  }
  13974 
  13975  *aOk = true;
  13976  return NS_OK;
  13977 }
  13978 
  13979 nsresult DatabaseMaintenance::DetermineMaintenanceAction(
  13980    mozIStorageConnection& aConnection, nsIFile* aDatabaseFile,
  13981    MaintenanceAction* aMaintenanceAction) {
  13982  MOZ_ASSERT(!NS_IsMainThread());
  13983  MOZ_ASSERT(!IsOnBackgroundThread());
  13984  MOZ_ASSERT(aDatabaseFile);
  13985  MOZ_ASSERT(aMaintenanceAction);
  13986 
  13987  if (NS_WARN_IF(IsAborted())) {
  13988    return NS_ERROR_ABORT;
  13989  }
  13990 
  13991  QM_TRY_INSPECT(const int32_t& schemaVersion,
  13992                 MOZ_TO_RESULT_INVOKE_MEMBER(aConnection, GetSchemaVersion));
  13993 
  13994  // Don't do anything if the schema version is less than 18; before that
  13995  // version no databases had |auto_vacuum == INCREMENTAL| set and we didn't
  13996  // track the values needed for the heuristics below.
  13997  if (schemaVersion < MakeSchemaVersion(18, 0)) {
  13998    *aMaintenanceAction = MaintenanceAction::Nothing;
  13999    return NS_OK;
  14000  }
  14001 
  14002  // This method shouldn't make any permanent changes to the database, so make
  14003  // sure everything gets rolled back when we leave.
  14004  mozStorageTransaction transaction(&aConnection,
  14005                                    /* aCommitOnComplete */ false);
  14006 
  14007  QM_TRY(MOZ_TO_RESULT(transaction.Start()))
  14008 
  14009  // Check to see when we last vacuumed this database.
  14010  QM_TRY_INSPECT(const auto& stmt,
  14011                 CreateAndExecuteSingleStepStatement(
  14012                     aConnection,
  14013                     "SELECT last_vacuum_time, last_vacuum_size "
  14014                     "FROM database;"_ns));
  14015 
  14016  QM_TRY_INSPECT(const PRTime& lastVacuumTime,
  14017                 MOZ_TO_RESULT_INVOKE_MEMBER(*stmt, GetInt64, 0));
  14018 
  14019  QM_TRY_INSPECT(const int64_t& lastVacuumSize,
  14020                 MOZ_TO_RESULT_INVOKE_MEMBER(*stmt, GetInt64, 1));
  14021 
  14022  NS_ASSERTION(lastVacuumSize > 0,
  14023               "Thy last vacuum size shall be greater than zero, less than "
  14024               "zero shall thy last vacuum size not be. Zero is right out.");
  14025 
  14026  const PRTime startTime = mMaintenance->StartTime();
  14027 
  14028  // This shouldn't really be possible...
  14029  if (NS_WARN_IF(startTime <= lastVacuumTime)) {
  14030    *aMaintenanceAction = MaintenanceAction::Nothing;
  14031    return NS_OK;
  14032  }
  14033 
  14034  if (startTime - lastVacuumTime < kMinVacuumAge) {
  14035    *aMaintenanceAction = MaintenanceAction::IncrementalVacuum;
  14036    return NS_OK;
  14037  }
  14038 
  14039  // It has been more than a week since the database was vacuumed, so gather
  14040  // statistics on its usage to see if vacuuming is worthwhile.
  14041 
  14042  // Create a temporary copy of the dbstat table to speed up the queries that
  14043  // come later.
  14044  QM_TRY(MOZ_TO_RESULT(aConnection.ExecuteSimpleSQL(
  14045      "CREATE VIRTUAL TABLE __stats__ USING dbstat;"
  14046      "CREATE TEMP TABLE __temp_stats__ AS SELECT * FROM __stats__;"_ns)));
  14047 
  14048  {  // Calculate the percentage of the database pages that are not in
  14049     // contiguous order.
  14050    QM_TRY_INSPECT(
  14051        const auto& stmt,
  14052        CreateAndExecuteSingleStepStatement(
  14053            aConnection,
  14054            "SELECT SUM(__ts1__.pageno != __ts2__.pageno + 1) * 100.0 / "
  14055            "COUNT(*) "
  14056            "FROM __temp_stats__ AS __ts1__, __temp_stats__ AS __ts2__ "
  14057            "WHERE __ts1__.name = __ts2__.name "
  14058            "AND __ts1__.rowid = __ts2__.rowid + 1;"_ns));
  14059 
  14060    QM_TRY_INSPECT(const int32_t& percentUnordered,
  14061                   MOZ_TO_RESULT_INVOKE_MEMBER(*stmt, GetInt32, 0));
  14062 
  14063    MOZ_ASSERT(percentUnordered >= 0);
  14064    MOZ_ASSERT(percentUnordered <= 100);
  14065 
  14066    if (percentUnordered >= kPercentUnorderedThreshold) {
  14067      *aMaintenanceAction = MaintenanceAction::FullVacuum;
  14068      return NS_OK;
  14069    }
  14070  }
  14071 
  14072  // Don't try a full vacuum if the file hasn't grown by 10%.
  14073  QM_TRY_INSPECT(const int64_t& currentFileSize,
  14074                 MOZ_TO_RESULT_INVOKE_MEMBER(aDatabaseFile, GetFileSize));
  14075 
  14076  if (currentFileSize <= lastVacuumSize ||
  14077      (((currentFileSize - lastVacuumSize) * 100 / currentFileSize) <
  14078       kPercentFileSizeGrowthThreshold)) {
  14079    *aMaintenanceAction = MaintenanceAction::IncrementalVacuum;
  14080    return NS_OK;
  14081  }
  14082 
  14083  {  // See if there are any free pages that we can reclaim.
  14084    QM_TRY_INSPECT(const auto& stmt,
  14085                   CreateAndExecuteSingleStepStatement(
  14086                       aConnection, "PRAGMA freelist_count;"_ns));
  14087 
  14088    QM_TRY_INSPECT(const int32_t& freelistCount,
  14089                   MOZ_TO_RESULT_INVOKE_MEMBER(*stmt, GetInt32, 0));
  14090 
  14091    MOZ_ASSERT(freelistCount >= 0);
  14092 
  14093    // If we have too many free pages then we should try an incremental
  14094    // vacuum. If that causes too much fragmentation then we'll try a full
  14095    // vacuum later.
  14096    if (freelistCount > kMaxFreelistThreshold) {
  14097      *aMaintenanceAction = MaintenanceAction::IncrementalVacuum;
  14098      return NS_OK;
  14099    }
  14100  }
  14101 
  14102  {  // Calculate the percentage of unused bytes on pages in the database.
  14103    QM_TRY_INSPECT(
  14104        const auto& stmt,
  14105        CreateAndExecuteSingleStepStatement(
  14106            aConnection,
  14107            "SELECT SUM(unused) * 100.0 / SUM(pgsize) FROM __temp_stats__;"_ns));
  14108 
  14109    QM_TRY_INSPECT(const int32_t& percentUnused,
  14110                   MOZ_TO_RESULT_INVOKE_MEMBER(*stmt, GetInt32, 0));
  14111 
  14112    MOZ_ASSERT(percentUnused >= 0);
  14113    MOZ_ASSERT(percentUnused <= 100);
  14114 
  14115    *aMaintenanceAction = percentUnused >= kPercentUnusedThreshold
  14116                              ? MaintenanceAction::FullVacuum
  14117                              : MaintenanceAction::IncrementalVacuum;
  14118  }
  14119 
  14120  return NS_OK;
  14121 }
  14122 
  14123 void DatabaseMaintenance::IncrementalVacuum(
  14124    mozIStorageConnection& aConnection) {
  14125  MOZ_ASSERT(!NS_IsMainThread());
  14126  MOZ_ASSERT(!IsOnBackgroundThread());
  14127 
  14128  if (NS_WARN_IF(IsAborted())) {
  14129    return;
  14130  }
  14131 
  14132  nsresult rv = aConnection.ExecuteSimpleSQL("PRAGMA incremental_vacuum;"_ns);
  14133  if (NS_WARN_IF(NS_FAILED(rv))) {
  14134    return;
  14135  }
  14136 }
  14137 
  14138 void DatabaseMaintenance::FullVacuum(mozIStorageConnection& aConnection,
  14139                                     nsIFile* aDatabaseFile) {
  14140  MOZ_ASSERT(!NS_IsMainThread());
  14141  MOZ_ASSERT(!IsOnBackgroundThread());
  14142  MOZ_ASSERT(aDatabaseFile);
  14143 
  14144  if (NS_WARN_IF(IsAborted())) {
  14145    return;
  14146  }
  14147 
  14148  QM_WARNONLY_TRY(([&]() -> Result<Ok, nsresult> {
  14149    QM_TRY(MOZ_TO_RESULT(aConnection.ExecuteSimpleSQL("VACUUM;"_ns)));
  14150 
  14151    const PRTime vacuumTime = PR_Now();
  14152    MOZ_ASSERT(vacuumTime > 0);
  14153 
  14154    QM_TRY_INSPECT(const int64_t& fileSize,
  14155                   MOZ_TO_RESULT_INVOKE_MEMBER(aDatabaseFile, GetFileSize));
  14156 
  14157    MOZ_ASSERT(fileSize > 0);
  14158 
  14159    // The parameter names are not used, parameters are bound by index only
  14160    // locally in the same function.
  14161    QM_TRY_INSPECT(const auto& stmt, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
  14162                                         nsCOMPtr<mozIStorageStatement>,
  14163                                         aConnection, CreateStatement,
  14164                                         "UPDATE database "
  14165                                         "SET last_vacuum_time = :time"
  14166                                         ", last_vacuum_size = :size;"_ns));
  14167 
  14168    QM_TRY(MOZ_TO_RESULT(stmt->BindInt64ByIndex(0, vacuumTime)));
  14169 
  14170    QM_TRY(MOZ_TO_RESULT(stmt->BindInt64ByIndex(1, fileSize)));
  14171 
  14172    QM_TRY(MOZ_TO_RESULT(stmt->Execute()));
  14173    return Ok{};
  14174  }()));
  14175 }
  14176 
  14177 void DatabaseMaintenance::RunOnOwningThread() {
  14178  AssertIsOnBackgroundThread();
  14179 
  14180  DropDirectoryLock(mDirectoryLock);
  14181 
  14182  if (mCompleteCallback) {
  14183    MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(mCompleteCallback.forget()));
  14184  }
  14185 
  14186  mMaintenance->UnregisterDatabaseMaintenance(this);
  14187 }
  14188 
  14189 void DatabaseMaintenance::RunOnConnectionThread() {
  14190  MOZ_ASSERT(!NS_IsMainThread());
  14191  MOZ_ASSERT(!IsOnBackgroundThread());
  14192 
  14193  PerformMaintenanceOnDatabase();
  14194 
  14195  MOZ_ALWAYS_SUCCEEDS(
  14196      mMaintenance->BackgroundThread()->Dispatch(this, NS_DISPATCH_NORMAL));
  14197 }
  14198 
  14199 NS_IMETHODIMP
  14200 DatabaseMaintenance::Run() {
  14201  if (IsOnBackgroundThread()) {
  14202    RunOnOwningThread();
  14203  } else {
  14204    RunOnConnectionThread();
  14205  }
  14206 
  14207  return NS_OK;
  14208 }
  14209 
  14210 /*******************************************************************************
  14211 * Local class implementations
  14212 ******************************************************************************/
  14213 
  14214 // static
  14215 nsAutoCString DatabaseOperationBase::MaybeGetBindingClauseForKeyRange(
  14216    const Maybe<SerializedKeyRange>& aOptionalKeyRange,
  14217    const nsACString& aKeyColumnName) {
  14218  return aOptionalKeyRange.isSome()
  14219             ? GetBindingClauseForKeyRange(aOptionalKeyRange.ref(),
  14220                                           aKeyColumnName)
  14221             : nsAutoCString{};
  14222 }
  14223 
  14224 // static
  14225 nsAutoCString DatabaseOperationBase::GetBindingClauseForKeyRange(
  14226    const SerializedKeyRange& aKeyRange, const nsACString& aKeyColumnName) {
  14227  MOZ_ASSERT(!IsOnBackgroundThread());
  14228  MOZ_ASSERT(!aKeyColumnName.IsEmpty());
  14229 
  14230  constexpr auto andStr = " AND "_ns;
  14231  constexpr auto spacecolon = " :"_ns;
  14232 
  14233  nsAutoCString result;
  14234  if (aKeyRange.isOnly()) {
  14235    // Both keys equal.
  14236    result =
  14237        andStr + aKeyColumnName + " ="_ns + spacecolon + kStmtParamNameLowerKey;
  14238  } else {
  14239    if (!aKeyRange.lower().IsUnset()) {
  14240      // Lower key is set.
  14241      result.Append(andStr + aKeyColumnName);
  14242      result.AppendLiteral(" >");
  14243      if (!aKeyRange.lowerOpen()) {
  14244        result.AppendLiteral("=");
  14245      }
  14246      result.Append(spacecolon + kStmtParamNameLowerKey);
  14247    }
  14248 
  14249    if (!aKeyRange.upper().IsUnset()) {
  14250      // Upper key is set.
  14251      result.Append(andStr + aKeyColumnName);
  14252      result.AppendLiteral(" <");
  14253      if (!aKeyRange.upperOpen()) {
  14254        result.AppendLiteral("=");
  14255      }
  14256      result.Append(spacecolon + kStmtParamNameUpperKey);
  14257    }
  14258  }
  14259 
  14260  MOZ_ASSERT(!result.IsEmpty());
  14261 
  14262  return result;
  14263 }
  14264 
  14265 // static
  14266 uint64_t DatabaseOperationBase::ReinterpretDoubleAsUInt64(double aDouble) {
  14267  // This is a duplicate of the js engine's byte munging in StructuredClone.cpp
  14268  return BitwiseCast<uint64_t>(aDouble);
  14269 }
  14270 
  14271 // static
  14272 template <typename KeyTransformation>
  14273 nsresult DatabaseOperationBase::MaybeBindKeyToStatement(
  14274    const Key& aKey, mozIStorageStatement* const aStatement,
  14275    const nsACString& aParameterName,
  14276    const KeyTransformation& aKeyTransformation) {
  14277  MOZ_ASSERT(!IsOnBackgroundThread());
  14278  MOZ_ASSERT(aStatement);
  14279 
  14280  if (!aKey.IsUnset()) {
  14281    // XXX This case distinction could be avoided if QM_TRY_INSPECT would also
  14282    // work with a function not returning a Result<V, E> but simply a V (which
  14283    // is const Key& here) and then assuming it is always a success. Or the
  14284    // transformation could be changed to return Result<const V&, void> but I
  14285    // don't think that Result supports that at the moment.
  14286    if constexpr (std::is_reference_v<
  14287                      std::invoke_result_t<KeyTransformation, Key>>) {
  14288      QM_TRY(MOZ_TO_RESULT(aKeyTransformation(aKey).BindToStatement(
  14289          aStatement, aParameterName)));
  14290    } else {
  14291      QM_TRY_INSPECT(const auto& transformedKey, aKeyTransformation(aKey));
  14292      QM_TRY(MOZ_TO_RESULT(
  14293          transformedKey.BindToStatement(aStatement, aParameterName)));
  14294    }
  14295  }
  14296 
  14297  return NS_OK;
  14298 }
  14299 
  14300 // static
  14301 template <typename KeyTransformation>
  14302 nsresult DatabaseOperationBase::BindTransformedKeyRangeToStatement(
  14303    const SerializedKeyRange& aKeyRange, mozIStorageStatement* const aStatement,
  14304    const KeyTransformation& aKeyTransformation) {
  14305  MOZ_ASSERT(!IsOnBackgroundThread());
  14306  MOZ_ASSERT(aStatement);
  14307 
  14308  QM_TRY(MOZ_TO_RESULT(MaybeBindKeyToStatement(aKeyRange.lower(), aStatement,
  14309                                               kStmtParamNameLowerKey,
  14310                                               aKeyTransformation)));
  14311 
  14312  if (aKeyRange.isOnly()) {
  14313    return NS_OK;
  14314  }
  14315 
  14316  QM_TRY(MOZ_TO_RESULT(MaybeBindKeyToStatement(aKeyRange.upper(), aStatement,
  14317                                               kStmtParamNameUpperKey,
  14318                                               aKeyTransformation)));
  14319 
  14320  return NS_OK;
  14321 }
  14322 
  14323 // static
  14324 nsresult DatabaseOperationBase::BindKeyRangeToStatement(
  14325    const SerializedKeyRange& aKeyRange,
  14326    mozIStorageStatement* const aStatement) {
  14327  return BindTransformedKeyRangeToStatement(
  14328      aKeyRange, aStatement, [](const Key& key) -> const auto& { return key; });
  14329 }
  14330 
  14331 // static
  14332 nsresult DatabaseOperationBase::BindKeyRangeToStatement(
  14333    const SerializedKeyRange& aKeyRange, mozIStorageStatement* const aStatement,
  14334    const nsCString& aLocale) {
  14335  MOZ_ASSERT(!aLocale.IsEmpty());
  14336 
  14337  return BindTransformedKeyRangeToStatement(
  14338      aKeyRange, aStatement,
  14339      [&aLocale](const Key& key) { return key.ToLocaleAwareKey(aLocale); });
  14340 }
  14341 
  14342 // static
  14343 void CommonOpenOpHelperBase::AppendConditionClause(
  14344    const nsACString& aColumnName, const nsACString& aStatementParameterName,
  14345    bool aLessThan, bool aEquals, nsCString& aResult) {
  14346  aResult += " AND "_ns + aColumnName + " "_ns;
  14347 
  14348  if (aLessThan) {
  14349    aResult.Append('<');
  14350  } else {
  14351    aResult.Append('>');
  14352  }
  14353 
  14354  if (aEquals) {
  14355    aResult.Append('=');
  14356  }
  14357 
  14358  aResult += " :"_ns + aStatementParameterName;
  14359 }
  14360 
  14361 // static
  14362 Result<IndexDataValuesAutoArray, nsresult>
  14363 DatabaseOperationBase::IndexDataValuesFromUpdateInfos(
  14364    const nsTArray<IndexUpdateInfo>& aUpdateInfos,
  14365    const UniqueIndexTable& aUniqueIndexTable) {
  14366  MOZ_ASSERT_IF(!aUpdateInfos.IsEmpty(), aUniqueIndexTable.Count());
  14367 
  14368  AUTO_PROFILER_LABEL("DatabaseOperationBase::IndexDataValuesFromUpdateInfos",
  14369                      DOM);
  14370 
  14371  // XXX We could use TransformIntoNewArray here if it allowed to specify that
  14372  // an AutoArray should be created.
  14373  IndexDataValuesAutoArray indexValues;
  14374 
  14375  if (NS_WARN_IF(!indexValues.SetCapacity(aUpdateInfos.Length(), fallible))) {
  14376    IDB_REPORT_INTERNAL_ERR();
  14377    return Err(NS_ERROR_OUT_OF_MEMORY);
  14378  }
  14379 
  14380  std::transform(aUpdateInfos.cbegin(), aUpdateInfos.cend(),
  14381                 MakeBackInserter(indexValues),
  14382                 [&aUniqueIndexTable](const IndexUpdateInfo& updateInfo) {
  14383                   const IndexOrObjectStoreId& indexId = updateInfo.indexId();
  14384 
  14385                   bool unique = false;
  14386                   MOZ_ALWAYS_TRUE(aUniqueIndexTable.Get(indexId, &unique));
  14387 
  14388                   return IndexDataValue{indexId, unique, updateInfo.value(),
  14389                                         updateInfo.localizedValue()};
  14390                 });
  14391  indexValues.Sort();
  14392 
  14393  return indexValues;
  14394 }
  14395 
  14396 // static
  14397 nsresult DatabaseOperationBase::InsertIndexTableRows(
  14398    DatabaseConnection* aConnection, const IndexOrObjectStoreId aObjectStoreId,
  14399    const Key& aObjectStoreKey, const nsTArray<IndexDataValue>& aIndexValues) {
  14400  MOZ_ASSERT(aConnection);
  14401  aConnection->AssertIsOnConnectionThread();
  14402  MOZ_ASSERT(!aObjectStoreKey.IsUnset());
  14403 
  14404  AUTO_PROFILER_LABEL("DatabaseOperationBase::InsertIndexTableRows", DOM);
  14405 
  14406  const uint32_t count = aIndexValues.Length();
  14407  if (!count) {
  14408    return NS_OK;
  14409  }
  14410 
  14411  auto insertUniqueStmt = DatabaseConnection::LazyStatement{
  14412      *aConnection,
  14413      "INSERT INTO unique_index_data "
  14414      "(index_id, value, object_store_id, "
  14415      "object_data_key, value_locale) "
  14416      "VALUES (:"_ns +
  14417          kStmtParamNameIndexId + ", :"_ns + kStmtParamNameValue + ", :"_ns +
  14418          kStmtParamNameObjectStoreId + ", :"_ns + kStmtParamNameObjectDataKey +
  14419          ", :"_ns + kStmtParamNameValueLocale + ");"_ns};
  14420  auto insertStmt = DatabaseConnection::LazyStatement{
  14421      *aConnection,
  14422      "INSERT OR IGNORE INTO index_data "
  14423      "(index_id, value, object_data_key, "
  14424      "object_store_id, value_locale) "
  14425      "VALUES (:"_ns +
  14426          kStmtParamNameIndexId + ", :"_ns + kStmtParamNameValue + ", :"_ns +
  14427          kStmtParamNameObjectDataKey + ", :"_ns + kStmtParamNameObjectStoreId +
  14428          ", :"_ns + kStmtParamNameValueLocale + ");"_ns};
  14429 
  14430  for (uint32_t index = 0; index < count; index++) {
  14431    const IndexDataValue& info = aIndexValues[index];
  14432 
  14433    auto& stmt = info.mUnique ? insertUniqueStmt : insertStmt;
  14434 
  14435    QM_TRY_INSPECT(const auto& borrowedStmt, stmt.Borrow());
  14436 
  14437    QM_TRY(MOZ_TO_RESULT(
  14438        borrowedStmt->BindInt64ByName(kStmtParamNameIndexId, info.mIndexId)));
  14439    QM_TRY(MOZ_TO_RESULT(
  14440        info.mPosition.BindToStatement(&*borrowedStmt, kStmtParamNameValue)));
  14441    QM_TRY(MOZ_TO_RESULT(info.mLocaleAwarePosition.BindToStatement(
  14442        &*borrowedStmt, kStmtParamNameValueLocale)));
  14443    QM_TRY(MOZ_TO_RESULT(borrowedStmt->BindInt64ByName(
  14444        kStmtParamNameObjectStoreId, aObjectStoreId)));
  14445    QM_TRY(MOZ_TO_RESULT(aObjectStoreKey.BindToStatement(
  14446        &*borrowedStmt, kStmtParamNameObjectDataKey)));
  14447 
  14448    // QM_OR_ELSE_WARN_IF is not used here since we just want to log the
  14449    // collision and not spam the reports.
  14450    QM_TRY(QM_OR_ELSE_LOG_VERBOSE_IF(
  14451        // Expression.
  14452        MOZ_TO_RESULT(borrowedStmt->Execute()),
  14453        // Predicate.
  14454        ([&info, index, &aIndexValues](nsresult rv) {
  14455          if (rv == NS_ERROR_STORAGE_CONSTRAINT && info.mUnique) {
  14456            // If we're inserting multiple entries for the same unique
  14457            // index, then we might have failed to insert due to
  14458            // colliding with another entry for the same index in which
  14459            // case we should ignore it.
  14460            for (int32_t index2 = int32_t(index) - 1;
  14461                 index2 >= 0 && aIndexValues[index2].mIndexId == info.mIndexId;
  14462                 --index2) {
  14463              if (info.mPosition == aIndexValues[index2].mPosition) {
  14464                // We found a key with the same value for the same
  14465                // index. So we must have had a collision with a value
  14466                // we just inserted.
  14467                return true;
  14468              }
  14469            }
  14470          }
  14471 
  14472          return false;
  14473        }),
  14474        // Fallback.
  14475        ErrToDefaultOk<>));
  14476  }
  14477 
  14478  return NS_OK;
  14479 }
  14480 
  14481 // static
  14482 nsresult DatabaseOperationBase::DeleteIndexDataTableRows(
  14483    DatabaseConnection* aConnection, const Key& aObjectStoreKey,
  14484    const nsTArray<IndexDataValue>& aIndexValues) {
  14485  MOZ_ASSERT(aConnection);
  14486  aConnection->AssertIsOnConnectionThread();
  14487  MOZ_ASSERT(!aObjectStoreKey.IsUnset());
  14488 
  14489  AUTO_PROFILER_LABEL("DatabaseOperationBase::DeleteIndexDataTableRows", DOM);
  14490 
  14491  const uint32_t count = aIndexValues.Length();
  14492  if (!count) {
  14493    return NS_OK;
  14494  }
  14495 
  14496  auto deleteUniqueStmt = DatabaseConnection::LazyStatement{
  14497      *aConnection, "DELETE FROM unique_index_data WHERE index_id = :"_ns +
  14498                        kStmtParamNameIndexId + " AND value = :"_ns +
  14499                        kStmtParamNameValue + ";"_ns};
  14500  auto deleteStmt = DatabaseConnection::LazyStatement{
  14501      *aConnection, "DELETE FROM index_data WHERE index_id = :"_ns +
  14502                        kStmtParamNameIndexId + " AND value = :"_ns +
  14503                        kStmtParamNameValue + " AND object_data_key = :"_ns +
  14504                        kStmtParamNameObjectDataKey + ";"_ns};
  14505 
  14506  for (uint32_t index = 0; index < count; index++) {
  14507    const IndexDataValue& indexValue = aIndexValues[index];
  14508 
  14509    auto& stmt = indexValue.mUnique ? deleteUniqueStmt : deleteStmt;
  14510 
  14511    QM_TRY_INSPECT(const auto& borrowedStmt, stmt.Borrow());
  14512 
  14513    QM_TRY(MOZ_TO_RESULT(borrowedStmt->BindInt64ByName(kStmtParamNameIndexId,
  14514                                                       indexValue.mIndexId)));
  14515 
  14516    QM_TRY(MOZ_TO_RESULT(indexValue.mPosition.BindToStatement(
  14517        &*borrowedStmt, kStmtParamNameValue)));
  14518 
  14519    if (!indexValue.mUnique) {
  14520      QM_TRY(MOZ_TO_RESULT(aObjectStoreKey.BindToStatement(
  14521          &*borrowedStmt, kStmtParamNameObjectDataKey)));
  14522    }
  14523 
  14524    QM_TRY(MOZ_TO_RESULT(borrowedStmt->Execute()));
  14525  }
  14526 
  14527  return NS_OK;
  14528 }
  14529 
  14530 // static
  14531 nsresult DatabaseOperationBase::DeleteObjectStoreDataTableRowsWithIndexes(
  14532    DatabaseConnection* aConnection, const IndexOrObjectStoreId aObjectStoreId,
  14533    const Maybe<SerializedKeyRange>& aKeyRange) {
  14534  MOZ_ASSERT(aConnection);
  14535  aConnection->AssertIsOnConnectionThread();
  14536  MOZ_ASSERT(aObjectStoreId);
  14537 
  14538 #ifdef DEBUG
  14539  {
  14540    QM_TRY_INSPECT(const bool& hasIndexes,
  14541                   ObjectStoreHasIndexes(*aConnection, aObjectStoreId),
  14542                   QM_PROPAGATE, [](const auto&) { MOZ_ASSERT(false); });
  14543    MOZ_ASSERT(hasIndexes,
  14544               "Don't use this slow method if there are no indexes!");
  14545  }
  14546 #endif
  14547 
  14548  AUTO_PROFILER_LABEL(
  14549      "DatabaseOperationBase::DeleteObjectStoreDataTableRowsWithIndexes", DOM);
  14550 
  14551  const bool singleRowOnly = aKeyRange.isSome() && aKeyRange.ref().isOnly();
  14552 
  14553  const auto keyRangeClause =
  14554      MaybeGetBindingClauseForKeyRange(aKeyRange, kColumnNameKey);
  14555 
  14556  Key objectStoreKey;
  14557  QM_TRY_INSPECT(
  14558      const auto& selectStmt,
  14559      ([singleRowOnly, &aConnection, &objectStoreKey, &aKeyRange,
  14560        &keyRangeClause]()
  14561           -> Result<CachingDatabaseConnection::BorrowedStatement, nsresult> {
  14562        if (singleRowOnly) {
  14563          QM_TRY_UNWRAP(auto selectStmt,
  14564                        aConnection->BorrowCachedStatement(
  14565                            "SELECT index_data_values "
  14566                            "FROM object_data "
  14567                            "WHERE object_store_id = :"_ns +
  14568                            kStmtParamNameObjectStoreId + " AND key = :"_ns +
  14569                            kStmtParamNameKey + ";"_ns));
  14570 
  14571          objectStoreKey = aKeyRange.ref().lower();
  14572 
  14573          QM_TRY(MOZ_TO_RESULT(
  14574              objectStoreKey.BindToStatement(&*selectStmt, kStmtParamNameKey)));
  14575 
  14576          return selectStmt;
  14577        }
  14578 
  14579        QM_TRY_UNWRAP(
  14580            auto selectStmt,
  14581            aConnection->BorrowCachedStatement(
  14582                "SELECT index_data_values, "_ns + kColumnNameKey +
  14583                " FROM object_data WHERE object_store_id = :"_ns +
  14584                kStmtParamNameObjectStoreId + keyRangeClause + ";"_ns));
  14585 
  14586        if (aKeyRange.isSome()) {
  14587          QM_TRY(MOZ_TO_RESULT(
  14588              BindKeyRangeToStatement(aKeyRange.ref(), &*selectStmt)));
  14589        }
  14590 
  14591        return selectStmt;
  14592      }()));
  14593 
  14594  QM_TRY(MOZ_TO_RESULT(selectStmt->BindInt64ByName(kStmtParamNameObjectStoreId,
  14595                                                   aObjectStoreId)));
  14596 
  14597  DebugOnly<uint32_t> resultCountDEBUG = 0;
  14598 
  14599  QM_TRY(CollectWhileHasResult(
  14600      *selectStmt,
  14601      [singleRowOnly, &objectStoreKey, &aConnection, &resultCountDEBUG,
  14602       indexValues = IndexDataValuesAutoArray{}](
  14603          auto& selectStmt) mutable -> Result<Ok, nsresult> {
  14604        if (!singleRowOnly) {
  14605          QM_TRY(
  14606              MOZ_TO_RESULT(objectStoreKey.SetFromStatement(&selectStmt, 1)));
  14607 
  14608          indexValues.ClearAndRetainStorage();
  14609        }
  14610 
  14611        QM_TRY(MOZ_TO_RESULT(
  14612            ReadCompressedIndexDataValues(selectStmt, 0, indexValues)));
  14613        QM_TRY(MOZ_TO_RESULT(DeleteIndexDataTableRows(
  14614            aConnection, objectStoreKey, indexValues)));
  14615 
  14616        resultCountDEBUG++;
  14617 
  14618        return Ok{};
  14619      }));
  14620 
  14621  MOZ_ASSERT_IF(singleRowOnly, resultCountDEBUG <= 1);
  14622 
  14623  QM_TRY_UNWRAP(
  14624      auto deleteManyStmt,
  14625      aConnection->BorrowCachedStatement(
  14626          "DELETE FROM object_data "_ns + "WHERE object_store_id = :"_ns +
  14627          kStmtParamNameObjectStoreId + keyRangeClause + ";"_ns));
  14628 
  14629  QM_TRY(MOZ_TO_RESULT(deleteManyStmt->BindInt64ByName(
  14630      kStmtParamNameObjectStoreId, aObjectStoreId)));
  14631 
  14632  if (aKeyRange.isSome()) {
  14633    QM_TRY(MOZ_TO_RESULT(
  14634        BindKeyRangeToStatement(aKeyRange.ref(), &*deleteManyStmt)));
  14635  }
  14636 
  14637  QM_TRY(MOZ_TO_RESULT(deleteManyStmt->Execute()));
  14638 
  14639  return NS_OK;
  14640 }
  14641 
  14642 // static
  14643 nsresult DatabaseOperationBase::UpdateIndexValues(
  14644    DatabaseConnection* aConnection, const IndexOrObjectStoreId aObjectStoreId,
  14645    const Key& aObjectStoreKey, const nsTArray<IndexDataValue>& aIndexValues) {
  14646  MOZ_ASSERT(aConnection);
  14647  aConnection->AssertIsOnConnectionThread();
  14648  MOZ_ASSERT(!aObjectStoreKey.IsUnset());
  14649 
  14650  AUTO_PROFILER_LABEL("DatabaseOperationBase::UpdateIndexValues", DOM);
  14651 
  14652  QM_TRY_UNWRAP((auto [indexDataValues, indexDataValuesLength]),
  14653                MakeCompressedIndexDataValues(aIndexValues));
  14654 
  14655  MOZ_ASSERT(!indexDataValuesLength == !(indexDataValues.get()));
  14656 
  14657  QM_TRY(MOZ_TO_RESULT(aConnection->ExecuteCachedStatement(
  14658      "UPDATE object_data SET index_data_values = :"_ns +
  14659          kStmtParamNameIndexDataValues + " WHERE object_store_id = :"_ns +
  14660          kStmtParamNameObjectStoreId + " AND key = :"_ns + kStmtParamNameKey +
  14661          ";"_ns,
  14662      [&indexDataValues = indexDataValues,
  14663       indexDataValuesLength = indexDataValuesLength, aObjectStoreId,
  14664       &aObjectStoreKey](
  14665          mozIStorageStatement& updateStmt) -> Result<Ok, nsresult> {
  14666        QM_TRY(MOZ_TO_RESULT(
  14667            indexDataValues
  14668                ? updateStmt.BindAdoptedBlobByName(
  14669                      kStmtParamNameIndexDataValues, indexDataValues.release(),
  14670                      indexDataValuesLength)
  14671                : updateStmt.BindNullByName(kStmtParamNameIndexDataValues)));
  14672 
  14673        QM_TRY(MOZ_TO_RESULT(updateStmt.BindInt64ByName(
  14674            kStmtParamNameObjectStoreId, aObjectStoreId)));
  14675 
  14676        QM_TRY(MOZ_TO_RESULT(
  14677            aObjectStoreKey.BindToStatement(&updateStmt, kStmtParamNameKey)));
  14678 
  14679        return Ok{};
  14680      })));
  14681 
  14682  return NS_OK;
  14683 }
  14684 
  14685 // static
  14686 Result<bool, nsresult> DatabaseOperationBase::ObjectStoreHasIndexes(
  14687    DatabaseConnection& aConnection,
  14688    const IndexOrObjectStoreId aObjectStoreId) {
  14689  aConnection.AssertIsOnConnectionThread();
  14690  MOZ_ASSERT(aObjectStoreId);
  14691 
  14692  QM_TRY_RETURN(aConnection
  14693                    .BorrowAndExecuteSingleStepStatement(
  14694                        "SELECT id "
  14695                        "FROM object_store_index "
  14696                        "WHERE object_store_id = :"_ns +
  14697                            kStmtParamNameObjectStoreId + kOpenLimit + "1;"_ns,
  14698                        [aObjectStoreId](auto& stmt) -> Result<Ok, nsresult> {
  14699                          QM_TRY(MOZ_TO_RESULT(stmt.BindInt64ByName(
  14700                              kStmtParamNameObjectStoreId, aObjectStoreId)));
  14701                          return Ok{};
  14702                        })
  14703                    .map(IsSome));
  14704 }
  14705 
  14706 NS_IMPL_ISUPPORTS_INHERITED(DatabaseOperationBase, Runnable,
  14707                            mozIStorageProgressHandler)
  14708 
  14709 NS_IMETHODIMP
  14710 DatabaseOperationBase::OnProgress(mozIStorageConnection* aConnection,
  14711                                  bool* _retval) {
  14712  MOZ_ASSERT(!IsOnBackgroundThread());
  14713  MOZ_ASSERT(_retval);
  14714 
  14715  // This is intentionally racy.
  14716  *_retval = QuotaClient::IsShuttingDownOnNonBackgroundThread() ||
  14717             !OperationMayProceed();
  14718  return NS_OK;
  14719 }
  14720 
  14721 DatabaseOperationBase::AutoSetProgressHandler::AutoSetProgressHandler()
  14722    : mConnection(Nothing())
  14723 #ifdef DEBUG
  14724      ,
  14725      mDEBUGDatabaseOp(nullptr)
  14726 #endif
  14727 {
  14728  MOZ_ASSERT(!IsOnBackgroundThread());
  14729 }
  14730 
  14731 DatabaseOperationBase::AutoSetProgressHandler::~AutoSetProgressHandler() {
  14732  MOZ_ASSERT(!IsOnBackgroundThread());
  14733 
  14734  if (mConnection) {
  14735    Unregister();
  14736  }
  14737 }
  14738 
  14739 nsresult DatabaseOperationBase::AutoSetProgressHandler::Register(
  14740    mozIStorageConnection& aConnection, DatabaseOperationBase* aDatabaseOp) {
  14741  MOZ_ASSERT(!IsOnBackgroundThread());
  14742  MOZ_ASSERT(aDatabaseOp);
  14743  MOZ_ASSERT(!mConnection);
  14744 
  14745  QM_TRY_UNWRAP(
  14746      const DebugOnly oldProgressHandler,
  14747      MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
  14748          nsCOMPtr<mozIStorageProgressHandler>, aConnection, SetProgressHandler,
  14749          kStorageProgressGranularity, aDatabaseOp));
  14750 
  14751  MOZ_ASSERT(!oldProgressHandler.inspect());
  14752 
  14753  mConnection = SomeRef(aConnection);
  14754 #ifdef DEBUG
  14755  mDEBUGDatabaseOp = aDatabaseOp;
  14756 #endif
  14757 
  14758  return NS_OK;
  14759 }
  14760 
  14761 void DatabaseOperationBase::AutoSetProgressHandler::Unregister() {
  14762  MOZ_ASSERT(!IsOnBackgroundThread());
  14763  MOZ_ASSERT(mConnection);
  14764 
  14765  nsCOMPtr<mozIStorageProgressHandler> oldHandler;
  14766  MOZ_ALWAYS_SUCCEEDS(
  14767      mConnection->RemoveProgressHandler(getter_AddRefs(oldHandler)));
  14768  MOZ_ASSERT(oldHandler == mDEBUGDatabaseOp);
  14769 
  14770  mConnection = Nothing();
  14771 }
  14772 
  14773 FactoryOp::FactoryOp(SafeRefPtr<Factory> aFactory,
  14774                     const Maybe<ContentParentId>& aContentParentId,
  14775                     const PersistenceType aPersistenceType,
  14776                     const PrincipalInfo& aPrincipalInfo,
  14777                     const Maybe<nsString>& aDatabaseName, bool aDeleting)
  14778    : DatabaseOperationBase(aFactory->GetLoggingInfo()->Id(),
  14779                            aFactory->GetLoggingInfo()->NextRequestSN()),
  14780      mFactory(std::move(aFactory)),
  14781      mContentParentId(aContentParentId),
  14782      mPrincipalInfo(aPrincipalInfo),
  14783      mDatabaseName(aDatabaseName),
  14784      mDirectoryLockId(-1),
  14785      mPersistenceType(aPersistenceType),
  14786      mState(State::Initial),
  14787      mWaitingForPermissionRetry(false),
  14788      mEnforcingQuota(true),
  14789      mDeleting(aDeleting) {
  14790  AssertIsOnBackgroundThread();
  14791  MOZ_ASSERT(mFactory);
  14792  MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
  14793 }
  14794 
  14795 nsresult FactoryOp::DispatchThisAfterProcessingCurrentEvent(
  14796    nsCOMPtr<nsIEventTarget> aEventTarget) {
  14797  QM_TRY(MOZ_TO_RESULT(RunAfterProcessingCurrentEvent(
  14798      [eventTarget = std::move(aEventTarget), self = RefPtr(this)]() mutable {
  14799        QM_WARNONLY_TRY(MOZ_TO_RESULT(
  14800            eventTarget->Dispatch(self.forget(), NS_DISPATCH_NORMAL)));
  14801      })));
  14802 
  14803  return NS_OK;
  14804 }
  14805 
  14806 void FactoryOp::NoteDatabaseBlocked(Database* aDatabase) {
  14807  AssertIsOnOwningThread();
  14808  MOZ_ASSERT(aDatabase);
  14809  MOZ_ASSERT(mState == State::WaitingForOtherDatabasesToClose);
  14810  MOZ_ASSERT(!mMaybeBlockedDatabases.IsEmpty());
  14811  MOZ_ASSERT(mMaybeBlockedDatabases.Contains(aDatabase));
  14812 
  14813  // Only send the blocked event if all databases have reported back. If the
  14814  // database was closed then it will have been removed from the array.
  14815  // Otherwise if it was blocked its |mBlocked| flag will be true.
  14816  bool sendBlockedEvent = true;
  14817 
  14818  for (auto& info : mMaybeBlockedDatabases) {
  14819    if (info == aDatabase) {
  14820      // This database was blocked, mark accordingly.
  14821      info.mBlocked = true;
  14822    } else if (!info.mBlocked) {
  14823      // A database has not yet reported back yet, don't send the event yet.
  14824      sendBlockedEvent = false;
  14825    }
  14826  }
  14827 
  14828  if (sendBlockedEvent) {
  14829    SendBlockedNotification();
  14830  }
  14831 }
  14832 
  14833 void FactoryOp::NoteDatabaseClosed(Database* const aDatabase) {
  14834  AssertIsOnOwningThread();
  14835  MOZ_ASSERT(aDatabase);
  14836  MOZ_ASSERT(mState == State::WaitingForOtherDatabasesToClose);
  14837  MOZ_ASSERT(!mMaybeBlockedDatabases.IsEmpty());
  14838  MOZ_ASSERT(mMaybeBlockedDatabases.Contains(aDatabase));
  14839 
  14840  mMaybeBlockedDatabases.RemoveElement(aDatabase);
  14841 
  14842  if (!mMaybeBlockedDatabases.IsEmpty()) {
  14843    return;
  14844  }
  14845 
  14846  DatabaseActorInfo* info;
  14847  MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(mDatabaseId.ref(), &info));
  14848  MOZ_ASSERT(info->mWaitingFactoryOp == this);
  14849 
  14850  if (AreActorsAlive()) {
  14851    // The IPDL strong reference has not yet been released, so we can clear
  14852    // mWaitingFactoryOp immediately.
  14853    info->mWaitingFactoryOp = nullptr;
  14854 
  14855    WaitForTransactions();
  14856    return;
  14857  }
  14858 
  14859  // The IPDL strong reference has been released, mWaitingFactoryOp holds the
  14860  // last strong reference to us, so we need to move it to a stack variable
  14861  // instead of clearing it immediately (We could clear it immediately if only
  14862  // the other actor is destroyed, but we don't need to optimize for that, and
  14863  // move it anyway).
  14864  const RefPtr<FactoryOp> waitingFactoryOp = std::move(info->mWaitingFactoryOp);
  14865 
  14866  IDB_REPORT_INTERNAL_ERR();
  14867  SetFailureCodeIfUnset(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  14868 
  14869  // We hold a strong ref in waitingFactoryOp, so it's safe to call Run()
  14870  // directly.
  14871 
  14872  mState = State::SendingResults;
  14873  MOZ_ALWAYS_SUCCEEDS(Run());
  14874 }
  14875 
  14876 void FactoryOp::StringifyState(nsACString& aResult) const {
  14877  AssertIsOnOwningThread();
  14878 
  14879  switch (mState) {
  14880    case State::Initial:
  14881      aResult.AppendLiteral("Initial");
  14882      return;
  14883 
  14884    case State::DirectoryOpenPending:
  14885      aResult.AppendLiteral("DirectoryOpenPending");
  14886      return;
  14887 
  14888    case State::DirectoryWorkOpen:
  14889      aResult.AppendLiteral("DirectoryWorkOpen");
  14890      return;
  14891 
  14892    case State::DirectoryWorkDone:
  14893      aResult.AppendLiteral("DirectoryWorkDone");
  14894      return;
  14895 
  14896    case State::DatabaseOpenPending:
  14897      aResult.AppendLiteral("DatabaseOpenPending");
  14898      return;
  14899 
  14900    case State::DatabaseWorkOpen:
  14901      aResult.AppendLiteral("DatabaseWorkOpen");
  14902      return;
  14903 
  14904    case State::BeginVersionChange:
  14905      aResult.AppendLiteral("BeginVersionChange");
  14906      return;
  14907 
  14908    case State::WaitingForOtherDatabasesToClose:
  14909      aResult.AppendLiteral("WaitingForOtherDatabasesToClose");
  14910      return;
  14911 
  14912    case State::WaitingForTransactionsToComplete:
  14913      aResult.AppendLiteral("WaitingForTransactionsToComplete");
  14914      return;
  14915 
  14916    case State::DatabaseWorkVersionChange:
  14917      aResult.AppendLiteral("DatabaseWorkVersionChange");
  14918      return;
  14919 
  14920    case State::SendingResults:
  14921      aResult.AppendLiteral("SendingResults");
  14922      return;
  14923 
  14924    case State::Completed:
  14925      aResult.AppendLiteral("Completed");
  14926      return;
  14927 
  14928    default:
  14929      MOZ_CRASH("Bad state!");
  14930  }
  14931 }
  14932 
  14933 void FactoryOp::Stringify(nsACString& aResult) const {
  14934  AssertIsOnOwningThread();
  14935 
  14936  aResult.AppendLiteral("PersistenceType:");
  14937  aResult.Append(PersistenceTypeToString(mPersistenceType));
  14938  aResult.Append(kQuotaGenericDelimiter);
  14939 
  14940  aResult.AppendLiteral("Origin:");
  14941  aResult.Append(AnonymizedOriginString(mOriginMetadata.mOrigin));
  14942  aResult.Append(kQuotaGenericDelimiter);
  14943 
  14944  aResult.AppendLiteral("State:");
  14945  StringifyState(aResult);
  14946 }
  14947 
  14948 nsresult FactoryOp::Open() {
  14949  AssertIsOnOwningThread();
  14950  MOZ_ASSERT(mState == State::Initial);
  14951  MOZ_ASSERT(mOriginMetadata.mOrigin.IsEmpty());
  14952  MOZ_ASSERT(!mDirectoryLockHandle);
  14953 
  14954  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
  14955      IsActorDestroyed()) {
  14956    IDB_REPORT_INTERNAL_ERR();
  14957    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
  14958  }
  14959 
  14960  QM_TRY(QuotaManager::EnsureCreated());
  14961 
  14962  QuotaManager* const quotaManager = QuotaManager::Get();
  14963  MOZ_ASSERT(quotaManager);
  14964 
  14965  QM_TRY_UNWRAP(
  14966      auto principalMetadata,
  14967      quota::GetInfoFromValidatedPrincipalInfo(*quotaManager, mPrincipalInfo));
  14968 
  14969  mOriginMetadata = {std::move(principalMetadata), mPersistenceType};
  14970 
  14971  if (mPrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
  14972    MOZ_ASSERT(mPersistenceType == PERSISTENCE_TYPE_PERSISTENT);
  14973 
  14974    mEnforcingQuota = false;
  14975  } else if (mPrincipalInfo.type() == PrincipalInfo::TContentPrincipalInfo) {
  14976    const ContentPrincipalInfo& contentPrincipalInfo =
  14977        mPrincipalInfo.get_ContentPrincipalInfo();
  14978 
  14979    MOZ_ASSERT_IF(
  14980        QuotaManager::IsOriginInternal(contentPrincipalInfo.originNoSuffix()),
  14981        mPersistenceType == PERSISTENCE_TYPE_PERSISTENT);
  14982 
  14983    mEnforcingQuota = mPersistenceType != PERSISTENCE_TYPE_PERSISTENT;
  14984 
  14985    if (mOriginMetadata.mIsPrivate) {
  14986      if (StaticPrefs::dom_indexedDB_privateBrowsing_enabled()) {
  14987        // Explicitly disallow moz-extension urls from using the encrypted
  14988        // indexedDB storage mode when the caller is an extension (see Bug
  14989        // 1841806).
  14990        if (StringBeginsWith(contentPrincipalInfo.originNoSuffix(),
  14991                             "moz-extension:"_ns)) {
  14992          return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
  14993        }
  14994 
  14995        mInPrivateBrowsing.Flip();
  14996      } else {
  14997        return NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR;
  14998      }
  14999    }
  15000  } else {
  15001    MOZ_ASSERT(false);
  15002  }
  15003 
  15004  if (mDatabaseName.isSome()) {
  15005    nsCString databaseId;
  15006 
  15007    QuotaManager::GetStorageId(mPersistenceType, mOriginMetadata.mOrigin,
  15008                               Client::IDB, databaseId);
  15009 
  15010    databaseId.Append('*');
  15011    databaseId.Append(NS_ConvertUTF16toUTF8(mDatabaseName.ref()));
  15012 
  15013    mDatabaseId = Some(std::move(databaseId));
  15014 
  15015    // Need to get database file path before opening the directory.
  15016    // XXX: For what reason?
  15017    QM_TRY_UNWRAP(
  15018        auto databaseFilePath,
  15019        ([this, quotaManager]() -> mozilla::Result<nsString, nsresult> {
  15020          QM_TRY_INSPECT(const auto& dbFile,
  15021                         quotaManager->GetOriginDirectory(mOriginMetadata));
  15022 
  15023          QM_TRY(MOZ_TO_RESULT(dbFile->Append(
  15024              NS_LITERAL_STRING_FROM_CSTRING(IDB_DIRECTORY_NAME))));
  15025 
  15026          QM_TRY(MOZ_TO_RESULT(dbFile->Append(
  15027              GetDatabaseFilenameBase(mDatabaseName.ref(),
  15028                                      mOriginMetadata.mIsPrivate) +
  15029              kSQLiteSuffix)));
  15030 
  15031          QM_TRY_RETURN(
  15032              MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsString, dbFile, GetPath));
  15033        }()));
  15034 
  15035    mDatabaseFilePath = Some(std::move(databaseFilePath));
  15036  }
  15037 
  15038  // Open directory
  15039  mState = State::DirectoryOpenPending;
  15040 
  15041  quotaManager->OpenClientDirectory({mOriginMetadata, Client::IDB})
  15042      ->Then(
  15043          GetCurrentSerialEventTarget(), __func__,
  15044          [self = RefPtr(this)](QuotaManager::ClientDirectoryLockHandlePromise::
  15045                                    ResolveOrRejectValue&& aValue) {
  15046            if (aValue.IsResolve()) {
  15047              self->DirectoryLockAcquired(std::move(aValue.ResolveValue()));
  15048            } else {
  15049              self->DirectoryLockFailed();
  15050            }
  15051          });
  15052 
  15053  return NS_OK;
  15054 }
  15055 
  15056 nsresult FactoryOp::DirectoryOpen() {
  15057  AssertIsOnOwningThread();
  15058  MOZ_ASSERT(mState == State::DirectoryOpenPending);
  15059  MOZ_ASSERT(mDirectoryLockHandle);
  15060 
  15061  if (mDatabaseName.isNothing()) {
  15062    QuotaManager* const quotaManager = QuotaManager::Get();
  15063    MOZ_ASSERT(quotaManager);
  15064 
  15065    // Must set this before dispatching otherwise we will race with the IO
  15066    // thread.
  15067    mState = State::DirectoryWorkOpen;
  15068 
  15069    QM_TRY(MOZ_TO_RESULT(
  15070               quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL)),
  15071           NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR, IDB_REPORT_INTERNAL_ERR_LAMBDA);
  15072 
  15073    return NS_OK;
  15074  }
  15075 
  15076  mState = State::DirectoryWorkDone;
  15077  MOZ_ALWAYS_SUCCEEDS(Run());
  15078 
  15079  return NS_OK;
  15080 }
  15081 
  15082 nsresult FactoryOp::DirectoryWorkDone() {
  15083  AssertIsOnOwningThread();
  15084  MOZ_ASSERT(mState == State::DirectoryWorkDone);
  15085  MOZ_ASSERT(mDirectoryLockHandle);
  15086  MOZ_ASSERT(gFactoryOps);
  15087 
  15088  // See if this FactoryOp needs to wait.
  15089  const bool blocked = [&self = *this] {
  15090    bool foundThis = false;
  15091    bool blocked = false;
  15092 
  15093    // XXX LinkedList should support reverse iteration via rbegin() and rend(),
  15094    // see bug 1964967.
  15095    for (FactoryOp* existingOp = gFactoryOps->getLast(); existingOp;
  15096         existingOp = existingOp->getPrevious()) {
  15097      if (existingOp == &self) {
  15098        foundThis = true;
  15099        continue;
  15100      }
  15101 
  15102      if (foundThis && self.MustWaitFor(*existingOp)) {
  15103        existingOp->AddBlockingOp(self);
  15104        self.AddBlockedOnOp(*existingOp);
  15105        blocked = true;
  15106      }
  15107    }
  15108 
  15109    return blocked;
  15110  }() || [&self = *this] {
  15111    QuotaClient* quotaClient = QuotaClient::GetInstance();
  15112    MOZ_ASSERT(quotaClient);
  15113 
  15114    if (RefPtr<Maintenance> currentMaintenance =
  15115            quotaClient->GetCurrentMaintenance()) {
  15116      if (self.mDatabaseName.isSome()) {
  15117        if (RefPtr<DatabaseMaintenance> databaseMaintenance =
  15118                currentMaintenance->GetDatabaseMaintenance(
  15119                    self.mDatabaseFilePath.ref())) {
  15120          databaseMaintenance->WaitForCompletion(&self);
  15121          return true;
  15122        }
  15123      } else if (currentMaintenance->HasDatabaseMaintenances()) {
  15124        currentMaintenance->WaitForCompletion(&self);
  15125        return true;
  15126      }
  15127    }
  15128 
  15129    return false;
  15130  }();
  15131 
  15132  mState = State::DatabaseOpenPending;
  15133  if (!blocked) {
  15134    QM_TRY(MOZ_TO_RESULT(DatabaseOpen()));
  15135  }
  15136 
  15137  return NS_OK;
  15138 }
  15139 
  15140 nsresult FactoryOp::SendToIOThread() {
  15141  AssertIsOnOwningThread();
  15142  MOZ_ASSERT(mState == State::DatabaseOpenPending);
  15143 
  15144  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
  15145      !OperationMayProceed()) {
  15146    IDB_REPORT_INTERNAL_ERR();
  15147    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
  15148  }
  15149 
  15150  QuotaManager* const quotaManager = QuotaManager::Get();
  15151  MOZ_ASSERT(quotaManager);
  15152 
  15153  // Must set this before dispatching otherwise we will race with the IO thread.
  15154  mState = State::DatabaseWorkOpen;
  15155 
  15156  QM_TRY(MOZ_TO_RESULT(
  15157             quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL)),
  15158         NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR, IDB_REPORT_INTERNAL_ERR_LAMBDA);
  15159 
  15160  NotifyDatabaseWorkStarted();
  15161 
  15162  return NS_OK;
  15163 }
  15164 
  15165 void FactoryOp::WaitForTransactions() {
  15166  AssertIsOnOwningThread();
  15167  MOZ_ASSERT(mState == State::BeginVersionChange ||
  15168             mState == State::WaitingForOtherDatabasesToClose);
  15169  MOZ_ASSERT(!mDatabaseId.ref().IsEmpty());
  15170  MOZ_ASSERT(!IsActorDestroyed());
  15171 
  15172  mState = State::WaitingForTransactionsToComplete;
  15173 
  15174  RefPtr<WaitForTransactionsHelper> helper =
  15175      new WaitForTransactionsHelper(mDatabaseId.ref(), this);
  15176  helper->WaitForTransactions();
  15177 }
  15178 
  15179 void FactoryOp::CleanupMetadata() {
  15180  AssertIsOnOwningThread();
  15181 
  15182  for (const NotNull<RefPtr<FactoryOp>>& blockingOp : mBlocking) {
  15183    blockingOp->MaybeUnblock(*this);
  15184  }
  15185  mBlocking.Clear();
  15186 
  15187  MOZ_ASSERT(gFactoryOps);
  15188  removeFrom(*gFactoryOps);
  15189 
  15190  // We might get here even after QuotaManagerOpen failed, so we need to check
  15191  // if we have a quota manager.
  15192  quota::QuotaManager::SafeMaybeRecordQuotaClientShutdownStep(
  15193      quota::Client::IDB, "An element was removed from gFactoryOps"_ns);
  15194 
  15195  // Match the IncreaseBusyCount in AllocPBackgroundIDBFactoryRequestParent().
  15196  DecreaseBusyCount();
  15197 }
  15198 
  15199 void FactoryOp::FinishSendResults() {
  15200  AssertIsOnOwningThread();
  15201  MOZ_ASSERT(mState == State::SendingResults);
  15202  MOZ_ASSERT(mFactory);
  15203 
  15204  mState = State::Completed;
  15205 
  15206  // Make sure to release the factory on this thread.
  15207  mFactory = nullptr;
  15208 }
  15209 
  15210 nsresult FactoryOp::SendVersionChangeMessages(
  15211    DatabaseActorInfo* aDatabaseActorInfo, Maybe<Database&> aOpeningDatabase,
  15212    uint64_t aOldVersion, const Maybe<uint64_t>& aNewVersion) {
  15213  AssertIsOnOwningThread();
  15214  MOZ_ASSERT(aDatabaseActorInfo);
  15215  MOZ_ASSERT(mState == State::BeginVersionChange);
  15216  MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty());
  15217  MOZ_ASSERT(!IsActorDestroyed());
  15218 
  15219  const uint32_t expectedCount = mDeleting ? 0 : 1;
  15220  const uint32_t liveCount = aDatabaseActorInfo->mLiveDatabases.length();
  15221  if (liveCount > expectedCount) {
  15222    nsTArray<MaybeBlockedDatabaseInfo> maybeBlockedDatabases;
  15223    for (Database* const database : aDatabaseActorInfo->mLiveDatabases) {
  15224      if ((!aOpeningDatabase || database != &aOpeningDatabase.ref()) &&
  15225          !database->IsClosed() &&
  15226          NS_WARN_IF(!maybeBlockedDatabases.AppendElement(
  15227              SafeRefPtr{database, AcquireStrongRefFromRawPtr{}}, fallible))) {
  15228        return NS_ERROR_OUT_OF_MEMORY;
  15229      }
  15230    }
  15231 
  15232    mMaybeBlockedDatabases = std::move(maybeBlockedDatabases);
  15233  }
  15234 
  15235  // We don't want to wait forever if we were not able to send the
  15236  // message.
  15237  mMaybeBlockedDatabases.RemoveLastElements(
  15238      mMaybeBlockedDatabases.end() -
  15239      std::remove_if(mMaybeBlockedDatabases.begin(),
  15240                     mMaybeBlockedDatabases.end(),
  15241                     [aOldVersion, &aNewVersion](auto& maybeBlockedDatabase) {
  15242                       return !maybeBlockedDatabase->SendVersionChange(
  15243                           aOldVersion, aNewVersion);
  15244                     }));
  15245 
  15246  return NS_OK;
  15247 }  // namespace indexedDB
  15248 
  15249 bool FactoryOp::MustWaitFor(const FactoryOp& aExistingOp) {
  15250  AssertIsOnOwningThread();
  15251 
  15252  // If the persistence types don't overlap, the op can proceed.
  15253  if (aExistingOp.mPersistenceType != mPersistenceType) {
  15254    return false;
  15255  }
  15256 
  15257  // If the origins don't overlap, the op can proceed.
  15258  if (aExistingOp.mOriginMetadata.mOrigin != mOriginMetadata.mOrigin) {
  15259    return false;
  15260  }
  15261 
  15262  // If the database ids don't overlap, the op can proceed.
  15263  if (!aExistingOp.mDatabaseId.isNothing() && !mDatabaseId.isNothing() &&
  15264      aExistingOp.mDatabaseId.ref() != mDatabaseId.ref()) {
  15265    return false;
  15266  }
  15267 
  15268  return true;
  15269 }
  15270 
  15271 // Run() assumes that the caller holds a strong reference to the object that
  15272 // can't be cleared while Run() is being executed.
  15273 // So if you call Run() directly (as opposed to dispatching to an event queue)
  15274 // you need to make sure there's such a reference.
  15275 // See bug 1356824 for more details.
  15276 NS_IMETHODIMP
  15277 FactoryOp::Run() {
  15278  const auto handleError = [this](const nsresult rv) {
  15279    if (mState != State::SendingResults) {
  15280      SetFailureCodeIfUnset(rv);
  15281 
  15282      // Must set mState before dispatching otherwise we will race with the
  15283      // owning thread.
  15284      mState = State::SendingResults;
  15285 
  15286      if (IsOnOwningThread()) {
  15287        SendResults();
  15288      } else {
  15289        MOZ_ALWAYS_SUCCEEDS(
  15290            DispatchThisAfterProcessingCurrentEvent(mOwningEventTarget));
  15291      }
  15292    }
  15293  };
  15294 
  15295  switch (mState) {
  15296    case State::Initial:
  15297      QM_WARNONLY_TRY(MOZ_TO_RESULT(Open()), handleError);
  15298      break;
  15299 
  15300    case State::DirectoryWorkOpen:
  15301      QM_WARNONLY_TRY(MOZ_TO_RESULT(DoDirectoryWork()), handleError);
  15302      break;
  15303 
  15304    case State::DirectoryWorkDone:
  15305      QM_WARNONLY_TRY(MOZ_TO_RESULT(DirectoryWorkDone()), handleError);
  15306      break;
  15307 
  15308    case State::DatabaseOpenPending:
  15309      QM_WARNONLY_TRY(MOZ_TO_RESULT(DatabaseOpen()), handleError);
  15310      break;
  15311 
  15312    case State::DatabaseWorkOpen:
  15313      QM_WARNONLY_TRY(MOZ_TO_RESULT(DoDatabaseWork()), handleError);
  15314      break;
  15315 
  15316    case State::BeginVersionChange:
  15317      QM_WARNONLY_TRY(MOZ_TO_RESULT(BeginVersionChange()), handleError);
  15318      break;
  15319 
  15320    case State::WaitingForTransactionsToComplete:
  15321      QM_WARNONLY_TRY(MOZ_TO_RESULT(DispatchToWorkThread()), handleError);
  15322      break;
  15323 
  15324    case State::DatabaseWorkVersionUpdate:
  15325      QM_WARNONLY_TRY(MOZ_TO_RESULT(DoVersionUpdate()), handleError);
  15326      break;
  15327 
  15328    case State::SendingResults:
  15329      SendResults();
  15330      break;
  15331 
  15332    default:
  15333      MOZ_CRASH("Bad state!");
  15334  }
  15335 
  15336  return NS_OK;
  15337 }
  15338 
  15339 void FactoryOp::DirectoryLockAcquired(ClientDirectoryLockHandle aLockHandle) {
  15340  AssertIsOnOwningThread();
  15341  MOZ_ASSERT(aLockHandle);
  15342  MOZ_ASSERT(mState == State::DirectoryOpenPending);
  15343  MOZ_ASSERT(!mDirectoryLockHandle);
  15344 
  15345  mDirectoryLockHandle = std::move(aLockHandle);
  15346 
  15347  MOZ_ASSERT(mDirectoryLockHandle->Id() >= 0);
  15348  mDirectoryLockId = mDirectoryLockHandle->Id();
  15349 
  15350  auto cleanupAndReturn = [self = RefPtr(this)](const nsresult rv) {
  15351    self->SetFailureCodeIfUnset(rv);
  15352 
  15353    // The caller holds a strong reference to us, no need for a self reference
  15354    // before calling Run().
  15355 
  15356    self->mState = State::SendingResults;
  15357    MOZ_ALWAYS_SUCCEEDS(self->Run());
  15358  };
  15359 
  15360  if (mDirectoryLockHandle->Invalidated()) {
  15361    return cleanupAndReturn(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
  15362  }
  15363 
  15364  QM_WARNONLY_TRY(MOZ_TO_RESULT(DirectoryOpen()), cleanupAndReturn);
  15365 }
  15366 
  15367 void FactoryOp::DirectoryLockFailed() {
  15368  AssertIsOnOwningThread();
  15369  MOZ_ASSERT(mState == State::DirectoryOpenPending);
  15370  MOZ_ASSERT(!mDirectoryLockHandle);
  15371 
  15372  if (!HasFailed()) {
  15373    IDB_REPORT_INTERNAL_ERR();
  15374    SetFailureCode(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  15375  }
  15376 
  15377  // The caller holds a strong reference to us, no need for a self reference
  15378  // before calling Run().
  15379 
  15380  mState = State::SendingResults;
  15381  MOZ_ALWAYS_SUCCEEDS(Run());
  15382 }
  15383 
  15384 nsresult FactoryRequestOp::DoDirectoryWork() {
  15385  MOZ_CRASH("Not implemented because this should be unreachable.");
  15386 }
  15387 
  15388 void FactoryRequestOp::ActorDestroy(ActorDestroyReason aWhy) {
  15389  AssertIsOnBackgroundThread();
  15390 
  15391  NoteActorDestroyed();
  15392 }
  15393 
  15394 OpenDatabaseOp::OpenDatabaseOp(SafeRefPtr<Factory> aFactory,
  15395                               const Maybe<ContentParentId>& aContentParentId,
  15396                               const CommonFactoryRequestParams& aParams)
  15397    : FactoryRequestOp(std::move(aFactory), aContentParentId, aParams,
  15398                       /* aDeleting */ false),
  15399      mMetadata(MakeSafeRefPtr<FullDatabaseMetadata>(aParams.metadata())),
  15400      mRequestedVersion(aParams.metadata().version()),
  15401      mVersionChangeOp(nullptr),
  15402      mTelemetryId(0) {}
  15403 
  15404 void OpenDatabaseOp::ActorDestroy(ActorDestroyReason aWhy) {
  15405  AssertIsOnOwningThread();
  15406 
  15407  FactoryRequestOp::ActorDestroy(aWhy);
  15408 
  15409  if (mVersionChangeOp) {
  15410    mVersionChangeOp->NoteActorDestroyed();
  15411  }
  15412 }
  15413 
  15414 nsresult OpenDatabaseOp::DatabaseOpen() {
  15415  AssertIsOnOwningThread();
  15416  MOZ_ASSERT(mState == State::DatabaseOpenPending);
  15417 
  15418  nsresult rv = SendToIOThread();
  15419  if (NS_WARN_IF(NS_FAILED(rv))) {
  15420    return rv;
  15421  }
  15422 
  15423  return NS_OK;
  15424 }
  15425 
  15426 nsresult OpenDatabaseOp::DoDatabaseWork() {
  15427  AssertIsOnIOThread();
  15428  MOZ_ASSERT(mState == State::DatabaseWorkOpen);
  15429 
  15430  AUTO_PROFILER_LABEL("OpenDatabaseOp::DoDatabaseWork", DOM);
  15431 
  15432  QM_TRY(OkIf(!QuotaClient::IsShuttingDownOnNonBackgroundThread()),
  15433         NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR, IDB_REPORT_INTERNAL_ERR_LAMBDA);
  15434 
  15435  if (!OperationMayProceed()) {
  15436    IDB_REPORT_INTERNAL_ERR();
  15437    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
  15438  }
  15439 
  15440  const nsAString& databaseName = mCommonParams.metadata().name();
  15441  const PersistenceType persistenceType =
  15442      mCommonParams.metadata().persistenceType();
  15443 
  15444  QuotaManager* const quotaManager = QuotaManager::Get();
  15445  MOZ_ASSERT(quotaManager);
  15446 
  15447  QM_TRY_INSPECT(
  15448      const auto& dbDirectory,
  15449      ([persistenceType, &quotaManager,
  15450        this]() -> mozilla::Result<nsCOMPtr<nsIFile>, nsresult> {
  15451        if (persistenceType == PERSISTENCE_TYPE_PERSISTENT) {
  15452          QM_TRY_RETURN(quotaManager->GetOriginDirectory(mOriginMetadata));
  15453        }
  15454 
  15455        QM_TRY_RETURN(
  15456            quotaManager->GetOrCreateTemporaryOriginDirectory(mOriginMetadata));
  15457      }()));
  15458 
  15459  QM_TRY(MOZ_TO_RESULT(
  15460      dbDirectory->Append(NS_LITERAL_STRING_FROM_CSTRING(IDB_DIRECTORY_NAME))));
  15461 
  15462  {
  15463    QM_TRY_INSPECT(const bool& exists,
  15464                   MOZ_TO_RESULT_INVOKE_MEMBER(dbDirectory, Exists));
  15465 
  15466    if (!exists) {
  15467      QM_TRY(MOZ_TO_RESULT(dbDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755)));
  15468    }
  15469 #ifdef DEBUG
  15470    else {
  15471      bool isDirectory;
  15472      MOZ_ASSERT(NS_SUCCEEDED(dbDirectory->IsDirectory(&isDirectory)));
  15473      MOZ_ASSERT(isDirectory);
  15474    }
  15475 #endif
  15476  }
  15477 
  15478  const auto databaseFilenameBase =
  15479      GetDatabaseFilenameBase(databaseName, mOriginMetadata.mIsPrivate);
  15480 
  15481  QM_TRY_INSPECT(const auto& markerFile,
  15482                 CloneFileAndAppend(*dbDirectory, kIdbDeletionMarkerFilePrefix +
  15483                                                      databaseFilenameBase));
  15484 
  15485  QM_TRY_INSPECT(const bool& exists,
  15486                 MOZ_TO_RESULT_INVOKE_MEMBER(markerFile, Exists));
  15487 
  15488  if (exists) {
  15489    // Delete the database and directroy since they should be deleted in
  15490    // previous operation.
  15491    // Note: only update usage to the QuotaManager when mEnforcingQuota == true
  15492    QM_TRY(MOZ_TO_RESULT(RemoveDatabaseFilesAndDirectory(
  15493        *dbDirectory, databaseFilenameBase,
  15494        mEnforcingQuota ? quotaManager : nullptr, persistenceType,
  15495        mOriginMetadata, databaseName)));
  15496  }
  15497 
  15498  QM_TRY_INSPECT(
  15499      const auto& dbFile,
  15500      CloneFileAndAppend(*dbDirectory, databaseFilenameBase + kSQLiteSuffix));
  15501 
  15502  mTelemetryId = TelemetryIdForFile(dbFile);
  15503 
  15504 #ifdef DEBUG
  15505  {
  15506    QM_TRY_INSPECT(
  15507        const auto& databaseFilePath,
  15508        MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsString, dbFile, GetPath));
  15509 
  15510    MOZ_ASSERT(databaseFilePath == mDatabaseFilePath.ref());
  15511  }
  15512 #endif
  15513 
  15514  QM_TRY_INSPECT(
  15515      const auto& fmDirectory,
  15516      CloneFileAndAppend(*dbDirectory, databaseFilenameBase +
  15517                                           kFileManagerDirectoryNameSuffix));
  15518 
  15519  IndexedDatabaseManager* const idm = IndexedDatabaseManager::Get();
  15520  MOZ_ASSERT(idm);
  15521 
  15522  SafeRefPtr<DatabaseFileManager> fileManager = idm->GetFileManager(
  15523      persistenceType, mOriginMetadata.mOrigin, databaseName);
  15524 
  15525  if (!fileManager) {
  15526    fileManager = MakeSafeRefPtr<DatabaseFileManager>(
  15527        persistenceType, mOriginMetadata, databaseName, mDatabaseId.ref(),
  15528        mDatabaseFilePath.ref(), mEnforcingQuota, mInPrivateBrowsing);
  15529  }
  15530 
  15531  Maybe<const CipherKey> maybeKey =
  15532      mInPrivateBrowsing
  15533          ? Some(fileManager->MutableCipherKeyManagerRef().Ensure())
  15534          : Nothing();
  15535 
  15536  MOZ_RELEASE_ASSERT(mInPrivateBrowsing == maybeKey.isSome());
  15537 
  15538  QM_TRY_UNWRAP(
  15539      NotNull<nsCOMPtr<mozIStorageConnection>> connection,
  15540      CreateStorageConnection(*dbFile, *fmDirectory, databaseName,
  15541                              mOriginMetadata.mOrigin, mDirectoryLockId,
  15542                              mTelemetryId, maybeKey));
  15543 
  15544  AutoSetProgressHandler asph;
  15545  QM_TRY(MOZ_TO_RESULT(asph.Register(*connection, this)));
  15546 
  15547  QM_TRY(MOZ_TO_RESULT(LoadDatabaseInformation(*connection)));
  15548 
  15549  MOZ_ASSERT(mMetadata->mNextObjectStoreId > mMetadata->mObjectStores.Count());
  15550  MOZ_ASSERT(mMetadata->mNextIndexId > 0);
  15551 
  15552  // See if we need to do a versionchange transaction
  15553 
  15554  // Optional version semantics.
  15555  if (!mRequestedVersion) {
  15556    // If the requested version was not specified and the database was created,
  15557    // treat it as if version 1 were requested.
  15558    // Otherwise, treat it as if the current version were requested.
  15559    mRequestedVersion = mMetadata->mCommonMetadata.version() == 0
  15560                            ? 1
  15561                            : mMetadata->mCommonMetadata.version();
  15562  }
  15563 
  15564  QM_TRY(OkIf(mMetadata->mCommonMetadata.version() <= mRequestedVersion),
  15565         NS_ERROR_DOM_INDEXEDDB_VERSION_ERR);
  15566 
  15567  if (!fileManager->Initialized()) {
  15568    QM_TRY(MOZ_TO_RESULT(fileManager->Init(
  15569        fmDirectory, mMetadata->mCommonMetadata.version(), *connection)));
  15570 
  15571    idm->AddFileManager(fileManager.clonePtr());
  15572  }
  15573 
  15574  mFileManager = std::move(fileManager);
  15575 
  15576  // Must close connection before dispatching otherwise we might race with the
  15577  // connection thread which needs to open the same database.
  15578  asph.Unregister();
  15579 
  15580  MOZ_ALWAYS_SUCCEEDS(connection->Close());
  15581 
  15582  SleepIfEnabled(
  15583      StaticPrefs::dom_indexedDB_databaseInitialization_pauseOnIOThreadMs());
  15584 
  15585  // Must set mState before dispatching otherwise we will race with the owning
  15586  // thread.
  15587  mState = (mMetadata->mCommonMetadata.version() == mRequestedVersion)
  15588               ? State::SendingResults
  15589               : State::BeginVersionChange;
  15590 
  15591  QM_TRY(MOZ_TO_RESULT(
  15592      DispatchThisAfterProcessingCurrentEvent(mOwningEventTarget)));
  15593 
  15594  return NS_OK;
  15595 }
  15596 
  15597 nsresult OpenDatabaseOp::LoadDatabaseInformation(
  15598    mozIStorageConnection& aConnection) {
  15599  AssertIsOnIOThread();
  15600  MOZ_ASSERT(mMetadata);
  15601 
  15602  {
  15603    // Load version information.
  15604    QM_TRY_INSPECT(
  15605        const auto& stmt,
  15606        CreateAndExecuteSingleStepStatement<
  15607            SingleStepResult::ReturnNullIfNoResult>(
  15608            aConnection, "SELECT name, origin, version FROM database"_ns));
  15609 
  15610    QM_TRY(OkIf(stmt), NS_ERROR_FILE_CORRUPTED);
  15611 
  15612    QM_TRY_INSPECT(const auto& databaseName, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
  15613                                                 nsString, stmt, GetString, 0));
  15614 
  15615    QM_TRY(OkIf(mCommonParams.metadata().name() == databaseName),
  15616           NS_ERROR_FILE_CORRUPTED);
  15617 
  15618    QM_TRY_INSPECT(const auto& origin, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
  15619                                           nsCString, stmt, GetUTF8String, 1));
  15620 
  15621    // We can't just compare these strings directly. See bug 1339081 comment 69.
  15622    QM_TRY(OkIf(QuotaManager::AreOriginsEqualOnDisk(mOriginMetadata.mOrigin,
  15623                                                    origin)),
  15624           NS_ERROR_FILE_CORRUPTED);
  15625 
  15626    QM_TRY_INSPECT(const int64_t& version,
  15627                   MOZ_TO_RESULT_INVOKE_MEMBER(stmt, GetInt64, 2));
  15628 
  15629    mMetadata->mCommonMetadata.version() = uint64_t(version);
  15630  }
  15631 
  15632  ObjectStoreTable& objectStores = mMetadata->mObjectStores;
  15633 
  15634  QM_TRY_INSPECT(
  15635      const auto& lastObjectStoreId,
  15636      ([&aConnection,
  15637        &objectStores]() -> mozilla::Result<IndexOrObjectStoreId, nsresult> {
  15638        // Load object store names and ids.
  15639        QM_TRY_INSPECT(
  15640            const auto& stmt,
  15641            MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
  15642                nsCOMPtr<mozIStorageStatement>, aConnection, CreateStatement,
  15643                "SELECT id, auto_increment, name, key_path "
  15644                "FROM object_store"_ns));
  15645 
  15646        IndexOrObjectStoreId lastObjectStoreId = 0;
  15647 
  15648        QM_TRY(CollectWhileHasResult(
  15649            *stmt,
  15650            [&lastObjectStoreId, &objectStores,
  15651             usedIds = Maybe<nsTHashSet<uint64_t>>{},
  15652             usedNames = Maybe<nsTHashSet<nsString>>{}](
  15653                auto& stmt) mutable -> mozilla::Result<Ok, nsresult> {
  15654              QM_TRY_INSPECT(const IndexOrObjectStoreId& objectStoreId,
  15655                             MOZ_TO_RESULT_INVOKE_MEMBER(stmt, GetInt64, 0));
  15656 
  15657              if (!usedIds) {
  15658                usedIds.emplace();
  15659              }
  15660 
  15661              QM_TRY(OkIf(objectStoreId > 0), Err(NS_ERROR_FILE_CORRUPTED));
  15662              QM_TRY(OkIf(!usedIds.ref().Contains(objectStoreId)),
  15663                     Err(NS_ERROR_FILE_CORRUPTED));
  15664 
  15665              QM_TRY(OkIf(usedIds.ref().Insert(objectStoreId, fallible)),
  15666                     Err(NS_ERROR_OUT_OF_MEMORY));
  15667 
  15668              nsString name;
  15669              QM_TRY(MOZ_TO_RESULT(stmt.GetString(2, name)));
  15670 
  15671              if (!usedNames) {
  15672                usedNames.emplace();
  15673              }
  15674 
  15675              QM_TRY(OkIf(!usedNames.ref().Contains(name)),
  15676                     Err(NS_ERROR_FILE_CORRUPTED));
  15677 
  15678              QM_TRY(OkIf(usedNames.ref().Insert(name, fallible)),
  15679                     Err(NS_ERROR_OUT_OF_MEMORY));
  15680 
  15681              ObjectStoreMetadata commonMetadata;
  15682              commonMetadata.id() = objectStoreId;
  15683              commonMetadata.name() = std::move(name);
  15684 
  15685              QM_TRY_INSPECT(
  15686                  const int32_t& columnType,
  15687                  MOZ_TO_RESULT_INVOKE_MEMBER(stmt, GetTypeOfIndex, 3));
  15688 
  15689              if (columnType == mozIStorageStatement::VALUE_TYPE_NULL) {
  15690                commonMetadata.keyPath() = KeyPath(0);
  15691              } else {
  15692                MOZ_ASSERT(columnType == mozIStorageStatement::VALUE_TYPE_TEXT);
  15693 
  15694                nsString keyPathSerialization;
  15695                QM_TRY(MOZ_TO_RESULT(stmt.GetString(3, keyPathSerialization)));
  15696 
  15697                commonMetadata.keyPath() =
  15698                    KeyPath::DeserializeFromString(keyPathSerialization);
  15699                QM_TRY(OkIf(commonMetadata.keyPath().IsValid()),
  15700                       Err(NS_ERROR_FILE_CORRUPTED));
  15701              }
  15702 
  15703              QM_TRY_INSPECT(const int64_t& nextAutoIncrementId,
  15704                             MOZ_TO_RESULT_INVOKE_MEMBER(stmt, GetInt64, 1));
  15705 
  15706              commonMetadata.autoIncrement() = !!nextAutoIncrementId;
  15707 
  15708              QM_TRY(OkIf(objectStores.InsertOrUpdate(
  15709                         objectStoreId,
  15710                         MakeSafeRefPtr<FullObjectStoreMetadata>(
  15711                             std::move(commonMetadata),
  15712                             FullObjectStoreMetadata::AutoIncrementIds{
  15713                                 nextAutoIncrementId, nextAutoIncrementId}),
  15714                         fallible)),
  15715                     Err(NS_ERROR_OUT_OF_MEMORY));
  15716 
  15717              lastObjectStoreId = std::max(lastObjectStoreId, objectStoreId);
  15718 
  15719              return Ok{};
  15720            }));
  15721 
  15722        return lastObjectStoreId;
  15723      }()));
  15724 
  15725  QM_TRY_INSPECT(
  15726      const auto& lastIndexId,
  15727      ([this, &aConnection,
  15728        &objectStores]() -> mozilla::Result<IndexOrObjectStoreId, nsresult> {
  15729        // Load index information
  15730        QM_TRY_INSPECT(
  15731            const auto& stmt,
  15732            MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
  15733                nsCOMPtr<mozIStorageStatement>, aConnection, CreateStatement,
  15734                "SELECT "
  15735                "id, object_store_id, name, key_path, "
  15736                "unique_index, multientry, "
  15737                "locale, is_auto_locale "
  15738                "FROM object_store_index"_ns));
  15739 
  15740        IndexOrObjectStoreId lastIndexId = 0;
  15741 
  15742        QM_TRY(CollectWhileHasResult(
  15743            *stmt,
  15744            [this, &lastIndexId, &objectStores, &aConnection,
  15745             usedIds = Maybe<nsTHashSet<uint64_t>>{},
  15746             usedNames = Maybe<nsTHashSet<nsString>>{}](
  15747                auto& stmt) mutable -> mozilla::Result<Ok, nsresult> {
  15748              QM_TRY_INSPECT(const IndexOrObjectStoreId& objectStoreId,
  15749                             MOZ_TO_RESULT_INVOKE_MEMBER(stmt, GetInt64, 1));
  15750 
  15751              // XXX Why does this return NS_ERROR_OUT_OF_MEMORY if we don't
  15752              // know the object store id?
  15753 
  15754              auto objectStoreMetadata = objectStores.Lookup(objectStoreId);
  15755              QM_TRY(OkIf(static_cast<bool>(objectStoreMetadata)),
  15756                     Err(NS_ERROR_OUT_OF_MEMORY));
  15757 
  15758              MOZ_ASSERT((*objectStoreMetadata)->mCommonMetadata.id() ==
  15759                         objectStoreId);
  15760 
  15761              IndexOrObjectStoreId indexId;
  15762              QM_TRY(MOZ_TO_RESULT(stmt.GetInt64(0, &indexId)));
  15763 
  15764              if (!usedIds) {
  15765                usedIds.emplace();
  15766              }
  15767 
  15768              QM_TRY(OkIf(indexId > 0), Err(NS_ERROR_FILE_CORRUPTED));
  15769              QM_TRY(OkIf(!usedIds.ref().Contains(indexId)),
  15770                     Err(NS_ERROR_FILE_CORRUPTED));
  15771 
  15772              QM_TRY(OkIf(usedIds.ref().Insert(indexId, fallible)),
  15773                     Err(NS_ERROR_OUT_OF_MEMORY));
  15774 
  15775              nsString name;
  15776              QM_TRY(MOZ_TO_RESULT(stmt.GetString(2, name)));
  15777 
  15778              const nsAutoString hashName =
  15779                  IntToString(indexId) + u":"_ns + name;
  15780 
  15781              if (!usedNames) {
  15782                usedNames.emplace();
  15783              }
  15784 
  15785              QM_TRY(OkIf(!usedNames.ref().Contains(hashName)),
  15786                     Err(NS_ERROR_FILE_CORRUPTED));
  15787 
  15788              QM_TRY(OkIf(usedNames.ref().Insert(hashName, fallible)),
  15789                     Err(NS_ERROR_OUT_OF_MEMORY));
  15790 
  15791              auto indexMetadata = MakeSafeRefPtr<FullIndexMetadata>();
  15792              indexMetadata->mCommonMetadata.id() = indexId;
  15793              indexMetadata->mCommonMetadata.name() = name;
  15794 
  15795 #ifdef DEBUG
  15796              {
  15797                int32_t columnType;
  15798                nsresult rv = stmt.GetTypeOfIndex(3, &columnType);
  15799                MOZ_ASSERT(NS_SUCCEEDED(rv));
  15800                MOZ_ASSERT(columnType != mozIStorageStatement::VALUE_TYPE_NULL);
  15801              }
  15802 #endif
  15803 
  15804              nsString keyPathSerialization;
  15805              QM_TRY(MOZ_TO_RESULT(stmt.GetString(3, keyPathSerialization)));
  15806 
  15807              indexMetadata->mCommonMetadata.keyPath() =
  15808                  KeyPath::DeserializeFromString(keyPathSerialization);
  15809              QM_TRY(OkIf(indexMetadata->mCommonMetadata.keyPath().IsValid()),
  15810                     Err(NS_ERROR_FILE_CORRUPTED));
  15811 
  15812              int32_t scratch;
  15813              QM_TRY(MOZ_TO_RESULT(stmt.GetInt32(4, &scratch)));
  15814 
  15815              indexMetadata->mCommonMetadata.unique() = !!scratch;
  15816 
  15817              QM_TRY(MOZ_TO_RESULT(stmt.GetInt32(5, &scratch)));
  15818 
  15819              indexMetadata->mCommonMetadata.multiEntry() = !!scratch;
  15820 
  15821              const bool localeAware = !stmt.IsNull(6);
  15822              if (localeAware) {
  15823                QM_TRY(MOZ_TO_RESULT(stmt.GetUTF8String(
  15824                    6, indexMetadata->mCommonMetadata.locale())));
  15825 
  15826                QM_TRY(MOZ_TO_RESULT(stmt.GetInt32(7, &scratch)));
  15827 
  15828                indexMetadata->mCommonMetadata.autoLocale() = !!scratch;
  15829 
  15830                // Update locale-aware indexes if necessary
  15831                const nsCString& indexedLocale =
  15832                    indexMetadata->mCommonMetadata.locale();
  15833                const bool& isAutoLocale =
  15834                    indexMetadata->mCommonMetadata.autoLocale();
  15835                const nsCString& systemLocale = mFactory->GetSystemLocale();
  15836                if (!systemLocale.IsEmpty() && isAutoLocale &&
  15837                    !indexedLocale.Equals(systemLocale)) {
  15838                  QM_TRY(MOZ_TO_RESULT(UpdateLocaleAwareIndex(
  15839                      aConnection, indexMetadata->mCommonMetadata,
  15840                      systemLocale)));
  15841                }
  15842              }
  15843 
  15844              QM_TRY(OkIf((*objectStoreMetadata)
  15845                              ->mIndexes.InsertOrUpdate(
  15846                                  indexId, std::move(indexMetadata), fallible)),
  15847                     Err(NS_ERROR_OUT_OF_MEMORY));
  15848 
  15849              lastIndexId = std::max(lastIndexId, indexId);
  15850 
  15851              return Ok{};
  15852            }));
  15853 
  15854        return lastIndexId;
  15855      }()));
  15856 
  15857  QM_TRY(OkIf(lastObjectStoreId != INT64_MAX),
  15858         NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR, IDB_REPORT_INTERNAL_ERR_LAMBDA);
  15859  QM_TRY(OkIf(lastIndexId != INT64_MAX), NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR,
  15860         IDB_REPORT_INTERNAL_ERR_LAMBDA);
  15861 
  15862  mMetadata->mNextObjectStoreId = lastObjectStoreId + 1;
  15863  mMetadata->mNextIndexId = lastIndexId + 1;
  15864 
  15865  return NS_OK;
  15866 }
  15867 
  15868 /* static */
  15869 nsresult OpenDatabaseOp::UpdateLocaleAwareIndex(
  15870    mozIStorageConnection& aConnection, const IndexMetadata& aIndexMetadata,
  15871    const nsCString& aLocale) {
  15872  const auto indexTable =
  15873      aIndexMetadata.unique() ? "unique_index_data"_ns : "index_data"_ns;
  15874 
  15875  // The parameter names are not used, parameters are bound by index only
  15876  // locally in the same function.
  15877  const nsCString readQuery = "SELECT value, object_data_key FROM "_ns +
  15878                              indexTable + " WHERE index_id = :index_id"_ns;
  15879 
  15880  QM_TRY_INSPECT(const auto& readStmt,
  15881                 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
  15882                     nsCOMPtr<mozIStorageStatement>, aConnection,
  15883                     CreateStatement, readQuery));
  15884 
  15885  QM_TRY(MOZ_TO_RESULT(readStmt->BindInt64ByIndex(0, aIndexMetadata.id())));
  15886 
  15887  QM_TRY(CollectWhileHasResult(
  15888      *readStmt,
  15889      [&aConnection, &indexTable, &aIndexMetadata, &aLocale,
  15890       writeStmt = nsCOMPtr<mozIStorageStatement>{}](
  15891          auto& readStmt) mutable -> mozilla::Result<Ok, nsresult> {
  15892        if (!writeStmt) {
  15893          QM_TRY_UNWRAP(
  15894              writeStmt,
  15895              MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
  15896                  nsCOMPtr<mozIStorageStatement>, aConnection, CreateStatement,
  15897                  "UPDATE "_ns + indexTable + "SET value_locale = :"_ns +
  15898                      kStmtParamNameValueLocale + " WHERE index_id = :"_ns +
  15899                      kStmtParamNameIndexId + " AND value = :"_ns +
  15900                      kStmtParamNameValue + " AND object_data_key = :"_ns +
  15901                      kStmtParamNameObjectDataKey));
  15902        }
  15903 
  15904        mozStorageStatementScoper scoper(writeStmt);
  15905        QM_TRY(MOZ_TO_RESULT(writeStmt->BindInt64ByName(kStmtParamNameIndexId,
  15906                                                        aIndexMetadata.id())));
  15907 
  15908        Key oldKey, objectStorePosition;
  15909        QM_TRY(MOZ_TO_RESULT(oldKey.SetFromStatement(&readStmt, 0)));
  15910        QM_TRY(MOZ_TO_RESULT(
  15911            oldKey.BindToStatement(writeStmt, kStmtParamNameValue)));
  15912 
  15913        QM_TRY_INSPECT(const auto& newSortKey,
  15914                       oldKey.ToLocaleAwareKey(aLocale));
  15915 
  15916        QM_TRY(MOZ_TO_RESULT(
  15917            newSortKey.BindToStatement(writeStmt, kStmtParamNameValueLocale)));
  15918        QM_TRY(
  15919            MOZ_TO_RESULT(objectStorePosition.SetFromStatement(&readStmt, 1)));
  15920        QM_TRY(MOZ_TO_RESULT(objectStorePosition.BindToStatement(
  15921            writeStmt, kStmtParamNameObjectDataKey)));
  15922 
  15923        QM_TRY(MOZ_TO_RESULT(writeStmt->Execute()));
  15924 
  15925        return Ok{};
  15926      }));
  15927 
  15928  // The parameter names are not used, parameters are bound by index only
  15929  // locally in the same function.
  15930  static constexpr auto metaQuery =
  15931      "UPDATE object_store_index SET "
  15932      "locale = :locale WHERE id = :id"_ns;
  15933 
  15934  QM_TRY_INSPECT(const auto& metaStmt,
  15935                 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
  15936                     nsCOMPtr<mozIStorageStatement>, aConnection,
  15937                     CreateStatement, metaQuery));
  15938 
  15939  QM_TRY(MOZ_TO_RESULT(
  15940      metaStmt->BindStringByIndex(0, NS_ConvertASCIItoUTF16(aLocale))));
  15941 
  15942  QM_TRY(MOZ_TO_RESULT(metaStmt->BindInt64ByIndex(1, aIndexMetadata.id())));
  15943 
  15944  QM_TRY(MOZ_TO_RESULT(metaStmt->Execute()));
  15945 
  15946  return NS_OK;
  15947 }
  15948 
  15949 nsresult OpenDatabaseOp::BeginVersionChange() {
  15950  AssertIsOnOwningThread();
  15951  MOZ_ASSERT(mState == State::BeginVersionChange);
  15952  MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty());
  15953  MOZ_ASSERT(mMetadata->mCommonMetadata.version() <= mRequestedVersion);
  15954  MOZ_ASSERT(!mDatabase);
  15955  MOZ_ASSERT(!mVersionChangeTransaction);
  15956 
  15957  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
  15958      IsActorDestroyed()) {
  15959    IDB_REPORT_INTERNAL_ERR();
  15960    QM_TRY(MOZ_TO_RESULT(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR));
  15961  }
  15962 
  15963  EnsureDatabaseActor();
  15964 
  15965  if (mDatabase->IsInvalidated()) {
  15966    IDB_REPORT_INTERNAL_ERR();
  15967    QM_TRY(MOZ_TO_RESULT(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR));
  15968  }
  15969 
  15970  MOZ_ASSERT(!mDatabase->IsClosed());
  15971 
  15972  DatabaseActorInfo* info;
  15973  MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(mDatabaseId.ref(), &info));
  15974 
  15975  MOZ_ASSERT(info->mLiveDatabases.contains(mDatabase.unsafeGetRawPtr()));
  15976  MOZ_ASSERT(!info->mWaitingFactoryOp);
  15977  MOZ_ASSERT(info->mMetadata == mMetadata);
  15978 
  15979  auto transaction = MakeSafeRefPtr<VersionChangeTransaction>(this);
  15980 
  15981  if (NS_WARN_IF(!transaction->CopyDatabaseMetadata())) {
  15982    return NS_ERROR_OUT_OF_MEMORY;
  15983  }
  15984 
  15985  MOZ_ASSERT(info->mMetadata != mMetadata);
  15986  mMetadata = info->mMetadata.clonePtr();
  15987 
  15988  const Maybe<uint64_t> newVersion = Some(mRequestedVersion);
  15989 
  15990  QM_TRY(MOZ_TO_RESULT(SendVersionChangeMessages(
  15991      info, mDatabase.maybeDeref(), mMetadata->mCommonMetadata.version(),
  15992      newVersion)));
  15993 
  15994  mVersionChangeTransaction = std::move(transaction);
  15995 
  15996  if (mMaybeBlockedDatabases.IsEmpty()) {
  15997    // We don't need to wait on any databases, just jump to the transaction
  15998    // pool.
  15999    WaitForTransactions();
  16000    return NS_OK;
  16001  }
  16002 
  16003  // If the actor gets destroyed, mWaitingFactoryOp will hold the last strong
  16004  // reference to us.
  16005  info->mWaitingFactoryOp = this;
  16006 
  16007  mState = State::WaitingForOtherDatabasesToClose;
  16008  return NS_OK;
  16009 }
  16010 
  16011 bool OpenDatabaseOp::AreActorsAlive() {
  16012  AssertIsOnOwningThread();
  16013  MOZ_ASSERT(mDatabase);
  16014 
  16015  return !(IsActorDestroyed() || mDatabase->IsActorDestroyed());
  16016 }
  16017 
  16018 void OpenDatabaseOp::SendBlockedNotification() {
  16019  AssertIsOnOwningThread();
  16020  MOZ_ASSERT(mState == State::WaitingForOtherDatabasesToClose);
  16021 
  16022  if (!IsActorDestroyed()) {
  16023    (void)SendBlocked(mMetadata->mCommonMetadata.version());
  16024  }
  16025 }
  16026 
  16027 nsresult OpenDatabaseOp::DispatchToWorkThread() {
  16028  AssertIsOnOwningThread();
  16029  MOZ_ASSERT(mState == State::WaitingForTransactionsToComplete);
  16030  MOZ_ASSERT(mVersionChangeTransaction);
  16031  MOZ_ASSERT(mVersionChangeTransaction->GetMode() ==
  16032             IDBTransaction::Mode::VersionChange);
  16033  MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty());
  16034 
  16035  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
  16036      IsActorDestroyed() || mDatabase->IsInvalidated()) {
  16037    IDB_REPORT_INTERNAL_ERR();
  16038    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
  16039  }
  16040 
  16041  mState = State::DatabaseWorkVersionChange;
  16042 
  16043  // Intentionally empty.
  16044  nsTArray<nsString> objectStoreNames;
  16045 
  16046  const int64_t loggingSerialNumber =
  16047      mVersionChangeTransaction->LoggingSerialNumber();
  16048  const nsID& backgroundChildLoggingId =
  16049      mVersionChangeTransaction->GetLoggingInfo()->Id();
  16050 
  16051  if (NS_WARN_IF(!mDatabase->RegisterTransaction(*mVersionChangeTransaction))) {
  16052    return NS_ERROR_OUT_OF_MEMORY;
  16053  }
  16054 
  16055  if (!gConnectionPool) {
  16056    gConnectionPool = new ConnectionPool();
  16057  }
  16058 
  16059  RefPtr<VersionChangeOp> versionChangeOp = new VersionChangeOp(this);
  16060 
  16061  uint64_t transactionId = versionChangeOp->StartOnConnectionPool(
  16062      backgroundChildLoggingId, mVersionChangeTransaction->DatabaseId(),
  16063      loggingSerialNumber, objectStoreNames,
  16064      /* aIsWriteTransaction */ true);
  16065 
  16066  mVersionChangeOp = versionChangeOp;
  16067 
  16068  mVersionChangeTransaction->NoteActiveRequest();
  16069  mVersionChangeTransaction->Init(transactionId);
  16070 
  16071  return NS_OK;
  16072 }
  16073 
  16074 nsresult OpenDatabaseOp::SendUpgradeNeeded() {
  16075  AssertIsOnOwningThread();
  16076  MOZ_ASSERT(mState == State::DatabaseWorkVersionChange);
  16077  MOZ_ASSERT(mVersionChangeTransaction);
  16078  MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty());
  16079  MOZ_ASSERT(!HasFailed());
  16080  MOZ_ASSERT_IF(!IsActorDestroyed(), mDatabase);
  16081 
  16082  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
  16083      IsActorDestroyed()) {
  16084    IDB_REPORT_INTERNAL_ERR();
  16085    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
  16086  }
  16087 
  16088  const SafeRefPtr<VersionChangeTransaction> transaction =
  16089      std::move(mVersionChangeTransaction);
  16090 
  16091  nsresult rv = EnsureDatabaseActorIsAlive();
  16092  if (NS_WARN_IF(NS_FAILED(rv))) {
  16093    return rv;
  16094  }
  16095 
  16096  // Transfer ownership to IPDL.
  16097  transaction->SetActorAlive();
  16098 
  16099  if (!mDatabase->SendPBackgroundIDBVersionChangeTransactionConstructor(
  16100          transaction.unsafeGetRawPtr(), mMetadata->mCommonMetadata.version(),
  16101          mRequestedVersion, mMetadata->mNextObjectStoreId,
  16102          mMetadata->mNextIndexId)) {
  16103    IDB_REPORT_INTERNAL_ERR();
  16104    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
  16105  }
  16106 
  16107  return NS_OK;
  16108 }
  16109 
  16110 nsresult OpenDatabaseOp::DoVersionUpdate() {
  16111  AssertIsOnIOThread();
  16112  MOZ_ASSERT(mState == State::DatabaseWorkVersionUpdate);
  16113  MOZ_ASSERT(!HasFailed());
  16114 
  16115  AUTO_PROFILER_LABEL("OpenDatabaseOp::DoVersionUpdate", DOM);
  16116 
  16117  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
  16118      !OperationMayProceed()) {
  16119    IDB_REPORT_INTERNAL_ERR();
  16120    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
  16121  }
  16122 
  16123  mFileManager->UpdateDatabaseVersion(mRequestedVersion);
  16124 
  16125  mState = State::SendingResults;
  16126 
  16127  QM_TRY(MOZ_TO_RESULT(
  16128      DispatchThisAfterProcessingCurrentEvent(mOwningEventTarget)));
  16129 
  16130  return NS_OK;
  16131 }
  16132 
  16133 void OpenDatabaseOp::SendResults() {
  16134  AssertIsOnOwningThread();
  16135  MOZ_ASSERT(mState == State::SendingResults);
  16136  MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty());
  16137  MOZ_ASSERT_IF(!HasFailed(), !mVersionChangeTransaction);
  16138 
  16139  if (mCompleteCallback) {
  16140    auto completeCallback = std::move(mCompleteCallback);
  16141    completeCallback();
  16142  }
  16143 
  16144  DebugOnly<DatabaseActorInfo*> info = nullptr;
  16145  MOZ_ASSERT_IF(mDatabaseId.isSome() && gLiveDatabaseHashtable &&
  16146                    gLiveDatabaseHashtable->Get(mDatabaseId.ref(), &info),
  16147                !info->mWaitingFactoryOp);
  16148 
  16149  if (mVersionChangeTransaction) {
  16150    MOZ_ASSERT(HasFailed());
  16151 
  16152    mVersionChangeTransaction->Abort(ResultCode(), /* aForce */ true);
  16153    mVersionChangeTransaction = nullptr;
  16154  }
  16155 
  16156  if (IsActorDestroyed()) {
  16157    SetFailureCodeIfUnset(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  16158  } else {
  16159    FactoryRequestResponse response;
  16160 
  16161    if (!HasFailed()) {
  16162      // If we just successfully completed a versionchange operation then we
  16163      // need to update the version in our metadata.
  16164      mMetadata->mCommonMetadata.version() = mRequestedVersion;
  16165 
  16166      nsresult rv = EnsureDatabaseActorIsAlive();
  16167      if (NS_SUCCEEDED(rv)) {
  16168        // We successfully opened a database so use its actor as the success
  16169        // result for this request.
  16170 
  16171        // XXX OpenDatabaseRequestResponse stores a raw pointer, can this be
  16172        // avoided?
  16173        response = OpenDatabaseRequestResponse{
  16174            WrapNotNull(mDatabase.unsafeGetRawPtr())};
  16175      } else {
  16176        response = ClampResultCode(rv);
  16177 #ifdef DEBUG
  16178        SetFailureCode(response.get_nsresult());
  16179 #endif
  16180      }
  16181    } else {
  16182 #ifdef DEBUG
  16183      // If something failed then our metadata pointer is now bad. No one should
  16184      // ever touch it again though so just null it out in DEBUG builds to make
  16185      // sure we find such cases.
  16186      mMetadata = nullptr;
  16187 #endif
  16188      response = ClampResultCode(ResultCode());
  16189    }
  16190 
  16191    (void)PBackgroundIDBFactoryRequestParent::Send__delete__(this, response);
  16192  }
  16193 
  16194  if (mDatabase) {
  16195    MOZ_ASSERT(!mDirectoryLockHandle);
  16196 
  16197    if (HasFailed()) {
  16198      mDatabase->Invalidate();
  16199    }
  16200 
  16201    // Make sure to release the database on this thread.
  16202    mDatabase = nullptr;
  16203 
  16204    CleanupMetadata();
  16205  } else if (mDirectoryLockHandle) {
  16206    // ConnectionClosedCallback will call CleanupMetadata().
  16207    nsCOMPtr<nsIRunnable> callback = NewRunnableMethod(
  16208        "dom::indexedDB::OpenDatabaseOp::ConnectionClosedCallback", this,
  16209        &OpenDatabaseOp::ConnectionClosedCallback);
  16210 
  16211    RefPtr<WaitForTransactionsHelper> helper =
  16212        new WaitForTransactionsHelper(mDatabaseId.ref(), callback);
  16213    helper->WaitForTransactions();
  16214  } else {
  16215    CleanupMetadata();
  16216  }
  16217 
  16218  FinishSendResults();
  16219 }
  16220 
  16221 void OpenDatabaseOp::ConnectionClosedCallback() {
  16222  AssertIsOnOwningThread();
  16223  MOZ_ASSERT(HasFailed());
  16224  MOZ_ASSERT(mDirectoryLockHandle);
  16225 
  16226  {
  16227    auto destroyingDirectoryLockHandle = std::move(mDirectoryLockHandle);
  16228  }
  16229 
  16230  CleanupMetadata();
  16231 }
  16232 
  16233 void OpenDatabaseOp::EnsureDatabaseActor() {
  16234  AssertIsOnOwningThread();
  16235  MOZ_ASSERT(mState == State::BeginVersionChange ||
  16236             mState == State::DatabaseWorkVersionChange ||
  16237             mState == State::SendingResults);
  16238  MOZ_ASSERT(!HasFailed());
  16239  MOZ_ASSERT(mDatabaseFilePath.isSome());
  16240  MOZ_ASSERT(!IsActorDestroyed());
  16241 
  16242  if (mDatabase) {
  16243    return;
  16244  }
  16245 
  16246  MOZ_ASSERT(mMetadata->mDatabaseId.IsEmpty());
  16247  mMetadata->mDatabaseId = mDatabaseId.ref();
  16248 
  16249  MOZ_ASSERT(mMetadata->mFilePath.IsEmpty());
  16250  mMetadata->mFilePath = mDatabaseFilePath.ref();
  16251 
  16252  DatabaseActorInfo* info;
  16253  if (gLiveDatabaseHashtable->Get(mDatabaseId.ref(), &info)) {
  16254    AssertMetadataConsistency(*info->mMetadata);
  16255    mMetadata = info->mMetadata.clonePtr();
  16256  }
  16257 
  16258  Maybe<const CipherKey> maybeKey =
  16259      mInPrivateBrowsing ? mFileManager->MutableCipherKeyManagerRef().Get()
  16260                         : Nothing();
  16261 
  16262  MOZ_RELEASE_ASSERT(mInPrivateBrowsing == maybeKey.isSome());
  16263 
  16264  const bool directoryLockInvalidated = mDirectoryLockHandle->Invalidated();
  16265 
  16266  // XXX Shouldn't Manager() return already_AddRefed when
  16267  // PBackgroundIDBFactoryParent is declared refcounted?
  16268  mDatabase = MakeSafeRefPtr<Database>(
  16269      SafeRefPtr{static_cast<Factory*>(Manager()),
  16270                 AcquireStrongRefFromRawPtr{}},
  16271      mCommonParams.principalInfo(), mContentParentId, mOriginMetadata,
  16272      mTelemetryId, mMetadata.clonePtr(), mFileManager.clonePtr(),
  16273      std::move(mDirectoryLockHandle), mInPrivateBrowsing, maybeKey);
  16274 
  16275  if (info) {
  16276    info->mLiveDatabases.insertBack(mDatabase.unsafeGetRawPtr());
  16277  } else {
  16278    // XXX Maybe use LookupOrInsertWith above, to avoid a second lookup here?
  16279    info = gLiveDatabaseHashtable
  16280               ->InsertOrUpdate(
  16281                   mDatabaseId.ref(),
  16282                   MakeUnique<DatabaseActorInfo>(
  16283                       mMetadata.clonePtr(),
  16284                       WrapNotNullUnchecked(mDatabase.unsafeGetRawPtr())))
  16285               .get();
  16286  }
  16287 
  16288  if (directoryLockInvalidated) {
  16289    mDatabase->Invalidate();
  16290  }
  16291 
  16292  // Balanced in Database::CleanupMetadata().
  16293  IncreaseBusyCount();
  16294 }
  16295 
  16296 nsresult OpenDatabaseOp::EnsureDatabaseActorIsAlive() {
  16297  AssertIsOnOwningThread();
  16298  MOZ_ASSERT(mState == State::DatabaseWorkVersionChange ||
  16299             mState == State::SendingResults);
  16300  MOZ_ASSERT(!HasFailed());
  16301  MOZ_ASSERT(!IsActorDestroyed());
  16302 
  16303  EnsureDatabaseActor();
  16304 
  16305  if (mDatabase->IsActorAlive()) {
  16306    return NS_OK;
  16307  }
  16308 
  16309  auto* const factory = static_cast<Factory*>(Manager());
  16310 
  16311  QM_TRY_INSPECT(const auto& spec, MetadataToSpec());
  16312 
  16313  mDatabase->SetActorAlive();
  16314 
  16315  if (!factory->SendPBackgroundIDBDatabaseConstructor(
  16316          mDatabase.unsafeGetRawPtr(), spec, WrapNotNull(this))) {
  16317    IDB_REPORT_INTERNAL_ERR();
  16318    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
  16319  }
  16320 
  16321  if (mDatabase->IsInvalidated()) {
  16322    (void)mDatabase->SendInvalidate();
  16323  }
  16324 
  16325  return NS_OK;
  16326 }
  16327 
  16328 Result<DatabaseSpec, nsresult> OpenDatabaseOp::MetadataToSpec() const {
  16329  AssertIsOnOwningThread();
  16330  MOZ_ASSERT(mMetadata);
  16331 
  16332  DatabaseSpec spec;
  16333  spec.metadata() = mMetadata->mCommonMetadata;
  16334 
  16335  QM_TRY_UNWRAP(spec.objectStores(),
  16336                TransformIntoNewArrayAbortOnErr(
  16337                    mMetadata->mObjectStores,
  16338                    [](const auto& objectStoreEntry)
  16339                        -> mozilla::Result<ObjectStoreSpec, nsresult> {
  16340                      FullObjectStoreMetadata* metadata =
  16341                          objectStoreEntry.GetWeak();
  16342                      MOZ_ASSERT(objectStoreEntry.GetKey());
  16343                      MOZ_ASSERT(metadata);
  16344 
  16345                      ObjectStoreSpec objectStoreSpec;
  16346                      objectStoreSpec.metadata() = metadata->mCommonMetadata;
  16347 
  16348                      QM_TRY_UNWRAP(auto indexes,
  16349                                    TransformIntoNewArray(
  16350                                        metadata->mIndexes,
  16351                                        [](const auto& indexEntry) {
  16352                                          FullIndexMetadata* indexMetadata =
  16353                                              indexEntry.GetWeak();
  16354                                          MOZ_ASSERT(indexEntry.GetKey());
  16355                                          MOZ_ASSERT(indexMetadata);
  16356 
  16357                                          return indexMetadata->mCommonMetadata;
  16358                                        },
  16359                                        fallible));
  16360 
  16361                      objectStoreSpec.indexes() = std::move(indexes);
  16362 
  16363                      return objectStoreSpec;
  16364                    },
  16365                    fallible));
  16366 
  16367  return spec;
  16368 }
  16369 
  16370 #ifdef DEBUG
  16371 
  16372 void OpenDatabaseOp::AssertMetadataConsistency(
  16373    const FullDatabaseMetadata& aMetadata) {
  16374  AssertIsOnBackgroundThread();
  16375 
  16376  const FullDatabaseMetadata& thisDB = *mMetadata;
  16377  const FullDatabaseMetadata& otherDB = aMetadata;
  16378 
  16379  MOZ_ASSERT(&thisDB != &otherDB);
  16380 
  16381  MOZ_ASSERT(thisDB.mCommonMetadata.name() == otherDB.mCommonMetadata.name());
  16382  MOZ_ASSERT(thisDB.mCommonMetadata.version() ==
  16383             otherDB.mCommonMetadata.version());
  16384  MOZ_ASSERT(thisDB.mCommonMetadata.persistenceType() ==
  16385             otherDB.mCommonMetadata.persistenceType());
  16386  MOZ_ASSERT(thisDB.mDatabaseId == otherDB.mDatabaseId);
  16387  MOZ_ASSERT(thisDB.mFilePath == otherDB.mFilePath);
  16388 
  16389  // |thisDB| reflects the latest objectStore and index ids that have committed
  16390  // to disk. The in-memory metadata |otherDB| keeps track of objectStores and
  16391  // indexes that were created and then removed as well, so the next ids for
  16392  // |otherDB| may be higher than for |thisDB|.
  16393  MOZ_ASSERT(thisDB.mNextObjectStoreId <= otherDB.mNextObjectStoreId);
  16394  MOZ_ASSERT(thisDB.mNextIndexId <= otherDB.mNextIndexId);
  16395 
  16396  MOZ_ASSERT(thisDB.mObjectStores.Count() == otherDB.mObjectStores.Count());
  16397 
  16398  for (const auto& thisObjectStore : thisDB.mObjectStores.Values()) {
  16399    MOZ_ASSERT(thisObjectStore);
  16400    MOZ_ASSERT(!thisObjectStore->mDeleted);
  16401 
  16402    auto otherObjectStore = MatchMetadataNameOrId(
  16403        otherDB.mObjectStores, thisObjectStore->mCommonMetadata.id());
  16404    MOZ_ASSERT(otherObjectStore);
  16405 
  16406    MOZ_ASSERT(thisObjectStore != &otherObjectStore.ref());
  16407 
  16408    MOZ_ASSERT(thisObjectStore->mCommonMetadata.id() ==
  16409               otherObjectStore->mCommonMetadata.id());
  16410    MOZ_ASSERT(thisObjectStore->mCommonMetadata.name() ==
  16411               otherObjectStore->mCommonMetadata.name());
  16412    MOZ_ASSERT(thisObjectStore->mCommonMetadata.autoIncrement() ==
  16413               otherObjectStore->mCommonMetadata.autoIncrement());
  16414    MOZ_ASSERT(thisObjectStore->mCommonMetadata.keyPath() ==
  16415               otherObjectStore->mCommonMetadata.keyPath());
  16416    // mNextAutoIncrementId and mCommittedAutoIncrementId may be modified
  16417    // concurrently with this OpenOp, so it is not possible to assert equality
  16418    // here. It's also possible that we've written the new ids to disk but not
  16419    // yet updated the in-memory count.
  16420    // TODO The first part of the comment should probably be rephrased. I think
  16421    // it still applies but it sounds as if this were thread-unsafe like it was
  16422    // before, which isn't true anymore.
  16423    {
  16424      const auto&& thisAutoIncrementIds =
  16425          thisObjectStore->mAutoIncrementIds.Lock();
  16426      const auto&& otherAutoIncrementIds =
  16427          otherObjectStore->mAutoIncrementIds.Lock();
  16428 
  16429      MOZ_ASSERT(thisAutoIncrementIds->next <= otherAutoIncrementIds->next);
  16430      MOZ_ASSERT(
  16431          thisAutoIncrementIds->committed <= otherAutoIncrementIds->committed ||
  16432          thisAutoIncrementIds->committed == otherAutoIncrementIds->next);
  16433    }
  16434    MOZ_ASSERT(!otherObjectStore->mDeleted);
  16435 
  16436    MOZ_ASSERT(thisObjectStore->mIndexes.Count() ==
  16437               otherObjectStore->mIndexes.Count());
  16438 
  16439    for (const auto& thisIndex : thisObjectStore->mIndexes.Values()) {
  16440      MOZ_ASSERT(thisIndex);
  16441      MOZ_ASSERT(!thisIndex->mDeleted);
  16442 
  16443      auto otherIndex = MatchMetadataNameOrId(otherObjectStore->mIndexes,
  16444                                              thisIndex->mCommonMetadata.id());
  16445      MOZ_ASSERT(otherIndex);
  16446 
  16447      MOZ_ASSERT(thisIndex != &otherIndex.ref());
  16448 
  16449      MOZ_ASSERT(thisIndex->mCommonMetadata.id() ==
  16450                 otherIndex->mCommonMetadata.id());
  16451      MOZ_ASSERT(thisIndex->mCommonMetadata.name() ==
  16452                 otherIndex->mCommonMetadata.name());
  16453      MOZ_ASSERT(thisIndex->mCommonMetadata.keyPath() ==
  16454                 otherIndex->mCommonMetadata.keyPath());
  16455      MOZ_ASSERT(thisIndex->mCommonMetadata.unique() ==
  16456                 otherIndex->mCommonMetadata.unique());
  16457      MOZ_ASSERT(thisIndex->mCommonMetadata.multiEntry() ==
  16458                 otherIndex->mCommonMetadata.multiEntry());
  16459      MOZ_ASSERT(!otherIndex->mDeleted);
  16460    }
  16461  }
  16462 }
  16463 
  16464 #endif  // DEBUG
  16465 
  16466 nsresult OpenDatabaseOp::VersionChangeOp::DoDatabaseWork(
  16467    DatabaseConnection* aConnection) {
  16468  MOZ_ASSERT(aConnection);
  16469  aConnection->AssertIsOnConnectionThread();
  16470  MOZ_ASSERT(mOpenDatabaseOp);
  16471  MOZ_ASSERT(mOpenDatabaseOp->mState == State::DatabaseWorkVersionChange);
  16472 
  16473  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
  16474      !OperationMayProceed()) {
  16475    IDB_REPORT_INTERNAL_ERR();
  16476    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
  16477  }
  16478 
  16479  AUTO_PROFILER_LABEL("OpenDatabaseOp::VersionChangeOp::DoDatabaseWork", DOM);
  16480 
  16481  IDB_LOG_MARK_PARENT_TRANSACTION("Beginning database work", "DB Start",
  16482                                  IDB_LOG_ID_STRING(mBackgroundChildLoggingId),
  16483                                  mTransactionLoggingSerialNumber);
  16484 
  16485  Transaction().SetActiveOnConnectionThread();
  16486 
  16487  QM_TRY(MOZ_TO_RESULT(
  16488      aConnection->BeginWriteTransaction(Transaction().GetDurability())));
  16489 
  16490  // The parameter names are not used, parameters are bound by index only
  16491  // locally in the same function.
  16492  QM_TRY(MOZ_TO_RESULT(aConnection->ExecuteCachedStatement(
  16493      "UPDATE database SET version = :version;"_ns,
  16494      ([&self = *this](
  16495           mozIStorageStatement& updateStmt) -> mozilla::Result<Ok, nsresult> {
  16496        QM_TRY(MOZ_TO_RESULT(
  16497            updateStmt.BindInt64ByIndex(0, int64_t(self.mRequestedVersion))));
  16498 
  16499        return Ok{};
  16500      }))));
  16501 
  16502  return NS_OK;
  16503 }
  16504 
  16505 nsresult OpenDatabaseOp::VersionChangeOp::SendSuccessResult() {
  16506  AssertIsOnOwningThread();
  16507  MOZ_ASSERT(mOpenDatabaseOp);
  16508  MOZ_ASSERT(mOpenDatabaseOp->mState == State::DatabaseWorkVersionChange);
  16509  MOZ_ASSERT(mOpenDatabaseOp->mVersionChangeOp == this);
  16510 
  16511  nsresult rv = mOpenDatabaseOp->SendUpgradeNeeded();
  16512  if (NS_WARN_IF(NS_FAILED(rv))) {
  16513    return rv;
  16514  }
  16515 
  16516  return NS_OK;
  16517 }
  16518 
  16519 bool OpenDatabaseOp::VersionChangeOp::SendFailureResult(nsresult aResultCode) {
  16520  AssertIsOnOwningThread();
  16521  MOZ_ASSERT(mOpenDatabaseOp);
  16522  MOZ_ASSERT(mOpenDatabaseOp->mState == State::DatabaseWorkVersionChange);
  16523  MOZ_ASSERT(mOpenDatabaseOp->mVersionChangeOp == this);
  16524 
  16525  mOpenDatabaseOp->SetFailureCode(aResultCode);
  16526  mOpenDatabaseOp->mState = State::SendingResults;
  16527 
  16528  MOZ_ALWAYS_SUCCEEDS(mOpenDatabaseOp->Run());
  16529 
  16530  return false;
  16531 }
  16532 
  16533 void OpenDatabaseOp::VersionChangeOp::Cleanup() {
  16534  AssertIsOnOwningThread();
  16535  MOZ_ASSERT(mOpenDatabaseOp);
  16536  MOZ_ASSERT(mOpenDatabaseOp->mVersionChangeOp == this);
  16537 
  16538  mOpenDatabaseOp->mVersionChangeOp = nullptr;
  16539  mOpenDatabaseOp = nullptr;
  16540 
  16541 #ifdef DEBUG
  16542  // A bit hacky but the VersionChangeOp is not generated in response to a
  16543  // child request like most other database operations. Do this to make our
  16544  // assertions happy.
  16545  //
  16546  // XXX: Depending on timing, in most cases, NoteActorDestroyed will not have
  16547  // been destroyed before, but in some cases it has. This should be reworked in
  16548  // a way this hack is not necessary. There are also several similar cases in
  16549  // other *Op classes.
  16550  if (!IsActorDestroyed()) {
  16551    NoteActorDestroyed();
  16552  }
  16553 #endif
  16554 
  16555  TransactionDatabaseOperationBase::Cleanup();
  16556 }
  16557 
  16558 void DeleteDatabaseOp::LoadPreviousVersion(nsIFile& aDatabaseFile) {
  16559  AssertIsOnIOThread();
  16560  MOZ_ASSERT(mState == State::DatabaseWorkOpen);
  16561  MOZ_ASSERT(!mPreviousVersion);
  16562 
  16563  AUTO_PROFILER_LABEL("DeleteDatabaseOp::LoadPreviousVersion", DOM);
  16564 
  16565  nsresult rv;
  16566 
  16567  nsCOMPtr<mozIStorageService> ss =
  16568      do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
  16569  if (NS_WARN_IF(NS_FAILED(rv))) {
  16570    return;
  16571  }
  16572 
  16573  IndexedDatabaseManager* const idm = IndexedDatabaseManager::Get();
  16574  MOZ_ASSERT(idm);
  16575 
  16576  const PersistenceType persistenceType =
  16577      mCommonParams.metadata().persistenceType();
  16578  const nsAString& databaseName = mCommonParams.metadata().name();
  16579 
  16580  SafeRefPtr<DatabaseFileManager> fileManager = idm->GetFileManager(
  16581      persistenceType, mOriginMetadata.mOrigin, databaseName);
  16582 
  16583  if (!fileManager) {
  16584    fileManager = MakeSafeRefPtr<DatabaseFileManager>(
  16585        persistenceType, mOriginMetadata, databaseName, mDatabaseId.ref(),
  16586        mDatabaseFilePath.ref(), mEnforcingQuota, mInPrivateBrowsing);
  16587  }
  16588 
  16589  const auto maybeKey =
  16590      mInPrivateBrowsing
  16591          ? Some(fileManager->MutableCipherKeyManagerRef().Ensure())
  16592          : Nothing();
  16593 
  16594  MOZ_RELEASE_ASSERT(mInPrivateBrowsing == maybeKey.isSome());
  16595 
  16596  // Pass -1 as the directoryLockId to disable quota checking, since we might
  16597  // temporarily exceed quota before deleting the database.
  16598  QM_TRY_INSPECT(const auto& dbFileUrl,
  16599                 GetDatabaseFileURL(aDatabaseFile, -1, maybeKey), QM_VOID);
  16600 
  16601  QM_TRY_UNWRAP(const NotNull<nsCOMPtr<mozIStorageConnection>> connection,
  16602                OpenDatabaseAndHandleBusy(*ss, *dbFileUrl), QM_VOID);
  16603 
  16604 #ifdef DEBUG
  16605  {
  16606    QM_TRY_INSPECT(const auto& stmt,
  16607                   CreateAndExecuteSingleStepStatement<
  16608                       SingleStepResult::ReturnNullIfNoResult>(
  16609                       *connection, "SELECT name FROM database"_ns),
  16610                   QM_VOID);
  16611 
  16612    QM_TRY(OkIf(stmt), QM_VOID);
  16613 
  16614    nsString databaseName;
  16615    rv = stmt->GetString(0, databaseName);
  16616    if (NS_WARN_IF(NS_FAILED(rv))) {
  16617      return;
  16618    }
  16619 
  16620    MOZ_ASSERT(mCommonParams.metadata().name() == databaseName);
  16621  }
  16622 #endif
  16623 
  16624  QM_TRY_INSPECT(const auto& stmt,
  16625                 CreateAndExecuteSingleStepStatement<
  16626                     SingleStepResult::ReturnNullIfNoResult>(
  16627                     *connection, "SELECT version FROM database"_ns),
  16628                 QM_VOID);
  16629 
  16630  QM_TRY(OkIf(stmt), QM_VOID);
  16631 
  16632  int64_t version;
  16633  rv = stmt->GetInt64(0, &version);
  16634  if (NS_WARN_IF(NS_FAILED(rv))) {
  16635    return;
  16636  }
  16637 
  16638  mPreviousVersion = uint64_t(version);
  16639 }
  16640 
  16641 nsresult DeleteDatabaseOp::DatabaseOpen() {
  16642  AssertIsOnOwningThread();
  16643  MOZ_ASSERT(mState == State::DatabaseOpenPending);
  16644 
  16645  nsresult rv = SendToIOThread();
  16646  if (NS_WARN_IF(NS_FAILED(rv))) {
  16647    return rv;
  16648  }
  16649 
  16650  return NS_OK;
  16651 }
  16652 
  16653 nsresult DeleteDatabaseOp::DoDatabaseWork() {
  16654  AssertIsOnIOThread();
  16655  MOZ_ASSERT(mState == State::DatabaseWorkOpen);
  16656  MOZ_ASSERT(mOriginMetadata.mPersistenceType ==
  16657             mCommonParams.metadata().persistenceType());
  16658 
  16659  AUTO_PROFILER_LABEL("DeleteDatabaseOp::DoDatabaseWork", DOM);
  16660 
  16661  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
  16662      !OperationMayProceed()) {
  16663    IDB_REPORT_INTERNAL_ERR();
  16664    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
  16665  }
  16666 
  16667  const nsAString& databaseName = mCommonParams.metadata().name();
  16668 
  16669  QuotaManager* const quotaManager = QuotaManager::Get();
  16670  MOZ_ASSERT(quotaManager);
  16671 
  16672  QM_TRY_UNWRAP(auto directory,
  16673                quotaManager->GetOriginDirectory(mOriginMetadata));
  16674 
  16675  QM_TRY(MOZ_TO_RESULT(
  16676      directory->Append(NS_LITERAL_STRING_FROM_CSTRING(IDB_DIRECTORY_NAME))));
  16677 
  16678  QM_TRY_UNWRAP(mDatabaseDirectoryPath, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
  16679                                            nsString, directory, GetPath));
  16680 
  16681  mDatabaseFilenameBase =
  16682      GetDatabaseFilenameBase(databaseName, mOriginMetadata.mIsPrivate);
  16683 
  16684  QM_TRY_INSPECT(
  16685      const auto& dbFile,
  16686      CloneFileAndAppend(*directory, mDatabaseFilenameBase + kSQLiteSuffix));
  16687 
  16688 #ifdef DEBUG
  16689  {
  16690    QM_TRY_INSPECT(
  16691        const auto& databaseFilePath,
  16692        MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsString, dbFile, GetPath));
  16693 
  16694    MOZ_ASSERT(databaseFilePath == mDatabaseFilePath.ref());
  16695  }
  16696 #endif
  16697 
  16698  QM_TRY_INSPECT(const bool& exists,
  16699                 MOZ_TO_RESULT_INVOKE_MEMBER(dbFile, Exists));
  16700 
  16701  if (exists) {
  16702    // Parts of this function may fail but that shouldn't prevent us from
  16703    // deleting the file eventually.
  16704    LoadPreviousVersion(*dbFile);
  16705 
  16706    mState = State::BeginVersionChange;
  16707  } else {
  16708    mState = State::SendingResults;
  16709  }
  16710 
  16711  QM_TRY(MOZ_TO_RESULT(
  16712      DispatchThisAfterProcessingCurrentEvent(mOwningEventTarget)));
  16713 
  16714  return NS_OK;
  16715 }
  16716 
  16717 nsresult DeleteDatabaseOp::BeginVersionChange() {
  16718  AssertIsOnOwningThread();
  16719  MOZ_ASSERT(mState == State::BeginVersionChange);
  16720  MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty());
  16721 
  16722  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
  16723      IsActorDestroyed()) {
  16724    IDB_REPORT_INTERNAL_ERR();
  16725    QM_TRY(MOZ_TO_RESULT(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR));
  16726  }
  16727 
  16728  DatabaseActorInfo* info;
  16729  if (gLiveDatabaseHashtable->Get(mDatabaseId.ref(), &info)) {
  16730    MOZ_ASSERT(!info->mWaitingFactoryOp);
  16731 
  16732    nsresult rv =
  16733        SendVersionChangeMessages(info, Nothing(), mPreviousVersion, Nothing());
  16734    if (NS_WARN_IF(NS_FAILED(rv))) {
  16735      return rv;
  16736    }
  16737 
  16738    if (!mMaybeBlockedDatabases.IsEmpty()) {
  16739      // If the actor gets destroyed, mWaitingFactoryOp will hold the last
  16740      // strong reference to us.
  16741      info->mWaitingFactoryOp = this;
  16742 
  16743      mState = State::WaitingForOtherDatabasesToClose;
  16744      return NS_OK;
  16745    }
  16746  }
  16747 
  16748  // No other databases need to be notified, just make sure that all
  16749  // transactions are complete.
  16750  WaitForTransactions();
  16751  return NS_OK;
  16752 }
  16753 
  16754 bool DeleteDatabaseOp::AreActorsAlive() {
  16755  AssertIsOnOwningThread();
  16756 
  16757  return !IsActorDestroyed();
  16758 }
  16759 
  16760 nsresult DeleteDatabaseOp::DispatchToWorkThread() {
  16761  AssertIsOnOwningThread();
  16762  MOZ_ASSERT(mState == State::WaitingForTransactionsToComplete);
  16763  MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty());
  16764 
  16765  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
  16766      IsActorDestroyed()) {
  16767    IDB_REPORT_INTERNAL_ERR();
  16768    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
  16769  }
  16770 
  16771  mState = State::DatabaseWorkVersionChange;
  16772 
  16773  RefPtr<VersionChangeOp> versionChangeOp = new VersionChangeOp(this);
  16774 
  16775  QuotaManager* const quotaManager = QuotaManager::Get();
  16776  MOZ_ASSERT(quotaManager);
  16777 
  16778  nsresult rv = quotaManager->IOThread()->Dispatch(versionChangeOp.forget(),
  16779                                                   NS_DISPATCH_NORMAL);
  16780  if (NS_WARN_IF(NS_FAILED(rv))) {
  16781    IDB_REPORT_INTERNAL_ERR();
  16782    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
  16783  }
  16784 
  16785  return NS_OK;
  16786 }
  16787 
  16788 void DeleteDatabaseOp::SendBlockedNotification() {
  16789  AssertIsOnOwningThread();
  16790  MOZ_ASSERT(mState == State::WaitingForOtherDatabasesToClose);
  16791 
  16792  if (!IsActorDestroyed()) {
  16793    (void)SendBlocked(mPreviousVersion);
  16794  }
  16795 }
  16796 
  16797 nsresult DeleteDatabaseOp::DoVersionUpdate() {
  16798  MOZ_CRASH("Not implemented because this should be unreachable.");
  16799 }
  16800 
  16801 void DeleteDatabaseOp::SendResults() {
  16802  AssertIsOnOwningThread();
  16803  MOZ_ASSERT(mState == State::SendingResults);
  16804  MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty());
  16805 
  16806  DebugOnly<DatabaseActorInfo*> info = nullptr;
  16807  MOZ_ASSERT_IF(mDatabaseId.isSome() && gLiveDatabaseHashtable &&
  16808                    gLiveDatabaseHashtable->Get(mDatabaseId.ref(), &info),
  16809                !info->mWaitingFactoryOp);
  16810 
  16811  if (!IsActorDestroyed()) {
  16812    FactoryRequestResponse response;
  16813 
  16814    if (!HasFailed()) {
  16815      response = DeleteDatabaseRequestResponse(mPreviousVersion);
  16816    } else {
  16817      response = ClampResultCode(ResultCode());
  16818    }
  16819 
  16820    (void)PBackgroundIDBFactoryRequestParent::Send__delete__(this, response);
  16821  }
  16822 
  16823  {
  16824    auto destroyingDirectoryLockHandle = std::move(mDirectoryLockHandle);
  16825  }
  16826 
  16827  CleanupMetadata();
  16828 
  16829  FinishSendResults();
  16830 }
  16831 
  16832 nsresult DeleteDatabaseOp::VersionChangeOp::RunOnIOThread() {
  16833  AssertIsOnIOThread();
  16834  MOZ_ASSERT(mDeleteDatabaseOp->mState == State::DatabaseWorkVersionChange);
  16835 
  16836  AUTO_PROFILER_LABEL("DeleteDatabaseOp::VersionChangeOp::RunOnIOThread", DOM);
  16837 
  16838  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
  16839      !OperationMayProceed()) {
  16840    IDB_REPORT_INTERNAL_ERR();
  16841    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
  16842  }
  16843 
  16844  const PersistenceType& persistenceType =
  16845      mDeleteDatabaseOp->mCommonParams.metadata().persistenceType();
  16846 
  16847  QuotaManager* quotaManager =
  16848      mDeleteDatabaseOp->mEnforcingQuota ? QuotaManager::Get() : nullptr;
  16849 
  16850  MOZ_ASSERT_IF(mDeleteDatabaseOp->mEnforcingQuota, quotaManager);
  16851 
  16852  nsCOMPtr<nsIFile> directory =
  16853      GetFileForPath(mDeleteDatabaseOp->mDatabaseDirectoryPath);
  16854  if (NS_WARN_IF(!directory)) {
  16855    IDB_REPORT_INTERNAL_ERR();
  16856    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
  16857  }
  16858 
  16859  nsresult rv = RemoveDatabaseFilesAndDirectory(
  16860      *directory, mDeleteDatabaseOp->mDatabaseFilenameBase, quotaManager,
  16861      persistenceType, mDeleteDatabaseOp->mOriginMetadata,
  16862      mDeleteDatabaseOp->mCommonParams.metadata().name());
  16863  if (NS_WARN_IF(NS_FAILED(rv))) {
  16864    return rv;
  16865  }
  16866 
  16867  rv = mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
  16868  if (NS_WARN_IF(NS_FAILED(rv))) {
  16869    return rv;
  16870  }
  16871 
  16872  return NS_OK;
  16873 }
  16874 
  16875 void DeleteDatabaseOp::VersionChangeOp::RunOnOwningThread() {
  16876  AssertIsOnOwningThread();
  16877  MOZ_ASSERT(mDeleteDatabaseOp->mState == State::DatabaseWorkVersionChange);
  16878 
  16879  const RefPtr<DeleteDatabaseOp> deleteOp = std::move(mDeleteDatabaseOp);
  16880 
  16881  if (deleteOp->IsActorDestroyed()) {
  16882    IDB_REPORT_INTERNAL_ERR();
  16883    deleteOp->SetFailureCode(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  16884  } else if (HasFailed()) {
  16885    deleteOp->SetFailureCodeIfUnset(ResultCode());
  16886  } else {
  16887    DatabaseActorInfo* info;
  16888 
  16889    // Inform all the other databases that they are now invalidated. That
  16890    // should remove the previous metadata from our table.
  16891    if (gLiveDatabaseHashtable->Get(deleteOp->mDatabaseId.ref(), &info)) {
  16892      MOZ_ASSERT(!info->mLiveDatabases.isEmpty());
  16893      MOZ_ASSERT(!info->mWaitingFactoryOp);
  16894 
  16895      nsTArray<SafeRefPtr<Database>> liveDatabases;
  16896      if (NS_WARN_IF(!liveDatabases.SetCapacity(info->mLiveDatabases.length(),
  16897                                                fallible))) {
  16898        deleteOp->SetFailureCode(NS_ERROR_OUT_OF_MEMORY);
  16899      } else {
  16900        std::transform(info->mLiveDatabases.begin(), info->mLiveDatabases.end(),
  16901                       MakeBackInserter(liveDatabases),
  16902                       [](Database* const aDatabase) -> SafeRefPtr<Database> {
  16903                         return {aDatabase, AcquireStrongRefFromRawPtr{}};
  16904                       });
  16905 
  16906 #ifdef DEBUG
  16907        // The code below should result in the deletion of |info|. Set to null
  16908        // here to make sure we find invalid uses later.
  16909        info = nullptr;
  16910 #endif
  16911 
  16912        for (const auto& database : liveDatabases) {
  16913          database->Invalidate();
  16914        }
  16915 
  16916        MOZ_ASSERT(!gLiveDatabaseHashtable->Get(deleteOp->mDatabaseId.ref()));
  16917      }
  16918    }
  16919  }
  16920 
  16921  // We hold a strong ref to the deleteOp, so it's safe to call Run() directly.
  16922 
  16923  deleteOp->mState = State::SendingResults;
  16924  MOZ_ALWAYS_SUCCEEDS(deleteOp->Run());
  16925 
  16926 #ifdef DEBUG
  16927  // A bit hacky but the DeleteDatabaseOp::VersionChangeOp is not really a
  16928  // normal database operation that is tied to an actor. Do this to make our
  16929  // assertions happy.
  16930  NoteActorDestroyed();
  16931 #endif
  16932 }
  16933 
  16934 nsresult DeleteDatabaseOp::VersionChangeOp::Run() {
  16935  nsresult rv;
  16936 
  16937  if (IsOnIOThread()) {
  16938    rv = RunOnIOThread();
  16939  } else {
  16940    RunOnOwningThread();
  16941    rv = NS_OK;
  16942  }
  16943 
  16944  if (NS_WARN_IF(NS_FAILED(rv))) {
  16945    SetFailureCodeIfUnset(rv);
  16946 
  16947    MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
  16948  }
  16949 
  16950  return NS_OK;
  16951 }
  16952 
  16953 nsresult GetDatabasesOp::DatabasesNotAvailable() {
  16954  AssertIsOnIOThread();
  16955  MOZ_ASSERT(mState == State::DatabaseWorkOpen);
  16956 
  16957  mState = State::SendingResults;
  16958 
  16959  QM_TRY(MOZ_TO_RESULT(
  16960      DispatchThisAfterProcessingCurrentEvent(mOwningEventTarget)));
  16961 
  16962  return NS_OK;
  16963 }
  16964 
  16965 nsresult GetDatabasesOp::DoDirectoryWork() {
  16966  AssertIsOnIOThread();
  16967  MOZ_ASSERT(mState == State::DirectoryWorkOpen);
  16968 
  16969  // This state (DirectoryWorkOpen) runs immediately on the I/O thread, before
  16970  // waiting for existing factory operations to complete (at which point
  16971  // DoDatabaseWork will be invoked). To match the spec, we must snapshot the
  16972  // current state of any databases that are being created (version = 0) or
  16973  // upgraded (version >= 1) now. If we only sampled these values in
  16974  // DoDatabaseWork, we would only see their post-creation/post-upgrade
  16975  // versions, which would be incorrect.
  16976 
  16977  IndexedDatabaseManager* const idm = IndexedDatabaseManager::Get();
  16978  MOZ_ASSERT(idm);
  16979 
  16980  const auto& fileManagers =
  16981      idm->GetFileManagers(mPersistenceType, mOriginMetadata.mOrigin);
  16982 
  16983  for (const auto& fileManager : fileManagers) {
  16984    auto& metadata =
  16985        mDatabaseMetadataTable.LookupOrInsert(fileManager->DatabaseFilePath());
  16986    metadata.name() = fileManager->DatabaseName();
  16987    metadata.version() = fileManager->DatabaseVersion();
  16988  }
  16989 
  16990  // Must set this before dispatching otherwise we will race with the IO thread.
  16991  mState = State::DirectoryWorkDone;
  16992 
  16993  QM_TRY(MOZ_TO_RESULT(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL)));
  16994 
  16995  return NS_OK;
  16996 }
  16997 
  16998 nsresult GetDatabasesOp::DatabaseOpen() {
  16999  AssertIsOnOwningThread();
  17000  MOZ_ASSERT(mState == State::DatabaseOpenPending);
  17001 
  17002  nsresult rv = SendToIOThread();
  17003  if (NS_WARN_IF(NS_FAILED(rv))) {
  17004    return rv;
  17005  }
  17006 
  17007  return NS_OK;
  17008 }
  17009 
  17010 nsresult GetDatabasesOp::DoDatabaseWork() {
  17011  AssertIsOnIOThread();
  17012  MOZ_ASSERT(mState == State::DatabaseWorkOpen);
  17013 
  17014  AUTO_PROFILER_LABEL("GetDatabasesOp::DoDatabaseWork", DOM);
  17015 
  17016  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
  17017      !OperationMayProceed()) {
  17018    IDB_REPORT_INTERNAL_ERR();
  17019    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
  17020  }
  17021 
  17022  QuotaManager* const quotaManager = QuotaManager::Get();
  17023  MOZ_ASSERT(quotaManager);
  17024 
  17025  {
  17026    QM_TRY_INSPECT(const bool& exists,
  17027                   quotaManager->DoesOriginDirectoryExist(mOriginMetadata));
  17028    if (!exists) {
  17029      return DatabasesNotAvailable();
  17030    }
  17031  }
  17032 
  17033  // XXX Is this really needed ?
  17034  QM_TRY(([&quotaManager,
  17035           this]() -> mozilla::Result<nsCOMPtr<nsIFile>, nsresult> {
  17036    if (mPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
  17037      QM_TRY_RETURN(quotaManager->GetOriginDirectory(mOriginMetadata));
  17038    }
  17039 
  17040    QM_TRY_RETURN(
  17041        quotaManager->GetOrCreateTemporaryOriginDirectory(mOriginMetadata));
  17042  }()
  17043                          .map([](const auto& res) { return Ok{}; })));
  17044 
  17045  {
  17046    QM_TRY_INSPECT(const bool& exists,
  17047                   quotaManager->DoesClientDirectoryExist(
  17048                       ClientMetadata{mOriginMetadata, Client::IDB}));
  17049    if (!exists) {
  17050      return DatabasesNotAvailable();
  17051    }
  17052  }
  17053 
  17054  QM_TRY_INSPECT(
  17055      const auto& clientDirectory,
  17056      ([&quotaManager, this]()
  17057           -> mozilla::Result<std::pair<nsCOMPtr<nsIFile>, bool>, nsresult> {
  17058        if (mPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
  17059          QM_TRY_RETURN(quotaManager->EnsurePersistentClientIsInitialized(
  17060              ClientMetadata{mOriginMetadata, Client::IDB}));
  17061        }
  17062 
  17063        QM_TRY_RETURN(quotaManager->EnsureTemporaryClientIsInitialized(
  17064            ClientMetadata{mOriginMetadata, Client::IDB},
  17065            /* aCreateIfNonExistent */ true));
  17066      }()
  17067                  .map([](const auto& res) { return res.first; })));
  17068 
  17069  QM_TRY_INSPECT(
  17070      (const auto& [subdirsToProcess, databaseFilenames]),
  17071      QuotaClient::GetDatabaseFilenames(*clientDirectory,
  17072                                        /* aCanceled */ Atomic<bool>{false}));
  17073 
  17074  for (const auto& databaseFilename : databaseFilenames) {
  17075    QM_TRY_INSPECT(
  17076        const auto& databaseFile,
  17077        CloneFileAndAppend(*clientDirectory, databaseFilename + kSQLiteSuffix));
  17078 
  17079    nsString path;
  17080    databaseFile->GetPath(path);
  17081 
  17082    // Use the snapshotted values from DoDirectoryWork which correctly
  17083    // snapshotted the state of any pending creations/upgrades. This does mean
  17084    // that we need to skip reporting databases that had a version of 0 at that
  17085    // time because they were still being created. In the event that any other
  17086    // creation or upgrade requests are made after our operation is created,
  17087    // this operation will block those, so it's not possible for this set of
  17088    // data to get out of sync. The snapshotting (using cached database name
  17089    // and version in DatabaseFileManager) also guarantees that we are not
  17090    // touching the SQLite database here on the QuotaManager I/O thread which
  17091    // is already open on the connection thread.
  17092 
  17093    auto metadata = mDatabaseMetadataTable.Lookup(path);
  17094    if (metadata) {
  17095      if (metadata->version() != 0) {
  17096        mDatabaseMetadataArray.AppendElement(DatabaseMetadata(
  17097            metadata->name(), metadata->version(), mPersistenceType));
  17098      }
  17099 
  17100      continue;
  17101    }
  17102 
  17103    // Since the database is not already open (there was no DatabaseFileManager
  17104    // for snapshotting in DoDirectoryWork which could provide us with the
  17105    // database name and version without needing to open the SQLite database),
  17106    // it is safe and necessary for us to open the database on this thread and
  17107    // retrieve its name and version. We do not need to worry about racing a
  17108    // database open because database opens can only be processed on this
  17109    // thread and we are performing the steps below synchronously.
  17110 
  17111    QM_TRY_INSPECT(
  17112        const auto& fmDirectory,
  17113        CloneFileAndAppend(*clientDirectory,
  17114                           databaseFilename + kFileManagerDirectoryNameSuffix));
  17115 
  17116    QM_TRY_UNWRAP(
  17117        const NotNull<nsCOMPtr<mozIStorageConnection>> connection,
  17118        CreateStorageConnection(*databaseFile, *fmDirectory, VoidString(),
  17119                                mOriginMetadata.mOrigin, mDirectoryLockId,
  17120                                TelemetryIdForFile(databaseFile), Nothing{}));
  17121 
  17122    {
  17123      // Load version information.
  17124      QM_TRY_INSPECT(const auto& stmt,
  17125                     CreateAndExecuteSingleStepStatement<
  17126                         SingleStepResult::ReturnNullIfNoResult>(
  17127                         *connection, "SELECT name, version FROM database"_ns));
  17128 
  17129      QM_TRY(OkIf(stmt), NS_ERROR_FILE_CORRUPTED);
  17130 
  17131      QM_TRY_INSPECT(
  17132          const auto& databaseName,
  17133          MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsString, stmt, GetString, 0));
  17134 
  17135      QM_TRY_INSPECT(const int64_t& version,
  17136                     MOZ_TO_RESULT_INVOKE_MEMBER(stmt, GetInt64, 1));
  17137 
  17138      mDatabaseMetadataArray.AppendElement(
  17139          DatabaseMetadata(databaseName, version, mPersistenceType));
  17140    }
  17141  }
  17142 
  17143  mState = State::SendingResults;
  17144 
  17145  QM_TRY(MOZ_TO_RESULT(
  17146      DispatchThisAfterProcessingCurrentEvent(mOwningEventTarget)));
  17147 
  17148  return NS_OK;
  17149 }
  17150 
  17151 nsresult GetDatabasesOp::BeginVersionChange() {
  17152  MOZ_CRASH("Not implemented because this should be unreachable.");
  17153 }
  17154 
  17155 bool GetDatabasesOp::AreActorsAlive() {
  17156  MOZ_CRASH("Not implemented because this should be unreachable.");
  17157 }
  17158 
  17159 void GetDatabasesOp::SendBlockedNotification() {
  17160  MOZ_CRASH("Not implemented because this should be unreachable.");
  17161 }
  17162 
  17163 nsresult GetDatabasesOp::DispatchToWorkThread() {
  17164  MOZ_CRASH("Not implemented because this should be unreachable.");
  17165 }
  17166 
  17167 nsresult GetDatabasesOp::DoVersionUpdate() {
  17168  MOZ_CRASH("Not implemented because this should be unreachable.");
  17169 }
  17170 
  17171 void GetDatabasesOp::SendResults() {
  17172  AssertIsOnOwningThread();
  17173  MOZ_ASSERT(mState == State::SendingResults);
  17174 
  17175 #ifdef DEBUG
  17176  NoteActorDestroyed();
  17177 #endif
  17178 
  17179  if (HasFailed()) {
  17180    mResolver(ClampResultCode(ResultCode()));
  17181  } else {
  17182    mResolver(mDatabaseMetadataArray);
  17183  }
  17184 
  17185  {
  17186    auto destroyingDirectoryLockHandle = std::move(mDirectoryLockHandle);
  17187  }
  17188 
  17189  CleanupMetadata();
  17190 
  17191  FinishSendResults();
  17192 }
  17193 
  17194 TransactionDatabaseOperationBase::TransactionDatabaseOperationBase(
  17195    SafeRefPtr<TransactionBase> aTransaction, const int64_t aRequestId)
  17196    : DatabaseOperationBase(aTransaction->GetLoggingInfo()->Id(),
  17197                            aTransaction->GetLoggingInfo()->NextRequestSN()),
  17198      mTransaction(WrapNotNull(std::move(aTransaction))),
  17199      mRequestId(aRequestId),
  17200      mTransactionIsAborted((*mTransaction)->IsAborted()),
  17201      mTransactionLoggingSerialNumber((*mTransaction)->LoggingSerialNumber()) {
  17202  MOZ_ASSERT(LoggingSerialNumber());
  17203 }
  17204 
  17205 TransactionDatabaseOperationBase::TransactionDatabaseOperationBase(
  17206    SafeRefPtr<TransactionBase> aTransaction, const int64_t aRequestId,
  17207    uint64_t aLoggingSerialNumber)
  17208    : DatabaseOperationBase(aTransaction->GetLoggingInfo()->Id(),
  17209                            aLoggingSerialNumber),
  17210      mTransaction(WrapNotNull(std::move(aTransaction))),
  17211      mRequestId(aRequestId),
  17212      mTransactionIsAborted((*mTransaction)->IsAborted()),
  17213      mTransactionLoggingSerialNumber((*mTransaction)->LoggingSerialNumber()) {}
  17214 
  17215 TransactionDatabaseOperationBase::~TransactionDatabaseOperationBase() {
  17216  MOZ_ASSERT(mInternalState == InternalState::Completed);
  17217  MOZ_ASSERT(!mTransaction,
  17218             "TransactionDatabaseOperationBase::Cleanup() was not called by a "
  17219             "subclass!");
  17220 }
  17221 
  17222 #ifdef DEBUG
  17223 
  17224 void TransactionDatabaseOperationBase::AssertIsOnConnectionThread() const {
  17225  (*mTransaction)->AssertIsOnConnectionThread();
  17226 }
  17227 
  17228 #endif  // DEBUG
  17229 
  17230 uint64_t TransactionDatabaseOperationBase::StartOnConnectionPool(
  17231    const nsID& aBackgroundChildLoggingId, const nsACString& aDatabaseId,
  17232    int64_t aLoggingSerialNumber, const nsTArray<nsString>& aObjectStoreNames,
  17233    bool aIsWriteTransaction) {
  17234  AssertIsOnOwningThread();
  17235  MOZ_ASSERT(mInternalState == InternalState::Initial);
  17236 
  17237  // Must set mInternalState before dispatching otherwise we will race with the
  17238  // connection thread.
  17239  mInternalState = InternalState::DatabaseWork;
  17240 
  17241  return gConnectionPool->Start(aBackgroundChildLoggingId, aDatabaseId,
  17242                                aLoggingSerialNumber, aObjectStoreNames,
  17243                                aIsWriteTransaction, this);
  17244 }
  17245 
  17246 void TransactionDatabaseOperationBase::DispatchToConnectionPool() {
  17247  AssertIsOnOwningThread();
  17248  MOZ_ASSERT(mInternalState == InternalState::Initial);
  17249 
  17250  (void)this->Run();
  17251 }
  17252 
  17253 void TransactionDatabaseOperationBase::RunOnConnectionThread() {
  17254  MOZ_ASSERT(!IsOnBackgroundThread());
  17255  MOZ_ASSERT(mInternalState == InternalState::DatabaseWork);
  17256  MOZ_ASSERT(!HasFailed());
  17257 
  17258  AUTO_PROFILER_LABEL("TransactionDatabaseOperationBase::RunOnConnectionThread",
  17259                      DOM);
  17260 
  17261  // There are several cases where we don't actually have to to any work here.
  17262 
  17263  if (mTransactionIsAborted || (*mTransaction)->IsInvalidatedOnAnyThread()) {
  17264    // This transaction is already set to be aborted or invalidated.
  17265    SetFailureCode(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
  17266  } else if (!OperationMayProceed()) {
  17267    // The operation was canceled in some way, likely because the child process
  17268    // has crashed.
  17269    IDB_REPORT_INTERNAL_ERR();
  17270    OverrideFailureCode(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  17271  } else {
  17272    Database& database = (*mTransaction)->GetMutableDatabase();
  17273 
  17274    // Here we're actually going to perform the database operation.
  17275    nsresult rv = database.EnsureConnection();
  17276    if (NS_WARN_IF(NS_FAILED(rv))) {
  17277      SetFailureCode(rv);
  17278    } else {
  17279      DatabaseConnection* connection = database.GetConnection();
  17280      MOZ_ASSERT(connection);
  17281 
  17282      auto& storageConnection = connection->MutableStorageConnection();
  17283 
  17284      AutoSetProgressHandler autoProgress;
  17285      if (mLoggingSerialNumber) {
  17286        rv = autoProgress.Register(storageConnection, this);
  17287        if (NS_WARN_IF(NS_FAILED(rv))) {
  17288          SetFailureCode(rv);
  17289        }
  17290      }
  17291 
  17292      if (NS_SUCCEEDED(rv)) {
  17293        if (mLoggingSerialNumber) {
  17294          IDB_LOG_MARK_PARENT_TRANSACTION_REQUEST(
  17295              "Beginning database work", "DB Start",
  17296              IDB_LOG_ID_STRING(mBackgroundChildLoggingId),
  17297              mTransactionLoggingSerialNumber, mLoggingSerialNumber);
  17298        }
  17299 
  17300        rv = DoDatabaseWork(connection);
  17301 
  17302        if (mLoggingSerialNumber) {
  17303          IDB_LOG_MARK_PARENT_TRANSACTION_REQUEST(
  17304              "Finished database work", "DB End",
  17305              IDB_LOG_ID_STRING(mBackgroundChildLoggingId),
  17306              mTransactionLoggingSerialNumber, mLoggingSerialNumber);
  17307        }
  17308 
  17309        if (NS_FAILED(rv)) {
  17310          SetFailureCode(rv);
  17311        }
  17312      }
  17313    }
  17314  }
  17315 
  17316  // Must set mInternalState before dispatching otherwise we will race with the
  17317  // owning thread.
  17318  if (HasPreprocessInfo()) {
  17319    mInternalState = InternalState::SendingPreprocess;
  17320  } else {
  17321    mInternalState = InternalState::SendingResults;
  17322  }
  17323 
  17324  MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
  17325 }
  17326 
  17327 bool TransactionDatabaseOperationBase::HasPreprocessInfo() { return false; }
  17328 
  17329 nsresult TransactionDatabaseOperationBase::SendPreprocessInfo() {
  17330  return NS_OK;
  17331 }
  17332 
  17333 void TransactionDatabaseOperationBase::NoteContinueReceived() {
  17334  AssertIsOnOwningThread();
  17335  MOZ_ASSERT(mInternalState == InternalState::WaitingForContinue);
  17336 
  17337  mWaitingForContinue = false;
  17338 
  17339  mInternalState = InternalState::SendingResults;
  17340 
  17341  // This TransactionDatabaseOperationBase can only be held alive by the IPDL.
  17342  // Run() can end up with clearing that last reference. So we need to add
  17343  // a self reference here.
  17344  RefPtr<TransactionDatabaseOperationBase> kungFuDeathGrip = this;
  17345 
  17346  (void)this->Run();
  17347 }
  17348 
  17349 void TransactionDatabaseOperationBase::SendToConnectionPool() {
  17350  AssertIsOnOwningThread();
  17351  MOZ_ASSERT(mInternalState == InternalState::Initial);
  17352 
  17353  // Must set mInternalState before dispatching otherwise we will race with the
  17354  // connection thread.
  17355  mInternalState = InternalState::DatabaseWork;
  17356 
  17357  gConnectionPool->StartOp((*mTransaction)->TransactionId(), this);
  17358 
  17359  (*mTransaction)->NoteActiveRequest();
  17360 }
  17361 
  17362 void TransactionDatabaseOperationBase::SendPreprocess() {
  17363  AssertIsOnOwningThread();
  17364  MOZ_ASSERT(mInternalState == InternalState::SendingPreprocess);
  17365 
  17366  SendPreprocessInfoOrResults(/* aSendPreprocessInfo */ true);
  17367 }
  17368 
  17369 void TransactionDatabaseOperationBase::SendResults() {
  17370  AssertIsOnOwningThread();
  17371  MOZ_ASSERT(mInternalState == InternalState::SendingResults);
  17372 
  17373  SendPreprocessInfoOrResults(/* aSendPreprocessInfo */ false);
  17374 }
  17375 
  17376 void TransactionDatabaseOperationBase::SendPreprocessInfoOrResults(
  17377    bool aSendPreprocessInfo) {
  17378  AssertIsOnOwningThread();
  17379  MOZ_ASSERT(mInternalState == InternalState::SendingPreprocess ||
  17380             mInternalState == InternalState::SendingResults);
  17381 
  17382  // The flag is raised only when there is no mUpdateRefcountFunction for the
  17383  // executing operation. It assume that is because the previous
  17384  // StartTransactionOp was failed to begin a write transaction and it reported
  17385  // when this operation has already jumped to the Connection thread.
  17386  MOZ_DIAGNOSTIC_ASSERT_IF(mAssumingPreviousOperationFail,
  17387                           (*mTransaction)->IsAborted());
  17388 
  17389  if (NS_WARN_IF(IsActorDestroyed())) {
  17390    // Normally we wouldn't need to send any notifications if the actor was
  17391    // already destroyed, but this can be a VersionChangeOp which needs to
  17392    // notify its parent operation (OpenDatabaseOp) about the failure.
  17393    // So SendFailureResult needs to be called even when the actor was
  17394    // destroyed.  Normal operations redundantly check if the actor was
  17395    // destroyed in SendSuccessResult and SendFailureResult, therefore it's
  17396    // ok to call it in all cases here.
  17397    if (!HasFailed()) {
  17398      IDB_REPORT_INTERNAL_ERR();
  17399      SetFailureCode(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
  17400    }
  17401  } else if ((*mTransaction)->IsInvalidated() || (*mTransaction)->IsAborted()) {
  17402    // Aborted transactions always see their requests fail with ABORT_ERR,
  17403    // even if the request succeeded or failed with another error.
  17404    OverrideFailureCode(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
  17405  }
  17406 
  17407  const nsresult rv = [aSendPreprocessInfo, this] {
  17408    if (HasFailed()) {
  17409      return ResultCode();
  17410    }
  17411    if (aSendPreprocessInfo) {
  17412      // This should not release the IPDL reference.
  17413      return SendPreprocessInfo();
  17414    }
  17415    // This may release the IPDL reference.
  17416    return SendSuccessResult();
  17417  }();
  17418 
  17419  if (NS_FAILED(rv)) {
  17420    SetFailureCodeIfUnset(rv);
  17421 
  17422    // This should definitely release the IPDL reference.
  17423    if (!SendFailureResult(rv)) {
  17424      // Abort the transaction.
  17425      (*mTransaction)->Abort(rv, /* aForce */ false);
  17426    }
  17427  }
  17428 
  17429  if (aSendPreprocessInfo && !HasFailed()) {
  17430    mInternalState = InternalState::WaitingForContinue;
  17431 
  17432    mWaitingForContinue = true;
  17433  } else {
  17434    if (mLoggingSerialNumber) {
  17435      (*mTransaction)->NoteFinishedRequest(mRequestId, ResultCode());
  17436    }
  17437 
  17438    gConnectionPool->FinishOp((*mTransaction)->TransactionId());
  17439 
  17440    Cleanup();
  17441 
  17442    mInternalState = InternalState::Completed;
  17443  }
  17444 }
  17445 
  17446 bool TransactionDatabaseOperationBase::Init(TransactionBase& aTransaction) {
  17447  AssertIsOnBackgroundThread();
  17448  MOZ_ASSERT(mInternalState == InternalState::Initial);
  17449 
  17450  return true;
  17451 }
  17452 
  17453 void TransactionDatabaseOperationBase::Cleanup() {
  17454  AssertIsOnOwningThread();
  17455  MOZ_ASSERT(mInternalState == InternalState::SendingResults);
  17456 
  17457  mTransaction.destroy();
  17458 }
  17459 
  17460 NS_IMETHODIMP
  17461 TransactionDatabaseOperationBase::Run() {
  17462  switch (mInternalState) {
  17463    case InternalState::Initial:
  17464      SendToConnectionPool();
  17465      return NS_OK;
  17466 
  17467    case InternalState::DatabaseWork:
  17468      RunOnConnectionThread();
  17469      return NS_OK;
  17470 
  17471    case InternalState::SendingPreprocess:
  17472      SendPreprocess();
  17473      return NS_OK;
  17474 
  17475    case InternalState::SendingResults:
  17476      SendResults();
  17477      return NS_OK;
  17478 
  17479    default:
  17480      MOZ_CRASH("Bad state!");
  17481  }
  17482 }
  17483 
  17484 TransactionBase::CommitOp::CommitOp(SafeRefPtr<TransactionBase> aTransaction,
  17485                                    nsresult aResultCode)
  17486    : DatabaseOperationBase(aTransaction->GetLoggingInfo()->Id(),
  17487                            aTransaction->GetLoggingInfo()->NextRequestSN()),
  17488      mTransaction(std::move(aTransaction)),
  17489      mResultCode(aResultCode) {
  17490  MOZ_ASSERT(mTransaction);
  17491  MOZ_ASSERT(LoggingSerialNumber());
  17492 }
  17493 
  17494 nsresult TransactionBase::CommitOp::WriteAutoIncrementCounts() {
  17495  MOZ_ASSERT(mTransaction);
  17496  mTransaction->AssertIsOnConnectionThread();
  17497  MOZ_ASSERT(mTransaction->GetMode() == IDBTransaction::Mode::ReadWrite ||
  17498             mTransaction->GetMode() == IDBTransaction::Mode::ReadWriteFlush ||
  17499             mTransaction->GetMode() == IDBTransaction::Mode::Cleanup ||
  17500             mTransaction->GetMode() == IDBTransaction::Mode::VersionChange);
  17501 
  17502  const nsTArray<SafeRefPtr<FullObjectStoreMetadata>>& metadataArray =
  17503      mTransaction->mModifiedAutoIncrementObjectStoreMetadataArray;
  17504 
  17505  if (!metadataArray.IsEmpty()) {
  17506    DatabaseConnection* connection =
  17507        mTransaction->GetDatabase().GetConnection();
  17508    MOZ_ASSERT(connection);
  17509 
  17510    // The parameter names are not used, parameters are bound by index only
  17511    // locally in the same function.
  17512    auto stmt = DatabaseConnection::LazyStatement(
  17513        *connection,
  17514        "UPDATE object_store "
  17515        "SET auto_increment = :auto_increment WHERE id "
  17516        "= :object_store_id;"_ns);
  17517 
  17518    for (const auto& metadata : metadataArray) {
  17519      MOZ_ASSERT(!metadata->mDeleted);
  17520 
  17521      const int64_t nextAutoIncrementId = [&metadata] {
  17522        const auto&& lockedAutoIncrementIds =
  17523            metadata->mAutoIncrementIds.Lock();
  17524        return lockedAutoIncrementIds->next;
  17525      }();
  17526 
  17527      MOZ_ASSERT(nextAutoIncrementId > 1);
  17528 
  17529      QM_TRY_INSPECT(const auto& borrowedStmt, stmt.Borrow());
  17530 
  17531      QM_TRY(MOZ_TO_RESULT(
  17532          borrowedStmt->BindInt64ByIndex(1, metadata->mCommonMetadata.id())));
  17533 
  17534      QM_TRY(MOZ_TO_RESULT(
  17535          borrowedStmt->BindInt64ByIndex(0, nextAutoIncrementId)));
  17536 
  17537      QM_TRY(MOZ_TO_RESULT(borrowedStmt->Execute()));
  17538    }
  17539  }
  17540 
  17541  return NS_OK;
  17542 }
  17543 
  17544 void TransactionBase::CommitOp::CommitOrRollbackAutoIncrementCounts() {
  17545  MOZ_ASSERT(mTransaction);
  17546  mTransaction->AssertIsOnConnectionThread();
  17547  MOZ_ASSERT(mTransaction->GetMode() == IDBTransaction::Mode::ReadWrite ||
  17548             mTransaction->GetMode() == IDBTransaction::Mode::ReadWriteFlush ||
  17549             mTransaction->GetMode() == IDBTransaction::Mode::Cleanup ||
  17550             mTransaction->GetMode() == IDBTransaction::Mode::VersionChange);
  17551 
  17552  const auto& metadataArray =
  17553      mTransaction->mModifiedAutoIncrementObjectStoreMetadataArray;
  17554 
  17555  if (!metadataArray.IsEmpty()) {
  17556    bool committed = NS_SUCCEEDED(mResultCode);
  17557 
  17558    for (const auto& metadata : metadataArray) {
  17559      auto&& lockedAutoIncrementIds = metadata->mAutoIncrementIds.Lock();
  17560 
  17561      if (committed) {
  17562        lockedAutoIncrementIds->committed = lockedAutoIncrementIds->next;
  17563      } else {
  17564        lockedAutoIncrementIds->next = lockedAutoIncrementIds->committed;
  17565      }
  17566    }
  17567  }
  17568 }
  17569 
  17570 #ifdef DEBUG
  17571 
  17572 void TransactionBase::CommitOp::AssertForeignKeyConsistency(
  17573    DatabaseConnection* aConnection) {
  17574  MOZ_ASSERT(aConnection);
  17575  MOZ_ASSERT(mTransaction);
  17576  mTransaction->AssertIsOnConnectionThread();
  17577  MOZ_ASSERT(mTransaction->GetMode() != IDBTransaction::Mode::ReadOnly);
  17578 
  17579  {
  17580    QM_TRY_INSPECT(
  17581        const auto& pragmaStmt,
  17582        CreateAndExecuteSingleStepStatement(
  17583            aConnection->MutableStorageConnection(), "PRAGMA foreign_keys;"_ns),
  17584        QM_ASSERT_UNREACHABLE_VOID);
  17585 
  17586    int32_t foreignKeysEnabled;
  17587    MOZ_ALWAYS_SUCCEEDS(pragmaStmt->GetInt32(0, &foreignKeysEnabled));
  17588 
  17589    MOZ_ASSERT(foreignKeysEnabled,
  17590               "Database doesn't have foreign keys enabled!");
  17591  }
  17592 
  17593  {
  17594    QM_TRY_INSPECT(const bool& foreignKeyError,
  17595                   CreateAndExecuteSingleStepStatement<
  17596                       SingleStepResult::ReturnNullIfNoResult>(
  17597                       aConnection->MutableStorageConnection(),
  17598                       "PRAGMA foreign_key_check;"_ns),
  17599                   QM_ASSERT_UNREACHABLE_VOID);
  17600 
  17601    MOZ_ASSERT(!foreignKeyError, "Database has inconsisistent foreign keys!");
  17602  }
  17603 }
  17604 
  17605 #endif  // DEBUG
  17606 
  17607 NS_IMPL_ISUPPORTS_INHERITED0(TransactionBase::CommitOp, DatabaseOperationBase)
  17608 
  17609 NS_IMETHODIMP
  17610 TransactionBase::CommitOp::Run() {
  17611  MOZ_ASSERT(mTransaction);
  17612  mTransaction->AssertIsOnConnectionThread();
  17613 
  17614  AUTO_PROFILER_LABEL("TransactionBase::CommitOp::Run", DOM);
  17615 
  17616  IDB_LOG_MARK_PARENT_TRANSACTION_REQUEST(
  17617      "Beginning database work", "DB Start",
  17618      IDB_LOG_ID_STRING(mBackgroundChildLoggingId),
  17619      mTransaction->LoggingSerialNumber(), mLoggingSerialNumber);
  17620 
  17621  if (mTransaction->GetMode() != IDBTransaction::Mode::ReadOnly &&
  17622      mTransaction->mHasBeenActiveOnConnectionThread) {
  17623    if (DatabaseConnection* connection =
  17624            mTransaction->GetDatabase().GetConnection()) {
  17625      // May be null if the VersionChangeOp was canceled.
  17626      DatabaseConnection::UpdateRefcountFunction* fileRefcountFunction =
  17627          connection->GetUpdateRefcountFunction();
  17628 
  17629      if (NS_SUCCEEDED(mResultCode)) {
  17630        if (fileRefcountFunction) {
  17631          mResultCode = fileRefcountFunction->WillCommit();
  17632          NS_WARNING_ASSERTION(NS_SUCCEEDED(mResultCode),
  17633                               "WillCommit() failed!");
  17634        }
  17635 
  17636        if (NS_SUCCEEDED(mResultCode)) {
  17637          mResultCode = WriteAutoIncrementCounts();
  17638          NS_WARNING_ASSERTION(NS_SUCCEEDED(mResultCode),
  17639                               "WriteAutoIncrementCounts() failed!");
  17640 
  17641          if (NS_SUCCEEDED(mResultCode)) {
  17642            AssertForeignKeyConsistency(connection);
  17643 
  17644            mResultCode = connection->CommitWriteTransaction();
  17645            NS_WARNING_ASSERTION(NS_SUCCEEDED(mResultCode), "Commit failed!");
  17646 
  17647            if (NS_SUCCEEDED(mResultCode) &&
  17648                mTransaction->GetMode() ==
  17649                    IDBTransaction::Mode::ReadWriteFlush) {
  17650              mResultCode = connection->Checkpoint();
  17651            }
  17652 
  17653            if (NS_SUCCEEDED(mResultCode) && fileRefcountFunction) {
  17654              fileRefcountFunction->DidCommit();
  17655            }
  17656          }
  17657        }
  17658      }
  17659 
  17660      if (NS_FAILED(mResultCode)) {
  17661        if (fileRefcountFunction) {
  17662          fileRefcountFunction->DidAbort();
  17663        }
  17664 
  17665        connection->RollbackWriteTransaction();
  17666      }
  17667 
  17668      CommitOrRollbackAutoIncrementCounts();
  17669 
  17670      connection->FinishWriteTransaction();
  17671 
  17672      if (mTransaction->GetMode() == IDBTransaction::Mode::Cleanup) {
  17673        connection->DoIdleProcessing(/* aNeedsCheckpoint */ true,
  17674                                     /* aInterrupted */ Atomic<bool>(false));
  17675 
  17676        connection->EnableQuotaChecks();
  17677      }
  17678    }
  17679  }
  17680 
  17681  IDB_LOG_MARK_PARENT_TRANSACTION_REQUEST(
  17682      "Finished database work", "DB End",
  17683      IDB_LOG_ID_STRING(mBackgroundChildLoggingId),
  17684      mTransaction->LoggingSerialNumber(), mLoggingSerialNumber);
  17685 
  17686  IDB_LOG_MARK_PARENT_TRANSACTION("Finished database work", "DB End",
  17687                                  IDB_LOG_ID_STRING(mBackgroundChildLoggingId),
  17688                                  mTransaction->LoggingSerialNumber());
  17689 
  17690  return NS_OK;
  17691 }
  17692 
  17693 void TransactionBase::CommitOp::TransactionFinishedBeforeUnblock() {
  17694  AssertIsOnBackgroundThread();
  17695  MOZ_ASSERT(mTransaction);
  17696 
  17697  AUTO_PROFILER_LABEL("CommitOp::TransactionFinishedBeforeUnblock", DOM);
  17698 
  17699  if (!IsActorDestroyed()) {
  17700    mTransaction->UpdateMetadata(mResultCode);
  17701  }
  17702 }
  17703 
  17704 void TransactionBase::CommitOp::TransactionFinishedAfterUnblock() {
  17705  AssertIsOnBackgroundThread();
  17706  MOZ_ASSERT(mTransaction);
  17707 
  17708  IDB_LOG_MARK_PARENT_TRANSACTION(
  17709      "Finished with result 0x%" PRIx32, "Transaction finished (0x%" PRIx32 ")",
  17710      IDB_LOG_ID_STRING(mTransaction->GetLoggingInfo()->Id()),
  17711      mTransaction->LoggingSerialNumber(), static_cast<uint32_t>(mResultCode));
  17712 
  17713  mTransaction->SendCompleteNotification(ClampResultCode(mResultCode));
  17714 
  17715  mTransaction->GetMutableDatabase().UnregisterTransaction(*mTransaction);
  17716 
  17717  mTransaction = nullptr;
  17718 
  17719 #ifdef DEBUG
  17720  // A bit hacky but the CommitOp is not really a normal database operation
  17721  // that is tied to an actor. Do this to make our assertions happy.
  17722  NoteActorDestroyed();
  17723 #endif
  17724 }
  17725 
  17726 nsresult VersionChangeTransactionOp::SendSuccessResult() {
  17727  AssertIsOnOwningThread();
  17728 
  17729  // Nothing to send here, the API assumes that this request always succeeds.
  17730  return NS_OK;
  17731 }
  17732 
  17733 bool VersionChangeTransactionOp::SendFailureResult(nsresult aResultCode) {
  17734  AssertIsOnOwningThread();
  17735 
  17736  // The only option here is to cause the transaction to abort.
  17737  return false;
  17738 }
  17739 
  17740 void VersionChangeTransactionOp::Cleanup() {
  17741  AssertIsOnOwningThread();
  17742 
  17743 #ifdef DEBUG
  17744  // A bit hacky but the VersionChangeTransactionOp is not generated in response
  17745  // to a child request like most other database operations. Do this to make our
  17746  // assertions happy.
  17747  NoteActorDestroyed();
  17748 #endif
  17749 
  17750  TransactionDatabaseOperationBase::Cleanup();
  17751 }
  17752 
  17753 nsresult CreateObjectStoreOp::DoDatabaseWork(DatabaseConnection* aConnection) {
  17754  MOZ_ASSERT(aConnection);
  17755  aConnection->AssertIsOnConnectionThread();
  17756 
  17757  AUTO_PROFILER_LABEL("CreateObjectStoreOp::DoDatabaseWork", DOM);
  17758 
  17759 #ifdef DEBUG
  17760  {
  17761    // Make sure that we're not creating an object store with the same name as
  17762    // another that already exists. This should be impossible because we should
  17763    // have thrown an error long before now...
  17764    // The parameter names are not used, parameters are bound by index only
  17765    // locally in the same function.
  17766    QM_TRY_INSPECT(const bool& hasResult,
  17767                   aConnection
  17768                       ->BorrowAndExecuteSingleStepStatement(
  17769                           "SELECT name "
  17770                           "FROM object_store "
  17771                           "WHERE name = :name;"_ns,
  17772                           [&self = *this](auto& stmt) -> Result<Ok, nsresult> {
  17773                             QM_TRY(MOZ_TO_RESULT(stmt.BindStringByIndex(
  17774                                 0, self.mMetadata.name())));
  17775                             return Ok{};
  17776                           })
  17777                       .map(IsSome),
  17778                   QM_ASSERT_UNREACHABLE);
  17779 
  17780    MOZ_ASSERT(!hasResult);
  17781  }
  17782 #endif
  17783 
  17784  DatabaseConnection::AutoSavepoint autoSave;
  17785  QM_TRY(MOZ_TO_RESULT(autoSave.Start(Transaction()))
  17786 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
  17787             ,
  17788         QM_PROPAGATE, MakeAutoSavepointCleanupHandler(*aConnection)
  17789 #endif
  17790  );
  17791 
  17792  // The parameter names are not used, parameters are bound by index only
  17793  // locally in the same function.
  17794  QM_TRY(MOZ_TO_RESULT(aConnection->ExecuteCachedStatement(
  17795      "INSERT INTO object_store (id, auto_increment, name, key_path) "
  17796      "VALUES (:id, :auto_increment, :name, :key_path);"_ns,
  17797      [&metadata =
  17798           mMetadata](mozIStorageStatement& stmt) -> Result<Ok, nsresult> {
  17799        QM_TRY(MOZ_TO_RESULT(stmt.BindInt64ByIndex(0, metadata.id())));
  17800 
  17801        QM_TRY(MOZ_TO_RESULT(
  17802            stmt.BindInt32ByIndex(1, metadata.autoIncrement() ? 1 : 0)));
  17803 
  17804        QM_TRY(MOZ_TO_RESULT(stmt.BindStringByIndex(2, metadata.name())));
  17805 
  17806        if (metadata.keyPath().IsValid()) {
  17807          QM_TRY(MOZ_TO_RESULT(stmt.BindStringByIndex(
  17808              3, metadata.keyPath().SerializeToString())));
  17809        } else {
  17810          QM_TRY(MOZ_TO_RESULT(stmt.BindNullByIndex(3)));
  17811        }
  17812 
  17813        return Ok{};
  17814      })));
  17815 
  17816 #ifdef DEBUG
  17817  {
  17818    int64_t id;
  17819    MOZ_ALWAYS_SUCCEEDS(
  17820        aConnection->MutableStorageConnection().GetLastInsertRowID(&id));
  17821    MOZ_ASSERT(mMetadata.id() == id);
  17822  }
  17823 #endif
  17824 
  17825  QM_TRY(MOZ_TO_RESULT(autoSave.Commit()));
  17826 
  17827  return NS_OK;
  17828 }
  17829 
  17830 nsresult DeleteObjectStoreOp::DoDatabaseWork(DatabaseConnection* aConnection) {
  17831  MOZ_ASSERT(aConnection);
  17832  aConnection->AssertIsOnConnectionThread();
  17833 
  17834  AUTO_PROFILER_LABEL("DeleteObjectStoreOp::DoDatabaseWork", DOM);
  17835 
  17836 #ifdef DEBUG
  17837  {
  17838    // Make sure |mIsLastObjectStore| is telling the truth.
  17839    QM_TRY_INSPECT(
  17840        const auto& stmt,
  17841        aConnection->BorrowCachedStatement("SELECT id FROM object_store;"_ns),
  17842        QM_ASSERT_UNREACHABLE);
  17843 
  17844    bool foundThisObjectStore = false;
  17845    bool foundOtherObjectStore = false;
  17846 
  17847    while (true) {
  17848      bool hasResult;
  17849      MOZ_ALWAYS_SUCCEEDS(stmt->ExecuteStep(&hasResult));
  17850 
  17851      if (!hasResult) {
  17852        break;
  17853      }
  17854 
  17855      int64_t id;
  17856      MOZ_ALWAYS_SUCCEEDS(stmt->GetInt64(0, &id));
  17857 
  17858      if (id == mMetadata->mCommonMetadata.id()) {
  17859        foundThisObjectStore = true;
  17860      } else {
  17861        foundOtherObjectStore = true;
  17862      }
  17863    }
  17864 
  17865    MOZ_ASSERT_IF(mIsLastObjectStore,
  17866                  foundThisObjectStore && !foundOtherObjectStore);
  17867    MOZ_ASSERT_IF(!mIsLastObjectStore,
  17868                  foundThisObjectStore && foundOtherObjectStore);
  17869  }
  17870 #endif
  17871 
  17872  DatabaseConnection::AutoSavepoint autoSave;
  17873  QM_TRY(MOZ_TO_RESULT(autoSave.Start(Transaction()))
  17874 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
  17875             ,
  17876         QM_PROPAGATE, MakeAutoSavepointCleanupHandler(*aConnection)
  17877 #endif
  17878  );
  17879 
  17880  if (mIsLastObjectStore) {
  17881    // We can just delete everything if this is the last object store.
  17882    QM_TRY(MOZ_TO_RESULT(
  17883        aConnection->ExecuteCachedStatement("DELETE FROM index_data;"_ns)));
  17884 
  17885    QM_TRY(MOZ_TO_RESULT(aConnection->ExecuteCachedStatement(
  17886        "DELETE FROM unique_index_data;"_ns)));
  17887 
  17888    QM_TRY(MOZ_TO_RESULT(
  17889        aConnection->ExecuteCachedStatement("DELETE FROM object_data;"_ns)));
  17890 
  17891    QM_TRY(MOZ_TO_RESULT(aConnection->ExecuteCachedStatement(
  17892        "DELETE FROM object_store_index;"_ns)));
  17893 
  17894    QM_TRY(MOZ_TO_RESULT(
  17895        aConnection->ExecuteCachedStatement("DELETE FROM object_store;"_ns)));
  17896  } else {
  17897    QM_TRY_INSPECT(
  17898        const bool& hasIndexes,
  17899        ObjectStoreHasIndexes(*aConnection, mMetadata->mCommonMetadata.id()));
  17900 
  17901    const auto bindObjectStoreIdToFirstParameter =
  17902        [this](mozIStorageStatement& stmt) -> Result<Ok, nsresult> {
  17903      QM_TRY(MOZ_TO_RESULT(
  17904          stmt.BindInt64ByIndex(0, mMetadata->mCommonMetadata.id())));
  17905 
  17906      return Ok{};
  17907    };
  17908 
  17909    // The parameter name :object_store_id in the SQL statements below is not
  17910    // used for binding, parameters are bound by index only locally by
  17911    // bindObjectStoreIdToFirstParameter.
  17912    if (hasIndexes) {
  17913      QM_TRY(MOZ_TO_RESULT(DeleteObjectStoreDataTableRowsWithIndexes(
  17914          aConnection, mMetadata->mCommonMetadata.id(), Nothing())));
  17915 
  17916      // Now clean up the object store index table.
  17917      QM_TRY(MOZ_TO_RESULT(aConnection->ExecuteCachedStatement(
  17918          "DELETE FROM object_store_index "
  17919          "WHERE object_store_id = :object_store_id;"_ns,
  17920          bindObjectStoreIdToFirstParameter)));
  17921    } else {
  17922      // We only have to worry about object data if this object store has no
  17923      // indexes.
  17924      QM_TRY(MOZ_TO_RESULT(aConnection->ExecuteCachedStatement(
  17925          "DELETE FROM object_data "
  17926          "WHERE object_store_id = :object_store_id;"_ns,
  17927          bindObjectStoreIdToFirstParameter)));
  17928    }
  17929 
  17930    QM_TRY(MOZ_TO_RESULT(aConnection->ExecuteCachedStatement(
  17931        "DELETE FROM object_store "
  17932        "WHERE id = :object_store_id;"_ns,
  17933        bindObjectStoreIdToFirstParameter)));
  17934 
  17935 #ifdef DEBUG
  17936    {
  17937      int32_t deletedRowCount;
  17938      MOZ_ALWAYS_SUCCEEDS(
  17939          aConnection->MutableStorageConnection().GetAffectedRows(
  17940              &deletedRowCount));
  17941      MOZ_ASSERT(deletedRowCount == 1);
  17942    }
  17943 #endif
  17944  }
  17945 
  17946  QM_TRY(MOZ_TO_RESULT(autoSave.Commit()));
  17947 
  17948  if (mMetadata->mCommonMetadata.autoIncrement()) {
  17949    Transaction().ForgetModifiedAutoIncrementObjectStore(*mMetadata);
  17950  }
  17951 
  17952  return NS_OK;
  17953 }
  17954 
  17955 nsresult RenameObjectStoreOp::DoDatabaseWork(DatabaseConnection* aConnection) {
  17956  MOZ_ASSERT(aConnection);
  17957  aConnection->AssertIsOnConnectionThread();
  17958 
  17959  AUTO_PROFILER_LABEL("RenameObjectStoreOp::DoDatabaseWork", DOM);
  17960 
  17961 #ifdef DEBUG
  17962  {
  17963    // Make sure that we're not renaming an object store with the same name as
  17964    // another that already exists. This should be impossible because we should
  17965    // have thrown an error long before now...
  17966    // The parameter names are not used, parameters are bound by index only
  17967    // locally in the same function.
  17968    QM_TRY_INSPECT(
  17969        const bool& hasResult,
  17970        aConnection
  17971            ->BorrowAndExecuteSingleStepStatement(
  17972                "SELECT name "
  17973                "FROM object_store "
  17974                "WHERE name = :name AND id != :id;"_ns,
  17975                [&self = *this](auto& stmt) -> Result<Ok, nsresult> {
  17976                  QM_TRY(
  17977                      MOZ_TO_RESULT(stmt.BindStringByIndex(0, self.mNewName)));
  17978 
  17979                  QM_TRY(MOZ_TO_RESULT(stmt.BindInt64ByIndex(1, self.mId)));
  17980                  return Ok{};
  17981                })
  17982            .map(IsSome),
  17983        QM_ASSERT_UNREACHABLE);
  17984 
  17985    MOZ_ASSERT(!hasResult);
  17986  }
  17987 #endif
  17988 
  17989  DatabaseConnection::AutoSavepoint autoSave;
  17990  QM_TRY(MOZ_TO_RESULT(autoSave.Start(Transaction()))
  17991 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
  17992             ,
  17993         QM_PROPAGATE, MakeAutoSavepointCleanupHandler(*aConnection)
  17994 #endif
  17995  );
  17996 
  17997  // The parameter names are not used, parameters are bound by index only
  17998  // locally in the same function.
  17999  QM_TRY(MOZ_TO_RESULT(aConnection->ExecuteCachedStatement(
  18000      "UPDATE object_store "
  18001      "SET name = :name "
  18002      "WHERE id = :id;"_ns,
  18003      [&self = *this](mozIStorageStatement& stmt) -> Result<Ok, nsresult> {
  18004        QM_TRY(MOZ_TO_RESULT(stmt.BindStringByIndex(0, self.mNewName)));
  18005 
  18006        QM_TRY(MOZ_TO_RESULT(stmt.BindInt64ByIndex(1, self.mId)));
  18007 
  18008        return Ok{};
  18009      })));
  18010 
  18011  QM_TRY(MOZ_TO_RESULT(autoSave.Commit()));
  18012 
  18013  return NS_OK;
  18014 }
  18015 
  18016 CreateIndexOp::CreateIndexOp(SafeRefPtr<VersionChangeTransaction> aTransaction,
  18017                             const IndexOrObjectStoreId aObjectStoreId,
  18018                             const IndexMetadata& aMetadata)
  18019    : VersionChangeTransactionOp(std::move(aTransaction)),
  18020      mMetadata(aMetadata),
  18021      mFileManager(Transaction().GetDatabase().GetFileManagerPtr()),
  18022      mDatabaseId(Transaction().DatabaseId()),
  18023      mObjectStoreId(aObjectStoreId) {
  18024  MOZ_ASSERT(aObjectStoreId);
  18025  MOZ_ASSERT(aMetadata.id());
  18026  MOZ_ASSERT(mFileManager);
  18027  MOZ_ASSERT(!mDatabaseId.IsEmpty());
  18028 }
  18029 
  18030 nsresult CreateIndexOp::InsertDataFromObjectStore(
  18031    DatabaseConnection* aConnection) {
  18032  MOZ_ASSERT(aConnection);
  18033  aConnection->AssertIsOnConnectionThread();
  18034  MOZ_ASSERT(mMaybeUniqueIndexTable);
  18035 
  18036  AUTO_PROFILER_LABEL("CreateIndexOp::InsertDataFromObjectStore", DOM);
  18037 
  18038  auto& storageConnection = aConnection->MutableStorageConnection();
  18039 
  18040  RefPtr<UpdateIndexDataValuesFunction> updateFunction =
  18041      new UpdateIndexDataValuesFunction(this, aConnection,
  18042                                        Transaction().GetDatabasePtr());
  18043 
  18044  constexpr auto updateFunctionName = "update_index_data_values"_ns;
  18045 
  18046  nsresult rv =
  18047      storageConnection.CreateFunction(updateFunctionName, 4, updateFunction);
  18048  if (NS_WARN_IF(NS_FAILED(rv))) {
  18049    return rv;
  18050  }
  18051 
  18052  rv = InsertDataFromObjectStoreInternal(aConnection);
  18053 
  18054  MOZ_ALWAYS_SUCCEEDS(storageConnection.RemoveFunction(updateFunctionName));
  18055 
  18056  if (NS_WARN_IF(NS_FAILED(rv))) {
  18057    return rv;
  18058  }
  18059 
  18060  return NS_OK;
  18061 }
  18062 
  18063 nsresult CreateIndexOp::InsertDataFromObjectStoreInternal(
  18064    DatabaseConnection* aConnection) const {
  18065  MOZ_ASSERT(aConnection);
  18066  aConnection->AssertIsOnConnectionThread();
  18067  MOZ_ASSERT(mMaybeUniqueIndexTable);
  18068 
  18069  MOZ_ASSERT(aConnection->HasStorageConnection());
  18070 
  18071  // The parameter names are not used, parameters are bound by index only
  18072  // locally in the same function.
  18073  QM_TRY(MOZ_TO_RESULT(aConnection->ExecuteCachedStatement(
  18074      "UPDATE object_data "
  18075      "SET index_data_values = update_index_data_values "
  18076      "(key, index_data_values, file_ids, data) "
  18077      "WHERE object_store_id = :object_store_id;"_ns,
  18078      [objectStoredId =
  18079           mObjectStoreId](mozIStorageStatement& stmt) -> Result<Ok, nsresult> {
  18080        QM_TRY(MOZ_TO_RESULT(stmt.BindInt64ByIndex(0, objectStoredId)));
  18081 
  18082        return Ok{};
  18083      })));
  18084 
  18085  return NS_OK;
  18086 }
  18087 
  18088 bool CreateIndexOp::Init(TransactionBase& aTransaction) {
  18089  AssertIsOnBackgroundThread();
  18090  MOZ_ASSERT(mObjectStoreId);
  18091  MOZ_ASSERT(mMaybeUniqueIndexTable.isNothing());
  18092 
  18093  const SafeRefPtr<FullObjectStoreMetadata> objectStoreMetadata =
  18094      aTransaction.GetMetadataForObjectStoreId(mObjectStoreId);
  18095  MOZ_ASSERT(objectStoreMetadata);
  18096 
  18097  const uint32_t indexCount = objectStoreMetadata->mIndexes.Count();
  18098  if (!indexCount) {
  18099    return true;
  18100  }
  18101 
  18102  auto uniqueIndexTable = UniqueIndexTable{indexCount};
  18103 
  18104  for (const auto& value : objectStoreMetadata->mIndexes.Values()) {
  18105    MOZ_ASSERT(!uniqueIndexTable.Contains(value->mCommonMetadata.id()));
  18106 
  18107    if (NS_WARN_IF(!uniqueIndexTable.InsertOrUpdate(
  18108            value->mCommonMetadata.id(), value->mCommonMetadata.unique(),
  18109            fallible))) {
  18110      IDB_REPORT_INTERNAL_ERR();
  18111      NS_WARNING("out of memory");
  18112      return false;
  18113    }
  18114  }
  18115 
  18116  uniqueIndexTable.MarkImmutable();
  18117 
  18118  mMaybeUniqueIndexTable.emplace(std::move(uniqueIndexTable));
  18119 
  18120  return true;
  18121 }
  18122 
  18123 nsresult CreateIndexOp::DoDatabaseWork(DatabaseConnection* aConnection) {
  18124  MOZ_ASSERT(aConnection);
  18125  aConnection->AssertIsOnConnectionThread();
  18126 
  18127  AUTO_PROFILER_LABEL("CreateIndexOp::DoDatabaseWork", DOM);
  18128 
  18129 #ifdef DEBUG
  18130  {
  18131    // Make sure that we're not creating an index with the same name and object
  18132    // store as another that already exists. This should be impossible because
  18133    // we should have thrown an error long before now...
  18134    // The parameter names are not used, parameters are bound by index only
  18135    // locally in the same function.
  18136    QM_TRY_INSPECT(
  18137        const bool& hasResult,
  18138        aConnection
  18139            ->BorrowAndExecuteSingleStepStatement(
  18140                "SELECT name "
  18141                "FROM object_store_index "
  18142                "WHERE object_store_id = :object_store_id AND name = :name;"_ns,
  18143                [&self = *this](auto& stmt) -> Result<Ok, nsresult> {
  18144                  QM_TRY(MOZ_TO_RESULT(
  18145                      stmt.BindInt64ByIndex(0, self.mObjectStoreId)));
  18146                  QM_TRY(MOZ_TO_RESULT(
  18147                      stmt.BindStringByIndex(1, self.mMetadata.name())));
  18148                  return Ok{};
  18149                })
  18150            .map(IsSome),
  18151        QM_ASSERT_UNREACHABLE);
  18152 
  18153    MOZ_ASSERT(!hasResult);
  18154  }
  18155 #endif
  18156 
  18157  DatabaseConnection::AutoSavepoint autoSave;
  18158  QM_TRY(MOZ_TO_RESULT(autoSave.Start(Transaction()))
  18159 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
  18160             ,
  18161         QM_PROPAGATE, MakeAutoSavepointCleanupHandler(*aConnection)
  18162 #endif
  18163  );
  18164 
  18165  // The parameter names are not used, parameters are bound by index only
  18166  // locally in the same function.
  18167  QM_TRY(MOZ_TO_RESULT(aConnection->ExecuteCachedStatement(
  18168      "INSERT INTO object_store_index (id, name, key_path, unique_index, "
  18169      "multientry, object_store_id, locale, "
  18170      "is_auto_locale) "
  18171      "VALUES (:id, :name, :key_path, :unique, :multientry, "
  18172      ":object_store_id, :locale, :is_auto_locale)"_ns,
  18173      [&metadata = mMetadata, objectStoreId = mObjectStoreId](
  18174          mozIStorageStatement& stmt) -> Result<Ok, nsresult> {
  18175        QM_TRY(MOZ_TO_RESULT(stmt.BindInt64ByIndex(0, metadata.id())));
  18176 
  18177        QM_TRY(MOZ_TO_RESULT(stmt.BindStringByIndex(1, metadata.name())));
  18178 
  18179        QM_TRY(MOZ_TO_RESULT(
  18180            stmt.BindStringByIndex(2, metadata.keyPath().SerializeToString())));
  18181 
  18182        QM_TRY(
  18183            MOZ_TO_RESULT(stmt.BindInt32ByIndex(3, metadata.unique() ? 1 : 0)));
  18184 
  18185        QM_TRY(MOZ_TO_RESULT(
  18186            stmt.BindInt32ByIndex(4, metadata.multiEntry() ? 1 : 0)));
  18187        QM_TRY(MOZ_TO_RESULT(stmt.BindInt64ByIndex(5, objectStoreId)));
  18188 
  18189        QM_TRY(MOZ_TO_RESULT(
  18190            metadata.locale().IsEmpty()
  18191                ? stmt.BindNullByIndex(6)
  18192                : stmt.BindUTF8StringByIndex(6, metadata.locale())));
  18193 
  18194        QM_TRY(MOZ_TO_RESULT(stmt.BindInt32ByIndex(7, metadata.autoLocale())));
  18195 
  18196        return Ok{};
  18197      })));
  18198 
  18199 #ifdef DEBUG
  18200  {
  18201    int64_t id;
  18202    MOZ_ALWAYS_SUCCEEDS(
  18203        aConnection->MutableStorageConnection().GetLastInsertRowID(&id));
  18204    MOZ_ASSERT(mMetadata.id() == id);
  18205  }
  18206 #endif
  18207 
  18208  QM_TRY(MOZ_TO_RESULT(InsertDataFromObjectStore(aConnection)));
  18209 
  18210  QM_TRY(MOZ_TO_RESULT(autoSave.Commit()));
  18211 
  18212  return NS_OK;
  18213 }
  18214 
  18215 NS_IMPL_ISUPPORTS(CreateIndexOp::UpdateIndexDataValuesFunction,
  18216                  mozIStorageFunction);
  18217 
  18218 NS_IMETHODIMP
  18219 CreateIndexOp::UpdateIndexDataValuesFunction::OnFunctionCall(
  18220    mozIStorageValueArray* aValues, nsIVariant** _retval) {
  18221  MOZ_ASSERT(aValues);
  18222  MOZ_ASSERT(_retval);
  18223  MOZ_ASSERT(mConnection);
  18224  mConnection->AssertIsOnConnectionThread();
  18225  MOZ_ASSERT(mOp);
  18226  MOZ_ASSERT(mOp->mFileManager);
  18227 
  18228  AUTO_PROFILER_LABEL(
  18229      "CreateIndexOp::UpdateIndexDataValuesFunction::OnFunctionCall", DOM);
  18230 
  18231 #ifdef DEBUG
  18232  {
  18233    uint32_t argCount;
  18234    MOZ_ALWAYS_SUCCEEDS(aValues->GetNumEntries(&argCount));
  18235    MOZ_ASSERT(argCount == 4);  // key, index_data_values, file_ids, data
  18236 
  18237    int32_t valueType;
  18238    MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(0, &valueType));
  18239    MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_BLOB);
  18240 
  18241    MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(1, &valueType));
  18242    MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_NULL ||
  18243               valueType == mozIStorageValueArray::VALUE_TYPE_BLOB);
  18244 
  18245    MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(2, &valueType));
  18246    MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_NULL ||
  18247               valueType == mozIStorageValueArray::VALUE_TYPE_TEXT);
  18248 
  18249    MOZ_ALWAYS_SUCCEEDS(aValues->GetTypeOfIndex(3, &valueType));
  18250    MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_BLOB ||
  18251               valueType == mozIStorageValueArray::VALUE_TYPE_INTEGER);
  18252  }
  18253 #endif
  18254 
  18255  QM_TRY_UNWRAP(auto cloneInfo, GetStructuredCloneReadInfoFromValueArray(
  18256                                    aValues,
  18257                                    /* aDataIndex */ 3,
  18258                                    /* aFileIdsIndex */ 2, *mOp->mFileManager));
  18259 
  18260  const IndexMetadata& metadata = mOp->mMetadata;
  18261  const IndexOrObjectStoreId& objectStoreId = mOp->mObjectStoreId;
  18262 
  18263  // XXX does this really need a non-const cloneInfo?
  18264  QM_TRY_INSPECT(const auto& updateInfos,
  18265                 DeserializeIndexValueToUpdateInfos(
  18266                     metadata.id(), metadata.keyPath(), metadata.multiEntry(),
  18267                     metadata.locale(), cloneInfo));
  18268 
  18269  if (updateInfos.IsEmpty()) {
  18270    // XXX See if we can do this without copying...
  18271 
  18272    nsCOMPtr<nsIVariant> unmodifiedValue;
  18273 
  18274    // No changes needed, just return the original value.
  18275    QM_TRY_INSPECT(const int32_t& valueType,
  18276                   MOZ_TO_RESULT_INVOKE_MEMBER(aValues, GetTypeOfIndex, 1));
  18277 
  18278    MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_NULL ||
  18279               valueType == mozIStorageValueArray::VALUE_TYPE_BLOB);
  18280 
  18281    if (valueType == mozIStorageValueArray::VALUE_TYPE_NULL) {
  18282      unmodifiedValue = new storage::NullVariant();
  18283      unmodifiedValue.forget(_retval);
  18284      return NS_OK;
  18285    }
  18286 
  18287    MOZ_ASSERT(valueType == mozIStorageValueArray::VALUE_TYPE_BLOB);
  18288 
  18289    const uint8_t* blobData;
  18290    uint32_t blobDataLength;
  18291    QM_TRY(
  18292        MOZ_TO_RESULT(aValues->GetSharedBlob(1, &blobDataLength, &blobData)));
  18293 
  18294    const std::pair<uint8_t*, int> copiedBlobDataPair(
  18295        static_cast<uint8_t*>(malloc(blobDataLength)), blobDataLength);
  18296 
  18297    if (!copiedBlobDataPair.first) {
  18298      IDB_REPORT_INTERNAL_ERR();
  18299      return NS_ERROR_OUT_OF_MEMORY;
  18300    }
  18301 
  18302    memcpy(copiedBlobDataPair.first, blobData, blobDataLength);
  18303 
  18304    unmodifiedValue = new storage::AdoptedBlobVariant(copiedBlobDataPair);
  18305    unmodifiedValue.forget(_retval);
  18306 
  18307    return NS_OK;
  18308  }
  18309 
  18310  Key key;
  18311  QM_TRY(MOZ_TO_RESULT(key.SetFromValueArray(aValues, 0)));
  18312 
  18313  QM_TRY_UNWRAP(auto indexValues, ReadCompressedIndexDataValues(*aValues, 1));
  18314 
  18315  const bool hadPreviousIndexValues = !indexValues.IsEmpty();
  18316 
  18317  const uint32_t updateInfoCount = updateInfos.Length();
  18318 
  18319  QM_TRY(OkIf(indexValues.SetCapacity(indexValues.Length() + updateInfoCount,
  18320                                      fallible)),
  18321         NS_ERROR_OUT_OF_MEMORY, IDB_REPORT_INTERNAL_ERR_LAMBDA);
  18322 
  18323  // First construct the full list to update the index_data_values row.
  18324  for (const IndexUpdateInfo& info : updateInfos) {
  18325    MOZ_ALWAYS_TRUE(indexValues.InsertElementSorted(
  18326        IndexDataValue(metadata.id(), metadata.unique(), info.value(),
  18327                       info.localizedValue()),
  18328        fallible));
  18329  }
  18330 
  18331  QM_TRY_UNWRAP((auto [indexValuesBlob, indexValuesBlobLength]),
  18332                MakeCompressedIndexDataValues(indexValues));
  18333 
  18334  MOZ_ASSERT(!indexValuesBlobLength == !(indexValuesBlob.get()));
  18335 
  18336  nsCOMPtr<nsIVariant> value;
  18337 
  18338  if (!indexValuesBlob) {
  18339    value = new storage::NullVariant();
  18340 
  18341    value.forget(_retval);
  18342    return NS_OK;
  18343  }
  18344 
  18345  // Now insert the new table rows. We only need to construct a new list if
  18346  // the full list is different.
  18347  if (hadPreviousIndexValues) {
  18348    indexValues.ClearAndRetainStorage();
  18349 
  18350    MOZ_ASSERT(indexValues.Capacity() >= updateInfoCount);
  18351 
  18352    for (const IndexUpdateInfo& info : updateInfos) {
  18353      MOZ_ALWAYS_TRUE(indexValues.InsertElementSorted(
  18354          IndexDataValue(metadata.id(), metadata.unique(), info.value(),
  18355                         info.localizedValue()),
  18356          fallible));
  18357    }
  18358  }
  18359 
  18360  QM_TRY(MOZ_TO_RESULT(
  18361      InsertIndexTableRows(mConnection, objectStoreId, key, indexValues)));
  18362 
  18363  value = new storage::AdoptedBlobVariant(
  18364      std::pair(indexValuesBlob.release(), indexValuesBlobLength));
  18365 
  18366  value.forget(_retval);
  18367  return NS_OK;
  18368 }
  18369 
  18370 DeleteIndexOp::DeleteIndexOp(SafeRefPtr<VersionChangeTransaction> aTransaction,
  18371                             const IndexOrObjectStoreId aObjectStoreId,
  18372                             const IndexOrObjectStoreId aIndexId,
  18373                             const bool aUnique, const bool aIsLastIndex)
  18374    : VersionChangeTransactionOp(std::move(aTransaction)),
  18375      mObjectStoreId(aObjectStoreId),
  18376      mIndexId(aIndexId),
  18377      mUnique(aUnique),
  18378      mIsLastIndex(aIsLastIndex) {
  18379  MOZ_ASSERT(aObjectStoreId);
  18380  MOZ_ASSERT(aIndexId);
  18381 }
  18382 
  18383 nsresult DeleteIndexOp::RemoveReferencesToIndex(
  18384    DatabaseConnection* aConnection, const Key& aObjectStoreKey,
  18385    nsTArray<IndexDataValue>& aIndexValues) const {
  18386  MOZ_ASSERT(!NS_IsMainThread());
  18387  MOZ_ASSERT(!IsOnBackgroundThread());
  18388  MOZ_ASSERT(aConnection);
  18389  MOZ_ASSERT(!aObjectStoreKey.IsUnset());
  18390  MOZ_ASSERT_IF(!mIsLastIndex, !aIndexValues.IsEmpty());
  18391 
  18392  AUTO_PROFILER_LABEL("DeleteIndexOp::RemoveReferencesToIndex", DOM);
  18393 
  18394  if (mIsLastIndex) {
  18395    // There is no need to parse the previous entry in the index_data_values
  18396    // column if this is the last index. Simply set it to NULL.
  18397    QM_TRY_INSPECT(const auto& stmt,
  18398                   aConnection->BorrowCachedStatement(
  18399                       "UPDATE object_data "
  18400                       "SET index_data_values = NULL "
  18401                       "WHERE object_store_id = :"_ns +
  18402                       kStmtParamNameObjectStoreId + " AND key = :"_ns +
  18403                       kStmtParamNameKey + ";"_ns));
  18404 
  18405    QM_TRY(MOZ_TO_RESULT(
  18406        stmt->BindInt64ByName(kStmtParamNameObjectStoreId, mObjectStoreId)));
  18407 
  18408    QM_TRY(MOZ_TO_RESULT(
  18409        aObjectStoreKey.BindToStatement(&*stmt, kStmtParamNameKey)));
  18410 
  18411    QM_TRY(MOZ_TO_RESULT(stmt->Execute()));
  18412 
  18413    return NS_OK;
  18414  }
  18415 
  18416  {
  18417    IndexDataValue search;
  18418    search.mIndexId = mIndexId;
  18419 
  18420    // Use raw pointers for search to avoid redundant index validity checks.
  18421    // Maybe this should better be encapsulated in nsTArray.
  18422    const auto* const begin = aIndexValues.Elements();
  18423    const auto* const end = aIndexValues.Elements() + aIndexValues.Length();
  18424 
  18425    const auto indexIdComparator = [](const IndexDataValue& aA,
  18426                                      const IndexDataValue& aB) {
  18427      return aA.mIndexId < aB.mIndexId;
  18428    };
  18429 
  18430    MOZ_ASSERT(std::is_sorted(begin, end, indexIdComparator));
  18431 
  18432    const auto [beginRange, endRange] =
  18433        std::equal_range(begin, end, search, indexIdComparator);
  18434    if (beginRange == end) {
  18435      IDB_REPORT_INTERNAL_ERR();
  18436      return NS_ERROR_FILE_CORRUPTED;
  18437    }
  18438 
  18439    aIndexValues.RemoveElementsAt(beginRange - begin, endRange - beginRange);
  18440  }
  18441 
  18442  QM_TRY(MOZ_TO_RESULT(UpdateIndexValues(aConnection, mObjectStoreId,
  18443                                         aObjectStoreKey, aIndexValues)));
  18444 
  18445  return NS_OK;
  18446 }
  18447 
  18448 nsresult DeleteIndexOp::DoDatabaseWork(DatabaseConnection* aConnection) {
  18449  MOZ_ASSERT(aConnection);
  18450  aConnection->AssertIsOnConnectionThread();
  18451 
  18452 #ifdef DEBUG
  18453  {
  18454    // Make sure |mIsLastIndex| is telling the truth.
  18455    // The parameter names are not used, parameters are bound by index only
  18456    // locally in the same function.
  18457    QM_TRY_INSPECT(const auto& stmt,
  18458                   aConnection->BorrowCachedStatement(
  18459                       "SELECT id "
  18460                       "FROM object_store_index "
  18461                       "WHERE object_store_id = :object_store_id;"_ns),
  18462                   QM_ASSERT_UNREACHABLE);
  18463 
  18464    MOZ_ALWAYS_SUCCEEDS(stmt->BindInt64ByIndex(0, mObjectStoreId));
  18465 
  18466    bool foundThisIndex = false;
  18467    bool foundOtherIndex = false;
  18468 
  18469    while (true) {
  18470      bool hasResult;
  18471      MOZ_ALWAYS_SUCCEEDS(stmt->ExecuteStep(&hasResult));
  18472 
  18473      if (!hasResult) {
  18474        break;
  18475      }
  18476 
  18477      int64_t id;
  18478      MOZ_ALWAYS_SUCCEEDS(stmt->GetInt64(0, &id));
  18479 
  18480      if (id == mIndexId) {
  18481        foundThisIndex = true;
  18482      } else {
  18483        foundOtherIndex = true;
  18484      }
  18485    }
  18486 
  18487    MOZ_ASSERT_IF(mIsLastIndex, foundThisIndex && !foundOtherIndex);
  18488    MOZ_ASSERT_IF(!mIsLastIndex, foundThisIndex && foundOtherIndex);
  18489  }
  18490 #endif
  18491 
  18492  AUTO_PROFILER_LABEL("DeleteIndexOp::DoDatabaseWork", DOM);
  18493 
  18494  DatabaseConnection::AutoSavepoint autoSave;
  18495  QM_TRY(MOZ_TO_RESULT(autoSave.Start(Transaction()))
  18496 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
  18497             ,
  18498         QM_PROPAGATE, MakeAutoSavepointCleanupHandler(*aConnection)
  18499 #endif
  18500  );
  18501 
  18502  // mozStorage warns that these statements trigger a sort operation but we
  18503  // don't care because this is a very rare call and we expect it to be slow.
  18504  // The cost of having an index on this field is too high.
  18505  QM_TRY_INSPECT(
  18506      const auto& selectStmt,
  18507      aConnection->BorrowCachedStatement(
  18508          mUnique
  18509              ? (mIsLastIndex
  18510                     ? "/* do not warn (bug someone else) */ "
  18511                       "SELECT value, object_data_key "
  18512                       "FROM unique_index_data "
  18513                       "WHERE index_id = :"_ns +
  18514                           kStmtParamNameIndexId +
  18515                           " ORDER BY object_data_key ASC;"_ns
  18516                     : "/* do not warn (bug out) */ "
  18517                       "SELECT unique_index_data.value, "
  18518                       "unique_index_data.object_data_key, "
  18519                       "object_data.index_data_values "
  18520                       "FROM unique_index_data "
  18521                       "JOIN object_data "
  18522                       "ON unique_index_data.object_data_key = object_data.key "
  18523                       "WHERE unique_index_data.index_id = :"_ns +
  18524                           kStmtParamNameIndexId +
  18525                           " AND object_data.object_store_id = :"_ns +
  18526                           kStmtParamNameObjectStoreId +
  18527                           " ORDER BY unique_index_data.object_data_key ASC;"_ns)
  18528              : (mIsLastIndex
  18529                     ? "/* do not warn (bug me not) */ "
  18530                       "SELECT value, object_data_key "
  18531                       "FROM index_data "
  18532                       "WHERE index_id = :"_ns +
  18533                           kStmtParamNameIndexId +
  18534                           " AND object_store_id = :"_ns +
  18535                           kStmtParamNameObjectStoreId +
  18536                           " ORDER BY object_data_key ASC;"_ns
  18537                     : "/* do not warn (bug off) */ "
  18538                       "SELECT index_data.value, "
  18539                       "index_data.object_data_key, "
  18540                       "object_data.index_data_values "
  18541                       "FROM index_data "
  18542                       "JOIN object_data "
  18543                       "ON index_data.object_data_key = object_data.key "
  18544                       "WHERE index_data.index_id = :"_ns +
  18545                           kStmtParamNameIndexId +
  18546                           " AND object_data.object_store_id = :"_ns +
  18547                           kStmtParamNameObjectStoreId +
  18548                           " ORDER BY index_data.object_data_key ASC;"_ns)));
  18549 
  18550  QM_TRY(MOZ_TO_RESULT(
  18551      selectStmt->BindInt64ByName(kStmtParamNameIndexId, mIndexId)));
  18552 
  18553  if (!mUnique || !mIsLastIndex) {
  18554    QM_TRY(MOZ_TO_RESULT(selectStmt->BindInt64ByName(
  18555        kStmtParamNameObjectStoreId, mObjectStoreId)));
  18556  }
  18557 
  18558  Key lastObjectStoreKey;
  18559  IndexDataValuesAutoArray lastIndexValues;
  18560 
  18561  QM_TRY(CollectWhileHasResult(
  18562      *selectStmt,
  18563      [this, &aConnection, &lastObjectStoreKey, &lastIndexValues,
  18564       deleteIndexRowStmt =
  18565           DatabaseConnection::LazyStatement{
  18566               *aConnection,
  18567               mUnique
  18568                   ? "DELETE FROM unique_index_data "
  18569                     "WHERE index_id = :"_ns +
  18570                         kStmtParamNameIndexId + " AND value = :"_ns +
  18571                         kStmtParamNameValue + ";"_ns
  18572                   : "DELETE FROM index_data "
  18573                     "WHERE index_id = :"_ns +
  18574                         kStmtParamNameIndexId + " AND value = :"_ns +
  18575                         kStmtParamNameValue + " AND object_data_key = :"_ns +
  18576                         kStmtParamNameObjectDataKey + ";"_ns}](
  18577          auto& selectStmt) mutable -> Result<Ok, nsresult> {
  18578        // We always need the index key to delete the index row.
  18579        Key indexKey;
  18580        QM_TRY(MOZ_TO_RESULT(indexKey.SetFromStatement(&selectStmt, 0)));
  18581 
  18582        QM_TRY(OkIf(!indexKey.IsUnset()), Err(NS_ERROR_FILE_CORRUPTED),
  18583               IDB_REPORT_INTERNAL_ERR_LAMBDA);
  18584 
  18585        // Don't call |lastObjectStoreKey.BindToStatement()| directly because we
  18586        // don't want to copy the same key multiple times.
  18587        const uint8_t* objectStoreKeyData;
  18588        uint32_t objectStoreKeyDataLength;
  18589        QM_TRY(MOZ_TO_RESULT(selectStmt.GetSharedBlob(
  18590            1, &objectStoreKeyDataLength, &objectStoreKeyData)));
  18591 
  18592        QM_TRY(OkIf(objectStoreKeyDataLength), Err(NS_ERROR_FILE_CORRUPTED),
  18593               IDB_REPORT_INTERNAL_ERR_LAMBDA);
  18594 
  18595        const nsDependentCString currentObjectStoreKeyBuffer(
  18596            reinterpret_cast<const char*>(objectStoreKeyData),
  18597            objectStoreKeyDataLength);
  18598        if (currentObjectStoreKeyBuffer != lastObjectStoreKey.GetBuffer()) {
  18599          // We just walked to the next object store key.
  18600          if (!lastObjectStoreKey.IsUnset()) {
  18601            // Before we move on to the next key we need to update the previous
  18602            // key's index_data_values column.
  18603            QM_TRY(MOZ_TO_RESULT(RemoveReferencesToIndex(
  18604                aConnection, lastObjectStoreKey, lastIndexValues)));
  18605          }
  18606 
  18607          // Save the object store key.
  18608          lastObjectStoreKey = Key(currentObjectStoreKeyBuffer);
  18609 
  18610          // And the |index_data_values| row if this isn't the only index.
  18611          if (!mIsLastIndex) {
  18612            lastIndexValues.ClearAndRetainStorage();
  18613            QM_TRY(MOZ_TO_RESULT(
  18614                ReadCompressedIndexDataValues(selectStmt, 2, lastIndexValues)));
  18615 
  18616            QM_TRY(OkIf(!lastIndexValues.IsEmpty()),
  18617                   Err(NS_ERROR_FILE_CORRUPTED),
  18618                   IDB_REPORT_INTERNAL_ERR_LAMBDA);
  18619          }
  18620        }
  18621 
  18622        // Now delete the index row.
  18623        {
  18624          QM_TRY_INSPECT(const auto& borrowedDeleteIndexRowStmt,
  18625                         deleteIndexRowStmt.Borrow());
  18626 
  18627          QM_TRY(MOZ_TO_RESULT(borrowedDeleteIndexRowStmt->BindInt64ByName(
  18628              kStmtParamNameIndexId, mIndexId)));
  18629 
  18630          QM_TRY(MOZ_TO_RESULT(indexKey.BindToStatement(
  18631              &*borrowedDeleteIndexRowStmt, kStmtParamNameValue)));
  18632 
  18633          if (!mUnique) {
  18634            QM_TRY(MOZ_TO_RESULT(lastObjectStoreKey.BindToStatement(
  18635                &*borrowedDeleteIndexRowStmt, kStmtParamNameObjectDataKey)));
  18636          }
  18637 
  18638          QM_TRY(MOZ_TO_RESULT(borrowedDeleteIndexRowStmt->Execute()));
  18639        }
  18640 
  18641        return Ok{};
  18642      }));
  18643 
  18644  // Take care of the last key.
  18645  if (!lastObjectStoreKey.IsUnset()) {
  18646    MOZ_ASSERT_IF(!mIsLastIndex, !lastIndexValues.IsEmpty());
  18647 
  18648    QM_TRY(MOZ_TO_RESULT(RemoveReferencesToIndex(
  18649        aConnection, lastObjectStoreKey, lastIndexValues)));
  18650  }
  18651 
  18652  QM_TRY(MOZ_TO_RESULT(aConnection->ExecuteCachedStatement(
  18653      "DELETE FROM object_store_index "
  18654      "WHERE id = :index_id;"_ns,
  18655      [indexId =
  18656           mIndexId](mozIStorageStatement& deleteStmt) -> Result<Ok, nsresult> {
  18657        QM_TRY(MOZ_TO_RESULT(deleteStmt.BindInt64ByIndex(0, indexId)));
  18658 
  18659        return Ok{};
  18660      })));
  18661 
  18662 #ifdef DEBUG
  18663  {
  18664    int32_t deletedRowCount;
  18665    MOZ_ALWAYS_SUCCEEDS(aConnection->MutableStorageConnection().GetAffectedRows(
  18666        &deletedRowCount));
  18667    MOZ_ASSERT(deletedRowCount == 1);
  18668  }
  18669 #endif
  18670 
  18671  QM_TRY(MOZ_TO_RESULT(autoSave.Commit()));
  18672 
  18673  return NS_OK;
  18674 }
  18675 
  18676 nsresult RenameIndexOp::DoDatabaseWork(DatabaseConnection* aConnection) {
  18677  MOZ_ASSERT(aConnection);
  18678  aConnection->AssertIsOnConnectionThread();
  18679 
  18680  AUTO_PROFILER_LABEL("RenameIndexOp::DoDatabaseWork", DOM);
  18681 
  18682 #ifdef DEBUG
  18683  {
  18684    // Make sure that we're not renaming an index with the same name as another
  18685    // that already exists. This should be impossible because we should have
  18686    // thrown an error long before now...
  18687    // The parameter names are not used, parameters are bound by index only
  18688    // locally in the same function.
  18689    QM_TRY_INSPECT(const bool& hasResult,
  18690                   aConnection
  18691                       ->BorrowAndExecuteSingleStepStatement(
  18692                           "SELECT name "
  18693                           "FROM object_store_index "
  18694                           "WHERE object_store_id = :object_store_id "
  18695                           "AND name = :name "
  18696                           "AND id != :id;"_ns,
  18697                           [&self = *this](auto& stmt) -> Result<Ok, nsresult> {
  18698                             QM_TRY(MOZ_TO_RESULT(stmt.BindInt64ByIndex(
  18699                                 0, self.mObjectStoreId)));
  18700                             QM_TRY(MOZ_TO_RESULT(
  18701                                 stmt.BindStringByIndex(1, self.mNewName)));
  18702                             QM_TRY(MOZ_TO_RESULT(
  18703                                 stmt.BindInt64ByIndex(2, self.mIndexId)));
  18704 
  18705                             return Ok{};
  18706                           })
  18707                       .map(IsSome),
  18708                   QM_ASSERT_UNREACHABLE);
  18709 
  18710    MOZ_ASSERT(!hasResult);
  18711  }
  18712 #else
  18713  (void)mObjectStoreId;
  18714 #endif
  18715 
  18716  DatabaseConnection::AutoSavepoint autoSave;
  18717  QM_TRY(MOZ_TO_RESULT(autoSave.Start(Transaction()))
  18718 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
  18719             ,
  18720         QM_PROPAGATE, MakeAutoSavepointCleanupHandler(*aConnection)
  18721 #endif
  18722  );
  18723 
  18724  // The parameter names are not used, parameters are bound by index only
  18725  // locally in the same function.
  18726  QM_TRY(MOZ_TO_RESULT(aConnection->ExecuteCachedStatement(
  18727      "UPDATE object_store_index "
  18728      "SET name = :name "
  18729      "WHERE id = :id;"_ns,
  18730      [&self = *this](mozIStorageStatement& stmt) -> Result<Ok, nsresult> {
  18731        QM_TRY(MOZ_TO_RESULT(stmt.BindStringByIndex(0, self.mNewName)));
  18732 
  18733        QM_TRY(MOZ_TO_RESULT(stmt.BindInt64ByIndex(1, self.mIndexId)));
  18734 
  18735        return Ok{};
  18736      })));
  18737 
  18738  QM_TRY(MOZ_TO_RESULT(autoSave.Commit()));
  18739 
  18740  return NS_OK;
  18741 }
  18742 
  18743 Result<bool, nsresult> NormalTransactionOp::ObjectStoreHasIndexes(
  18744    DatabaseConnection& aConnection, const IndexOrObjectStoreId aObjectStoreId,
  18745    const bool aMayHaveIndexes) {
  18746  aConnection.AssertIsOnConnectionThread();
  18747  MOZ_ASSERT(aObjectStoreId);
  18748 
  18749  if (Transaction().GetMode() == IDBTransaction::Mode::VersionChange &&
  18750      aMayHaveIndexes) {
  18751    // If this is a version change transaction then mObjectStoreMayHaveIndexes
  18752    // could be wrong (e.g. if a unique index failed to be created due to a
  18753    // constraint error). We have to check on this thread by asking the database
  18754    // directly.
  18755    QM_TRY_RETURN(DatabaseOperationBase::ObjectStoreHasIndexes(aConnection,
  18756                                                               aObjectStoreId));
  18757  }
  18758 
  18759 #ifdef DEBUG
  18760  QM_TRY_INSPECT(
  18761      const bool& hasIndexes,
  18762      DatabaseOperationBase::ObjectStoreHasIndexes(aConnection, aObjectStoreId),
  18763      QM_ASSERT_UNREACHABLE);
  18764  MOZ_ASSERT(aMayHaveIndexes == hasIndexes);
  18765 #endif
  18766 
  18767  return aMayHaveIndexes;
  18768 }
  18769 
  18770 Result<PreprocessParams, nsresult> NormalTransactionOp::GetPreprocessParams() {
  18771  return PreprocessParams{};
  18772 }
  18773 
  18774 nsresult NormalTransactionOp::SendPreprocessInfo() {
  18775  AssertIsOnOwningThread();
  18776  MOZ_ASSERT(!IsActorDestroyed());
  18777 
  18778  QM_TRY_INSPECT(const auto& params, GetPreprocessParams());
  18779 
  18780  MOZ_ASSERT(params.type() != PreprocessParams::T__None);
  18781 
  18782  if (NS_WARN_IF(!PBackgroundIDBRequestParent::SendPreprocess(params))) {
  18783    IDB_REPORT_INTERNAL_ERR();
  18784    return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
  18785  }
  18786 
  18787  return NS_OK;
  18788 }
  18789 
  18790 nsresult NormalTransactionOp::SendSuccessResult() {
  18791  AssertIsOnOwningThread();
  18792 
  18793  if (!IsActorDestroyed()) {
  18794    static const size_t kMaxIDBMsgOverhead = 1024 * 1024 * 10;  // 10MB
  18795    const uint32_t maximalSizeFromPref =
  18796        IndexedDatabaseManager::MaxSerializedMsgSize();
  18797    MOZ_ASSERT(maximalSizeFromPref > kMaxIDBMsgOverhead);
  18798    const size_t kMaxMessageSize = maximalSizeFromPref - kMaxIDBMsgOverhead;
  18799 
  18800    RequestResponse response;
  18801    size_t responseSize = kMaxMessageSize;
  18802    GetResponse(response, &responseSize);
  18803 
  18804    // TODO: Adjust the calculation of the response size in relevant
  18805    // GetResponse methods to account for the fallback to shared memory during
  18806    // serialization of the primary key and index keys if their size exceeds
  18807    // IPC::kMessageBufferShmemThreshold. This ensures the calculated size
  18808    // accurately reflects the actual IPC message size.
  18809    // See also bug 1945040.
  18810 
  18811    if (responseSize >= kMaxMessageSize) {
  18812      nsPrintfCString warning(
  18813          "The serialized value is too large"
  18814          " (size=%zu bytes, max=%zu bytes).",
  18815          responseSize, kMaxMessageSize);
  18816      NS_WARNING(warning.get());
  18817      return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
  18818    }
  18819 
  18820    MOZ_ASSERT(response.type() != RequestResponse::T__None);
  18821 
  18822    if (response.type() == RequestResponse::Tnsresult) {
  18823      MOZ_ASSERT(NS_FAILED(response.get_nsresult()));
  18824 
  18825      return response.get_nsresult();
  18826    }
  18827 
  18828    if (NS_WARN_IF(
  18829            !PBackgroundIDBRequestParent::Send__delete__(this, response))) {
  18830      IDB_REPORT_INTERNAL_ERR();
  18831      return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
  18832    }
  18833  }
  18834 
  18835 #ifdef DEBUG
  18836  mResponseSent = true;
  18837 #endif
  18838 
  18839  return NS_OK;
  18840 }
  18841 
  18842 bool NormalTransactionOp::SendFailureResult(nsresult aResultCode) {
  18843  AssertIsOnOwningThread();
  18844  MOZ_ASSERT(NS_FAILED(aResultCode));
  18845 
  18846  bool result = false;
  18847 
  18848  if (!IsActorDestroyed()) {
  18849    result = PBackgroundIDBRequestParent::Send__delete__(
  18850        this, ClampResultCode(aResultCode));
  18851  }
  18852 
  18853 #ifdef DEBUG
  18854  mResponseSent = true;
  18855 #endif
  18856 
  18857  return result;
  18858 }
  18859 
  18860 void NormalTransactionOp::Cleanup() {
  18861  AssertIsOnOwningThread();
  18862  MOZ_ASSERT_IF(!IsActorDestroyed(), mResponseSent);
  18863 
  18864  TransactionDatabaseOperationBase::Cleanup();
  18865 }
  18866 
  18867 void NormalTransactionOp::ActorDestroy(ActorDestroyReason aWhy) {
  18868  AssertIsOnOwningThread();
  18869 
  18870  NoteActorDestroyed();
  18871 
  18872  // Assume ActorDestroy can happen at any time, so we can't probe the current
  18873  // state since mInternalState can be modified on any thread (only one thread
  18874  // at a time based on the state machine).
  18875  // However we can use mWaitingForContinue which is only touched on the owning
  18876  // thread.  If mWaitingForContinue is true, we can also modify mInternalState
  18877  // since we are guaranteed that there are no pending runnables which would
  18878  // probe mInternalState to decide what code needs to run (there shouldn't be
  18879  // any running runnables on other threads either).
  18880 
  18881  if (IsWaitingForContinue()) {
  18882    NoteContinueReceived();
  18883  }
  18884 
  18885  // We don't have to handle the case when mWaitingForContinue is not true since
  18886  // it means that either nothing has been initialized yet, so nothing to
  18887  // cleanup or there are pending runnables that will detect that the actor has
  18888  // been destroyed and cleanup accordingly.
  18889 }
  18890 
  18891 mozilla::ipc::IPCResult NormalTransactionOp::RecvContinue(
  18892    const PreprocessResponse& aResponse) {
  18893  AssertIsOnOwningThread();
  18894 
  18895  switch (aResponse.type()) {
  18896    case PreprocessResponse::Tnsresult:
  18897      SetFailureCode(aResponse.get_nsresult());
  18898      break;
  18899 
  18900    case PreprocessResponse::TObjectStoreGetPreprocessResponse:
  18901    case PreprocessResponse::TObjectStoreGetAllPreprocessResponse:
  18902      break;
  18903 
  18904    default:
  18905      MOZ_CRASH("Should never get here!");
  18906  }
  18907 
  18908  NoteContinueReceived();
  18909 
  18910  return IPC_OK();
  18911 }
  18912 
  18913 ObjectStoreAddOrPutRequestOp::ObjectStoreAddOrPutRequestOp(
  18914    SafeRefPtr<TransactionBase> aTransaction, const int64_t aRequestId,
  18915    RequestParams&& aParams)
  18916    : NormalTransactionOp(std::move(aTransaction), aRequestId),
  18917      mParams(
  18918          std::move(aParams.type() == RequestParams::TObjectStoreAddParams
  18919                        ? aParams.get_ObjectStoreAddParams().commonParams()
  18920                        : aParams.get_ObjectStorePutParams().commonParams())),
  18921      mOriginMetadata(Transaction().GetDatabase().OriginMetadata()),
  18922      mPersistenceType(Transaction().GetDatabase().Type()),
  18923      mOverwrite(aParams.type() == RequestParams::TObjectStorePutParams),
  18924      mObjectStoreMayHaveIndexes(false) {
  18925  MOZ_ASSERT(aParams.type() == RequestParams::TObjectStoreAddParams ||
  18926             aParams.type() == RequestParams::TObjectStorePutParams);
  18927 
  18928  mMetadata =
  18929      Transaction().GetMetadataForObjectStoreId(mParams.objectStoreId());
  18930  MOZ_ASSERT(mMetadata);
  18931 
  18932  mObjectStoreMayHaveIndexes = mMetadata->HasLiveIndexes();
  18933 
  18934  mDataOverThreshold =
  18935      snappy::MaxCompressedLength(mParams.cloneInfo().data().data.Size()) >
  18936      IndexedDatabaseManager::DataThreshold();
  18937 }
  18938 
  18939 nsresult ObjectStoreAddOrPutRequestOp::RemoveOldIndexDataValues(
  18940    DatabaseConnection* aConnection) {
  18941  AssertIsOnConnectionThread();
  18942  MOZ_ASSERT(aConnection);
  18943  MOZ_ASSERT(mOverwrite);
  18944  MOZ_ASSERT(!mResponse.IsUnset());
  18945 
  18946 #ifdef DEBUG
  18947  {
  18948    QM_TRY_INSPECT(const bool& hasIndexes,
  18949                   DatabaseOperationBase::ObjectStoreHasIndexes(
  18950                       *aConnection, mParams.objectStoreId()),
  18951                   QM_ASSERT_UNREACHABLE);
  18952 
  18953    MOZ_ASSERT(hasIndexes,
  18954               "Don't use this slow method if there are no indexes!");
  18955  }
  18956 #endif
  18957 
  18958  QM_TRY_INSPECT(
  18959      const auto& indexValuesStmt,
  18960      aConnection->BorrowAndExecuteSingleStepStatement(
  18961          "SELECT index_data_values "
  18962          "FROM object_data "
  18963          "WHERE object_store_id = :"_ns +
  18964              kStmtParamNameObjectStoreId + " AND key = :"_ns +
  18965              kStmtParamNameKey + ";"_ns,
  18966          [&self = *this](auto& stmt) -> mozilla::Result<Ok, nsresult> {
  18967            QM_TRY(MOZ_TO_RESULT(stmt.BindInt64ByName(
  18968                kStmtParamNameObjectStoreId, self.mParams.objectStoreId())));
  18969 
  18970            QM_TRY(MOZ_TO_RESULT(
  18971                self.mResponse.BindToStatement(&stmt, kStmtParamNameKey)));
  18972 
  18973            return Ok{};
  18974          }));
  18975 
  18976  if (indexValuesStmt) {
  18977    QM_TRY_INSPECT(const auto& existingIndexValues,
  18978                   ReadCompressedIndexDataValues(**indexValuesStmt, 0));
  18979 
  18980    QM_TRY(MOZ_TO_RESULT(
  18981        DeleteIndexDataTableRows(aConnection, mResponse, existingIndexValues)));
  18982  }
  18983 
  18984  return NS_OK;
  18985 }
  18986 
  18987 bool ObjectStoreAddOrPutRequestOp::Init(TransactionBase& aTransaction) {
  18988  AssertIsOnOwningThread();
  18989 
  18990  const nsTArray<IndexUpdateInfo>& indexUpdateInfos =
  18991      mParams.indexUpdateInfos();
  18992 
  18993  if (!indexUpdateInfos.IsEmpty()) {
  18994    mUniqueIndexTable.emplace();
  18995 
  18996    for (const auto& updateInfo : indexUpdateInfos) {
  18997      auto indexMetadata = mMetadata->mIndexes.Lookup(updateInfo.indexId());
  18998      MOZ_ALWAYS_TRUE(indexMetadata);
  18999 
  19000      MOZ_ASSERT(!(*indexMetadata)->mDeleted);
  19001 
  19002      const IndexOrObjectStoreId& indexId =
  19003          (*indexMetadata)->mCommonMetadata.id();
  19004      const bool& unique = (*indexMetadata)->mCommonMetadata.unique();
  19005 
  19006      MOZ_ASSERT(indexId == updateInfo.indexId());
  19007      MOZ_ASSERT_IF(!(*indexMetadata)->mCommonMetadata.multiEntry(),
  19008                    !mUniqueIndexTable.ref().Contains(indexId));
  19009 
  19010      if (NS_WARN_IF(!mUniqueIndexTable.ref().InsertOrUpdate(indexId, unique,
  19011                                                             fallible))) {
  19012        return false;
  19013      }
  19014    }
  19015  } else if (mOverwrite) {
  19016    mUniqueIndexTable.emplace();
  19017  }
  19018 
  19019  if (mUniqueIndexTable.isSome()) {
  19020    mUniqueIndexTable.ref().MarkImmutable();
  19021  }
  19022 
  19023  QM_TRY_UNWRAP(
  19024      mStoredFileInfos,
  19025      TransformIntoNewArray(
  19026          mParams.fileAddInfos(),
  19027          [](const auto& fileAddInfo) {
  19028            MOZ_ASSERT(fileAddInfo.type() == StructuredCloneFileBase::eBlob ||
  19029                       fileAddInfo.type() ==
  19030                           StructuredCloneFileBase::eMutableFile);
  19031 
  19032            switch (fileAddInfo.type()) {
  19033              case StructuredCloneFileBase::eBlob: {
  19034                PBackgroundIDBDatabaseFileParent* file =
  19035                    fileAddInfo.file().AsParent();
  19036                MOZ_ASSERT(file);
  19037 
  19038                auto* const fileActor = static_cast<DatabaseFile*>(file);
  19039                MOZ_ASSERT(fileActor);
  19040 
  19041                return StoredFileInfo::CreateForBlob(
  19042                    fileActor->GetFileInfoPtr(), fileActor);
  19043              }
  19044 
  19045              default:
  19046                MOZ_CRASH("Should never get here!");
  19047            }
  19048          },
  19049          fallible),
  19050      false);
  19051 
  19052  if (mDataOverThreshold) {
  19053    auto fileInfo =
  19054        aTransaction.GetDatabase().GetFileManager().CreateFileInfo();
  19055    if (NS_WARN_IF(!fileInfo)) {
  19056      return false;
  19057    }
  19058 
  19059    mStoredFileInfos.EmplaceBack(StoredFileInfo::CreateForStructuredClone(
  19060        std::move(fileInfo),
  19061        MakeRefPtr<SCInputStream>(mParams.cloneInfo().data().data)));
  19062  }
  19063 
  19064  return true;
  19065 }
  19066 
  19067 nsresult ObjectStoreAddOrPutRequestOp::DoDatabaseWork(
  19068    DatabaseConnection* aConnection) {
  19069  MOZ_ASSERT(aConnection);
  19070  aConnection->AssertIsOnConnectionThread();
  19071  MOZ_ASSERT(aConnection->HasStorageConnection());
  19072 
  19073  AUTO_PROFILER_LABEL("ObjectStoreAddOrPutRequestOp::DoDatabaseWork", DOM);
  19074 
  19075  DatabaseConnection::AutoSavepoint autoSave;
  19076  QM_TRY(MOZ_TO_RESULT(autoSave.Start(Transaction()))
  19077 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
  19078             ,
  19079         QM_PROPAGATE, MakeAutoSavepointCleanupHandler(*aConnection)
  19080 #endif
  19081  );
  19082 
  19083  QM_TRY_INSPECT(const bool& objectStoreHasIndexes,
  19084                 ObjectStoreHasIndexes(*aConnection, mParams.objectStoreId(),
  19085                                       mObjectStoreMayHaveIndexes));
  19086 
  19087  // This will be the final key we use.
  19088  Key& key = mResponse;
  19089  key = mParams.key();
  19090 
  19091  const bool keyUnset = key.IsUnset();
  19092  const IndexOrObjectStoreId osid = mParams.objectStoreId();
  19093 
  19094  // First delete old index_data_values if we're overwriting something and we
  19095  // have indexes.
  19096  if (mOverwrite && !keyUnset && objectStoreHasIndexes) {
  19097    QM_TRY(MOZ_TO_RESULT(RemoveOldIndexDataValues(aConnection)));
  19098  }
  19099 
  19100  int64_t autoIncrementNum = 0;
  19101 
  19102  {
  19103    // The "|| keyUnset" here is mostly a debugging tool. If a key isn't
  19104    // specified we should never have a collision and so it shouldn't matter
  19105    // if we allow overwrite or not. By not allowing overwrite we raise
  19106    // detectable errors rather than corrupting data.
  19107    const auto optReplaceDirective =
  19108        (!mOverwrite || keyUnset) ? ""_ns : "OR REPLACE "_ns;
  19109    QM_TRY_INSPECT(const auto& stmt,
  19110                   aConnection->BorrowCachedStatement(
  19111                       "INSERT "_ns + optReplaceDirective +
  19112                       "INTO object_data "
  19113                       "(object_store_id, key, file_ids, data) "
  19114                       "VALUES (:"_ns +
  19115                       kStmtParamNameObjectStoreId + ", :"_ns +
  19116                       kStmtParamNameKey + ", :"_ns + kStmtParamNameFileIds +
  19117                       ", :"_ns + kStmtParamNameData + ");"_ns));
  19118 
  19119    QM_TRY(MOZ_TO_RESULT(
  19120        stmt->BindInt64ByName(kStmtParamNameObjectStoreId, osid)));
  19121 
  19122    const SerializedStructuredCloneWriteInfo& cloneInfo = mParams.cloneInfo();
  19123    const JSStructuredCloneData& cloneData = cloneInfo.data().data;
  19124    const size_t cloneDataSize = cloneData.Size();
  19125 
  19126    MOZ_ASSERT(!keyUnset || mMetadata->mCommonMetadata.autoIncrement(),
  19127               "Should have key unless autoIncrement");
  19128 
  19129    if (mMetadata->mCommonMetadata.autoIncrement()) {
  19130      if (keyUnset) {
  19131        {
  19132          const auto&& lockedAutoIncrementIds =
  19133              mMetadata->mAutoIncrementIds.Lock();
  19134 
  19135          autoIncrementNum = lockedAutoIncrementIds->next;
  19136        }
  19137 
  19138        MOZ_ASSERT(autoIncrementNum > 0);
  19139 
  19140        if (autoIncrementNum > (1LL << 53)) {
  19141          return NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
  19142        }
  19143 
  19144        QM_TRY(key.SetFromInteger(autoIncrementNum));
  19145 
  19146        // Update index keys if primary key is preserved in child.
  19147        for (auto& updateInfo : mParams.indexUpdateInfos()) {
  19148          updateInfo.value().MaybeUpdateAutoIncrementKey(autoIncrementNum);
  19149        }
  19150      } else if (key.IsFloat()) {
  19151        double numericKey = key.ToFloat();
  19152        numericKey = std::min(numericKey, double(1LL << 53));
  19153        numericKey = floor(numericKey);
  19154 
  19155        const auto&& lockedAutoIncrementIds =
  19156            mMetadata->mAutoIncrementIds.Lock();
  19157        if (numericKey >= lockedAutoIncrementIds->next) {
  19158          autoIncrementNum = numericKey;
  19159        }
  19160      }
  19161 
  19162      if (keyUnset && mMetadata->mCommonMetadata.keyPath().IsValid()) {
  19163        const SerializedStructuredCloneWriteInfo& cloneInfo =
  19164            mParams.cloneInfo();
  19165        MOZ_ASSERT(cloneInfo.offsetToKeyProp());
  19166        MOZ_ASSERT(cloneDataSize > sizeof(uint64_t));
  19167        MOZ_ASSERT(cloneInfo.offsetToKeyProp() <=
  19168                   (cloneDataSize - sizeof(uint64_t)));
  19169 
  19170        // Special case where someone put an object into an autoIncrement'ing
  19171        // objectStore with no key in its keyPath set. We needed to figure out
  19172        // which row id we would get above before we could set that properly.
  19173        uint64_t keyPropValue =
  19174            ReinterpretDoubleAsUInt64(static_cast<double>(autoIncrementNum));
  19175 
  19176        static const size_t keyPropSize = sizeof(uint64_t);
  19177 
  19178        char keyPropBuffer[keyPropSize];
  19179        LittleEndian::writeUint64(keyPropBuffer, keyPropValue);
  19180 
  19181        auto iter = cloneData.Start();
  19182        MOZ_ALWAYS_TRUE(cloneData.Advance(iter, cloneInfo.offsetToKeyProp()));
  19183        MOZ_ALWAYS_TRUE(
  19184            cloneData.UpdateBytes(iter, keyPropBuffer, keyPropSize));
  19185      }
  19186    }
  19187 
  19188    key.BindToStatement(&*stmt, kStmtParamNameKey);
  19189 
  19190    if (mDataOverThreshold) {
  19191      // The data we store in the SQLite database is a (signed) 64-bit integer.
  19192      // The flags are left-shifted 32 bits so the max value is 0xFFFFFFFF.
  19193      // The file_ids index occupies the lower 32 bits and its max is
  19194      // 0xFFFFFFFF.
  19195      static const uint32_t kCompressedFlag = (1 << 0);
  19196 
  19197      uint32_t flags = 0;
  19198      flags |= kCompressedFlag;
  19199 
  19200      const uint32_t index = mStoredFileInfos.Length() - 1;
  19201 
  19202      const int64_t data = (uint64_t(flags) << 32) | index;
  19203 
  19204      QM_TRY(MOZ_TO_RESULT(stmt->BindInt64ByName(kStmtParamNameData, data)));
  19205    } else {
  19206      AutoTArray<char, 4096> flatCloneData;  // 4096 from JSStructuredCloneData
  19207      QM_TRY(OkIf(flatCloneData.SetLength(cloneDataSize, fallible)),
  19208             Err(NS_ERROR_OUT_OF_MEMORY));
  19209 
  19210      {
  19211        auto iter = cloneData.Start();
  19212        MOZ_ALWAYS_TRUE(
  19213            cloneData.ReadBytes(iter, flatCloneData.Elements(), cloneDataSize));
  19214      }
  19215 
  19216      // Compress the bytes before adding into the database.
  19217      const char* const uncompressed = flatCloneData.Elements();
  19218      const size_t uncompressedLength = cloneDataSize;
  19219 
  19220      size_t compressedLength = snappy::MaxCompressedLength(uncompressedLength);
  19221 
  19222      UniqueFreePtr<char> compressed(
  19223          static_cast<char*>(malloc(compressedLength)));
  19224      if (NS_WARN_IF(!compressed)) {
  19225        return NS_ERROR_OUT_OF_MEMORY;
  19226      }
  19227 
  19228      snappy::RawCompress(uncompressed, uncompressedLength, compressed.get(),
  19229                          &compressedLength);
  19230 
  19231      uint8_t* const dataBuffer =
  19232          reinterpret_cast<uint8_t*>(compressed.release());
  19233      const size_t dataBufferLength = compressedLength;
  19234 
  19235      QM_TRY(MOZ_TO_RESULT(stmt->BindAdoptedBlobByName(
  19236          kStmtParamNameData, dataBuffer, dataBufferLength)));
  19237    }
  19238 
  19239    if (!mStoredFileInfos.IsEmpty()) {
  19240      // Moved outside the loop to allow it to be cached when demanded by the
  19241      // first write.  (We may have mStoredFileInfos without any required
  19242      // writes.)
  19243      Maybe<FileHelper> fileHelper;
  19244      nsAutoString fileIds;
  19245 
  19246      for (auto& storedFileInfo : mStoredFileInfos) {
  19247        MOZ_ASSERT(storedFileInfo.IsValid());
  19248 
  19249        QM_TRY_INSPECT(const auto& inputStream,
  19250                       storedFileInfo.GetInputStream());
  19251 
  19252        if (inputStream) {
  19253          if (fileHelper.isNothing()) {
  19254            fileHelper.emplace(Transaction().GetDatabase().GetFileManagerPtr());
  19255            QM_TRY(MOZ_TO_RESULT(fileHelper->Init()),
  19256                   NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR,
  19257                   IDB_REPORT_INTERNAL_ERR_LAMBDA);
  19258          }
  19259 
  19260          const DatabaseFileInfo& fileInfo = storedFileInfo.GetFileInfo();
  19261          const DatabaseFileManager& fileManager = fileInfo.Manager();
  19262 
  19263          const auto file = fileHelper->GetFile(fileInfo);
  19264          QM_TRY(OkIf(file), NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR,
  19265                 IDB_REPORT_INTERNAL_ERR_LAMBDA);
  19266 
  19267          const auto journalFile = fileHelper->GetJournalFile(fileInfo);
  19268          QM_TRY(OkIf(journalFile), NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR,
  19269                 IDB_REPORT_INTERNAL_ERR_LAMBDA);
  19270 
  19271          nsCString fileKeyId;
  19272          fileKeyId.AppendInt(fileInfo.Id());
  19273 
  19274          const auto maybeKey =
  19275              fileManager.IsInPrivateBrowsingMode()
  19276                  ? fileManager.MutableCipherKeyManagerRef().Get(fileKeyId)
  19277                  : Nothing();
  19278 
  19279          QM_TRY(MOZ_TO_RESULT(fileHelper->CreateFileFromStream(
  19280                                   *file, *journalFile, *inputStream,
  19281                                   storedFileInfo.ShouldCompress(), maybeKey))
  19282                     .mapErr([](const nsresult rv) {
  19283                       if (NS_ERROR_GET_MODULE(rv) !=
  19284                           NS_ERROR_MODULE_DOM_INDEXEDDB) {
  19285                         IDB_REPORT_INTERNAL_ERR();
  19286                         return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
  19287                       }
  19288                       return rv;
  19289                     }),
  19290                 QM_PROPAGATE,
  19291                 ([&fileManager, &file = *file,
  19292                   &journalFile = *journalFile](const auto) {
  19293                   // Try to remove the file if the copy failed.
  19294                   QM_TRY(MOZ_TO_RESULT(
  19295                              fileManager.SyncDeleteFile(file, journalFile)),
  19296                          QM_VOID);
  19297                 }));
  19298 
  19299          storedFileInfo.NotifyWriteSucceeded();
  19300        }
  19301 
  19302        if (!fileIds.IsEmpty()) {
  19303          fileIds.Append(' ');
  19304        }
  19305        storedFileInfo.Serialize(fileIds);
  19306      }
  19307 
  19308      QM_TRY(MOZ_TO_RESULT(
  19309          stmt->BindStringByName(kStmtParamNameFileIds, fileIds)));
  19310    } else {
  19311      QM_TRY(MOZ_TO_RESULT(stmt->BindNullByName(kStmtParamNameFileIds)));
  19312    }
  19313 
  19314    QM_TRY(MOZ_TO_RESULT(stmt->Execute()), QM_PROPAGATE,
  19315           [keyUnset = DebugOnly{keyUnset}](const nsresult rv) {
  19316             if (rv == NS_ERROR_STORAGE_CONSTRAINT) {
  19317               MOZ_ASSERT(!keyUnset, "Generated key had a collision!");
  19318             }
  19319           });
  19320  }
  19321 
  19322  // Update our indexes if needed.
  19323  if (!mParams.indexUpdateInfos().IsEmpty()) {
  19324    MOZ_ASSERT(mUniqueIndexTable.isSome());
  19325 
  19326    // Write the index_data_values column.
  19327    QM_TRY_INSPECT(const auto& indexValues,
  19328                   IndexDataValuesFromUpdateInfos(mParams.indexUpdateInfos(),
  19329                                                  mUniqueIndexTable.ref()));
  19330 
  19331    QM_TRY(
  19332        MOZ_TO_RESULT(UpdateIndexValues(aConnection, osid, key, indexValues)));
  19333 
  19334    QM_TRY(MOZ_TO_RESULT(
  19335        InsertIndexTableRows(aConnection, osid, key, indexValues)));
  19336  }
  19337 
  19338  QM_TRY(MOZ_TO_RESULT(autoSave.Commit()));
  19339 
  19340  if (autoIncrementNum) {
  19341    {
  19342      auto&& lockedAutoIncrementIds = mMetadata->mAutoIncrementIds.Lock();
  19343 
  19344      lockedAutoIncrementIds->next = autoIncrementNum + 1;
  19345    }
  19346 
  19347    Transaction().NoteModifiedAutoIncrementObjectStore(mMetadata);
  19348  }
  19349 
  19350  return NS_OK;
  19351 }
  19352 
  19353 void ObjectStoreAddOrPutRequestOp::GetResponse(RequestResponse& aResponse,
  19354                                               size_t* aResponseSize) {
  19355  AssertIsOnOwningThread();
  19356 
  19357  if (mOverwrite) {
  19358    aResponse = ObjectStorePutResponse(mResponse);
  19359    *aResponseSize = mResponse.GetBuffer().Length();
  19360  } else {
  19361    aResponse = ObjectStoreAddResponse(mResponse);
  19362    *aResponseSize = mResponse.GetBuffer().Length();
  19363  }
  19364 }
  19365 
  19366 void ObjectStoreAddOrPutRequestOp::Cleanup() {
  19367  AssertIsOnOwningThread();
  19368 
  19369  mStoredFileInfos.Clear();
  19370 
  19371  NormalTransactionOp::Cleanup();
  19372 }
  19373 
  19374 NS_IMPL_ISUPPORTS(ObjectStoreAddOrPutRequestOp::SCInputStream, nsIInputStream)
  19375 
  19376 NS_IMETHODIMP
  19377 ObjectStoreAddOrPutRequestOp::SCInputStream::Close() { return NS_OK; }
  19378 
  19379 NS_IMETHODIMP
  19380 ObjectStoreAddOrPutRequestOp::SCInputStream::Available(uint64_t* _retval) {
  19381  return NS_ERROR_NOT_IMPLEMENTED;
  19382 }
  19383 
  19384 NS_IMETHODIMP
  19385 ObjectStoreAddOrPutRequestOp::SCInputStream::StreamStatus() { return NS_OK; }
  19386 
  19387 NS_IMETHODIMP
  19388 ObjectStoreAddOrPutRequestOp::SCInputStream::Read(char* aBuf, uint32_t aCount,
  19389                                                  uint32_t* _retval) {
  19390  return ReadSegments(NS_CopySegmentToBuffer, aBuf, aCount, _retval);
  19391 }
  19392 
  19393 NS_IMETHODIMP
  19394 ObjectStoreAddOrPutRequestOp::SCInputStream::ReadSegments(
  19395    nsWriteSegmentFun aWriter, void* aClosure, uint32_t aCount,
  19396    uint32_t* _retval) {
  19397  *_retval = 0;
  19398 
  19399  while (aCount) {
  19400    uint32_t count = std::min(uint32_t(mIter.RemainingInSegment()), aCount);
  19401    if (!count) {
  19402      // We've run out of data in the last segment.
  19403      break;
  19404    }
  19405 
  19406    uint32_t written;
  19407    nsresult rv =
  19408        aWriter(this, aClosure, mIter.Data(), *_retval, count, &written);
  19409    if (NS_WARN_IF(NS_FAILED(rv))) {
  19410      // InputStreams do not propagate errors to caller.
  19411      return NS_OK;
  19412    }
  19413 
  19414    // Writer should write what we asked it to write.
  19415    MOZ_ASSERT(written == count);
  19416 
  19417    *_retval += count;
  19418    aCount -= count;
  19419 
  19420    if (NS_WARN_IF(!mData.Advance(mIter, count))) {
  19421      // InputStreams do not propagate errors to caller.
  19422      return NS_OK;
  19423    }
  19424  }
  19425 
  19426  return NS_OK;
  19427 }
  19428 
  19429 NS_IMETHODIMP
  19430 ObjectStoreAddOrPutRequestOp::SCInputStream::IsNonBlocking(bool* _retval) {
  19431  *_retval = false;
  19432  return NS_OK;
  19433 }
  19434 
  19435 ObjectStoreGetRequestOp::ObjectStoreGetRequestOp(
  19436    SafeRefPtr<TransactionBase> aTransaction, const int64_t aRequestId,
  19437    const RequestParams& aParams, bool aGetAll)
  19438    : NormalTransactionOp(std::move(aTransaction), aRequestId),
  19439      mObjectStoreId(aGetAll
  19440                         ? aParams.get_ObjectStoreGetAllParams().objectStoreId()
  19441                         : aParams.get_ObjectStoreGetParams().objectStoreId()),
  19442      mDatabase(Transaction().GetDatabasePtr()),
  19443      mOptionalKeyRange(
  19444          aGetAll ? aParams.get_ObjectStoreGetAllParams().optionalKeyRange()
  19445                  : Some(aParams.get_ObjectStoreGetParams().keyRange())),
  19446      mBackgroundParent(Transaction().GetBackgroundParent()),
  19447      mPreprocessInfoCount(0),
  19448      mLimit(aGetAll ? aParams.get_ObjectStoreGetAllParams().limit() : 1),
  19449      mGetAll(aGetAll) {
  19450  MOZ_ASSERT(aParams.type() == RequestParams::TObjectStoreGetParams ||
  19451             aParams.type() == RequestParams::TObjectStoreGetAllParams);
  19452  MOZ_ASSERT(mObjectStoreId);
  19453  MOZ_ASSERT(mDatabase);
  19454  MOZ_ASSERT_IF(!aGetAll, mOptionalKeyRange.isSome());
  19455  MOZ_ASSERT(mBackgroundParent);
  19456 }
  19457 
  19458 template <typename T>
  19459 Result<T, nsresult> ObjectStoreGetRequestOp::ConvertResponse(
  19460    StructuredCloneReadInfoParent&& aInfo) {
  19461  T result;
  19462 
  19463  static_assert(std::is_same_v<T, SerializedStructuredCloneReadInfo> ||
  19464                std::is_same_v<T, PreprocessInfo>);
  19465 
  19466  if constexpr (std::is_same_v<T, SerializedStructuredCloneReadInfo>) {
  19467    result.data().data = aInfo.ReleaseData();
  19468    result.hasPreprocessInfo() = aInfo.HasPreprocessInfo();
  19469  }
  19470 
  19471  QM_TRY_UNWRAP(result.files(), SerializeStructuredCloneFiles(
  19472                                    mDatabase, aInfo.Files(),
  19473                                    std::is_same_v<T, PreprocessInfo>));
  19474 
  19475  return result;
  19476 }
  19477 
  19478 nsresult ObjectStoreGetRequestOp::DoDatabaseWork(
  19479    DatabaseConnection* aConnection) {
  19480  MOZ_ASSERT(aConnection);
  19481  aConnection->AssertIsOnConnectionThread();
  19482  MOZ_ASSERT_IF(!mGetAll, mOptionalKeyRange.isSome());
  19483  MOZ_ASSERT_IF(!mGetAll, mLimit == 1);
  19484 
  19485  AUTO_PROFILER_LABEL("ObjectStoreGetRequestOp::DoDatabaseWork", DOM);
  19486 
  19487  const nsCString query =
  19488      "SELECT file_ids, data "
  19489      "FROM object_data "
  19490      "WHERE object_store_id = :"_ns +
  19491      kStmtParamNameObjectStoreId +
  19492      MaybeGetBindingClauseForKeyRange(mOptionalKeyRange, kColumnNameKey) +
  19493      " ORDER BY key ASC"_ns +
  19494      (mLimit ? kOpenLimit + IntToCString(mLimit) : EmptyCString());
  19495 
  19496  QM_TRY_INSPECT(const auto& stmt, aConnection->BorrowCachedStatement(query));
  19497 
  19498  QM_TRY(MOZ_TO_RESULT(
  19499      stmt->BindInt64ByName(kStmtParamNameObjectStoreId, mObjectStoreId)));
  19500 
  19501  if (mOptionalKeyRange.isSome()) {
  19502    QM_TRY(MOZ_TO_RESULT(
  19503        BindKeyRangeToStatement(mOptionalKeyRange.ref(), &*stmt)));
  19504  }
  19505 
  19506  QM_TRY(CollectWhileHasResult(
  19507      *stmt, [this](auto& stmt) mutable -> mozilla::Result<Ok, nsresult> {
  19508        QM_TRY_UNWRAP(auto cloneInfo,
  19509                      GetStructuredCloneReadInfoFromStatement(
  19510                          &stmt, 1, 0, mDatabase->GetFileManager()));
  19511 
  19512        if (cloneInfo.HasPreprocessInfo()) {
  19513          mPreprocessInfoCount++;
  19514        }
  19515 
  19516        QM_TRY(OkIf(mResponse.EmplaceBack(fallible, std::move(cloneInfo))),
  19517               Err(NS_ERROR_OUT_OF_MEMORY));
  19518 
  19519        return Ok{};
  19520      }));
  19521 
  19522  MOZ_ASSERT_IF(!mGetAll, mResponse.Length() <= 1);
  19523 
  19524  return NS_OK;
  19525 }
  19526 
  19527 bool ObjectStoreGetRequestOp::HasPreprocessInfo() {
  19528  return mPreprocessInfoCount > 0;
  19529 }
  19530 
  19531 Result<PreprocessParams, nsresult>
  19532 ObjectStoreGetRequestOp::GetPreprocessParams() {
  19533  AssertIsOnOwningThread();
  19534  MOZ_ASSERT(!mResponse.IsEmpty());
  19535 
  19536  if (mGetAll) {
  19537    auto params = ObjectStoreGetAllPreprocessParams();
  19538 
  19539    auto& preprocessInfos = params.preprocessInfos();
  19540    if (NS_WARN_IF(
  19541            !preprocessInfos.SetCapacity(mPreprocessInfoCount, fallible))) {
  19542      return Err(NS_ERROR_OUT_OF_MEMORY);
  19543    }
  19544 
  19545    QM_TRY(TransformIfAbortOnErr(
  19546        std::make_move_iterator(mResponse.begin()),
  19547        std::make_move_iterator(mResponse.end()),
  19548        MakeBackInserter(preprocessInfos),
  19549        [](const auto& info) { return info.HasPreprocessInfo(); },
  19550        [&self = *this](StructuredCloneReadInfoParent&& info) {
  19551          return self.ConvertResponse<PreprocessInfo>(std::move(info));
  19552        }));
  19553 
  19554    return PreprocessParams{std::move(params)};
  19555  }
  19556 
  19557  auto params = ObjectStoreGetPreprocessParams();
  19558 
  19559  QM_TRY_UNWRAP(params.preprocessInfo(),
  19560                ConvertResponse<PreprocessInfo>(std::move(mResponse[0])));
  19561 
  19562  return PreprocessParams{std::move(params)};
  19563 }
  19564 
  19565 void ObjectStoreGetRequestOp::GetResponse(RequestResponse& aResponse,
  19566                                          size_t* aResponseSize) {
  19567  MOZ_ASSERT_IF(mLimit, mResponse.Length() <= mLimit);
  19568 
  19569  if (mGetAll) {
  19570    aResponse = ObjectStoreGetAllResponse();
  19571    *aResponseSize = 0;
  19572 
  19573    if (!mResponse.IsEmpty()) {
  19574      QM_TRY_UNWRAP(
  19575          aResponse.get_ObjectStoreGetAllResponse().cloneInfos(),
  19576          TransformIntoNewArrayAbortOnErr(
  19577              std::make_move_iterator(mResponse.begin()),
  19578              std::make_move_iterator(mResponse.end()),
  19579              [this, &aResponseSize](StructuredCloneReadInfoParent&& info) {
  19580                *aResponseSize += info.Size();
  19581                return ConvertResponse<SerializedStructuredCloneReadInfo>(
  19582                    std::move(info));
  19583              },
  19584              fallible),
  19585          QM_VOID, [&aResponse](const nsresult result) { aResponse = result; });
  19586    }
  19587 
  19588    return;
  19589  }
  19590 
  19591  aResponse = ObjectStoreGetResponse();
  19592  *aResponseSize = 0;
  19593 
  19594  if (!mResponse.IsEmpty()) {
  19595    SerializedStructuredCloneReadInfo& serializedInfo =
  19596        aResponse.get_ObjectStoreGetResponse().cloneInfo();
  19597 
  19598    *aResponseSize += mResponse[0].Size();
  19599    QM_TRY_UNWRAP(serializedInfo,
  19600                  ConvertResponse<SerializedStructuredCloneReadInfo>(
  19601                      std::move(mResponse[0])),
  19602                  QM_VOID,
  19603                  [&aResponse](const nsresult result) { aResponse = result; });
  19604  }
  19605 }
  19606 
  19607 ObjectStoreGetKeyRequestOp::ObjectStoreGetKeyRequestOp(
  19608    SafeRefPtr<TransactionBase> aTransaction, const int64_t aRequestId,
  19609    const RequestParams& aParams, bool aGetAll)
  19610    : NormalTransactionOp(std::move(aTransaction), aRequestId),
  19611      mObjectStoreId(
  19612          aGetAll ? aParams.get_ObjectStoreGetAllKeysParams().objectStoreId()
  19613                  : aParams.get_ObjectStoreGetKeyParams().objectStoreId()),
  19614      mOptionalKeyRange(
  19615          aGetAll ? aParams.get_ObjectStoreGetAllKeysParams().optionalKeyRange()
  19616                  : Some(aParams.get_ObjectStoreGetKeyParams().keyRange())),
  19617      mLimit(aGetAll ? aParams.get_ObjectStoreGetAllKeysParams().limit() : 1),
  19618      mGetAll(aGetAll) {
  19619  MOZ_ASSERT(aParams.type() == RequestParams::TObjectStoreGetKeyParams ||
  19620             aParams.type() == RequestParams::TObjectStoreGetAllKeysParams);
  19621  MOZ_ASSERT(mObjectStoreId);
  19622  MOZ_ASSERT_IF(!aGetAll, mOptionalKeyRange.isSome());
  19623 }
  19624 
  19625 nsresult ObjectStoreGetKeyRequestOp::DoDatabaseWork(
  19626    DatabaseConnection* aConnection) {
  19627  MOZ_ASSERT(aConnection);
  19628  aConnection->AssertIsOnConnectionThread();
  19629 
  19630  AUTO_PROFILER_LABEL("ObjectStoreGetKeyRequestOp::DoDatabaseWork", DOM);
  19631 
  19632  const nsCString query =
  19633      "SELECT key "
  19634      "FROM object_data "
  19635      "WHERE object_store_id = :"_ns +
  19636      kStmtParamNameObjectStoreId +
  19637      MaybeGetBindingClauseForKeyRange(mOptionalKeyRange, kColumnNameKey) +
  19638      " ORDER BY key ASC"_ns +
  19639      (mLimit ? " LIMIT "_ns + IntToCString(mLimit) : EmptyCString());
  19640 
  19641  QM_TRY_INSPECT(const auto& stmt, aConnection->BorrowCachedStatement(query));
  19642 
  19643  nsresult rv =
  19644      stmt->BindInt64ByName(kStmtParamNameObjectStoreId, mObjectStoreId);
  19645  if (NS_WARN_IF(NS_FAILED(rv))) {
  19646    return rv;
  19647  }
  19648 
  19649  if (mOptionalKeyRange.isSome()) {
  19650    rv = BindKeyRangeToStatement(mOptionalKeyRange.ref(), &*stmt);
  19651    if (NS_WARN_IF(NS_FAILED(rv))) {
  19652      return rv;
  19653    }
  19654  }
  19655 
  19656  QM_TRY(CollectWhileHasResult(
  19657      *stmt, [this](auto& stmt) mutable -> mozilla::Result<Ok, nsresult> {
  19658        Key* const key = mResponse.AppendElement(fallible);
  19659        QM_TRY(OkIf(key), Err(NS_ERROR_OUT_OF_MEMORY));
  19660        QM_TRY(MOZ_TO_RESULT(key->SetFromStatement(&stmt, 0)));
  19661 
  19662        return Ok{};
  19663      }));
  19664 
  19665  MOZ_ASSERT_IF(!mGetAll, mResponse.Length() <= 1);
  19666 
  19667  return NS_OK;
  19668 }
  19669 
  19670 void ObjectStoreGetKeyRequestOp::GetResponse(RequestResponse& aResponse,
  19671                                             size_t* aResponseSize) {
  19672  MOZ_ASSERT_IF(mLimit, mResponse.Length() <= mLimit);
  19673 
  19674  if (mGetAll) {
  19675    aResponse = ObjectStoreGetAllKeysResponse();
  19676    *aResponseSize = std::accumulate(mResponse.begin(), mResponse.end(), 0u,
  19677                                     [](size_t old, const auto& entry) {
  19678                                       return old + entry.GetBuffer().Length();
  19679                                     });
  19680 
  19681    aResponse.get_ObjectStoreGetAllKeysResponse().keys() = std::move(mResponse);
  19682 
  19683    return;
  19684  }
  19685 
  19686  aResponse = ObjectStoreGetKeyResponse();
  19687  *aResponseSize = 0;
  19688 
  19689  if (!mResponse.IsEmpty()) {
  19690    *aResponseSize = mResponse[0].GetBuffer().Length();
  19691    aResponse.get_ObjectStoreGetKeyResponse().key() = std::move(mResponse[0]);
  19692  }
  19693 }
  19694 
  19695 ObjectStoreDeleteRequestOp::ObjectStoreDeleteRequestOp(
  19696    SafeRefPtr<TransactionBase> aTransaction, const int64_t aRequestId,
  19697    const ObjectStoreDeleteParams& aParams)
  19698    : NormalTransactionOp(std::move(aTransaction), aRequestId),
  19699      mParams(aParams),
  19700      mObjectStoreMayHaveIndexes(false) {
  19701  AssertIsOnBackgroundThread();
  19702 
  19703  SafeRefPtr<FullObjectStoreMetadata> metadata =
  19704      Transaction().GetMetadataForObjectStoreId(mParams.objectStoreId());
  19705  MOZ_ASSERT(metadata);
  19706 
  19707  mObjectStoreMayHaveIndexes = metadata->HasLiveIndexes();
  19708 }
  19709 
  19710 nsresult ObjectStoreDeleteRequestOp::DoDatabaseWork(
  19711    DatabaseConnection* aConnection) {
  19712  MOZ_ASSERT(aConnection);
  19713  aConnection->AssertIsOnConnectionThread();
  19714  AUTO_PROFILER_LABEL("ObjectStoreDeleteRequestOp::DoDatabaseWork", DOM);
  19715 
  19716  DatabaseConnection::AutoSavepoint autoSave;
  19717  QM_TRY(MOZ_TO_RESULT(autoSave.Start(Transaction()))
  19718 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
  19719             ,
  19720         QM_PROPAGATE, MakeAutoSavepointCleanupHandler(*aConnection)
  19721 #endif
  19722  );
  19723 
  19724  QM_TRY_INSPECT(const bool& objectStoreHasIndexes,
  19725                 ObjectStoreHasIndexes(*aConnection, mParams.objectStoreId(),
  19726                                       mObjectStoreMayHaveIndexes));
  19727 
  19728  if (objectStoreHasIndexes) {
  19729    QM_TRY(MOZ_TO_RESULT(DeleteObjectStoreDataTableRowsWithIndexes(
  19730        aConnection, mParams.objectStoreId(), Some(mParams.keyRange()))));
  19731  } else {
  19732    const auto keyRangeClause =
  19733        GetBindingClauseForKeyRange(mParams.keyRange(), kColumnNameKey);
  19734 
  19735    QM_TRY(MOZ_TO_RESULT(aConnection->ExecuteCachedStatement(
  19736        "DELETE FROM object_data "
  19737        "WHERE object_store_id = :"_ns +
  19738            kStmtParamNameObjectStoreId + keyRangeClause + ";"_ns,
  19739        [&params = mParams](
  19740            mozIStorageStatement& stmt) -> mozilla::Result<Ok, nsresult> {
  19741          QM_TRY(MOZ_TO_RESULT(stmt.BindInt64ByName(kStmtParamNameObjectStoreId,
  19742                                                    params.objectStoreId())));
  19743 
  19744          QM_TRY(
  19745              MOZ_TO_RESULT(BindKeyRangeToStatement(params.keyRange(), &stmt)));
  19746 
  19747          return Ok{};
  19748        })));
  19749  }
  19750 
  19751  QM_TRY(MOZ_TO_RESULT(autoSave.Commit()));
  19752 
  19753  return NS_OK;
  19754 }
  19755 
  19756 ObjectStoreClearRequestOp::ObjectStoreClearRequestOp(
  19757    SafeRefPtr<TransactionBase> aTransaction, const int64_t aRequestId,
  19758    const ObjectStoreClearParams& aParams)
  19759    : NormalTransactionOp(std::move(aTransaction), aRequestId),
  19760      mParams(aParams),
  19761      mObjectStoreMayHaveIndexes(false) {
  19762  AssertIsOnBackgroundThread();
  19763 
  19764  SafeRefPtr<FullObjectStoreMetadata> metadata =
  19765      Transaction().GetMetadataForObjectStoreId(mParams.objectStoreId());
  19766  MOZ_ASSERT(metadata);
  19767 
  19768  mObjectStoreMayHaveIndexes = metadata->HasLiveIndexes();
  19769 }
  19770 
  19771 nsresult ObjectStoreClearRequestOp::DoDatabaseWork(
  19772    DatabaseConnection* aConnection) {
  19773  MOZ_ASSERT(aConnection);
  19774  aConnection->AssertIsOnConnectionThread();
  19775 
  19776  AUTO_PROFILER_LABEL("ObjectStoreClearRequestOp::DoDatabaseWork", DOM);
  19777 
  19778  DatabaseConnection::AutoSavepoint autoSave;
  19779  QM_TRY(MOZ_TO_RESULT(autoSave.Start(Transaction()))
  19780 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
  19781             ,
  19782         QM_PROPAGATE, MakeAutoSavepointCleanupHandler(*aConnection)
  19783 #endif
  19784  );
  19785 
  19786  QM_TRY_INSPECT(const bool& objectStoreHasIndexes,
  19787                 ObjectStoreHasIndexes(*aConnection, mParams.objectStoreId(),
  19788                                       mObjectStoreMayHaveIndexes));
  19789 
  19790  // The parameter names are not used, parameters are bound by index only
  19791  // locally in the same function.
  19792  QM_TRY(MOZ_TO_RESULT(
  19793      objectStoreHasIndexes
  19794          ? DeleteObjectStoreDataTableRowsWithIndexes(
  19795                aConnection, mParams.objectStoreId(), Nothing())
  19796          : aConnection->ExecuteCachedStatement(
  19797                "DELETE FROM object_data "
  19798                "WHERE object_store_id = :object_store_id;"_ns,
  19799                [objectStoreId =
  19800                     mParams.objectStoreId()](mozIStorageStatement& stmt)
  19801                    -> mozilla::Result<Ok, nsresult> {
  19802                  QM_TRY(
  19803                      MOZ_TO_RESULT(stmt.BindInt64ByIndex(0, objectStoreId)));
  19804 
  19805                  return Ok{};
  19806                })));
  19807 
  19808  QM_TRY(MOZ_TO_RESULT(autoSave.Commit()));
  19809 
  19810  return NS_OK;
  19811 }
  19812 
  19813 nsresult ObjectStoreCountRequestOp::DoDatabaseWork(
  19814    DatabaseConnection* aConnection) {
  19815  MOZ_ASSERT(aConnection);
  19816  aConnection->AssertIsOnConnectionThread();
  19817 
  19818  AUTO_PROFILER_LABEL("ObjectStoreCountRequestOp::DoDatabaseWork", DOM);
  19819 
  19820  const auto keyRangeClause = MaybeGetBindingClauseForKeyRange(
  19821      mParams.optionalKeyRange(), kColumnNameKey);
  19822 
  19823  QM_TRY_INSPECT(
  19824      const auto& maybeStmt,
  19825      aConnection->BorrowAndExecuteSingleStepStatement(
  19826          "SELECT count(*) "
  19827          "FROM object_data "
  19828          "WHERE object_store_id = :"_ns +
  19829              kStmtParamNameObjectStoreId + keyRangeClause,
  19830          [&params = mParams](auto& stmt) -> mozilla::Result<Ok, nsresult> {
  19831            QM_TRY(MOZ_TO_RESULT(stmt.BindInt64ByName(
  19832                kStmtParamNameObjectStoreId, params.objectStoreId())));
  19833 
  19834            if (params.optionalKeyRange().isSome()) {
  19835              QM_TRY(MOZ_TO_RESULT(BindKeyRangeToStatement(
  19836                  params.optionalKeyRange().ref(), &stmt)));
  19837            }
  19838 
  19839            return Ok{};
  19840          }));
  19841 
  19842  QM_TRY(OkIf(maybeStmt.isSome()), NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR,
  19843         [](const auto) {
  19844           // XXX Why do we have an assertion here, but not at most other
  19845           // places using IDB_REPORT_INTERNAL_ERR(_LAMBDA)?
  19846           MOZ_ASSERT(false, "This should never be possible!");
  19847           IDB_REPORT_INTERNAL_ERR();
  19848         });
  19849 
  19850  const auto& stmt = *maybeStmt;
  19851 
  19852  const int64_t count = stmt->AsInt64(0);
  19853  QM_TRY(OkIf(count >= 0), NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR, [](const auto) {
  19854    // XXX Why do we have an assertion here, but not at most other places using
  19855    // IDB_REPORT_INTERNAL_ERR(_LAMBDA)?
  19856    MOZ_ASSERT(false, "This should never be possible!");
  19857    IDB_REPORT_INTERNAL_ERR();
  19858  });
  19859 
  19860  mResponse.count() = count;
  19861 
  19862  return NS_OK;
  19863 }
  19864 
  19865 // static
  19866 SafeRefPtr<FullIndexMetadata> IndexRequestOpBase::IndexMetadataForParams(
  19867    const TransactionBase& aTransaction, const RequestParams& aParams) {
  19868  AssertIsOnBackgroundThread();
  19869  MOZ_ASSERT(aParams.type() == RequestParams::TIndexGetParams ||
  19870             aParams.type() == RequestParams::TIndexGetKeyParams ||
  19871             aParams.type() == RequestParams::TIndexGetAllParams ||
  19872             aParams.type() == RequestParams::TIndexGetAllKeysParams ||
  19873             aParams.type() == RequestParams::TIndexCountParams);
  19874 
  19875  IndexOrObjectStoreId objectStoreId;
  19876  IndexOrObjectStoreId indexId;
  19877 
  19878  switch (aParams.type()) {
  19879    case RequestParams::TIndexGetParams: {
  19880      const IndexGetParams& params = aParams.get_IndexGetParams();
  19881      objectStoreId = params.objectStoreId();
  19882      indexId = params.indexId();
  19883      break;
  19884    }
  19885 
  19886    case RequestParams::TIndexGetKeyParams: {
  19887      const IndexGetKeyParams& params = aParams.get_IndexGetKeyParams();
  19888      objectStoreId = params.objectStoreId();
  19889      indexId = params.indexId();
  19890      break;
  19891    }
  19892 
  19893    case RequestParams::TIndexGetAllParams: {
  19894      const IndexGetAllParams& params = aParams.get_IndexGetAllParams();
  19895      objectStoreId = params.objectStoreId();
  19896      indexId = params.indexId();
  19897      break;
  19898    }
  19899 
  19900    case RequestParams::TIndexGetAllKeysParams: {
  19901      const IndexGetAllKeysParams& params = aParams.get_IndexGetAllKeysParams();
  19902      objectStoreId = params.objectStoreId();
  19903      indexId = params.indexId();
  19904      break;
  19905    }
  19906 
  19907    case RequestParams::TIndexCountParams: {
  19908      const IndexCountParams& params = aParams.get_IndexCountParams();
  19909      objectStoreId = params.objectStoreId();
  19910      indexId = params.indexId();
  19911      break;
  19912    }
  19913 
  19914    default:
  19915      MOZ_CRASH("Should never get here!");
  19916  }
  19917 
  19918  const SafeRefPtr<FullObjectStoreMetadata> objectStoreMetadata =
  19919      aTransaction.GetMetadataForObjectStoreId(objectStoreId);
  19920  MOZ_ASSERT(objectStoreMetadata);
  19921 
  19922  SafeRefPtr<FullIndexMetadata> indexMetadata =
  19923      aTransaction.GetMetadataForIndexId(*objectStoreMetadata, indexId);
  19924  MOZ_ASSERT(indexMetadata);
  19925 
  19926  return indexMetadata;
  19927 }
  19928 
  19929 IndexGetRequestOp::IndexGetRequestOp(SafeRefPtr<TransactionBase> aTransaction,
  19930                                     const int64_t aRequestId,
  19931                                     const RequestParams& aParams, bool aGetAll)
  19932    : IndexRequestOpBase(std::move(aTransaction), aRequestId, aParams),
  19933      mDatabase(Transaction().GetDatabasePtr()),
  19934      mOptionalKeyRange(aGetAll
  19935                            ? aParams.get_IndexGetAllParams().optionalKeyRange()
  19936                            : Some(aParams.get_IndexGetParams().keyRange())),
  19937      mBackgroundParent(Transaction().GetBackgroundParent()),
  19938      mLimit(aGetAll ? aParams.get_IndexGetAllParams().limit() : 1),
  19939      mGetAll(aGetAll) {
  19940  MOZ_ASSERT(aParams.type() == RequestParams::TIndexGetParams ||
  19941             aParams.type() == RequestParams::TIndexGetAllParams);
  19942  MOZ_ASSERT(mDatabase);
  19943  MOZ_ASSERT_IF(!aGetAll, mOptionalKeyRange.isSome());
  19944  MOZ_ASSERT(mBackgroundParent);
  19945 }
  19946 
  19947 nsresult IndexGetRequestOp::DoDatabaseWork(DatabaseConnection* aConnection) {
  19948  MOZ_ASSERT(aConnection);
  19949  aConnection->AssertIsOnConnectionThread();
  19950  MOZ_ASSERT_IF(!mGetAll, mOptionalKeyRange.isSome());
  19951  MOZ_ASSERT_IF(!mGetAll, mLimit == 1);
  19952 
  19953  AUTO_PROFILER_LABEL("IndexGetRequestOp::DoDatabaseWork", DOM);
  19954 
  19955  const auto indexTable = mMetadata->mCommonMetadata.unique()
  19956                              ? "unique_index_data "_ns
  19957                              : "index_data "_ns;
  19958 
  19959  QM_TRY_INSPECT(
  19960      const auto& stmt,
  19961      aConnection->BorrowCachedStatement(
  19962          "SELECT file_ids, data "
  19963          "FROM object_data "
  19964          "INNER JOIN "_ns +
  19965          indexTable +
  19966          "AS index_table "
  19967          "ON object_data.object_store_id = "
  19968          "index_table.object_store_id "
  19969          "AND object_data.key = "
  19970          "index_table.object_data_key "
  19971          "WHERE index_id = :"_ns +
  19972          kStmtParamNameIndexId +
  19973          MaybeGetBindingClauseForKeyRange(mOptionalKeyRange,
  19974                                           kColumnNameValue) +
  19975          (mLimit ? kOpenLimit + IntToCString(mLimit) : EmptyCString())));
  19976 
  19977  QM_TRY(MOZ_TO_RESULT(stmt->BindInt64ByName(kStmtParamNameIndexId,
  19978                                             mMetadata->mCommonMetadata.id())));
  19979 
  19980  if (mOptionalKeyRange.isSome()) {
  19981    QM_TRY(MOZ_TO_RESULT(
  19982        BindKeyRangeToStatement(mOptionalKeyRange.ref(), &*stmt)));
  19983  }
  19984 
  19985  QM_TRY(CollectWhileHasResult(
  19986      *stmt, [this](auto& stmt) mutable -> mozilla::Result<Ok, nsresult> {
  19987        QM_TRY_UNWRAP(auto cloneInfo,
  19988                      GetStructuredCloneReadInfoFromStatement(
  19989                          &stmt, 1, 0, mDatabase->GetFileManager()));
  19990 
  19991        if (cloneInfo.HasPreprocessInfo()) {
  19992          IDB_WARNING("Preprocessing for indexes not yet implemented!");
  19993          return Err(NS_ERROR_NOT_IMPLEMENTED);
  19994        }
  19995 
  19996        QM_TRY(OkIf(mResponse.EmplaceBack(fallible, std::move(cloneInfo))),
  19997               Err(NS_ERROR_OUT_OF_MEMORY));
  19998 
  19999        return Ok{};
  20000      }));
  20001 
  20002  MOZ_ASSERT_IF(!mGetAll, mResponse.Length() <= 1);
  20003 
  20004  return NS_OK;
  20005 }
  20006 
  20007 // XXX This is more or less a duplicate of ObjectStoreGetRequestOp::GetResponse
  20008 void IndexGetRequestOp::GetResponse(RequestResponse& aResponse,
  20009                                    size_t* aResponseSize) {
  20010  MOZ_ASSERT_IF(!mGetAll, mResponse.Length() <= 1);
  20011 
  20012  auto convertResponse = [this](StructuredCloneReadInfoParent&& info)
  20013      -> mozilla::Result<SerializedStructuredCloneReadInfo, nsresult> {
  20014    SerializedStructuredCloneReadInfo result;
  20015 
  20016    result.data().data = info.ReleaseData();
  20017 
  20018    QM_TRY_UNWRAP(result.files(), SerializeStructuredCloneFiles(
  20019                                      mDatabase, info.Files(), false));
  20020 
  20021    return result;
  20022  };
  20023 
  20024  if (mGetAll) {
  20025    aResponse = IndexGetAllResponse();
  20026    *aResponseSize = 0;
  20027 
  20028    if (!mResponse.IsEmpty()) {
  20029      QM_TRY_UNWRAP(
  20030          aResponse.get_IndexGetAllResponse().cloneInfos(),
  20031          TransformIntoNewArrayAbortOnErr(
  20032              std::make_move_iterator(mResponse.begin()),
  20033              std::make_move_iterator(mResponse.end()),
  20034              [convertResponse,
  20035               &aResponseSize](StructuredCloneReadInfoParent&& info) {
  20036                *aResponseSize += info.Size();
  20037                return convertResponse(std::move(info));
  20038              },
  20039              fallible),
  20040          QM_VOID, [&aResponse](const nsresult result) { aResponse = result; });
  20041    }
  20042 
  20043    return;
  20044  }
  20045 
  20046  aResponse = IndexGetResponse();
  20047  *aResponseSize = 0;
  20048 
  20049  if (!mResponse.IsEmpty()) {
  20050    SerializedStructuredCloneReadInfo& serializedInfo =
  20051        aResponse.get_IndexGetResponse().cloneInfo();
  20052 
  20053    *aResponseSize += mResponse[0].Size();
  20054    QM_TRY_UNWRAP(serializedInfo, convertResponse(std::move(mResponse[0])),
  20055                  QM_VOID,
  20056                  [&aResponse](const nsresult result) { aResponse = result; });
  20057  }
  20058 }
  20059 
  20060 IndexGetKeyRequestOp::IndexGetKeyRequestOp(
  20061    SafeRefPtr<TransactionBase> aTransaction, const int64_t aRequestId,
  20062    const RequestParams& aParams, bool aGetAll)
  20063    : IndexRequestOpBase(std::move(aTransaction), aRequestId, aParams),
  20064      mOptionalKeyRange(
  20065          aGetAll ? aParams.get_IndexGetAllKeysParams().optionalKeyRange()
  20066                  : Some(aParams.get_IndexGetKeyParams().keyRange())),
  20067      mLimit(aGetAll ? aParams.get_IndexGetAllKeysParams().limit() : 1),
  20068      mGetAll(aGetAll) {
  20069  MOZ_ASSERT(aParams.type() == RequestParams::TIndexGetKeyParams ||
  20070             aParams.type() == RequestParams::TIndexGetAllKeysParams);
  20071  MOZ_ASSERT_IF(!aGetAll, mOptionalKeyRange.isSome());
  20072 }
  20073 
  20074 nsresult IndexGetKeyRequestOp::DoDatabaseWork(DatabaseConnection* aConnection) {
  20075  MOZ_ASSERT(aConnection);
  20076  aConnection->AssertIsOnConnectionThread();
  20077  MOZ_ASSERT_IF(!mGetAll, mOptionalKeyRange.isSome());
  20078  MOZ_ASSERT_IF(!mGetAll, mLimit == 1);
  20079 
  20080  AUTO_PROFILER_LABEL("IndexGetKeyRequestOp::DoDatabaseWork", DOM);
  20081 
  20082  const bool hasKeyRange = mOptionalKeyRange.isSome();
  20083 
  20084  const auto indexTable = mMetadata->mCommonMetadata.unique()
  20085                              ? "unique_index_data "_ns
  20086                              : "index_data "_ns;
  20087 
  20088  const nsCString query =
  20089      "SELECT object_data_key "
  20090      "FROM "_ns +
  20091      indexTable + "WHERE index_id = :"_ns + kStmtParamNameIndexId +
  20092      MaybeGetBindingClauseForKeyRange(mOptionalKeyRange, kColumnNameValue) +
  20093      (mLimit ? kOpenLimit + IntToCString(mLimit) : EmptyCString());
  20094 
  20095  QM_TRY_INSPECT(const auto& stmt, aConnection->BorrowCachedStatement(query));
  20096 
  20097  QM_TRY(MOZ_TO_RESULT(stmt->BindInt64ByName(kStmtParamNameIndexId,
  20098                                             mMetadata->mCommonMetadata.id())));
  20099 
  20100  if (hasKeyRange) {
  20101    QM_TRY(MOZ_TO_RESULT(
  20102        BindKeyRangeToStatement(mOptionalKeyRange.ref(), &*stmt)));
  20103  }
  20104 
  20105  QM_TRY(CollectWhileHasResult(
  20106      *stmt, [this](auto& stmt) mutable -> mozilla::Result<Ok, nsresult> {
  20107        Key* const key = mResponse.AppendElement(fallible);
  20108        QM_TRY(OkIf(key), Err(NS_ERROR_OUT_OF_MEMORY));
  20109        QM_TRY(MOZ_TO_RESULT(key->SetFromStatement(&stmt, 0)));
  20110 
  20111        return Ok{};
  20112      }));
  20113 
  20114  MOZ_ASSERT_IF(!mGetAll, mResponse.Length() <= 1);
  20115 
  20116  return NS_OK;
  20117 }
  20118 
  20119 void IndexGetKeyRequestOp::GetResponse(RequestResponse& aResponse,
  20120                                       size_t* aResponseSize) {
  20121  MOZ_ASSERT_IF(!mGetAll, mResponse.Length() <= 1);
  20122 
  20123  if (mGetAll) {
  20124    aResponse = IndexGetAllKeysResponse();
  20125    *aResponseSize = std::accumulate(mResponse.begin(), mResponse.end(), 0u,
  20126                                     [](size_t old, const auto& entry) {
  20127                                       return old + entry.GetBuffer().Length();
  20128                                     });
  20129 
  20130    aResponse.get_IndexGetAllKeysResponse().keys() = std::move(mResponse);
  20131 
  20132    return;
  20133  }
  20134 
  20135  aResponse = IndexGetKeyResponse();
  20136  *aResponseSize = 0;
  20137 
  20138  if (!mResponse.IsEmpty()) {
  20139    *aResponseSize = mResponse[0].GetBuffer().Length();
  20140    aResponse.get_IndexGetKeyResponse().key() = std::move(mResponse[0]);
  20141  }
  20142 }
  20143 
  20144 nsresult IndexCountRequestOp::DoDatabaseWork(DatabaseConnection* aConnection) {
  20145  MOZ_ASSERT(aConnection);
  20146  aConnection->AssertIsOnConnectionThread();
  20147 
  20148  AUTO_PROFILER_LABEL("IndexCountRequestOp::DoDatabaseWork", DOM);
  20149 
  20150  const auto indexTable = mMetadata->mCommonMetadata.unique()
  20151                              ? "unique_index_data "_ns
  20152                              : "index_data "_ns;
  20153 
  20154  const auto keyRangeClause = MaybeGetBindingClauseForKeyRange(
  20155      mParams.optionalKeyRange(), kColumnNameValue);
  20156 
  20157  QM_TRY_INSPECT(
  20158      const auto& maybeStmt,
  20159      aConnection->BorrowAndExecuteSingleStepStatement(
  20160          "SELECT count(*) "
  20161          "FROM "_ns +
  20162              indexTable + "WHERE index_id = :"_ns + kStmtParamNameIndexId +
  20163              keyRangeClause,
  20164          [&self = *this](auto& stmt) -> mozilla::Result<Ok, nsresult> {
  20165            QM_TRY(MOZ_TO_RESULT(stmt.BindInt64ByName(
  20166                kStmtParamNameIndexId, self.mMetadata->mCommonMetadata.id())));
  20167 
  20168            if (self.mParams.optionalKeyRange().isSome()) {
  20169              QM_TRY(MOZ_TO_RESULT(BindKeyRangeToStatement(
  20170                  self.mParams.optionalKeyRange().ref(), &stmt)));
  20171            }
  20172 
  20173            return Ok{};
  20174          }));
  20175 
  20176  QM_TRY(OkIf(maybeStmt.isSome()), NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR,
  20177         [](const auto) {
  20178           // XXX Why do we have an assertion here, but not at most other
  20179           // places using IDB_REPORT_INTERNAL_ERR(_LAMBDA)?
  20180           MOZ_ASSERT(false, "This should never be possible!");
  20181           IDB_REPORT_INTERNAL_ERR();
  20182         });
  20183 
  20184  const auto& stmt = *maybeStmt;
  20185 
  20186  const int64_t count = stmt->AsInt64(0);
  20187  QM_TRY(OkIf(count >= 0), NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR, [](const auto) {
  20188    // XXX Why do we have an assertion here, but not at most other places using
  20189    // IDB_REPORT_INTERNAL_ERR(_LAMBDA)?
  20190    MOZ_ASSERT(false, "This should never be possible!");
  20191    IDB_REPORT_INTERNAL_ERR();
  20192  });
  20193 
  20194  mResponse.count() = count;
  20195 
  20196  return NS_OK;
  20197 }
  20198 
  20199 template <IDBCursorType CursorType>
  20200 bool Cursor<CursorType>::CursorOpBase::SendFailureResult(nsresult aResultCode) {
  20201  AssertIsOnOwningThread();
  20202  MOZ_ASSERT(NS_FAILED(aResultCode));
  20203  MOZ_ASSERT(mCursor);
  20204  MOZ_ASSERT(mCursor->mCurrentlyRunningOp == this);
  20205  MOZ_ASSERT(!mResponseSent);
  20206 
  20207  if (!IsActorDestroyed()) {
  20208    mResponse = ClampResultCode(aResultCode);
  20209 
  20210    // This is an expected race when the transaction is invalidated after
  20211    // data is retrieved from database.
  20212    //
  20213    // TODO: There seem to be other cases when mFiles is non-empty here, which
  20214    // have been present before adding cursor preloading, but with cursor
  20215    // preloading they have become more frequent (also during startup). One
  20216    // possible cause with cursor preloading is to be addressed by Bug 1597191.
  20217    NS_WARNING_ASSERTION(
  20218        !mFiles.IsEmpty() && !Transaction().IsInvalidated(),
  20219        "Expected empty mFiles when transaction has not been invalidated");
  20220 
  20221    // SendResponseInternal will assert when mResponse.type() is
  20222    // CursorResponse::Tnsresult and mFiles is non-empty, so we clear mFiles
  20223    // here.
  20224    mFiles.Clear();
  20225 
  20226    mCursor->SendResponseInternal(mResponse, mFiles);
  20227  }
  20228 
  20229 #ifdef DEBUG
  20230  mResponseSent = true;
  20231 #endif
  20232  return false;
  20233 }
  20234 
  20235 template <IDBCursorType CursorType>
  20236 void Cursor<CursorType>::CursorOpBase::Cleanup() {
  20237  AssertIsOnOwningThread();
  20238  MOZ_ASSERT(mCursor);
  20239  MOZ_ASSERT_IF(!IsActorDestroyed(), mResponseSent);
  20240 
  20241  mCursor = nullptr;
  20242 
  20243 #ifdef DEBUG
  20244  // A bit hacky but the CursorOp request is not generated in response to a
  20245  // child request like most other database operations. Do this to make our
  20246  // assertions happy.
  20247  NoteActorDestroyed();
  20248 #endif
  20249 
  20250  TransactionDatabaseOperationBase::Cleanup();
  20251 }
  20252 
  20253 template <IDBCursorType CursorType>
  20254 ResponseSizeOrError
  20255 CursorOpBaseHelperBase<CursorType>::PopulateResponseFromStatement(
  20256    mozIStorageStatement* const aStmt, const bool aInitializeResponse,
  20257    Key* const aOptOutSortKey) {
  20258  mOp.Transaction().AssertIsOnConnectionThread();
  20259  MOZ_ASSERT_IF(aInitializeResponse,
  20260                mOp.mResponse.type() == CursorResponse::T__None);
  20261  MOZ_ASSERT_IF(!aInitializeResponse,
  20262                mOp.mResponse.type() != CursorResponse::T__None);
  20263  MOZ_ASSERT_IF(
  20264      mOp.mFiles.IsEmpty() &&
  20265          (mOp.mResponse.type() ==
  20266               CursorResponse::TArrayOfObjectStoreCursorResponse ||
  20267           mOp.mResponse.type() == CursorResponse::TArrayOfIndexCursorResponse),
  20268      aInitializeResponse);
  20269 
  20270  auto populateResponseHelper = PopulateResponseHelper<CursorType>{mOp};
  20271  auto previousKey = aOptOutSortKey ? std::move(*aOptOutSortKey) : Key{};
  20272 
  20273  QM_TRY(MOZ_TO_RESULT(populateResponseHelper.GetKeys(aStmt, aOptOutSortKey)));
  20274 
  20275  // aOptOutSortKey must be set iff the cursor is a unique cursor. For unique
  20276  // cursors, we need to skip records with the same key. The SQL queries
  20277  // currently do not filter these out.
  20278  if (aOptOutSortKey && !previousKey.IsUnset() &&
  20279      previousKey == *aOptOutSortKey) {
  20280    return 0;
  20281  }
  20282 
  20283  QM_TRY(MOZ_TO_RESULT(
  20284      populateResponseHelper.MaybeGetCloneInfo(aStmt, GetCursor())));
  20285 
  20286  // CAUTION: It is important that only the part of the function above this
  20287  // comment may fail, and modifications to the data structure (in particular
  20288  // mResponse and mFiles) may only be made below. This is necessary to allow to
  20289  // discard entries that were attempted to be preloaded without causing an
  20290  // inconsistent state.
  20291 
  20292  if (aInitializeResponse) {
  20293    mOp.mResponse = std::remove_reference_t<
  20294        decltype(populateResponseHelper.GetTypedResponse(&mOp.mResponse))>();
  20295  }
  20296 
  20297  auto& responses = populateResponseHelper.GetTypedResponse(&mOp.mResponse);
  20298  auto& response = *responses.AppendElement();
  20299 
  20300  populateResponseHelper.FillKeys(response);
  20301  if constexpr (!CursorTypeTraits<CursorType>::IsKeyOnlyCursor) {
  20302    populateResponseHelper.MaybeFillCloneInfo(response, &mOp.mFiles);
  20303  }
  20304 
  20305  return populateResponseHelper.GetKeySize(response) +
  20306         populateResponseHelper.MaybeGetCloneInfoSize(response);
  20307 }
  20308 
  20309 template <IDBCursorType CursorType>
  20310 void CursorOpBaseHelperBase<CursorType>::PopulateExtraResponses(
  20311    mozIStorageStatement* const aStmt, const uint32_t aMaxExtraCount,
  20312    const size_t aInitialResponseSize, const nsACString& aOperation,
  20313    Key* const aOptPreviousSortKey) {
  20314  mOp.AssertIsOnConnectionThread();
  20315 
  20316  const auto extraCount = [&]() -> uint32_t {
  20317    auto accumulatedResponseSize = aInitialResponseSize;
  20318    uint32_t extraCount = 0;
  20319 
  20320    do {
  20321      bool hasResult;
  20322      nsresult rv = aStmt->ExecuteStep(&hasResult);
  20323      if (NS_WARN_IF(NS_FAILED(rv))) {
  20324        // In case of a failure on one step, do not attempt to execute further
  20325        // steps, but use the results already populated.
  20326 
  20327        break;
  20328      }
  20329 
  20330      if (!hasResult) {
  20331        break;
  20332      }
  20333 
  20334      // PopulateResponseFromStatement does not modify the data in case of
  20335      // failure, so we can just use the results already populated, and discard
  20336      // any remaining entries, and signal overall success. Probably, future
  20337      // attempts to access the same entry will fail as well, but it might never
  20338      // be accessed by the application.
  20339      QM_TRY_INSPECT(
  20340          const auto& responseSize,
  20341          PopulateResponseFromStatement(aStmt, false, aOptPreviousSortKey),
  20342          extraCount, [](const auto&) {
  20343            // TODO: Maybe disable preloading for this cursor? The problem will
  20344            // probably reoccur on the next attempt, and disabling preloading
  20345            // will reduce latency. However, if some problematic entry will be
  20346            // skipped over, after that it might be fine again. To judge this,
  20347            // the causes for such failures would need to be analyzed more
  20348            // thoroughly. Since this seems to be rare, maybe no further action
  20349            // is necessary at all.
  20350          });
  20351 
  20352      // Check accumulated size of individual responses and maybe break early.
  20353      accumulatedResponseSize += responseSize;
  20354      if (accumulatedResponseSize > IPC::Channel::kMaximumMessageSize / 2) {
  20355        IDB_LOG_MARK_PARENT_TRANSACTION_REQUEST(
  20356            "PRELOAD: %s: Dropping entries because maximum message size is "
  20357            "exceeded: %" PRIu32 "/%zu bytes",
  20358            "%.0s Dropping too large (%" PRIu32 "/%zu)",
  20359            IDB_LOG_ID_STRING(mOp.mBackgroundChildLoggingId),
  20360            mOp.mTransactionLoggingSerialNumber, mOp.mLoggingSerialNumber,
  20361            PromiseFlatCString(aOperation).get(), extraCount,
  20362            accumulatedResponseSize);
  20363 
  20364        break;
  20365      }
  20366 
  20367      // TODO: Do not count entries skipped for unique cursors.
  20368      ++extraCount;
  20369    } while (true);
  20370 
  20371    return extraCount;
  20372  }();
  20373 
  20374  IDB_LOG_MARK_PARENT_TRANSACTION_REQUEST(
  20375      "PRELOAD: %s: Number of extra results populated: %" PRIu32 "/%" PRIu32,
  20376      "%.0s Populated (%" PRIu32 "/%" PRIu32 ")",
  20377      IDB_LOG_ID_STRING(mOp.mBackgroundChildLoggingId),
  20378      mOp.mTransactionLoggingSerialNumber, mOp.mLoggingSerialNumber,
  20379      PromiseFlatCString(aOperation).get(), extraCount, aMaxExtraCount);
  20380 }
  20381 
  20382 template <IDBCursorType CursorType>
  20383 void Cursor<CursorType>::SetOptionalKeyRange(
  20384    const Maybe<SerializedKeyRange>& aOptionalKeyRange, bool* const aOpen) {
  20385  MOZ_ASSERT(aOpen);
  20386 
  20387  Key localeAwareRangeBound;
  20388 
  20389  if (aOptionalKeyRange.isSome()) {
  20390    const SerializedKeyRange& range = aOptionalKeyRange.ref();
  20391 
  20392    const bool lowerBound = !IsIncreasingOrder(mDirection);
  20393    *aOpen =
  20394        !range.isOnly() && (lowerBound ? range.lowerOpen() : range.upperOpen());
  20395 
  20396    const auto& bound =
  20397        (range.isOnly() || lowerBound) ? range.lower() : range.upper();
  20398    if constexpr (IsIndexCursor) {
  20399      if (this->IsLocaleAware()) {
  20400        // XXX Don't we need to propagate the error?
  20401        QM_TRY_UNWRAP(localeAwareRangeBound,
  20402                      bound.ToLocaleAwareKey(this->mLocale), QM_VOID);
  20403      } else {
  20404        localeAwareRangeBound = bound;
  20405      }
  20406    } else {
  20407      localeAwareRangeBound = bound;
  20408    }
  20409  } else {
  20410    *aOpen = false;
  20411  }
  20412 
  20413  this->mLocaleAwareRangeBound.init(std::move(localeAwareRangeBound));
  20414 }
  20415 
  20416 template <IDBCursorType CursorType>
  20417 void ObjectStoreOpenOpHelper<CursorType>::PrepareKeyConditionClauses(
  20418    const nsACString& aDirectionClause, const nsACString& aQueryStart) {
  20419  const bool isIncreasingOrder = IsIncreasingOrder(GetCursor().mDirection);
  20420 
  20421  nsAutoCString keyRangeClause;
  20422  nsAutoCString continueToKeyRangeClause;
  20423  AppendConditionClause(kStmtParamNameKey, kStmtParamNameCurrentKey,
  20424                        !isIncreasingOrder, false, keyRangeClause);
  20425  AppendConditionClause(kStmtParamNameKey, kStmtParamNameCurrentKey,
  20426                        !isIncreasingOrder, true, continueToKeyRangeClause);
  20427 
  20428  {
  20429    bool open;
  20430    GetCursor().SetOptionalKeyRange(GetOptionalKeyRange(), &open);
  20431 
  20432    if (GetOptionalKeyRange().isSome() &&
  20433        !GetCursor().mLocaleAwareRangeBound->IsUnset()) {
  20434      AppendConditionClause(kStmtParamNameKey, kStmtParamNameRangeBound,
  20435                            isIncreasingOrder, !open, keyRangeClause);
  20436      AppendConditionClause(kStmtParamNameKey, kStmtParamNameRangeBound,
  20437                            isIncreasingOrder, !open, continueToKeyRangeClause);
  20438    }
  20439  }
  20440 
  20441  const nsAutoCString suffix =
  20442      aDirectionClause + kOpenLimit + ":"_ns + kStmtParamNameLimit;
  20443 
  20444  GetCursor().mContinueQueries.init(
  20445      aQueryStart + keyRangeClause + suffix,
  20446      aQueryStart + continueToKeyRangeClause + suffix);
  20447 }
  20448 
  20449 template <IDBCursorType CursorType>
  20450 void IndexOpenOpHelper<CursorType>::PrepareIndexKeyConditionClause(
  20451    const nsACString& aDirectionClause,
  20452    const nsLiteralCString& aObjectDataKeyPrefix, nsAutoCString aQueryStart) {
  20453  const bool isIncreasingOrder = IsIncreasingOrder(GetCursor().mDirection);
  20454 
  20455  {
  20456    bool open;
  20457    GetCursor().SetOptionalKeyRange(GetOptionalKeyRange(), &open);
  20458    if (GetOptionalKeyRange().isSome() &&
  20459        !GetCursor().mLocaleAwareRangeBound->IsUnset()) {
  20460      AppendConditionClause(kColumnNameAliasSortKey, kStmtParamNameRangeBound,
  20461                            isIncreasingOrder, !open, aQueryStart);
  20462    }
  20463  }
  20464 
  20465  nsCString continueQuery, continueToQuery, continuePrimaryKeyQuery;
  20466 
  20467  continueToQuery =
  20468      aQueryStart + " AND "_ns +
  20469      GetSortKeyClause(isIncreasingOrder ? ComparisonOperator::GreaterOrEquals
  20470                                         : ComparisonOperator::LessOrEquals,
  20471                       kStmtParamNameCurrentKey);
  20472 
  20473  switch (GetCursor().mDirection) {
  20474    case IDBCursorDirection::Next:
  20475    case IDBCursorDirection::Prev:
  20476      continueQuery =
  20477          aQueryStart + " AND "_ns +
  20478          GetSortKeyClause(isIncreasingOrder
  20479                               ? ComparisonOperator::GreaterOrEquals
  20480                               : ComparisonOperator::LessOrEquals,
  20481                           kStmtParamNameCurrentKey) +
  20482          " AND ( "_ns +
  20483          GetSortKeyClause(isIncreasingOrder ? ComparisonOperator::GreaterThan
  20484                                             : ComparisonOperator::LessThan,
  20485                           kStmtParamNameCurrentKey) +
  20486          " OR "_ns +
  20487          GetKeyClause(aObjectDataKeyPrefix + "object_data_key"_ns,
  20488                       isIncreasingOrder ? ComparisonOperator::GreaterThan
  20489                                         : ComparisonOperator::LessThan,
  20490                       kStmtParamNameObjectStorePosition) +
  20491          " ) "_ns;
  20492 
  20493      continuePrimaryKeyQuery =
  20494          aQueryStart +
  20495          " AND ("
  20496          "("_ns +
  20497          GetSortKeyClause(ComparisonOperator::Equals,
  20498                           kStmtParamNameCurrentKey) +
  20499          " AND "_ns +
  20500          GetKeyClause(aObjectDataKeyPrefix + "object_data_key"_ns,
  20501                       isIncreasingOrder ? ComparisonOperator::GreaterOrEquals
  20502                                         : ComparisonOperator::LessOrEquals,
  20503                       kStmtParamNameObjectStorePosition) +
  20504          ") OR "_ns +
  20505          GetSortKeyClause(isIncreasingOrder ? ComparisonOperator::GreaterThan
  20506                                             : ComparisonOperator::LessThan,
  20507                           kStmtParamNameCurrentKey) +
  20508          ")"_ns;
  20509      break;
  20510 
  20511    case IDBCursorDirection::Nextunique:
  20512    case IDBCursorDirection::Prevunique:
  20513      continueQuery =
  20514          aQueryStart + " AND "_ns +
  20515          GetSortKeyClause(isIncreasingOrder ? ComparisonOperator::GreaterThan
  20516                                             : ComparisonOperator::LessThan,
  20517                           kStmtParamNameCurrentKey);
  20518      break;
  20519 
  20520    default:
  20521      MOZ_CRASH("Should never get here!");
  20522  }
  20523 
  20524  const nsAutoCString suffix =
  20525      aDirectionClause + kOpenLimit + ":"_ns + kStmtParamNameLimit;
  20526  continueQuery += suffix;
  20527  continueToQuery += suffix;
  20528  if (!continuePrimaryKeyQuery.IsEmpty()) {
  20529    continuePrimaryKeyQuery += suffix;
  20530  }
  20531 
  20532  GetCursor().mContinueQueries.init(std::move(continueQuery),
  20533                                    std::move(continueToQuery),
  20534                                    std::move(continuePrimaryKeyQuery));
  20535 }
  20536 
  20537 template <IDBCursorType CursorType>
  20538 nsresult CommonOpenOpHelper<CursorType>::ProcessStatementSteps(
  20539    mozIStorageStatement* const aStmt) {
  20540  QM_TRY_INSPECT(const bool& hasResult,
  20541                 MOZ_TO_RESULT_INVOKE_MEMBER(aStmt, ExecuteStep));
  20542 
  20543  if (!hasResult) {
  20544    SetResponse(void_t{});
  20545    return NS_OK;
  20546  }
  20547 
  20548  Key previousKey;
  20549  auto* optPreviousKey =
  20550      IsUnique(GetCursor().mDirection) ? &previousKey : nullptr;
  20551 
  20552  QM_TRY_INSPECT(const auto& responseSize,
  20553                 PopulateResponseFromStatement(aStmt, true, optPreviousKey));
  20554 
  20555  // The degree to which extra responses on OpenOp can actually be used depends
  20556  // on the parameters of subsequent ContinueOp operations, see also comment in
  20557  // ContinueOp::DoDatabaseWork.
  20558  //
  20559  // TODO: We should somehow evaluate the effects of this. Maybe use a smaller
  20560  // extra count than for ContinueOp?
  20561  PopulateExtraResponses(aStmt, GetCursor().mMaxExtraCount, responseSize,
  20562                         "OpenOp"_ns, optPreviousKey);
  20563 
  20564  return NS_OK;
  20565 }
  20566 
  20567 nsresult OpenOpHelper<IDBCursorType::ObjectStore>::DoDatabaseWork(
  20568    DatabaseConnection* aConnection) {
  20569  MOZ_ASSERT(aConnection);
  20570  aConnection->AssertIsOnConnectionThread();
  20571  MOZ_ASSERT(GetCursor().mObjectStoreId);
  20572 
  20573  AUTO_PROFILER_LABEL("Cursor::OpenOp::DoObjectStoreDatabaseWork", DOM);
  20574 
  20575  const bool usingKeyRange = GetOptionalKeyRange().isSome();
  20576 
  20577  const nsCString queryStart = "SELECT "_ns + kColumnNameKey +
  20578                               ", file_ids, data "
  20579                               "FROM object_data "
  20580                               "WHERE object_store_id = :"_ns +
  20581                               kStmtParamNameId;
  20582 
  20583  const auto keyRangeClause =
  20584      DatabaseOperationBase::MaybeGetBindingClauseForKeyRange(
  20585          GetOptionalKeyRange(), kColumnNameKey);
  20586 
  20587  const auto& directionClause = MakeDirectionClause(GetCursor().mDirection);
  20588 
  20589  // Note: Changing the number or order of SELECT columns in the query will
  20590  // require changes to CursorOpBase::PopulateResponseFromStatement.
  20591  const nsCString firstQuery = queryStart + keyRangeClause + directionClause +
  20592                               kOpenLimit +
  20593                               IntToCString(1 + GetCursor().mMaxExtraCount);
  20594 
  20595  QM_TRY_INSPECT(const auto& stmt,
  20596                 aConnection->BorrowCachedStatement(firstQuery));
  20597 
  20598  QM_TRY(MOZ_TO_RESULT(
  20599      stmt->BindInt64ByName(kStmtParamNameId, GetCursor().mObjectStoreId)));
  20600 
  20601  if (usingKeyRange) {
  20602    QM_TRY(MOZ_TO_RESULT(DatabaseOperationBase::BindKeyRangeToStatement(
  20603        GetOptionalKeyRange().ref(), &*stmt)));
  20604  }
  20605 
  20606  // Now we need to make the query for ContinueOp.
  20607  PrepareKeyConditionClauses(directionClause, queryStart);
  20608 
  20609  return ProcessStatementSteps(&*stmt);
  20610 }
  20611 
  20612 nsresult OpenOpHelper<IDBCursorType::ObjectStoreKey>::DoDatabaseWork(
  20613    DatabaseConnection* aConnection) {
  20614  MOZ_ASSERT(aConnection);
  20615  aConnection->AssertIsOnConnectionThread();
  20616  MOZ_ASSERT(GetCursor().mObjectStoreId);
  20617 
  20618  AUTO_PROFILER_LABEL("Cursor::OpenOp::DoObjectStoreKeyDatabaseWork", DOM);
  20619 
  20620  const bool usingKeyRange = GetOptionalKeyRange().isSome();
  20621 
  20622  const nsCString queryStart = "SELECT "_ns + kColumnNameKey +
  20623                               " FROM object_data "
  20624                               "WHERE object_store_id = :"_ns +
  20625                               kStmtParamNameId;
  20626 
  20627  const auto keyRangeClause =
  20628      DatabaseOperationBase::MaybeGetBindingClauseForKeyRange(
  20629          GetOptionalKeyRange(), kColumnNameKey);
  20630 
  20631  const auto& directionClause = MakeDirectionClause(GetCursor().mDirection);
  20632 
  20633  // Note: Changing the number or order of SELECT columns in the query will
  20634  // require changes to CursorOpBase::PopulateResponseFromStatement.
  20635  const nsCString firstQuery =
  20636      queryStart + keyRangeClause + directionClause + kOpenLimit + "1"_ns;
  20637 
  20638  QM_TRY_INSPECT(const auto& stmt,
  20639                 aConnection->BorrowCachedStatement(firstQuery));
  20640 
  20641  QM_TRY(MOZ_TO_RESULT(
  20642      stmt->BindInt64ByName(kStmtParamNameId, GetCursor().mObjectStoreId)));
  20643 
  20644  if (usingKeyRange) {
  20645    QM_TRY(MOZ_TO_RESULT(DatabaseOperationBase::BindKeyRangeToStatement(
  20646        GetOptionalKeyRange().ref(), &*stmt)));
  20647  }
  20648 
  20649  // Now we need to make the query to get the next match.
  20650  PrepareKeyConditionClauses(directionClause, queryStart);
  20651 
  20652  return ProcessStatementSteps(&*stmt);
  20653 }
  20654 
  20655 nsresult OpenOpHelper<IDBCursorType::Index>::DoDatabaseWork(
  20656    DatabaseConnection* aConnection) {
  20657  MOZ_ASSERT(aConnection);
  20658  aConnection->AssertIsOnConnectionThread();
  20659  MOZ_ASSERT(GetCursor().mObjectStoreId);
  20660  MOZ_ASSERT(GetCursor().mIndexId);
  20661 
  20662  AUTO_PROFILER_LABEL("Cursor::OpenOp::DoIndexDatabaseWork", DOM);
  20663 
  20664  const bool usingKeyRange = GetOptionalKeyRange().isSome();
  20665 
  20666  const auto indexTable =
  20667      GetCursor().mUniqueIndex ? "unique_index_data"_ns : "index_data"_ns;
  20668 
  20669  // The result of MakeColumnPairSelectionList is stored in a local variable,
  20670  // since inlining it into the next statement causes a crash on some Mac OS X
  20671  // builds (see https://bugzilla.mozilla.org/show_bug.cgi?id=1168606#c110).
  20672  const auto columnPairSelectionList = MakeColumnPairSelectionList(
  20673      "index_table.value"_ns, "index_table.value_locale"_ns,
  20674      kColumnNameAliasSortKey, GetCursor().IsLocaleAware());
  20675  const nsCString sortColumnAlias =
  20676      "SELECT "_ns + columnPairSelectionList + ", "_ns;
  20677 
  20678  const nsAutoCString queryStart = sortColumnAlias +
  20679                                   "index_table.object_data_key, "
  20680                                   "object_data.file_ids, "
  20681                                   "object_data.data "
  20682                                   "FROM "_ns +
  20683                                   indexTable +
  20684                                   " AS index_table "
  20685                                   "JOIN object_data "
  20686                                   "ON index_table.object_store_id = "
  20687                                   "object_data.object_store_id "
  20688                                   "AND index_table.object_data_key = "
  20689                                   "object_data.key "
  20690                                   "WHERE index_table.index_id = :"_ns +
  20691                                   kStmtParamNameId;
  20692 
  20693  const auto keyRangeClause =
  20694      DatabaseOperationBase::MaybeGetBindingClauseForKeyRange(
  20695          GetOptionalKeyRange(), kColumnNameAliasSortKey);
  20696 
  20697  nsAutoCString directionClause = " ORDER BY "_ns + kColumnNameAliasSortKey;
  20698 
  20699  switch (GetCursor().mDirection) {
  20700    case IDBCursorDirection::Next:
  20701    case IDBCursorDirection::Nextunique:
  20702      directionClause.AppendLiteral(" ASC, index_table.object_data_key ASC");
  20703      break;
  20704 
  20705    case IDBCursorDirection::Prev:
  20706      directionClause.AppendLiteral(" DESC, index_table.object_data_key DESC");
  20707      break;
  20708 
  20709    case IDBCursorDirection::Prevunique:
  20710      directionClause.AppendLiteral(" DESC, index_table.object_data_key ASC");
  20711      break;
  20712 
  20713    default:
  20714      MOZ_CRASH("Should never get here!");
  20715  }
  20716 
  20717  // Note: Changing the number or order of SELECT columns in the query will
  20718  // require changes to CursorOpBase::PopulateResponseFromStatement.
  20719  const nsCString firstQuery = queryStart + keyRangeClause + directionClause +
  20720                               kOpenLimit +
  20721                               IntToCString(1 + GetCursor().mMaxExtraCount);
  20722 
  20723  QM_TRY_INSPECT(const auto& stmt,
  20724                 aConnection->BorrowCachedStatement(firstQuery));
  20725 
  20726  QM_TRY(MOZ_TO_RESULT(
  20727      stmt->BindInt64ByName(kStmtParamNameId, GetCursor().mIndexId)));
  20728 
  20729  if (usingKeyRange) {
  20730    if (GetCursor().IsLocaleAware()) {
  20731      QM_TRY(MOZ_TO_RESULT(DatabaseOperationBase::BindKeyRangeToStatement(
  20732          GetOptionalKeyRange().ref(), &*stmt, GetCursor().mLocale)));
  20733    } else {
  20734      QM_TRY(MOZ_TO_RESULT(DatabaseOperationBase::BindKeyRangeToStatement(
  20735          GetOptionalKeyRange().ref(), &*stmt)));
  20736    }
  20737  }
  20738 
  20739  // TODO: At least the last two statements are almost the same in all
  20740  // DoDatabaseWork variants, consider removing this duplication.
  20741 
  20742  // Now we need to make the query to get the next match.
  20743  PrepareKeyConditionClauses(directionClause, std::move(queryStart));
  20744 
  20745  return ProcessStatementSteps(&*stmt);
  20746 }
  20747 
  20748 nsresult OpenOpHelper<IDBCursorType::IndexKey>::DoDatabaseWork(
  20749    DatabaseConnection* aConnection) {
  20750  MOZ_ASSERT(aConnection);
  20751  aConnection->AssertIsOnConnectionThread();
  20752  MOZ_ASSERT(GetCursor().mObjectStoreId);
  20753  MOZ_ASSERT(GetCursor().mIndexId);
  20754 
  20755  AUTO_PROFILER_LABEL("Cursor::OpenOp::DoIndexKeyDatabaseWork", DOM);
  20756 
  20757  const bool usingKeyRange = GetOptionalKeyRange().isSome();
  20758 
  20759  const auto table =
  20760      GetCursor().mUniqueIndex ? "unique_index_data"_ns : "index_data"_ns;
  20761 
  20762  // The result of MakeColumnPairSelectionList is stored in a local variable,
  20763  // since inlining it into the next statement causes a crash on some Mac OS X
  20764  // builds (see https://bugzilla.mozilla.org/show_bug.cgi?id=1168606#c110).
  20765  const auto columnPairSelectionList = MakeColumnPairSelectionList(
  20766      "value"_ns, "value_locale"_ns, kColumnNameAliasSortKey,
  20767      GetCursor().IsLocaleAware());
  20768  const nsCString sortColumnAlias =
  20769      "SELECT "_ns + columnPairSelectionList + ", "_ns;
  20770 
  20771  const nsAutoCString queryStart = sortColumnAlias +
  20772                                   "object_data_key "
  20773                                   " FROM "_ns +
  20774                                   table + " WHERE index_id = :"_ns +
  20775                                   kStmtParamNameId;
  20776 
  20777  const auto keyRangeClause =
  20778      DatabaseOperationBase::MaybeGetBindingClauseForKeyRange(
  20779          GetOptionalKeyRange(), kColumnNameAliasSortKey);
  20780 
  20781  nsAutoCString directionClause = " ORDER BY "_ns + kColumnNameAliasSortKey;
  20782 
  20783  switch (GetCursor().mDirection) {
  20784    case IDBCursorDirection::Next:
  20785    case IDBCursorDirection::Nextunique:
  20786      directionClause.AppendLiteral(" ASC, object_data_key ASC");
  20787      break;
  20788 
  20789    case IDBCursorDirection::Prev:
  20790      directionClause.AppendLiteral(" DESC, object_data_key DESC");
  20791      break;
  20792 
  20793    case IDBCursorDirection::Prevunique:
  20794      directionClause.AppendLiteral(" DESC, object_data_key ASC");
  20795      break;
  20796 
  20797    default:
  20798      MOZ_CRASH("Should never get here!");
  20799  }
  20800 
  20801  // Note: Changing the number or order of SELECT columns in the query will
  20802  // require changes to CursorOpBase::PopulateResponseFromStatement.
  20803  const nsCString firstQuery =
  20804      queryStart + keyRangeClause + directionClause + kOpenLimit + "1"_ns;
  20805 
  20806  QM_TRY_INSPECT(const auto& stmt,
  20807                 aConnection->BorrowCachedStatement(firstQuery));
  20808 
  20809  QM_TRY(MOZ_TO_RESULT(
  20810      stmt->BindInt64ByName(kStmtParamNameId, GetCursor().mIndexId)));
  20811 
  20812  if (usingKeyRange) {
  20813    if (GetCursor().IsLocaleAware()) {
  20814      QM_TRY(MOZ_TO_RESULT(DatabaseOperationBase::BindKeyRangeToStatement(
  20815          GetOptionalKeyRange().ref(), &*stmt, GetCursor().mLocale)));
  20816    } else {
  20817      QM_TRY(MOZ_TO_RESULT(DatabaseOperationBase::BindKeyRangeToStatement(
  20818          GetOptionalKeyRange().ref(), &*stmt)));
  20819    }
  20820  }
  20821 
  20822  // Now we need to make the query to get the next match.
  20823  PrepareKeyConditionClauses(directionClause, std::move(queryStart));
  20824 
  20825  return ProcessStatementSteps(&*stmt);
  20826 }
  20827 
  20828 template <IDBCursorType CursorType>
  20829 nsresult Cursor<CursorType>::OpenOp::DoDatabaseWork(
  20830    DatabaseConnection* aConnection) {
  20831  MOZ_ASSERT(aConnection);
  20832  aConnection->AssertIsOnConnectionThread();
  20833  MOZ_ASSERT(mCursor);
  20834  MOZ_ASSERT(!mCursor->mContinueQueries);
  20835 
  20836  AUTO_PROFILER_LABEL("Cursor::OpenOp::DoDatabaseWork", DOM);
  20837 
  20838  auto helper = OpenOpHelper<CursorType>{*this};
  20839  const auto rv = helper.DoDatabaseWork(aConnection);
  20840  if (NS_WARN_IF(NS_FAILED(rv))) {
  20841    return rv;
  20842  }
  20843 
  20844  return NS_OK;
  20845 }
  20846 
  20847 template <IDBCursorType CursorType>
  20848 nsresult Cursor<CursorType>::CursorOpBase::SendSuccessResult() {
  20849  AssertIsOnOwningThread();
  20850  MOZ_ASSERT(mCursor);
  20851  MOZ_ASSERT(mCursor->mCurrentlyRunningOp == this);
  20852  MOZ_ASSERT(mResponse.type() != CursorResponse::T__None);
  20853 
  20854  if (IsActorDestroyed()) {
  20855    return NS_ERROR_DOM_INDEXEDDB_ABORT_ERR;
  20856  }
  20857 
  20858  mCursor->SendResponseInternal(mResponse, mFiles);
  20859 
  20860 #ifdef DEBUG
  20861  mResponseSent = true;
  20862 #endif
  20863  return NS_OK;
  20864 }
  20865 
  20866 template <IDBCursorType CursorType>
  20867 nsresult Cursor<CursorType>::ContinueOp::DoDatabaseWork(
  20868    DatabaseConnection* aConnection) {
  20869  MOZ_ASSERT(aConnection);
  20870  aConnection->AssertIsOnConnectionThread();
  20871  MOZ_ASSERT(mCursor);
  20872  MOZ_ASSERT(mCursor->mObjectStoreId);
  20873  MOZ_ASSERT(!mCursor->mContinueQueries->mContinueQuery.IsEmpty());
  20874  MOZ_ASSERT(!mCursor->mContinueQueries->mContinueToQuery.IsEmpty());
  20875  MOZ_ASSERT(!mCurrentPosition.mKey.IsUnset());
  20876 
  20877  if constexpr (IsIndexCursor) {
  20878    MOZ_ASSERT_IF(
  20879        mCursor->mDirection == IDBCursorDirection::Next ||
  20880            mCursor->mDirection == IDBCursorDirection::Prev,
  20881        !mCursor->mContinueQueries->mContinuePrimaryKeyQuery.IsEmpty());
  20882    MOZ_ASSERT(mCursor->mIndexId);
  20883    MOZ_ASSERT(!mCurrentPosition.mObjectStoreKey.IsUnset());
  20884  }
  20885 
  20886  AUTO_PROFILER_LABEL("Cursor::ContinueOp::DoDatabaseWork", DOM);
  20887 
  20888  // We need to pick a query based on whether or not a key was passed to the
  20889  // continue function. If not we'll grab the next item in the database that
  20890  // is greater than (or less than, if we're running a PREV cursor) the current
  20891  // key. If a key was passed we'll grab the next item in the database that is
  20892  // greater than (or less than, if we're running a PREV cursor) or equal to the
  20893  // key that was specified.
  20894  //
  20895  // TODO: The description above is not complete, it does not take account of
  20896  // ContinuePrimaryKey nor Advance.
  20897  //
  20898  // Note: Changing the number or order of SELECT columns in the query will
  20899  // require changes to CursorOpBase::PopulateResponseFromStatement.
  20900 
  20901  const uint32_t advanceCount =
  20902      mParams.type() == CursorRequestParams::TAdvanceParams
  20903          ? mParams.get_AdvanceParams().count()
  20904          : 1;
  20905  MOZ_ASSERT(advanceCount > 0);
  20906 
  20907  bool hasContinueKey = false;
  20908  bool hasContinuePrimaryKey = false;
  20909 
  20910  auto explicitContinueKey = Key{};
  20911 
  20912  switch (mParams.type()) {
  20913    case CursorRequestParams::TContinueParams:
  20914      if (!mParams.get_ContinueParams().key().IsUnset()) {
  20915        hasContinueKey = true;
  20916        explicitContinueKey = mParams.get_ContinueParams().key();
  20917      }
  20918      break;
  20919    case CursorRequestParams::TContinuePrimaryKeyParams:
  20920      MOZ_ASSERT(!mParams.get_ContinuePrimaryKeyParams().key().IsUnset());
  20921      MOZ_ASSERT(
  20922          !mParams.get_ContinuePrimaryKeyParams().primaryKey().IsUnset());
  20923      MOZ_ASSERT(mCursor->mDirection == IDBCursorDirection::Next ||
  20924                 mCursor->mDirection == IDBCursorDirection::Prev);
  20925      hasContinueKey = true;
  20926      hasContinuePrimaryKey = true;
  20927      explicitContinueKey = mParams.get_ContinuePrimaryKeyParams().key();
  20928      break;
  20929    case CursorRequestParams::TAdvanceParams:
  20930      break;
  20931    default:
  20932      MOZ_CRASH("Should never get here!");
  20933  }
  20934 
  20935  // TODO: Whether it makes sense to preload depends on the kind of the
  20936  // subsequent operations, not of the current operation. We could assume that
  20937  // the subsequent operations are:
  20938  // - the same as the current operation (with the same parameter values)
  20939  // - as above, except for Advance, where we assume the count will be 1 on the
  20940  // next call
  20941  // - basic operations (Advance with count 1 or Continue-without-key)
  20942  //
  20943  // For now, we implement the second option for now (which correspond to
  20944  // !hasContinueKey).
  20945  //
  20946  // Based on that, we could in both cases either preload for any assumed
  20947  // subsequent operations, or only for the basic operations. For now, we
  20948  // preload only for an assumed basic operation. Other operations would require
  20949  // more work on the client side for invalidation, and may not make any sense
  20950  // at all.
  20951  const uint32_t maxExtraCount = hasContinueKey ? 0 : mCursor->mMaxExtraCount;
  20952 
  20953  QM_TRY_INSPECT(const auto& stmt,
  20954                 aConnection->BorrowCachedStatement(
  20955                     mCursor->mContinueQueries->GetContinueQuery(
  20956                         hasContinueKey, hasContinuePrimaryKey)));
  20957 
  20958  QM_TRY(MOZ_TO_RESULT(stmt->BindUTF8StringByName(
  20959      kStmtParamNameLimit,
  20960      IntToCString(advanceCount + mCursor->mMaxExtraCount))));
  20961 
  20962  QM_TRY(MOZ_TO_RESULT(stmt->BindInt64ByName(kStmtParamNameId, mCursor->Id())));
  20963 
  20964  // Bind current key.
  20965  const auto& continueKey =
  20966      hasContinueKey ? explicitContinueKey
  20967                     : mCurrentPosition.GetSortKey(mCursor->IsLocaleAware());
  20968  QM_TRY(MOZ_TO_RESULT(
  20969      continueKey.BindToStatement(&*stmt, kStmtParamNameCurrentKey)));
  20970 
  20971  // Bind range bound if it is specified.
  20972  if (!mCursor->mLocaleAwareRangeBound->IsUnset()) {
  20973    QM_TRY(MOZ_TO_RESULT(mCursor->mLocaleAwareRangeBound->BindToStatement(
  20974        &*stmt, kStmtParamNameRangeBound)));
  20975  }
  20976 
  20977  // Bind object store position if duplicates are allowed and we're not
  20978  // continuing to a specific key.
  20979  if constexpr (IsIndexCursor) {
  20980    if (!hasContinueKey && (mCursor->mDirection == IDBCursorDirection::Next ||
  20981                            mCursor->mDirection == IDBCursorDirection::Prev)) {
  20982      QM_TRY(MOZ_TO_RESULT(mCurrentPosition.mObjectStoreKey.BindToStatement(
  20983          &*stmt, kStmtParamNameObjectStorePosition)));
  20984    } else if (hasContinuePrimaryKey) {
  20985      QM_TRY(MOZ_TO_RESULT(
  20986          mParams.get_ContinuePrimaryKeyParams().primaryKey().BindToStatement(
  20987              &*stmt, kStmtParamNameObjectStorePosition)));
  20988    }
  20989  }
  20990 
  20991  // TODO: Why do we query the records we don't need and skip them here, rather
  20992  // than using a OFFSET clause in the query?
  20993  for (uint32_t index = 0; index < advanceCount; index++) {
  20994    QM_TRY_INSPECT(const bool& hasResult,
  20995                   MOZ_TO_RESULT_INVOKE_MEMBER(&*stmt, ExecuteStep));
  20996 
  20997    if (!hasResult) {
  20998      mResponse = void_t();
  20999      return NS_OK;
  21000    }
  21001  }
  21002 
  21003  Key previousKey;
  21004  auto* const optPreviousKey =
  21005      IsUnique(mCursor->mDirection) ? &previousKey : nullptr;
  21006 
  21007  auto helper = CursorOpBaseHelperBase<CursorType>{*this};
  21008  QM_TRY_INSPECT(const auto& responseSize, helper.PopulateResponseFromStatement(
  21009                                               &*stmt, true, optPreviousKey));
  21010 
  21011  helper.PopulateExtraResponses(&*stmt, maxExtraCount, responseSize,
  21012                                "ContinueOp"_ns, optPreviousKey);
  21013 
  21014  return NS_OK;
  21015 }
  21016 
  21017 Utils::Utils()
  21018 #ifdef DEBUG
  21019    : mActorDestroyed(false)
  21020 #endif
  21021 {
  21022  AssertIsOnBackgroundThread();
  21023 }
  21024 
  21025 Utils::~Utils() { MOZ_ASSERT(mActorDestroyed); }
  21026 
  21027 void Utils::ActorDestroy(ActorDestroyReason aWhy) {
  21028  AssertIsOnBackgroundThread();
  21029  MOZ_ASSERT(!mActorDestroyed);
  21030 
  21031 #ifdef DEBUG
  21032  mActorDestroyed = true;
  21033 #endif
  21034 }
  21035 
  21036 mozilla::ipc::IPCResult Utils::RecvDeleteMe() {
  21037  AssertIsOnBackgroundThread();
  21038  MOZ_ASSERT(!mActorDestroyed);
  21039 
  21040  QM_WARNONLY_TRY(OkIf(PBackgroundIndexedDBUtilsParent::Send__delete__(this)));
  21041 
  21042  return IPC_OK();
  21043 }
  21044 
  21045 mozilla::ipc::IPCResult Utils::RecvGetFileReferences(
  21046    const PersistenceType& aPersistenceType, const nsACString& aOrigin,
  21047    const nsAString& aDatabaseName, const int64_t& aFileId, int32_t* aRefCnt,
  21048    int32_t* aDBRefCnt, bool* aResult) {
  21049  AssertIsOnBackgroundThread();
  21050  MOZ_ASSERT(aRefCnt);
  21051  MOZ_ASSERT(aDBRefCnt);
  21052  MOZ_ASSERT(aResult);
  21053  MOZ_ASSERT(!mActorDestroyed);
  21054 
  21055  if (NS_WARN_IF(!IndexedDatabaseManager::Get())) {
  21056    return IPC_FAIL(this, "No IndexedDatabaseManager active!");
  21057  }
  21058 
  21059  if (NS_WARN_IF(!QuotaManager::Get())) {
  21060    return IPC_FAIL(this, "No QuotaManager active!");
  21061  }
  21062 
  21063  if (NS_WARN_IF(!StaticPrefs::dom_indexedDB_testing())) {
  21064    return IPC_FAIL(this, "IndexedDB is not in testing mode!");
  21065  }
  21066 
  21067  if (NS_WARN_IF(!IsValidPersistenceType(aPersistenceType))) {
  21068    return IPC_FAIL(this, "PersistenceType is not valid!");
  21069  }
  21070 
  21071  if (NS_WARN_IF(aOrigin.IsEmpty())) {
  21072    return IPC_FAIL(this, "Origin is empty!");
  21073  }
  21074 
  21075  if (NS_WARN_IF(aDatabaseName.IsEmpty())) {
  21076    return IPC_FAIL(this, "DatabaseName is empty!");
  21077  }
  21078 
  21079  if (NS_WARN_IF(aFileId == 0)) {
  21080    return IPC_FAIL(this, "No FileId!");
  21081  }
  21082 
  21083  nsresult rv =
  21084      DispatchAndReturnFileReferences(aPersistenceType, aOrigin, aDatabaseName,
  21085                                      aFileId, aRefCnt, aDBRefCnt, aResult);
  21086  if (NS_WARN_IF(NS_FAILED(rv))) {
  21087    return IPC_FAIL(this, "DispatchAndReturnFileReferences failed!");
  21088  }
  21089 
  21090  return IPC_OK();
  21091 }
  21092 
  21093 mozilla::ipc::IPCResult Utils::RecvDoMaintenance(
  21094    DoMaintenanceResolver&& aResolver) {
  21095  AssertIsOnBackgroundThread();
  21096 
  21097  QM_TRY(MOZ_TO_RESULT(!QuotaManager::IsShuttingDown()),
  21098         ResolveNSResultAndReturn(aResolver));
  21099 
  21100  QM_TRY(QuotaManager::EnsureCreated(), ResolveNSResultAndReturn(aResolver));
  21101 
  21102  QuotaClient* quotaClient = QuotaClient::GetInstance();
  21103  QM_TRY(MOZ_TO_RESULT(quotaClient), QM_IPC_FAIL(this));
  21104 
  21105  quotaClient->DoMaintenance()->Then(
  21106      GetCurrentSerialEventTarget(), __func__,
  21107      [self = RefPtr(this), resolver = std::move(aResolver)](
  21108          const BoolPromise::ResolveOrRejectValue& aValue) {
  21109        if (!self->CanSend()) {
  21110          return;
  21111        }
  21112 
  21113        if (aValue.IsResolve()) {
  21114          resolver(NS_OK);
  21115        } else {
  21116          resolver(aValue.RejectValue());
  21117        }
  21118      });
  21119 
  21120  return IPC_OK();
  21121 }
  21122 
  21123 #ifdef DEBUG
  21124 
  21125 NS_IMPL_ISUPPORTS(DEBUGThreadSlower, nsIThreadObserver)
  21126 
  21127 NS_IMETHODIMP
  21128 DEBUGThreadSlower::OnDispatchedEvent() { MOZ_CRASH("Should never be called!"); }
  21129 
  21130 NS_IMETHODIMP
  21131 DEBUGThreadSlower::OnProcessNextEvent(nsIThreadInternal* /* aThread */,
  21132                                      bool /* aMayWait */) {
  21133  return NS_OK;
  21134 }
  21135 
  21136 NS_IMETHODIMP
  21137 DEBUGThreadSlower::AfterProcessNextEvent(nsIThreadInternal* /* aThread */,
  21138                                         bool /* aEventWasProcessed */) {
  21139  MOZ_ASSERT(kDEBUGThreadSleepMS);
  21140 
  21141  MOZ_ALWAYS_TRUE(PR_Sleep(PR_MillisecondsToInterval(kDEBUGThreadSleepMS)) ==
  21142                  PR_SUCCESS);
  21143  return NS_OK;
  21144 }
  21145 
  21146 #endif  // DEBUG
  21147 
  21148 nsresult FileHelper::Init() {
  21149  MOZ_ASSERT(!IsOnBackgroundThread());
  21150 
  21151  auto fileDirectory = mFileManager->GetCheckedDirectory();
  21152  if (NS_WARN_IF(!fileDirectory)) {
  21153    return NS_ERROR_FAILURE;
  21154  }
  21155 
  21156  auto journalDirectory = mFileManager->EnsureJournalDirectory();
  21157  if (NS_WARN_IF(!journalDirectory)) {
  21158    return NS_ERROR_FAILURE;
  21159  }
  21160 
  21161  DebugOnly<bool> exists;
  21162  MOZ_ASSERT(NS_SUCCEEDED(journalDirectory->Exists(&exists)));
  21163  MOZ_ASSERT(exists);
  21164 
  21165  DebugOnly<bool> isDirectory;
  21166  MOZ_ASSERT(NS_SUCCEEDED(journalDirectory->IsDirectory(&isDirectory)));
  21167  MOZ_ASSERT(isDirectory);
  21168 
  21169  mFileDirectory.init(WrapNotNullUnchecked(std::move(fileDirectory)));
  21170  mJournalDirectory.init(WrapNotNullUnchecked(std::move(journalDirectory)));
  21171 
  21172  return NS_OK;
  21173 }
  21174 
  21175 nsCOMPtr<nsIFile> FileHelper::GetFile(const DatabaseFileInfo& aFileInfo) {
  21176  MOZ_ASSERT(!IsOnBackgroundThread());
  21177 
  21178  return mFileManager->GetFileForId(mFileDirectory->get(), aFileInfo.Id());
  21179 }
  21180 
  21181 nsCOMPtr<nsIFile> FileHelper::GetJournalFile(
  21182    const DatabaseFileInfo& aFileInfo) {
  21183  MOZ_ASSERT(!IsOnBackgroundThread());
  21184 
  21185  return mFileManager->GetFileForId(mJournalDirectory->get(), aFileInfo.Id());
  21186 }
  21187 
  21188 nsresult FileHelper::CreateFileFromStream(nsIFile& aFile, nsIFile& aJournalFile,
  21189                                          nsIInputStream& aInputStream,
  21190                                          bool aCompress,
  21191                                          const Maybe<CipherKey>& aMaybeKey) {
  21192  MOZ_ASSERT(!IsOnBackgroundThread());
  21193 
  21194  QM_TRY_INSPECT(const auto& exists,
  21195                 MOZ_TO_RESULT_INVOKE_MEMBER(aFile, Exists));
  21196 
  21197  // DOM blobs that are being stored in IDB are cached by calling
  21198  // IDBDatabase::GetOrCreateFileActorForBlob. So if the same DOM blob is stored
  21199  // again under a different key or in a different object store, we just add
  21200  // a new reference instead of creating a new copy (all such stored blobs share
  21201  // the same id).
  21202  // However, it can happen that CreateFileFromStream failed due to quota
  21203  // exceeded error and for some reason the orphaned file couldn't be deleted
  21204  // immediately. Now, if the operation is being repeated, the DOM blob is
  21205  // already cached, so it has the same file id which clashes with the orphaned
  21206  // file. We could do some tricks to restore previous copy loop, but it's safer
  21207  // to just delete the orphaned file and start from scratch.
  21208  // This corner case is partially simulated in test_file_copy_failure.js
  21209  if (exists) {
  21210    QM_TRY_INSPECT(const auto& isFile,
  21211                   MOZ_TO_RESULT_INVOKE_MEMBER(aFile, IsFile));
  21212 
  21213    QM_TRY(OkIf(isFile), NS_ERROR_FAILURE);
  21214 
  21215    QM_TRY_INSPECT(const auto& journalExists,
  21216                   MOZ_TO_RESULT_INVOKE_MEMBER(aJournalFile, Exists));
  21217 
  21218    QM_TRY(OkIf(journalExists), NS_ERROR_FAILURE);
  21219 
  21220    QM_TRY_INSPECT(const auto& journalIsFile,
  21221                   MOZ_TO_RESULT_INVOKE_MEMBER(aJournalFile, IsFile));
  21222 
  21223    QM_TRY(OkIf(journalIsFile), NS_ERROR_FAILURE);
  21224 
  21225    IDB_WARNING("Deleting orphaned file!");
  21226 
  21227    QM_TRY(MOZ_TO_RESULT(mFileManager->SyncDeleteFile(aFile, aJournalFile)));
  21228  }
  21229 
  21230  // Create a journal file first.
  21231  QM_TRY(MOZ_TO_RESULT(aJournalFile.Create(nsIFile::NORMAL_FILE_TYPE, 0644)));
  21232 
  21233  // Now try to copy the stream.
  21234  QM_TRY_UNWRAP(nsCOMPtr<nsIOutputStream> fileOutputStream,
  21235                CreateFileOutputStream(mFileManager->Type(),
  21236                                       mFileManager->OriginMetadata(),
  21237                                       Client::IDB, &aFile));
  21238 
  21239  AutoTArray<char, kFileCopyBufferSize> buffer;
  21240  const auto actualOutputStream =
  21241      [aCompress, &aMaybeKey, &buffer,
  21242       baseOutputStream =
  21243           std::move(fileOutputStream)]() mutable -> nsCOMPtr<nsIOutputStream> {
  21244    if (aMaybeKey) {
  21245      baseOutputStream =
  21246          MakeRefPtr<EncryptingOutputStream<IndexedDBCipherStrategy>>(
  21247              std::move(baseOutputStream), kEncryptedStreamBlockSize,
  21248              *aMaybeKey);
  21249    }
  21250 
  21251    if (aCompress) {
  21252      auto snappyOutputStream =
  21253          MakeRefPtr<SnappyCompressOutputStream>(baseOutputStream);
  21254 
  21255      buffer.SetLength(snappyOutputStream->BlockSize());
  21256 
  21257      return snappyOutputStream;
  21258    }
  21259 
  21260    buffer.SetLength(kFileCopyBufferSize);
  21261    return std::move(baseOutputStream);
  21262  }();
  21263 
  21264  QM_TRY(MOZ_TO_RESULT(SyncCopy(aInputStream, *actualOutputStream,
  21265                                buffer.Elements(), buffer.Length())));
  21266 
  21267  return NS_OK;
  21268 }
  21269 
  21270 class FileHelper::ReadCallback final : public nsIInputStreamCallback {
  21271 public:
  21272  NS_DECL_THREADSAFE_ISUPPORTS
  21273 
  21274  ReadCallback()
  21275      : mMutex("ReadCallback::mMutex"),
  21276        mCondVar(mMutex, "ReadCallback::mCondVar"),
  21277        mInputAvailable(false) {}
  21278 
  21279  NS_IMETHOD
  21280  OnInputStreamReady(nsIAsyncInputStream* aStream) override {
  21281    mozilla::MutexAutoLock autolock(mMutex);
  21282 
  21283    mInputAvailable = true;
  21284    mCondVar.Notify();
  21285 
  21286    return NS_OK;
  21287  }
  21288 
  21289  nsresult AsyncWait(nsIAsyncInputStream* aStream, uint32_t aBufferSize,
  21290                     nsIEventTarget* aTarget) {
  21291    MOZ_ASSERT(aStream);
  21292    mozilla::MutexAutoLock autolock(mMutex);
  21293 
  21294    nsresult rv = aStream->AsyncWait(this, 0, aBufferSize, aTarget);
  21295    if (NS_WARN_IF(NS_FAILED(rv))) {
  21296      return rv;
  21297    }
  21298 
  21299    mInputAvailable = false;
  21300    while (!mInputAvailable) {
  21301      mCondVar.Wait();
  21302    }
  21303 
  21304    return NS_OK;
  21305  }
  21306 
  21307 private:
  21308  ~ReadCallback() = default;
  21309 
  21310  mozilla::Mutex mMutex MOZ_UNANNOTATED;
  21311  mozilla::CondVar mCondVar;
  21312  bool mInputAvailable;
  21313 };
  21314 
  21315 NS_IMPL_ADDREF(FileHelper::ReadCallback);
  21316 NS_IMPL_RELEASE(FileHelper::ReadCallback);
  21317 
  21318 NS_INTERFACE_MAP_BEGIN(FileHelper::ReadCallback)
  21319  NS_INTERFACE_MAP_ENTRY(nsIInputStreamCallback)
  21320  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStreamCallback)
  21321 NS_INTERFACE_MAP_END
  21322 
  21323 nsresult FileHelper::SyncRead(nsIInputStream& aInputStream, char* const aBuffer,
  21324                              const uint32_t aBufferSize,
  21325                              uint32_t* const aRead) {
  21326  MOZ_ASSERT(!IsOnBackgroundThread());
  21327 
  21328  // Let's try to read, directly.
  21329  nsresult rv = aInputStream.Read(aBuffer, aBufferSize, aRead);
  21330  if (NS_SUCCEEDED(rv) || rv != NS_BASE_STREAM_WOULD_BLOCK) {
  21331    return rv;
  21332  }
  21333 
  21334  // We need to proceed async.
  21335  nsCOMPtr<nsIAsyncInputStream> asyncStream = do_QueryInterface(&aInputStream);
  21336  if (!asyncStream) {
  21337    return rv;
  21338  }
  21339 
  21340  if (!mReadCallback) {
  21341    mReadCallback.init(MakeNotNull<RefPtr<ReadCallback>>());
  21342  }
  21343 
  21344  // We just need any thread with an event loop for receiving the
  21345  // OnInputStreamReady callback. Let's use the I/O thread.
  21346  nsCOMPtr<nsIEventTarget> target =
  21347      do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
  21348  MOZ_ASSERT(target);
  21349 
  21350  rv = (*mReadCallback)->AsyncWait(asyncStream, aBufferSize, target);
  21351  if (NS_WARN_IF(NS_FAILED(rv))) {
  21352    return rv;
  21353  }
  21354 
  21355  return SyncRead(aInputStream, aBuffer, aBufferSize, aRead);
  21356 }
  21357 
  21358 nsresult FileHelper::SyncCopy(nsIInputStream& aInputStream,
  21359                              nsIOutputStream& aOutputStream,
  21360                              char* const aBuffer, const uint32_t aBufferSize) {
  21361  MOZ_ASSERT(!IsOnBackgroundThread());
  21362 
  21363  AUTO_PROFILER_LABEL("FileHelper::SyncCopy", DOM);
  21364 
  21365  nsresult rv;
  21366 
  21367  do {
  21368    uint32_t numRead;
  21369    rv = SyncRead(aInputStream, aBuffer, aBufferSize, &numRead);
  21370    if (NS_WARN_IF(NS_FAILED(rv))) {
  21371      break;
  21372    }
  21373 
  21374    if (!numRead) {
  21375      break;
  21376    }
  21377 
  21378    uint32_t numWrite;
  21379    rv = aOutputStream.Write(aBuffer, numRead, &numWrite);
  21380    if (rv == NS_ERROR_FILE_NO_DEVICE_SPACE) {
  21381      rv = NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
  21382    }
  21383    if (NS_WARN_IF(NS_FAILED(rv))) {
  21384      break;
  21385    }
  21386 
  21387    if (NS_WARN_IF(numWrite != numRead)) {
  21388      rv = NS_ERROR_FAILURE;
  21389      break;
  21390    }
  21391  } while (true);
  21392 
  21393  if (NS_SUCCEEDED(rv)) {
  21394    rv = aOutputStream.Flush();
  21395    if (NS_WARN_IF(NS_FAILED(rv))) {
  21396      return rv;
  21397    }
  21398  }
  21399 
  21400  nsresult rv2 = aOutputStream.Close();
  21401  if (NS_WARN_IF(NS_FAILED(rv2))) {
  21402    return NS_SUCCEEDED(rv) ? rv2 : rv;
  21403  }
  21404 
  21405  return rv;
  21406 }
  21407 
  21408 }  // namespace dom::indexedDB
  21409 }  // namespace mozilla
  21410 
  21411 #undef IDB_MOBILE
  21412 #undef IDB_DEBUG_LOG