tor-browser

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

ActorsParent.cpp (282955B)


      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 // Local includes
     10 #include "LSInitializationTypes.h"
     11 #include "LSObject.h"
     12 #include "ReportInternalError.h"
     13 
     14 // Global includes
     15 #include <cinttypes>
     16 #include <cstdlib>
     17 #include <cstring>
     18 #include <new>
     19 #include <type_traits>
     20 #include <utility>
     21 
     22 #include "ErrorList.h"
     23 #include "MainThreadUtils.h"
     24 #include "NotifyUtils.h"
     25 #include "mozIStorageAsyncConnection.h"
     26 #include "mozIStorageConnection.h"
     27 #include "mozIStorageFunction.h"
     28 #include "mozIStorageService.h"
     29 #include "mozIStorageStatement.h"
     30 #include "mozIStorageValueArray.h"
     31 #include "mozStorageCID.h"
     32 #include "mozStorageHelper.h"
     33 #include "mozilla/Assertions.h"
     34 #include "mozilla/Atomics.h"
     35 #include "mozilla/Attributes.h"
     36 #include "mozilla/DebugOnly.h"
     37 #include "mozilla/GeckoTrace.h"
     38 #include "mozilla/Logging.h"
     39 #include "mozilla/Maybe.h"
     40 #include "mozilla/Monitor.h"
     41 #include "mozilla/Mutex.h"
     42 #include "mozilla/NotNull.h"
     43 #include "mozilla/OriginAttributes.h"
     44 #include "mozilla/Preferences.h"
     45 #include "mozilla/RefPtr.h"
     46 #include "mozilla/Result.h"
     47 #include "mozilla/ResultExtensions.h"
     48 #include "mozilla/ScopeExit.h"
     49 #include "mozilla/Services.h"
     50 #include "mozilla/StaticPrefs_dom.h"
     51 #include "mozilla/StaticPtr.h"
     52 #include "mozilla/StoragePrincipalHelper.h"
     53 #include "mozilla/UniquePtr.h"
     54 #include "mozilla/Variant.h"
     55 #include "mozilla/dom/ClientManagerService.h"
     56 #include "mozilla/dom/FlippedOnce.h"
     57 #include "mozilla/dom/LSSnapshot.h"
     58 #include "mozilla/dom/LSValue.h"
     59 #include "mozilla/dom/LSWriteOptimizer.h"
     60 #include "mozilla/dom/LSWriteOptimizerImpl.h"
     61 #include "mozilla/dom/LocalStorageCommon.h"
     62 #include "mozilla/dom/Nullable.h"
     63 #include "mozilla/dom/PBackgroundLSDatabase.h"
     64 #include "mozilla/dom/PBackgroundLSDatabaseParent.h"
     65 #include "mozilla/dom/PBackgroundLSObserverParent.h"
     66 #include "mozilla/dom/PBackgroundLSRequestParent.h"
     67 #include "mozilla/dom/PBackgroundLSSharedTypes.h"
     68 #include "mozilla/dom/PBackgroundLSSimpleRequestParent.h"
     69 #include "mozilla/dom/PBackgroundLSSnapshotParent.h"
     70 #include "mozilla/dom/SnappyUtils.h"
     71 #include "mozilla/dom/StorageDBUpdater.h"
     72 #include "mozilla/dom/StorageUtils.h"
     73 #include "mozilla/dom/ipc/IdType.h"
     74 #include "mozilla/dom/quota/CachingDatabaseConnection.h"
     75 #include "mozilla/dom/quota/CheckedUnsafePtr.h"
     76 #include "mozilla/dom/quota/Client.h"
     77 #include "mozilla/dom/quota/ClientDirectoryLock.h"
     78 #include "mozilla/dom/quota/ClientDirectoryLockHandle.h"
     79 #include "mozilla/dom/quota/ClientImpl.h"
     80 #include "mozilla/dom/quota/FirstInitializationAttemptsImpl.h"
     81 #include "mozilla/dom/quota/HashKeys.h"
     82 #include "mozilla/dom/quota/OriginScope.h"
     83 #include "mozilla/dom/quota/PersistenceScope.h"
     84 #include "mozilla/dom/quota/PersistenceType.h"
     85 #include "mozilla/dom/quota/PrincipalUtils.h"
     86 #include "mozilla/dom/quota/QuotaCommon.h"
     87 #include "mozilla/dom/quota/QuotaManager.h"
     88 #include "mozilla/dom/quota/QuotaObject.h"
     89 #include "mozilla/dom/quota/ResultExtensions.h"
     90 #include "mozilla/dom/quota/StorageHelpers.h"
     91 #include "mozilla/dom/quota/ThreadUtils.h"
     92 #include "mozilla/dom/quota/UsageInfo.h"
     93 #include "mozilla/glean/DomLocalstorageMetrics.h"
     94 #include "mozilla/ipc/BackgroundChild.h"
     95 #include "mozilla/ipc/BackgroundParent.h"
     96 #include "mozilla/ipc/PBackgroundChild.h"
     97 #include "mozilla/ipc/PBackgroundParent.h"
     98 #include "mozilla/ipc/PBackgroundSharedTypes.h"
     99 #include "mozilla/ipc/ProtocolUtils.h"
    100 #include "mozilla/storage/Variant.h"
    101 #include "nsBaseHashtable.h"
    102 #include "nsCOMPtr.h"
    103 #include "nsClassHashtable.h"
    104 #include "nsDebug.h"
    105 #include "nsError.h"
    106 #include "nsHashKeys.h"
    107 #include "nsIBinaryInputStream.h"
    108 #include "nsIBinaryOutputStream.h"
    109 #include "nsIDirectoryEnumerator.h"
    110 #include "nsIEventTarget.h"
    111 #include "nsIFile.h"
    112 #include "nsIInputStream.h"
    113 #include "nsIObjectInputStream.h"
    114 #include "nsIObjectOutputStream.h"
    115 #include "nsIObserver.h"
    116 #include "nsIObserverService.h"
    117 #include "nsIOutputStream.h"
    118 #include "nsIRunnable.h"
    119 #include "nsISerialEventTarget.h"
    120 #include "nsISupports.h"
    121 #include "nsIThread.h"
    122 #include "nsITimer.h"
    123 #include "nsIVariant.h"
    124 #include "nsInterfaceHashtable.h"
    125 #include "nsLiteralString.h"
    126 #include "nsNetUtil.h"
    127 #include "nsPointerHashKeys.h"
    128 #include "nsPrintfCString.h"
    129 #include "nsRefPtrHashtable.h"
    130 #include "nsServiceManagerUtils.h"
    131 #include "nsString.h"
    132 #include "nsStringFlags.h"
    133 #include "nsStringFwd.h"
    134 #include "nsTArray.h"
    135 #include "nsTHashMap.h"
    136 #include "nsTHashSet.h"
    137 #include "nsTLiteralString.h"
    138 #include "nsTStringRepr.h"
    139 #include "nsThreadUtils.h"
    140 #include "nsVariant.h"
    141 #include "nsXPCOM.h"
    142 #include "nsXULAppAPI.h"
    143 #include "nscore.h"
    144 #include "prenv.h"
    145 #include "prtime.h"
    146 
    147 #define LS_LOG_TEST() MOZ_LOG_TEST(GetLocalStorageLogger(), LogLevel::Info)
    148 #define LS_LOG(_args) MOZ_LOG(GetLocalStorageLogger(), LogLevel::Info, _args)
    149 
    150 #if defined(MOZ_WIDGET_ANDROID)
    151 #  define LS_MOBILE
    152 #endif
    153 
    154 namespace mozilla::dom {
    155 
    156 using namespace mozilla::dom::quota;
    157 using namespace mozilla::dom::StorageUtils;
    158 using namespace mozilla::ipc;
    159 
    160 namespace {
    161 
    162 struct ArchivedOriginInfo;
    163 class ArchivedOriginScope;
    164 class Connection;
    165 class ConnectionThread;
    166 class Database;
    167 class Observer;
    168 class PrepareDatastoreOp;
    169 class PreparedDatastore;
    170 class QuotaClient;
    171 class Snapshot;
    172 
    173 using ArchivedOriginHashtable =
    174    nsClassHashtable<nsCStringHashKey, ArchivedOriginInfo>;
    175 
    176 /*******************************************************************************
    177 * Constants
    178 ******************************************************************************/
    179 
    180 // Major schema version. Bump for almost everything.
    181 const uint32_t kMajorSchemaVersion = 5;
    182 
    183 // Minor schema version. Should almost always be 0 (maybe bump on release
    184 // branches if we have to).
    185 const uint32_t kMinorSchemaVersion = 0;
    186 
    187 // The schema version we store in the SQLite database is a (signed) 32-bit
    188 // integer. The major version is left-shifted 4 bits so the max value is
    189 // 0xFFFFFFF. The minor version occupies the lower 4 bits and its max is 0xF.
    190 static_assert(kMajorSchemaVersion <= 0xFFFFFFF,
    191              "Major version needs to fit in 28 bits.");
    192 static_assert(kMinorSchemaVersion <= 0xF,
    193              "Minor version needs to fit in 4 bits.");
    194 
    195 const int32_t kSQLiteSchemaVersion =
    196    int32_t((kMajorSchemaVersion << 4) + kMinorSchemaVersion);
    197 
    198 // Changing the value here will override the page size of new databases only.
    199 // A journal mode change and VACUUM are needed to change existing databases, so
    200 // the best way to do that is to use the schema version upgrade mechanism.
    201 const uint32_t kSQLitePageSizeOverride =
    202 #ifdef LS_MOBILE
    203    512;
    204 #else
    205    1024;
    206 #endif
    207 
    208 static_assert(kSQLitePageSizeOverride == /* mozStorage default */ 0 ||
    209                  (kSQLitePageSizeOverride % 2 == 0 &&
    210                   kSQLitePageSizeOverride >= 512 &&
    211                   kSQLitePageSizeOverride <= 65536),
    212              "Must be 0 (disabled) or a power of 2 between 512 and 65536!");
    213 
    214 // Set to some multiple of the page size to grow the database in larger chunks.
    215 const uint32_t kSQLiteGrowthIncrement = kSQLitePageSizeOverride * 2;
    216 
    217 static_assert(kSQLiteGrowthIncrement >= 0 &&
    218                  kSQLiteGrowthIncrement % kSQLitePageSizeOverride == 0 &&
    219                  kSQLiteGrowthIncrement < uint32_t(INT32_MAX),
    220              "Must be 0 (disabled) or a positive multiple of the page size!");
    221 
    222 /**
    223 * The database name for LocalStorage data in a per-origin directory.
    224 */
    225 constexpr auto kDataFileName = u"data.sqlite"_ns;
    226 
    227 /**
    228 * The journal corresponding to kDataFileName.  (We don't use WAL mode.)
    229 * Currently only needed in QuotaClient::InitOrigin and only in DEBUG builds.
    230 * See the corresponding comment in QuotaClient::InitOrigin.
    231 */
    232 #ifdef DEBUG
    233 constexpr auto kJournalFileName = u"data.sqlite-journal"_ns;
    234 #endif
    235 
    236 /**
    237 * This file contains the current usage of the LocalStorage database as defined
    238 * by the mozLength totals of all keys and values for the database, which
    239 * differs from the actual size on disk.  We store this value in a separate
    240 * file as a cache so that we can initialize the QuotaClient faster.
    241 * In the future, this file will be eliminated and the information will be
    242 * stored in PROFILE/storage.sqlite or similar QuotaManager-wide storage.
    243 *
    244 * The file contains a binary verification cookie (32-bits) followed by the
    245 * actual usage (64-bits).
    246 */
    247 constexpr auto kUsageFileName = u"usage"_ns;
    248 
    249 /**
    250 * Following a QuotaManager idiom, this journal file's existence is a marker
    251 * that the usage file was in the process of being updated and is currently
    252 * invalid.  This file is created prior to updating the usage file and only
    253 * deleted after the usage file has been written and closed and any pending
    254 * database transactions have been committed.  Note that this idiom is expected
    255 * to work if Gecko crashes in the middle of a write, but is not expected to be
    256 * foolproof in the face of a system crash, as we do not explicitly attempt to
    257 * fsync the directory containing the journal file.
    258 *
    259 * If the journal file is found to exist at origin initialization time, the
    260 * usage will be re-computed from the current state of DATA_FILE_NAME.
    261 */
    262 constexpr auto kUsageJournalFileName = u"usage-journal"_ns;
    263 
    264 static const uint32_t kUsageFileSize = 12;
    265 static const uint32_t kUsageFileCookie = 0x420a420a;
    266 
    267 /**
    268 * How long between the first moment we know we have data to be written on a
    269 * `Connection` and when we should actually perform the write.  This helps
    270 * limit disk churn under silly usage patterns and is historically consistent
    271 * with the previous, legacy implementation.
    272 *
    273 * Note that flushing happens downstream of Snapshot checkpointing and its
    274 * batch mechanism which helps avoid wasteful IPC in the case of silly content
    275 * code.
    276 */
    277 const uint32_t kFlushTimeoutMs = 5000;
    278 
    279 const bool kDefaultShadowWrites = false;
    280 const uint32_t kDefaultSnapshotPrefill = 16384;
    281 const uint32_t kDefaultSnapshotGradualPrefill = 4096;
    282 const bool kDefaultClientValidation = true;
    283 /**
    284 * Should all mutations also be reflected in the "shadow" database, which is
    285 * the legacy webappsstore.sqlite database.  When this is enabled, users can
    286 * downgrade their version of Firefox and/or otherwise fall back to the legacy
    287 * implementation without loss of data.  (Older versions of Firefox will
    288 * recognize the presence of ls-archive.sqlite and purge it and the other
    289 * LocalStorage directories so privacy is maintained.)
    290 */
    291 const char kShadowWritesPref[] = "dom.storage.shadow_writes";
    292 /**
    293 * Byte budget for sending data down to the LSSnapshot instance when it is first
    294 * created.  If there is less data than this (measured by tallying the string
    295 * length of the keys and values), all data is sent, otherwise partial data is
    296 * sent.  See `Snapshot`.
    297 */
    298 const char kSnapshotPrefillPref[] = "dom.storage.snapshot_prefill";
    299 /**
    300 * When a specific value is requested by an LSSnapshot that is not already fully
    301 * populated, gradual prefill is used. This preference specifies the number of
    302 * bytes to be used to send values beyond the specific value that is requested.
    303 * (The size of the explicitly requested value does not impact this preference.)
    304 * Setting the value to 0 disables gradual prefill. Tests may set this value to
    305 * -1 which is converted to INT_MAX in order to cause gradual prefill to send
    306 * all values not previously sent.
    307 */
    308 const char kSnapshotGradualPrefillPref[] =
    309    "dom.storage.snapshot_gradual_prefill";
    310 
    311 const char kClientValidationPref[] = "dom.storage.client_validation";
    312 
    313 /**
    314 * The amount of time a PreparedDatastore instance should stick around after a
    315 * preload is triggered in order to give time for the page to use LocalStorage
    316 * without triggering worst-case synchronous jank.
    317 */
    318 const uint32_t kPreparedDatastoreTimeoutMs = 20000;
    319 
    320 /**
    321 * Cold storage for LocalStorage data extracted from webappsstore.sqlite at
    322 * LSNG first-run that has not yet been migrated to its own per-origin directory
    323 * by use.
    324 *
    325 * In other words, at first run, LSNG copies the contents of webappsstore.sqlite
    326 * into this database.  As requests are made for that LocalStorage data, the
    327 * contents are removed from this database and placed into per-origin QM
    328 * storage.  So the contents of this database are always old, unused
    329 * LocalStorage data that we can potentially get rid of at some point in the
    330 * future.
    331 */
    332 #define LS_ARCHIVE_FILE_NAME u"ls-archive.sqlite"
    333 /**
    334 * The legacy LocalStorage database.  Its contents are maintained as our
    335 * "shadow" database so that LSNG can be disabled without loss of user data.
    336 */
    337 #define WEB_APPS_STORE_FILE_NAME u"webappsstore.sqlite"
    338 
    339 // Shadow database Write Ahead Log's maximum size is 512KB
    340 const uint32_t kShadowMaxWALSize = 512 * 1024;
    341 
    342 bool IsOnGlobalConnectionThread();
    343 
    344 void AssertIsOnGlobalConnectionThread();
    345 
    346 /*******************************************************************************
    347 * SQLite functions
    348 ******************************************************************************/
    349 
    350 int32_t MakeSchemaVersion(uint32_t aMajorSchemaVersion,
    351                          uint32_t aMinorSchemaVersion) {
    352  return int32_t((aMajorSchemaVersion << 4) + aMinorSchemaVersion);
    353 }
    354 
    355 nsCString GetArchivedOriginHashKey(const nsACString& aOriginSuffix,
    356                                   const nsACString& aOriginNoSuffix) {
    357  return aOriginSuffix + ":"_ns + aOriginNoSuffix;
    358 }
    359 
    360 nsresult CreateDataTable(mozIStorageConnection* aConnection) {
    361  return aConnection->ExecuteSimpleSQL(
    362      "CREATE TABLE data"
    363      "( key TEXT PRIMARY KEY"
    364      ", utf16_length INTEGER NOT NULL"
    365      ", conversion_type INTEGER NOT NULL"
    366      ", compression_type INTEGER NOT NULL"
    367      ", last_access_time INTEGER NOT NULL DEFAULT 0"
    368      ", value BLOB NOT NULL"
    369      ");"_ns);
    370 }
    371 
    372 nsresult CreateTables(mozIStorageConnection* aConnection) {
    373  MOZ_ASSERT(IsOnIOThread() || IsOnGlobalConnectionThread());
    374  MOZ_ASSERT(aConnection);
    375 
    376  // Table `database`
    377  QM_TRY(MOZ_TO_RESULT(aConnection->ExecuteSimpleSQL(
    378      "CREATE TABLE database"
    379      "( origin TEXT NOT NULL"
    380      ", usage INTEGER NOT NULL DEFAULT 0"
    381      ", last_vacuum_time INTEGER NOT NULL DEFAULT 0"
    382      ", last_analyze_time INTEGER NOT NULL DEFAULT 0"
    383      ", last_vacuum_size INTEGER NOT NULL DEFAULT 0"
    384      ");"_ns)));
    385 
    386  // Table `data`
    387  QM_TRY(MOZ_TO_RESULT(CreateDataTable(aConnection)));
    388 
    389  QM_TRY(MOZ_TO_RESULT(aConnection->SetSchemaVersion(kSQLiteSchemaVersion)));
    390 
    391  return NS_OK;
    392 }
    393 
    394 nsresult UpgradeSchemaFrom1_0To2_0(mozIStorageConnection* aConnection) {
    395  AssertIsOnIOThread();
    396  MOZ_ASSERT(aConnection);
    397 
    398  QM_TRY(MOZ_TO_RESULT(aConnection->ExecuteSimpleSQL(
    399      "ALTER TABLE database ADD COLUMN usage INTEGER NOT NULL DEFAULT 0;"_ns)));
    400 
    401  QM_TRY(MOZ_TO_RESULT(aConnection->ExecuteSimpleSQL(
    402      "UPDATE database "
    403      "SET usage = (SELECT total(utf16Length(key) + utf16Length(value)) "
    404      "FROM data);"_ns)));
    405 
    406  QM_TRY(MOZ_TO_RESULT(aConnection->SetSchemaVersion(MakeSchemaVersion(2, 0))));
    407 
    408  return NS_OK;
    409 }
    410 
    411 nsresult UpgradeSchemaFrom2_0To3_0(mozIStorageConnection* aConnection) {
    412  AssertIsOnIOThread();
    413  MOZ_ASSERT(aConnection);
    414 
    415  QM_TRY(MOZ_TO_RESULT(aConnection->ExecuteSimpleSQL(
    416      "ALTER TABLE data ADD COLUMN utf16Length INTEGER NOT NULL DEFAULT 0;"_ns)));
    417 
    418  QM_TRY(MOZ_TO_RESULT(aConnection->ExecuteSimpleSQL(
    419      "UPDATE data SET utf16Length = utf16Length(value);"_ns)));
    420 
    421  QM_TRY(MOZ_TO_RESULT(aConnection->SetSchemaVersion(MakeSchemaVersion(3, 0))));
    422 
    423  return NS_OK;
    424 }
    425 
    426 nsresult UpgradeSchemaFrom3_0To4_0(mozIStorageConnection* aConnection) {
    427  AssertIsOnIOThread();
    428  MOZ_ASSERT(aConnection);
    429 
    430  QM_TRY(MOZ_TO_RESULT(aConnection->SetSchemaVersion(MakeSchemaVersion(4, 0))));
    431 
    432  return NS_OK;
    433 }
    434 
    435 nsresult UpgradeSchemaFrom4_0To5_0(mozIStorageConnection* aConnection) {
    436  AssertIsOnIOThread();
    437  MOZ_ASSERT(aConnection);
    438 
    439  // Recreate data table in new format following steps at
    440  // https://www.sqlite.org/lang_altertable.html
    441  // section "Making Other Kinds Of Table Schema Changes"
    442  QM_TRY(MOZ_TO_RESULT(aConnection->ExecuteSimpleSQL(
    443      "CREATE TABLE migrated_data"
    444      "( key TEXT PRIMARY KEY"
    445      ", utf16_length INTEGER NOT NULL"
    446      ", conversion_type INTEGER NOT NULL"
    447      ", compression_type INTEGER NOT NULL"
    448      ", last_access_time INTEGER NOT NULL DEFAULT 0"
    449      ", value BLOB NOT NULL"
    450      ");"_ns)));
    451 
    452  // Reinsert old data, all legacy data is UTF8
    453  static_assert(1u ==
    454                static_cast<uint8_t>(LSValue::ConversionType::UTF16_UTF8));
    455  QM_TRY(MOZ_TO_RESULT(aConnection->ExecuteSimpleSQL(
    456      "INSERT INTO migrated_data (key, utf16_length, conversion_type, "
    457      "compression_type, last_access_time, value) "
    458      "SELECT key, utf16Length, 1, compressed, lastAccessTime, value "
    459      "FROM data;"_ns)));
    460 
    461  QM_TRY(MOZ_TO_RESULT(aConnection->ExecuteSimpleSQL("DROP TABLE data;"_ns)));
    462 
    463  // Rename to data
    464  QM_TRY(MOZ_TO_RESULT(aConnection->ExecuteSimpleSQL(
    465      "ALTER TABLE migrated_data RENAME TO data;"_ns)));
    466 
    467  QM_TRY(MOZ_TO_RESULT(aConnection->SetSchemaVersion(MakeSchemaVersion(5, 0))));
    468 
    469  return NS_OK;
    470 }
    471 
    472 nsresult SetDefaultPragmas(mozIStorageConnection* aConnection) {
    473  MOZ_ASSERT(!NS_IsMainThread());
    474  MOZ_ASSERT(aConnection);
    475 
    476  QM_TRY(MOZ_TO_RESULT(
    477      aConnection->ExecuteSimpleSQL("PRAGMA synchronous = FULL;"_ns)));
    478 
    479 #ifndef LS_MOBILE
    480  if (kSQLiteGrowthIncrement) {
    481    // This is just an optimization so ignore the failure if the disk is
    482    // currently too full.
    483    QM_TRY(QM_OR_ELSE_WARN_IF(
    484        // Expression.
    485        MOZ_TO_RESULT(
    486            aConnection->SetGrowthIncrement(kSQLiteGrowthIncrement, ""_ns)),
    487        // Predicate.
    488        IsSpecificError<NS_ERROR_FILE_TOO_BIG>,
    489        // Fallback.
    490        ErrToDefaultOk<>));
    491  }
    492 #endif  // LS_MOBILE
    493 
    494  return NS_OK;
    495 }
    496 
    497 Result<nsCOMPtr<mozIStorageConnection>, nsresult> CreateStorageConnection(
    498    nsIFile& aDBFile, nsIFile& aUsageFile, const nsACString& aOrigin) {
    499  MOZ_ASSERT(IsOnIOThread() || IsOnGlobalConnectionThread());
    500 
    501  // XXX Common logic should be refactored out of this method and
    502  // cache::DBAction::OpenDBConnection, and maybe other similar functions.
    503 
    504  QM_TRY_INSPECT(const auto& storageService,
    505                 MOZ_TO_RESULT_GET_TYPED(nsCOMPtr<mozIStorageService>,
    506                                         MOZ_SELECT_OVERLOAD(do_GetService),
    507                                         MOZ_STORAGE_SERVICE_CONTRACTID));
    508 
    509  QM_TRY_UNWRAP(auto connection, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
    510                                     nsCOMPtr<mozIStorageConnection>,
    511                                     storageService, OpenDatabase, &aDBFile,
    512                                     mozIStorageService::CONNECTION_DEFAULT));
    513 
    514  QM_TRY(MOZ_TO_RESULT(SetDefaultPragmas(connection)));
    515 
    516  // Check to make sure that the database schema is correct.
    517  // XXX Try to make schemaVersion const.
    518  QM_TRY_UNWRAP(int32_t schemaVersion,
    519                MOZ_TO_RESULT_INVOKE_MEMBER(connection, GetSchemaVersion));
    520 
    521  QM_TRY(OkIf(schemaVersion <= kSQLiteSchemaVersion), Err(NS_ERROR_FAILURE));
    522 
    523  if (schemaVersion != kSQLiteSchemaVersion) {
    524    const bool newDatabase = !schemaVersion;
    525 
    526    if (newDatabase) {
    527      // Set the page size first.
    528      if (kSQLitePageSizeOverride) {
    529        QM_TRY(MOZ_TO_RESULT(connection->ExecuteSimpleSQL(nsPrintfCString(
    530            "PRAGMA page_size = %" PRIu32 ";", kSQLitePageSizeOverride))));
    531      }
    532 
    533      // We have to set the auto_vacuum mode before opening a transaction.
    534      QM_TRY(MOZ_TO_RESULT(connection->ExecuteSimpleSQL(
    535 #ifdef LS_MOBILE
    536          // Turn on full auto_vacuum mode to reclaim disk space on mobile
    537          // devices (at the cost of some COMMIT speed).
    538          "PRAGMA auto_vacuum = FULL;"_ns
    539 #else
    540          // Turn on incremental auto_vacuum mode on desktop builds.
    541          "PRAGMA auto_vacuum = INCREMENTAL;"_ns
    542 #endif
    543          )));
    544    }
    545 
    546    bool vacuumNeeded = false;
    547 
    548    if (newDatabase) {
    549      mozStorageTransaction transaction(
    550          connection,
    551          /* aCommitOnComplete */ false,
    552          mozIStorageConnection::TRANSACTION_IMMEDIATE);
    553 
    554      QM_TRY(MOZ_TO_RESULT(transaction.Start()));
    555 
    556      QM_TRY(MOZ_TO_RESULT(CreateTables(connection)));
    557 
    558 #ifdef DEBUG
    559      {
    560        QM_TRY_INSPECT(
    561            const int32_t& schemaVersion,
    562            MOZ_TO_RESULT_INVOKE_MEMBER(connection, GetSchemaVersion),
    563            QM_ASSERT_UNREACHABLE);
    564 
    565        MOZ_ASSERT(schemaVersion == kSQLiteSchemaVersion);
    566      }
    567 #endif
    568 
    569      QM_TRY_INSPECT(
    570          const auto& stmt,
    571          MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
    572              nsCOMPtr<mozIStorageStatement>, connection, CreateStatement,
    573              "INSERT INTO database (origin) VALUES (:origin)"_ns));
    574 
    575      QM_TRY(MOZ_TO_RESULT(stmt->BindUTF8StringByName("origin"_ns, aOrigin)));
    576 
    577      QM_TRY(MOZ_TO_RESULT(stmt->Execute()));
    578 
    579      QM_TRY(MOZ_TO_RESULT(transaction.Commit()));
    580    } else {
    581      // This logic needs to change next time we change the schema!
    582      static_assert(kSQLiteSchemaVersion == int32_t((5 << 4) + 0),
    583                    "Upgrade function needed due to schema version increase.");
    584 
    585      while (schemaVersion != kSQLiteSchemaVersion) {
    586        mozStorageTransaction transaction(
    587            connection,
    588            /* aCommitOnComplete */ false,
    589            mozIStorageConnection::TRANSACTION_IMMEDIATE);
    590 
    591        QM_TRY(MOZ_TO_RESULT(transaction.Start()));
    592 
    593        if (schemaVersion == MakeSchemaVersion(1, 0)) {
    594          QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom1_0To2_0(connection)));
    595        } else if (schemaVersion == MakeSchemaVersion(2, 0)) {
    596          QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom2_0To3_0(connection)));
    597        } else if (schemaVersion == MakeSchemaVersion(3, 0)) {
    598          QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom3_0To4_0(connection)));
    599        } else if (schemaVersion == MakeSchemaVersion(4, 0)) {
    600          QM_TRY(MOZ_TO_RESULT(UpgradeSchemaFrom4_0To5_0(connection)));
    601          vacuumNeeded = true;
    602        } else {
    603          LS_WARNING(
    604              "Unable to open LocalStorage database, no upgrade path is "
    605              "available!");
    606          return Err(NS_ERROR_FAILURE);
    607        }
    608 
    609        QM_TRY(MOZ_TO_RESULT(transaction.Commit()));
    610 
    611        QM_TRY_UNWRAP(schemaVersion, MOZ_TO_RESULT_INVOKE_MEMBER(
    612                                         connection, GetSchemaVersion));
    613      }
    614 
    615      MOZ_ASSERT(schemaVersion == kSQLiteSchemaVersion);
    616    }
    617 
    618    if (vacuumNeeded) {
    619      QM_TRY(MOZ_TO_RESULT(connection->ExecuteSimpleSQL("VACUUM;"_ns)));
    620    }
    621 
    622    if (newDatabase) {
    623      // Windows caches the file size, let's force it to stat the file again.
    624      QM_TRY_INSPECT(const bool& exists,
    625                     MOZ_TO_RESULT_INVOKE_MEMBER(aDBFile, Exists));
    626      (void)exists;
    627 
    628      QM_TRY_INSPECT(const int64_t& fileSize,
    629                     MOZ_TO_RESULT_INVOKE_MEMBER(aDBFile, GetFileSize));
    630 
    631      MOZ_ASSERT(fileSize > 0);
    632 
    633      const PRTime vacuumTime = PR_Now();
    634      MOZ_ASSERT(vacuumTime);
    635 
    636      QM_TRY_INSPECT(
    637          const auto& vacuumTimeStmt,
    638          MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsCOMPtr<mozIStorageStatement>,
    639                                            connection, CreateStatement,
    640                                            "UPDATE database "
    641                                            "SET last_vacuum_time = :time"
    642                                            ", last_vacuum_size = :size;"_ns));
    643 
    644      QM_TRY(MOZ_TO_RESULT(
    645          vacuumTimeStmt->BindInt64ByName("time"_ns, vacuumTime)));
    646 
    647      QM_TRY(
    648          MOZ_TO_RESULT(vacuumTimeStmt->BindInt64ByName("size"_ns, fileSize)));
    649 
    650      QM_TRY(MOZ_TO_RESULT(vacuumTimeStmt->Execute()));
    651    }
    652  }
    653 
    654  return connection;
    655 }
    656 
    657 template <typename CorruptedFileHandler>
    658 Result<nsCOMPtr<mozIStorageConnection>, nsresult>
    659 CreateStorageConnectionWithRecovery(
    660    nsIFile& aDBFile, nsIFile& aUsageFile, const nsACString& aOrigin,
    661    CorruptedFileHandler&& aCorruptedFileHandler) {
    662  QM_TRY_RETURN(QM_OR_ELSE_WARN_IF(
    663      // Expression.
    664      CreateStorageConnection(aDBFile, aUsageFile, aOrigin),
    665      // Predicate.
    666      IsDatabaseCorruptionError,
    667      // Fallback.
    668      ([&aDBFile, &aUsageFile, &aOrigin,
    669        &aCorruptedFileHandler](const nsresult rv)
    670           -> Result<nsCOMPtr<mozIStorageConnection>, nsresult> {
    671        // Remove the usage file first (it might not exist at all due
    672        // to corrupted state, which is ignored here).
    673 
    674        // Usually we only use QM_OR_ELSE_LOG_VERBOSE(_IF) with Remove and
    675        // NS_ERROR_FILE_NOT_FOUND check, but we're already in the rare case
    676        // of corruption here, so the use of QM_OR_ELSE_WARN_IF is ok here.
    677        QM_TRY(QM_OR_ELSE_WARN_IF(
    678            // Expression.
    679            MOZ_TO_RESULT(aUsageFile.Remove(false)),
    680            // Predicate.
    681            ([](const nsresult rv) { return rv == NS_ERROR_FILE_NOT_FOUND; }),
    682            // Fallback.
    683            ErrToDefaultOk<>));
    684 
    685        // Call the corrupted file handler before trying to remove the
    686        // database file, which might fail.
    687        aCorruptedFileHandler();
    688 
    689        // Nuke the database file.
    690        QM_TRY(MOZ_TO_RESULT(aDBFile.Remove(false)));
    691 
    692        QM_TRY_RETURN(CreateStorageConnection(aDBFile, aUsageFile, aOrigin));
    693      })));
    694 }
    695 
    696 Result<nsCOMPtr<mozIStorageConnection>, nsresult> GetStorageConnection(
    697    const nsAString& aDatabaseFilePath) {
    698  AssertIsOnGlobalConnectionThread();
    699  MOZ_ASSERT(!aDatabaseFilePath.IsEmpty());
    700  MOZ_ASSERT(StringEndsWith(aDatabaseFilePath, u".sqlite"_ns));
    701 
    702  QM_TRY_INSPECT(const auto& databaseFile, QM_NewLocalFile(aDatabaseFilePath));
    703 
    704  QM_TRY_INSPECT(const bool& exists,
    705                 MOZ_TO_RESULT_INVOKE_MEMBER(databaseFile, Exists));
    706 
    707  QM_TRY(OkIf(exists), Err(NS_ERROR_FAILURE));
    708 
    709  QM_TRY_INSPECT(const auto& ss,
    710                 MOZ_TO_RESULT_GET_TYPED(nsCOMPtr<mozIStorageService>,
    711                                         MOZ_SELECT_OVERLOAD(do_GetService),
    712                                         MOZ_STORAGE_SERVICE_CONTRACTID));
    713 
    714  QM_TRY_UNWRAP(auto connection,
    715                MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
    716                    nsCOMPtr<mozIStorageConnection>, ss, OpenDatabase,
    717                    databaseFile, mozIStorageService::CONNECTION_DEFAULT));
    718 
    719  QM_TRY(MOZ_TO_RESULT(SetDefaultPragmas(connection)));
    720 
    721  return connection;
    722 }
    723 
    724 Result<nsCOMPtr<nsIFile>, nsresult> GetArchiveFile(
    725    const nsAString& aStoragePath) {
    726  AssertIsOnIOThread();
    727  MOZ_ASSERT(!aStoragePath.IsEmpty());
    728 
    729  QM_TRY_UNWRAP(auto archiveFile, QM_NewLocalFile(aStoragePath));
    730 
    731  QM_TRY(MOZ_TO_RESULT(
    732      archiveFile->Append(nsLiteralString(LS_ARCHIVE_FILE_NAME))));
    733 
    734  return archiveFile;
    735 }
    736 
    737 Result<nsCOMPtr<mozIStorageConnection>, nsresult>
    738 CreateArchiveStorageConnection(const nsAString& aStoragePath) {
    739  GECKO_TRACE_SCOPE("dom::localstorage", "CreateArchiveStorageConnection");
    740 
    741  AssertIsOnIOThread();
    742  MOZ_ASSERT(!aStoragePath.IsEmpty());
    743 
    744  QM_TRY_INSPECT(const auto& archiveFile, GetArchiveFile(aStoragePath));
    745 
    746  // QuotaManager ensures this file always exists.
    747  DebugOnly<bool> exists;
    748  MOZ_ASSERT(NS_SUCCEEDED(archiveFile->Exists(&exists)));
    749  MOZ_ASSERT(exists);
    750 
    751  QM_TRY_INSPECT(const bool& isDirectory,
    752                 MOZ_TO_RESULT_INVOKE_MEMBER(archiveFile, IsDirectory));
    753 
    754  if (isDirectory) {
    755    LS_WARNING("ls-archive is not a file!");
    756    return nsCOMPtr<mozIStorageConnection>{};
    757  }
    758 
    759  QM_TRY_INSPECT(const auto& ss,
    760                 MOZ_TO_RESULT_GET_TYPED(nsCOMPtr<mozIStorageService>,
    761                                         MOZ_SELECT_OVERLOAD(do_GetService),
    762                                         MOZ_STORAGE_SERVICE_CONTRACTID));
    763 
    764  QM_TRY_UNWRAP(
    765      auto connection,
    766      QM_OR_ELSE_WARN_IF(
    767          // Expression.
    768          MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
    769              nsCOMPtr<mozIStorageConnection>, ss, OpenUnsharedDatabase,
    770              archiveFile, mozIStorageService::CONNECTION_DEFAULT),
    771          // Predicate.
    772          IsDatabaseCorruptionError,
    773          // Fallback. Don't throw an error, leave a corrupted ls-archive
    774          // database as it is.
    775          ErrToDefaultOk<nsCOMPtr<mozIStorageConnection>>));
    776 
    777  if (connection) {
    778    const nsresult rv = StorageDBUpdater::Update(connection);
    779    if (NS_FAILED(rv)) {
    780      // Don't throw an error, leave a non-updateable ls-archive database as
    781      // it is.
    782      return nsCOMPtr<mozIStorageConnection>{};
    783    }
    784  }
    785 
    786  return connection;
    787 }
    788 
    789 Result<nsCOMPtr<nsIFile>, nsresult> GetShadowFile(const nsAString& aBasePath) {
    790  MOZ_ASSERT(IsOnIOThread() || IsOnGlobalConnectionThread());
    791  MOZ_ASSERT(!aBasePath.IsEmpty());
    792 
    793  QM_TRY_UNWRAP(auto archiveFile, QM_NewLocalFile(aBasePath));
    794 
    795  QM_TRY(MOZ_TO_RESULT(
    796      archiveFile->Append(nsLiteralString(WEB_APPS_STORE_FILE_NAME))));
    797 
    798  return archiveFile;
    799 }
    800 
    801 nsresult SetShadowJournalMode(mozIStorageConnection* aConnection) {
    802  MOZ_ASSERT(IsOnIOThread() || IsOnGlobalConnectionThread());
    803  MOZ_ASSERT(aConnection);
    804 
    805  // Try enabling WAL mode. This can fail in various circumstances so we have to
    806  // check the results here.
    807  constexpr auto journalModeQueryStart = "PRAGMA journal_mode = "_ns;
    808  constexpr auto journalModeWAL = "wal"_ns;
    809 
    810  QM_TRY_INSPECT(const auto& stmt,
    811                 CreateAndExecuteSingleStepStatement(
    812                     *aConnection, journalModeQueryStart + journalModeWAL));
    813 
    814  QM_TRY_INSPECT(const auto& journalMode,
    815                 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsAutoCString, *stmt,
    816                                                   GetUTF8String, 0));
    817 
    818  if (journalMode.Equals(journalModeWAL)) {
    819    // WAL mode successfully enabled. Set limits on its size here.
    820 
    821    // Set the threshold for auto-checkpointing the WAL. We don't want giant
    822    // logs slowing down us.
    823    QM_TRY_INSPECT(const auto& stmt, CreateAndExecuteSingleStepStatement(
    824                                         *aConnection, "PRAGMA page_size;"_ns));
    825 
    826    QM_TRY_INSPECT(const int32_t& pageSize,
    827                   MOZ_TO_RESULT_INVOKE_MEMBER(*stmt, GetInt32, 0));
    828 
    829    MOZ_ASSERT(pageSize >= 512 && pageSize <= 65536);
    830 
    831    // Note there is a default journal_size_limit set by mozStorage.
    832    QM_TRY(MOZ_TO_RESULT(aConnection->ExecuteSimpleSQL(
    833        "PRAGMA wal_autocheckpoint = "_ns +
    834        IntToCString(static_cast<int32_t>(kShadowMaxWALSize / pageSize)))));
    835  } else {
    836    QM_TRY(MOZ_TO_RESULT(
    837        aConnection->ExecuteSimpleSQL(journalModeQueryStart + "truncate"_ns)));
    838  }
    839 
    840  return NS_OK;
    841 }
    842 
    843 Result<nsCOMPtr<mozIStorageConnection>, nsresult> CreateShadowStorageConnection(
    844    const nsAString& aBasePath) {
    845  MOZ_ASSERT(IsOnIOThread() || IsOnGlobalConnectionThread());
    846  MOZ_ASSERT(!aBasePath.IsEmpty());
    847 
    848  QM_TRY_INSPECT(const auto& shadowFile, GetShadowFile(aBasePath));
    849 
    850  QM_TRY_INSPECT(const auto& ss,
    851                 MOZ_TO_RESULT_GET_TYPED(nsCOMPtr<mozIStorageService>,
    852                                         MOZ_SELECT_OVERLOAD(do_GetService),
    853                                         MOZ_STORAGE_SERVICE_CONTRACTID));
    854 
    855  QM_TRY_UNWRAP(
    856      auto connection,
    857      QM_OR_ELSE_WARN_IF(
    858          // Expression.
    859          MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
    860              nsCOMPtr<mozIStorageConnection>, ss, OpenUnsharedDatabase,
    861              shadowFile, mozIStorageService::CONNECTION_DEFAULT),
    862          // Predicate.
    863          IsDatabaseCorruptionError,
    864          // Fallback.
    865          ([&shadowFile, &ss](const nsresult rv)
    866               -> Result<nsCOMPtr<mozIStorageConnection>, nsresult> {
    867            QM_TRY(MOZ_TO_RESULT(shadowFile->Remove(false)));
    868 
    869            QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
    870                nsCOMPtr<mozIStorageConnection>, ss, OpenUnsharedDatabase,
    871                shadowFile, mozIStorageService::CONNECTION_DEFAULT));
    872          })));
    873 
    874  QM_TRY(MOZ_TO_RESULT(SetShadowJournalMode(connection)));
    875 
    876  // XXX Depending on whether the *first* call to OpenUnsharedDatabase above
    877  // failed, we (a) might or (b) might not be dealing with a fresh database
    878  // here. This is confusing, since in a failure of case (a) we would do the
    879  // same thing again. Probably, the control flow should be changed here so that
    880  // it's clear we only delete & create a fresh database once. If we still have
    881  // a failure then, we better give up. Or, if we really want to handle that,
    882  // the number of 2 retries seems arbitrary, and we should better do this in
    883  // some loop until a maximum number of retries is reached.
    884  //
    885  // Compare this with QuotaManager::CreateLocalStorageArchiveConnection, which
    886  // actually tracks if the file was removed before, but it's also more
    887  // complicated than it should be. Maybe these two methods can be merged (which
    888  // would mean that a parameter must be added that indicates whether it's
    889  // handling the shadow file or not).
    890  QM_TRY(QM_OR_ELSE_WARN(
    891      // Expression.
    892      MOZ_TO_RESULT(StorageDBUpdater::Update(connection)),
    893      // Fallback.
    894      ([&connection, &shadowFile, &ss](const nsresult) -> Result<Ok, nsresult> {
    895        QM_TRY(MOZ_TO_RESULT(connection->Close()));
    896        QM_TRY(MOZ_TO_RESULT(shadowFile->Remove(false)));
    897 
    898        QM_TRY_UNWRAP(connection, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
    899                                      nsCOMPtr<mozIStorageConnection>, ss,
    900                                      OpenUnsharedDatabase, shadowFile,
    901                                      mozIStorageService::CONNECTION_DEFAULT));
    902 
    903        QM_TRY(MOZ_TO_RESULT(SetShadowJournalMode(connection)));
    904 
    905        QM_TRY(
    906            MOZ_TO_RESULT(StorageDBUpdater::CreateCurrentSchema(connection)));
    907 
    908        return Ok{};
    909      })));
    910 
    911  return connection;
    912 }
    913 
    914 Result<nsCOMPtr<mozIStorageConnection>, nsresult> GetShadowStorageConnection(
    915    const nsAString& aBasePath) {
    916  AssertIsOnIOThread();
    917  MOZ_ASSERT(!aBasePath.IsEmpty());
    918 
    919  QM_TRY_INSPECT(const auto& shadowFile, GetShadowFile(aBasePath));
    920 
    921  QM_TRY_INSPECT(const bool& exists,
    922                 MOZ_TO_RESULT_INVOKE_MEMBER(shadowFile, Exists));
    923 
    924  QM_TRY(OkIf(exists), Err(NS_ERROR_FAILURE));
    925 
    926  QM_TRY_INSPECT(const auto& ss,
    927                 MOZ_TO_RESULT_GET_TYPED(nsCOMPtr<mozIStorageService>,
    928                                         MOZ_SELECT_OVERLOAD(do_GetService),
    929                                         MOZ_STORAGE_SERVICE_CONTRACTID));
    930 
    931  QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
    932      nsCOMPtr<mozIStorageConnection>, ss, OpenUnsharedDatabase, shadowFile,
    933      mozIStorageService::CONNECTION_DEFAULT));
    934 }
    935 
    936 nsresult AttachShadowDatabase(const nsAString& aBasePath,
    937                              mozIStorageConnection* aConnection) {
    938  AssertIsOnGlobalConnectionThread();
    939  MOZ_ASSERT(!aBasePath.IsEmpty());
    940  MOZ_ASSERT(aConnection);
    941 
    942  QM_TRY_INSPECT(const auto& shadowFile, GetShadowFile(aBasePath));
    943 
    944 #ifdef DEBUG
    945  {
    946    QM_TRY_INSPECT(const bool& exists,
    947                   MOZ_TO_RESULT_INVOKE_MEMBER(shadowFile, Exists));
    948 
    949    MOZ_ASSERT(exists);
    950  }
    951 #endif
    952 
    953  QM_TRY_INSPECT(const auto& path, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
    954                                       nsString, shadowFile, GetPath));
    955 
    956  QM_TRY_INSPECT(const auto& stmt,
    957                 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
    958                     nsCOMPtr<mozIStorageStatement>, aConnection,
    959                     CreateStatement, "ATTACH DATABASE :path AS shadow;"_ns));
    960 
    961  QM_TRY(MOZ_TO_RESULT(stmt->BindStringByName("path"_ns, path)));
    962 
    963  QM_TRY(MOZ_TO_RESULT(stmt->Execute()));
    964 
    965  return NS_OK;
    966 }
    967 
    968 nsresult DetachShadowDatabase(mozIStorageConnection* aConnection) {
    969  AssertIsOnGlobalConnectionThread();
    970  MOZ_ASSERT(aConnection);
    971 
    972  QM_TRY(MOZ_TO_RESULT(
    973      aConnection->ExecuteSimpleSQL("DETACH DATABASE shadow"_ns)));
    974 
    975  return NS_OK;
    976 }
    977 
    978 Result<nsCOMPtr<nsIFile>, nsresult> GetUsageFile(
    979    const nsAString& aDirectoryPath) {
    980  MOZ_ASSERT(IsOnIOThread() || IsOnGlobalConnectionThread());
    981  MOZ_ASSERT(!aDirectoryPath.IsEmpty());
    982 
    983  QM_TRY_UNWRAP(auto usageFile, QM_NewLocalFile(aDirectoryPath));
    984 
    985  QM_TRY(MOZ_TO_RESULT(usageFile->Append(kUsageFileName)));
    986 
    987  return usageFile;
    988 }
    989 
    990 Result<nsCOMPtr<nsIFile>, nsresult> GetUsageJournalFile(
    991    const nsAString& aDirectoryPath) {
    992  MOZ_ASSERT(IsOnIOThread() || IsOnGlobalConnectionThread());
    993  MOZ_ASSERT(!aDirectoryPath.IsEmpty());
    994 
    995  QM_TRY_UNWRAP(auto usageJournalFile, QM_NewLocalFile(aDirectoryPath));
    996 
    997  QM_TRY(MOZ_TO_RESULT(usageJournalFile->Append(kUsageJournalFileName)));
    998 
    999  return usageJournalFile;
   1000 }
   1001 
   1002 // Checks if aFile exists and is a file. Returns true if it exists and is a
   1003 // file, false if it doesn't exist, and an error if it exists but isn't a file.
   1004 Result<bool, nsresult> ExistsAsFile(nsIFile& aFile) {
   1005  enum class ExistsAsFileResult { DoesNotExist, IsDirectory, IsFile };
   1006 
   1007  // This is an optimization to check both properties in one OS case, rather
   1008  // than calling Exists first, and then IsDirectory. IsDirectory also checks
   1009  // if the path exists. QM_OR_ELSE_WARN_IF is not used here since we just want
   1010  // to log NS_ERROR_FILE_NOT_FOUND result and not spam the reports.
   1011  QM_TRY_INSPECT(
   1012      const auto& res,
   1013      QM_OR_ELSE_LOG_VERBOSE_IF(
   1014          // Expression.
   1015          MOZ_TO_RESULT_INVOKE_MEMBER(aFile, IsDirectory)
   1016              .map([](const bool isDirectory) {
   1017                return isDirectory ? ExistsAsFileResult::IsDirectory
   1018                                   : ExistsAsFileResult::IsFile;
   1019              }),
   1020          // Predicate.
   1021          ([](const nsresult rv) { return rv == NS_ERROR_FILE_NOT_FOUND; }),
   1022          // Fallback.
   1023          ErrToOk<ExistsAsFileResult::DoesNotExist>));
   1024 
   1025  QM_TRY(OkIf(res != ExistsAsFileResult::IsDirectory), Err(NS_ERROR_FAILURE));
   1026 
   1027  return res == ExistsAsFileResult::IsFile;
   1028 }
   1029 
   1030 nsresult UpdateUsageFile(nsIFile* aUsageFile, nsIFile* aUsageJournalFile,
   1031                         int64_t aUsage) {
   1032  MOZ_ASSERT(IsOnIOThread() || IsOnGlobalConnectionThread());
   1033  MOZ_ASSERT(aUsageFile);
   1034  MOZ_ASSERT(aUsageJournalFile);
   1035  MOZ_ASSERT(aUsage >= 0);
   1036 
   1037  QM_TRY_INSPECT(const bool& usageJournalFileExists,
   1038                 ExistsAsFile(*aUsageJournalFile));
   1039  if (!usageJournalFileExists) {
   1040    QM_TRY(MOZ_TO_RESULT(
   1041        aUsageJournalFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644)));
   1042  }
   1043 
   1044  QM_TRY_INSPECT(const auto& stream, NS_NewLocalFileOutputStream(aUsageFile));
   1045 
   1046  nsCOMPtr<nsIBinaryOutputStream> binaryStream =
   1047      NS_NewObjectOutputStream(stream);
   1048 
   1049  QM_TRY(MOZ_TO_RESULT(binaryStream->Write32(kUsageFileCookie)));
   1050 
   1051  QM_TRY(MOZ_TO_RESULT(binaryStream->Write64(aUsage)));
   1052 
   1053 #if defined(EARLY_BETA_OR_EARLIER) || defined(DEBUG)
   1054  QM_TRY(MOZ_TO_RESULT(stream->Flush()));
   1055 #endif
   1056 
   1057  QM_TRY(MOZ_TO_RESULT(stream->Close()));
   1058 
   1059  return NS_OK;
   1060 }
   1061 
   1062 Result<UsageInfo, nsresult> LoadUsageFile(nsIFile& aUsageFile) {
   1063  GECKO_TRACE_SCOPE("dom::localstorage", "LoadUsageFile");
   1064 
   1065  AssertIsOnIOThread();
   1066 
   1067  QM_TRY_INSPECT(const int64_t& fileSize,
   1068                 MOZ_TO_RESULT_INVOKE_MEMBER(aUsageFile, GetFileSize));
   1069 
   1070  QM_TRY(OkIf(fileSize == kUsageFileSize), Err(NS_ERROR_FILE_CORRUPTED));
   1071 
   1072  QM_TRY_UNWRAP(auto stream, NS_NewLocalFileInputStream(&aUsageFile));
   1073 
   1074  QM_TRY_INSPECT(const auto& bufferedStream,
   1075                 NS_NewBufferedInputStream(stream.forget(), 16));
   1076 
   1077  const nsCOMPtr<nsIBinaryInputStream> binaryStream =
   1078      NS_NewObjectInputStream(bufferedStream);
   1079 
   1080  QM_TRY_INSPECT(const uint32_t& cookie,
   1081                 MOZ_TO_RESULT_INVOKE_MEMBER(binaryStream, Read32));
   1082 
   1083  QM_TRY(OkIf(cookie == kUsageFileCookie), Err(NS_ERROR_FILE_CORRUPTED));
   1084 
   1085  QM_TRY_INSPECT(const uint64_t& usage,
   1086                 MOZ_TO_RESULT_INVOKE_MEMBER(binaryStream, Read64));
   1087 
   1088  return UsageInfo{DatabaseUsageType(Some(usage))};
   1089 }
   1090 
   1091 /*******************************************************************************
   1092 * Non-actor class declarations
   1093 ******************************************************************************/
   1094 
   1095 /**
   1096 * Coalescing manipulation queue used by `Datastore`.  Used by `Datastore` to
   1097 * update `Datastore::mOrderedItems` efficiently/for code simplification.
   1098 * (Datastore does not actually depend on the coalescing, as mutations are
   1099 * applied atomically when a Snapshot Checkpoints, and with `Datastore::mValues`
   1100 * being updated at the same time the mutations are applied to Datastore's
   1101 * mWriteOptimizer.)
   1102 */
   1103 class DatastoreWriteOptimizer final : public LSWriteOptimizer<LSValue> {
   1104 public:
   1105  void ApplyAndReset(nsTArray<LSItemInfo>& aOrderedItems);
   1106 };
   1107 
   1108 /**
   1109 * Coalescing manipulation queue used by `Connection`.  Used by `Connection` to
   1110 * buffer and coalesce manipulations applied to the Datastore in batches by
   1111 * Snapshot Checkpointing until flushed to disk.
   1112 */
   1113 class ConnectionWriteOptimizer final : public LSWriteOptimizer<LSValue> {
   1114 public:
   1115  // Returns the usage as the success value.
   1116  Result<int64_t, nsresult> Perform(Connection* aConnection,
   1117                                    bool aShadowWrites);
   1118 
   1119 private:
   1120  /**
   1121   * Handlers for specific mutations.  Each method knows how to `Perform` the
   1122   * manipulation against a `Connection` and the "shadow" database (legacy
   1123   * webappsstore.sqlite database that exists so LSNG can be disabled/safely
   1124   * downgraded from.)
   1125   */
   1126  nsresult PerformInsertOrUpdate(Connection* aConnection, bool aShadowWrites,
   1127                                 const nsAString& aKey, const LSValue& aValue);
   1128 
   1129  nsresult PerformDelete(Connection* aConnection, bool aShadowWrites,
   1130                         const nsAString& aKey);
   1131 
   1132  nsresult PerformTruncate(Connection* aConnection, bool aShadowWrites);
   1133 };
   1134 
   1135 class DatastoreOperationBase : public Runnable {
   1136  nsCOMPtr<nsIEventTarget> mOwningEventTarget;
   1137  nsresult mResultCode;
   1138  Atomic<bool> mMayProceedOnNonOwningThread;
   1139  bool mMayProceed;
   1140 
   1141 public:
   1142  nsIEventTarget* OwningEventTarget() const {
   1143    MOZ_ASSERT(mOwningEventTarget);
   1144 
   1145    return mOwningEventTarget;
   1146  }
   1147 
   1148  bool IsOnOwningThread() const {
   1149    MOZ_ASSERT(mOwningEventTarget);
   1150 
   1151    bool current;
   1152    return NS_SUCCEEDED(mOwningEventTarget->IsOnCurrentThread(&current)) &&
   1153           current;
   1154  }
   1155 
   1156  void AssertIsOnOwningThread() const {
   1157    MOZ_ASSERT(IsOnBackgroundThread());
   1158    MOZ_ASSERT(IsOnOwningThread());
   1159  }
   1160 
   1161  nsresult ResultCode() const { return mResultCode; }
   1162 
   1163  void SetFailureCode(nsresult aErrorCode) {
   1164    MOZ_ASSERT(NS_SUCCEEDED(mResultCode));
   1165    MOZ_ASSERT(NS_FAILED(aErrorCode));
   1166 
   1167    mResultCode = aErrorCode;
   1168  }
   1169 
   1170  void MaybeSetFailureCode(nsresult aErrorCode) {
   1171    MOZ_ASSERT(NS_FAILED(aErrorCode));
   1172 
   1173    if (NS_SUCCEEDED(mResultCode)) {
   1174      mResultCode = aErrorCode;
   1175    }
   1176  }
   1177 
   1178  void NoteComplete() {
   1179    AssertIsOnOwningThread();
   1180 
   1181    mMayProceed = false;
   1182    mMayProceedOnNonOwningThread = false;
   1183  }
   1184 
   1185  bool MayProceed() const {
   1186    AssertIsOnOwningThread();
   1187 
   1188    return mMayProceed;
   1189  }
   1190 
   1191  // May be called on any thread, but you should call MayProceed() if you know
   1192  // you're on the background thread because it is slightly faster.
   1193  bool MayProceedOnNonOwningThread() const {
   1194    return mMayProceedOnNonOwningThread;
   1195  }
   1196 
   1197 protected:
   1198  DatastoreOperationBase()
   1199      : Runnable("dom::DatastoreOperationBase"),
   1200        mOwningEventTarget(GetCurrentSerialEventTarget()),
   1201        mResultCode(NS_OK),
   1202        mMayProceedOnNonOwningThread(true),
   1203        mMayProceed(true) {}
   1204 
   1205  ~DatastoreOperationBase() override { MOZ_ASSERT(!mMayProceed); }
   1206 };
   1207 
   1208 class ConnectionDatastoreOperationBase : public DatastoreOperationBase {
   1209 protected:
   1210  RefPtr<Connection> mConnection;
   1211  /**
   1212   * This boolean flag is used by the CloseOp to avoid creating empty databases.
   1213   */
   1214  const bool mEnsureStorageConnection;
   1215 
   1216 public:
   1217  // This callback will be called on the background thread before releasing the
   1218  // final reference to this request object. Subclasses may perform any
   1219  // additional cleanup here but must always call the base class implementation.
   1220  virtual void Cleanup();
   1221 
   1222 protected:
   1223  ConnectionDatastoreOperationBase(Connection* aConnection,
   1224                                   bool aEnsureStorageConnection = true);
   1225 
   1226  ~ConnectionDatastoreOperationBase();
   1227 
   1228  // Must be overridden in subclasses. Called on the target thread to allow the
   1229  // subclass to perform necessary datastore operations. A successful return
   1230  // value will trigger an OnSuccess callback on the background thread while
   1231  // while a failure value will trigger an OnFailure callback.
   1232  virtual nsresult DoDatastoreWork() = 0;
   1233 
   1234  // Methods that subclasses may implement.
   1235  virtual void OnSuccess();
   1236 
   1237  virtual void OnFailure(nsresult aResultCode);
   1238 
   1239 private:
   1240  void RunOnConnectionThread();
   1241 
   1242  void RunOnOwningThread();
   1243 
   1244  // Not to be overridden by subclasses.
   1245  NS_DECL_NSIRUNNABLE
   1246 };
   1247 
   1248 class Connection final : public CachingDatabaseConnection {
   1249  friend class ConnectionThread;
   1250 
   1251  class GetOrCreateTemporaryOriginDirectoryHelper;
   1252 
   1253  class FlushOp;
   1254  class CloseOp;
   1255 
   1256  RefPtr<ConnectionThread> mConnectionThread;
   1257  RefPtr<QuotaClient> mQuotaClient;
   1258  nsCOMPtr<nsITimer> mFlushTimer;
   1259  UniquePtr<ArchivedOriginScope> mArchivedOriginScope;
   1260  ConnectionWriteOptimizer mWriteOptimizer;
   1261  // XXX Consider changing this to ClientMetadata.
   1262  const OriginMetadata mOriginMetadata;
   1263  nsString mDirectoryPath;
   1264  /**
   1265   * Propagated from PrepareDatastoreOp. PrepareDatastoreOp may defer the
   1266   * creation of the localstorage client directory and database on the
   1267   * QuotaManager IO thread in its DatabaseWork method to
   1268   * Connection::EnsureStorageConnection, in which case the method needs to know
   1269   * it is responsible for taking those actions (without redundantly performing
   1270   * the existence checks).
   1271   */
   1272  const bool mDatabaseWasNotAvailable;
   1273  bool mHasCreatedDatabase;
   1274  bool mFlushScheduled;
   1275 #ifdef DEBUG
   1276  bool mInUpdateBatch;
   1277  bool mFinished;
   1278 #endif
   1279 
   1280 public:
   1281  NS_INLINE_DECL_REFCOUNTING(mozilla::dom::Connection)
   1282 
   1283  void AssertIsOnOwningThread() const { NS_ASSERT_OWNINGTHREAD(Connection); }
   1284 
   1285  QuotaClient* GetQuotaClient() const {
   1286    MOZ_ASSERT(mQuotaClient);
   1287 
   1288    return mQuotaClient;
   1289  }
   1290 
   1291  ArchivedOriginScope* GetArchivedOriginScope() const {
   1292    return mArchivedOriginScope.get();
   1293  }
   1294 
   1295  const nsCString& Origin() const { return mOriginMetadata.mOrigin; }
   1296 
   1297  const nsString& DirectoryPath() const { return mDirectoryPath; }
   1298 
   1299  void GetFinishInfo(bool& aDatabaseWasNotAvailable,
   1300                     bool& aHasCreatedDatabase) const {
   1301    AssertIsOnOwningThread();
   1302    MOZ_ASSERT(mFinished);
   1303 
   1304    aDatabaseWasNotAvailable = mDatabaseWasNotAvailable;
   1305    aHasCreatedDatabase = mHasCreatedDatabase;
   1306  }
   1307 
   1308  //////////////////////////////////////////////////////////////////////////////
   1309  // Methods which can only be called on the owning thread.
   1310 
   1311  // This method is used to asynchronously execute a connection datastore
   1312  // operation on the connection thread.
   1313  void Dispatch(ConnectionDatastoreOperationBase* aOp);
   1314 
   1315  // This method is used to asynchronously close the storage connection on the
   1316  // connection thread.
   1317  void Close(nsIRunnable* aCallback);
   1318 
   1319  void SetItem(const nsString& aKey, const LSValue& aValue, int64_t aDelta,
   1320               bool aIsNewItem);
   1321 
   1322  void RemoveItem(const nsString& aKey, int64_t aDelta);
   1323 
   1324  void Clear(int64_t aDelta);
   1325 
   1326  void BeginUpdateBatch();
   1327 
   1328  void EndUpdateBatch();
   1329 
   1330  //////////////////////////////////////////////////////////////////////////////
   1331  // Methods which can only be called on the connection thread.
   1332 
   1333  nsresult EnsureStorageConnection();
   1334 
   1335  mozIStorageConnection* StorageConnection() const {
   1336    AssertIsOnGlobalConnectionThread();
   1337 
   1338    return &MutableStorageConnection();
   1339  }
   1340 
   1341  void CloseStorageConnection();
   1342 
   1343  nsresult BeginWriteTransaction();
   1344 
   1345  nsresult CommitWriteTransaction();
   1346 
   1347  nsresult RollbackWriteTransaction();
   1348 
   1349 private:
   1350  // Only created by ConnectionThread.
   1351  Connection(ConnectionThread* aConnectionThread,
   1352             const OriginMetadata& aOriginMetadata,
   1353             UniquePtr<ArchivedOriginScope>&& aArchivedOriginScope,
   1354             bool aDatabaseWasNotAvailable);
   1355 
   1356  ~Connection();
   1357 
   1358  void ScheduleFlush();
   1359 
   1360  void Flush();
   1361 
   1362  static void FlushTimerCallback(nsITimer* aTimer, void* aClosure);
   1363 };
   1364 
   1365 /**
   1366 * Helper to invoke GetOrCreateTemporaryOriginDirectory on the QuotaManager IO
   1367 * thread from the LocalStorage connection thread when creating a database
   1368 * connection on demand. This is necessary because we attempt to defer the
   1369 * creation of the origin directory and the database until absolutely needed,
   1370 * but the directory creation must happen on the QM IO thread for invariant
   1371 * reasons. (We can't just use a mutex because there could be logic on the IO
   1372 * thread that also wants to deal with the same origin, so we need to queue a
   1373 * runnable and wait our turn.)
   1374 */
   1375 class Connection::GetOrCreateTemporaryOriginDirectoryHelper final
   1376    : public Runnable {
   1377  mozilla::Monitor mMonitor MOZ_UNANNOTATED;
   1378  const OriginMetadata mOriginMetadata;
   1379  nsString mOriginDirectoryPath;
   1380  nsresult mIOThreadResultCode;
   1381  bool mWaiting;
   1382 
   1383 public:
   1384  explicit GetOrCreateTemporaryOriginDirectoryHelper(
   1385      const OriginMetadata& aOriginMetadata)
   1386      : Runnable(
   1387            "dom::localstorage::Connection::"
   1388            "GetOrCreateTemporaryOriginDirectoryHelper"),
   1389        mMonitor("GetOrCreateTemporaryOriginDirectoryHelper::mMonitor"),
   1390        mOriginMetadata(aOriginMetadata),
   1391        mIOThreadResultCode(NS_OK),
   1392        mWaiting(true) {
   1393    AssertIsOnGlobalConnectionThread();
   1394  }
   1395 
   1396  Result<nsString, nsresult> BlockAndReturnOriginDirectoryPath();
   1397 
   1398 private:
   1399  ~GetOrCreateTemporaryOriginDirectoryHelper() = default;
   1400 
   1401  nsresult RunOnIOThread();
   1402 
   1403  NS_DECL_NSIRUNNABLE
   1404 };
   1405 
   1406 class Connection::FlushOp final : public ConnectionDatastoreOperationBase {
   1407  ConnectionWriteOptimizer mWriteOptimizer;
   1408  bool mShadowWrites;
   1409 
   1410 public:
   1411  FlushOp(Connection* aConnection, ConnectionWriteOptimizer&& aWriteOptimizer);
   1412 
   1413 private:
   1414  nsresult DoDatastoreWork() override;
   1415 
   1416  void Cleanup() override;
   1417 };
   1418 
   1419 class Connection::CloseOp final : public ConnectionDatastoreOperationBase {
   1420  nsCOMPtr<nsIRunnable> mCallback;
   1421 
   1422 public:
   1423  CloseOp(Connection* aConnection, nsIRunnable* aCallback)
   1424      : ConnectionDatastoreOperationBase(aConnection,
   1425                                         /* aEnsureStorageConnection */ false),
   1426        mCallback(aCallback) {}
   1427 
   1428 private:
   1429  nsresult DoDatastoreWork() override;
   1430 
   1431  void Cleanup() override;
   1432 };
   1433 
   1434 class ConnectionThread final {
   1435  friend class Connection;
   1436 
   1437  nsCOMPtr<nsIThread> mThread;
   1438  nsRefPtrHashtable<nsCStringHashKey, Connection> mConnections;
   1439 
   1440 public:
   1441  ConnectionThread();
   1442 
   1443  void AssertIsOnOwningThread() const {
   1444    NS_ASSERT_OWNINGTHREAD(ConnectionThread);
   1445  }
   1446 
   1447  bool IsOnConnectionThread();
   1448 
   1449  void AssertIsOnConnectionThread();
   1450 
   1451  already_AddRefed<Connection> CreateConnection(
   1452      const OriginMetadata& aOriginMetadata,
   1453      UniquePtr<ArchivedOriginScope>&& aArchivedOriginScope,
   1454      bool aDatabaseWasNotAvailable);
   1455 
   1456  void Shutdown();
   1457 
   1458  NS_INLINE_DECL_REFCOUNTING(ConnectionThread)
   1459 
   1460 private:
   1461  ~ConnectionThread();
   1462 };
   1463 
   1464 /**
   1465 * Canonical state of Storage for an origin, containing all keys and their
   1466 * values in the parent process.  Specifically, this is the state that will
   1467 * be handed out to freshly created Snapshots and that will be persisted to disk
   1468 * when the Connection's flush completes.  State is mutated in batches as
   1469 * Snapshot instances Checkpoint their mutations locally accumulated in the
   1470 * child LSSnapshots.
   1471 */
   1472 class Datastore final
   1473    : public SupportsCheckedUnsafePtr<CheckIf<DiagnosticAssertEnabled>> {
   1474  ClientDirectoryLockHandle mDirectoryLockHandle;
   1475  RefPtr<Connection> mConnection;
   1476  RefPtr<QuotaObject> mQuotaObject;
   1477  nsCOMPtr<nsIRunnable> mCompleteCallback;
   1478  /**
   1479   * PrepareDatastoreOps register themselves with the Datastore at
   1480   * and unregister in PrepareDatastoreOp::Cleanup.
   1481   */
   1482  nsTHashSet<PrepareDatastoreOp*> mPrepareDatastoreOps;
   1483  /**
   1484   * PreparedDatastore instances register themselves with their associated
   1485   * Datastore at construction time and unregister at destruction time.  They
   1486   * hang around for kPreparedDatastoreTimeoutMs in order to keep the Datastore
   1487   * from closing itself via MaybeClose(), thereby giving the document enough
   1488   * time to load and access LocalStorage.
   1489   */
   1490  nsTHashSet<PreparedDatastore*> mPreparedDatastores;
   1491  /**
   1492   * A database is live (and in this hashtable) if it has a live LSDatabase
   1493   * actor.  There is at most one Database per origin per content process.  Each
   1494   * Database corresponds to an LSDatabase in its associated content process.
   1495   */
   1496  nsTHashSet<Database*> mDatabases;
   1497  /**
   1498   * A database is active if it has a non-null `mSnapshot`.  As long as there
   1499   * are any active databases final deltas can't be calculated and
   1500   * `UpdateUsage()` can't be invoked.
   1501   */
   1502  nsTHashSet<Database*> mActiveDatabases;
   1503  /**
   1504   * Non-authoritative hashtable representation of mOrderedItems for efficient
   1505   * lookup.
   1506   */
   1507  nsTHashMap<nsStringHashKey, LSValue> mValues;
   1508  /**
   1509   * The authoritative ordered state of the Datastore; mValue also exists as an
   1510   * unordered hashtable for efficient lookup.
   1511   */
   1512  nsTArray<LSItemInfo> mOrderedItems;
   1513  nsTArray<int64_t> mPendingUsageDeltas;
   1514  DatastoreWriteOptimizer mWriteOptimizer;
   1515  const OriginMetadata mOriginMetadata;
   1516  const uint32_t mPrivateBrowsingId;
   1517  int64_t mUsage;
   1518  int64_t mUpdateBatchUsage;
   1519  int64_t mSizeOfKeys;
   1520  int64_t mSizeOfItems;
   1521  bool mClosed;
   1522  bool mInUpdateBatch;
   1523  bool mHasLivePrivateDatastore;
   1524 
   1525 public:
   1526  // Created by PrepareDatastoreOp.
   1527  Datastore(const OriginMetadata& aOriginMetadata, uint32_t aPrivateBrowsingId,
   1528            int64_t aUsage, int64_t aSizeOfKeys, int64_t aSizeOfItems,
   1529            ClientDirectoryLockHandle&& aDirectoryLockHandle,
   1530            RefPtr<Connection>&& aConnection,
   1531            RefPtr<QuotaObject>&& aQuotaObject,
   1532            nsTHashMap<nsStringHashKey, LSValue>& aValues,
   1533            nsTArray<LSItemInfo>&& aOrderedItems);
   1534 
   1535  Maybe<ClientDirectoryLock&> MaybeDirectoryLockRef() const {
   1536    AssertIsOnBackgroundThread();
   1537 
   1538    return ToMaybeRef(mDirectoryLockHandle.get());
   1539  }
   1540 
   1541  const nsCString& Origin() const { return mOriginMetadata.mOrigin; }
   1542 
   1543  uint32_t PrivateBrowsingId() const { return mPrivateBrowsingId; }
   1544 
   1545  bool IsPersistent() const {
   1546    // Private-browsing is forbidden from touching disk.
   1547    return mPrivateBrowsingId == 0;
   1548  }
   1549 
   1550  void Close();
   1551 
   1552  bool IsClosed() const {
   1553    AssertIsOnBackgroundThread();
   1554 
   1555    return mClosed;
   1556  }
   1557 
   1558  void WaitForConnectionToComplete(nsIRunnable* aCallback);
   1559 
   1560  void NoteLivePrepareDatastoreOp(PrepareDatastoreOp* aPrepareDatastoreOp);
   1561 
   1562  void NoteFinishedPrepareDatastoreOp(PrepareDatastoreOp* aPrepareDatastoreOp);
   1563 
   1564  void NoteLivePrivateDatastore();
   1565 
   1566  void NoteFinishedPrivateDatastore();
   1567 
   1568  void NoteLivePreparedDatastore(PreparedDatastore* aPreparedDatastore);
   1569 
   1570  void NoteFinishedPreparedDatastore(PreparedDatastore* aPreparedDatastore);
   1571 
   1572  bool HasOtherProcessDatabases(Database* aDatabase);
   1573 
   1574  void NoteLiveDatabase(Database* aDatabase);
   1575 
   1576  void NoteFinishedDatabase(Database* aDatabase);
   1577 
   1578  void NoteActiveDatabase(Database* aDatabase);
   1579 
   1580  void NoteInactiveDatabase(Database* aDatabase);
   1581 
   1582  void GetSnapshotLoadInfo(const nsAString& aKey, bool& aAddKeyToUnknownItems,
   1583                           nsTHashtable<nsStringHashKey>& aLoadedItems,
   1584                           nsTArray<LSItemInfo>& aItemInfos,
   1585                           uint32_t& aNextLoadIndex,
   1586                           LSSnapshot::LoadState& aLoadState);
   1587 
   1588  uint32_t GetLength() const { return mValues.Count(); }
   1589 
   1590  const nsTArray<LSItemInfo>& GetOrderedItems() const { return mOrderedItems; }
   1591 
   1592  void GetItem(const nsAString& aKey, LSValue& aValue) const;
   1593 
   1594  void GetKeys(nsTArray<nsString>& aKeys) const;
   1595 
   1596  //////////////////////////////////////////////////////////////////////////////
   1597  // Mutation Methods
   1598  //
   1599  // These are only called during Snapshot::Checkpoint
   1600 
   1601  /**
   1602   * Used by Snapshot::Checkpoint to set a key/value pair as part of an
   1603   * explicit batch.
   1604   */
   1605  void SetItem(Database* aDatabase, const nsString& aKey,
   1606               const LSValue& aValue);
   1607 
   1608  void RemoveItem(Database* aDatabase, const nsString& aKey);
   1609 
   1610  void Clear(Database* aDatabase);
   1611 
   1612  void BeginUpdateBatch(int64_t aSnapshotUsage);
   1613 
   1614  int64_t EndUpdateBatch(int64_t aSnapshotPeakUsage);
   1615 
   1616  int64_t GetUsage() const { return mUsage; }
   1617 
   1618  int64_t AttemptToUpdateUsage(int64_t aMinSize, bool aInitial);
   1619 
   1620  bool HasOtherProcessObservers(Database* aDatabase);
   1621 
   1622  void NotifyOtherProcessObservers(Database* aDatabase,
   1623                                   const nsString& aDocumentURI,
   1624                                   const nsString& aKey,
   1625                                   const LSValue& aOldValue,
   1626                                   const LSValue& aNewValue);
   1627 
   1628  void NoteChangedObserverArray(const nsTArray<NotNull<Observer*>>& aObservers);
   1629 
   1630  void Stringify(nsACString& aResult) const;
   1631 
   1632  NS_INLINE_DECL_REFCOUNTING(Datastore)
   1633 
   1634 private:
   1635  // Reference counted.
   1636  ~Datastore();
   1637 
   1638  bool UpdateUsage(int64_t aDelta);
   1639 
   1640  void MaybeClose();
   1641 
   1642  void ConnectionClosedCallback();
   1643 
   1644  void CleanupMetadata();
   1645 
   1646  void NotifySnapshots(Database* aDatabase, const nsAString& aKey,
   1647                       const LSValue& aOldValue, bool aAffectsOrder);
   1648 
   1649  void NoteChangedDatabaseMap();
   1650 };
   1651 
   1652 class PrivateDatastore {
   1653  const NotNull<RefPtr<Datastore>> mDatastore;
   1654 
   1655 public:
   1656  explicit PrivateDatastore(MovingNotNull<RefPtr<Datastore>> aDatastore)
   1657      : mDatastore(std::move(aDatastore)) {
   1658    AssertIsOnBackgroundThread();
   1659 
   1660    mDatastore->NoteLivePrivateDatastore();
   1661  }
   1662 
   1663  ~PrivateDatastore() { mDatastore->NoteFinishedPrivateDatastore(); }
   1664 
   1665  const Datastore& DatastoreRef() const {
   1666    AssertIsOnBackgroundThread();
   1667 
   1668    return *mDatastore;
   1669  }
   1670 
   1671  Datastore& MutableDatastoreRef() const {
   1672    AssertIsOnBackgroundThread();
   1673 
   1674    return *mDatastore;
   1675  }
   1676 };
   1677 
   1678 class PreparedDatastore {
   1679  RefPtr<Datastore> mDatastore;
   1680  nsCOMPtr<nsITimer> mTimer;
   1681  const Maybe<ContentParentId> mContentParentId;
   1682  // Strings share buffers if possible, so it's not a problem to duplicate the
   1683  // origin here.
   1684  const nsCString mOrigin;
   1685  uint64_t mDatastoreId;
   1686  bool mForPreload;
   1687  bool mInvalidated;
   1688 
   1689 public:
   1690  PreparedDatastore(Datastore* aDatastore,
   1691                    const Maybe<ContentParentId>& aContentParentId,
   1692                    const nsACString& aOrigin, uint64_t aDatastoreId,
   1693                    bool aForPreload)
   1694      : mDatastore(aDatastore),
   1695        mTimer(NS_NewTimer()),
   1696        mContentParentId(aContentParentId),
   1697        mOrigin(aOrigin),
   1698        mDatastoreId(aDatastoreId),
   1699        mForPreload(aForPreload),
   1700        mInvalidated(false) {
   1701    AssertIsOnBackgroundThread();
   1702    MOZ_ASSERT(aDatastore);
   1703    MOZ_ASSERT(mTimer);
   1704 
   1705    aDatastore->NoteLivePreparedDatastore(this);
   1706 
   1707    MOZ_ALWAYS_SUCCEEDS(mTimer->InitWithNamedFuncCallback(
   1708        TimerCallback, this, kPreparedDatastoreTimeoutMs,
   1709        nsITimer::TYPE_ONE_SHOT, "PreparedDatastore::TimerCallback"_ns));
   1710  }
   1711 
   1712  ~PreparedDatastore() {
   1713    MOZ_ASSERT(mDatastore);
   1714    MOZ_ASSERT(mTimer);
   1715 
   1716    mTimer->Cancel();
   1717 
   1718    mDatastore->NoteFinishedPreparedDatastore(this);
   1719  }
   1720 
   1721  const Datastore& DatastoreRef() const {
   1722    AssertIsOnBackgroundThread();
   1723    MOZ_ASSERT(mDatastore);
   1724 
   1725    return *mDatastore;
   1726  }
   1727 
   1728  Datastore& MutableDatastoreRef() const {
   1729    AssertIsOnBackgroundThread();
   1730    MOZ_ASSERT(mDatastore);
   1731 
   1732    return *mDatastore;
   1733  }
   1734 
   1735  const Maybe<ContentParentId>& GetContentParentId() const {
   1736    return mContentParentId;
   1737  }
   1738 
   1739  const nsCString& Origin() const { return mOrigin; }
   1740 
   1741  void Invalidate() {
   1742    AssertIsOnBackgroundThread();
   1743 
   1744    mInvalidated = true;
   1745 
   1746    if (mForPreload) {
   1747      mTimer->Cancel();
   1748 
   1749      MOZ_ALWAYS_SUCCEEDS(mTimer->InitWithNamedFuncCallback(
   1750          TimerCallback, this, 0, nsITimer::TYPE_ONE_SHOT,
   1751          "PreparedDatastore::TimerCallback"_ns));
   1752    }
   1753  }
   1754 
   1755  bool IsInvalidated() const {
   1756    AssertIsOnBackgroundThread();
   1757 
   1758    return mInvalidated;
   1759  }
   1760 
   1761 private:
   1762  void Destroy();
   1763 
   1764  static void TimerCallback(nsITimer* aTimer, void* aClosure);
   1765 };
   1766 
   1767 /*******************************************************************************
   1768 * Actor class declarations
   1769 ******************************************************************************/
   1770 
   1771 class Database final
   1772    : public PBackgroundLSDatabaseParent,
   1773      public SupportsCheckedUnsafePtr<CheckIf<DiagnosticAssertEnabled>> {
   1774  RefPtr<Datastore> mDatastore;
   1775  Snapshot* mSnapshot;
   1776  const PrincipalInfo mPrincipalInfo;
   1777  const Maybe<ContentParentId> mContentParentId;
   1778  // Strings share buffers if possible, so it's not a problem to duplicate the
   1779  // origin here.
   1780  nsCString mOrigin;
   1781  mozilla::glean::TimerId mRequestAllowToCloseTimerId;
   1782  uint32_t mPrivateBrowsingId;
   1783  bool mAllowedToClose;
   1784  bool mActorDestroyed;
   1785  bool mRequestedAllowToClose;
   1786 #ifdef DEBUG
   1787  bool mActorWasAlive;
   1788 #endif
   1789 
   1790 public:
   1791  // Created in AllocPBackgroundLSDatabaseParent.
   1792  Database(const PrincipalInfo& aPrincipalInfo,
   1793           const Maybe<ContentParentId>& aContentParentId,
   1794           const nsACString& aOrigin, uint32_t aPrivateBrowsingId);
   1795 
   1796  void AssertIsOnOwningThread() const {
   1797    AssertIsOnBackgroundThread();
   1798    NS_ASSERT_OWNINGTHREAD(mozilla::dom::Database);
   1799  }
   1800 
   1801  Datastore* GetDatastore() const {
   1802    AssertIsOnOwningThread();
   1803    return mDatastore;
   1804  }
   1805 
   1806  Maybe<Datastore&> MaybeDatastoreRef() const {
   1807    AssertIsOnOwningThread();
   1808 
   1809    return ToMaybeRef(mDatastore.get());
   1810  }
   1811 
   1812  const PrincipalInfo& GetPrincipalInfo() const { return mPrincipalInfo; }
   1813 
   1814  const Maybe<ContentParentId>& ContentParentIdRef() const {
   1815    return mContentParentId;
   1816  }
   1817 
   1818  uint32_t PrivateBrowsingId() const { return mPrivateBrowsingId; }
   1819 
   1820  const nsCString& Origin() const { return mOrigin; }
   1821 
   1822  void SetActorAlive(Datastore* aDatastore);
   1823 
   1824  void RegisterSnapshot(Snapshot* aSnapshot);
   1825 
   1826  void UnregisterSnapshot(Snapshot* aSnapshot);
   1827 
   1828  Snapshot* GetSnapshot() const {
   1829    AssertIsOnOwningThread();
   1830    return mSnapshot;
   1831  }
   1832 
   1833  void RequestAllowToClose();
   1834 
   1835  void ForceKill();
   1836 
   1837  void Stringify(nsACString& aResult) const;
   1838 
   1839  NS_INLINE_DECL_REFCOUNTING(mozilla::dom::Database, override)
   1840 
   1841 private:
   1842  // Reference counted.
   1843  ~Database();
   1844 
   1845  void AllowToClose();
   1846 
   1847  // IPDL methods are only called by IPDL.
   1848  void ActorDestroy(ActorDestroyReason aWhy) override;
   1849 
   1850  mozilla::ipc::IPCResult RecvAllowToClose() override;
   1851 
   1852  PBackgroundLSSnapshotParent* AllocPBackgroundLSSnapshotParent(
   1853      const nsAString& aDocumentURI, const nsAString& aKey,
   1854      const bool& aIncreasePeakUsage, const int64_t& aMinSize,
   1855      LSSnapshotInitInfo* aInitInfo) override;
   1856 
   1857  mozilla::ipc::IPCResult RecvPBackgroundLSSnapshotConstructor(
   1858      PBackgroundLSSnapshotParent* aActor, const nsAString& aDocumentURI,
   1859      const nsAString& aKey, const bool& aIncreasePeakUsage,
   1860      const int64_t& aMinSize, LSSnapshotInitInfo* aInitInfo) override;
   1861 
   1862  bool DeallocPBackgroundLSSnapshotParent(
   1863      PBackgroundLSSnapshotParent* aActor) override;
   1864 };
   1865 
   1866 /**
   1867 * Attempts to capture the state of the underlying Datastore at the time of its
   1868 * creation so run-to-completion semantics can be honored.
   1869 *
   1870 * Rather than simply duplicate the contents of `DataStore::mValues` and
   1871 * `Datastore::mOrderedItems` at the time of their creation, the Snapshot tracks
   1872 * mutations to the Datastore as they happen, saving off the state of values as
   1873 * they existed when the Snapshot was created.  In other words, given an initial
   1874 * Datastore state of { foo: 'bar', bar: 'baz' }, the Snapshot won't store those
   1875 * values until it hears via `SaveItem` that "foo" is being over-written.  At
   1876 * that time, it will save off foo='bar' in mValues.
   1877 *
   1878 * ## Quota Allocation ##
   1879 *
   1880 * ## States ##
   1881 *
   1882 */
   1883 class Snapshot final : public PBackgroundLSSnapshotParent {
   1884  /**
   1885   * The Database that owns this snapshot.  There is a 1:1 relationship between
   1886   * snapshots and databases.
   1887   */
   1888  RefPtr<Database> mDatabase;
   1889  RefPtr<Datastore> mDatastore;
   1890  /**
   1891   * The set of keys for which values have been sent to the child LSSnapshot.
   1892   * Cleared once all values have been sent as indicated by
   1893   * mLoadedItems.Count()==mTotalLength and therefore mLoadedAllItems should be
   1894   * true.  No requests should be received for keys already in this set, and
   1895   * this is enforced by fatal IPC error (unless fuzzing).
   1896   */
   1897  nsTHashtable<nsStringHashKey> mLoadedItems;
   1898  /**
   1899   * The set of keys for which a RecvLoadValueAndMoreItems request was received
   1900   * but there was no such key, and so null was returned.  The child LSSnapshot
   1901   * will also cache these values, so redundant requests are also handled with
   1902   * fatal process termination just like for mLoadedItems.  Also cleared when
   1903   * mLoadedAllItems becomes true because then the child can infer that all
   1904   * other values must be null.  (Note: this could also be done when
   1905   * mLoadKeysReceived is true as a further optimization, but is not.)
   1906   */
   1907  nsTHashSet<nsString> mUnknownItems;
   1908  /**
   1909   * Values that have changed in mDatastore as reported by SaveItem
   1910   * notifications that are not yet known to the child LSSnapshot.
   1911   *
   1912   * The naive way to snapshot the state of mDatastore would be to duplicate its
   1913   * internal mValues at the time of our creation, but that is wasteful if few
   1914   * changes are made to the Datastore's state.  So we only track values that
   1915   * are changed/evicted from the Datastore as they happen, as reported to us by
   1916   * SaveItem notifications.
   1917   */
   1918  nsTHashMap<nsStringHashKey, LSValue> mValues;
   1919  /**
   1920   * Latched state of mDatastore's keys during a SaveItem notification with
   1921   * aAffectsOrder=true.  The ordered keys needed to be saved off so that a
   1922   * consistent ordering could be presented to the child LSSnapshot when it asks
   1923   * for them via RecvLoadKeys.
   1924   */
   1925  nsTArray<nsString> mKeys;
   1926  nsString mDocumentURI;
   1927  /**
   1928   * The index used for restoring iteration over not yet sent key/value pairs to
   1929   * the child LSSnapshot.
   1930   */
   1931  uint32_t mNextLoadIndex;
   1932  /**
   1933   * The number of key/value pairs that were present in the Datastore at the
   1934   * time the snapshot was created.  Once we have sent this many values to the
   1935   * child LSSnapshot, we can infer that it has received all of the keys/values
   1936   * and set mLoadedAllItems to true and clear mLoadedItems and mUnknownItems.
   1937   * Note that knowing the keys/values is not the same as knowing their ordering
   1938   * and so mKeys may be retained.
   1939   */
   1940  uint32_t mTotalLength;
   1941  int64_t mUsage;
   1942  int64_t mPeakUsage;
   1943  /**
   1944   * True if SaveItem has saved mDatastore's keys into mKeys because a SaveItem
   1945   * notification with aAffectsOrder=true was received.
   1946   */
   1947  bool mSavedKeys;
   1948  bool mActorDestroyed;
   1949  bool mFinishReceived;
   1950  bool mLoadedReceived;
   1951  /**
   1952   * True if LSSnapshot's mLoadState should be LoadState::AllOrderedItems or
   1953   * LoadState::AllUnorderedItems.  It will be AllOrderedItems if the initial
   1954   * snapshot contained all the data or if the state was AllOrderedKeys and
   1955   * successive RecvLoadValueAndMoreItems requests have resulted in the
   1956   * LSSnapshot being told all of the key/value pairs.  It will be
   1957   * AllUnorderedItems if the state was LoadState::Partial and successive
   1958   * RecvLoadValueAndMoreItem requests got all the keys/values but the key
   1959   * ordering was not retrieved.
   1960   */
   1961  bool mLoadedAllItems;
   1962  /**
   1963   * True if LSSnapshot's mLoadState should be LoadState::AllOrderedItems or
   1964   * AllOrderedKeys.  This can occur because of the initial snapshot, or because
   1965   * a RecvLoadKeys request was received.
   1966   */
   1967  bool mLoadKeysReceived;
   1968  bool mSentMarkDirty;
   1969 
   1970  /**
   1971   * True if there are Database objects in other content processes. The value
   1972   * never gets updated, we instead mark snapshots as dirty when Database
   1973   * objects are added or removed. Marking snapshots as dirty forces creation
   1974   * of new snapshots for new tasks.
   1975   */
   1976  bool mHasOtherProcessDatabases;
   1977  bool mHasOtherProcessObservers;
   1978 
   1979 public:
   1980  // Created in AllocPBackgroundLSSnapshotParent.
   1981  Snapshot(Database* aDatabase, const nsAString& aDocumentURI);
   1982 
   1983  void Init(nsTHashtable<nsStringHashKey>& aLoadedItems,
   1984            nsTHashSet<nsString>&& aUnknownItems, uint32_t aNextLoadIndex,
   1985            uint32_t aTotalLength, int64_t aUsage, int64_t aPeakUsage,
   1986            LSSnapshot::LoadState aLoadState, bool aHasOtherProcessDatabases,
   1987            bool aHasOtherProcessObservers) {
   1988    AssertIsOnBackgroundThread();
   1989    MOZ_ASSERT(aUsage >= 0);
   1990    MOZ_ASSERT(aPeakUsage >= aUsage);
   1991    MOZ_ASSERT_IF(aLoadState != LSSnapshot::LoadState::AllOrderedItems,
   1992                  aNextLoadIndex < aTotalLength);
   1993    MOZ_ASSERT(mTotalLength == 0);
   1994    MOZ_ASSERT(mUsage == -1);
   1995    MOZ_ASSERT(mPeakUsage == -1);
   1996 
   1997    mLoadedItems.SwapElements(aLoadedItems);
   1998    mUnknownItems = std::move(aUnknownItems);
   1999    mNextLoadIndex = aNextLoadIndex;
   2000    mTotalLength = aTotalLength;
   2001    mUsage = aUsage;
   2002    mPeakUsage = aPeakUsage;
   2003    if (aLoadState == LSSnapshot::LoadState::AllOrderedKeys) {
   2004      MOZ_ASSERT(mUnknownItems.Count() == 0);
   2005      mLoadKeysReceived = true;
   2006    } else if (aLoadState == LSSnapshot::LoadState::AllOrderedItems) {
   2007      MOZ_ASSERT(mLoadedItems.Count() == 0);
   2008      MOZ_ASSERT(mUnknownItems.Count() == 0);
   2009      MOZ_ASSERT(mNextLoadIndex == mTotalLength);
   2010      mLoadedReceived = true;
   2011      mLoadedAllItems = true;
   2012      mLoadKeysReceived = true;
   2013    }
   2014    mHasOtherProcessDatabases = aHasOtherProcessDatabases;
   2015    mHasOtherProcessObservers = aHasOtherProcessObservers;
   2016  }
   2017 
   2018  /**
   2019   * Called via NotifySnapshots by Datastore whenever it is updating its
   2020   * internal state so that snapshots can save off the state of a value at the
   2021   * time of their creation.
   2022   */
   2023  void SaveItem(const nsAString& aKey, const LSValue& aOldValue,
   2024                bool aAffectsOrder);
   2025 
   2026  void MarkDirty();
   2027 
   2028  bool IsDirty() const {
   2029    AssertIsOnBackgroundThread();
   2030 
   2031    return mSentMarkDirty;
   2032  }
   2033 
   2034  bool HasOtherProcessDatabases() const {
   2035    AssertIsOnBackgroundThread();
   2036 
   2037    return mHasOtherProcessDatabases;
   2038  }
   2039 
   2040  bool HasOtherProcessObservers() const {
   2041    AssertIsOnBackgroundThread();
   2042 
   2043    return mHasOtherProcessObservers;
   2044  }
   2045 
   2046  NS_INLINE_DECL_REFCOUNTING(mozilla::dom::Snapshot)
   2047 
   2048 private:
   2049  // Reference counted.
   2050  ~Snapshot();
   2051 
   2052  mozilla::ipc::IPCResult Checkpoint(nsTArray<LSWriteInfo>&& aWriteInfos);
   2053 
   2054  mozilla::ipc::IPCResult CheckpointAndNotify(
   2055      nsTArray<LSWriteAndNotifyInfo>&& aWriteAndNotifyInfos);
   2056 
   2057  void Finish();
   2058 
   2059  // IPDL methods are only called by IPDL.
   2060  void ActorDestroy(ActorDestroyReason aWhy) override;
   2061 
   2062  mozilla::ipc::IPCResult RecvDeleteMe() override;
   2063 
   2064  mozilla::ipc::IPCResult RecvAsyncCheckpoint(
   2065      nsTArray<LSWriteInfo>&& aWriteInfos) override;
   2066 
   2067  mozilla::ipc::IPCResult RecvAsyncCheckpointAndNotify(
   2068      nsTArray<LSWriteAndNotifyInfo>&& aWriteAndNotifyInfos) override;
   2069 
   2070  mozilla::ipc::IPCResult RecvSyncCheckpoint(
   2071      nsTArray<LSWriteInfo>&& aWriteInfos) override;
   2072 
   2073  mozilla::ipc::IPCResult RecvSyncCheckpointAndNotify(
   2074      nsTArray<LSWriteAndNotifyInfo>&& aWriteAndNotifyInfos) override;
   2075 
   2076  mozilla::ipc::IPCResult RecvAsyncFinish() override;
   2077 
   2078  mozilla::ipc::IPCResult RecvSyncFinish() override;
   2079 
   2080  mozilla::ipc::IPCResult RecvLoaded() override;
   2081 
   2082  mozilla::ipc::IPCResult RecvLoadValueAndMoreItems(
   2083      const nsAString& aKey, LSValue* aValue,
   2084      nsTArray<LSItemInfo>* aItemInfos) override;
   2085 
   2086  mozilla::ipc::IPCResult RecvLoadKeys(nsTArray<nsString>* aKeys) override;
   2087 
   2088  mozilla::ipc::IPCResult RecvIncreasePeakUsage(const int64_t& aMinSize,
   2089                                                int64_t* aSize) override;
   2090 };
   2091 
   2092 class Observer final : public PBackgroundLSObserverParent {
   2093  const Maybe<ContentParentId> mContentParentId;
   2094  nsCString mOrigin;
   2095  bool mActorDestroyed;
   2096 
   2097 public:
   2098  // Created in AllocPBackgroundLSObserverParent.
   2099  Observer(const Maybe<ContentParentId>& aContentParentId,
   2100           const nsACString& aOrigin);
   2101 
   2102  const Maybe<ContentParentId>& ContentParentIdRef() const {
   2103    return mContentParentId;
   2104  }
   2105 
   2106  const nsCString& Origin() const { return mOrigin; }
   2107 
   2108  void Observe(Database* aDatabase, const nsString& aDocumentURI,
   2109               const nsString& aKey, const LSValue& aOldValue,
   2110               const LSValue& aNewValue);
   2111 
   2112  NS_INLINE_DECL_REFCOUNTING(mozilla::dom::Observer)
   2113 
   2114 private:
   2115  // Reference counted.
   2116  ~Observer();
   2117 
   2118  // IPDL methods are only called by IPDL.
   2119  void ActorDestroy(ActorDestroyReason aWhy) override;
   2120 
   2121  mozilla::ipc::IPCResult RecvDeleteMe() override;
   2122 };
   2123 
   2124 class LSRequestBase : public DatastoreOperationBase,
   2125                      public PBackgroundLSRequestParent {
   2126 protected:
   2127  enum class State {
   2128    // Just created on the PBackground thread. Next step is StartingRequest.
   2129    Initial,
   2130 
   2131    // Waiting to start/starting request on the PBackground thread. Next step is
   2132    // either Nesting if a subclass needs to process more nested states or
   2133    // SendingReadyMessage if a subclass doesn't need any nested processing.
   2134    StartingRequest,
   2135 
   2136    // Doing nested processing.
   2137    Nesting,
   2138 
   2139    // Waiting to send/sending the ready message on the PBackground thread. Next
   2140    // step is WaitingForFinish.
   2141    SendingReadyMessage,
   2142 
   2143    // Waiting for the finish message on the PBackground thread. Next step is
   2144    // SendingResults.
   2145    WaitingForFinish,
   2146 
   2147    // Waiting to send/sending results on the PBackground thread. Next step is
   2148    // Completed.
   2149    SendingResults,
   2150 
   2151    // All done.
   2152    Completed
   2153  };
   2154 
   2155  const LSRequestParams mParams;
   2156  Maybe<ContentParentId> mContentParentId;
   2157  State mState;
   2158  bool mWaitingForFinish;
   2159 
   2160 public:
   2161  LSRequestBase(const LSRequestParams& aParams,
   2162                const Maybe<ContentParentId>& aContentParentId);
   2163 
   2164  void Dispatch();
   2165 
   2166  void StringifyState(nsACString& aResult) const;
   2167 
   2168  virtual void Stringify(nsACString& aResult) const;
   2169 
   2170  virtual void Log();
   2171 
   2172 protected:
   2173  ~LSRequestBase() override;
   2174 
   2175  virtual nsresult Start() = 0;
   2176 
   2177  virtual nsresult NestedRun();
   2178 
   2179  virtual void GetResponse(LSRequestResponse& aResponse) = 0;
   2180 
   2181  virtual void Cleanup() {}
   2182 
   2183 private:
   2184  bool VerifyRequestParams();
   2185 
   2186  nsresult StartRequest();
   2187 
   2188  void SendReadyMessage();
   2189 
   2190  nsresult SendReadyMessageInternal();
   2191 
   2192  void Finish();
   2193 
   2194  void FinishInternal();
   2195 
   2196  void SendResults();
   2197 
   2198 protected:
   2199  // Common nsIRunnable implementation that subclasses may not override.
   2200  NS_IMETHOD
   2201  Run() final;
   2202 
   2203  // IPDL methods.
   2204  void ActorDestroy(ActorDestroyReason aWhy) override;
   2205 
   2206 private:
   2207  mozilla::ipc::IPCResult RecvCancel() final;
   2208 
   2209  mozilla::ipc::IPCResult RecvFinish() final;
   2210 };
   2211 
   2212 template <typename T>
   2213 class SerializedOp {
   2214 protected:
   2215  void AddBlockingOp(T& aOp) { mBlocking.AppendElement(WrapNotNull(&aOp)); }
   2216 
   2217  void AddBlockedOnOp(T& aOp) { mBlockedOn.AppendElement(WrapNotNull(&aOp)); }
   2218 
   2219  void MaybeUnblock(T& aOp) {
   2220    mBlockedOn.RemoveElement(&aOp);
   2221    if (mBlockedOn.IsEmpty()) {
   2222      Unblock();
   2223    }
   2224  }
   2225 
   2226  virtual void Unblock() = 0;
   2227 
   2228  nsTArray<NotNull<RefPtr<T>>> mBlocking;
   2229  nsTArray<NotNull<RefPtr<T>>> mBlockedOn;
   2230 };
   2231 
   2232 class PrepareDatastoreOp
   2233    : public LSRequestBase,
   2234      public SerializedOp<PrepareDatastoreOp>,
   2235      public SupportsCheckedUnsafePtr<CheckIf<DiagnosticAssertEnabled>> {
   2236  class LoadDataOp;
   2237 
   2238  class CompressFunction;
   2239  class CompressionTypeFunction;
   2240 
   2241  enum class NestedState {
   2242    // The nesting has not yet taken place. Next step is
   2243    // CheckExistingOperations.
   2244    BeforeNesting,
   2245 
   2246    // Starting directory opening on the PBackground thread. The next step is
   2247    // DirectoryOpenPending.
   2248    OpenDirectory,
   2249 
   2250    // Waiting for directory open allowed on the PBackground thread. The next
   2251    // step is either SendingReadyMessage if directory lock failed to acquire,
   2252    // or CheckExistingOperations if directory lock is acquired.
   2253    DirectoryOpenPending,
   2254 
   2255    // Checking if a prepare datastore operation is already running for given
   2256    // origin on the PBackground thread. Next step is CheckClosingDatastore.
   2257    CheckExistingOperations,
   2258 
   2259    // Checking if a datastore is closing the connection for given origin on
   2260    // the PBackground thread. Next step is PreparationPending.
   2261    CheckClosingDatastore,
   2262 
   2263    // Ensuring quota manager is created and opening directory on the
   2264    // PBackground thread. Next step is either SendingResults if quota manager
   2265    // is not available or DatabaseWorkOpen if quota manager is available. If
   2266    // a datastore already exists for given origin then the next state is
   2267    // SendingReadyMessage.
   2268    PreparationPending,
   2269 
   2270    // Waiting to do/doing work on the QuotaManager IO thread. Its next step is
   2271    // BeginLoadData.
   2272    DatabaseWorkOpen,
   2273 
   2274    // Starting a load data operation on the PBackground thread. Next step is
   2275    // DatabaseWorkLoadData.
   2276    BeginLoadData,
   2277 
   2278    // Waiting to do/doing work on the connection thread. This involves waiting
   2279    // for the LoadDataOp to do its work. Eventually the state will transition
   2280    // to SendingReadyMessage.
   2281    DatabaseWorkLoadData,
   2282 
   2283    // The nesting has completed.
   2284    AfterNesting
   2285  };
   2286 
   2287  mozilla::glean::TimerId mProcessingTimerId;
   2288  RefPtr<ClientDirectoryLock> mPendingDirectoryLock;
   2289  ClientDirectoryLockHandle mDirectoryLockHandle;
   2290  ClientDirectoryLockHandle mExtraDirectoryLockHandle;
   2291  RefPtr<Connection> mConnection;
   2292  RefPtr<Datastore> mDatastore;
   2293  UniquePtr<ArchivedOriginScope> mArchivedOriginScope;
   2294  LoadDataOp* mLoadDataOp;
   2295  nsTHashMap<nsStringHashKey, LSValue> mValues;
   2296  nsTArray<LSItemInfo> mOrderedItems;
   2297  OriginMetadata mOriginMetadata;
   2298  nsCString mMainThreadOrigin;
   2299  nsString mDatabaseFilePath;
   2300  uint32_t mPrivateBrowsingId;
   2301  int64_t mUsage;
   2302  int64_t mSizeOfKeys;
   2303  int64_t mSizeOfItems;
   2304  uint64_t mDatastoreId;
   2305  NestedState mNestedState;
   2306  const bool mForPreload;
   2307  const bool mEnableMigration;
   2308  bool mDatabaseNotAvailable;
   2309  // Set when the Datastore has been registered with gPrivateDatastores so that
   2310  // it can be unregistered if an error is encountered in PrepareDatastoreOp.
   2311  FlippedOnce<false> mPrivateDatastoreRegistered;
   2312  // Set when the Datastore has been registered with gPreparedDatastores so
   2313  // that it can be unregistered if an error is encountered in
   2314  // PrepareDatastoreOp.
   2315  FlippedOnce<false> mPreparedDatastoreRegistered;
   2316  bool mInvalidated;
   2317 
   2318 #ifdef DEBUG
   2319  int64_t mDEBUGUsage;
   2320 #endif
   2321 
   2322 public:
   2323  PrepareDatastoreOp(const LSRequestParams& aParams,
   2324                     const Maybe<ContentParentId>& aContentParentId);
   2325 
   2326  Maybe<ClientDirectoryLock&> MaybeDirectoryLockRef() const {
   2327    AssertIsOnBackgroundThread();
   2328 
   2329    if (mDirectoryLockHandle) {
   2330      return SomeRef(*mDirectoryLockHandle);
   2331    }
   2332    if (mExtraDirectoryLockHandle) {
   2333      return SomeRef(*mExtraDirectoryLockHandle);
   2334    }
   2335    return Nothing();
   2336  }
   2337 
   2338  bool OriginIsKnown() const {
   2339    MOZ_ASSERT(IsOnOwningThread() || IsOnIOThread());
   2340 
   2341    return !mOriginMetadata.mOrigin.IsEmpty();
   2342  }
   2343 
   2344  const nsCString& Origin() const {
   2345    MOZ_ASSERT(IsOnOwningThread() || IsOnIOThread());
   2346    MOZ_ASSERT(OriginIsKnown());
   2347 
   2348    return mOriginMetadata.mOrigin;
   2349  }
   2350 
   2351  void Invalidate() {
   2352    AssertIsOnOwningThread();
   2353 
   2354    mInvalidated = true;
   2355  }
   2356 
   2357  void StringifyNestedState(nsACString& aResult) const;
   2358 
   2359  void Stringify(nsACString& aResult) const override;
   2360 
   2361  void Log() override;
   2362 
   2363 private:
   2364  ~PrepareDatastoreOp() override;
   2365 
   2366  nsresult Start() override;
   2367 
   2368  nsresult OpenDirectory();
   2369 
   2370  void Unblock() override {
   2371    MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(this));
   2372  }
   2373 
   2374  nsresult CheckExistingOperations();
   2375 
   2376  nsresult CheckClosingDatastoreInternal();
   2377 
   2378  nsresult CheckClosingDatastore();
   2379 
   2380  nsresult BeginDatastorePreparationInternal();
   2381 
   2382  nsresult BeginDatastorePreparation();
   2383 
   2384  void SendToIOThread();
   2385 
   2386  nsresult DatabaseWork();
   2387 
   2388  nsresult DatabaseNotAvailable();
   2389 
   2390  nsresult EnsureDirectoryEntry(nsIFile* aEntry, bool aCreateIfNotExists,
   2391                                bool aDirectory,
   2392                                bool* aAlreadyExisted = nullptr);
   2393 
   2394  nsresult VerifyDatabaseInformation(mozIStorageConnection* aConnection);
   2395 
   2396  already_AddRefed<QuotaObject> GetQuotaObject();
   2397 
   2398  nsresult BeginLoadData();
   2399 
   2400  void FinishNesting();
   2401 
   2402  nsresult FinishNestingOnNonOwningThread();
   2403 
   2404  nsresult NestedRun() override;
   2405 
   2406  void GetResponse(LSRequestResponse& aResponse) override;
   2407 
   2408  void Cleanup() override;
   2409 
   2410  void ConnectionClosedCallback();
   2411 
   2412  void CleanupMetadata();
   2413 
   2414  // IPDL overrides.
   2415  void ActorDestroy(ActorDestroyReason aWhy) override;
   2416 
   2417  void DirectoryLockAcquired(ClientDirectoryLockHandle aLockHandle);
   2418 
   2419  void DirectoryLockFailed();
   2420 };
   2421 
   2422 class PrepareDatastoreOp::LoadDataOp final
   2423    : public ConnectionDatastoreOperationBase {
   2424  RefPtr<PrepareDatastoreOp> mPrepareDatastoreOp;
   2425 
   2426 public:
   2427  explicit LoadDataOp(PrepareDatastoreOp* aPrepareDatastoreOp)
   2428      : ConnectionDatastoreOperationBase(aPrepareDatastoreOp->mConnection),
   2429        mPrepareDatastoreOp(aPrepareDatastoreOp) {}
   2430 
   2431 private:
   2432  ~LoadDataOp() = default;
   2433 
   2434  nsresult DoDatastoreWork() override;
   2435 
   2436  void OnSuccess() override;
   2437 
   2438  void OnFailure(nsresult aResultCode) override;
   2439 
   2440  void Cleanup() override;
   2441 };
   2442 
   2443 class PrepareDatastoreOp::CompressFunction final : public mozIStorageFunction {
   2444 private:
   2445  ~CompressFunction() = default;
   2446 
   2447  NS_DECL_ISUPPORTS
   2448  NS_DECL_MOZISTORAGEFUNCTION
   2449 };
   2450 
   2451 class PrepareDatastoreOp::CompressionTypeFunction final
   2452    : public mozIStorageFunction {
   2453 private:
   2454  ~CompressionTypeFunction() = default;
   2455 
   2456  NS_DECL_ISUPPORTS
   2457  NS_DECL_MOZISTORAGEFUNCTION
   2458 };
   2459 
   2460 class PrepareObserverOp : public LSRequestBase {
   2461  nsCString mOrigin;
   2462 
   2463 public:
   2464  PrepareObserverOp(const LSRequestParams& aParams,
   2465                    const Maybe<ContentParentId>& aContentParentId);
   2466 
   2467 private:
   2468  nsresult Start() override;
   2469 
   2470  void GetResponse(LSRequestResponse& aResponse) override;
   2471 };
   2472 
   2473 class LSSimpleRequestBase : public DatastoreOperationBase,
   2474                            public PBackgroundLSSimpleRequestParent {
   2475 protected:
   2476  enum class State {
   2477    // Just created on the PBackground thread. Next step is StartingRequest.
   2478    Initial,
   2479 
   2480    // Waiting to start/starting request on the PBackground thread. Next step is
   2481    // SendingResults.
   2482    StartingRequest,
   2483 
   2484    // Waiting to send/sending results on the PBackground thread. Next step is
   2485    // Completed.
   2486    SendingResults,
   2487 
   2488    // All done.
   2489    Completed
   2490  };
   2491 
   2492  const LSSimpleRequestParams mParams;
   2493  Maybe<ContentParentId> mContentParentId;
   2494  State mState;
   2495 
   2496 public:
   2497  LSSimpleRequestBase(const LSSimpleRequestParams& aParams,
   2498                      const Maybe<ContentParentId>& aContentParentId);
   2499 
   2500  void Dispatch();
   2501 
   2502 protected:
   2503  ~LSSimpleRequestBase() override;
   2504 
   2505  virtual nsresult Start() = 0;
   2506 
   2507  virtual void GetResponse(LSSimpleRequestResponse& aResponse) = 0;
   2508 
   2509 private:
   2510  bool VerifyRequestParams();
   2511 
   2512  nsresult StartRequest();
   2513 
   2514  void SendResults();
   2515 
   2516  // Common nsIRunnable implementation that subclasses may not override.
   2517  NS_IMETHOD
   2518  Run() final;
   2519 
   2520  // IPDL methods.
   2521  void ActorDestroy(ActorDestroyReason aWhy) override;
   2522 };
   2523 
   2524 class PreloadedOp : public LSSimpleRequestBase {
   2525  nsCString mOrigin;
   2526 
   2527 public:
   2528  PreloadedOp(const LSSimpleRequestParams& aParams,
   2529              const Maybe<ContentParentId>& aContentParentId);
   2530 
   2531 private:
   2532  nsresult Start() override;
   2533 
   2534  void GetResponse(LSSimpleRequestResponse& aResponse) override;
   2535 };
   2536 
   2537 class GetStateOp : public LSSimpleRequestBase {
   2538  nsCString mOrigin;
   2539 
   2540 public:
   2541  GetStateOp(const LSSimpleRequestParams& aParams,
   2542             const Maybe<ContentParentId>& aContentParentId);
   2543 
   2544 private:
   2545  nsresult Start() override;
   2546 
   2547  void GetResponse(LSSimpleRequestResponse& aResponse) override;
   2548 };
   2549 
   2550 /*******************************************************************************
   2551 * Other class declarations
   2552 ******************************************************************************/
   2553 
   2554 struct ArchivedOriginInfo {
   2555  OriginAttributes mOriginAttributes;
   2556  nsCString mOriginNoSuffix;
   2557 
   2558  ArchivedOriginInfo(const OriginAttributes& aOriginAttributes,
   2559                     const nsACString& aOriginNoSuffix)
   2560      : mOriginAttributes(aOriginAttributes),
   2561        mOriginNoSuffix(aOriginNoSuffix) {}
   2562 };
   2563 
   2564 class ArchivedOriginScope {
   2565  struct Origin {
   2566    nsCString mOriginSuffix;
   2567    nsCString mOriginNoSuffix;
   2568 
   2569    Origin(const nsACString& aOriginSuffix, const nsACString& aOriginNoSuffix)
   2570        : mOriginSuffix(aOriginSuffix), mOriginNoSuffix(aOriginNoSuffix) {}
   2571 
   2572    const nsACString& OriginSuffix() const { return mOriginSuffix; }
   2573 
   2574    const nsACString& OriginNoSuffix() const { return mOriginNoSuffix; }
   2575  };
   2576 
   2577  struct Prefix {
   2578    nsCString mOriginNoSuffix;
   2579 
   2580    explicit Prefix(const nsACString& aOriginNoSuffix)
   2581        : mOriginNoSuffix(aOriginNoSuffix) {}
   2582 
   2583    const nsACString& OriginNoSuffix() const { return mOriginNoSuffix; }
   2584  };
   2585 
   2586  struct Pattern {
   2587    UniquePtr<OriginAttributesPattern> mPattern;
   2588 
   2589    explicit Pattern(const OriginAttributesPattern& aPattern)
   2590        : mPattern(MakeUnique<OriginAttributesPattern>(aPattern)) {}
   2591 
   2592    Pattern(const Pattern& aOther)
   2593        : mPattern(MakeUnique<OriginAttributesPattern>(*aOther.mPattern)) {}
   2594 
   2595    Pattern(Pattern&& aOther) = default;
   2596 
   2597    const OriginAttributesPattern& GetPattern() const {
   2598      MOZ_ASSERT(mPattern);
   2599      return *mPattern;
   2600    }
   2601  };
   2602 
   2603  struct Null {};
   2604 
   2605  using DataType = Variant<Origin, Pattern, Prefix, Null>;
   2606 
   2607  DataType mData;
   2608 
   2609 public:
   2610  static UniquePtr<ArchivedOriginScope> CreateFromOrigin(
   2611      const nsACString& aOriginAttrSuffix, const nsACString& aOriginKey);
   2612 
   2613  static UniquePtr<ArchivedOriginScope> CreateFromPrefix(
   2614      const nsACString& aOriginKey);
   2615 
   2616  static UniquePtr<ArchivedOriginScope> CreateFromPattern(
   2617      const OriginAttributesPattern& aPattern);
   2618 
   2619  static UniquePtr<ArchivedOriginScope> CreateFromNull();
   2620 
   2621  bool IsOrigin() const { return mData.is<Origin>(); }
   2622 
   2623  bool IsPrefix() const { return mData.is<Prefix>(); }
   2624 
   2625  bool IsPattern() const { return mData.is<Pattern>(); }
   2626 
   2627  bool IsNull() const { return mData.is<Null>(); }
   2628 
   2629  const nsACString& OriginSuffix() const {
   2630    MOZ_ASSERT(IsOrigin());
   2631 
   2632    return mData.as<Origin>().OriginSuffix();
   2633  }
   2634 
   2635  const nsACString& OriginNoSuffix() const {
   2636    MOZ_ASSERT(IsOrigin() || IsPrefix());
   2637 
   2638    if (IsOrigin()) {
   2639      return mData.as<Origin>().OriginNoSuffix();
   2640    }
   2641    return mData.as<Prefix>().OriginNoSuffix();
   2642  }
   2643 
   2644  const OriginAttributesPattern& GetPattern() const {
   2645    MOZ_ASSERT(IsPattern());
   2646 
   2647    return mData.as<Pattern>().GetPattern();
   2648  }
   2649 
   2650  nsLiteralCString GetBindingClause() const;
   2651 
   2652  nsresult BindToStatement(mozIStorageStatement* aStatement) const;
   2653 
   2654  bool HasMatches(ArchivedOriginHashtable* aHashtable) const;
   2655 
   2656  void RemoveMatches(ArchivedOriginHashtable* aHashtable) const;
   2657 
   2658 private:
   2659  // Move constructors
   2660  explicit ArchivedOriginScope(const Origin&& aOrigin) : mData(aOrigin) {}
   2661 
   2662  explicit ArchivedOriginScope(const Pattern&& aPattern) : mData(aPattern) {}
   2663 
   2664  explicit ArchivedOriginScope(const Prefix&& aPrefix) : mData(aPrefix) {}
   2665 
   2666  explicit ArchivedOriginScope(const Null&& aNull) : mData(aNull) {}
   2667 };
   2668 
   2669 class QuotaClient final : public mozilla::dom::quota::Client {
   2670  class MatchFunction;
   2671 
   2672  static QuotaClient* sInstance;
   2673 
   2674  Mutex mShadowDatabaseMutex MOZ_UNANNOTATED;
   2675 
   2676 public:
   2677  QuotaClient();
   2678 
   2679  static QuotaClient* GetInstance() {
   2680    AssertIsOnBackgroundThread();
   2681 
   2682    return sInstance;
   2683  }
   2684 
   2685  mozilla::Mutex& ShadowDatabaseMutex() {
   2686    MOZ_ASSERT(IsOnIOThread() || IsOnGlobalConnectionThread());
   2687 
   2688    return mShadowDatabaseMutex;
   2689  }
   2690 
   2691  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::QuotaClient, override)
   2692 
   2693  Type GetType() override;
   2694 
   2695  Result<UsageInfo, nsresult> InitOrigin(PersistenceType aPersistenceType,
   2696                                         const OriginMetadata& aOriginMetadata,
   2697                                         const AtomicBool& aCanceled) override;
   2698 
   2699  nsresult InitOriginWithoutTracking(PersistenceType aPersistenceType,
   2700                                     const OriginMetadata& aOriginMetadata,
   2701                                     const AtomicBool& aCanceled) override;
   2702 
   2703  Result<UsageInfo, nsresult> GetUsageForOrigin(
   2704      PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata,
   2705      const AtomicBool& aCanceled) override;
   2706 
   2707  nsresult AboutToClearOrigins(const PersistenceScope& aPersistenceScope,
   2708                               const OriginScope& aOriginScope) override;
   2709 
   2710  void OnOriginClearCompleted(const OriginMetadata& aOriginMetadata) override;
   2711 
   2712  void OnRepositoryClearCompleted(PersistenceType aPersistenceType) override;
   2713 
   2714  void ReleaseIOThreadObjects() override;
   2715 
   2716  void AbortOperationsForLocks(
   2717      const DirectoryLockIdTable& aDirectoryLockIds) override;
   2718 
   2719  void AbortOperationsForProcess(ContentParentId aContentParentId) override;
   2720 
   2721  void AbortAllOperations() override;
   2722 
   2723  void StartIdleMaintenance() override;
   2724 
   2725  void StopIdleMaintenance() override;
   2726 
   2727 private:
   2728  ~QuotaClient() override;
   2729 
   2730  void InitiateShutdown() override;
   2731  bool IsShutdownCompleted() const override;
   2732  nsCString GetShutdownStatus() const override;
   2733  void ForceKillActors() override;
   2734  void FinalizeShutdown() override;
   2735 
   2736  Result<UniquePtr<ArchivedOriginScope>, nsresult> CreateArchivedOriginScope(
   2737      const OriginScope& aOriginScope);
   2738 
   2739  nsresult PerformDelete(mozIStorageConnection* aConnection,
   2740                         const nsACString& aSchemaName,
   2741                         ArchivedOriginScope* aArchivedOriginScope) const;
   2742 };
   2743 
   2744 class QuotaClient::MatchFunction final : public mozIStorageFunction {
   2745  OriginAttributesPattern mPattern;
   2746 
   2747 public:
   2748  explicit MatchFunction(const OriginAttributesPattern& aPattern)
   2749      : mPattern(aPattern) {}
   2750 
   2751 private:
   2752  ~MatchFunction() = default;
   2753 
   2754  NS_DECL_ISUPPORTS
   2755  NS_DECL_MOZISTORAGEFUNCTION
   2756 };
   2757 
   2758 /*******************************************************************************
   2759 * Helper classes
   2760 ******************************************************************************/
   2761 
   2762 class MOZ_STACK_CLASS AutoWriteTransaction final {
   2763  Connection* mConnection;
   2764  Maybe<MutexAutoLock> mShadowDatabaseLock;
   2765  bool mShadowWrites;
   2766 
   2767 public:
   2768  explicit AutoWriteTransaction(bool aShadowWrites);
   2769 
   2770  ~AutoWriteTransaction();
   2771 
   2772  nsresult Start(Connection* aConnection);
   2773 
   2774  nsresult Commit();
   2775 
   2776 private:
   2777  nsresult LockAndAttachShadowDatabase(Connection* aConnection);
   2778 
   2779  nsresult DetachShadowDatabaseAndUnlock();
   2780 };
   2781 
   2782 /*******************************************************************************
   2783 * Globals
   2784 ******************************************************************************/
   2785 
   2786 #ifdef DEBUG
   2787 bool gLocalStorageInitialized = false;
   2788 #endif
   2789 
   2790 using PrepareDatastoreOpArray =
   2791    nsTArray<NotNull<CheckedUnsafePtr<PrepareDatastoreOp>>>;
   2792 
   2793 StaticAutoPtr<PrepareDatastoreOpArray> gPrepareDatastoreOps;
   2794 
   2795 // When CheckedUnsafePtr's checking is enabled, it's necessary to ensure that
   2796 // the hashtable uses the copy constructor instead of memmove for moving entries
   2797 // since memmove will break CheckedUnsafePtr in a memory-corrupting way.
   2798 using DatastoreHashKey = std::conditional<DiagnosticAssertEnabled::value,
   2799                                          nsCStringHashKeyWithDisabledMemmove,
   2800                                          nsCStringHashKey>::type;
   2801 
   2802 using DatastoreHashtable =
   2803    nsBaseHashtable<DatastoreHashKey, NotNull<CheckedUnsafePtr<Datastore>>,
   2804                    MovingNotNull<CheckedUnsafePtr<Datastore>>>;
   2805 
   2806 StaticAutoPtr<DatastoreHashtable> gDatastores;
   2807 
   2808 uint64_t gLastDatastoreId = 0;
   2809 
   2810 using PreparedDatastoreHashtable =
   2811    nsClassHashtable<nsUint64HashKey, PreparedDatastore>;
   2812 
   2813 StaticAutoPtr<PreparedDatastoreHashtable> gPreparedDatastores;
   2814 
   2815 using PrivateDatastoreHashtable =
   2816    nsClassHashtable<nsCStringHashKey, PrivateDatastore>;
   2817 
   2818 // Keeps Private Browsing Datastores alive until the private browsing session
   2819 // is closed. This is necessary because LocalStorage Private Browsing data is
   2820 // (currently) not written to disk and therefore needs to explicitly be kept
   2821 // alive in memory so that if a user browses away from a site during a session
   2822 // and then back to it that they will still have their data.
   2823 //
   2824 // The entries are wrapped by PrivateDatastore instances which call
   2825 // NoteLivePrivateDatastore and NoteFinishedPrivateDatastore which set and
   2826 // clear mHasLivePrivateDatastore which inhibits MaybeClose() from closing the
   2827 // datastore (which would discard the data) when there are no active windows
   2828 // using LocalStorage for the origin.
   2829 //
   2830 // The table is cleared when the Private Browsing session is closed, which will
   2831 // cause NoteFinishedPrivateDatastore to be called on each Datastore which will
   2832 // in turn call MaybeClose which should then discard the Datastore. Or in the
   2833 // event of an (unlikely) race where the private browsing windows are still
   2834 // being torn down, will cause the Datastore to be discarded when the last
   2835 // window actually goes away.
   2836 constinit UniquePtr<PrivateDatastoreHashtable> gPrivateDatastores;
   2837 
   2838 using DatabaseArray = nsTArray<Database*>;
   2839 
   2840 StaticAutoPtr<DatabaseArray> gDatabases;
   2841 
   2842 using LiveDatabaseArray = nsTArray<NotNull<CheckedUnsafePtr<Database>>>;
   2843 
   2844 StaticAutoPtr<LiveDatabaseArray> gLiveDatabases;
   2845 
   2846 StaticRefPtr<ConnectionThread> gConnectionThread;
   2847 
   2848 uint64_t gLastObserverId = 0;
   2849 
   2850 using PreparedObserverHashtable = nsRefPtrHashtable<nsUint64HashKey, Observer>;
   2851 
   2852 StaticAutoPtr<PreparedObserverHashtable> gPreparedObsevers;
   2853 
   2854 using ObserverHashtable =
   2855    nsClassHashtable<nsCStringHashKey, nsTArray<NotNull<Observer*>>>;
   2856 
   2857 StaticAutoPtr<ObserverHashtable> gObservers;
   2858 
   2859 Atomic<bool> gShadowWrites(kDefaultShadowWrites);
   2860 Atomic<int32_t, Relaxed> gSnapshotPrefill(kDefaultSnapshotPrefill);
   2861 Atomic<int32_t, Relaxed> gSnapshotGradualPrefill(
   2862    kDefaultSnapshotGradualPrefill);
   2863 Atomic<bool> gClientValidation(kDefaultClientValidation);
   2864 
   2865 using UsageHashtable = nsTHashMap<nsCStringHashKey, int64_t>;
   2866 
   2867 StaticAutoPtr<ArchivedOriginHashtable> gArchivedOrigins;
   2868 
   2869 // Can only be touched on the Quota Manager I/O thread.
   2870 bool gInitializedShadowStorage = false;
   2871 
   2872 StaticAutoPtr<LSInitializationInfo> gInitializationInfo;
   2873 
   2874 bool IsOnGlobalConnectionThread() {
   2875  MOZ_ASSERT(gConnectionThread);
   2876  return gConnectionThread->IsOnConnectionThread();
   2877 }
   2878 
   2879 void AssertIsOnGlobalConnectionThread() {
   2880  MOZ_ASSERT(gConnectionThread);
   2881  gConnectionThread->AssertIsOnConnectionThread();
   2882 }
   2883 
   2884 already_AddRefed<Datastore> GetDatastore(const nsACString& aOrigin) {
   2885  AssertIsOnBackgroundThread();
   2886 
   2887  if (gDatastores) {
   2888    auto maybeDatastore = gDatastores->MaybeGet(aOrigin);
   2889    if (maybeDatastore) {
   2890      RefPtr<Datastore> result(std::move(*maybeDatastore).unwrapBasePtr());
   2891      return result.forget();
   2892    }
   2893  }
   2894 
   2895  return nullptr;
   2896 }
   2897 
   2898 nsresult LoadArchivedOrigins() {
   2899  GECKO_TRACE_SCOPE("dom::localstorage", "LoadArchivedOrigins");
   2900 
   2901  AssertIsOnIOThread();
   2902  MOZ_ASSERT(!gArchivedOrigins);
   2903 
   2904  QuotaManager* quotaManager = QuotaManager::Get();
   2905  MOZ_ASSERT(quotaManager);
   2906 
   2907  QM_TRY_INSPECT(const auto& connection, CreateArchiveStorageConnection(
   2908                                             quotaManager->GetStoragePath()));
   2909 
   2910  if (!connection) {
   2911    gArchivedOrigins = new ArchivedOriginHashtable();
   2912    return NS_OK;
   2913  }
   2914 
   2915  QM_TRY_INSPECT(
   2916      const auto& stmt,
   2917      MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
   2918          nsCOMPtr<mozIStorageStatement>, connection, CreateStatement,
   2919          "SELECT DISTINCT originAttributes, originKey "
   2920          "FROM webappsstore2;"_ns));
   2921 
   2922  auto archivedOrigins = MakeUnique<ArchivedOriginHashtable>();
   2923 
   2924  // XXX Actually, this could use a hashtable variant of
   2925  // CollectElementsWhileHasResult
   2926  QM_TRY(quota::CollectWhileHasResult(
   2927      *stmt, [&archivedOrigins](auto& stmt) -> Result<Ok, nsresult> {
   2928        QM_TRY_INSPECT(const auto& originSuffix,
   2929                       MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsCString, stmt,
   2930                                                         GetUTF8String, 0));
   2931        QM_TRY_INSPECT(const auto& originNoSuffix,
   2932                       MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsCString, stmt,
   2933                                                         GetUTF8String, 1));
   2934 
   2935        const nsCString hashKey =
   2936            GetArchivedOriginHashKey(originSuffix, originNoSuffix);
   2937 
   2938        OriginAttributes originAttributes;
   2939        QM_TRY(OkIf(originAttributes.PopulateFromSuffix(originSuffix)),
   2940               Err(NS_ERROR_FAILURE));
   2941 
   2942        archivedOrigins->InsertOrUpdate(
   2943            hashKey,
   2944            MakeUnique<ArchivedOriginInfo>(originAttributes, originNoSuffix));
   2945 
   2946        return Ok{};
   2947      }));
   2948 
   2949  gArchivedOrigins = archivedOrigins.release();
   2950  return NS_OK;
   2951 }
   2952 
   2953 Result<int64_t, nsresult> GetUsage(mozIStorageConnection& aConnection,
   2954                                   ArchivedOriginScope* aArchivedOriginScope) {
   2955  AssertIsOnIOThread();
   2956 
   2957  QM_TRY_INSPECT(
   2958      const auto& stmt,
   2959      ([aArchivedOriginScope,
   2960        &aConnection]() -> Result<nsCOMPtr<mozIStorageStatement>, nsresult> {
   2961        if (aArchivedOriginScope) {
   2962          QM_TRY_RETURN(CreateAndExecuteSingleStepStatement<
   2963                        SingleStepResult::ReturnNullIfNoResult>(
   2964              aConnection,
   2965              "SELECT "
   2966              "total(utf16Length(key) + utf16Length(value)) "
   2967              "FROM webappsstore2 "
   2968              "WHERE originKey = :originKey "
   2969              "AND originAttributes = :originAttributes;"_ns,
   2970              [aArchivedOriginScope](auto& stmt) -> Result<Ok, nsresult> {
   2971                QM_TRY(MOZ_TO_RESULT(
   2972                    aArchivedOriginScope->BindToStatement(&stmt)));
   2973                return Ok{};
   2974              }));
   2975        }
   2976 
   2977        QM_TRY_RETURN(CreateAndExecuteSingleStepStatement<
   2978                      SingleStepResult::ReturnNullIfNoResult>(
   2979            aConnection, "SELECT usage FROM database"_ns));
   2980      }()));
   2981 
   2982  QM_TRY(OkIf(stmt), Err(NS_ERROR_FAILURE));
   2983 
   2984  QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_MEMBER(stmt, GetInt64, 0));
   2985 }
   2986 
   2987 void ShadowWritesPrefChangedCallback(const char* aPrefName, void* aClosure) {
   2988  MOZ_ASSERT(NS_IsMainThread());
   2989  MOZ_ASSERT(!strcmp(aPrefName, kShadowWritesPref));
   2990  MOZ_ASSERT(!aClosure);
   2991 
   2992  gShadowWrites = Preferences::GetBool(aPrefName, kDefaultShadowWrites);
   2993 }
   2994 
   2995 void SnapshotPrefillPrefChangedCallback(const char* aPrefName, void* aClosure) {
   2996  MOZ_ASSERT(NS_IsMainThread());
   2997  MOZ_ASSERT(!strcmp(aPrefName, kSnapshotPrefillPref));
   2998  MOZ_ASSERT(!aClosure);
   2999 
   3000  int32_t snapshotPrefill =
   3001      Preferences::GetInt(aPrefName, kDefaultSnapshotPrefill);
   3002 
   3003  // The magic -1 is for use only by tests.
   3004  if (snapshotPrefill == -1) {
   3005    snapshotPrefill = INT32_MAX;
   3006  }
   3007 
   3008  gSnapshotPrefill = snapshotPrefill;
   3009 }
   3010 
   3011 void SnapshotGradualPrefillPrefChangedCallback(const char* aPrefName,
   3012                                               void* aClosure) {
   3013  MOZ_ASSERT(NS_IsMainThread());
   3014  MOZ_ASSERT(!strcmp(aPrefName, kSnapshotGradualPrefillPref));
   3015  MOZ_ASSERT(!aClosure);
   3016 
   3017  int32_t snapshotGradualPrefill =
   3018      Preferences::GetInt(aPrefName, kDefaultSnapshotGradualPrefill);
   3019 
   3020  // The magic -1 is for use only by tests.
   3021  if (snapshotGradualPrefill == -1) {
   3022    snapshotGradualPrefill = INT32_MAX;
   3023  }
   3024 
   3025  gSnapshotGradualPrefill = snapshotGradualPrefill;
   3026 }
   3027 
   3028 int64_t GetSnapshotPeakUsagePreincrement(bool aInitial) {
   3029  return aInitial ? StaticPrefs::
   3030                        dom_storage_snapshot_peak_usage_initial_preincrement()
   3031                  : StaticPrefs::
   3032                        dom_storage_snapshot_peak_usage_gradual_preincrement();
   3033 }
   3034 
   3035 int64_t GetSnapshotPeakUsageReducedPreincrement(bool aInitial) {
   3036  return aInitial
   3037             ? StaticPrefs::
   3038                   dom_storage_snapshot_peak_usage_reduced_initial_preincrement()
   3039             : StaticPrefs::
   3040                   dom_storage_snapshot_peak_usage_reduced_gradual_preincrement();
   3041 }
   3042 
   3043 void ClientValidationPrefChangedCallback(const char* aPrefName,
   3044                                         void* aClosure) {
   3045  MOZ_ASSERT(NS_IsMainThread());
   3046  MOZ_ASSERT(!strcmp(aPrefName, kClientValidationPref));
   3047  MOZ_ASSERT(!aClosure);
   3048 
   3049  gClientValidation = Preferences::GetBool(aPrefName, kDefaultClientValidation);
   3050 }
   3051 
   3052 template <typename Condition>
   3053 void InvalidatePrepareDatastoreOpsMatching(const Condition& aCondition) {
   3054  if (!gPrepareDatastoreOps) {
   3055    return;
   3056  }
   3057 
   3058  for (const auto& prepareDatastoreOp : *gPrepareDatastoreOps) {
   3059    if (aCondition(*prepareDatastoreOp)) {
   3060      prepareDatastoreOp->Invalidate();
   3061    }
   3062  }
   3063 }
   3064 
   3065 template <typename Condition>
   3066 void InvalidatePreparedDatastoresMatching(const Condition& aCondition) {
   3067  if (!gPreparedDatastores) {
   3068    return;
   3069  }
   3070 
   3071  for (const auto& preparedDatastore : gPreparedDatastores->Values()) {
   3072    MOZ_ASSERT(preparedDatastore);
   3073 
   3074    if (aCondition(*preparedDatastore)) {
   3075      preparedDatastore->Invalidate();
   3076    }
   3077  }
   3078 }
   3079 
   3080 template <typename Condition>
   3081 nsTArray<RefPtr<Database>> CollectDatabasesMatching(Condition aCondition) {
   3082  AssertIsOnBackgroundThread();
   3083 
   3084  if (!gLiveDatabases) {
   3085    return nsTArray<RefPtr<Database>>{};
   3086  }
   3087 
   3088  nsTArray<RefPtr<Database>> databases;
   3089 
   3090  for (const auto& database : *gLiveDatabases) {
   3091    if (aCondition(*database)) {
   3092      databases.AppendElement(database.get());
   3093    }
   3094  }
   3095 
   3096  return databases;
   3097 }
   3098 
   3099 template <typename Condition>
   3100 void RequestAllowToCloseDatabasesMatching(Condition aCondition) {
   3101  AssertIsOnBackgroundThread();
   3102 
   3103  nsTArray<RefPtr<Database>> databases = CollectDatabasesMatching(aCondition);
   3104 
   3105  for (const auto& database : databases) {
   3106    MOZ_ASSERT(database);
   3107 
   3108    database->RequestAllowToClose();
   3109  }
   3110 }
   3111 
   3112 void ForceKillAllDatabases() {
   3113  AssertIsOnBackgroundThread();
   3114 
   3115  nsTArray<RefPtr<Database>> databases =
   3116      CollectDatabasesMatching([](const auto&) { return true; });
   3117 
   3118  for (const auto& database : databases) {
   3119    MOZ_ASSERT(database);
   3120 
   3121    database->ForceKill();
   3122  }
   3123 }
   3124 
   3125 bool VerifyPrincipalInfo(const PrincipalInfo& aPrincipalInfo,
   3126                         const PrincipalInfo& aStoragePrincipalInfo,
   3127                         bool aCheckClientPrincipal) {
   3128  AssertIsOnBackgroundThread();
   3129 
   3130  if (NS_WARN_IF(!quota::IsPrincipalInfoValid(aPrincipalInfo))) {
   3131    return false;
   3132  }
   3133 
   3134  // Note that the client prinicpal could have a different spec than the node
   3135  // principal but they should have the same origin. It's because the client
   3136  // could be initialized when opening the initial about:blank document and pass
   3137  // to the newly opened window and reuse over there if the new window has the
   3138  // same origin as the initial about:blank document. But, the FilePath could be
   3139  // different. Therefore, we have to ignore comparing the Spec of the
   3140  // principals if we are verifying clinet principal here. Also, when
   3141  // document.domain is set, client principal won't get it. So, we don't compare
   3142  // domain for client princpal too.
   3143  bool result = aCheckClientPrincipal
   3144                    ? StoragePrincipalHelper::
   3145                          VerifyValidClientPrincipalInfoForPrincipalInfo(
   3146                              aStoragePrincipalInfo, aPrincipalInfo)
   3147                    : StoragePrincipalHelper::
   3148                          VerifyValidStoragePrincipalInfoForPrincipalInfo(
   3149                              aStoragePrincipalInfo, aPrincipalInfo);
   3150  if (NS_WARN_IF(!result)) {
   3151    return false;
   3152  }
   3153 
   3154  return true;
   3155 }
   3156 
   3157 bool VerifyClientId(const Maybe<ContentParentId>& aContentParentId,
   3158                    const Maybe<PrincipalInfo>& aPrincipalInfo,
   3159                    const Maybe<nsID>& aClientId) {
   3160  AssertIsOnBackgroundThread();
   3161 
   3162  if (gClientValidation) {
   3163    if (NS_WARN_IF(aClientId.isNothing())) {
   3164      return false;
   3165    }
   3166 
   3167    if (NS_WARN_IF(aPrincipalInfo.isNothing())) {
   3168      return false;
   3169    }
   3170 
   3171    RefPtr<ClientManagerService> svc = ClientManagerService::GetInstance();
   3172    if (svc && NS_WARN_IF(!svc->HasWindow(
   3173                   aContentParentId, aPrincipalInfo.ref(), aClientId.ref()))) {
   3174      return false;
   3175    }
   3176  }
   3177 
   3178  return true;
   3179 }
   3180 
   3181 bool VerifyOriginKey(const nsACString& aOriginKey,
   3182                     const PrincipalInfo& aPrincipalInfo) {
   3183  AssertIsOnBackgroundThread();
   3184 
   3185  QM_TRY_INSPECT((const auto& [originAttrSuffix, originKey]),
   3186                 GenerateOriginKey2(aPrincipalInfo), false);
   3187 
   3188  (void)originAttrSuffix;
   3189 
   3190  QM_TRY(OkIf(originKey == aOriginKey), false,
   3191         ([&originKey = originKey, &aOriginKey](const auto) {
   3192           LS_WARNING("originKey (%s) doesn't match passed one (%s)!",
   3193                      originKey.get(), nsCString(aOriginKey).get());
   3194         }));
   3195 
   3196  return true;
   3197 }
   3198 
   3199 LSInitializationInfo& MutableInitializationInfoRef(const CreateIfNonExistent&) {
   3200  if (!gInitializationInfo) {
   3201    gInitializationInfo = new LSInitializationInfo();
   3202  }
   3203  return *gInitializationInfo;
   3204 }
   3205 
   3206 template <typename Func>
   3207 auto ExecuteOriginInitialization(const nsACString& aOrigin,
   3208                                 const LSOriginInitialization aInitialization,
   3209                                 const nsACString& aContext, Func&& aFunc)
   3210    -> std::invoke_result_t<Func, const FirstInitializationAttempt<
   3211                                      LSOriginInitialization, Nothing>&> {
   3212  return ExecuteInitialization(
   3213      MutableInitializationInfoRef(CreateIfNonExistent{})
   3214          .MutableOriginInitializationInfoRef(aOrigin, CreateIfNonExistent{}),
   3215      aInitialization, aContext, std::forward<Func>(aFunc));
   3216 }
   3217 
   3218 }  // namespace
   3219 
   3220 /*******************************************************************************
   3221 * Exported functions
   3222 ******************************************************************************/
   3223 
   3224 void InitializeLocalStorage() {
   3225  MOZ_ASSERT(XRE_IsParentProcess());
   3226  MOZ_ASSERT(NS_IsMainThread());
   3227  MOZ_ASSERT(!gLocalStorageInitialized);
   3228 
   3229  // XXX Isn't this redundant? It's already done in InitializeQuotaManager.
   3230  if (!QuotaManager::IsRunningGTests()) {
   3231    // This service has to be started on the main thread currently.
   3232    const nsCOMPtr<mozIStorageService> ss =
   3233        do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
   3234 
   3235    QM_WARNONLY_TRY(OkIf(ss));
   3236  }
   3237 
   3238  Preferences::RegisterCallbackAndCall(ShadowWritesPrefChangedCallback,
   3239                                       kShadowWritesPref);
   3240 
   3241  Preferences::RegisterCallbackAndCall(SnapshotPrefillPrefChangedCallback,
   3242                                       kSnapshotPrefillPref);
   3243 
   3244  Preferences::RegisterCallbackAndCall(
   3245      SnapshotGradualPrefillPrefChangedCallback, kSnapshotGradualPrefillPref);
   3246 
   3247  Preferences::RegisterCallbackAndCall(ClientValidationPrefChangedCallback,
   3248                                       kClientValidationPref);
   3249 
   3250 #ifdef DEBUG
   3251  gLocalStorageInitialized = true;
   3252 #endif
   3253 }
   3254 
   3255 namespace {
   3256 
   3257 // XXX Merge these three methods into a new factory method Database::Create
   3258 
   3259 already_AddRefed<PBackgroundLSDatabaseParent> AllocPBackgroundLSDatabaseParent(
   3260    const PrincipalInfo& aPrincipalInfo, const uint32_t& aPrivateBrowsingId,
   3261    const uint64_t& aDatastoreId) {
   3262  AssertIsOnBackgroundThread();
   3263 
   3264  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) {
   3265    return nullptr;
   3266  }
   3267 
   3268  if (NS_WARN_IF(!gPreparedDatastores)) {
   3269    MOZ_ASSERT_UNLESS_FUZZING(false);
   3270    return nullptr;
   3271  }
   3272 
   3273  PreparedDatastore* preparedDatastore = gPreparedDatastores->Get(aDatastoreId);
   3274  if (NS_WARN_IF(!preparedDatastore)) {
   3275    MOZ_ASSERT_UNLESS_FUZZING(false);
   3276    return nullptr;
   3277  }
   3278 
   3279  // If we ever decide to return null from this point on, we need to make sure
   3280  // that the datastore is closed and the prepared datastore is removed from the
   3281  // gPreparedDatastores hashtable.
   3282  // We also assume that RecvCreateBackgroundLSDatabaseParent must call
   3283  // RecvPBackgroundLSDatabaseConstructor once we return a valid actor in this
   3284  // method.
   3285 
   3286  RefPtr<Database> database =
   3287      new Database(aPrincipalInfo, preparedDatastore->GetContentParentId(),
   3288                   preparedDatastore->Origin(), aPrivateBrowsingId);
   3289 
   3290  return database.forget();
   3291 }
   3292 
   3293 bool RecvPBackgroundLSDatabaseConstructor(PBackgroundLSDatabaseParent* aActor,
   3294                                          const PrincipalInfo& aPrincipalInfo,
   3295                                          const uint32_t& aPrivateBrowsingId,
   3296                                          const uint64_t& aDatastoreId) {
   3297  AssertIsOnBackgroundThread();
   3298  MOZ_ASSERT(aActor);
   3299  MOZ_ASSERT(gPreparedDatastores);
   3300  MOZ_ASSERT(gPreparedDatastores->Get(aDatastoreId));
   3301  MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
   3302 
   3303  // The actor is now completely built (it has a channel and it's registered
   3304  // as a top level protocol).
   3305  // ActorDestroy will be called if we fail here.
   3306 
   3307  mozilla::UniquePtr<PreparedDatastore> preparedDatastore;
   3308  gPreparedDatastores->Remove(aDatastoreId, &preparedDatastore);
   3309  MOZ_ASSERT(preparedDatastore);
   3310 
   3311  auto* database = static_cast<Database*>(aActor);
   3312 
   3313  database->SetActorAlive(&preparedDatastore->MutableDatastoreRef());
   3314 
   3315  // It's possible that AbortOperationsForLocks was called before the database
   3316  // actor was created and became live. Let the child know that the database is
   3317  // no longer valid.
   3318  if (preparedDatastore->IsInvalidated()) {
   3319    database->RequestAllowToClose();
   3320  }
   3321 
   3322  return true;
   3323 }
   3324 
   3325 bool RecvCreateBackgroundLSDatabaseParent(
   3326    const PrincipalInfo& aPrincipalInfo, const uint32_t& aPrivateBrowsingId,
   3327    const uint64_t& aDatastoreId,
   3328    Endpoint<PBackgroundLSDatabaseParent>&& aParentEndpoint) {
   3329  RefPtr<PBackgroundLSDatabaseParent> parent = AllocPBackgroundLSDatabaseParent(
   3330      aPrincipalInfo, aPrivateBrowsingId, aDatastoreId);
   3331  if (!parent) {
   3332    return false;
   3333  }
   3334 
   3335  // Transfer ownership to IPDL.
   3336  MOZ_ALWAYS_TRUE(aParentEndpoint.Bind(parent));
   3337 
   3338  return RecvPBackgroundLSDatabaseConstructor(parent, aPrincipalInfo,
   3339                                              aPrivateBrowsingId, aDatastoreId);
   3340 }
   3341 
   3342 }  // namespace
   3343 
   3344 PBackgroundLSObserverParent* AllocPBackgroundLSObserverParent(
   3345    const uint64_t& aObserverId) {
   3346  AssertIsOnBackgroundThread();
   3347 
   3348  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) {
   3349    return nullptr;
   3350  }
   3351 
   3352  if (NS_WARN_IF(!gPreparedObsevers)) {
   3353    MOZ_ASSERT_UNLESS_FUZZING(false);
   3354    return nullptr;
   3355  }
   3356 
   3357  RefPtr<Observer> observer = gPreparedObsevers->Get(aObserverId);
   3358  if (NS_WARN_IF(!observer)) {
   3359    MOZ_ASSERT_UNLESS_FUZZING(false);
   3360    return nullptr;
   3361  }
   3362 
   3363  // observer->SetObject(this);
   3364 
   3365  // Transfer ownership to IPDL.
   3366  return observer.forget().take();
   3367 }
   3368 
   3369 bool RecvPBackgroundLSObserverConstructor(PBackgroundLSObserverParent* aActor,
   3370                                          const uint64_t& aObserverId) {
   3371  AssertIsOnBackgroundThread();
   3372  MOZ_ASSERT(aActor);
   3373  MOZ_ASSERT(gPreparedObsevers);
   3374  MOZ_ASSERT(gPreparedObsevers->GetWeak(aObserverId));
   3375 
   3376  RefPtr<Observer> observer;
   3377  gPreparedObsevers->Remove(aObserverId, observer.StartAssignment());
   3378 
   3379  if (!gPreparedObsevers->Count()) {
   3380    gPreparedObsevers = nullptr;
   3381  }
   3382 
   3383  if (!gObservers) {
   3384    gObservers = new ObserverHashtable();
   3385  }
   3386 
   3387  const auto notNullObserver = WrapNotNull(observer.get());
   3388 
   3389  nsTArray<NotNull<Observer*>>* const array =
   3390      gObservers->GetOrInsertNew(notNullObserver->Origin());
   3391  array->AppendElement(notNullObserver);
   3392 
   3393  if (RefPtr<Datastore> datastore = GetDatastore(observer->Origin())) {
   3394    datastore->NoteChangedObserverArray(*array);
   3395  }
   3396 
   3397  return true;
   3398 }
   3399 
   3400 bool DeallocPBackgroundLSObserverParent(PBackgroundLSObserverParent* aActor) {
   3401  AssertIsOnBackgroundThread();
   3402  MOZ_ASSERT(aActor);
   3403 
   3404  // Transfer ownership back from IPDL.
   3405  RefPtr<Observer> actor = dont_AddRef(static_cast<Observer*>(aActor));
   3406 
   3407  return true;
   3408 }
   3409 
   3410 PBackgroundLSRequestParent* AllocPBackgroundLSRequestParent(
   3411    PBackgroundParent* aBackgroundActor, const LSRequestParams& aParams) {
   3412  AssertIsOnBackgroundThread();
   3413  MOZ_ASSERT(aParams.type() != LSRequestParams::T__None);
   3414 
   3415  if (NS_WARN_IF(!NextGenLocalStorageEnabled())) {
   3416    return nullptr;
   3417  }
   3418 
   3419  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) {
   3420    return nullptr;
   3421  }
   3422 
   3423  Maybe<ContentParentId> contentParentId;
   3424 
   3425  uint64_t childID = BackgroundParent::GetChildID(aBackgroundActor);
   3426  if (childID) {
   3427    contentParentId = Some(ContentParentId(childID));
   3428  }
   3429 
   3430  RefPtr<LSRequestBase> actor;
   3431 
   3432  switch (aParams.type()) {
   3433    case LSRequestParams::TLSRequestPreloadDatastoreParams:
   3434    case LSRequestParams::TLSRequestPrepareDatastoreParams: {
   3435      RefPtr<PrepareDatastoreOp> prepareDatastoreOp =
   3436          new PrepareDatastoreOp(aParams, contentParentId);
   3437 
   3438      if (!gPrepareDatastoreOps) {
   3439        gPrepareDatastoreOps = new PrepareDatastoreOpArray();
   3440      }
   3441      gPrepareDatastoreOps->AppendElement(
   3442          WrapNotNullUnchecked(prepareDatastoreOp.get()));
   3443 
   3444      actor = std::move(prepareDatastoreOp);
   3445 
   3446      break;
   3447    }
   3448 
   3449    case LSRequestParams::TLSRequestPrepareObserverParams: {
   3450      RefPtr<PrepareObserverOp> prepareObserverOp =
   3451          new PrepareObserverOp(aParams, contentParentId);
   3452 
   3453      actor = std::move(prepareObserverOp);
   3454 
   3455      break;
   3456    }
   3457 
   3458    default:
   3459      MOZ_CRASH("Should never get here!");
   3460  }
   3461 
   3462  // Transfer ownership to IPDL.
   3463  return actor.forget().take();
   3464 }
   3465 
   3466 bool RecvPBackgroundLSRequestConstructor(PBackgroundLSRequestParent* aActor,
   3467                                         const LSRequestParams& aParams) {
   3468  AssertIsOnBackgroundThread();
   3469  MOZ_ASSERT(aActor);
   3470  MOZ_ASSERT(aParams.type() != LSRequestParams::T__None);
   3471  MOZ_ASSERT(NextGenLocalStorageEnabled());
   3472  MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
   3473 
   3474  // The actor is now completely built.
   3475 
   3476  auto* op = static_cast<LSRequestBase*>(aActor);
   3477 
   3478  op->Dispatch();
   3479 
   3480  return true;
   3481 }
   3482 
   3483 bool DeallocPBackgroundLSRequestParent(PBackgroundLSRequestParent* aActor) {
   3484  AssertIsOnBackgroundThread();
   3485 
   3486  // Transfer ownership back from IPDL.
   3487  RefPtr<LSRequestBase> actor =
   3488      dont_AddRef(static_cast<LSRequestBase*>(aActor));
   3489 
   3490  return true;
   3491 }
   3492 
   3493 PBackgroundLSSimpleRequestParent* AllocPBackgroundLSSimpleRequestParent(
   3494    PBackgroundParent* aBackgroundActor, const LSSimpleRequestParams& aParams) {
   3495  AssertIsOnBackgroundThread();
   3496  MOZ_ASSERT(aParams.type() != LSSimpleRequestParams::T__None);
   3497 
   3498  if (NS_WARN_IF(!NextGenLocalStorageEnabled())) {
   3499    return nullptr;
   3500  }
   3501 
   3502  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) {
   3503    return nullptr;
   3504  }
   3505 
   3506  Maybe<ContentParentId> contentParentId;
   3507 
   3508  uint64_t childID = BackgroundParent::GetChildID(aBackgroundActor);
   3509  if (childID) {
   3510    contentParentId = Some(ContentParentId(childID));
   3511  }
   3512 
   3513  RefPtr<LSSimpleRequestBase> actor;
   3514 
   3515  switch (aParams.type()) {
   3516    case LSSimpleRequestParams::TLSSimpleRequestPreloadedParams: {
   3517      RefPtr<PreloadedOp> preloadedOp =
   3518          new PreloadedOp(aParams, contentParentId);
   3519 
   3520      actor = std::move(preloadedOp);
   3521 
   3522      break;
   3523    }
   3524 
   3525    case LSSimpleRequestParams::TLSSimpleRequestGetStateParams: {
   3526      RefPtr<GetStateOp> getStateOp = new GetStateOp(aParams, contentParentId);
   3527 
   3528      actor = std::move(getStateOp);
   3529 
   3530      break;
   3531    }
   3532 
   3533    default:
   3534      MOZ_CRASH("Should never get here!");
   3535  }
   3536 
   3537  // Transfer ownership to IPDL.
   3538  return actor.forget().take();
   3539 }
   3540 
   3541 bool RecvPBackgroundLSSimpleRequestConstructor(
   3542    PBackgroundLSSimpleRequestParent* aActor,
   3543    const LSSimpleRequestParams& aParams) {
   3544  AssertIsOnBackgroundThread();
   3545  MOZ_ASSERT(aActor);
   3546  MOZ_ASSERT(aParams.type() != LSSimpleRequestParams::T__None);
   3547  MOZ_ASSERT(NextGenLocalStorageEnabled());
   3548  MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
   3549 
   3550  // The actor is now completely built.
   3551 
   3552  auto* op = static_cast<LSSimpleRequestBase*>(aActor);
   3553 
   3554  op->Dispatch();
   3555 
   3556  return true;
   3557 }
   3558 
   3559 bool DeallocPBackgroundLSSimpleRequestParent(
   3560    PBackgroundLSSimpleRequestParent* aActor) {
   3561  AssertIsOnBackgroundThread();
   3562 
   3563  // Transfer ownership back from IPDL.
   3564  RefPtr<LSSimpleRequestBase> actor =
   3565      dont_AddRef(static_cast<LSSimpleRequestBase*>(aActor));
   3566 
   3567  return true;
   3568 }
   3569 
   3570 namespace localstorage {
   3571 
   3572 already_AddRefed<mozilla::dom::quota::Client> CreateQuotaClient() {
   3573  AssertIsOnBackgroundThread();
   3574  MOZ_ASSERT(CachedNextGenLocalStorageEnabled());
   3575 
   3576  RefPtr<QuotaClient> client = new QuotaClient();
   3577  return client.forget();
   3578 }
   3579 
   3580 }  // namespace localstorage
   3581 
   3582 /*******************************************************************************
   3583 * DatastoreWriteOptimizer
   3584 ******************************************************************************/
   3585 
   3586 void DatastoreWriteOptimizer::ApplyAndReset(
   3587    nsTArray<LSItemInfo>& aOrderedItems) {
   3588  AssertIsOnOwningThread();
   3589 
   3590  // The mWriteInfos hash table contains all write infos, but it keeps them in
   3591  // an arbitrary order, which means write infos need to be sorted before being
   3592  // processed. However, the order is not important for deletions and normal
   3593  // updates. Usually, filtering out deletions and updates would require extra
   3594  // work, but we have to check the hash table for each ordered item anyway, so
   3595  // we can remove the write info if it is a deletion or update without adding
   3596  // extra overhead. In the end, only insertions need to be sorted before being
   3597  // processed.
   3598 
   3599  if (mTruncateInfo) {
   3600    aOrderedItems.Clear();
   3601    mTruncateInfo = nullptr;
   3602  }
   3603 
   3604  for (int32_t index = aOrderedItems.Length() - 1; index >= 0; index--) {
   3605    LSItemInfo& item = aOrderedItems[index];
   3606 
   3607    if (auto entry = mWriteInfos.Lookup(item.key())) {
   3608      WriteInfo* writeInfo = entry->get();
   3609 
   3610      switch (writeInfo->GetType()) {
   3611        case WriteInfo::DeleteItem:
   3612          aOrderedItems.RemoveElementAt(index);
   3613          entry.Remove();
   3614          break;
   3615 
   3616        case WriteInfo::UpdateItem: {
   3617          auto updateItemInfo = static_cast<UpdateItemInfo*>(writeInfo);
   3618          if (updateItemInfo->UpdateWithMove()) {
   3619            // See the comment in LSWriteOptimizer::InsertItem for more details
   3620            // about the UpdateWithMove flag.
   3621 
   3622            aOrderedItems.RemoveElementAt(index);
   3623            entry.Data() = MakeUnique<InsertItemInfo>(
   3624                updateItemInfo->SerialNumber(), updateItemInfo->GetKey(),
   3625                updateItemInfo->GetValue());
   3626          } else {
   3627            item.value() = updateItemInfo->GetValue();
   3628            entry.Remove();
   3629          }
   3630          break;
   3631        }
   3632 
   3633        case WriteInfo::InsertItem:
   3634          break;
   3635 
   3636        default:
   3637          MOZ_CRASH("Bad type!");
   3638      }
   3639    }
   3640  }
   3641 
   3642  nsTArray<NotNull<WriteInfo*>> writeInfos;
   3643  GetSortedWriteInfos(writeInfos);
   3644 
   3645  for (WriteInfo* writeInfo : writeInfos) {
   3646    MOZ_ASSERT(writeInfo->GetType() == WriteInfo::InsertItem);
   3647 
   3648    auto insertItemInfo = static_cast<InsertItemInfo*>(writeInfo);
   3649 
   3650    LSItemInfo* itemInfo = aOrderedItems.AppendElement();
   3651    itemInfo->key() = insertItemInfo->GetKey();
   3652    itemInfo->value() = insertItemInfo->GetValue();
   3653  }
   3654 
   3655  mWriteInfos.Clear();
   3656 }
   3657 
   3658 /*******************************************************************************
   3659 * ConnectionWriteOptimizer
   3660 ******************************************************************************/
   3661 
   3662 Result<int64_t, nsresult> ConnectionWriteOptimizer::Perform(
   3663    Connection* aConnection, bool aShadowWrites) {
   3664  AssertIsOnGlobalConnectionThread();
   3665  MOZ_ASSERT(aConnection);
   3666 
   3667  // The order of elements is not stored in the database, so write infos don't
   3668  // need to be sorted before being processed.
   3669 
   3670  if (mTruncateInfo) {
   3671    QM_TRY(MOZ_TO_RESULT(PerformTruncate(aConnection, aShadowWrites)));
   3672  }
   3673 
   3674  for (const auto& entry : mWriteInfos) {
   3675    const WriteInfo* const writeInfo = entry.GetWeak();
   3676 
   3677    switch (writeInfo->GetType()) {
   3678      case WriteInfo::InsertItem:
   3679      case WriteInfo::UpdateItem: {
   3680        const auto* const insertItemInfo =
   3681            static_cast<const InsertItemInfo*>(writeInfo);
   3682 
   3683        QM_TRY(MOZ_TO_RESULT(PerformInsertOrUpdate(
   3684            aConnection, aShadowWrites, insertItemInfo->GetKey(),
   3685            insertItemInfo->GetValue())));
   3686 
   3687        break;
   3688      }
   3689 
   3690      case WriteInfo::DeleteItem: {
   3691        const auto* const deleteItemInfo =
   3692            static_cast<const DeleteItemInfo*>(writeInfo);
   3693 
   3694        QM_TRY(MOZ_TO_RESULT(PerformDelete(aConnection, aShadowWrites,
   3695                                           deleteItemInfo->GetKey())));
   3696 
   3697        break;
   3698      }
   3699 
   3700      default:
   3701        MOZ_CRASH("Bad type!");
   3702    }
   3703  }
   3704 
   3705  QM_TRY(MOZ_TO_RESULT(aConnection->ExecuteCachedStatement(
   3706      "UPDATE database "
   3707      "SET usage = usage + :delta"_ns,
   3708      [this](auto& stmt) -> Result<Ok, nsresult> {
   3709        QM_TRY(MOZ_TO_RESULT(stmt.BindInt64ByName("delta"_ns, mTotalDelta)));
   3710 
   3711        return Ok{};
   3712      })));
   3713 
   3714  QM_TRY_INSPECT(const auto& stmt, CreateAndExecuteSingleStepStatement<
   3715                                       SingleStepResult::ReturnNullIfNoResult>(
   3716                                       aConnection->MutableStorageConnection(),
   3717                                       "SELECT usage FROM database"_ns));
   3718 
   3719  QM_TRY(OkIf(stmt), Err(NS_ERROR_FAILURE));
   3720 
   3721  QM_TRY_RETURN(MOZ_TO_RESULT_INVOKE_MEMBER(*stmt, GetInt64, 0));
   3722 }
   3723 
   3724 nsresult ConnectionWriteOptimizer::PerformInsertOrUpdate(
   3725    Connection* aConnection, bool aShadowWrites, const nsAString& aKey,
   3726    const LSValue& aValue) {
   3727  AssertIsOnGlobalConnectionThread();
   3728  MOZ_ASSERT(aConnection);
   3729 
   3730  QM_TRY(MOZ_TO_RESULT(aConnection->ExecuteCachedStatement(
   3731      "INSERT OR REPLACE INTO data (key, utf16_length, conversion_type, "
   3732      "compression_type, value) "
   3733      "VALUES(:key, :utf16_length, :conversion_type, :compression_type, :value)"_ns,
   3734      [&aKey, &aValue](auto& stmt) -> Result<Ok, nsresult> {
   3735        QM_TRY(MOZ_TO_RESULT(stmt.BindStringByName("key"_ns, aKey)));
   3736        QM_TRY(MOZ_TO_RESULT(
   3737            stmt.BindInt32ByName("utf16_length"_ns, aValue.UTF16Length())));
   3738        QM_TRY(MOZ_TO_RESULT(stmt.BindInt32ByName(
   3739            "conversion_type"_ns,
   3740            static_cast<int32_t>(aValue.GetConversionType()))));
   3741        QM_TRY(MOZ_TO_RESULT(stmt.BindInt32ByName(
   3742            "compression_type"_ns,
   3743            static_cast<int32_t>(aValue.GetCompressionType()))));
   3744 
   3745        if (0u == aValue.Length()) {  // Otherwise empty string becomes null
   3746          QM_TRY(MOZ_TO_RESULT(
   3747              stmt.BindUTF8StringByName("value"_ns, aValue.AsCString())));
   3748        } else {
   3749          QM_TRY(MOZ_TO_RESULT(
   3750              stmt.BindUTF8StringAsBlobByName("value"_ns, aValue.AsCString())));
   3751        }
   3752 
   3753        return Ok{};
   3754      })));
   3755 
   3756  if (!aShadowWrites) {
   3757    return NS_OK;
   3758  }
   3759 
   3760  QM_TRY(MOZ_TO_RESULT(aConnection->ExecuteCachedStatement(
   3761      "INSERT OR REPLACE INTO shadow.webappsstore2 "
   3762      "(originAttributes, originKey, scope, key, value) "
   3763      "VALUES (:originAttributes, :originKey, :scope, :key, :value) "_ns,
   3764      [&aConnection, &aKey, &aValue](auto& stmt) -> Result<Ok, nsresult> {
   3765        using ConversionType = LSValue::ConversionType;
   3766        using CompressionType = LSValue::CompressionType;
   3767 
   3768        const ArchivedOriginScope* const archivedOriginScope =
   3769            aConnection->GetArchivedOriginScope();
   3770 
   3771        QM_TRY(MOZ_TO_RESULT(archivedOriginScope->BindToStatement(&stmt)));
   3772 
   3773        QM_TRY(MOZ_TO_RESULT(stmt.BindUTF8StringByName(
   3774            "scope"_ns, Scheme0Scope(archivedOriginScope->OriginSuffix(),
   3775                                     archivedOriginScope->OriginNoSuffix()))));
   3776 
   3777        QM_TRY(MOZ_TO_RESULT(stmt.BindStringByName("key"_ns, aKey)));
   3778 
   3779        bool isCompressed =
   3780            CompressionType::UNCOMPRESSED != aValue.GetCompressionType();
   3781        bool isAlreadyConverted =
   3782            ConversionType::NONE != aValue.GetConversionType();
   3783 
   3784        nsCString buffer;
   3785        const nsCString& valueBlob = aValue.AsCString();
   3786        if (isCompressed) {
   3787          QM_TRY(OkIf(SnappyUncompress(valueBlob, buffer)),
   3788                 Err(NS_ERROR_FAILURE));
   3789        }
   3790        const nsCString& value = isCompressed ? buffer : valueBlob;
   3791 
   3792        // For shadow writes, we undo buffer swap and convert destructively
   3793        nsCString unconverted;
   3794        if (!isAlreadyConverted) {
   3795          nsString converted;
   3796          QM_TRY(OkIf(PutCStringBytesToString(value, converted)),
   3797                 Err(NS_ERROR_OUT_OF_MEMORY));
   3798          QM_TRY(OkIf(CopyUTF16toUTF8(converted, unconverted, fallible)),
   3799                 Err(NS_ERROR_OUT_OF_MEMORY));  // Corrupt invalid data
   3800        }
   3801        const nsCString& untransformed =
   3802            (!isAlreadyConverted) ? unconverted : value;
   3803 
   3804        QM_TRY(MOZ_TO_RESULT(
   3805            stmt.BindUTF8StringByName("value"_ns, untransformed)));
   3806 
   3807        return Ok{};
   3808      })));
   3809 
   3810  return NS_OK;
   3811 }
   3812 
   3813 nsresult ConnectionWriteOptimizer::PerformDelete(Connection* aConnection,
   3814                                                 bool aShadowWrites,
   3815                                                 const nsAString& aKey) {
   3816  AssertIsOnGlobalConnectionThread();
   3817  MOZ_ASSERT(aConnection);
   3818 
   3819  QM_TRY(MOZ_TO_RESULT(aConnection->ExecuteCachedStatement(
   3820      "DELETE FROM data "
   3821      "WHERE key = :key;"_ns,
   3822      [&aKey](auto& stmt) -> Result<Ok, nsresult> {
   3823        QM_TRY(MOZ_TO_RESULT(stmt.BindStringByName("key"_ns, aKey)));
   3824 
   3825        return Ok{};
   3826      })));
   3827 
   3828  if (!aShadowWrites) {
   3829    return NS_OK;
   3830  }
   3831 
   3832  QM_TRY(MOZ_TO_RESULT(aConnection->ExecuteCachedStatement(
   3833      "DELETE FROM shadow.webappsstore2 "
   3834      "WHERE originAttributes = :originAttributes "
   3835      "AND originKey = :originKey "
   3836      "AND key = :key;"_ns,
   3837      [&aConnection, &aKey](auto& stmt) -> Result<Ok, nsresult> {
   3838        QM_TRY(MOZ_TO_RESULT(
   3839            aConnection->GetArchivedOriginScope()->BindToStatement(&stmt)));
   3840 
   3841        QM_TRY(MOZ_TO_RESULT(stmt.BindStringByName("key"_ns, aKey)));
   3842 
   3843        return Ok{};
   3844      })));
   3845 
   3846  return NS_OK;
   3847 }
   3848 
   3849 nsresult ConnectionWriteOptimizer::PerformTruncate(Connection* aConnection,
   3850                                                   bool aShadowWrites) {
   3851  AssertIsOnGlobalConnectionThread();
   3852  MOZ_ASSERT(aConnection);
   3853 
   3854  QM_TRY(MOZ_TO_RESULT(
   3855      aConnection->ExecuteCachedStatement("DELETE FROM data;"_ns)));
   3856 
   3857  if (!aShadowWrites) {
   3858    return NS_OK;
   3859  }
   3860 
   3861  QM_TRY(MOZ_TO_RESULT(aConnection->ExecuteCachedStatement(
   3862      "DELETE FROM shadow.webappsstore2 "
   3863      "WHERE originAttributes = :originAttributes "
   3864      "AND originKey = :originKey;"_ns,
   3865      [&aConnection](auto& stmt) -> Result<Ok, nsresult> {
   3866        QM_TRY(MOZ_TO_RESULT(
   3867            aConnection->GetArchivedOriginScope()->BindToStatement(&stmt)));
   3868 
   3869        return Ok{};
   3870      })));
   3871 
   3872  return NS_OK;
   3873 }
   3874 
   3875 /*******************************************************************************
   3876 * DatastoreOperationBase
   3877 ******************************************************************************/
   3878 
   3879 /*******************************************************************************
   3880 * ConnectionDatastoreOperationBase
   3881 ******************************************************************************/
   3882 
   3883 ConnectionDatastoreOperationBase::ConnectionDatastoreOperationBase(
   3884    Connection* aConnection, bool aEnsureStorageConnection)
   3885    : mConnection(aConnection),
   3886      mEnsureStorageConnection(aEnsureStorageConnection) {
   3887  MOZ_ASSERT(aConnection);
   3888 }
   3889 
   3890 ConnectionDatastoreOperationBase::~ConnectionDatastoreOperationBase() {
   3891  MOZ_ASSERT(!mConnection,
   3892             "ConnectionDatabaseOperationBase::Cleanup() was not called by a "
   3893             "subclass!");
   3894 }
   3895 
   3896 void ConnectionDatastoreOperationBase::Cleanup() {
   3897  AssertIsOnOwningThread();
   3898  MOZ_ASSERT(mConnection);
   3899 
   3900  mConnection = nullptr;
   3901 
   3902  NoteComplete();
   3903 }
   3904 
   3905 void ConnectionDatastoreOperationBase::OnSuccess() { AssertIsOnOwningThread(); }
   3906 
   3907 void ConnectionDatastoreOperationBase::OnFailure(nsresult aResultCode) {
   3908  AssertIsOnOwningThread();
   3909  MOZ_ASSERT(NS_FAILED(aResultCode));
   3910 }
   3911 
   3912 void ConnectionDatastoreOperationBase::RunOnConnectionThread() {
   3913  AssertIsOnGlobalConnectionThread();
   3914  MOZ_ASSERT(mConnection);
   3915  MOZ_ASSERT(NS_SUCCEEDED(ResultCode()));
   3916 
   3917  if (!MayProceedOnNonOwningThread()) {
   3918    SetFailureCode(NS_ERROR_ABORT);
   3919  } else {
   3920    nsresult rv = NS_OK;
   3921 
   3922    // The boolean flag is only used by the CloseOp to avoid creating empty
   3923    // databases.
   3924    if (mEnsureStorageConnection) {
   3925      rv = mConnection->EnsureStorageConnection();
   3926      if (NS_WARN_IF(NS_FAILED(rv))) {
   3927        SetFailureCode(rv);
   3928      } else {
   3929        MOZ_ASSERT(mConnection->HasStorageConnection());
   3930      }
   3931    }
   3932 
   3933    if (NS_SUCCEEDED(rv)) {
   3934      rv = DoDatastoreWork();
   3935      if (NS_FAILED(rv)) {
   3936        SetFailureCode(rv);
   3937      }
   3938    }
   3939  }
   3940 
   3941  MOZ_ALWAYS_SUCCEEDS(OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
   3942 }
   3943 
   3944 void ConnectionDatastoreOperationBase::RunOnOwningThread() {
   3945  AssertIsOnOwningThread();
   3946  MOZ_ASSERT(mConnection);
   3947 
   3948  if (!MayProceed()) {
   3949    MaybeSetFailureCode(NS_ERROR_ABORT);
   3950  }
   3951 
   3952  if (NS_SUCCEEDED(ResultCode())) {
   3953    OnSuccess();
   3954  } else {
   3955    OnFailure(ResultCode());
   3956  }
   3957 
   3958  Cleanup();
   3959 }
   3960 
   3961 NS_IMETHODIMP
   3962 ConnectionDatastoreOperationBase::Run() {
   3963  if (IsOnGlobalConnectionThread()) {
   3964    RunOnConnectionThread();
   3965  } else {
   3966    RunOnOwningThread();
   3967  }
   3968 
   3969  return NS_OK;
   3970 }
   3971 
   3972 /*******************************************************************************
   3973 * Connection implementation
   3974 ******************************************************************************/
   3975 
   3976 Connection::Connection(ConnectionThread* aConnectionThread,
   3977                       const OriginMetadata& aOriginMetadata,
   3978                       UniquePtr<ArchivedOriginScope>&& aArchivedOriginScope,
   3979                       bool aDatabaseWasNotAvailable)
   3980    : mConnectionThread(aConnectionThread),
   3981      mQuotaClient(QuotaClient::GetInstance()),
   3982      mArchivedOriginScope(std::move(aArchivedOriginScope)),
   3983      mOriginMetadata(aOriginMetadata),
   3984      mDatabaseWasNotAvailable(aDatabaseWasNotAvailable),
   3985      mHasCreatedDatabase(false),
   3986      mFlushScheduled(false)
   3987 #ifdef DEBUG
   3988      ,
   3989      mInUpdateBatch(false),
   3990      mFinished(false)
   3991 #endif
   3992 {
   3993  AssertIsOnOwningThread();
   3994  MOZ_ASSERT(!aOriginMetadata.mGroup.IsEmpty());
   3995  MOZ_ASSERT(!aOriginMetadata.mOrigin.IsEmpty());
   3996 }
   3997 
   3998 Connection::~Connection() {
   3999  AssertIsOnOwningThread();
   4000  MOZ_ASSERT(!mFlushScheduled);
   4001  MOZ_ASSERT(!mInUpdateBatch);
   4002  MOZ_ASSERT(mFinished);
   4003 }
   4004 
   4005 void Connection::Dispatch(ConnectionDatastoreOperationBase* aOp) {
   4006  AssertIsOnOwningThread();
   4007  MOZ_ASSERT(mConnectionThread);
   4008 
   4009  MOZ_ALWAYS_SUCCEEDS(
   4010      mConnectionThread->mThread->Dispatch(aOp, NS_DISPATCH_NORMAL));
   4011 }
   4012 
   4013 void Connection::Close(nsIRunnable* aCallback) {
   4014  AssertIsOnOwningThread();
   4015  MOZ_ASSERT(aCallback);
   4016 
   4017  if (mFlushScheduled) {
   4018    MOZ_ASSERT(mFlushTimer);
   4019    MOZ_ALWAYS_SUCCEEDS(mFlushTimer->Cancel());
   4020 
   4021    Flush();
   4022 
   4023    mFlushTimer = nullptr;
   4024  }
   4025 
   4026  RefPtr<CloseOp> op = new CloseOp(this, aCallback);
   4027 
   4028  Dispatch(op);
   4029 }
   4030 
   4031 void Connection::SetItem(const nsString& aKey, const LSValue& aValue,
   4032                         int64_t aDelta, bool aIsNewItem) {
   4033  AssertIsOnOwningThread();
   4034  MOZ_ASSERT(mInUpdateBatch);
   4035 
   4036  if (aIsNewItem) {
   4037    mWriteOptimizer.InsertItem(aKey, aValue, aDelta);
   4038  } else {
   4039    mWriteOptimizer.UpdateItem(aKey, aValue, aDelta);
   4040  }
   4041 }
   4042 
   4043 void Connection::RemoveItem(const nsString& aKey, int64_t aDelta) {
   4044  AssertIsOnOwningThread();
   4045  MOZ_ASSERT(mInUpdateBatch);
   4046 
   4047  mWriteOptimizer.DeleteItem(aKey, aDelta);
   4048 }
   4049 
   4050 void Connection::Clear(int64_t aDelta) {
   4051  AssertIsOnOwningThread();
   4052  MOZ_ASSERT(mInUpdateBatch);
   4053 
   4054  mWriteOptimizer.Truncate(aDelta);
   4055 }
   4056 
   4057 void Connection::BeginUpdateBatch() {
   4058  AssertIsOnOwningThread();
   4059  MOZ_ASSERT(!mInUpdateBatch);
   4060 
   4061 #ifdef DEBUG
   4062  mInUpdateBatch = true;
   4063 #endif
   4064 }
   4065 
   4066 void Connection::EndUpdateBatch() {
   4067  AssertIsOnOwningThread();
   4068  MOZ_ASSERT(mInUpdateBatch);
   4069 
   4070  if (mWriteOptimizer.HasWrites() && !mFlushScheduled) {
   4071    ScheduleFlush();
   4072  }
   4073 
   4074 #ifdef DEBUG
   4075  mInUpdateBatch = false;
   4076 #endif
   4077 }
   4078 
   4079 nsresult Connection::EnsureStorageConnection() {
   4080  AssertIsOnGlobalConnectionThread();
   4081 
   4082  if (HasStorageConnection()) {
   4083    return NS_OK;
   4084  }
   4085 
   4086  QuotaManager* quotaManager = QuotaManager::Get();
   4087  MOZ_ASSERT(quotaManager);
   4088 
   4089  if (!mDatabaseWasNotAvailable || mHasCreatedDatabase) {
   4090    MOZ_ASSERT(mOriginMetadata.mPersistenceType == PERSISTENCE_TYPE_DEFAULT);
   4091 
   4092    QM_TRY_INSPECT(const auto& directoryEntry,
   4093                   quotaManager->GetOriginDirectory(mOriginMetadata));
   4094 
   4095    QM_TRY(MOZ_TO_RESULT(directoryEntry->Append(
   4096        NS_LITERAL_STRING_FROM_CSTRING(LS_DIRECTORY_NAME))));
   4097 
   4098    QM_TRY(MOZ_TO_RESULT(directoryEntry->GetPath(mDirectoryPath)));
   4099    QM_TRY(MOZ_TO_RESULT(directoryEntry->Append(kDataFileName)));
   4100 
   4101    QM_TRY_INSPECT(
   4102        const auto& databaseFilePath,
   4103        MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsString, directoryEntry, GetPath));
   4104 
   4105    QM_TRY_UNWRAP(auto storageConnection,
   4106                  GetStorageConnection(databaseFilePath));
   4107    LazyInit(WrapMovingNotNull(std::move(storageConnection)));
   4108 
   4109    return NS_OK;
   4110  }
   4111 
   4112  auto helper =
   4113      MakeRefPtr<GetOrCreateTemporaryOriginDirectoryHelper>(mOriginMetadata);
   4114 
   4115  QM_TRY_INSPECT(const auto& originDirectoryPath,
   4116                 helper->BlockAndReturnOriginDirectoryPath());
   4117 
   4118  QM_TRY_INSPECT(const auto& directoryEntry,
   4119                 QM_NewLocalFile(originDirectoryPath));
   4120 
   4121  QM_TRY(MOZ_TO_RESULT(directoryEntry->Append(
   4122      NS_LITERAL_STRING_FROM_CSTRING(LS_DIRECTORY_NAME))));
   4123 
   4124  QM_TRY(MOZ_TO_RESULT(directoryEntry->GetPath(mDirectoryPath)));
   4125 
   4126  QM_TRY_INSPECT(const bool& exists,
   4127                 MOZ_TO_RESULT_INVOKE_MEMBER(directoryEntry, Exists));
   4128 
   4129  if (!exists) {
   4130    QM_TRY(
   4131        MOZ_TO_RESULT(directoryEntry->Create(nsIFile::DIRECTORY_TYPE, 0755)));
   4132  }
   4133 
   4134  QM_TRY(MOZ_TO_RESULT(directoryEntry->Append(kDataFileName)));
   4135 
   4136 #ifdef DEBUG
   4137  {
   4138    QM_TRY_INSPECT(const bool& exists,
   4139                   MOZ_TO_RESULT_INVOKE_MEMBER(directoryEntry, Exists));
   4140 
   4141    MOZ_ASSERT(!exists);
   4142  }
   4143 #endif
   4144 
   4145  QM_TRY_INSPECT(const auto& usageFile, GetUsageFile(mDirectoryPath));
   4146 
   4147  nsCOMPtr<mozIStorageConnection> storageConnection;
   4148 
   4149  auto autoRemove = MakeScopeExit([&storageConnection, &directoryEntry] {
   4150    if (storageConnection) {
   4151      MOZ_ALWAYS_SUCCEEDS(storageConnection->Close());
   4152    }
   4153 
   4154    nsresult rv = directoryEntry->Remove(false);
   4155    if (rv != NS_ERROR_FILE_NOT_FOUND && NS_FAILED(rv)) {
   4156      NS_WARNING("Failed to remove database file!");
   4157    }
   4158  });
   4159 
   4160  QM_TRY_UNWRAP(storageConnection, CreateStorageConnectionWithRecovery(
   4161                                       *directoryEntry, *usageFile, Origin(),
   4162                                       [] { MOZ_ASSERT_UNREACHABLE(); }));
   4163 
   4164  MOZ_ASSERT(mQuotaClient);
   4165 
   4166  MutexAutoLock shadowDatabaseLock(mQuotaClient->ShadowDatabaseMutex());
   4167 
   4168  nsCOMPtr<mozIStorageConnection> shadowConnection;
   4169  if (!gInitializedShadowStorage) {
   4170    QM_TRY_UNWRAP(shadowConnection,
   4171                  CreateShadowStorageConnection(quotaManager->GetBasePath()));
   4172 
   4173    gInitializedShadowStorage = true;
   4174  }
   4175 
   4176  autoRemove.release();
   4177 
   4178  if (!mHasCreatedDatabase) {
   4179    mHasCreatedDatabase = true;
   4180  }
   4181 
   4182  LazyInit(WrapMovingNotNull(std::move(storageConnection)));
   4183 
   4184  return NS_OK;
   4185 }
   4186 
   4187 void Connection::CloseStorageConnection() {
   4188  AssertIsOnGlobalConnectionThread();
   4189 
   4190  CachingDatabaseConnection::Close();
   4191 }
   4192 
   4193 nsresult Connection::BeginWriteTransaction() {
   4194  AssertIsOnGlobalConnectionThread();
   4195  MOZ_ASSERT(HasStorageConnection());
   4196 
   4197  QM_TRY(MOZ_TO_RESULT(ExecuteCachedStatement("BEGIN IMMEDIATE;"_ns)));
   4198 
   4199  return NS_OK;
   4200 }
   4201 
   4202 nsresult Connection::CommitWriteTransaction() {
   4203  AssertIsOnGlobalConnectionThread();
   4204  MOZ_ASSERT(HasStorageConnection());
   4205 
   4206  QM_TRY(MOZ_TO_RESULT(ExecuteCachedStatement("COMMIT;"_ns)));
   4207 
   4208  return NS_OK;
   4209 }
   4210 
   4211 nsresult Connection::RollbackWriteTransaction() {
   4212  AssertIsOnGlobalConnectionThread();
   4213  MOZ_ASSERT(HasStorageConnection());
   4214 
   4215  QM_TRY_INSPECT(const auto& stmt, BorrowCachedStatement("ROLLBACK;"_ns));
   4216 
   4217  // This may fail if SQLite already rolled back the transaction so ignore any
   4218  // errors.
   4219  (void)stmt->Execute();
   4220 
   4221  return NS_OK;
   4222 }
   4223 
   4224 void Connection::ScheduleFlush() {
   4225  AssertIsOnOwningThread();
   4226  MOZ_ASSERT(mWriteOptimizer.HasWrites());
   4227  MOZ_ASSERT(!mFlushScheduled);
   4228 
   4229  if (!mFlushTimer) {
   4230    mFlushTimer = NS_NewTimer();
   4231    MOZ_ASSERT(mFlushTimer);
   4232  }
   4233 
   4234  MOZ_ALWAYS_SUCCEEDS(mFlushTimer->InitWithNamedFuncCallback(
   4235      FlushTimerCallback, this, kFlushTimeoutMs, nsITimer::TYPE_ONE_SHOT,
   4236      "Connection::FlushTimerCallback"_ns));
   4237 
   4238  mFlushScheduled = true;
   4239 }
   4240 
   4241 void Connection::Flush() {
   4242  AssertIsOnOwningThread();
   4243  MOZ_ASSERT(mFlushScheduled);
   4244 
   4245  if (mWriteOptimizer.HasWrites()) {
   4246    RefPtr<FlushOp> op = new FlushOp(this, std::move(mWriteOptimizer));
   4247 
   4248    Dispatch(op);
   4249  }
   4250 
   4251  mFlushScheduled = false;
   4252 }
   4253 
   4254 // static
   4255 void Connection::FlushTimerCallback(nsITimer* aTimer, void* aClosure) {
   4256  MOZ_ASSERT(aClosure);
   4257 
   4258  auto* self = static_cast<Connection*>(aClosure);
   4259  MOZ_ASSERT(self);
   4260  MOZ_ASSERT(self->mFlushScheduled);
   4261 
   4262  self->Flush();
   4263 }
   4264 
   4265 Result<nsString, nsresult>
   4266 Connection::GetOrCreateTemporaryOriginDirectoryHelper::
   4267    BlockAndReturnOriginDirectoryPath() {
   4268  AssertIsOnGlobalConnectionThread();
   4269 
   4270  QuotaManager* quotaManager = QuotaManager::Get();
   4271  MOZ_ASSERT(quotaManager);
   4272 
   4273  MOZ_ALWAYS_SUCCEEDS(
   4274      quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL));
   4275 
   4276  mozilla::MonitorAutoLock lock(mMonitor);
   4277  while (mWaiting) {
   4278    lock.Wait();
   4279  }
   4280 
   4281  QM_TRY(MOZ_TO_RESULT(mIOThreadResultCode));
   4282 
   4283  return mOriginDirectoryPath;
   4284 }
   4285 
   4286 nsresult
   4287 Connection::GetOrCreateTemporaryOriginDirectoryHelper::RunOnIOThread() {
   4288  AssertIsOnIOThread();
   4289 
   4290  QuotaManager* quotaManager = QuotaManager::Get();
   4291  MOZ_ASSERT(quotaManager);
   4292 
   4293  QM_TRY_INSPECT(
   4294      const auto& directoryEntry,
   4295      quotaManager->GetOrCreateTemporaryOriginDirectory(mOriginMetadata));
   4296 
   4297  QM_TRY(MOZ_TO_RESULT(directoryEntry->GetPath(mOriginDirectoryPath)));
   4298 
   4299  return NS_OK;
   4300 }
   4301 
   4302 NS_IMETHODIMP
   4303 Connection::GetOrCreateTemporaryOriginDirectoryHelper::Run() {
   4304  AssertIsOnIOThread();
   4305 
   4306  nsresult rv = RunOnIOThread();
   4307  if (NS_WARN_IF(NS_FAILED(rv))) {
   4308    mIOThreadResultCode = rv;
   4309  }
   4310 
   4311  mozilla::MonitorAutoLock lock(mMonitor);
   4312  MOZ_ASSERT(mWaiting);
   4313 
   4314  mWaiting = false;
   4315  lock.Notify();
   4316 
   4317  return NS_OK;
   4318 }
   4319 
   4320 Connection::FlushOp::FlushOp(Connection* aConnection,
   4321                             ConnectionWriteOptimizer&& aWriteOptimizer)
   4322    : ConnectionDatastoreOperationBase(aConnection),
   4323      mWriteOptimizer(std::move(aWriteOptimizer)),
   4324      mShadowWrites(gShadowWrites) {}
   4325 
   4326 nsresult Connection::FlushOp::DoDatastoreWork() {
   4327  AssertIsOnGlobalConnectionThread();
   4328  MOZ_ASSERT(mConnection);
   4329 
   4330  AutoWriteTransaction autoWriteTransaction(mShadowWrites);
   4331 
   4332  QM_TRY(MOZ_TO_RESULT(autoWriteTransaction.Start(mConnection)));
   4333 
   4334  QM_TRY_INSPECT(const int64_t& usage,
   4335                 mWriteOptimizer.Perform(mConnection, mShadowWrites));
   4336 
   4337  QM_TRY_INSPECT(const auto& usageFile,
   4338                 GetUsageFile(mConnection->DirectoryPath()));
   4339 
   4340  QM_TRY_INSPECT(const auto& usageJournalFile,
   4341                 GetUsageJournalFile(mConnection->DirectoryPath()));
   4342 
   4343  QM_TRY(MOZ_TO_RESULT(UpdateUsageFile(usageFile, usageJournalFile, usage)));
   4344 
   4345  QM_TRY(MOZ_TO_RESULT(autoWriteTransaction.Commit()));
   4346 
   4347  QM_TRY(MOZ_TO_RESULT(usageJournalFile->Remove(false)));
   4348 
   4349  return NS_OK;
   4350 }
   4351 
   4352 void Connection::FlushOp::Cleanup() {
   4353  AssertIsOnOwningThread();
   4354 
   4355  mWriteOptimizer.Reset();
   4356 
   4357  MOZ_ASSERT(!mWriteOptimizer.HasWrites());
   4358 
   4359  ConnectionDatastoreOperationBase::Cleanup();
   4360 }
   4361 
   4362 nsresult Connection::CloseOp::DoDatastoreWork() {
   4363  AssertIsOnGlobalConnectionThread();
   4364  MOZ_ASSERT(mConnection);
   4365 
   4366  if (mConnection->HasStorageConnection()) {
   4367    mConnection->CloseStorageConnection();
   4368  }
   4369 
   4370  return NS_OK;
   4371 }
   4372 
   4373 void Connection::CloseOp::Cleanup() {
   4374  AssertIsOnOwningThread();
   4375  MOZ_ASSERT(mConnection);
   4376 
   4377  mConnection->mConnectionThread->mConnections.Remove(mConnection->Origin());
   4378 
   4379 #ifdef DEBUG
   4380  MOZ_ASSERT(!mConnection->mFinished);
   4381  mConnection->mFinished = true;
   4382 #endif
   4383 
   4384  nsCOMPtr<nsIRunnable> callback;
   4385  mCallback.swap(callback);
   4386 
   4387  callback->Run();
   4388 
   4389  ConnectionDatastoreOperationBase::Cleanup();
   4390 }
   4391 
   4392 /*******************************************************************************
   4393 * ConnectionThread implementation
   4394 ******************************************************************************/
   4395 
   4396 ConnectionThread::ConnectionThread() {
   4397  AssertIsOnOwningThread();
   4398  AssertIsOnBackgroundThread();
   4399 
   4400  MOZ_ALWAYS_SUCCEEDS(NS_NewNamedThread("LS Thread", getter_AddRefs(mThread)));
   4401 }
   4402 
   4403 ConnectionThread::~ConnectionThread() {
   4404  AssertIsOnOwningThread();
   4405  MOZ_ASSERT(!mConnections.Count());
   4406 }
   4407 
   4408 bool ConnectionThread::IsOnConnectionThread() {
   4409  MOZ_ASSERT(mThread);
   4410 
   4411  bool current;
   4412  return NS_SUCCEEDED(mThread->IsOnCurrentThread(&current)) && current;
   4413 }
   4414 
   4415 void ConnectionThread::AssertIsOnConnectionThread() {
   4416  MOZ_ASSERT(IsOnConnectionThread());
   4417 }
   4418 
   4419 already_AddRefed<Connection> ConnectionThread::CreateConnection(
   4420    const OriginMetadata& aOriginMetadata,
   4421    UniquePtr<ArchivedOriginScope>&& aArchivedOriginScope,
   4422    bool aDatabaseWasNotAvailable) {
   4423  AssertIsOnOwningThread();
   4424  MOZ_ASSERT(!aOriginMetadata.mOrigin.IsEmpty());
   4425  MOZ_DIAGNOSTIC_ASSERT(!mConnections.Contains(aOriginMetadata.mOrigin));
   4426 
   4427  RefPtr<Connection> connection =
   4428      new Connection(this, aOriginMetadata, std::move(aArchivedOriginScope),
   4429                     aDatabaseWasNotAvailable);
   4430  mConnections.InsertOrUpdate(aOriginMetadata.mOrigin, RefPtr{connection});
   4431 
   4432  return connection.forget();
   4433 }
   4434 
   4435 void ConnectionThread::Shutdown() {
   4436  AssertIsOnOwningThread();
   4437  MOZ_ASSERT(mThread);
   4438 
   4439  mThread->Shutdown();
   4440 }
   4441 
   4442 /*******************************************************************************
   4443 * Datastore
   4444 ******************************************************************************/
   4445 
   4446 Datastore::Datastore(const OriginMetadata& aOriginMetadata,
   4447                     uint32_t aPrivateBrowsingId, int64_t aUsage,
   4448                     int64_t aSizeOfKeys, int64_t aSizeOfItems,
   4449                     ClientDirectoryLockHandle&& aDirectoryLockHandle,
   4450                     RefPtr<Connection>&& aConnection,
   4451                     RefPtr<QuotaObject>&& aQuotaObject,
   4452                     nsTHashMap<nsStringHashKey, LSValue>& aValues,
   4453                     nsTArray<LSItemInfo>&& aOrderedItems)
   4454    : mDirectoryLockHandle(std::move(aDirectoryLockHandle)),
   4455      mConnection(std::move(aConnection)),
   4456      mQuotaObject(std::move(aQuotaObject)),
   4457      mOrderedItems(std::move(aOrderedItems)),
   4458      mOriginMetadata(aOriginMetadata),
   4459      mPrivateBrowsingId(aPrivateBrowsingId),
   4460      mUsage(aUsage),
   4461      mUpdateBatchUsage(-1),
   4462      mSizeOfKeys(aSizeOfKeys),
   4463      mSizeOfItems(aSizeOfItems),
   4464      mClosed(false),
   4465      mInUpdateBatch(false),
   4466      mHasLivePrivateDatastore(false) {
   4467  AssertIsOnBackgroundThread();
   4468 
   4469  mValues.SwapElements(aValues);
   4470 }
   4471 
   4472 Datastore::~Datastore() {
   4473  AssertIsOnBackgroundThread();
   4474  MOZ_ASSERT(mClosed);
   4475 }
   4476 
   4477 void Datastore::Close() {
   4478  AssertIsOnBackgroundThread();
   4479  MOZ_DIAGNOSTIC_ASSERT(!mClosed);
   4480  MOZ_ASSERT(!mPrepareDatastoreOps.Count());
   4481  MOZ_ASSERT(!mPreparedDatastores.Count());
   4482  MOZ_ASSERT(!mDatabases.Count());
   4483  MOZ_ASSERT(mDirectoryLockHandle);
   4484 
   4485  mClosed = true;
   4486 
   4487  if (IsPersistent()) {
   4488    MOZ_ASSERT(mConnection);
   4489    MOZ_ASSERT(mQuotaObject);
   4490 
   4491    // We can't release the directory lock and unregister itself from the
   4492    // hashtable until the connection is fully closed.
   4493    nsCOMPtr<nsIRunnable> callback =
   4494        NewRunnableMethod("dom::Datastore::ConnectionClosedCallback", this,
   4495                          &Datastore::ConnectionClosedCallback);
   4496    mConnection->Close(callback);
   4497  } else {
   4498    MOZ_ASSERT(!mConnection);
   4499    MOZ_ASSERT(!mQuotaObject);
   4500 
   4501    // There's no connection, so it's safe to release the directory lock and
   4502    // unregister itself from the hashtable.
   4503 
   4504    {
   4505      auto destroyingDirectoryLockHandle = std::move(mDirectoryLockHandle);
   4506    }
   4507 
   4508    CleanupMetadata();
   4509  }
   4510 }
   4511 
   4512 void Datastore::WaitForConnectionToComplete(nsIRunnable* aCallback) {
   4513  AssertIsOnBackgroundThread();
   4514  MOZ_ASSERT(aCallback);
   4515  MOZ_ASSERT(!mCompleteCallback);
   4516  MOZ_ASSERT(mClosed);
   4517 
   4518  mCompleteCallback = aCallback;
   4519 }
   4520 
   4521 void Datastore::NoteLivePrepareDatastoreOp(
   4522    PrepareDatastoreOp* aPrepareDatastoreOp) {
   4523  AssertIsOnBackgroundThread();
   4524  MOZ_ASSERT(aPrepareDatastoreOp);
   4525  MOZ_ASSERT(!mPrepareDatastoreOps.Contains(aPrepareDatastoreOp));
   4526  MOZ_ASSERT(mDirectoryLockHandle);
   4527  MOZ_ASSERT(!mClosed);
   4528 
   4529  mPrepareDatastoreOps.Insert(aPrepareDatastoreOp);
   4530 }
   4531 
   4532 void Datastore::NoteFinishedPrepareDatastoreOp(
   4533    PrepareDatastoreOp* aPrepareDatastoreOp) {
   4534  AssertIsOnBackgroundThread();
   4535  MOZ_ASSERT(aPrepareDatastoreOp);
   4536  MOZ_ASSERT(mPrepareDatastoreOps.Contains(aPrepareDatastoreOp));
   4537  MOZ_ASSERT(mDirectoryLockHandle);
   4538  MOZ_ASSERT(!mClosed);
   4539 
   4540  mPrepareDatastoreOps.Remove(aPrepareDatastoreOp);
   4541 
   4542  QuotaManager::MaybeRecordQuotaClientShutdownStep(
   4543      quota::Client::LS, "PrepareDatastoreOp finished"_ns);
   4544 
   4545  MaybeClose();
   4546 }
   4547 
   4548 void Datastore::NoteLivePrivateDatastore() {
   4549  AssertIsOnBackgroundThread();
   4550  MOZ_ASSERT(!mHasLivePrivateDatastore);
   4551  MOZ_ASSERT(mDirectoryLockHandle);
   4552  MOZ_ASSERT(!mClosed);
   4553 
   4554  mHasLivePrivateDatastore = true;
   4555 }
   4556 
   4557 void Datastore::NoteFinishedPrivateDatastore() {
   4558  AssertIsOnBackgroundThread();
   4559  MOZ_ASSERT(mHasLivePrivateDatastore);
   4560  MOZ_ASSERT(mDirectoryLockHandle);
   4561  MOZ_ASSERT(!mClosed);
   4562 
   4563  mHasLivePrivateDatastore = false;
   4564 
   4565  QuotaManager::MaybeRecordQuotaClientShutdownStep(
   4566      quota::Client::LS, "PrivateDatastore finished"_ns);
   4567 
   4568  MaybeClose();
   4569 }
   4570 
   4571 void Datastore::NoteLivePreparedDatastore(
   4572    PreparedDatastore* aPreparedDatastore) {
   4573  AssertIsOnBackgroundThread();
   4574  MOZ_ASSERT(aPreparedDatastore);
   4575  MOZ_ASSERT(!mPreparedDatastores.Contains(aPreparedDatastore));
   4576  MOZ_ASSERT(mDirectoryLockHandle);
   4577  MOZ_ASSERT(!mClosed);
   4578 
   4579  mPreparedDatastores.Insert(aPreparedDatastore);
   4580 }
   4581 
   4582 void Datastore::NoteFinishedPreparedDatastore(
   4583    PreparedDatastore* aPreparedDatastore) {
   4584  AssertIsOnBackgroundThread();
   4585  MOZ_ASSERT(aPreparedDatastore);
   4586  MOZ_ASSERT(mPreparedDatastores.Contains(aPreparedDatastore));
   4587  MOZ_ASSERT(mDirectoryLockHandle);
   4588  MOZ_ASSERT(!mClosed);
   4589 
   4590  mPreparedDatastores.Remove(aPreparedDatastore);
   4591 
   4592  QuotaManager::MaybeRecordQuotaClientShutdownStep(
   4593      quota::Client::LS, "PreparedDatastore finished"_ns);
   4594 
   4595  MaybeClose();
   4596 }
   4597 
   4598 bool Datastore::HasOtherProcessDatabases(Database* aDatabase) {
   4599  AssertIsOnBackgroundThread();
   4600 
   4601  for (Database* database : mDatabases) {
   4602    if (database->ContentParentIdRef() != aDatabase->ContentParentIdRef()) {
   4603      return true;
   4604    }
   4605  }
   4606 
   4607  return false;
   4608 }
   4609 
   4610 void Datastore::NoteLiveDatabase(Database* aDatabase) {
   4611  AssertIsOnBackgroundThread();
   4612  MOZ_ASSERT(aDatabase);
   4613  MOZ_ASSERT(!mDatabases.Contains(aDatabase));
   4614  MOZ_ASSERT(mDirectoryLockHandle);
   4615  MOZ_ASSERT(!mClosed);
   4616 
   4617  mDatabases.Insert(aDatabase);
   4618 
   4619  NoteChangedDatabaseMap();
   4620 }
   4621 
   4622 void Datastore::NoteFinishedDatabase(Database* aDatabase) {
   4623  AssertIsOnBackgroundThread();
   4624  MOZ_ASSERT(aDatabase);
   4625  MOZ_ASSERT(mDatabases.Contains(aDatabase));
   4626  MOZ_ASSERT(!mActiveDatabases.Contains(aDatabase));
   4627  MOZ_ASSERT(mDirectoryLockHandle);
   4628  MOZ_ASSERT(!mClosed);
   4629 
   4630  mDatabases.Remove(aDatabase);
   4631 
   4632  NoteChangedDatabaseMap();
   4633 
   4634  QuotaManager::MaybeRecordQuotaClientShutdownStep(quota::Client::LS,
   4635                                                   "Database finished"_ns);
   4636 
   4637  MaybeClose();
   4638 }
   4639 
   4640 void Datastore::NoteActiveDatabase(Database* aDatabase) {
   4641  AssertIsOnBackgroundThread();
   4642  MOZ_ASSERT(aDatabase);
   4643  MOZ_ASSERT(mDatabases.Contains(aDatabase));
   4644  MOZ_ASSERT(!mActiveDatabases.Contains(aDatabase));
   4645  MOZ_ASSERT(!mClosed);
   4646 
   4647  mActiveDatabases.Insert(aDatabase);
   4648 }
   4649 
   4650 void Datastore::NoteInactiveDatabase(Database* aDatabase) {
   4651  AssertIsOnBackgroundThread();
   4652  MOZ_ASSERT(aDatabase);
   4653  MOZ_ASSERT(mDatabases.Contains(aDatabase));
   4654  MOZ_ASSERT(mActiveDatabases.Contains(aDatabase));
   4655  MOZ_ASSERT(!mClosed);
   4656 
   4657  mActiveDatabases.Remove(aDatabase);
   4658 
   4659  if (!mActiveDatabases.Count() && mPendingUsageDeltas.Length()) {
   4660    int64_t finalDelta = 0;
   4661 
   4662    for (auto delta : mPendingUsageDeltas) {
   4663      finalDelta += delta;
   4664    }
   4665 
   4666    MOZ_ASSERT(finalDelta <= 0);
   4667 
   4668    if (finalDelta != 0) {
   4669      DebugOnly<bool> ok = UpdateUsage(finalDelta);
   4670      MOZ_ASSERT(ok);
   4671    }
   4672 
   4673    mPendingUsageDeltas.Clear();
   4674  }
   4675 }
   4676 
   4677 void Datastore::GetSnapshotLoadInfo(const nsAString& aKey,
   4678                                    bool& aAddKeyToUnknownItems,
   4679                                    nsTHashtable<nsStringHashKey>& aLoadedItems,
   4680                                    nsTArray<LSItemInfo>& aItemInfos,
   4681                                    uint32_t& aNextLoadIndex,
   4682                                    LSSnapshot::LoadState& aLoadState) {
   4683  AssertIsOnBackgroundThread();
   4684  MOZ_ASSERT(!mClosed);
   4685  MOZ_ASSERT(!mInUpdateBatch);
   4686 
   4687 #ifdef DEBUG
   4688  int64_t sizeOfKeys = 0;
   4689  int64_t sizeOfItems = 0;
   4690  for (auto item : mOrderedItems) {
   4691    int64_t sizeOfKey = static_cast<int64_t>(item.key().Length());
   4692    sizeOfKeys += sizeOfKey;
   4693    sizeOfItems += sizeOfKey + static_cast<int64_t>(item.value().Length());
   4694  }
   4695  MOZ_ASSERT(mSizeOfKeys == sizeOfKeys);
   4696  MOZ_ASSERT(mSizeOfItems == sizeOfItems);
   4697 #endif
   4698 
   4699  // Computes load state optimized for current size of keys and items.
   4700  // Zero key length and value can be passed to do a quick initial estimation.
   4701  // If computed load state is already AllOrderedItems then excluded key length
   4702  // and value length can't make it any better.
   4703  auto GetLoadState = [&](int64_t aKeyLength, int64_t aValueLength) {
   4704    if (mSizeOfKeys - aKeyLength <= gSnapshotPrefill) {
   4705      if (mSizeOfItems - aKeyLength - aValueLength <= gSnapshotPrefill) {
   4706        return LSSnapshot::LoadState::AllOrderedItems;
   4707      }
   4708 
   4709      return LSSnapshot::LoadState::AllOrderedKeys;
   4710    }
   4711 
   4712    return LSSnapshot::LoadState::Partial;
   4713  };
   4714 
   4715  // Value for given aKey if aKey is not void (can be void too if value doesn't
   4716  // exist for given aKey).
   4717  LSValue value;
   4718  // If aKey and value are not void, checkKey will be set to true. Once we find
   4719  // an item for given aKey in one of the loops below, checkKey is set to false
   4720  // to prevent additional comparison of strings (string implementation compares
   4721  // string lengths first to avoid char by char comparison if possible).
   4722  bool checkKey = false;
   4723 
   4724  // Avoid additional hash lookup if all ordered items fit into initial prefill
   4725  // already.
   4726  LSSnapshot::LoadState loadState = GetLoadState(/* aKeyLength */ 0,
   4727                                                 /* aValueLength */ 0);
   4728  if (loadState != LSSnapshot::LoadState::AllOrderedItems && !aKey.IsVoid()) {
   4729    GetItem(aKey, value);
   4730    if (!value.IsVoid()) {
   4731      // Ok, we have a non void aKey and value.
   4732 
   4733      // We have to watch for aKey during one of the loops below to exclude it
   4734      // from the size computation. The super fast mode (AllOrderedItems)
   4735      // doesn't have to do that though.
   4736      checkKey = true;
   4737 
   4738      // We have to compute load state again because aKey length and value
   4739      // length is excluded from the size in this case.
   4740      loadState = GetLoadState(aKey.Length(), value.Length());
   4741    }
   4742  }
   4743 
   4744  switch (loadState) {
   4745    case LSSnapshot::LoadState::AllOrderedItems: {
   4746      // We're sending all ordered items, we don't need to check keys because
   4747      // mOrderedItems must contain a value for aKey if checkKey is true.
   4748 
   4749      aItemInfos.AppendElements(mOrderedItems);
   4750 
   4751      MOZ_ASSERT(aItemInfos.Length() == mValues.Count());
   4752      aNextLoadIndex = mValues.Count();
   4753 
   4754      aAddKeyToUnknownItems = false;
   4755 
   4756      break;
   4757    }
   4758 
   4759    case LSSnapshot::LoadState::AllOrderedKeys: {
   4760      // We don't have enough snapshot budget to send all items, but we do have
   4761      // enough to send all of the keys and to make a best effort to populate as
   4762      // many values as possible. We send void string values once we run out of
   4763      // budget. A complicating factor is that we want to make sure that we send
   4764      // the value for aKey which is a localStorage read that's triggering this
   4765      // request. Since that key can happen anywhere in the list of items, we
   4766      // need to handle it specially.
   4767      //
   4768      // The loop is effectively doing 2 things in parallel:
   4769      //
   4770      //   1. Looking for the `aKey` to send. This is tracked by `checkKey`
   4771      //      which is true if there was an `aKey` specified and until we
   4772      //      populate its value, and false thereafter.
   4773      //   2. Sending values until we run out of `size` budget and switch to
   4774      //      sending void values. `doneSendingValues` tracks when we've run out
   4775      //      of size budget, with `setVoidValue` tracking whether a value
   4776      //      should be sent for each turn of the event loop but can be
   4777      //      overridden when `aKey` is found.
   4778 
   4779      int64_t size = mSizeOfKeys;
   4780      bool setVoidValue = false;
   4781      bool doneSendingValues = false;
   4782      for (uint32_t index = 0; index < mOrderedItems.Length(); index++) {
   4783        const LSItemInfo& item = mOrderedItems[index];
   4784 
   4785        const nsString& key = item.key();
   4786        const LSValue& value = item.value();
   4787 
   4788        if (checkKey && key == aKey) {
   4789          checkKey = false;
   4790          setVoidValue = false;
   4791        } else if (!setVoidValue) {
   4792          if (doneSendingValues) {
   4793            setVoidValue = true;
   4794          } else {
   4795            size += static_cast<int64_t>(value.Length());
   4796 
   4797            if (size > gSnapshotPrefill) {
   4798              setVoidValue = true;
   4799              doneSendingValues = true;
   4800 
   4801              // We set doneSendingValues to true and that will guard against
   4802              // entering this branch during next iterations. So aNextLoadIndex
   4803              // is set only once.
   4804              aNextLoadIndex = index;
   4805            }
   4806          }
   4807        }
   4808 
   4809        LSItemInfo* itemInfo = aItemInfos.AppendElement();
   4810        itemInfo->key() = key;
   4811        if (setVoidValue) {
   4812          itemInfo->value().SetIsVoid(true);
   4813        } else {
   4814          aLoadedItems.PutEntry(key);
   4815          itemInfo->value() = value;
   4816        }
   4817      }
   4818 
   4819      aAddKeyToUnknownItems = false;
   4820 
   4821      break;
   4822    }
   4823 
   4824    case LSSnapshot::LoadState::Partial: {
   4825      int64_t size = 0;
   4826      for (uint32_t index = 0; index < mOrderedItems.Length(); index++) {
   4827        const LSItemInfo& item = mOrderedItems[index];
   4828 
   4829        const nsString& key = item.key();
   4830        const LSValue& value = item.value();
   4831 
   4832        if (checkKey && key == aKey) {
   4833          checkKey = false;
   4834        } else {
   4835          size += static_cast<int64_t>(key.Length()) +
   4836                  static_cast<int64_t>(value.Length());
   4837 
   4838          if (size > gSnapshotPrefill) {
   4839            aNextLoadIndex = index;
   4840            break;
   4841          }
   4842        }
   4843 
   4844        aLoadedItems.PutEntry(key);
   4845 
   4846        LSItemInfo* itemInfo = aItemInfos.AppendElement();
   4847        itemInfo->key() = key;
   4848        itemInfo->value() = value;
   4849      }
   4850 
   4851      aAddKeyToUnknownItems = false;
   4852 
   4853      if (!aKey.IsVoid()) {
   4854        if (value.IsVoid()) {
   4855          aAddKeyToUnknownItems = true;
   4856        } else if (checkKey) {
   4857          // The item wasn't added in the loop above, add it here.
   4858 
   4859          LSItemInfo* itemInfo = aItemInfos.AppendElement();
   4860          itemInfo->key() = aKey;
   4861          itemInfo->value() = value;
   4862        }
   4863      }
   4864 
   4865      MOZ_ASSERT(aItemInfos.Length() < mOrderedItems.Length());
   4866 
   4867      break;
   4868    }
   4869 
   4870    default:
   4871      MOZ_CRASH("Bad load state value!");
   4872  }
   4873 
   4874  aLoadState = loadState;
   4875 }
   4876 
   4877 void Datastore::GetItem(const nsAString& aKey, LSValue& aValue) const {
   4878  AssertIsOnBackgroundThread();
   4879  MOZ_ASSERT(!mClosed);
   4880 
   4881  if (!mValues.Get(aKey, &aValue)) {
   4882    aValue.SetIsVoid(true);
   4883  }
   4884 }
   4885 
   4886 void Datastore::GetKeys(nsTArray<nsString>& aKeys) const {
   4887  AssertIsOnBackgroundThread();
   4888  MOZ_ASSERT(!mClosed);
   4889 
   4890  for (auto item : mOrderedItems) {
   4891    aKeys.AppendElement(item.key());
   4892  }
   4893 }
   4894 
   4895 void Datastore::SetItem(Database* aDatabase, const nsString& aKey,
   4896                        const LSValue& aValue) {
   4897  AssertIsOnBackgroundThread();
   4898  MOZ_ASSERT(aDatabase);
   4899  MOZ_ASSERT(!mClosed);
   4900  MOZ_ASSERT(mInUpdateBatch);
   4901 
   4902  LSValue oldValue;
   4903  GetItem(aKey, oldValue);
   4904 
   4905  if (oldValue != aValue) {
   4906    bool isNewItem = oldValue.IsVoid();
   4907 
   4908    NotifySnapshots(aDatabase, aKey, oldValue, /* affectsOrder */ isNewItem);
   4909 
   4910    mValues.InsertOrUpdate(aKey, aValue);
   4911 
   4912    int64_t delta;
   4913 
   4914    if (isNewItem) {
   4915      mWriteOptimizer.InsertItem(aKey, aValue);
   4916 
   4917      int64_t sizeOfKey = static_cast<int64_t>(aKey.Length());
   4918 
   4919      delta = sizeOfKey + static_cast<int64_t>(aValue.UTF16Length());
   4920 
   4921      mUpdateBatchUsage += delta;
   4922 
   4923      mSizeOfKeys += sizeOfKey;
   4924      mSizeOfItems += sizeOfKey + static_cast<int64_t>(aValue.Length());
   4925    } else {
   4926      mWriteOptimizer.UpdateItem(aKey, aValue);
   4927 
   4928      delta = static_cast<int64_t>(aValue.UTF16Length()) -
   4929              static_cast<int64_t>(oldValue.UTF16Length());
   4930 
   4931      mUpdateBatchUsage += delta;
   4932 
   4933      mSizeOfItems += static_cast<int64_t>(aValue.Length()) -
   4934                      static_cast<int64_t>(oldValue.Length());
   4935    }
   4936 
   4937    if (IsPersistent()) {
   4938      mConnection->SetItem(aKey, aValue, delta, isNewItem);
   4939    }
   4940  }
   4941 }
   4942 
   4943 void Datastore::RemoveItem(Database* aDatabase, const nsString& aKey) {
   4944  AssertIsOnBackgroundThread();
   4945  MOZ_ASSERT(aDatabase);
   4946  MOZ_ASSERT(!mClosed);
   4947  MOZ_ASSERT(mInUpdateBatch);
   4948 
   4949  LSValue oldValue;
   4950  GetItem(aKey, oldValue);
   4951 
   4952  if (!oldValue.IsVoid()) {
   4953    NotifySnapshots(aDatabase, aKey, oldValue, /* aAffectsOrder */ true);
   4954 
   4955    mValues.Remove(aKey);
   4956 
   4957    mWriteOptimizer.DeleteItem(aKey);
   4958 
   4959    int64_t sizeOfKey = static_cast<int64_t>(aKey.Length());
   4960 
   4961    int64_t delta = -sizeOfKey - static_cast<int64_t>(oldValue.UTF16Length());
   4962 
   4963    mUpdateBatchUsage += delta;
   4964 
   4965    mSizeOfKeys -= sizeOfKey;
   4966    mSizeOfItems -= sizeOfKey + static_cast<int64_t>(oldValue.Length());
   4967 
   4968    if (IsPersistent()) {
   4969      mConnection->RemoveItem(aKey, delta);
   4970    }
   4971  }
   4972 }
   4973 
   4974 void Datastore::Clear(Database* aDatabase) {
   4975  AssertIsOnBackgroundThread();
   4976  MOZ_ASSERT(!mClosed);
   4977 
   4978  if (mValues.Count()) {
   4979    int64_t delta = 0;
   4980    for (const auto& entry : mValues) {
   4981      const nsAString& key = entry.GetKey();
   4982      const LSValue& value = entry.GetData();
   4983 
   4984      delta += -static_cast<int64_t>(key.Length()) -
   4985               static_cast<int64_t>(value.UTF16Length());
   4986 
   4987      NotifySnapshots(aDatabase, key, value, /* aAffectsOrder */ true);
   4988    }
   4989 
   4990    mValues.Clear();
   4991 
   4992    if (mInUpdateBatch) {
   4993      mWriteOptimizer.Truncate();
   4994 
   4995      mUpdateBatchUsage += delta;
   4996    } else {
   4997      mOrderedItems.Clear();
   4998 
   4999      DebugOnly<bool> ok = UpdateUsage(delta);
   5000      MOZ_ASSERT(ok);
   5001    }
   5002 
   5003    mSizeOfKeys = 0;
   5004    mSizeOfItems = 0;
   5005 
   5006    if (IsPersistent()) {
   5007      mConnection->Clear(delta);
   5008    }
   5009  }
   5010 }
   5011 
   5012 void Datastore::BeginUpdateBatch(int64_t aSnapshotUsage) {
   5013  AssertIsOnBackgroundThread();
   5014  // Don't assert `aSnapshotUsage >= 0`, it can be negative when multiple
   5015  // snapshots are operating in parallel.
   5016  MOZ_ASSERT(!mClosed);
   5017  MOZ_ASSERT(mUpdateBatchUsage == -1);
   5018  MOZ_ASSERT(!mInUpdateBatch);
   5019 
   5020  mUpdateBatchUsage = aSnapshotUsage;
   5021 
   5022  if (IsPersistent()) {
   5023    mConnection->BeginUpdateBatch();
   5024  }
   5025 
   5026  mInUpdateBatch = true;
   5027 }
   5028 
   5029 int64_t Datastore::EndUpdateBatch(int64_t aSnapshotPeakUsage) {
   5030  AssertIsOnBackgroundThread();
   5031  MOZ_ASSERT(!mClosed);
   5032  MOZ_ASSERT(mInUpdateBatch);
   5033 
   5034  mWriteOptimizer.ApplyAndReset(mOrderedItems);
   5035 
   5036  MOZ_ASSERT(!mWriteOptimizer.HasWrites());
   5037 
   5038  if (aSnapshotPeakUsage >= 0) {
   5039    int64_t delta = mUpdateBatchUsage - aSnapshotPeakUsage;
   5040 
   5041    if (mActiveDatabases.Count()) {
   5042      // We can't apply deltas while other databases are still active.
   5043      // The final delta must be zero or negative, but individual deltas can
   5044      // be positive. A positive delta can't be applied asynchronously since
   5045      // there's no way to fire the quota exceeded error event.
   5046 
   5047      mPendingUsageDeltas.AppendElement(delta);
   5048    } else {
   5049      MOZ_ASSERT(delta <= 0);
   5050      if (delta != 0) {
   5051        DebugOnly<bool> ok = UpdateUsage(delta);
   5052        MOZ_ASSERT(ok);
   5053      }
   5054    }
   5055  }
   5056 
   5057  int64_t result = mUpdateBatchUsage;
   5058  mUpdateBatchUsage = -1;
   5059 
   5060  if (IsPersistent()) {
   5061    mConnection->EndUpdateBatch();
   5062  }
   5063 
   5064  mInUpdateBatch = false;
   5065 
   5066  return result;
   5067 }
   5068 
   5069 int64_t Datastore::AttemptToUpdateUsage(int64_t aMinSize, bool aInitial) {
   5070  AssertIsOnBackgroundThread();
   5071  MOZ_ASSERT_IF(aInitial, aMinSize >= 0);
   5072  MOZ_ASSERT_IF(!aInitial, aMinSize > 0);
   5073 
   5074  const int64_t size = aMinSize + GetSnapshotPeakUsagePreincrement(aInitial);
   5075 
   5076  if (size && UpdateUsage(size)) {
   5077    return size;
   5078  }
   5079 
   5080  const int64_t reducedSize =
   5081      aMinSize + GetSnapshotPeakUsageReducedPreincrement(aInitial);
   5082 
   5083  if (reducedSize && UpdateUsage(reducedSize)) {
   5084    return reducedSize;
   5085  }
   5086 
   5087  if (aMinSize > 0 && UpdateUsage(aMinSize)) {
   5088    return aMinSize;
   5089  }
   5090 
   5091  return 0;
   5092 }
   5093 
   5094 bool Datastore::HasOtherProcessObservers(Database* aDatabase) {
   5095  AssertIsOnBackgroundThread();
   5096  MOZ_ASSERT(aDatabase);
   5097 
   5098  if (!gObservers) {
   5099    return false;
   5100  }
   5101 
   5102  nsTArray<NotNull<Observer*>>* array;
   5103  if (!gObservers->Get(mOriginMetadata.mOrigin, &array)) {
   5104    return false;
   5105  }
   5106 
   5107  MOZ_ASSERT(array);
   5108 
   5109  for (Observer* observer : *array) {
   5110    if (observer->ContentParentIdRef() != aDatabase->ContentParentIdRef()) {
   5111      return true;
   5112    }
   5113  }
   5114 
   5115  return false;
   5116 }
   5117 
   5118 void Datastore::NotifyOtherProcessObservers(Database* aDatabase,
   5119                                            const nsString& aDocumentURI,
   5120                                            const nsString& aKey,
   5121                                            const LSValue& aOldValue,
   5122                                            const LSValue& aNewValue) {
   5123  AssertIsOnBackgroundThread();
   5124  MOZ_ASSERT(aDatabase);
   5125 
   5126  if (!gObservers) {
   5127    return;
   5128  }
   5129 
   5130  nsTArray<NotNull<Observer*>>* array;
   5131  if (!gObservers->Get(mOriginMetadata.mOrigin, &array)) {
   5132    return;
   5133  }
   5134 
   5135  MOZ_ASSERT(array);
   5136 
   5137  // We do not want to send information about events back to the content process
   5138  // that caused the change.
   5139 
   5140  for (Observer* observer : *array) {
   5141    if (observer->ContentParentIdRef() != aDatabase->ContentParentIdRef()) {
   5142      observer->Observe(aDatabase, aDocumentURI, aKey, aOldValue, aNewValue);
   5143    }
   5144  }
   5145 }
   5146 
   5147 void Datastore::NoteChangedObserverArray(
   5148    const nsTArray<NotNull<Observer*>>& aObservers) {
   5149  AssertIsOnBackgroundThread();
   5150 
   5151  for (Database* database : mActiveDatabases) {
   5152    Snapshot* snapshot = database->GetSnapshot();
   5153    MOZ_ASSERT(snapshot);
   5154 
   5155    if (snapshot->IsDirty()) {
   5156      continue;
   5157    }
   5158 
   5159    bool hasOtherProcessObservers = false;
   5160 
   5161    for (Observer* observer : aObservers) {
   5162      if (observer->ContentParentIdRef() != database->ContentParentIdRef()) {
   5163        hasOtherProcessObservers = true;
   5164        break;
   5165      }
   5166    }
   5167 
   5168    if (snapshot->HasOtherProcessObservers() != hasOtherProcessObservers) {
   5169      snapshot->MarkDirty();
   5170    }
   5171  }
   5172 }
   5173 
   5174 void Datastore::Stringify(nsACString& aResult) const {
   5175  AssertIsOnBackgroundThread();
   5176 
   5177  aResult.AppendLiteral("DirectoryLock:");
   5178  aResult.AppendInt(!!mDirectoryLockHandle);
   5179  aResult.Append(kQuotaGenericDelimiter);
   5180 
   5181  aResult.AppendLiteral("Connection:");
   5182  aResult.AppendInt(!!mConnection);
   5183  aResult.Append(kQuotaGenericDelimiter);
   5184 
   5185  aResult.AppendLiteral("QuotaObject:");
   5186  aResult.AppendInt(!!mQuotaObject);
   5187  aResult.Append(kQuotaGenericDelimiter);
   5188 
   5189  aResult.AppendLiteral("PrepareDatastoreOps:");
   5190  aResult.AppendInt(mPrepareDatastoreOps.Count());
   5191  aResult.Append(kQuotaGenericDelimiter);
   5192 
   5193  aResult.AppendLiteral("PreparedDatastores:");
   5194  aResult.AppendInt(mPreparedDatastores.Count());
   5195  aResult.Append(kQuotaGenericDelimiter);
   5196 
   5197  aResult.AppendLiteral("Databases:");
   5198  aResult.AppendInt(mDatabases.Count());
   5199  aResult.Append(kQuotaGenericDelimiter);
   5200 
   5201  aResult.AppendLiteral("ActiveDatabases:");
   5202  aResult.AppendInt(mActiveDatabases.Count());
   5203  aResult.Append(kQuotaGenericDelimiter);
   5204 
   5205  aResult.AppendLiteral("Origin:");
   5206  aResult.Append(AnonymizedOriginString(mOriginMetadata.mOrigin));
   5207  aResult.Append(kQuotaGenericDelimiter);
   5208 
   5209  aResult.AppendLiteral("PrivateBrowsingId:");
   5210  aResult.AppendInt(mPrivateBrowsingId);
   5211  aResult.Append(kQuotaGenericDelimiter);
   5212 
   5213  aResult.AppendLiteral("Closed:");
   5214  aResult.AppendInt(mClosed);
   5215 }
   5216 
   5217 bool Datastore::UpdateUsage(int64_t aDelta) {
   5218  AssertIsOnBackgroundThread();
   5219 
   5220  // Check internal LocalStorage origin limit.
   5221  int64_t newUsage = mUsage + aDelta;
   5222 
   5223  MOZ_ASSERT(newUsage >= 0);
   5224 
   5225  if (newUsage > StaticPrefs::dom_storage_default_quota() * 1024) {
   5226    return false;
   5227  }
   5228 
   5229  // Check QuotaManager limits (group and global limit).
   5230  if (IsPersistent()) {
   5231    MOZ_ASSERT(mQuotaObject);
   5232 
   5233    if (!mQuotaObject->MaybeUpdateSize(newUsage, /* aTruncate */ true)) {
   5234      return false;
   5235    }
   5236  }
   5237 
   5238  // Quota checks passed, set new usage.
   5239  mUsage = newUsage;
   5240 
   5241  return true;
   5242 }
   5243 
   5244 void Datastore::MaybeClose() {
   5245  AssertIsOnBackgroundThread();
   5246 
   5247  if (!mPrepareDatastoreOps.Count() && !mHasLivePrivateDatastore &&
   5248      !mPreparedDatastores.Count() && !mDatabases.Count()) {
   5249    Close();
   5250  }
   5251 }
   5252 
   5253 void Datastore::ConnectionClosedCallback() {
   5254  AssertIsOnBackgroundThread();
   5255  MOZ_ASSERT(mDirectoryLockHandle);
   5256  MOZ_ASSERT(mConnection);
   5257  MOZ_ASSERT(mQuotaObject);
   5258  MOZ_ASSERT(mClosed);
   5259 
   5260  // Release the quota object first.
   5261  mQuotaObject = nullptr;
   5262 
   5263  bool databaseWasNotAvailable;
   5264  bool hasCreatedDatabase;
   5265  mConnection->GetFinishInfo(databaseWasNotAvailable, hasCreatedDatabase);
   5266 
   5267  if (databaseWasNotAvailable && !hasCreatedDatabase) {
   5268    MOZ_ASSERT(mUsage == 0);
   5269 
   5270    QuotaManager* quotaManager = QuotaManager::Get();
   5271    MOZ_ASSERT(quotaManager);
   5272 
   5273    quotaManager->ResetUsageForClient(
   5274        ClientMetadata{mOriginMetadata, mozilla::dom::quota::Client::LS});
   5275  }
   5276 
   5277  mConnection = nullptr;
   5278 
   5279  // Now it's safe to release the directory lock and unregister itself from
   5280  // the hashtable.
   5281 
   5282  {
   5283    auto destroyingDirectoryLockHandle = std::move(mDirectoryLockHandle);
   5284  }
   5285 
   5286  CleanupMetadata();
   5287 
   5288  if (mCompleteCallback) {
   5289    MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(mCompleteCallback.forget()));
   5290  }
   5291 }
   5292 
   5293 void Datastore::CleanupMetadata() {
   5294  AssertIsOnBackgroundThread();
   5295 
   5296  MOZ_ASSERT(gDatastores);
   5297  const DebugOnly<bool> removed = gDatastores->Remove(mOriginMetadata.mOrigin);
   5298  MOZ_ASSERT(removed);
   5299 
   5300  QuotaManager::MaybeRecordQuotaClientShutdownStep(quota::Client::LS,
   5301                                                   "Datastore removed"_ns);
   5302 
   5303  if (!gDatastores->Count()) {
   5304    gDatastores = nullptr;
   5305  }
   5306 }
   5307 
   5308 void Datastore::NotifySnapshots(Database* aDatabase, const nsAString& aKey,
   5309                                const LSValue& aOldValue, bool aAffectsOrder) {
   5310  AssertIsOnBackgroundThread();
   5311 
   5312  for (Database* database : mDatabases) {
   5313    MOZ_ASSERT(database);
   5314 
   5315    if (database == aDatabase) {
   5316      continue;
   5317    }
   5318 
   5319    Snapshot* snapshot = database->GetSnapshot();
   5320    if (snapshot) {
   5321      snapshot->SaveItem(aKey, aOldValue, aAffectsOrder);
   5322    }
   5323  }
   5324 }
   5325 
   5326 void Datastore::NoteChangedDatabaseMap() {
   5327  AssertIsOnBackgroundThread();
   5328 
   5329  for (Database* database : mActiveDatabases) {
   5330    Snapshot* snapshot = database->GetSnapshot();
   5331    MOZ_ASSERT(snapshot);
   5332 
   5333    if (snapshot->IsDirty()) {
   5334      continue;
   5335    }
   5336 
   5337    if (snapshot->HasOtherProcessDatabases() !=
   5338        HasOtherProcessDatabases(database)) {
   5339      snapshot->MarkDirty();
   5340    }
   5341  }
   5342 }
   5343 
   5344 /*******************************************************************************
   5345 * PreparedDatastore
   5346 ******************************************************************************/
   5347 
   5348 void PreparedDatastore::Destroy() {
   5349  AssertIsOnBackgroundThread();
   5350  MOZ_ASSERT(gPreparedDatastores);
   5351  DebugOnly<bool> removed = gPreparedDatastores->Remove(mDatastoreId);
   5352  MOZ_ASSERT(removed);
   5353 }
   5354 
   5355 // static
   5356 void PreparedDatastore::TimerCallback(nsITimer* aTimer, void* aClosure) {
   5357  AssertIsOnBackgroundThread();
   5358 
   5359  auto* self = static_cast<PreparedDatastore*>(aClosure);
   5360  MOZ_ASSERT(self);
   5361 
   5362  self->Destroy();
   5363 }
   5364 
   5365 /*******************************************************************************
   5366 * Database
   5367 ******************************************************************************/
   5368 
   5369 Database::Database(const PrincipalInfo& aPrincipalInfo,
   5370                   const Maybe<ContentParentId>& aContentParentId,
   5371                   const nsACString& aOrigin, uint32_t aPrivateBrowsingId)
   5372    : mSnapshot(nullptr),
   5373      mPrincipalInfo(aPrincipalInfo),
   5374      mContentParentId(aContentParentId),
   5375      mOrigin(aOrigin),
   5376      mRequestAllowToCloseTimerId(0),
   5377      mPrivateBrowsingId(aPrivateBrowsingId),
   5378      mAllowedToClose(false),
   5379      mActorDestroyed(false),
   5380      mRequestedAllowToClose(false)
   5381 #ifdef DEBUG
   5382      ,
   5383      mActorWasAlive(false)
   5384 #endif
   5385 {
   5386  AssertIsOnOwningThread();
   5387 
   5388  if (!gDatabases) {
   5389    gDatabases = new DatabaseArray();
   5390  }
   5391 
   5392  gDatabases->AppendElement(this);
   5393 }
   5394 
   5395 Database::~Database() {
   5396  AssertIsOnOwningThread();
   5397  MOZ_ASSERT_IF(mActorWasAlive, mAllowedToClose);
   5398  MOZ_ASSERT_IF(mActorWasAlive, mActorDestroyed);
   5399 
   5400  MOZ_ASSERT(gDatabases);
   5401  gDatabases->RemoveElement(this);
   5402 
   5403  if (gDatabases->IsEmpty()) {
   5404    gDatabases = nullptr;
   5405  }
   5406 }
   5407 
   5408 void Database::SetActorAlive(Datastore* aDatastore) {
   5409  AssertIsOnOwningThread();
   5410  MOZ_ASSERT(!mActorWasAlive);
   5411  MOZ_ASSERT(!mActorDestroyed);
   5412 
   5413 #ifdef DEBUG
   5414  mActorWasAlive = true;
   5415 #endif
   5416 
   5417  mDatastore = aDatastore;
   5418 
   5419  mDatastore->NoteLiveDatabase(this);
   5420 
   5421  if (!gLiveDatabases) {
   5422    gLiveDatabases = new LiveDatabaseArray();
   5423  }
   5424 
   5425  gLiveDatabases->AppendElement(WrapNotNullUnchecked(this));
   5426 }
   5427 
   5428 void Database::RegisterSnapshot(Snapshot* aSnapshot) {
   5429  AssertIsOnOwningThread();
   5430  MOZ_ASSERT(aSnapshot);
   5431  MOZ_ASSERT(!mSnapshot);
   5432  MOZ_ASSERT(!mAllowedToClose);
   5433 
   5434  // Only one snapshot at a time is currently supported.
   5435  mSnapshot = aSnapshot;
   5436 
   5437  mDatastore->NoteActiveDatabase(this);
   5438 }
   5439 
   5440 void Database::UnregisterSnapshot(Snapshot* aSnapshot) {
   5441  MOZ_ASSERT(aSnapshot);
   5442  MOZ_ASSERT(mSnapshot == aSnapshot);
   5443 
   5444  mSnapshot = nullptr;
   5445 
   5446  mDatastore->NoteInactiveDatabase(this);
   5447 }
   5448 
   5449 void Database::RequestAllowToClose() {
   5450  AssertIsOnOwningThread();
   5451 
   5452  if (mRequestedAllowToClose) {
   5453    return;
   5454  }
   5455 
   5456  mRequestedAllowToClose = true;
   5457 
   5458  // Send the RequestAllowToClose message to the child to avoid racing with the
   5459  // child actor. Except the case when the actor was already destroyed.
   5460  if (mActorDestroyed) {
   5461    MOZ_ASSERT(mAllowedToClose);
   5462    return;
   5463  }
   5464 
   5465  if (NS_WARN_IF(!SendRequestAllowToClose())) {
   5466    if (!mSnapshot) {
   5467      // This is not necessary, because there should be a runnable scheduled
   5468      // that will call ActorDestroy which calls AllowToClose. However we can
   5469      // speedup the shutdown a bit if we do it here directly, but only if
   5470      // there's no registered snapshot.
   5471      AllowToClose();
   5472    }
   5473  } else {
   5474    mRequestAllowToCloseTimerId =
   5475        glean::localstorage_database::request_allow_to_close_response_time
   5476            .Start();
   5477  }
   5478 }
   5479 
   5480 void Database::ForceKill() {
   5481  AssertIsOnOwningThread();
   5482 
   5483  if (mActorDestroyed) {
   5484    MOZ_ASSERT(mAllowedToClose);
   5485    return;
   5486  }
   5487 
   5488  Close();
   5489 }
   5490 
   5491 void Database::Stringify(nsACString& aResult) const {
   5492  AssertIsOnOwningThread();
   5493 
   5494  aResult.AppendLiteral("SnapshotRegistered:");
   5495  aResult.AppendInt(!!mSnapshot);
   5496  aResult.Append(kQuotaGenericDelimiter);
   5497 
   5498  aResult.AppendLiteral("OtherProcessActor:");
   5499  aResult.AppendInt(mContentParentId.isSome());
   5500  aResult.Append(kQuotaGenericDelimiter);
   5501 
   5502  aResult.AppendLiteral("Origin:");
   5503  aResult.Append(AnonymizedOriginString(mOrigin));
   5504  aResult.Append(kQuotaGenericDelimiter);
   5505 
   5506  aResult.AppendLiteral("PrivateBrowsingId:");
   5507  aResult.AppendInt(mPrivateBrowsingId);
   5508  aResult.Append(kQuotaGenericDelimiter);
   5509 
   5510  aResult.AppendLiteral("AllowedToClose:");
   5511  aResult.AppendInt(mAllowedToClose);
   5512  aResult.Append(kQuotaGenericDelimiter);
   5513 
   5514  aResult.AppendLiteral("ActorDestroyed:");
   5515  aResult.AppendInt(mActorDestroyed);
   5516  aResult.Append(kQuotaGenericDelimiter);
   5517 
   5518  aResult.AppendLiteral("RequestedAllowToClose:");
   5519  aResult.AppendInt(mRequestedAllowToClose);
   5520 }
   5521 
   5522 void Database::AllowToClose() {
   5523  AssertIsOnOwningThread();
   5524  MOZ_ASSERT(!mAllowedToClose);
   5525  MOZ_ASSERT(mDatastore);
   5526  MOZ_ASSERT(!mSnapshot);
   5527 
   5528  mAllowedToClose = true;
   5529 
   5530  mDatastore->NoteFinishedDatabase(this);
   5531 
   5532  mDatastore = nullptr;
   5533 
   5534  MOZ_ASSERT(gLiveDatabases);
   5535  gLiveDatabases->RemoveElement(this);
   5536 
   5537  QuotaManager::MaybeRecordQuotaClientShutdownStep(quota::Client::LS,
   5538                                                   "Live database removed"_ns);
   5539 
   5540  if (gLiveDatabases->IsEmpty()) {
   5541    gLiveDatabases = nullptr;
   5542  }
   5543 }
   5544 
   5545 void Database::ActorDestroy(ActorDestroyReason aWhy) {
   5546  AssertIsOnOwningThread();
   5547  MOZ_ASSERT(!mActorDestroyed);
   5548 
   5549  mActorDestroyed = true;
   5550 
   5551  if (!mAllowedToClose) {
   5552    AllowToClose();
   5553  }
   5554 }
   5555 
   5556 mozilla::ipc::IPCResult Database::RecvAllowToClose() {
   5557  AssertIsOnOwningThread();
   5558 
   5559  if (NS_WARN_IF(mAllowedToClose)) {
   5560    return IPC_FAIL(this, "mAllowedToClose already set!");
   5561  }
   5562 
   5563  // AllowToClose can be sent from content without a prior request from the
   5564  // parent (RequestAllowToClose). Therefore, we need to check for this;
   5565  // otherwise, we would receive warnings from Glean about timing not running.
   5566  if (mRequestedAllowToClose) {
   5567    // mRequestAllowToCloseTimerId is initialized to zero in the constructor,
   5568    // and the Start() method always returns id that is greater than zero, so
   5569    // we can assert that here.
   5570    MOZ_ASSERT(mRequestAllowToCloseTimerId);
   5571 
   5572    glean::localstorage_database::request_allow_to_close_response_time
   5573        .StopAndAccumulate(std::move(mRequestAllowToCloseTimerId));
   5574  }
   5575 
   5576  AllowToClose();
   5577 
   5578  return IPC_OK();
   5579 }
   5580 
   5581 PBackgroundLSSnapshotParent* Database::AllocPBackgroundLSSnapshotParent(
   5582    const nsAString& aDocumentURI, const nsAString& aKey,
   5583    const bool& aIncreasePeakUsage, const int64_t& aMinSize,
   5584    LSSnapshotInitInfo* aInitInfo) {
   5585  AssertIsOnOwningThread();
   5586 
   5587  if (NS_WARN_IF(aIncreasePeakUsage && aMinSize < 0)) {
   5588    MOZ_ASSERT_UNLESS_FUZZING(false);
   5589    return nullptr;
   5590  }
   5591 
   5592  if (NS_WARN_IF(mAllowedToClose)) {
   5593    MOZ_ASSERT_UNLESS_FUZZING(false);
   5594    return nullptr;
   5595  }
   5596 
   5597  RefPtr<Snapshot> snapshot = new Snapshot(this, aDocumentURI);
   5598 
   5599  // Transfer ownership to IPDL.
   5600  return snapshot.forget().take();
   5601 }
   5602 
   5603 mozilla::ipc::IPCResult Database::RecvPBackgroundLSSnapshotConstructor(
   5604    PBackgroundLSSnapshotParent* aActor, const nsAString& aDocumentURI,
   5605    const nsAString& aKey, const bool& aIncreasePeakUsage,
   5606    const int64_t& aMinSize, LSSnapshotInitInfo* aInitInfo) {
   5607  AssertIsOnOwningThread();
   5608  MOZ_ASSERT_IF(aIncreasePeakUsage, aMinSize >= 0);
   5609  MOZ_ASSERT(aInitInfo);
   5610  MOZ_ASSERT(!mAllowedToClose);
   5611 
   5612  auto* snapshot = static_cast<Snapshot*>(aActor);
   5613 
   5614  bool addKeyToUnknownItems;
   5615  nsTHashtable<nsStringHashKey> loadedItems;
   5616  nsTArray<LSItemInfo> itemInfos;
   5617  uint32_t nextLoadIndex;
   5618  LSSnapshot::LoadState loadState;
   5619  mDatastore->GetSnapshotLoadInfo(aKey, addKeyToUnknownItems, loadedItems,
   5620                                  itemInfos, nextLoadIndex, loadState);
   5621 
   5622  nsTHashSet<nsString> unknownItems;
   5623  if (addKeyToUnknownItems) {
   5624    unknownItems.Insert(aKey);
   5625  }
   5626 
   5627  uint32_t totalLength = mDatastore->GetLength();
   5628 
   5629  int64_t usage = mDatastore->GetUsage();
   5630 
   5631  int64_t peakUsage = usage;
   5632 
   5633  if (aIncreasePeakUsage) {
   5634    int64_t size =
   5635        mDatastore->AttemptToUpdateUsage(aMinSize, /* aInitial */ true);
   5636 
   5637    peakUsage += size;
   5638  }
   5639 
   5640  bool hasOtherProcessDatabases = mDatastore->HasOtherProcessDatabases(this);
   5641  bool hasOtherProcessObservers = mDatastore->HasOtherProcessObservers(this);
   5642 
   5643  snapshot->Init(loadedItems, std::move(unknownItems), nextLoadIndex,
   5644                 totalLength, usage, peakUsage, loadState,
   5645                 hasOtherProcessDatabases, hasOtherProcessObservers);
   5646 
   5647  RegisterSnapshot(snapshot);
   5648 
   5649  aInitInfo->addKeyToUnknownItems() = addKeyToUnknownItems;
   5650  aInitInfo->itemInfos() = std::move(itemInfos);
   5651  aInitInfo->totalLength() = totalLength;
   5652  aInitInfo->usage() = usage;
   5653  aInitInfo->peakUsage() = peakUsage;
   5654  aInitInfo->loadState() = loadState;
   5655  aInitInfo->hasOtherProcessDatabases() = hasOtherProcessDatabases;
   5656  aInitInfo->hasOtherProcessObservers() = hasOtherProcessObservers;
   5657 
   5658  return IPC_OK();
   5659 }
   5660 
   5661 bool Database::DeallocPBackgroundLSSnapshotParent(
   5662    PBackgroundLSSnapshotParent* aActor) {
   5663  AssertIsOnOwningThread();
   5664  MOZ_ASSERT(aActor);
   5665 
   5666  // Transfer ownership back from IPDL.
   5667  RefPtr<Snapshot> actor = dont_AddRef(static_cast<Snapshot*>(aActor));
   5668 
   5669  return true;
   5670 }
   5671 
   5672 /*******************************************************************************
   5673 * Snapshot
   5674 ******************************************************************************/
   5675 
   5676 Snapshot::Snapshot(Database* aDatabase, const nsAString& aDocumentURI)
   5677    : mDatabase(aDatabase),
   5678      mDatastore(aDatabase->GetDatastore()),
   5679      mDocumentURI(aDocumentURI),
   5680      mTotalLength(0),
   5681      mUsage(-1),
   5682      mPeakUsage(-1),
   5683      mSavedKeys(false),
   5684      mActorDestroyed(false),
   5685      mFinishReceived(false),
   5686      mLoadedReceived(false),
   5687      mLoadedAllItems(false),
   5688      mLoadKeysReceived(false),
   5689      mSentMarkDirty(false) {
   5690  AssertIsOnBackgroundThread();
   5691  MOZ_ASSERT(aDatabase);
   5692 }
   5693 
   5694 Snapshot::~Snapshot() {
   5695  MOZ_ASSERT(mActorDestroyed);
   5696  MOZ_ASSERT(mFinishReceived);
   5697 }
   5698 
   5699 void Snapshot::SaveItem(const nsAString& aKey, const LSValue& aOldValue,
   5700                        bool aAffectsOrder) {
   5701  AssertIsOnBackgroundThread();
   5702 
   5703  MarkDirty();
   5704 
   5705  if (mLoadedAllItems) {
   5706    return;
   5707  }
   5708 
   5709  if (!mLoadedItems.Contains(aKey) && !mUnknownItems.Contains(aKey)) {
   5710    mValues.LookupOrInsert(aKey, aOldValue);
   5711  }
   5712 
   5713  if (aAffectsOrder && !mSavedKeys) {
   5714    mDatastore->GetKeys(mKeys);
   5715    mSavedKeys = true;
   5716  }
   5717 }
   5718 
   5719 void Snapshot::MarkDirty() {
   5720  AssertIsOnBackgroundThread();
   5721 
   5722  if (!mSentMarkDirty) {
   5723    (void)SendMarkDirty();
   5724    mSentMarkDirty = true;
   5725  }
   5726 }
   5727 
   5728 void Snapshot::Finish() {
   5729  AssertIsOnBackgroundThread();
   5730  MOZ_ASSERT(mDatabase);
   5731  MOZ_ASSERT(mDatastore);
   5732  MOZ_ASSERT(!mFinishReceived);
   5733 
   5734  mDatastore->BeginUpdateBatch(mUsage);
   5735 
   5736  mDatastore->EndUpdateBatch(mPeakUsage);
   5737 
   5738  mDatabase->UnregisterSnapshot(this);
   5739 
   5740  mFinishReceived = true;
   5741 }
   5742 
   5743 void Snapshot::ActorDestroy(ActorDestroyReason aWhy) {
   5744  AssertIsOnBackgroundThread();
   5745  MOZ_ASSERT(!mActorDestroyed);
   5746 
   5747  mActorDestroyed = true;
   5748 
   5749  if (!mFinishReceived) {
   5750    Finish();
   5751  }
   5752 }
   5753 
   5754 mozilla::ipc::IPCResult Snapshot::RecvDeleteMe() {
   5755  AssertIsOnBackgroundThread();
   5756  MOZ_ASSERT(!mActorDestroyed);
   5757 
   5758  IProtocol* mgr = Manager();
   5759  if (!PBackgroundLSSnapshotParent::Send__delete__(this)) {
   5760    return IPC_FAIL(mgr, "Send__delete__ failed!");
   5761  }
   5762  return IPC_OK();
   5763 }
   5764 
   5765 mozilla::ipc::IPCResult Snapshot::Checkpoint(
   5766    nsTArray<LSWriteInfo>&& aWriteInfos) {
   5767  AssertIsOnBackgroundThread();
   5768  // Don't assert `mUsage >= 0`, it can be negative when multiple snapshots are
   5769  // operating in parallel.
   5770  MOZ_ASSERT(mPeakUsage >= mUsage);
   5771 
   5772  if (NS_WARN_IF(aWriteInfos.IsEmpty())) {
   5773    return IPC_FAIL(this, "aWriteInfos is empty!");
   5774  }
   5775 
   5776  if (NS_WARN_IF(mHasOtherProcessObservers)) {
   5777    return IPC_FAIL(this, "mHasOtherProcessObservers already set!");
   5778  }
   5779 
   5780  mDatastore->BeginUpdateBatch(mUsage);
   5781 
   5782  for (uint32_t index = 0; index < aWriteInfos.Length(); index++) {
   5783    const LSWriteInfo& writeInfo = aWriteInfos[index];
   5784 
   5785    switch (writeInfo.type()) {
   5786      case LSWriteInfo::TLSSetItemInfo: {
   5787        const LSSetItemInfo& info = writeInfo.get_LSSetItemInfo();
   5788 
   5789        mDatastore->SetItem(mDatabase, info.key(), info.value());
   5790 
   5791        break;
   5792      }
   5793 
   5794      case LSWriteInfo::TLSRemoveItemInfo: {
   5795        const LSRemoveItemInfo& info = writeInfo.get_LSRemoveItemInfo();
   5796 
   5797        mDatastore->RemoveItem(mDatabase, info.key());
   5798 
   5799        break;
   5800      }
   5801 
   5802      case LSWriteInfo::TLSClearInfo: {
   5803        mDatastore->Clear(mDatabase);
   5804 
   5805        break;
   5806      }
   5807 
   5808      default:
   5809        MOZ_CRASH("Should never get here!");
   5810    }
   5811  }
   5812 
   5813  mUsage = mDatastore->EndUpdateBatch(-1);
   5814 
   5815  return IPC_OK();
   5816 }
   5817 
   5818 mozilla::ipc::IPCResult Snapshot::CheckpointAndNotify(
   5819    nsTArray<LSWriteAndNotifyInfo>&& aWriteAndNotifyInfos) {
   5820  AssertIsOnBackgroundThread();
   5821  // Don't assert `mUsage >= 0`, it can be negative when multiple snapshots are
   5822  // operating in parallel.
   5823  MOZ_ASSERT(mPeakUsage >= mUsage);
   5824 
   5825  if (NS_WARN_IF(aWriteAndNotifyInfos.IsEmpty())) {
   5826    return IPC_FAIL(this, "aWriteAndNotifyInfos is empty!");
   5827  }
   5828 
   5829  if (NS_WARN_IF(!mHasOtherProcessObservers)) {
   5830    return IPC_FAIL(this, "mHasOtherProcessObservers is not set!");
   5831  }
   5832 
   5833  mDatastore->BeginUpdateBatch(mUsage);
   5834 
   5835  for (uint32_t index = 0; index < aWriteAndNotifyInfos.Length(); index++) {
   5836    const LSWriteAndNotifyInfo& writeAndNotifyInfo =
   5837        aWriteAndNotifyInfos[index];
   5838 
   5839    switch (writeAndNotifyInfo.type()) {
   5840      case LSWriteAndNotifyInfo::TLSSetItemAndNotifyInfo: {
   5841        const LSSetItemAndNotifyInfo& info =
   5842            writeAndNotifyInfo.get_LSSetItemAndNotifyInfo();
   5843 
   5844        mDatastore->SetItem(mDatabase, info.key(), info.value());
   5845 
   5846        mDatastore->NotifyOtherProcessObservers(
   5847            mDatabase, mDocumentURI, info.key(), info.oldValue(), info.value());
   5848 
   5849        break;
   5850      }
   5851 
   5852      case LSWriteAndNotifyInfo::TLSRemoveItemAndNotifyInfo: {
   5853        const LSRemoveItemAndNotifyInfo& info =
   5854            writeAndNotifyInfo.get_LSRemoveItemAndNotifyInfo();
   5855 
   5856        mDatastore->RemoveItem(mDatabase, info.key());
   5857 
   5858        mDatastore->NotifyOtherProcessObservers(mDatabase, mDocumentURI,
   5859                                                info.key(), info.oldValue(),
   5860                                                VoidLSValue());
   5861 
   5862        break;
   5863      }
   5864 
   5865      case LSWriteAndNotifyInfo::TLSClearInfo: {
   5866        mDatastore->Clear(mDatabase);
   5867 
   5868        mDatastore->NotifyOtherProcessObservers(mDatabase, mDocumentURI,
   5869                                                VoidString(), VoidLSValue(),
   5870                                                VoidLSValue());
   5871 
   5872        break;
   5873      }
   5874 
   5875      default:
   5876        MOZ_CRASH("Should never get here!");
   5877    }
   5878  }
   5879 
   5880  mUsage = mDatastore->EndUpdateBatch(-1);
   5881 
   5882  return IPC_OK();
   5883 }
   5884 
   5885 mozilla::ipc::IPCResult Snapshot::RecvAsyncCheckpoint(
   5886    nsTArray<LSWriteInfo>&& aWriteInfos) {
   5887  return Checkpoint(std::move(aWriteInfos));
   5888 }
   5889 
   5890 mozilla::ipc::IPCResult Snapshot::RecvAsyncCheckpointAndNotify(
   5891    nsTArray<LSWriteAndNotifyInfo>&& aWriteAndNotifyInfos) {
   5892  return CheckpointAndNotify(std::move(aWriteAndNotifyInfos));
   5893 }
   5894 
   5895 mozilla::ipc::IPCResult Snapshot::RecvSyncCheckpoint(
   5896    nsTArray<LSWriteInfo>&& aWriteInfos) {
   5897  return Checkpoint(std::move(aWriteInfos));
   5898 }
   5899 
   5900 mozilla::ipc::IPCResult Snapshot::RecvSyncCheckpointAndNotify(
   5901    nsTArray<LSWriteAndNotifyInfo>&& aWriteAndNotifyInfos) {
   5902  return CheckpointAndNotify(std::move(aWriteAndNotifyInfos));
   5903 }
   5904 
   5905 mozilla::ipc::IPCResult Snapshot::RecvAsyncFinish() {
   5906  AssertIsOnBackgroundThread();
   5907 
   5908  if (NS_WARN_IF(mFinishReceived)) {
   5909    MOZ_ASSERT_UNLESS_FUZZING(false);
   5910    return IPC_FAIL(this, "Already finished");
   5911  }
   5912 
   5913  Finish();
   5914 
   5915  return IPC_OK();
   5916 }
   5917 
   5918 mozilla::ipc::IPCResult Snapshot::RecvSyncFinish() {
   5919  AssertIsOnBackgroundThread();
   5920 
   5921  if (NS_WARN_IF(mFinishReceived)) {
   5922    MOZ_ASSERT_UNLESS_FUZZING(false);
   5923    return IPC_FAIL(this, "Already finished");
   5924  }
   5925 
   5926  Finish();
   5927 
   5928  return IPC_OK();
   5929 }
   5930 
   5931 mozilla::ipc::IPCResult Snapshot::RecvLoaded() {
   5932  AssertIsOnBackgroundThread();
   5933 
   5934  if (NS_WARN_IF(mFinishReceived)) {
   5935    return IPC_FAIL(this, "mFinishReceived already set!");
   5936  }
   5937 
   5938  if (NS_WARN_IF(mLoadedReceived)) {
   5939    return IPC_FAIL(this, "mLoadedReceived already set!");
   5940  }
   5941 
   5942  if (NS_WARN_IF(mLoadedAllItems)) {
   5943    return IPC_FAIL(this, "mLoadedAllItems already set!");
   5944  }
   5945 
   5946  if (NS_WARN_IF(mLoadKeysReceived)) {
   5947    return IPC_FAIL(this, "mLoadKeysReceived already set!");
   5948  }
   5949 
   5950  mLoadedReceived = true;
   5951 
   5952  mLoadedItems.Clear();
   5953  mUnknownItems.Clear();
   5954  mValues.Clear();
   5955  mKeys.Clear();
   5956  mLoadedAllItems = true;
   5957  mLoadKeysReceived = true;
   5958 
   5959  return IPC_OK();
   5960 }
   5961 
   5962 mozilla::ipc::IPCResult Snapshot::RecvLoadValueAndMoreItems(
   5963    const nsAString& aKey, LSValue* aValue, nsTArray<LSItemInfo>* aItemInfos) {
   5964  AssertIsOnBackgroundThread();
   5965  MOZ_ASSERT(aValue);
   5966  MOZ_ASSERT(aItemInfos);
   5967  MOZ_ASSERT(mDatastore);
   5968 
   5969  if (NS_WARN_IF(mFinishReceived)) {
   5970    return IPC_FAIL(this, "mFinishReceived already set!");
   5971  }
   5972 
   5973  if (NS_WARN_IF(mLoadedReceived)) {
   5974    return IPC_FAIL(this, "mLoadedReceived already set!");
   5975  }
   5976 
   5977  if (NS_WARN_IF(mLoadedAllItems)) {
   5978    return IPC_FAIL(this, "mLoadedAllItems already set!");
   5979  }
   5980 
   5981  if (mLoadedItems.Contains(aKey)) {
   5982    return IPC_FAIL(this, "mLoadedItems already contains aKey!");
   5983  }
   5984 
   5985  if (mUnknownItems.Contains(aKey)) {
   5986    return IPC_FAIL(this, "mUnknownItems already contains aKey!");
   5987  }
   5988 
   5989  if (auto entry = mValues.Lookup(aKey)) {
   5990    *aValue = entry.Data();
   5991    entry.Remove();
   5992  } else {
   5993    mDatastore->GetItem(aKey, *aValue);
   5994  }
   5995 
   5996  if (aValue->IsVoid()) {
   5997    mUnknownItems.Insert(aKey);
   5998  } else {
   5999    mLoadedItems.PutEntry(aKey);
   6000 
   6001    // mLoadedItems.Count()==mTotalLength is checked below.
   6002  }
   6003 
   6004  // Load some more key/value pairs (as many as the snapshot gradual prefill
   6005  // byte budget allows).
   6006 
   6007  if (gSnapshotGradualPrefill > 0) {
   6008    const nsTArray<LSItemInfo>& orderedItems = mDatastore->GetOrderedItems();
   6009 
   6010    uint32_t length;
   6011    if (mSavedKeys) {
   6012      length = mKeys.Length();
   6013    } else {
   6014      length = orderedItems.Length();
   6015    }
   6016 
   6017    int64_t size = 0;
   6018    while (mNextLoadIndex < length) {
   6019      // If the datastore's ordering has changed, mSavedKeys will be true and
   6020      // mKeys contains an ordered list of the keys. Otherwise we can use the
   6021      // datastore's key ordering which is still the same as when the snapshot
   6022      // was created.
   6023 
   6024      nsString key;
   6025      if (mSavedKeys) {
   6026        key = mKeys[mNextLoadIndex];
   6027      } else {
   6028        key = orderedItems[mNextLoadIndex].key();
   6029      }
   6030 
   6031      // Normally we would do this:
   6032      // if (!mLoadedItems.GetEntry(key)) {
   6033      //   ...
   6034      //   mLoadedItems.PutEntry(key);
   6035      // }
   6036      // but that requires two hash lookups. We can reduce that to just one
   6037      // hash lookup if we always call PutEntry and check the number of entries
   6038      // before and after the put (which is very cheap). However, if we reach
   6039      // the prefill limit, we need to call RemoveEntry, but that is also cheap
   6040      // because we pass the entry (not the key).
   6041 
   6042      uint32_t countBeforePut = mLoadedItems.Count();
   6043      auto loadedItemEntry = mLoadedItems.PutEntry(key);
   6044      if (countBeforePut != mLoadedItems.Count()) {
   6045        // Check mValues first since that contains values as they existed when
   6046        // our snapshot was created, but have since been changed/removed in the
   6047        // datastore. If it's not there, then the datastore has the
   6048        // still-current value. However, if the datastore's key ordering has
   6049        // changed, we need to do a hash lookup rather than being able to do an
   6050        // optimized direct access to the index.
   6051 
   6052        LSValue value;
   6053        auto valueEntry = mValues.Lookup(key);
   6054        if (valueEntry) {
   6055          value = valueEntry.Data();
   6056        } else if (mSavedKeys) {
   6057          mDatastore->GetItem(nsString(key), value);
   6058        } else {
   6059          value = orderedItems[mNextLoadIndex].value();
   6060        }
   6061 
   6062        // All not loaded keys must have a value.
   6063        MOZ_ASSERT(!value.IsVoid());
   6064 
   6065        size += static_cast<int64_t>(key.Length()) +
   6066                static_cast<int64_t>(value.Length());
   6067 
   6068        if (size > gSnapshotGradualPrefill) {
   6069          mLoadedItems.RemoveEntry(loadedItemEntry);
   6070 
   6071          // mNextLoadIndex is not incremented, so we will resume at the same
   6072          // position next time.
   6073          break;
   6074        }
   6075 
   6076        if (valueEntry) {
   6077          valueEntry.Remove();
   6078        }
   6079 
   6080        LSItemInfo* itemInfo = aItemInfos->AppendElement();
   6081        itemInfo->key() = key;
   6082        itemInfo->value() = value;
   6083      }
   6084 
   6085      mNextLoadIndex++;
   6086    }
   6087  }
   6088 
   6089  if (mLoadedItems.Count() == mTotalLength) {
   6090    mLoadedItems.Clear();
   6091    mUnknownItems.Clear();
   6092 #ifdef DEBUG
   6093    const bool allValuesVoid =
   6094        std::all_of(mValues.Values().cbegin(), mValues.Values().cend(),
   6095                    [](const auto& entry) { return entry.IsVoid(); });
   6096    MOZ_ASSERT(allValuesVoid);
   6097 #endif
   6098    mValues.Clear();
   6099    mLoadedAllItems = true;
   6100  }
   6101 
   6102  return IPC_OK();
   6103 }
   6104 
   6105 mozilla::ipc::IPCResult Snapshot::RecvLoadKeys(nsTArray<nsString>* aKeys) {
   6106  AssertIsOnBackgroundThread();
   6107  MOZ_ASSERT(aKeys);
   6108  MOZ_ASSERT(mDatastore);
   6109 
   6110  if (NS_WARN_IF(mFinishReceived)) {
   6111    return IPC_FAIL(this, "mFinishReceived already set!");
   6112  }
   6113 
   6114  if (NS_WARN_IF(mLoadedReceived)) {
   6115    return IPC_FAIL(this, "mLoadedReceived already set!");
   6116  }
   6117 
   6118  if (NS_WARN_IF(mLoadKeysReceived)) {
   6119    return IPC_FAIL(this, "mLoadKeysReceived already set!");
   6120  }
   6121 
   6122  mLoadKeysReceived = true;
   6123 
   6124  if (mSavedKeys) {
   6125    aKeys->AppendElements(std::move(mKeys));
   6126  } else {
   6127    mDatastore->GetKeys(*aKeys);
   6128  }
   6129 
   6130  return IPC_OK();
   6131 }
   6132 
   6133 mozilla::ipc::IPCResult Snapshot::RecvIncreasePeakUsage(const int64_t& aMinSize,
   6134                                                        int64_t* aSize) {
   6135  AssertIsOnBackgroundThread();
   6136  MOZ_ASSERT(aSize);
   6137 
   6138  if (NS_WARN_IF(aMinSize <= 0)) {
   6139    return IPC_FAIL(this, "aMinSize not valid!");
   6140  }
   6141 
   6142  if (NS_WARN_IF(mFinishReceived)) {
   6143    return IPC_FAIL(this, "mFinishReceived already set!");
   6144  }
   6145 
   6146  int64_t size =
   6147      mDatastore->AttemptToUpdateUsage(aMinSize, /* aInitial */ false);
   6148 
   6149  mPeakUsage += size;
   6150 
   6151  *aSize = size;
   6152 
   6153  return IPC_OK();
   6154 }
   6155 
   6156 /*******************************************************************************
   6157 * Observer
   6158 ******************************************************************************/
   6159 
   6160 Observer::Observer(const Maybe<ContentParentId>& aContentParentId,
   6161                   const nsACString& aOrigin)
   6162    : mContentParentId(aContentParentId),
   6163      mOrigin(aOrigin),
   6164      mActorDestroyed(false) {
   6165  AssertIsOnBackgroundThread();
   6166 }
   6167 
   6168 Observer::~Observer() { MOZ_ASSERT(mActorDestroyed); }
   6169 
   6170 void Observer::Observe(Database* aDatabase, const nsString& aDocumentURI,
   6171                       const nsString& aKey, const LSValue& aOldValue,
   6172                       const LSValue& aNewValue) {
   6173  AssertIsOnBackgroundThread();
   6174  MOZ_ASSERT(aDatabase);
   6175 
   6176  (void)SendObserve(aDatabase->GetPrincipalInfo(),
   6177                    aDatabase->PrivateBrowsingId(), aDocumentURI, aKey,
   6178                    aOldValue, aNewValue);
   6179 }
   6180 
   6181 void Observer::ActorDestroy(ActorDestroyReason aWhy) {
   6182  AssertIsOnBackgroundThread();
   6183  MOZ_ASSERT(!mActorDestroyed);
   6184 
   6185  mActorDestroyed = true;
   6186 
   6187  MOZ_ASSERT(gObservers);
   6188 
   6189  nsTArray<NotNull<Observer*>>* array;
   6190  gObservers->Get(mOrigin, &array);
   6191  MOZ_ASSERT(array);
   6192 
   6193  array->RemoveElement(this);
   6194 
   6195  if (RefPtr<Datastore> datastore = GetDatastore(mOrigin)) {
   6196    datastore->NoteChangedObserverArray(*array);
   6197  }
   6198 
   6199  if (array->IsEmpty()) {
   6200    gObservers->Remove(mOrigin);
   6201  }
   6202 
   6203  if (!gObservers->Count()) {
   6204    gObservers = nullptr;
   6205  }
   6206 }
   6207 
   6208 mozilla::ipc::IPCResult Observer::RecvDeleteMe() {
   6209  AssertIsOnBackgroundThread();
   6210  MOZ_ASSERT(!mActorDestroyed);
   6211 
   6212  IProtocol* mgr = Manager();
   6213  if (!PBackgroundLSObserverParent::Send__delete__(this)) {
   6214    return IPC_FAIL(mgr, "Send__delete__ failed!");
   6215  }
   6216  return IPC_OK();
   6217 }
   6218 
   6219 /*******************************************************************************
   6220 * LSRequestBase
   6221 ******************************************************************************/
   6222 
   6223 LSRequestBase::LSRequestBase(const LSRequestParams& aParams,
   6224                             const Maybe<ContentParentId>& aContentParentId)
   6225    : mParams(aParams),
   6226      mContentParentId(aContentParentId),
   6227      mState(State::Initial),
   6228      mWaitingForFinish(false) {}
   6229 
   6230 LSRequestBase::~LSRequestBase() {
   6231  MOZ_ASSERT_IF(MayProceedOnNonOwningThread(),
   6232                mState == State::Initial || mState == State::Completed);
   6233 }
   6234 
   6235 void LSRequestBase::Dispatch() {
   6236  AssertIsOnOwningThread();
   6237 
   6238  mState = State::StartingRequest;
   6239 
   6240  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(this));
   6241 }
   6242 
   6243 void LSRequestBase::StringifyState(nsACString& aResult) const {
   6244  AssertIsOnOwningThread();
   6245 
   6246  switch (mState) {
   6247    case State::Initial:
   6248      aResult.AppendLiteral("Initial");
   6249      return;
   6250 
   6251    case State::StartingRequest:
   6252      aResult.AppendLiteral("StartingRequest");
   6253      return;
   6254 
   6255    case State::Nesting:
   6256      aResult.AppendLiteral("Nesting");
   6257      return;
   6258 
   6259    case State::SendingReadyMessage:
   6260      aResult.AppendLiteral("SendingReadyMessage");
   6261      return;
   6262 
   6263    case State::WaitingForFinish:
   6264      aResult.AppendLiteral("WaitingForFinish");
   6265      return;
   6266 
   6267    case State::SendingResults:
   6268      aResult.AppendLiteral("SendingResults");
   6269      return;
   6270 
   6271    case State::Completed:
   6272      aResult.AppendLiteral("Completed");
   6273      return;
   6274 
   6275    default:
   6276      MOZ_CRASH("Bad state!");
   6277  }
   6278 }
   6279 
   6280 void LSRequestBase::Stringify(nsACString& aResult) const {
   6281  AssertIsOnOwningThread();
   6282 
   6283  aResult.AppendLiteral("State:");
   6284  StringifyState(aResult);
   6285 }
   6286 
   6287 void LSRequestBase::Log() {
   6288  AssertIsOnOwningThread();
   6289 
   6290  if (!LS_LOG_TEST()) {
   6291    return;
   6292  }
   6293 
   6294  LS_LOG(("LSRequestBase [%p]", this));
   6295 
   6296  nsCString state;
   6297  StringifyState(state);
   6298 
   6299  LS_LOG(("  mState: %s", state.get()));
   6300 }
   6301 
   6302 nsresult LSRequestBase::NestedRun() { return NS_OK; }
   6303 
   6304 bool LSRequestBase::VerifyRequestParams() {
   6305  AssertIsOnBackgroundThread();
   6306 
   6307  MOZ_ASSERT(mParams.type() != LSRequestParams::T__None);
   6308 
   6309  switch (mParams.type()) {
   6310    case LSRequestParams::TLSRequestPreloadDatastoreParams: {
   6311      const LSRequestCommonParams& params =
   6312          mParams.get_LSRequestPreloadDatastoreParams().commonParams();
   6313 
   6314      if (NS_WARN_IF(!VerifyPrincipalInfo(
   6315              params.principalInfo(), params.storagePrincipalInfo(), false))) {
   6316        return false;
   6317      }
   6318 
   6319      if (NS_WARN_IF(
   6320              !VerifyOriginKey(params.originKey(), params.principalInfo()))) {
   6321        return false;
   6322      }
   6323 
   6324      break;
   6325    }
   6326 
   6327    case LSRequestParams::TLSRequestPrepareDatastoreParams: {
   6328      const LSRequestPrepareDatastoreParams& params =
   6329          mParams.get_LSRequestPrepareDatastoreParams();
   6330 
   6331      const LSRequestCommonParams& commonParams = params.commonParams();
   6332 
   6333      if (NS_WARN_IF(!VerifyPrincipalInfo(commonParams.principalInfo(),
   6334                                          commonParams.storagePrincipalInfo(),
   6335                                          false))) {
   6336        return false;
   6337      }
   6338 
   6339      if (params.clientPrincipalInfo() &&
   6340          NS_WARN_IF(!VerifyPrincipalInfo(commonParams.principalInfo(),
   6341                                          params.clientPrincipalInfo().ref(),
   6342                                          true))) {
   6343        return false;
   6344      }
   6345 
   6346      if (NS_WARN_IF(!VerifyClientId(mContentParentId,
   6347                                     params.clientPrincipalInfo(),
   6348                                     params.clientId()))) {
   6349        return false;
   6350      }
   6351 
   6352      if (NS_WARN_IF(!VerifyOriginKey(commonParams.originKey(),
   6353                                      commonParams.principalInfo()))) {
   6354        return false;
   6355      }
   6356 
   6357      break;
   6358    }
   6359 
   6360    case LSRequestParams::TLSRequestPrepareObserverParams: {
   6361      const LSRequestPrepareObserverParams& params =
   6362          mParams.get_LSRequestPrepareObserverParams();
   6363 
   6364      if (NS_WARN_IF(!VerifyPrincipalInfo(
   6365              params.principalInfo(), params.storagePrincipalInfo(), false))) {
   6366        return false;
   6367      }
   6368 
   6369      if (params.clientPrincipalInfo() &&
   6370          NS_WARN_IF(!VerifyPrincipalInfo(params.principalInfo(),
   6371                                          params.clientPrincipalInfo().ref(),
   6372                                          true))) {
   6373        return false;
   6374      }
   6375 
   6376      if (NS_WARN_IF(!VerifyClientId(mContentParentId,
   6377                                     params.clientPrincipalInfo(),
   6378                                     params.clientId()))) {
   6379        return false;
   6380      }
   6381 
   6382      break;
   6383    }
   6384 
   6385    default:
   6386      MOZ_CRASH("Should never get here!");
   6387  }
   6388 
   6389  return true;
   6390 }
   6391 
   6392 nsresult LSRequestBase::StartRequest() {
   6393  AssertIsOnOwningThread();
   6394  MOZ_ASSERT(mState == State::StartingRequest);
   6395 
   6396  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
   6397      !MayProceed()) {
   6398    return NS_ERROR_ABORT;
   6399  }
   6400 
   6401 #ifdef DEBUG
   6402  // Always verify parameters in DEBUG builds!
   6403  bool trustParams = false;
   6404 #else
   6405  bool trustParams = !BackgroundParent::IsOtherProcessActor(Manager());
   6406 #endif
   6407 
   6408  if (!trustParams && NS_WARN_IF(!VerifyRequestParams())) {
   6409    return NS_ERROR_FAILURE;
   6410  }
   6411 
   6412  QM_TRY(MOZ_TO_RESULT(Start()));
   6413 
   6414  return NS_OK;
   6415 }
   6416 
   6417 void LSRequestBase::SendReadyMessage() {
   6418  AssertIsOnOwningThread();
   6419  MOZ_ASSERT(mState == State::SendingReadyMessage);
   6420 
   6421  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
   6422      !MayProceed()) {
   6423    MaybeSetFailureCode(NS_ERROR_ABORT);
   6424  }
   6425 
   6426  nsresult rv = SendReadyMessageInternal();
   6427  if (NS_WARN_IF(NS_FAILED(rv))) {
   6428    MaybeSetFailureCode(rv);
   6429 
   6430    FinishInternal();
   6431  }
   6432 }
   6433 
   6434 nsresult LSRequestBase::SendReadyMessageInternal() {
   6435  AssertIsOnOwningThread();
   6436  MOZ_ASSERT(mState == State::SendingReadyMessage);
   6437 
   6438  if (!MayProceed()) {
   6439    return NS_ERROR_ABORT;
   6440  }
   6441 
   6442  if (NS_WARN_IF(!SendReady())) {
   6443    return NS_ERROR_FAILURE;
   6444  }
   6445 
   6446  localstorage::NotifyRequestFinalizationStarted();
   6447 
   6448  mState = State::WaitingForFinish;
   6449 
   6450  mWaitingForFinish = true;
   6451 
   6452  return NS_OK;
   6453 }
   6454 
   6455 void LSRequestBase::Finish() {
   6456  AssertIsOnOwningThread();
   6457  MOZ_ASSERT(mState == State::WaitingForFinish);
   6458 
   6459  mWaitingForFinish = false;
   6460 
   6461  FinishInternal();
   6462 }
   6463 
   6464 void LSRequestBase::FinishInternal() {
   6465  AssertIsOnOwningThread();
   6466  MOZ_ASSERT(mState == State::SendingReadyMessage ||
   6467             mState == State::WaitingForFinish);
   6468 
   6469  mState = State::SendingResults;
   6470 
   6471  // This LSRequestBase can only be held alive by the IPDL. Run() can end up
   6472  // with clearing that last reference. So we need to add a self reference here.
   6473  RefPtr<LSRequestBase> kungFuDeathGrip = this;
   6474 
   6475  MOZ_ALWAYS_SUCCEEDS(this->Run());
   6476 }
   6477 
   6478 void LSRequestBase::SendResults() {
   6479  AssertIsOnOwningThread();
   6480  MOZ_ASSERT(mState == State::SendingResults);
   6481 
   6482  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
   6483      !MayProceed()) {
   6484    MaybeSetFailureCode(NS_ERROR_ABORT);
   6485  }
   6486 
   6487  if (MayProceed()) {
   6488    LSRequestResponse response;
   6489 
   6490    if (NS_SUCCEEDED(ResultCode())) {
   6491      GetResponse(response);
   6492 
   6493      MOZ_ASSERT(response.type() != LSRequestResponse::T__None);
   6494 
   6495      if (response.type() == LSRequestResponse::Tnsresult) {
   6496        MOZ_ASSERT(NS_FAILED(response.get_nsresult()));
   6497 
   6498        SetFailureCode(response.get_nsresult());
   6499      }
   6500    } else {
   6501      response = ResultCode();
   6502    }
   6503 
   6504    (void)PBackgroundLSRequestParent::Send__delete__(this, std::move(response));
   6505  }
   6506 
   6507  Cleanup();
   6508 
   6509  mState = State::Completed;
   6510 }
   6511 
   6512 NS_IMETHODIMP
   6513 LSRequestBase::Run() {
   6514  nsresult rv;
   6515 
   6516  switch (mState) {
   6517    case State::StartingRequest:
   6518      rv = StartRequest();
   6519      break;
   6520 
   6521    case State::Nesting:
   6522      rv = NestedRun();
   6523      break;
   6524 
   6525    case State::SendingReadyMessage:
   6526      SendReadyMessage();
   6527      return NS_OK;
   6528 
   6529    case State::SendingResults:
   6530      SendResults();
   6531      return NS_OK;
   6532 
   6533    default:
   6534      MOZ_CRASH("Bad state!");
   6535  }
   6536 
   6537  if (NS_WARN_IF(NS_FAILED(rv)) && mState != State::SendingReadyMessage) {
   6538    MaybeSetFailureCode(rv);
   6539 
   6540    // Must set mState before dispatching otherwise we will race with the owning
   6541    // thread.
   6542    mState = State::SendingReadyMessage;
   6543 
   6544    if (IsOnOwningThread()) {
   6545      SendReadyMessage();
   6546    } else {
   6547      MOZ_ALWAYS_SUCCEEDS(
   6548          OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
   6549    }
   6550  }
   6551 
   6552  return NS_OK;
   6553 }
   6554 
   6555 void LSRequestBase::ActorDestroy(ActorDestroyReason aWhy) {
   6556  AssertIsOnOwningThread();
   6557 
   6558  NoteComplete();
   6559 
   6560  // Assume ActorDestroy can happen at any time, so we can't probe the current
   6561  // state since mState can be modified on any thread (only one thread at a time
   6562  // based on the state machine).  However we can use mWaitingForFinish which is
   6563  // only touched on the owning thread.  If mWaitingForFinisg is true, we can
   6564  // also modify mState since we are guaranteed that there are no pending
   6565  // runnables which would probe mState to decide what code needs to run (there
   6566  // shouldn't be any running runnables on other threads either).
   6567 
   6568  if (mWaitingForFinish) {
   6569    Finish();
   6570  }
   6571 
   6572  // We don't have to handle the case when mWaitingForFinish is not true since
   6573  // it means that either nothing has been initialized yet, so nothing to
   6574  // cleanup or there are pending runnables that will detect that the actor has
   6575  // been destroyed and cleanup accordingly.
   6576 }
   6577 
   6578 mozilla::ipc::IPCResult LSRequestBase::RecvCancel() {
   6579  AssertIsOnOwningThread();
   6580 
   6581  Log();
   6582 
   6583  glean::localstorage_request::recv_cancel_counter.Add();
   6584 
   6585  const char* crashOnCancel = PR_GetEnv("LSNG_CRASH_ON_CANCEL");
   6586  if (crashOnCancel) {
   6587    MOZ_CRASH("LSNG: Crash on cancel.");
   6588  }
   6589 
   6590  IProtocol* mgr = Manager();
   6591  if (!PBackgroundLSRequestParent::Send__delete__(this, NS_ERROR_ABORT)) {
   6592    return IPC_FAIL(mgr, "Send__delete__ failed!");
   6593  }
   6594 
   6595  return IPC_OK();
   6596 }
   6597 
   6598 mozilla::ipc::IPCResult LSRequestBase::RecvFinish() {
   6599  AssertIsOnOwningThread();
   6600 
   6601  Finish();
   6602 
   6603  return IPC_OK();
   6604 }
   6605 
   6606 /*******************************************************************************
   6607 * PrepareDatastoreOp
   6608 ******************************************************************************/
   6609 
   6610 PrepareDatastoreOp::PrepareDatastoreOp(
   6611    const LSRequestParams& aParams,
   6612    const Maybe<ContentParentId>& aContentParentId)
   6613    : LSRequestBase(aParams, aContentParentId),
   6614      mProcessingTimerId(
   6615          glean::localstorage_request::prepare_datastore_processing_time
   6616              .Start()),
   6617      mLoadDataOp(nullptr),
   6618      mPrivateBrowsingId(0),
   6619      mUsage(0),
   6620      mSizeOfKeys(0),
   6621      mSizeOfItems(0),
   6622      mDatastoreId(0),
   6623      mNestedState(NestedState::BeforeNesting),
   6624      mForPreload(aParams.type() ==
   6625                  LSRequestParams::TLSRequestPreloadDatastoreParams),
   6626      mEnableMigration(
   6627          StaticPrefs::
   6628              dom_storage_enable_migration_from_unsupported_legacy_implementation()),
   6629      mDatabaseNotAvailable(false),
   6630      mInvalidated(false)
   6631 #ifdef DEBUG
   6632      ,
   6633      mDEBUGUsage(0)
   6634 #endif
   6635 {
   6636  MOZ_ASSERT(
   6637      aParams.type() == LSRequestParams::TLSRequestPreloadDatastoreParams ||
   6638      aParams.type() == LSRequestParams::TLSRequestPrepareDatastoreParams);
   6639 }
   6640 
   6641 PrepareDatastoreOp::~PrepareDatastoreOp() {
   6642  MOZ_DIAGNOSTIC_ASSERT(mDirectoryLockHandle.IsInert());
   6643  MOZ_DIAGNOSTIC_ASSERT(mExtraDirectoryLockHandle.IsInert());
   6644  MOZ_ASSERT_IF(MayProceedOnNonOwningThread(),
   6645                mState == State::Initial || mState == State::Completed);
   6646  MOZ_ASSERT(!mLoadDataOp);
   6647 }
   6648 
   6649 void PrepareDatastoreOp::StringifyNestedState(nsACString& aResult) const {
   6650  AssertIsOnOwningThread();
   6651 
   6652  switch (mNestedState) {
   6653    case NestedState::BeforeNesting:
   6654      aResult.AppendLiteral("BeforeNesting");
   6655      return;
   6656 
   6657    case NestedState::CheckExistingOperations:
   6658      aResult.AppendLiteral("CheckExistingOperations");
   6659      return;
   6660 
   6661    case NestedState::CheckClosingDatastore:
   6662      aResult.AppendLiteral("CheckClosingDatastore");
   6663      return;
   6664 
   6665    case NestedState::PreparationPending:
   6666      aResult.AppendLiteral("PreparationPending");
   6667      return;
   6668 
   6669    case NestedState::DirectoryOpenPending:
   6670      aResult.AppendLiteral("DirectoryOpenPending");
   6671      return;
   6672 
   6673    case NestedState::DatabaseWorkOpen:
   6674      aResult.AppendLiteral("DatabaseWorkOpen");
   6675      return;
   6676 
   6677    case NestedState::BeginLoadData:
   6678      aResult.AppendLiteral("BeginLoadData");
   6679      return;
   6680 
   6681    case NestedState::DatabaseWorkLoadData:
   6682      aResult.AppendLiteral("DatabaseWorkLoadData");
   6683      return;
   6684 
   6685    case NestedState::AfterNesting:
   6686      aResult.AppendLiteral("AfterNesting");
   6687      return;
   6688 
   6689    default:
   6690      MOZ_CRASH("Bad state!");
   6691  }
   6692 }
   6693 
   6694 void PrepareDatastoreOp::Stringify(nsACString& aResult) const {
   6695  AssertIsOnOwningThread();
   6696 
   6697  LSRequestBase::Stringify(aResult);
   6698  aResult.Append(kQuotaGenericDelimiter);
   6699 
   6700  aResult.AppendLiteral("Origin:");
   6701  aResult.Append(AnonymizedOriginString(Origin()));
   6702  aResult.Append(kQuotaGenericDelimiter);
   6703 
   6704  aResult.AppendLiteral("NestedState:");
   6705  StringifyNestedState(aResult);
   6706 }
   6707 
   6708 void PrepareDatastoreOp::Log() {
   6709  AssertIsOnOwningThread();
   6710 
   6711  LSRequestBase::Log();
   6712 
   6713  if (!LS_LOG_TEST()) {
   6714    return;
   6715  }
   6716 
   6717  nsCString nestedState;
   6718  StringifyNestedState(nestedState);
   6719 
   6720  LS_LOG(("  mNestedState: %s", nestedState.get()));
   6721 
   6722  switch (mNestedState) {
   6723    case NestedState::CheckClosingDatastore: {
   6724      for (const auto& blockedOn : mBlockedOn) {
   6725        LS_LOG(("  blockedOn: [%p]", blockedOn.get().get()));
   6726 
   6727        blockedOn->Log();
   6728      }
   6729 
   6730      break;
   6731    }
   6732 
   6733    case NestedState::DirectoryOpenPending: {
   6734      MOZ_ASSERT(mPendingDirectoryLock);
   6735 
   6736      LS_LOG(("  mPendingDirectoryLock: [%p]", mPendingDirectoryLock.get()));
   6737 
   6738      mPendingDirectoryLock->Log();
   6739 
   6740      break;
   6741    }
   6742 
   6743    default:;
   6744  }
   6745 }
   6746 
   6747 nsresult PrepareDatastoreOp::Start() {
   6748  AssertIsOnOwningThread();
   6749  MOZ_ASSERT(mState == State::StartingRequest);
   6750  MOZ_ASSERT(mNestedState == NestedState::BeforeNesting);
   6751  MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
   6752  MOZ_ASSERT(MayProceed());
   6753 
   6754  QM_TRY(QuotaManager::EnsureCreated());
   6755 
   6756  const LSRequestCommonParams& commonParams =
   6757      mForPreload
   6758          ? mParams.get_LSRequestPreloadDatastoreParams().commonParams()
   6759          : mParams.get_LSRequestPrepareDatastoreParams().commonParams();
   6760 
   6761  const PrincipalInfo& storagePrincipalInfo =
   6762      commonParams.storagePrincipalInfo();
   6763 
   6764  if (storagePrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
   6765    mOriginMetadata = {quota::GetInfoForChrome(), PERSISTENCE_TYPE_DEFAULT};
   6766  } else {
   6767    MOZ_ASSERT(storagePrincipalInfo.type() ==
   6768               PrincipalInfo::TContentPrincipalInfo);
   6769 
   6770    QM_TRY_UNWRAP(auto principalMetadata,
   6771                  quota::GetInfoFromValidatedPrincipalInfo(
   6772                      *QuotaManager::Get(), storagePrincipalInfo));
   6773 
   6774    mOriginMetadata.mSuffix = std::move(principalMetadata.mSuffix);
   6775    mOriginMetadata.mGroup = std::move(principalMetadata.mGroup);
   6776    // XXX We can probably get rid of mMainThreadOrigin if we change
   6777    // LSRequestBase::Dispatch to synchronously run LSRequestBase::StartRequest
   6778    // through LSRequestBase::Run.
   6779    mMainThreadOrigin = std::move(principalMetadata.mOrigin);
   6780    mOriginMetadata.mStorageOrigin =
   6781        std::move(principalMetadata.mStorageOrigin);
   6782    mOriginMetadata.mIsPrivate = principalMetadata.mIsPrivate;
   6783    mOriginMetadata.mPersistenceType = principalMetadata.mIsPrivate
   6784                                           ? PERSISTENCE_TYPE_PRIVATE
   6785                                           : PERSISTENCE_TYPE_DEFAULT;
   6786  }
   6787 
   6788  mState = State::Nesting;
   6789  mNestedState = NestedState::OpenDirectory;
   6790 
   6791  // XXX We could call OpenDirectory directly here or even fold it here instead
   6792  // of dispatching to the same event target.
   6793  MOZ_ALWAYS_SUCCEEDS(OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
   6794 
   6795  return NS_OK;
   6796 }
   6797 
   6798 nsresult PrepareDatastoreOp::OpenDirectory() {
   6799  AssertIsOnOwningThread();
   6800  MOZ_ASSERT(mState == State::Nesting);
   6801  MOZ_ASSERT(mNestedState == NestedState::OpenDirectory);
   6802  MOZ_ASSERT(!mDirectoryLockHandle);
   6803 
   6804  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
   6805      !MayProceed()) {
   6806    return NS_ERROR_ABORT;
   6807  }
   6808 
   6809  const LSRequestCommonParams& commonParams =
   6810      mForPreload
   6811          ? mParams.get_LSRequestPreloadDatastoreParams().commonParams()
   6812          : mParams.get_LSRequestPrepareDatastoreParams().commonParams();
   6813 
   6814  const PrincipalInfo& storagePrincipalInfo =
   6815      commonParams.storagePrincipalInfo();
   6816 
   6817  nsCString originAttrSuffix;
   6818  uint32_t privateBrowsingId;
   6819 
   6820  if (storagePrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
   6821    privateBrowsingId = 0;
   6822  } else {
   6823    MOZ_ASSERT(storagePrincipalInfo.type() ==
   6824               PrincipalInfo::TContentPrincipalInfo);
   6825 
   6826    const ContentPrincipalInfo& info =
   6827        storagePrincipalInfo.get_ContentPrincipalInfo();
   6828    const OriginAttributes& attrs = info.attrs();
   6829    attrs.CreateSuffix(originAttrSuffix);
   6830 
   6831    privateBrowsingId = attrs.mPrivateBrowsingId;
   6832  }
   6833 
   6834  mArchivedOriginScope = ArchivedOriginScope::CreateFromOrigin(
   6835      originAttrSuffix, commonParams.originKey());
   6836  MOZ_ASSERT(mArchivedOriginScope);
   6837 
   6838  // Normally it's safe to access member variables without a mutex because even
   6839  // though we hop between threads, the variables are never accessed by multiple
   6840  // threads at the same time.
   6841  // However, the methods OriginIsKnown and Origin can be called at any time.
   6842  // So we have to make sure the member variable is set on the same thread as
   6843  // those methods are called.
   6844  mOriginMetadata.mOrigin = mMainThreadOrigin;
   6845 
   6846  MOZ_ASSERT(OriginIsKnown());
   6847 
   6848  mPrivateBrowsingId = privateBrowsingId;
   6849 
   6850  QuotaManager* quotaManager = QuotaManager::Get();
   6851  MOZ_ASSERT(quotaManager);
   6852 
   6853  mNestedState = NestedState::DirectoryOpenPending;
   6854 
   6855  quotaManager
   6856      ->OpenClientDirectory(
   6857          {mOriginMetadata, mozilla::dom::quota::Client::LS},
   6858          /* aInitializeOrigin */ !mForPreload || mEnableMigration,
   6859          /* aCreateIfNonExistent */ false, SomeRef(mPendingDirectoryLock))
   6860      ->Then(
   6861          GetCurrentSerialEventTarget(), __func__,
   6862          [self = RefPtr(this)](QuotaManager::ClientDirectoryLockHandlePromise::
   6863                                    ResolveOrRejectValue&& aValue) {
   6864            self->mPendingDirectoryLock = nullptr;
   6865 
   6866            if (aValue.IsResolve()) {
   6867              self->DirectoryLockAcquired(std::move(aValue.ResolveValue()));
   6868            } else {
   6869              self->DirectoryLockFailed();
   6870            }
   6871          });
   6872 
   6873  return NS_OK;
   6874 }
   6875 
   6876 nsresult PrepareDatastoreOp::CheckExistingOperations() {
   6877  AssertIsOnOwningThread();
   6878  MOZ_ASSERT(mState == State::Nesting);
   6879  MOZ_ASSERT(mNestedState == NestedState::CheckExistingOperations);
   6880  MOZ_ASSERT(gPrepareDatastoreOps);
   6881 
   6882  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
   6883      !MayProceed()) {
   6884    return NS_ERROR_ABORT;
   6885  }
   6886 
   6887  mNestedState = NestedState::CheckClosingDatastore;
   6888 
   6889  // See if this PrepareDatastoreOp needs to wait.
   6890  bool foundThis = false;
   6891  bool blocked = false;
   6892 
   6893  for (uint32_t index = gPrepareDatastoreOps->Length(); index > 0; index--) {
   6894    const auto& existingOp = (*gPrepareDatastoreOps)[index - 1];
   6895 
   6896    if (existingOp == this) {
   6897      foundThis = true;
   6898      continue;
   6899    }
   6900 
   6901    if (foundThis && existingOp->Origin() == Origin()) {
   6902      existingOp->AddBlockingOp(*this);
   6903      AddBlockedOnOp(*existingOp);
   6904      blocked = true;
   6905    }
   6906  }
   6907 
   6908  if (!blocked) {
   6909    QM_TRY(MOZ_TO_RESULT(CheckClosingDatastoreInternal()));
   6910  }
   6911 
   6912  return NS_OK;
   6913 }
   6914 
   6915 nsresult PrepareDatastoreOp::CheckClosingDatastore() {
   6916  AssertIsOnOwningThread();
   6917  MOZ_ASSERT(mState == State::Nesting);
   6918  MOZ_ASSERT(mNestedState == NestedState::CheckClosingDatastore);
   6919 
   6920  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
   6921      !MayProceed()) {
   6922    return NS_ERROR_ABORT;
   6923  }
   6924 
   6925  QM_TRY(MOZ_TO_RESULT(CheckClosingDatastoreInternal()));
   6926 
   6927  return NS_OK;
   6928 }
   6929 
   6930 nsresult PrepareDatastoreOp::CheckClosingDatastoreInternal() {
   6931  AssertIsOnOwningThread();
   6932  MOZ_ASSERT(mState == State::Nesting);
   6933  MOZ_ASSERT(mNestedState == NestedState::CheckClosingDatastore);
   6934  MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
   6935  MOZ_ASSERT(MayProceed());
   6936 
   6937  mNestedState = NestedState::PreparationPending;
   6938 
   6939  RefPtr<Datastore> datastore;
   6940  if ((datastore = GetDatastore(Origin())) && datastore->IsClosed()) {
   6941    datastore->WaitForConnectionToComplete(this);
   6942 
   6943    return NS_OK;
   6944  }
   6945 
   6946  QM_TRY(MOZ_TO_RESULT(BeginDatastorePreparationInternal()));
   6947 
   6948  return NS_OK;
   6949 }
   6950 
   6951 nsresult PrepareDatastoreOp::BeginDatastorePreparation() {
   6952  AssertIsOnOwningThread();
   6953  MOZ_ASSERT(mState == State::Nesting);
   6954  MOZ_ASSERT(mNestedState == NestedState::PreparationPending);
   6955 
   6956  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
   6957      !MayProceed()) {
   6958    return NS_ERROR_ABORT;
   6959  }
   6960 
   6961  QM_TRY(MOZ_TO_RESULT(BeginDatastorePreparationInternal()));
   6962 
   6963  return NS_OK;
   6964 }
   6965 
   6966 nsresult PrepareDatastoreOp::BeginDatastorePreparationInternal() {
   6967  AssertIsOnOwningThread();
   6968  MOZ_ASSERT(mState == State::Nesting);
   6969  MOZ_ASSERT(mNestedState == NestedState::PreparationPending);
   6970  MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
   6971  MOZ_ASSERT(MayProceed());
   6972  MOZ_ASSERT(OriginIsKnown());
   6973 
   6974  if ((mDatastore = GetDatastore(Origin()))) {
   6975    MOZ_ASSERT(!mDatastore->IsClosed());
   6976 
   6977    mExtraDirectoryLockHandle = std::move(mDirectoryLockHandle);
   6978 
   6979    mDatastore->NoteLivePrepareDatastoreOp(this);
   6980 
   6981    FinishNesting();
   6982 
   6983    return NS_OK;
   6984  }
   6985 
   6986  SendToIOThread();
   6987 
   6988  return NS_OK;
   6989 }
   6990 
   6991 void PrepareDatastoreOp::SendToIOThread() {
   6992  AssertIsOnOwningThread();
   6993  MOZ_ASSERT(mState == State::Nesting);
   6994  MOZ_ASSERT(mNestedState == NestedState::PreparationPending);
   6995  MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
   6996  MOZ_ASSERT(MayProceed());
   6997 
   6998  // Skip all disk related stuff and transition to SendingReadyMessage if we
   6999  // are preparing a datastore for private browsing.
   7000  // Note that we do use a directory lock for private browsing even though we
   7001  // don't do any stuff on disk. The thing is that without a directory lock,
   7002  // quota manager wouldn't call AbortOperationsForLocks for our private
   7003  // browsing origin when a clear origin operation is requested.
   7004  // AbortOperationsForLocks requests all databases to close and the datastore
   7005  // is destroyed in the end. Any following LocalStorage API call will trigger
   7006  // preparation of a new (empty) datastore.
   7007  if (mPrivateBrowsingId) {
   7008    FinishNesting();
   7009 
   7010    return;
   7011  }
   7012 
   7013  QuotaManager* quotaManager = QuotaManager::Get();
   7014  MOZ_ASSERT(quotaManager);
   7015 
   7016  // Must set this before dispatching otherwise we will race with the IO thread.
   7017  mNestedState = NestedState::DatabaseWorkOpen;
   7018 
   7019  MOZ_ALWAYS_SUCCEEDS(
   7020      quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL));
   7021 
   7022  localstorage::NotifyDatabaseWorkStarted();
   7023 }
   7024 
   7025 nsresult PrepareDatastoreOp::DatabaseWork() {
   7026  GECKO_TRACE_SCOPE("dom::localstorage", "PrepareDatastoreOp::DatabaseWork");
   7027 
   7028  AssertIsOnIOThread();
   7029  MOZ_ASSERT(mArchivedOriginScope);
   7030  MOZ_ASSERT(mUsage == 0);
   7031  MOZ_ASSERT(mState == State::Nesting);
   7032  MOZ_ASSERT(mNestedState == NestedState::DatabaseWorkOpen);
   7033 
   7034  const auto innerFunc = [&](const auto&) -> nsresult {
   7035    // XXX This function is too long, refactor it into helper functions for
   7036    // readability.
   7037 
   7038    if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
   7039        !MayProceedOnNonOwningThread()) {
   7040      return NS_ERROR_ABORT;
   7041    }
   7042 
   7043    QuotaManager* quotaManager = QuotaManager::Get();
   7044    MOZ_ASSERT(quotaManager);
   7045 
   7046    const UsageInfo usageInfo = quotaManager->GetUsageForClient(
   7047        PERSISTENCE_TYPE_DEFAULT, mOriginMetadata,
   7048        mozilla::dom::quota::Client::LS);
   7049 
   7050    const bool hasUsage = usageInfo.DatabaseUsage().isSome();
   7051    MOZ_ASSERT(usageInfo.FileUsage().isNothing());
   7052 
   7053    if (!gArchivedOrigins) {
   7054      QM_TRY(MOZ_TO_RESULT(LoadArchivedOrigins()));
   7055      MOZ_ASSERT(gArchivedOrigins);
   7056    }
   7057 
   7058    bool hasDataForMigration =
   7059        mEnableMigration && mArchivedOriginScope->HasMatches(gArchivedOrigins);
   7060 
   7061    // If there's nothing to preload (except the case when we want to migrate
   7062    // data during preloading), then we can finish the operation without
   7063    // creating a datastore in GetResponse (GetResponse won't create a datastore
   7064    // if mDatatabaseNotAvailable and mForPreload are both true).
   7065    if (mForPreload && !hasUsage && !hasDataForMigration) {
   7066      return DatabaseNotAvailable();
   7067    }
   7068 
   7069    // The origin directory doesn't need to be created when we don't have data
   7070    // for migration. It will be created on the connection thread in
   7071    // Connection::EnsureStorageConnection.
   7072    // However, origin quota must be initialized, GetQuotaObject in GetResponse
   7073    // would fail otherwise.
   7074    QM_TRY_INSPECT(
   7075        const auto& directoryEntry,
   7076        ([hasDataForMigration, &quotaManager,
   7077          this]() -> mozilla::Result<nsCOMPtr<nsIFile>, nsresult> {
   7078          if (hasDataForMigration) {
   7079            QM_TRY_RETURN(quotaManager->GetOrCreateTemporaryOriginDirectory(
   7080                mOriginMetadata));
   7081          }
   7082 
   7083          MOZ_ASSERT(mOriginMetadata.mPersistenceType ==
   7084                     PERSISTENCE_TYPE_DEFAULT);
   7085 
   7086          QM_TRY_RETURN(quotaManager->GetOriginDirectory(mOriginMetadata));
   7087        }()));
   7088 
   7089    QM_TRY(MOZ_TO_RESULT(directoryEntry->Append(
   7090        NS_LITERAL_STRING_FROM_CSTRING(LS_DIRECTORY_NAME))));
   7091 
   7092    QM_TRY_INSPECT(
   7093        const auto& directoryPath,
   7094        MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsString, directoryEntry, GetPath));
   7095 
   7096    // The ls directory doesn't need to be created when we don't have data for
   7097    // migration. It will be created on the connection thread in
   7098    // Connection::EnsureStorageConnection.
   7099    QM_TRY(MOZ_TO_RESULT(
   7100        EnsureDirectoryEntry(directoryEntry,
   7101                             /* aCreateIfNotExists */ hasDataForMigration,
   7102                             /* aIsDirectory */ true)));
   7103 
   7104    QM_TRY(MOZ_TO_RESULT(directoryEntry->Append(kDataFileName)));
   7105 
   7106    QM_TRY(MOZ_TO_RESULT(directoryEntry->GetPath(mDatabaseFilePath)));
   7107 
   7108    // The database doesn't need to be created when we don't have data for
   7109    // migration. It will be created on the connection thread in
   7110    // Connection::EnsureStorageConnection.
   7111    bool alreadyExisted;
   7112    QM_TRY(MOZ_TO_RESULT(
   7113        EnsureDirectoryEntry(directoryEntry,
   7114                             /* aCreateIfNotExists */ hasDataForMigration,
   7115                             /* aIsDirectory */ false, &alreadyExisted)));
   7116 
   7117    if (alreadyExisted) {
   7118      // The database does exist.
   7119      MOZ_ASSERT(hasUsage);
   7120 
   7121      // XXX Change type of mUsage to UsageInfo or DatabaseUsageType.
   7122      mUsage = usageInfo.DatabaseUsage().valueOr(0);
   7123    } else {
   7124      // The database doesn't exist.
   7125      MOZ_ASSERT(!hasUsage);
   7126 
   7127      if (!hasDataForMigration) {
   7128        // The database doesn't exist and we don't have data for migration.
   7129        // Finish the operation, but create an empty datastore in GetResponse
   7130        // (GetResponse will create an empty datastore if mDatabaseNotAvailable
   7131        // is true and mForPreload is false).
   7132        return DatabaseNotAvailable();
   7133      }
   7134    }
   7135 
   7136    // We initialized mDatabaseFilePath and mUsage, GetQuotaObject can now be
   7137    // called.
   7138    const RefPtr<QuotaObject> quotaObject = GetQuotaObject();
   7139 
   7140    QM_TRY(OkIf(quotaObject), Err(NS_ERROR_FAILURE));
   7141 
   7142    QM_TRY_INSPECT(const auto& usageFile, GetUsageFile(directoryPath));
   7143 
   7144    QM_TRY_INSPECT(const auto& usageJournalFile,
   7145                   GetUsageJournalFile(directoryPath));
   7146 
   7147    QM_TRY_INSPECT(
   7148        const auto& connection,
   7149        (CreateStorageConnectionWithRecovery(
   7150            *directoryEntry, *usageFile, Origin(), [&quotaObject, this] {
   7151              // This is called when the usage file was removed or we notice
   7152              // that the usage file doesn't exist anymore. Adjust the usage
   7153              // accordingly.
   7154 
   7155              MOZ_ALWAYS_TRUE(
   7156                  quotaObject->MaybeUpdateSize(0, /* aTruncate */ true));
   7157 
   7158              mUsage = 0;
   7159            })));
   7160 
   7161    QM_TRY(MOZ_TO_RESULT(VerifyDatabaseInformation(connection)));
   7162 
   7163    if (hasDataForMigration) {
   7164      MOZ_ASSERT(mUsage == 0);
   7165 
   7166      {
   7167        QM_TRY_INSPECT(const auto& archiveFile,
   7168                       GetArchiveFile(quotaManager->GetStoragePath()));
   7169 
   7170        auto autoArchiveDatabaseAttacher =
   7171            AutoDatabaseAttacher(connection, archiveFile, "archive"_ns);
   7172 
   7173        QM_TRY(MOZ_TO_RESULT(autoArchiveDatabaseAttacher.Attach()));
   7174 
   7175        QM_TRY_INSPECT(const int64_t& newUsage,
   7176                       GetUsage(*connection, mArchivedOriginScope.get()));
   7177 
   7178        QM_TRY(
   7179            OkIf(quotaObject->MaybeUpdateSize(newUsage, /* aTruncate */ true)),
   7180            NS_ERROR_FILE_NO_DEVICE_SPACE);
   7181 
   7182        auto autoUpdateSize = MakeScopeExit([&quotaObject] {
   7183          MOZ_ALWAYS_TRUE(
   7184              quotaObject->MaybeUpdateSize(0, /* aTruncate */ true));
   7185        });
   7186 
   7187        mozStorageTransaction transaction(
   7188            connection, false, mozIStorageConnection::TRANSACTION_IMMEDIATE);
   7189 
   7190        QM_TRY(MOZ_TO_RESULT(transaction.Start()));
   7191 
   7192        {
   7193          nsCOMPtr<mozIStorageFunction> function = new CompressFunction();
   7194 
   7195          QM_TRY(MOZ_TO_RESULT(
   7196              connection->CreateFunction("compress"_ns, 1, function)));
   7197 
   7198          function = new CompressionTypeFunction();
   7199 
   7200          QM_TRY(MOZ_TO_RESULT(
   7201              connection->CreateFunction("compressionType"_ns, 1, function)));
   7202 
   7203          QM_TRY_INSPECT(
   7204              const auto& stmt,
   7205              MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
   7206                  nsCOMPtr<mozIStorageStatement>, connection, CreateStatement,
   7207                  "INSERT INTO data (key, utf16_length, conversion_type, "
   7208                  "compression_type, value) "
   7209                  "SELECT key, utf16Length(value), :conversionType, "
   7210                  "compressionType(value), compress(value)"
   7211                  "FROM webappsstore2 "
   7212                  "WHERE originKey = :originKey "
   7213                  "AND originAttributes = :originAttributes;"_ns));
   7214 
   7215          QM_TRY(MOZ_TO_RESULT(stmt->BindInt32ByName(
   7216              "conversionType"_ns,
   7217              static_cast<int32_t>(LSValue::ConversionType::UTF16_UTF8))));
   7218 
   7219          QM_TRY(MOZ_TO_RESULT(mArchivedOriginScope->BindToStatement(stmt)));
   7220 
   7221          QM_TRY(MOZ_TO_RESULT(stmt->Execute()));
   7222 
   7223          QM_TRY(MOZ_TO_RESULT(connection->RemoveFunction("compress"_ns)));
   7224 
   7225          QM_TRY(
   7226              MOZ_TO_RESULT(connection->RemoveFunction("compressionType"_ns)));
   7227        }
   7228 
   7229        {
   7230          QM_TRY_INSPECT(
   7231              const auto& stmt,
   7232              MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
   7233                  nsCOMPtr<mozIStorageStatement>, connection, CreateStatement,
   7234                  "UPDATE database SET usage = :usage;"_ns));
   7235 
   7236          QM_TRY(MOZ_TO_RESULT(stmt->BindInt64ByName("usage"_ns, newUsage)));
   7237 
   7238          QM_TRY(MOZ_TO_RESULT(stmt->Execute()));
   7239        }
   7240 
   7241        {
   7242          QM_TRY_INSPECT(
   7243              const auto& stmt,
   7244              MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
   7245                  nsCOMPtr<mozIStorageStatement>, connection, CreateStatement,
   7246                  "DELETE FROM webappsstore2 "
   7247                  "WHERE originKey = :originKey "
   7248                  "AND originAttributes = :originAttributes;"_ns));
   7249 
   7250          QM_TRY(MOZ_TO_RESULT(mArchivedOriginScope->BindToStatement(stmt)));
   7251          QM_TRY(MOZ_TO_RESULT(stmt->Execute()));
   7252        }
   7253 
   7254        QM_TRY(MOZ_TO_RESULT(
   7255            UpdateUsageFile(usageFile, usageJournalFile, newUsage)));
   7256        QM_TRY(MOZ_TO_RESULT(transaction.Commit()));
   7257 
   7258        autoUpdateSize.release();
   7259 
   7260        QM_TRY(MOZ_TO_RESULT(usageJournalFile->Remove(false)));
   7261 
   7262        mUsage = newUsage;
   7263 
   7264        QM_TRY(MOZ_TO_RESULT(autoArchiveDatabaseAttacher.Detach()));
   7265      }
   7266 
   7267      MOZ_ASSERT(gArchivedOrigins);
   7268      MOZ_ASSERT(mArchivedOriginScope->HasMatches(gArchivedOrigins));
   7269      mArchivedOriginScope->RemoveMatches(gArchivedOrigins);
   7270    }
   7271 
   7272    nsCOMPtr<mozIStorageConnection> shadowConnection;
   7273    if (!gInitializedShadowStorage) {
   7274      QM_TRY_UNWRAP(shadowConnection,
   7275                    CreateShadowStorageConnection(quotaManager->GetBasePath()));
   7276 
   7277      gInitializedShadowStorage = true;
   7278    }
   7279 
   7280    // Must close connections before dispatching otherwise we might race with
   7281    // the connection thread which needs to open the same databases.
   7282    MOZ_ALWAYS_SUCCEEDS(connection->Close());
   7283 
   7284    if (shadowConnection) {
   7285      MOZ_ALWAYS_SUCCEEDS(shadowConnection->Close());
   7286    }
   7287 
   7288    SleepIfEnabled(
   7289        StaticPrefs::dom_storage_databaseInitialization_pauseOnIOThreadMs());
   7290 
   7291    // Must set this before dispatching otherwise we will race with the owning
   7292    // thread.
   7293    mNestedState = NestedState::BeginLoadData;
   7294 
   7295    QM_TRY(
   7296        MOZ_TO_RESULT(OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL)));
   7297 
   7298    return NS_OK;
   7299  };
   7300 
   7301  return ExecuteOriginInitialization(
   7302      mOriginMetadata.mOrigin, LSOriginInitialization::Datastore,
   7303      "dom::localstorage::FirstOriginInitializationAttempt::Datastore"_ns,
   7304      innerFunc);
   7305 }
   7306 
   7307 nsresult PrepareDatastoreOp::DatabaseNotAvailable() {
   7308  AssertIsOnIOThread();
   7309  MOZ_ASSERT(mState == State::Nesting);
   7310  MOZ_ASSERT(mNestedState == NestedState::DatabaseWorkOpen);
   7311 
   7312  mDatabaseNotAvailable = true;
   7313 
   7314  nsresult rv = FinishNestingOnNonOwningThread();
   7315  if (NS_WARN_IF(NS_FAILED(rv))) {
   7316    return rv;
   7317  }
   7318 
   7319  return NS_OK;
   7320 }
   7321 
   7322 nsresult PrepareDatastoreOp::EnsureDirectoryEntry(nsIFile* aEntry,
   7323                                                  bool aCreateIfNotExists,
   7324                                                  bool aIsDirectory,
   7325                                                  bool* aAlreadyExisted) {
   7326  AssertIsOnIOThread();
   7327  MOZ_ASSERT(aEntry);
   7328 
   7329  QM_TRY_INSPECT(const bool& exists,
   7330                 MOZ_TO_RESULT_INVOKE_MEMBER(aEntry, Exists));
   7331 
   7332  if (!exists) {
   7333    if (!aCreateIfNotExists) {
   7334      if (aAlreadyExisted) {
   7335        *aAlreadyExisted = false;
   7336      }
   7337      return NS_OK;
   7338    }
   7339 
   7340    if (aIsDirectory) {
   7341      QM_TRY(MOZ_TO_RESULT(aEntry->Create(nsIFile::DIRECTORY_TYPE, 0755)));
   7342    }
   7343  }
   7344 #ifdef DEBUG
   7345  else {
   7346    bool isDirectory;
   7347    MOZ_ASSERT(NS_SUCCEEDED(aEntry->IsDirectory(&isDirectory)));
   7348    MOZ_ASSERT(isDirectory == aIsDirectory);
   7349  }
   7350 #endif
   7351 
   7352  if (aAlreadyExisted) {
   7353    *aAlreadyExisted = exists;
   7354  }
   7355  return NS_OK;
   7356 }
   7357 
   7358 nsresult PrepareDatastoreOp::VerifyDatabaseInformation(
   7359    mozIStorageConnection* aConnection) {
   7360  AssertIsOnIOThread();
   7361  MOZ_ASSERT(aConnection);
   7362 
   7363  QM_TRY_INSPECT(const auto& stmt,
   7364                 CreateAndExecuteSingleStepStatement<
   7365                     SingleStepResult::ReturnNullIfNoResult>(
   7366                     *aConnection, "SELECT origin FROM database"_ns));
   7367 
   7368  QM_TRY(OkIf(stmt), NS_ERROR_FILE_CORRUPTED);
   7369 
   7370  QM_TRY_INSPECT(const auto& origin, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
   7371                                         nsCString, stmt, GetUTF8String, 0));
   7372 
   7373  QM_TRY(OkIf(QuotaManager::AreOriginsEqualOnDisk(Origin(), origin)),
   7374         NS_ERROR_FILE_CORRUPTED);
   7375 
   7376  return NS_OK;
   7377 }
   7378 
   7379 already_AddRefed<QuotaObject> PrepareDatastoreOp::GetQuotaObject() {
   7380  MOZ_ASSERT(IsOnOwningThread() || IsOnIOThread());
   7381  MOZ_ASSERT(!mOriginMetadata.mGroup.IsEmpty());
   7382  MOZ_ASSERT(OriginIsKnown());
   7383  MOZ_ASSERT(!mDatabaseFilePath.IsEmpty());
   7384 
   7385  QuotaManager* quotaManager = QuotaManager::Get();
   7386  MOZ_ASSERT(quotaManager);
   7387 
   7388  RefPtr<QuotaObject> quotaObject = quotaManager->GetQuotaObject(
   7389      PERSISTENCE_TYPE_DEFAULT, mOriginMetadata,
   7390      mozilla::dom::quota::Client::LS, mDatabaseFilePath, mUsage);
   7391 
   7392  if (!quotaObject) {
   7393    LS_WARNING("Failed to get quota object for group (%s) and origin (%s)!",
   7394               mOriginMetadata.mGroup.get(), Origin().get());
   7395  }
   7396 
   7397  return quotaObject.forget();
   7398 }
   7399 
   7400 nsresult PrepareDatastoreOp::BeginLoadData() {
   7401  AssertIsOnOwningThread();
   7402  MOZ_ASSERT(mState == State::Nesting);
   7403  MOZ_ASSERT(mNestedState == NestedState::BeginLoadData);
   7404  MOZ_ASSERT(!mConnection);
   7405 
   7406  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
   7407      !MayProceed()) {
   7408    return NS_ERROR_ABORT;
   7409  }
   7410 
   7411  if (!gConnectionThread) {
   7412    gConnectionThread = new ConnectionThread();
   7413  }
   7414 
   7415  mConnection = gConnectionThread->CreateConnection(
   7416      mOriginMetadata, std::move(mArchivedOriginScope),
   7417      /* aDatabaseWasNotAvailable */ false);
   7418  MOZ_ASSERT(mConnection);
   7419 
   7420  // Must set this before dispatching otherwise we will race with the
   7421  // connection thread.
   7422  mNestedState = NestedState::DatabaseWorkLoadData;
   7423 
   7424  // Can't assign to mLoadDataOp directly since that's a weak reference and
   7425  // LoadDataOp is reference counted.
   7426  RefPtr<LoadDataOp> loadDataOp = new LoadDataOp(this);
   7427 
   7428  // This add refs loadDataOp.
   7429  mConnection->Dispatch(loadDataOp);
   7430 
   7431  // This is cleared in LoadDataOp::Cleanup() before the load data op is
   7432  // destroyed.
   7433  mLoadDataOp = loadDataOp;
   7434 
   7435  return NS_OK;
   7436 }
   7437 
   7438 void PrepareDatastoreOp::FinishNesting() {
   7439  AssertIsOnOwningThread();
   7440  MOZ_ASSERT(mState == State::Nesting);
   7441 
   7442  // The caller holds a strong reference to us, no need for a self reference
   7443  // before calling Run().
   7444 
   7445  mState = State::SendingReadyMessage;
   7446  mNestedState = NestedState::AfterNesting;
   7447 
   7448  MOZ_ALWAYS_SUCCEEDS(Run());
   7449 }
   7450 
   7451 nsresult PrepareDatastoreOp::FinishNestingOnNonOwningThread() {
   7452  MOZ_ASSERT(!IsOnOwningThread());
   7453  MOZ_ASSERT(mState == State::Nesting);
   7454 
   7455  // Must set mState before dispatching otherwise we will race with the owning
   7456  // thread.
   7457  mState = State::SendingReadyMessage;
   7458  mNestedState = NestedState::AfterNesting;
   7459 
   7460  QM_TRY(
   7461      MOZ_TO_RESULT(OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL)));
   7462 
   7463  return NS_OK;
   7464 }
   7465 
   7466 nsresult PrepareDatastoreOp::NestedRun() {
   7467  nsresult rv;
   7468 
   7469  switch (mNestedState) {
   7470    case NestedState::OpenDirectory:
   7471      rv = OpenDirectory();
   7472      break;
   7473 
   7474    case NestedState::CheckExistingOperations:
   7475      rv = CheckExistingOperations();
   7476      break;
   7477 
   7478    case NestedState::CheckClosingDatastore:
   7479      rv = CheckClosingDatastore();
   7480      break;
   7481 
   7482    case NestedState::PreparationPending:
   7483      rv = BeginDatastorePreparation();
   7484      break;
   7485 
   7486    case NestedState::DatabaseWorkOpen:
   7487      rv = DatabaseWork();
   7488      break;
   7489 
   7490    case NestedState::BeginLoadData:
   7491      rv = BeginLoadData();
   7492      break;
   7493 
   7494    default:
   7495      MOZ_CRASH("Bad state!");
   7496  }
   7497 
   7498  if (NS_WARN_IF(NS_FAILED(rv))) {
   7499    mNestedState = NestedState::AfterNesting;
   7500 
   7501    return rv;
   7502  }
   7503 
   7504  return NS_OK;
   7505 }
   7506 
   7507 void PrepareDatastoreOp::GetResponse(LSRequestResponse& aResponse) {
   7508  AssertIsOnOwningThread();
   7509  MOZ_ASSERT(mState == State::SendingResults);
   7510  MOZ_ASSERT(NS_SUCCEEDED(ResultCode()));
   7511  MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
   7512  MOZ_ASSERT(MayProceed());
   7513 
   7514  // A datastore is not created when we are just trying to preload data and
   7515  // there's no database file.
   7516  if (mDatabaseNotAvailable && mForPreload) {
   7517    LSRequestPreloadDatastoreResponse preloadDatastoreResponse;
   7518 
   7519    aResponse = preloadDatastoreResponse;
   7520 
   7521    return;
   7522  }
   7523 
   7524  if (!mDatastore) {
   7525    MOZ_ASSERT(mUsage == mDEBUGUsage);
   7526 
   7527    RefPtr<QuotaObject> quotaObject;
   7528 
   7529    if (mPrivateBrowsingId == 0) {
   7530      if (!mConnection) {
   7531        // This can happen when there's no database file.
   7532        MOZ_ASSERT(mDatabaseNotAvailable);
   7533 
   7534        // Even though there's no database file, we need to create a connection
   7535        // and pass it to datastore.
   7536        if (!gConnectionThread) {
   7537          gConnectionThread = new ConnectionThread();
   7538        }
   7539 
   7540        mConnection = gConnectionThread->CreateConnection(
   7541            mOriginMetadata, std::move(mArchivedOriginScope),
   7542            /* aDatabaseWasNotAvailable */ true);
   7543        MOZ_ASSERT(mConnection);
   7544      }
   7545 
   7546      quotaObject = GetQuotaObject();
   7547      if (!quotaObject) {
   7548        aResponse = NS_ERROR_FAILURE;
   7549        return;
   7550      }
   7551    }
   7552 
   7553    MOZ_ASSERT(mDirectoryLockHandle);
   7554    MOZ_ASSERT_IF(mDirectoryLockHandle->Invalidated(), mInvalidated);
   7555 
   7556    mDatastore = new Datastore(
   7557        mOriginMetadata, mPrivateBrowsingId, mUsage, mSizeOfKeys, mSizeOfItems,
   7558        std::move(mDirectoryLockHandle), std::move(mConnection),
   7559        std::move(quotaObject), mValues, std::move(mOrderedItems));
   7560 
   7561    mDatastore->NoteLivePrepareDatastoreOp(this);
   7562 
   7563    if (!gDatastores) {
   7564      gDatastores = new DatastoreHashtable();
   7565    }
   7566 
   7567    MOZ_DIAGNOSTIC_ASSERT(!gDatastores->Contains(Origin()));
   7568    gDatastores->InsertOrUpdate(Origin(),
   7569                                WrapMovingNotNullUnchecked(mDatastore));
   7570  }
   7571 
   7572  if (mPrivateBrowsingId && !mInvalidated) {
   7573    if (!gPrivateDatastores) {
   7574      gPrivateDatastores = MakeUnique<PrivateDatastoreHashtable>();
   7575    }
   7576 
   7577    gPrivateDatastores->LookupOrInsertWith(Origin(), [&] {
   7578      auto privateDatastore =
   7579          MakeUnique<PrivateDatastore>(WrapMovingNotNull(mDatastore));
   7580 
   7581      mPrivateDatastoreRegistered.Flip();
   7582 
   7583      return privateDatastore;
   7584    });
   7585  }
   7586 
   7587  mDatastoreId = ++gLastDatastoreId;
   7588 
   7589  if (!gPreparedDatastores) {
   7590    gPreparedDatastores = new PreparedDatastoreHashtable();
   7591  }
   7592  const auto& preparedDatastore = gPreparedDatastores->InsertOrUpdate(
   7593      mDatastoreId, MakeUnique<PreparedDatastore>(
   7594                        mDatastore, mContentParentId, Origin(), mDatastoreId,
   7595                        /* aForPreload */ mForPreload));
   7596 
   7597  if (mInvalidated) {
   7598    preparedDatastore->Invalidate();
   7599  }
   7600 
   7601  mPreparedDatastoreRegistered.Flip();
   7602 
   7603  if (mForPreload) {
   7604    LSRequestPreloadDatastoreResponse preloadDatastoreResponse;
   7605    preloadDatastoreResponse.invalidated() = mInvalidated;
   7606 
   7607    aResponse = preloadDatastoreResponse;
   7608  } else {
   7609    const LSRequestCommonParams& commonParams =
   7610        mParams.get_LSRequestPrepareDatastoreParams().commonParams();
   7611 
   7612    const PrincipalInfo& storagePrincipalInfo =
   7613        commonParams.storagePrincipalInfo();
   7614 
   7615    Endpoint<PBackgroundLSDatabaseParent> parentEndpoint;
   7616    Endpoint<PBackgroundLSDatabaseChild> childEndpoint;
   7617    MOZ_ALWAYS_SUCCEEDS(PBackgroundLSDatabase::CreateEndpoints(&parentEndpoint,
   7618                                                               &childEndpoint));
   7619 
   7620    if (!RecvCreateBackgroundLSDatabaseParent(storagePrincipalInfo,
   7621                                              mPrivateBrowsingId, mDatastoreId,
   7622                                              std::move(parentEndpoint))) {
   7623      aResponse = NS_ERROR_FAILURE;
   7624    } else {
   7625      LSRequestPrepareDatastoreResponse prepareDatastoreResponse;
   7626      prepareDatastoreResponse.databaseChildEndpoint() =
   7627          std::move(childEndpoint);
   7628      prepareDatastoreResponse.invalidated() = mInvalidated;
   7629 
   7630      aResponse = std::move(prepareDatastoreResponse);
   7631    }
   7632  }
   7633 }
   7634 
   7635 void PrepareDatastoreOp::Cleanup() {
   7636  AssertIsOnOwningThread();
   7637 
   7638  if (mDatastore) {
   7639    MOZ_ASSERT(!mDirectoryLockHandle);
   7640    MOZ_ASSERT(!mConnection);
   7641 
   7642    if (NS_FAILED(ResultCode())) {
   7643      if (mPrivateDatastoreRegistered) {
   7644        MOZ_ASSERT(gPrivateDatastores);
   7645        DebugOnly<bool> removed = gPrivateDatastores->Remove(Origin());
   7646        MOZ_ASSERT(removed);
   7647 
   7648        if (!gPrivateDatastores->Count()) {
   7649          gPrivateDatastores = nullptr;
   7650        }
   7651      }
   7652 
   7653      if (mPreparedDatastoreRegistered) {
   7654        // Just in case we failed to send datastoreId to the child, we need to
   7655        // destroy prepared datastore, otherwise it won't be destroyed until
   7656        // the timer fires (after 20 seconds).
   7657        MOZ_ASSERT(gPreparedDatastores);
   7658        MOZ_ASSERT(mDatastoreId > 0);
   7659        DebugOnly<bool> removed = gPreparedDatastores->Remove(mDatastoreId);
   7660        MOZ_ASSERT(removed);
   7661 
   7662        if (!gPreparedDatastores->Count()) {
   7663          gPreparedDatastores = nullptr;
   7664        }
   7665      }
   7666    }
   7667 
   7668    // Make sure to release the datastore on this thread.
   7669 
   7670    mDatastore->NoteFinishedPrepareDatastoreOp(this);
   7671 
   7672    mDatastore = nullptr;
   7673 
   7674    {
   7675      auto extraDirectoryLockHandle = std::move(mExtraDirectoryLockHandle);
   7676    }
   7677 
   7678    CleanupMetadata();
   7679  } else if (mConnection) {
   7680    // If we have a connection then the operation must have failed and there
   7681    // must be a directory lock too.
   7682    MOZ_ASSERT(NS_FAILED(ResultCode()));
   7683    MOZ_ASSERT(mDirectoryLockHandle);
   7684    MOZ_ASSERT(!mExtraDirectoryLockHandle);
   7685 
   7686    // We must close the connection on the connection thread before releasing
   7687    // it on this thread. The directory lock can't be released either.
   7688    nsCOMPtr<nsIRunnable> callback =
   7689        NewRunnableMethod("dom::OpenDatabaseOp::ConnectionClosedCallback", this,
   7690                          &PrepareDatastoreOp::ConnectionClosedCallback);
   7691 
   7692    mConnection->Close(callback);
   7693  } else {
   7694    // If we don't have a connection, but we do have a directory lock then the
   7695    // operation must have failed or we were preloading a datastore and there
   7696    // was no physical database on disk.
   7697    MOZ_ASSERT_IF(mDirectoryLockHandle,
   7698                  NS_FAILED(ResultCode()) || mDatabaseNotAvailable);
   7699    MOZ_ASSERT(!mExtraDirectoryLockHandle);
   7700 
   7701    // There's no connection, so it's safe to release the directory lock and
   7702    // unregister itself from the array.
   7703 
   7704    {
   7705      auto destroyingDdirectoryLockHandle = std::move(mDirectoryLockHandle);
   7706    }
   7707 
   7708    CleanupMetadata();
   7709  }
   7710 }
   7711 
   7712 void PrepareDatastoreOp::ConnectionClosedCallback() {
   7713  AssertIsOnOwningThread();
   7714  MOZ_ASSERT(NS_FAILED(ResultCode()));
   7715  MOZ_ASSERT(mDirectoryLockHandle);
   7716  MOZ_ASSERT(mConnection);
   7717 
   7718  mConnection = nullptr;
   7719 
   7720  {
   7721    auto destroyingDirectoryLockHandle = std::move(mDirectoryLockHandle);
   7722  }
   7723 
   7724  CleanupMetadata();
   7725 }
   7726 
   7727 void PrepareDatastoreOp::CleanupMetadata() {
   7728  AssertIsOnOwningThread();
   7729 
   7730  for (const NotNull<RefPtr<PrepareDatastoreOp>>& blockingOp : mBlocking) {
   7731    blockingOp->MaybeUnblock(*this);
   7732  }
   7733  mBlocking.Clear();
   7734 
   7735  MOZ_ASSERT(gPrepareDatastoreOps);
   7736  gPrepareDatastoreOps->RemoveElement(this);
   7737 
   7738  QuotaManager::MaybeRecordQuotaClientShutdownStep(
   7739      quota::Client::LS, "PrepareDatastoreOp completed"_ns);
   7740 
   7741  if (gPrepareDatastoreOps->IsEmpty()) {
   7742    gPrepareDatastoreOps = nullptr;
   7743  }
   7744 
   7745  if (NS_SUCCEEDED(ResultCode())) {
   7746    glean::localstorage_request::prepare_datastore_processing_time
   7747        .StopAndAccumulate(std::move(mProcessingTimerId));
   7748  } else {
   7749    glean::localstorage_request::prepare_datastore_processing_time.Cancel(
   7750        std::move(mProcessingTimerId));
   7751  }
   7752 }
   7753 
   7754 void PrepareDatastoreOp::ActorDestroy(ActorDestroyReason aWhy) {
   7755  AssertIsOnOwningThread();
   7756 
   7757  LSRequestBase::ActorDestroy(aWhy);
   7758 
   7759  if (mLoadDataOp) {
   7760    mLoadDataOp->NoteComplete();
   7761  }
   7762 }
   7763 
   7764 void PrepareDatastoreOp::DirectoryLockAcquired(
   7765    ClientDirectoryLockHandle aLockHandle) {
   7766  AssertIsOnOwningThread();
   7767  MOZ_ASSERT(mState == State::Nesting);
   7768  MOZ_ASSERT(mNestedState == NestedState::DirectoryOpenPending);
   7769  MOZ_ASSERT(!mDirectoryLockHandle);
   7770 
   7771  mPendingDirectoryLock = nullptr;
   7772 
   7773  mDirectoryLockHandle = std::move(aLockHandle);
   7774 
   7775  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
   7776      !MayProceed() || mDirectoryLockHandle->Invalidated()) {
   7777    MaybeSetFailureCode(NS_ERROR_ABORT);
   7778 
   7779    FinishNesting();
   7780 
   7781    return;
   7782  }
   7783 
   7784  mNestedState = NestedState::CheckExistingOperations;
   7785 
   7786  // XXX We could call CheckExistingOperations directly here or even fold it
   7787  // here instead of dispatching to the same event target
   7788  MOZ_ALWAYS_SUCCEEDS(OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
   7789 }
   7790 
   7791 void PrepareDatastoreOp::DirectoryLockFailed() {
   7792  AssertIsOnOwningThread();
   7793  MOZ_ASSERT(mState == State::Nesting);
   7794  MOZ_ASSERT(mNestedState == NestedState::DirectoryOpenPending);
   7795  MOZ_ASSERT(!mDirectoryLockHandle);
   7796 
   7797  mPendingDirectoryLock = nullptr;
   7798 
   7799  MaybeSetFailureCode(NS_ERROR_FAILURE);
   7800 
   7801  FinishNesting();
   7802 }
   7803 
   7804 nsresult PrepareDatastoreOp::LoadDataOp::DoDatastoreWork() {
   7805  AssertIsOnGlobalConnectionThread();
   7806  MOZ_ASSERT(mConnection);
   7807  MOZ_ASSERT(mPrepareDatastoreOp);
   7808  MOZ_ASSERT(mPrepareDatastoreOp->mState == State::Nesting);
   7809  MOZ_ASSERT(mPrepareDatastoreOp->mNestedState ==
   7810             NestedState::DatabaseWorkLoadData);
   7811 
   7812  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
   7813      !MayProceedOnNonOwningThread()) {
   7814    return NS_ERROR_ABORT;
   7815  }
   7816 
   7817  QM_TRY_INSPECT(
   7818      const auto& stmt,
   7819      mConnection->BorrowCachedStatement(
   7820          "SELECT key, utf16_length, conversion_type, compression_type, value "
   7821          "FROM data;"_ns));
   7822 
   7823  QM_TRY(quota::CollectWhileHasResult(
   7824      *stmt, [this](auto& stmt) -> mozilla::Result<Ok, nsresult> {
   7825        QM_TRY_UNWRAP(auto key, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
   7826                                    nsString, stmt, GetString, 0));
   7827 
   7828        LSValue value;
   7829        QM_TRY(MOZ_TO_RESULT(value.InitFromStatement(&stmt, 1)));
   7830 
   7831        mPrepareDatastoreOp->mValues.InsertOrUpdate(key, value);
   7832        mPrepareDatastoreOp->mSizeOfKeys += key.Length();
   7833        mPrepareDatastoreOp->mSizeOfItems += key.Length() + value.Length();
   7834 #ifdef DEBUG
   7835        mPrepareDatastoreOp->mDEBUGUsage += key.Length() + value.UTF16Length();
   7836 #endif
   7837 
   7838        auto item = mPrepareDatastoreOp->mOrderedItems.AppendElement();
   7839        item->key() = std::move(key);
   7840        item->value() = std::move(value);
   7841 
   7842        return Ok{};
   7843      }));
   7844 
   7845  return NS_OK;
   7846 }
   7847 
   7848 void PrepareDatastoreOp::LoadDataOp::OnSuccess() {
   7849  AssertIsOnOwningThread();
   7850  MOZ_ASSERT(mPrepareDatastoreOp);
   7851  MOZ_ASSERT(mPrepareDatastoreOp->mState == State::Nesting);
   7852  MOZ_ASSERT(mPrepareDatastoreOp->mNestedState ==
   7853             NestedState::DatabaseWorkLoadData);
   7854  MOZ_ASSERT(mPrepareDatastoreOp->mLoadDataOp == this);
   7855 
   7856  mPrepareDatastoreOp->FinishNesting();
   7857 }
   7858 
   7859 void PrepareDatastoreOp::LoadDataOp::OnFailure(nsresult aResultCode) {
   7860  AssertIsOnOwningThread();
   7861  MOZ_ASSERT(mPrepareDatastoreOp);
   7862  MOZ_ASSERT(mPrepareDatastoreOp->mState == State::Nesting);
   7863  MOZ_ASSERT(mPrepareDatastoreOp->mNestedState ==
   7864             NestedState::DatabaseWorkLoadData);
   7865  MOZ_ASSERT(mPrepareDatastoreOp->mLoadDataOp == this);
   7866 
   7867  mPrepareDatastoreOp->SetFailureCode(aResultCode);
   7868 
   7869  mPrepareDatastoreOp->FinishNesting();
   7870 }
   7871 
   7872 void PrepareDatastoreOp::LoadDataOp::Cleanup() {
   7873  AssertIsOnOwningThread();
   7874  MOZ_ASSERT(mPrepareDatastoreOp);
   7875  MOZ_ASSERT(mPrepareDatastoreOp->mLoadDataOp == this);
   7876 
   7877  mPrepareDatastoreOp->mLoadDataOp = nullptr;
   7878  mPrepareDatastoreOp = nullptr;
   7879 
   7880  ConnectionDatastoreOperationBase::Cleanup();
   7881 }
   7882 
   7883 NS_IMPL_ISUPPORTS(PrepareDatastoreOp::CompressFunction, mozIStorageFunction)
   7884 
   7885 NS_IMETHODIMP
   7886 PrepareDatastoreOp::CompressFunction::OnFunctionCall(
   7887    mozIStorageValueArray* aFunctionArguments, nsIVariant** aResult) {
   7888  AssertIsOnIOThread();
   7889  MOZ_ASSERT(aFunctionArguments);
   7890  MOZ_ASSERT(aResult);
   7891 
   7892 #ifdef DEBUG
   7893  {
   7894    uint32_t argCount;
   7895    MOZ_ALWAYS_SUCCEEDS(aFunctionArguments->GetNumEntries(&argCount));
   7896    MOZ_ASSERT(argCount == 1);
   7897 
   7898    int32_t type;
   7899    MOZ_ALWAYS_SUCCEEDS(aFunctionArguments->GetTypeOfIndex(0, &type));
   7900    MOZ_ASSERT(type == mozIStorageValueArray::VALUE_TYPE_TEXT);
   7901  }
   7902 #endif
   7903 
   7904  QM_TRY_INSPECT(const auto& value,
   7905                 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
   7906                     nsCString, aFunctionArguments, GetUTF8String, 0));
   7907 
   7908  nsCString compressed;
   7909  QM_TRY(OkIf(SnappyCompress(value, compressed)), NS_ERROR_OUT_OF_MEMORY);
   7910 
   7911  const nsCString& buffer = compressed.IsVoid() ? value : compressed;
   7912 
   7913  // mozStorage transforms empty blobs into null values, but our database
   7914  // schema doesn't allow null values. We can workaround this by storing
   7915  // empty buffers as UTF8 text (SQLite supports the type affinity, so the type
   7916  // of the column is not fixed).
   7917  nsCOMPtr<nsIVariant> result;
   7918  if (0u == buffer.Length()) {  // Otherwise empty string becomes null
   7919    result = new storage::UTF8TextVariant(buffer);
   7920  } else {
   7921    result = new storage::BlobVariant(std::make_pair(
   7922        static_cast<const void*>(buffer.get()), int(buffer.Length())));
   7923  }
   7924 
   7925  result.forget(aResult);
   7926  return NS_OK;
   7927 }
   7928 
   7929 NS_IMPL_ISUPPORTS(PrepareDatastoreOp::CompressionTypeFunction,
   7930                  mozIStorageFunction)
   7931 
   7932 NS_IMETHODIMP
   7933 PrepareDatastoreOp::CompressionTypeFunction::OnFunctionCall(
   7934    mozIStorageValueArray* aFunctionArguments, nsIVariant** aResult) {
   7935  AssertIsOnIOThread();
   7936  MOZ_ASSERT(aFunctionArguments);
   7937  MOZ_ASSERT(aResult);
   7938 
   7939 #ifdef DEBUG
   7940  {
   7941    uint32_t argCount;
   7942    MOZ_ALWAYS_SUCCEEDS(aFunctionArguments->GetNumEntries(&argCount));
   7943    MOZ_ASSERT(argCount == 1);
   7944 
   7945    int32_t type;
   7946    MOZ_ALWAYS_SUCCEEDS(aFunctionArguments->GetTypeOfIndex(0, &type));
   7947    MOZ_ASSERT(type == mozIStorageValueArray::VALUE_TYPE_TEXT);
   7948  }
   7949 #endif
   7950 
   7951  QM_TRY_INSPECT(const auto& value,
   7952                 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
   7953                     nsCString, aFunctionArguments, GetUTF8String, 0));
   7954 
   7955  nsCString compressed;
   7956  QM_TRY(OkIf(SnappyCompress(value, compressed)), NS_ERROR_OUT_OF_MEMORY);
   7957 
   7958  const int32_t compression = static_cast<int32_t>(
   7959      compressed.IsVoid() ? LSValue::CompressionType::UNCOMPRESSED
   7960                          : LSValue::CompressionType::SNAPPY);
   7961 
   7962  nsCOMPtr<nsIVariant> result = new storage::IntegerVariant(compression);
   7963 
   7964  result.forget(aResult);
   7965  return NS_OK;
   7966 }
   7967 
   7968 /*******************************************************************************
   7969 * PrepareObserverOp
   7970 ******************************************************************************/
   7971 
   7972 PrepareObserverOp::PrepareObserverOp(
   7973    const LSRequestParams& aParams,
   7974    const Maybe<ContentParentId>& aContentParentId)
   7975    : LSRequestBase(aParams, aContentParentId) {
   7976  MOZ_ASSERT(aParams.type() ==
   7977             LSRequestParams::TLSRequestPrepareObserverParams);
   7978 }
   7979 
   7980 nsresult PrepareObserverOp::Start() {
   7981  AssertIsOnOwningThread();
   7982  MOZ_ASSERT(mState == State::StartingRequest);
   7983  MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
   7984  MOZ_ASSERT(MayProceed());
   7985 
   7986  const LSRequestPrepareObserverParams params =
   7987      mParams.get_LSRequestPrepareObserverParams();
   7988 
   7989  const PrincipalInfo& storagePrincipalInfo = params.storagePrincipalInfo();
   7990 
   7991  if (storagePrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
   7992    mOrigin = quota::GetOriginForChrome();
   7993  } else {
   7994    MOZ_ASSERT(storagePrincipalInfo.type() ==
   7995               PrincipalInfo::TContentPrincipalInfo);
   7996 
   7997    mOrigin = quota::GetOriginFromValidatedPrincipalInfo(storagePrincipalInfo);
   7998  }
   7999 
   8000  mState = State::SendingReadyMessage;
   8001  MOZ_ALWAYS_SUCCEEDS(OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
   8002 
   8003  return NS_OK;
   8004 }
   8005 
   8006 void PrepareObserverOp::GetResponse(LSRequestResponse& aResponse) {
   8007  AssertIsOnOwningThread();
   8008  MOZ_ASSERT(mState == State::SendingResults);
   8009  MOZ_ASSERT(NS_SUCCEEDED(ResultCode()));
   8010  MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
   8011  MOZ_ASSERT(MayProceed());
   8012 
   8013  uint64_t observerId = ++gLastObserverId;
   8014 
   8015  RefPtr<Observer> observer = new Observer(mContentParentId, mOrigin);
   8016 
   8017  if (!gPreparedObsevers) {
   8018    gPreparedObsevers = new PreparedObserverHashtable();
   8019  }
   8020  gPreparedObsevers->InsertOrUpdate(observerId, std::move(observer));
   8021 
   8022  LSRequestPrepareObserverResponse prepareObserverResponse;
   8023  prepareObserverResponse.observerId() = observerId;
   8024 
   8025  aResponse = prepareObserverResponse;
   8026 }
   8027 
   8028 /*******************************************************************************
   8029 + * LSSimpleRequestBase
   8030 +
   8031 ******************************************************************************/
   8032 
   8033 LSSimpleRequestBase::LSSimpleRequestBase(
   8034    const LSSimpleRequestParams& aParams,
   8035    const Maybe<ContentParentId>& aContentParentId)
   8036    : mParams(aParams),
   8037      mContentParentId(aContentParentId),
   8038      mState(State::Initial) {}
   8039 
   8040 LSSimpleRequestBase::~LSSimpleRequestBase() {
   8041  MOZ_ASSERT_IF(MayProceedOnNonOwningThread(),
   8042                mState == State::Initial || mState == State::Completed);
   8043 }
   8044 
   8045 void LSSimpleRequestBase::Dispatch() {
   8046  AssertIsOnOwningThread();
   8047 
   8048  mState = State::StartingRequest;
   8049 
   8050  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(this));
   8051 }
   8052 
   8053 bool LSSimpleRequestBase::VerifyRequestParams() {
   8054  AssertIsOnBackgroundThread();
   8055 
   8056  MOZ_ASSERT(mParams.type() != LSSimpleRequestParams::T__None);
   8057 
   8058  switch (mParams.type()) {
   8059    case LSSimpleRequestParams::TLSSimpleRequestPreloadedParams: {
   8060      const LSSimpleRequestPreloadedParams& params =
   8061          mParams.get_LSSimpleRequestPreloadedParams();
   8062 
   8063      if (NS_WARN_IF(!VerifyPrincipalInfo(
   8064              params.principalInfo(), params.storagePrincipalInfo(), false))) {
   8065        return false;
   8066      }
   8067 
   8068      break;
   8069    }
   8070 
   8071    case LSSimpleRequestParams::TLSSimpleRequestGetStateParams: {
   8072      const LSSimpleRequestGetStateParams& params =
   8073          mParams.get_LSSimpleRequestGetStateParams();
   8074 
   8075      if (NS_WARN_IF(!VerifyPrincipalInfo(
   8076              params.principalInfo(), params.storagePrincipalInfo(), false))) {
   8077        return false;
   8078      }
   8079 
   8080      break;
   8081    }
   8082 
   8083    default:
   8084      MOZ_CRASH("Should never get here!");
   8085  }
   8086 
   8087  return true;
   8088 }
   8089 
   8090 nsresult LSSimpleRequestBase::StartRequest() {
   8091  AssertIsOnOwningThread();
   8092  MOZ_ASSERT(mState == State::StartingRequest);
   8093 
   8094  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
   8095      !MayProceed()) {
   8096    return NS_ERROR_ABORT;
   8097  }
   8098 
   8099 #ifdef DEBUG
   8100  // Always verify parameters in DEBUG builds!
   8101  bool trustParams = false;
   8102 #else
   8103  bool trustParams = !BackgroundParent::IsOtherProcessActor(Manager());
   8104 #endif
   8105 
   8106  if (!trustParams && NS_WARN_IF(!VerifyRequestParams())) {
   8107    return NS_ERROR_FAILURE;
   8108  }
   8109 
   8110  QM_TRY(MOZ_TO_RESULT(Start()));
   8111 
   8112  return NS_OK;
   8113 }
   8114 
   8115 void LSSimpleRequestBase::SendResults() {
   8116  AssertIsOnOwningThread();
   8117  MOZ_ASSERT(mState == State::SendingResults);
   8118 
   8119  if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
   8120      !MayProceed()) {
   8121    MaybeSetFailureCode(NS_ERROR_ABORT);
   8122  }
   8123 
   8124  if (MayProceed()) {
   8125    LSSimpleRequestResponse response;
   8126 
   8127    if (NS_SUCCEEDED(ResultCode())) {
   8128      GetResponse(response);
   8129    } else {
   8130      response = ResultCode();
   8131    }
   8132 
   8133    (void)PBackgroundLSSimpleRequestParent::Send__delete__(this, response);
   8134  }
   8135 
   8136  mState = State::Completed;
   8137 }
   8138 
   8139 NS_IMETHODIMP
   8140 LSSimpleRequestBase::Run() {
   8141  nsresult rv;
   8142 
   8143  switch (mState) {
   8144    case State::StartingRequest:
   8145      rv = StartRequest();
   8146      break;
   8147 
   8148    case State::SendingResults:
   8149      SendResults();
   8150      return NS_OK;
   8151 
   8152    default:
   8153      MOZ_CRASH("Bad state!");
   8154  }
   8155 
   8156  if (NS_WARN_IF(NS_FAILED(rv)) && mState != State::SendingResults) {
   8157    MaybeSetFailureCode(rv);
   8158 
   8159    // Must set mState before dispatching otherwise we will race with the owning
   8160    // thread.
   8161    mState = State::SendingResults;
   8162 
   8163    if (IsOnOwningThread()) {
   8164      SendResults();
   8165    } else {
   8166      MOZ_ALWAYS_SUCCEEDS(
   8167          OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
   8168    }
   8169  }
   8170 
   8171  return NS_OK;
   8172 }
   8173 
   8174 void LSSimpleRequestBase::ActorDestroy(ActorDestroyReason aWhy) {
   8175  AssertIsOnOwningThread();
   8176 
   8177  NoteComplete();
   8178 }
   8179 
   8180 /*******************************************************************************
   8181 * PreloadedOp
   8182 ******************************************************************************/
   8183 
   8184 PreloadedOp::PreloadedOp(const LSSimpleRequestParams& aParams,
   8185                         const Maybe<ContentParentId>& aContentParentId)
   8186    : LSSimpleRequestBase(aParams, aContentParentId) {
   8187  MOZ_ASSERT(aParams.type() ==
   8188             LSSimpleRequestParams::TLSSimpleRequestPreloadedParams);
   8189 }
   8190 
   8191 nsresult PreloadedOp::Start() {
   8192  AssertIsOnOwningThread();
   8193  MOZ_ASSERT(mState == State::StartingRequest);
   8194  MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
   8195  MOZ_ASSERT(MayProceed());
   8196 
   8197  const LSSimpleRequestPreloadedParams& params =
   8198      mParams.get_LSSimpleRequestPreloadedParams();
   8199 
   8200  const PrincipalInfo& storagePrincipalInfo = params.storagePrincipalInfo();
   8201 
   8202  MOZ_ASSERT(
   8203      storagePrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo ||
   8204      storagePrincipalInfo.type() == PrincipalInfo::TContentPrincipalInfo);
   8205  mOrigin =
   8206      storagePrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo
   8207          ? nsCString{quota::GetOriginForChrome()}
   8208          : quota::GetOriginFromValidatedPrincipalInfo(storagePrincipalInfo);
   8209 
   8210  mState = State::SendingResults;
   8211  MOZ_ALWAYS_SUCCEEDS(OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
   8212 
   8213  return NS_OK;
   8214 }
   8215 
   8216 void PreloadedOp::GetResponse(LSSimpleRequestResponse& aResponse) {
   8217  AssertIsOnOwningThread();
   8218  MOZ_ASSERT(mState == State::SendingResults);
   8219  MOZ_ASSERT(NS_SUCCEEDED(ResultCode()));
   8220  MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
   8221  MOZ_ASSERT(MayProceed());
   8222 
   8223  bool preloaded;
   8224  RefPtr<Datastore> datastore;
   8225  if ((datastore = GetDatastore(mOrigin)) && !datastore->IsClosed()) {
   8226    preloaded = true;
   8227  } else {
   8228    preloaded = false;
   8229  }
   8230 
   8231  LSSimpleRequestPreloadedResponse preloadedResponse;
   8232  preloadedResponse.preloaded() = preloaded;
   8233 
   8234  aResponse = preloadedResponse;
   8235 }
   8236 
   8237 /*******************************************************************************
   8238 * GetStateOp
   8239 ******************************************************************************/
   8240 
   8241 GetStateOp::GetStateOp(const LSSimpleRequestParams& aParams,
   8242                       const Maybe<ContentParentId>& aContentParentId)
   8243    : LSSimpleRequestBase(aParams, aContentParentId) {
   8244  MOZ_ASSERT(aParams.type() ==
   8245             LSSimpleRequestParams::TLSSimpleRequestGetStateParams);
   8246 }
   8247 
   8248 nsresult GetStateOp::Start() {
   8249  AssertIsOnOwningThread();
   8250  MOZ_ASSERT(mState == State::StartingRequest);
   8251  MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
   8252  MOZ_ASSERT(MayProceed());
   8253 
   8254  const LSSimpleRequestGetStateParams& params =
   8255      mParams.get_LSSimpleRequestGetStateParams();
   8256 
   8257  const PrincipalInfo& storagePrincipalInfo = params.storagePrincipalInfo();
   8258 
   8259  MOZ_ASSERT(
   8260      storagePrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo ||
   8261      storagePrincipalInfo.type() == PrincipalInfo::TContentPrincipalInfo);
   8262  mOrigin =
   8263      storagePrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo
   8264          ? nsCString{quota::GetOriginForChrome()}
   8265          : quota::GetOriginFromValidatedPrincipalInfo(storagePrincipalInfo);
   8266 
   8267  mState = State::SendingResults;
   8268  MOZ_ALWAYS_SUCCEEDS(OwningEventTarget()->Dispatch(this, NS_DISPATCH_NORMAL));
   8269 
   8270  return NS_OK;
   8271 }
   8272 
   8273 void GetStateOp::GetResponse(LSSimpleRequestResponse& aResponse) {
   8274  AssertIsOnOwningThread();
   8275  MOZ_ASSERT(mState == State::SendingResults);
   8276  MOZ_ASSERT(NS_SUCCEEDED(ResultCode()));
   8277  MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
   8278  MOZ_ASSERT(MayProceed());
   8279 
   8280  LSSimpleRequestGetStateResponse getStateResponse;
   8281 
   8282  if (RefPtr<Datastore> datastore = GetDatastore(mOrigin)) {
   8283    if (!datastore->IsClosed()) {
   8284      getStateResponse.itemInfos() = datastore->GetOrderedItems().Clone();
   8285    }
   8286  }
   8287 
   8288  aResponse = getStateResponse;
   8289 }
   8290 
   8291 /*******************************************************************************
   8292 * ArchivedOriginScope
   8293 ******************************************************************************/
   8294 
   8295 // static
   8296 UniquePtr<ArchivedOriginScope> ArchivedOriginScope::CreateFromOrigin(
   8297    const nsACString& aOriginAttrSuffix, const nsACString& aOriginKey) {
   8298  return WrapUnique(
   8299      new ArchivedOriginScope(Origin(aOriginAttrSuffix, aOriginKey)));
   8300 }
   8301 
   8302 // static
   8303 UniquePtr<ArchivedOriginScope> ArchivedOriginScope::CreateFromPrefix(
   8304    const nsACString& aOriginKey) {
   8305  return WrapUnique(new ArchivedOriginScope(Prefix(aOriginKey)));
   8306 }
   8307 
   8308 // static
   8309 UniquePtr<ArchivedOriginScope> ArchivedOriginScope::CreateFromPattern(
   8310    const OriginAttributesPattern& aPattern) {
   8311  return WrapUnique(new ArchivedOriginScope(Pattern(aPattern)));
   8312 }
   8313 
   8314 // static
   8315 UniquePtr<ArchivedOriginScope> ArchivedOriginScope::CreateFromNull() {
   8316  return WrapUnique(new ArchivedOriginScope(Null()));
   8317 }
   8318 
   8319 nsLiteralCString ArchivedOriginScope::GetBindingClause() const {
   8320  return mData.match(
   8321      [](const Origin&) {
   8322        return " WHERE originKey = :originKey "
   8323               "AND originAttributes = :originAttributes"_ns;
   8324      },
   8325      [](const Pattern&) {
   8326        return " WHERE originAttributes MATCH :originAttributesPattern"_ns;
   8327      },
   8328      [](const Prefix&) { return " WHERE originKey = :originKey"_ns; },
   8329      [](const Null&) { return ""_ns; });
   8330 }
   8331 
   8332 nsresult ArchivedOriginScope::BindToStatement(
   8333    mozIStorageStatement* aStmt) const {
   8334  MOZ_ASSERT(IsOnIOThread() || IsOnGlobalConnectionThread());
   8335  MOZ_ASSERT(aStmt);
   8336 
   8337  struct Matcher {
   8338    mozIStorageStatement* mStmt;
   8339 
   8340    explicit Matcher(mozIStorageStatement* aStmt) : mStmt(aStmt) {}
   8341 
   8342    nsresult operator()(const Origin& aOrigin) {
   8343      QM_TRY(MOZ_TO_RESULT(mStmt->BindUTF8StringByName(
   8344          "originKey"_ns, aOrigin.OriginNoSuffix())));
   8345 
   8346      QM_TRY(MOZ_TO_RESULT(mStmt->BindUTF8StringByName(
   8347          "originAttributes"_ns, aOrigin.OriginSuffix())));
   8348 
   8349      return NS_OK;
   8350    }
   8351 
   8352    nsresult operator()(const Prefix& aPrefix) {
   8353      QM_TRY(MOZ_TO_RESULT(mStmt->BindUTF8StringByName(
   8354          "originKey"_ns, aPrefix.OriginNoSuffix())));
   8355 
   8356      return NS_OK;
   8357    }
   8358 
   8359    nsresult operator()(const Pattern& aPattern) {
   8360      QM_TRY(MOZ_TO_RESULT(mStmt->BindUTF8StringByName(
   8361          "originAttributesPattern"_ns, "pattern1"_ns)));
   8362 
   8363      return NS_OK;
   8364    }
   8365 
   8366    nsresult operator()(const Null& aNull) { return NS_OK; }
   8367  };
   8368 
   8369  QM_TRY(MOZ_TO_RESULT(mData.match(Matcher(aStmt))));
   8370 
   8371  return NS_OK;
   8372 }
   8373 
   8374 bool ArchivedOriginScope::HasMatches(
   8375    ArchivedOriginHashtable* aHashtable) const {
   8376  AssertIsOnIOThread();
   8377  MOZ_ASSERT(aHashtable);
   8378 
   8379  return mData.match(
   8380      [aHashtable](const Origin& aOrigin) {
   8381        const nsCString hashKey = GetArchivedOriginHashKey(
   8382            aOrigin.OriginSuffix(), aOrigin.OriginNoSuffix());
   8383 
   8384        return aHashtable->Contains(hashKey);
   8385      },
   8386      [aHashtable](const Pattern& aPattern) {
   8387        return std::any_of(
   8388            aHashtable->Values().cbegin(), aHashtable->Values().cend(),
   8389            [&aPattern](const auto& entry) {
   8390              return aPattern.GetPattern().Matches(entry->mOriginAttributes);
   8391            });
   8392      },
   8393      [aHashtable](const Prefix& aPrefix) {
   8394        return std::any_of(
   8395            aHashtable->Values().cbegin(), aHashtable->Values().cend(),
   8396            [&aPrefix](const auto& entry) {
   8397              return entry->mOriginNoSuffix == aPrefix.OriginNoSuffix();
   8398            });
   8399      },
   8400      [aHashtable](const Null& aNull) { return !aHashtable->IsEmpty(); });
   8401 }
   8402 
   8403 void ArchivedOriginScope::RemoveMatches(
   8404    ArchivedOriginHashtable* aHashtable) const {
   8405  AssertIsOnIOThread();
   8406  MOZ_ASSERT(aHashtable);
   8407 
   8408  struct Matcher {
   8409    ArchivedOriginHashtable* mHashtable;
   8410 
   8411    explicit Matcher(ArchivedOriginHashtable* aHashtable)
   8412        : mHashtable(aHashtable) {}
   8413 
   8414    void operator()(const Origin& aOrigin) {
   8415      nsCString hashKey = GetArchivedOriginHashKey(aOrigin.OriginSuffix(),
   8416                                                   aOrigin.OriginNoSuffix());
   8417 
   8418      mHashtable->Remove(hashKey);
   8419    }
   8420 
   8421    void operator()(const Prefix& aPrefix) {
   8422      for (auto iter = mHashtable->Iter(); !iter.Done(); iter.Next()) {
   8423        const auto& archivedOriginInfo = iter.Data();
   8424 
   8425        if (archivedOriginInfo->mOriginNoSuffix == aPrefix.OriginNoSuffix()) {
   8426          iter.Remove();
   8427        }
   8428      }
   8429    }
   8430 
   8431    void operator()(const Pattern& aPattern) {
   8432      for (auto iter = mHashtable->Iter(); !iter.Done(); iter.Next()) {
   8433        const auto& archivedOriginInfo = iter.Data();
   8434 
   8435        if (aPattern.GetPattern().Matches(
   8436                archivedOriginInfo->mOriginAttributes)) {
   8437          iter.Remove();
   8438        }
   8439      }
   8440    }
   8441 
   8442    void operator()(const Null& aNull) { mHashtable->Clear(); }
   8443  };
   8444 
   8445  mData.match(Matcher(aHashtable));
   8446 }
   8447 
   8448 /*******************************************************************************
   8449 * QuotaClient
   8450 ******************************************************************************/
   8451 
   8452 QuotaClient* QuotaClient::sInstance = nullptr;
   8453 
   8454 QuotaClient::QuotaClient()
   8455    : mShadowDatabaseMutex("LocalStorage mShadowDatabaseMutex") {
   8456  AssertIsOnBackgroundThread();
   8457  MOZ_ASSERT(!sInstance, "We expect this to be a singleton!");
   8458 
   8459  sInstance = this;
   8460 }
   8461 
   8462 QuotaClient::~QuotaClient() {
   8463  AssertIsOnBackgroundThread();
   8464  MOZ_ASSERT(sInstance == this, "We expect this to be a singleton!");
   8465 
   8466  sInstance = nullptr;
   8467 }
   8468 
   8469 mozilla::dom::quota::Client::Type QuotaClient::GetType() {
   8470  return QuotaClient::LS;
   8471 }
   8472 
   8473 Result<UsageInfo, nsresult> QuotaClient::InitOrigin(
   8474    PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata,
   8475    const AtomicBool& aCanceled) {
   8476  AssertIsOnIOThread();
   8477  MOZ_ASSERT(aPersistenceType == PERSISTENCE_TYPE_DEFAULT);
   8478  MOZ_ASSERT(aOriginMetadata.mPersistenceType == aPersistenceType);
   8479 
   8480  QuotaManager* quotaManager = QuotaManager::Get();
   8481  MOZ_ASSERT(quotaManager);
   8482 
   8483  QM_TRY_INSPECT(const auto& directory,
   8484                 quotaManager->GetOriginDirectory(aOriginMetadata));
   8485 
   8486  MOZ_ASSERT(directory);
   8487 
   8488  QM_TRY(MOZ_TO_RESULT(
   8489      directory->Append(NS_LITERAL_STRING_FROM_CSTRING(LS_DIRECTORY_NAME))));
   8490 
   8491 #ifdef DEBUG
   8492  {
   8493    QM_TRY_INSPECT(const bool& exists,
   8494                   MOZ_TO_RESULT_INVOKE_MEMBER(directory, Exists));
   8495    MOZ_ASSERT(exists);
   8496  }
   8497 #endif
   8498 
   8499  QM_TRY_INSPECT(const auto& directoryPath, MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
   8500                                                nsString, directory, GetPath));
   8501 
   8502  QM_TRY_INSPECT(const auto& usageFile, GetUsageFile(directoryPath));
   8503 
   8504  // XXX Try to make usageFileExists const
   8505  QM_TRY_UNWRAP(bool usageFileExists, ExistsAsFile(*usageFile));
   8506 
   8507  QM_TRY_INSPECT(const auto& usageJournalFile,
   8508                 GetUsageJournalFile(directoryPath));
   8509 
   8510  QM_TRY_INSPECT(const bool& usageJournalFileExists,
   8511                 ExistsAsFile(*usageJournalFile));
   8512 
   8513  if (usageJournalFileExists) {
   8514    if (usageFileExists) {
   8515      QM_TRY(MOZ_TO_RESULT(usageFile->Remove(false)));
   8516 
   8517      usageFileExists = false;
   8518    }
   8519 
   8520    QM_TRY(MOZ_TO_RESULT(usageJournalFile->Remove(false)));
   8521  }
   8522 
   8523  QM_TRY_INSPECT(const auto& file,
   8524                 CloneFileAndAppend(*directory, kDataFileName));
   8525 
   8526  QM_TRY_INSPECT(const bool& fileExists, ExistsAsFile(*file));
   8527 
   8528  QM_TRY_INSPECT(
   8529      const UsageInfo& res,
   8530      ([fileExists, usageFileExists, &file, &usageFile, &usageJournalFile,
   8531        &aOriginMetadata]() -> Result<UsageInfo, nsresult> {
   8532        if (fileExists) {
   8533          QM_TRY_RETURN(QM_OR_ELSE_WARN(
   8534              // Expression. To simplify control flow, we call LoadUsageFile
   8535              // unconditionally here, even though it will necessarily fail if
   8536              // usageFileExists is false.
   8537              LoadUsageFile(*usageFile),
   8538              // Fallback.
   8539              ([&file, &usageFile, &usageJournalFile, &aOriginMetadata](
   8540                   const nsresult) -> Result<UsageInfo, nsresult> {
   8541                QM_TRY_INSPECT(
   8542                    const auto& connection,
   8543                    CreateStorageConnectionWithRecovery(
   8544                        *file, *usageFile, aOriginMetadata.mOrigin, [] {}));
   8545 
   8546                QM_TRY_INSPECT(const int64_t& usage,
   8547                               GetUsage(*connection,
   8548                                        /* aArchivedOriginScope */ nullptr));
   8549 
   8550                QM_TRY(MOZ_TO_RESULT(
   8551                    UpdateUsageFile(usageFile, usageJournalFile, usage)));
   8552 
   8553                QM_TRY(MOZ_TO_RESULT(usageJournalFile->Remove(false)));
   8554 
   8555                MOZ_ASSERT(usage >= 0);
   8556                return UsageInfo{DatabaseUsageType(Some(uint64_t(usage)))};
   8557              })));
   8558        }
   8559 
   8560        if (usageFileExists) {
   8561          QM_TRY(MOZ_TO_RESULT(usageFile->Remove(false)));
   8562        }
   8563 
   8564        return UsageInfo{};
   8565      }()));
   8566 
   8567  // Report unknown files in debug builds, but don't fail, just warn (we don't
   8568  // report unknown files in release builds because that requires extra
   8569  // scanning of the directory which would slow down entire initialization for
   8570  // little benefit).
   8571 
   8572 #ifdef DEBUG
   8573  QM_TRY(CollectEachFileAtomicCancelable(
   8574      *directory, aCanceled,
   8575      [](const nsCOMPtr<nsIFile>& file) -> Result<Ok, nsresult> {
   8576        QM_TRY_INSPECT(const auto& dirEntryKind, GetDirEntryKind(*file));
   8577 
   8578        switch (dirEntryKind) {
   8579          case nsIFileKind::ExistsAsDirectory:
   8580            (void)WARN_IF_FILE_IS_UNKNOWN(*file);
   8581            break;
   8582 
   8583          case nsIFileKind::ExistsAsFile: {
   8584            QM_TRY_INSPECT(
   8585                const auto& leafName,
   8586                MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsString, file, GetLeafName));
   8587 
   8588            if (leafName.Equals(kDataFileName) ||
   8589                leafName.Equals(kJournalFileName) ||
   8590                leafName.Equals(kUsageFileName) ||
   8591                leafName.Equals(kUsageJournalFileName)) {
   8592              return Ok{};
   8593            }
   8594 
   8595            (void)WARN_IF_FILE_IS_UNKNOWN(*file);
   8596 
   8597            break;
   8598          }
   8599 
   8600          case nsIFileKind::DoesNotExist:
   8601            // Ignore files that got removed externally while iterating.
   8602            break;
   8603        }
   8604        return Ok{};
   8605      }));
   8606 #endif
   8607 
   8608  return res;
   8609 }
   8610 
   8611 nsresult QuotaClient::InitOriginWithoutTracking(
   8612    PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata,
   8613    const AtomicBool& aCanceled) {
   8614  AssertIsOnIOThread();
   8615 
   8616  // This is called when a storage/permanent/${origin}/ls directory exists. Even
   8617  // though this shouldn't happen with a "good" profile, we shouldn't return an
   8618  // error here, since that would cause origin initialization to fail. We just
   8619  // warn and otherwise ignore that.
   8620  UNKNOWN_FILE_WARNING(NS_LITERAL_STRING_FROM_CSTRING(LS_DIRECTORY_NAME));
   8621  return NS_OK;
   8622 }
   8623 
   8624 Result<UsageInfo, nsresult> QuotaClient::GetUsageForOrigin(
   8625    PersistenceType aPersistenceType, const OriginMetadata& aOriginMetadata,
   8626    const AtomicBool& aCanceled) {
   8627  AssertIsOnIOThread();
   8628  MOZ_ASSERT(aPersistenceType == PERSISTENCE_TYPE_DEFAULT);
   8629 
   8630  // We can't open the database at this point, since it can be already used
   8631  // by the connection thread. Use the cached value instead.
   8632 
   8633  QuotaManager* quotaManager = QuotaManager::Get();
   8634  MOZ_ASSERT(quotaManager);
   8635 
   8636  return quotaManager->GetUsageForClient(PERSISTENCE_TYPE_DEFAULT,
   8637                                         aOriginMetadata, Client::LS);
   8638 }
   8639 
   8640 nsresult QuotaClient::AboutToClearOrigins(
   8641    const PersistenceScope& aPersistenceScope,
   8642    const OriginScope& aOriginScope) {
   8643  AssertIsOnIOThread();
   8644 
   8645  // This method is not called when the clearing is triggered by the eviction
   8646  // process. It's on purpose to avoid a problem with the origin access time
   8647  // which can be described as follows:
   8648  // When there's a storage pressure condition and quota manager starts
   8649  // collecting origins for eviction, there can be an origin that hasn't been
   8650  // touched for long time. However, the old implementation of local storage
   8651  // could have touched the origin only recently and the new implementation
   8652  // hasn't had a chance to create a new per origin database for it yet (the
   8653  // data is still in the archive database), so the origin access time hasn't
   8654  // been updated either. In the end, the origin would be evicted despite the
   8655  // fact that there was recent local storage activity.
   8656  // So this method clears the archived data and shadow database entries for
   8657  // given origin scope, but only if it's a privacy-related origin clearing.
   8658 
   8659  if (!aPersistenceScope.Matches(
   8660          PersistenceScope::CreateFromValue(PERSISTENCE_TYPE_DEFAULT))) {
   8661    return NS_OK;
   8662  }
   8663 
   8664  // There can be no data for the system principal in the archive or the shadow
   8665  // database. This early return silences potential warnings caused by failed
   8666  // `CreateAerchivedOriginScope` because it calls `GenerateOriginKey2` which
   8667  // doesn't support the system principal.
   8668  if (aOriginScope.IsOrigin() &&
   8669      aOriginScope.GetOrigin() == quota::GetOriginForChrome()) {
   8670    return NS_OK;
   8671  }
   8672 
   8673  const bool shadowWrites = gShadowWrites;
   8674 
   8675  QM_TRY_INSPECT(const auto& archivedOriginScope,
   8676                 CreateArchivedOriginScope(aOriginScope));
   8677 
   8678  if (!gArchivedOrigins) {
   8679    QM_TRY(MOZ_TO_RESULT(LoadArchivedOrigins()));
   8680    MOZ_ASSERT(gArchivedOrigins);
   8681  }
   8682 
   8683  const bool hasDataForRemoval =
   8684      archivedOriginScope->HasMatches(gArchivedOrigins);
   8685 
   8686  QuotaManager* quotaManager = QuotaManager::Get();
   8687  MOZ_ASSERT(quotaManager);
   8688 
   8689  const nsString& basePath = quotaManager->GetBasePath();
   8690 
   8691  {
   8692    MutexAutoLock shadowDatabaseLock(mShadowDatabaseMutex);
   8693 
   8694    QM_TRY_INSPECT(
   8695        const auto& connection,
   8696        ([&basePath]() -> Result<nsCOMPtr<mozIStorageConnection>, nsresult> {
   8697          if (gInitializedShadowStorage) {
   8698            QM_TRY_RETURN(GetShadowStorageConnection(basePath));
   8699          }
   8700 
   8701          QM_TRY_UNWRAP(auto connection,
   8702                        CreateShadowStorageConnection(basePath));
   8703 
   8704          gInitializedShadowStorage = true;
   8705 
   8706          return connection;
   8707        }()));
   8708 
   8709    {
   8710      Maybe<AutoDatabaseAttacher> maybeAutoArchiveDatabaseAttacher;
   8711 
   8712      if (hasDataForRemoval) {
   8713        QM_TRY_INSPECT(const auto& archiveFile,
   8714                       GetArchiveFile(quotaManager->GetStoragePath()));
   8715 
   8716        maybeAutoArchiveDatabaseAttacher.emplace(
   8717            AutoDatabaseAttacher(connection, archiveFile, "archive"_ns));
   8718 
   8719        QM_TRY(MOZ_TO_RESULT(maybeAutoArchiveDatabaseAttacher->Attach()));
   8720      }
   8721 
   8722      if (archivedOriginScope->IsPattern()) {
   8723        nsCOMPtr<mozIStorageFunction> function(
   8724            new MatchFunction(archivedOriginScope->GetPattern()));
   8725 
   8726        QM_TRY(
   8727            MOZ_TO_RESULT(connection->CreateFunction("match"_ns, 2, function)));
   8728      }
   8729 
   8730      {
   8731        QM_TRY_INSPECT(const auto& stmt,
   8732                       MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
   8733                           nsCOMPtr<mozIStorageStatement>, connection,
   8734                           CreateStatement, "BEGIN IMMEDIATE;"_ns));
   8735 
   8736        QM_TRY(MOZ_TO_RESULT(stmt->Execute()));
   8737      }
   8738 
   8739      if (shadowWrites) {
   8740        QM_TRY(MOZ_TO_RESULT(
   8741            PerformDelete(connection, "main"_ns, archivedOriginScope.get())));
   8742      }
   8743 
   8744      if (hasDataForRemoval) {
   8745        QM_TRY(MOZ_TO_RESULT(PerformDelete(connection, "archive"_ns,
   8746                                           archivedOriginScope.get())));
   8747      }
   8748 
   8749      {
   8750        QM_TRY_INSPECT(const auto& stmt,
   8751                       MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
   8752                           nsCOMPtr<mozIStorageStatement>, connection,
   8753                           CreateStatement, "COMMIT;"_ns));
   8754 
   8755        QM_TRY(MOZ_TO_RESULT(stmt->Execute()));
   8756      }
   8757 
   8758      if (archivedOriginScope->IsPattern()) {
   8759        QM_TRY(MOZ_TO_RESULT(connection->RemoveFunction("match"_ns)));
   8760      }
   8761 
   8762      if (hasDataForRemoval) {
   8763        MOZ_ASSERT(maybeAutoArchiveDatabaseAttacher.isSome());
   8764        QM_TRY(MOZ_TO_RESULT(maybeAutoArchiveDatabaseAttacher->Detach()));
   8765 
   8766        maybeAutoArchiveDatabaseAttacher.reset();
   8767 
   8768        MOZ_ASSERT(gArchivedOrigins);
   8769        MOZ_ASSERT(archivedOriginScope->HasMatches(gArchivedOrigins));
   8770        archivedOriginScope->RemoveMatches(gArchivedOrigins);
   8771      }
   8772    }
   8773    QM_TRY(MOZ_TO_RESULT(connection->Close()));
   8774  }
   8775 
   8776  if (aOriginScope.IsNull()) {
   8777    QM_TRY_INSPECT(const auto& shadowFile, GetShadowFile(basePath));
   8778 
   8779    QM_TRY(MOZ_TO_RESULT(shadowFile->Remove(false)));
   8780 
   8781    gInitializedShadowStorage = false;
   8782  }
   8783 
   8784  return NS_OK;
   8785 }
   8786 
   8787 void QuotaClient::OnOriginClearCompleted(
   8788    const OriginMetadata& aOriginMetadata) {
   8789  AssertIsOnIOThread();
   8790 }
   8791 
   8792 void QuotaClient::OnRepositoryClearCompleted(PersistenceType aPersistenceType) {
   8793  AssertIsOnIOThread();
   8794 }
   8795 
   8796 void QuotaClient::ReleaseIOThreadObjects() {
   8797  AssertIsOnIOThread();
   8798 
   8799  gInitializationInfo = nullptr;
   8800 
   8801  // Delete archived origins hashtable since QuotaManager clears the whole
   8802  // storage directory including ls-archive.sqlite.
   8803 
   8804  gArchivedOrigins = nullptr;
   8805 }
   8806 
   8807 void QuotaClient::AbortOperationsForLocks(
   8808    const DirectoryLockIdTable& aDirectoryLockIds) {
   8809  AssertIsOnBackgroundThread();
   8810 
   8811  // A PrepareDatastoreOp object could already acquire a directory lock for
   8812  // the given origin. Its last step is creation of a Datastore object (which
   8813  // will take ownership of the directory lock) and a PreparedDatastore object
   8814  // which keeps the Datastore alive until a database actor is created.
   8815  // We need to invalidate the PreparedDatastore object when it's created,
   8816  // otherwise the Datastore object can block the origin clear operation for
   8817  // long time. It's not a problem that we don't fail the PrepareDatastoreOp
   8818  // immediatelly (avoiding the creation of the Datastore and PreparedDatastore
   8819  // object). We will call RequestAllowToClose on the database actor once it's
   8820  // created and the child actor will respond by sending AllowToClose which
   8821  // will close the Datastore on the parent side (the closing releases the
   8822  // directory lock).
   8823 
   8824  InvalidatePrepareDatastoreOpsMatching(
   8825      [&aDirectoryLockIds](const auto& prepareDatastoreOp) {
   8826        // Check if the PrepareDatastoreOp holds an acquired DirectoryLock.
   8827        // Origin clearing can't be blocked by this PrepareDatastoreOp if there
   8828        // is no acquired DirectoryLock. If there is an acquired DirectoryLock,
   8829        // check if the table contains the lock for the PrepareDatastoreOp.
   8830        return IsLockForObjectAcquiredAndContainedInLockTable(
   8831            prepareDatastoreOp, aDirectoryLockIds);
   8832      });
   8833 
   8834  if (gPrivateDatastores) {
   8835    gPrivateDatastores->RemoveIf([&aDirectoryLockIds](const auto& iter) {
   8836      const auto& privateDatastore = iter.Data();
   8837 
   8838      // The PrivateDatastore::mDatastore member is not cleared until the
   8839      // PrivateDatastore is destroyed.
   8840      auto& datastore = privateDatastore->MutableDatastoreRef();
   8841 
   8842      // If the PrivateDatastore exists then it must be registered in
   8843      // Datastore::mHasLivePrivateDatastore as well. The Datastore must have
   8844      // a DirectoryLock if there is a registered PrivateDatastore.
   8845      bool result =
   8846          IsLockForObjectContainedInLockTable(datastore, aDirectoryLockIds);
   8847 
   8848      // The datastore should be closed after removing from gPrivateDatastores
   8849      // (and eventually unregistered from gDatastores, so a new private
   8850      // browsing session won't see any data from a previous private browsing
   8851      // session) but just in case something still keeps alive the datastore,
   8852      // let's explicitly clear it here.
   8853      if (result) {
   8854        datastore.Clear(nullptr);
   8855      }
   8856 
   8857      return result;
   8858    });
   8859 
   8860    if (!gPrivateDatastores->Count()) {
   8861      gPrivateDatastores = nullptr;
   8862    }
   8863  }
   8864 
   8865  InvalidatePreparedDatastoresMatching([&aDirectoryLockIds](
   8866                                           const auto& preparedDatastore) {
   8867    // The PreparedDatastore::mDatastore member is not cleared until the
   8868    // PreparedDatastore is destroyed.
   8869    const auto& datastore = preparedDatastore.DatastoreRef();
   8870 
   8871    // If the PreparedDatastore exists then it must be registered in
   8872    // Datastore::mPreparedDatastores as well. The Datastore must have a
   8873    // DirectoryLock if there are registered PreparedDatastore objects.
   8874    return IsLockForObjectContainedInLockTable(datastore, aDirectoryLockIds);
   8875  });
   8876 
   8877  RequestAllowToCloseDatabasesMatching(
   8878      [&aDirectoryLockIds](const auto& database) {
   8879        const auto& maybeDatastore = database.MaybeDatastoreRef();
   8880 
   8881        // If the Database is registered in gLiveDatabases then it must have a
   8882        // Datastore.
   8883        MOZ_ASSERT(maybeDatastore.isSome());
   8884 
   8885        // If the Database is registered in gLiveDatabases then it must be
   8886        // registered in Datastore::mDatabases as well. The Datastore must have
   8887        // a DirectoryLock if there are registered Database objects.
   8888        return IsLockForObjectContainedInLockTable(*maybeDatastore,
   8889                                                   aDirectoryLockIds);
   8890      });
   8891 }
   8892 
   8893 void QuotaClient::AbortOperationsForProcess(ContentParentId aContentParentId) {
   8894  AssertIsOnBackgroundThread();
   8895 
   8896  // XXX Quota Manager should do the wrapping.
   8897  Maybe<ContentParentId> contentParentId;
   8898 
   8899  if (aContentParentId) {
   8900    contentParentId = Some(aContentParentId);
   8901  }
   8902 
   8903  // XXX We could try to invalidate other objects here.
   8904 
   8905  RequestAllowToCloseDatabasesMatching(
   8906      [&contentParentId](const auto& database) {
   8907        return database.ContentParentIdRef() == contentParentId;
   8908      });
   8909 }
   8910 
   8911 void QuotaClient::AbortAllOperations() {
   8912  AssertIsOnBackgroundThread();
   8913 
   8914  InvalidatePrepareDatastoreOpsMatching([](const auto& prepareDatastoreOp) {
   8915    return prepareDatastoreOp.MaybeDirectoryLockRef();
   8916  });
   8917 
   8918  if (gPrivateDatastores) {
   8919    gPrivateDatastores = nullptr;
   8920  }
   8921 
   8922  InvalidatePreparedDatastoresMatching([](const auto&) { return true; });
   8923 
   8924  RequestAllowToCloseDatabasesMatching([](const auto&) { return true; });
   8925 }
   8926 
   8927 void QuotaClient::StartIdleMaintenance() { AssertIsOnBackgroundThread(); }
   8928 
   8929 void QuotaClient::StopIdleMaintenance() { AssertIsOnBackgroundThread(); }
   8930 
   8931 void QuotaClient::InitiateShutdown() {
   8932  // gPrepareDatastoreOps are short lived objects running a state machine.
   8933  // The shutdown flag is checked between states, so we don't have to notify
   8934  // all the objects here.
   8935  // Allocation of a new PrepareDatastoreOp object is prevented once the
   8936  // shutdown flag is set.
   8937  // When the last PrepareDatastoreOp finishes, the gPrepareDatastoreOps array
   8938  // is destroyed.
   8939 
   8940  if (gPreparedDatastores) {
   8941    gPreparedDatastores = nullptr;
   8942  }
   8943 
   8944  if (gPrivateDatastores) {
   8945    gPrivateDatastores = nullptr;
   8946  }
   8947 
   8948  RequestAllowToCloseDatabasesMatching([](const auto&) { return true; });
   8949 
   8950  if (gPreparedObsevers) {
   8951    gPreparedObsevers = nullptr;
   8952  }
   8953 }
   8954 
   8955 bool QuotaClient::IsShutdownCompleted() const {
   8956  // Don't have to check gPrivateDatastores and gPreparedDatastores since we
   8957  // nulled it out in InitiateShutdown.
   8958  return !gPrepareDatastoreOps && !gDatastores && !gLiveDatabases;
   8959 }
   8960 
   8961 void QuotaClient::ForceKillActors() { ForceKillAllDatabases(); }
   8962 
   8963 nsCString QuotaClient::GetShutdownStatus() const {
   8964  AssertIsOnBackgroundThread();
   8965 
   8966  nsCString data;
   8967 
   8968  if (gPrepareDatastoreOps) {
   8969    data.Append("PrepareDatastoreOperations: ");
   8970    data.AppendInt(static_cast<uint32_t>(gPrepareDatastoreOps->Length()));
   8971    data.Append(" (");
   8972 
   8973    // XXX What's the purpose of adding these to a hashtable before joining them
   8974    // to the string? (Maybe this used to be an ordered container before???)
   8975    nsTHashSet<nsCString> ids;
   8976    std::transform(gPrepareDatastoreOps->cbegin(), gPrepareDatastoreOps->cend(),
   8977                   MakeInserter(ids), [](const auto& prepareDatastoreOp) {
   8978                     nsCString id;
   8979                     prepareDatastoreOp->Stringify(id);
   8980                     return id;
   8981                   });
   8982 
   8983    StringJoinAppend(data, ", "_ns, ids);
   8984 
   8985    data.Append(")\n");
   8986  }
   8987 
   8988  if (gDatastores) {
   8989    data.Append("Datastores: ");
   8990    data.AppendInt(gDatastores->Count());
   8991    data.Append(" (");
   8992 
   8993    // XXX It might be confusing to remove duplicates here, as the actual list
   8994    // won't match the count then.
   8995    nsTHashSet<nsCString> ids;
   8996    std::transform(gDatastores->Values().cbegin(), gDatastores->Values().cend(),
   8997                   MakeInserter(ids), [](const auto& entry) {
   8998                     nsCString id;
   8999                     entry->Stringify(id);
   9000                     return id;
   9001                   });
   9002 
   9003    StringJoinAppend(data, ", "_ns, ids);
   9004 
   9005    data.Append(")\n");
   9006  }
   9007 
   9008  if (gLiveDatabases) {
   9009    data.Append("LiveDatabases: ");
   9010    data.AppendInt(static_cast<uint32_t>(gLiveDatabases->Length()));
   9011    data.Append(" (");
   9012 
   9013    // XXX It might be confusing to remove duplicates here, as the actual list
   9014    // won't match the count then.
   9015    nsTHashSet<nsCString> ids;
   9016    std::transform(gLiveDatabases->cbegin(), gLiveDatabases->cend(),
   9017                   MakeInserter(ids), [](const auto& database) {
   9018                     nsCString id;
   9019                     database->Stringify(id);
   9020                     return id;
   9021                   });
   9022 
   9023    StringJoinAppend(data, ", "_ns, ids);
   9024 
   9025    data.Append(")\n");
   9026  }
   9027 
   9028  return data;
   9029 }
   9030 
   9031 void QuotaClient::FinalizeShutdown() {
   9032  // And finally, shutdown the connection thread.
   9033  if (gConnectionThread) {
   9034    gConnectionThread->Shutdown();
   9035 
   9036    gConnectionThread = nullptr;
   9037  }
   9038 
   9039  if (gDatabases) {
   9040    nsTArray<RefPtr<Database>> databases;
   9041 
   9042    for (const auto& database : *gDatabases) {
   9043      databases.AppendElement(database);
   9044    }
   9045 
   9046    for (const auto& database : databases) {
   9047      database->Close();
   9048    }
   9049  }
   9050 }
   9051 
   9052 Result<UniquePtr<ArchivedOriginScope>, nsresult>
   9053 QuotaClient::CreateArchivedOriginScope(const OriginScope& aOriginScope) {
   9054  AssertIsOnIOThread();
   9055 
   9056  if (aOriginScope.IsOrigin()) {
   9057    QM_TRY_INSPECT(const auto& principalInfo,
   9058                   QuotaManager::ParseOrigin(aOriginScope.GetOrigin()));
   9059 
   9060    QM_TRY_INSPECT((const auto& [originAttrSuffix, originKey]),
   9061                   GenerateOriginKey2(principalInfo));
   9062 
   9063    return ArchivedOriginScope::CreateFromOrigin(originAttrSuffix, originKey);
   9064  }
   9065 
   9066  if (aOriginScope.IsPrefix()) {
   9067    QM_TRY_INSPECT(const auto& principalInfo,
   9068                   QuotaManager::ParseOrigin(aOriginScope.GetOriginNoSuffix()));
   9069 
   9070    QM_TRY_INSPECT((const auto& [originAttrSuffix, originKey]),
   9071                   GenerateOriginKey2(principalInfo));
   9072 
   9073    (void)originAttrSuffix;
   9074 
   9075    return ArchivedOriginScope::CreateFromPrefix(originKey);
   9076  }
   9077 
   9078  if (aOriginScope.IsPattern()) {
   9079    return ArchivedOriginScope::CreateFromPattern(aOriginScope.GetPattern());
   9080  }
   9081 
   9082  MOZ_ASSERT(aOriginScope.IsNull());
   9083 
   9084  return ArchivedOriginScope::CreateFromNull();
   9085 }
   9086 
   9087 nsresult QuotaClient::PerformDelete(
   9088    mozIStorageConnection* aConnection, const nsACString& aSchemaName,
   9089    ArchivedOriginScope* aArchivedOriginScope) const {
   9090  AssertIsOnIOThread();
   9091  MOZ_ASSERT(aConnection);
   9092  MOZ_ASSERT(aArchivedOriginScope);
   9093 
   9094  QM_TRY_INSPECT(
   9095      const auto& stmt,
   9096      MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
   9097          nsCOMPtr<mozIStorageStatement>, aConnection, CreateStatement,
   9098          "DELETE FROM "_ns + aSchemaName + ".webappsstore2"_ns +
   9099              aArchivedOriginScope->GetBindingClause() + ";"_ns));
   9100 
   9101  QM_TRY(MOZ_TO_RESULT(aArchivedOriginScope->BindToStatement(stmt)));
   9102 
   9103  QM_TRY(MOZ_TO_RESULT(stmt->Execute()));
   9104 
   9105  return NS_OK;
   9106 }
   9107 
   9108 NS_IMPL_ISUPPORTS(QuotaClient::MatchFunction, mozIStorageFunction)
   9109 
   9110 NS_IMETHODIMP
   9111 QuotaClient::MatchFunction::OnFunctionCall(
   9112    mozIStorageValueArray* aFunctionArguments, nsIVariant** aResult) {
   9113  AssertIsOnIOThread();
   9114  MOZ_ASSERT(aFunctionArguments);
   9115  MOZ_ASSERT(aResult);
   9116 
   9117  QM_TRY_INSPECT(const auto& suffix,
   9118                 MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(
   9119                     nsAutoCString, aFunctionArguments, GetUTF8String, 1));
   9120 
   9121  OriginAttributes oa;
   9122  QM_TRY(OkIf(oa.PopulateFromSuffix(suffix)), NS_ERROR_FAILURE);
   9123 
   9124  const bool result = mPattern.Matches(oa);
   9125 
   9126  RefPtr<nsVariant> outVar(new nsVariant());
   9127  QM_TRY(MOZ_TO_RESULT(outVar->SetAsBool(result)));
   9128 
   9129  outVar.forget(aResult);
   9130  return NS_OK;
   9131 }
   9132 
   9133 /*******************************************************************************
   9134 * AutoWriteTransaction
   9135 ******************************************************************************/
   9136 
   9137 AutoWriteTransaction::AutoWriteTransaction(bool aShadowWrites)
   9138    : mConnection(nullptr), mShadowWrites(aShadowWrites) {
   9139  AssertIsOnGlobalConnectionThread();
   9140 
   9141  MOZ_COUNT_CTOR(mozilla::dom::AutoWriteTransaction);
   9142 }
   9143 
   9144 AutoWriteTransaction::~AutoWriteTransaction() {
   9145  AssertIsOnGlobalConnectionThread();
   9146 
   9147  MOZ_COUNT_DTOR(mozilla::dom::AutoWriteTransaction);
   9148 
   9149  if (mConnection) {
   9150    QM_WARNONLY_TRY(QM_TO_RESULT(mConnection->RollbackWriteTransaction()));
   9151 
   9152    if (mShadowWrites) {
   9153      QM_WARNONLY_TRY(QM_TO_RESULT(DetachShadowDatabaseAndUnlock()));
   9154    }
   9155  }
   9156 }
   9157 
   9158 nsresult AutoWriteTransaction::Start(Connection* aConnection) {
   9159  AssertIsOnGlobalConnectionThread();
   9160  MOZ_ASSERT(aConnection);
   9161  MOZ_ASSERT(!mConnection);
   9162 
   9163  if (mShadowWrites) {
   9164    QM_TRY(MOZ_TO_RESULT(LockAndAttachShadowDatabase(aConnection)));
   9165  }
   9166 
   9167  QM_TRY(MOZ_TO_RESULT(aConnection->BeginWriteTransaction()));
   9168 
   9169  mConnection = aConnection;
   9170 
   9171  return NS_OK;
   9172 }
   9173 
   9174 nsresult AutoWriteTransaction::Commit() {
   9175  AssertIsOnGlobalConnectionThread();
   9176  MOZ_ASSERT(mConnection);
   9177 
   9178  QM_TRY(MOZ_TO_RESULT(mConnection->CommitWriteTransaction()));
   9179 
   9180  if (mShadowWrites) {
   9181    QM_TRY(MOZ_TO_RESULT(DetachShadowDatabaseAndUnlock()));
   9182  }
   9183 
   9184  mConnection = nullptr;
   9185 
   9186  return NS_OK;
   9187 }
   9188 
   9189 nsresult AutoWriteTransaction::LockAndAttachShadowDatabase(
   9190    Connection* aConnection) {
   9191  AssertIsOnGlobalConnectionThread();
   9192  MOZ_ASSERT(aConnection);
   9193  MOZ_ASSERT(!mConnection);
   9194  MOZ_ASSERT(mShadowDatabaseLock.isNothing());
   9195  MOZ_ASSERT(mShadowWrites);
   9196 
   9197  QuotaManager* quotaManager = QuotaManager::Get();
   9198  MOZ_ASSERT(quotaManager);
   9199 
   9200  mShadowDatabaseLock.emplace(
   9201      aConnection->GetQuotaClient()->ShadowDatabaseMutex());
   9202 
   9203  QM_TRY(MOZ_TO_RESULT(AttachShadowDatabase(
   9204      quotaManager->GetBasePath(), &aConnection->MutableStorageConnection())));
   9205 
   9206  return NS_OK;
   9207 }
   9208 
   9209 nsresult AutoWriteTransaction::DetachShadowDatabaseAndUnlock() {
   9210  AssertIsOnGlobalConnectionThread();
   9211  MOZ_ASSERT(mConnection);
   9212  MOZ_ASSERT(mShadowDatabaseLock.isSome());
   9213  MOZ_ASSERT(mShadowWrites);
   9214 
   9215  nsCOMPtr<mozIStorageConnection> storageConnection =
   9216      mConnection->StorageConnection();
   9217  MOZ_ASSERT(storageConnection);
   9218 
   9219  QM_TRY(MOZ_TO_RESULT(DetachShadowDatabase(storageConnection)));
   9220 
   9221  mShadowDatabaseLock.reset();
   9222 
   9223  return NS_OK;
   9224 }
   9225 
   9226 }  // namespace mozilla::dom