tor-browser

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

mozStorageService.cpp (26061B)


      1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
      2 * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
      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
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "BaseVFS.h"
      8 #include "mozilla/DebugOnly.h"
      9 #include "mozilla/SpinEventLoopUntil.h"
     10 #include "nsIFile.h"
     11 #include "nsIFileURL.h"
     12 #include "mozStorageService.h"
     13 #include "mozStorageConnection.h"
     14 #include "nsComponentManagerUtils.h"
     15 #include "nsEmbedCID.h"
     16 #include "nsExceptionHandler.h"
     17 #include "nsThreadUtils.h"
     18 #include "mozStoragePrivateHelpers.h"
     19 #include "nsIObserverService.h"
     20 #include "nsIPropertyBag2.h"
     21 #include "ObfuscatingVFS.h"
     22 #include "QuotaVFS.h"
     23 #include "mozilla/Services.h"
     24 #include "mozilla/LateWriteChecks.h"
     25 #include "mozIStorageCompletionCallback.h"
     26 #include "mozIStoragePendingStatement.h"
     27 #include "mozilla/StaticPrefs_storage.h"
     28 #include "mozilla/intl/Collator.h"
     29 #include "mozilla/intl/LocaleService.h"
     30 
     31 #include "sqlite3.h"
     32 #include "mozilla/AutoSQLiteLifetime.h"
     33 
     34 #ifdef XP_WIN
     35 // "windows.h" was included and it can #define lots of things we care about...
     36 #  undef CompareString
     37 #endif
     38 
     39 using mozilla::intl::Collator;
     40 
     41 namespace mozilla::storage {
     42 
     43 ////////////////////////////////////////////////////////////////////////////////
     44 //// Memory Reporting
     45 
     46 #ifdef MOZ_DMD
     47 mozilla::Atomic<size_t> gSqliteMemoryUsed;
     48 #endif
     49 
     50 static int64_t StorageSQLiteDistinguishedAmount() {
     51  return ::sqlite3_memory_used();
     52 }
     53 
     54 /**
     55 * Passes a single SQLite memory statistic to a memory reporter callback.
     56 *
     57 * @param aHandleReport
     58 *        The callback.
     59 * @param aData
     60 *        The data for the callback.
     61 * @param aConn
     62 *        The SQLite connection.
     63 * @param aPathHead
     64 *        Head of the path for the memory report.
     65 * @param aKind
     66 *        The memory report statistic kind, one of "stmt", "cache" or
     67 *        "schema".
     68 * @param aDesc
     69 *        The memory report description.
     70 * @param aOption
     71 *        The SQLite constant for getting the measurement.
     72 * @param aTotal
     73 *        The accumulator for the measurement.
     74 */
     75 static void ReportConn(nsIHandleReportCallback* aHandleReport,
     76                       nsISupports* aData, Connection* aConn,
     77                       const nsACString& aPathHead, const nsACString& aKind,
     78                       const nsACString& aDesc, int32_t aOption,
     79                       size_t* aTotal) {
     80  nsCString path(aPathHead);
     81  path.Append(aKind);
     82  path.AppendLiteral("-used");
     83 
     84  int32_t val = aConn->getSqliteRuntimeStatus(aOption);
     85  aHandleReport->Callback(""_ns, path, nsIMemoryReporter::KIND_HEAP,
     86                          nsIMemoryReporter::UNITS_BYTES, int64_t(val), aDesc,
     87                          aData);
     88  *aTotal += val;
     89 }
     90 
     91 // Warning: To get a Connection's measurements requires holding its lock.
     92 // There may be a delay getting the lock if another thread is accessing the
     93 // Connection.  This isn't very nice if CollectReports is called from the main
     94 // thread!  But at the time of writing this function is only called when
     95 // about:memory is loaded (not, for example, when telemetry pings occur) and
     96 // any delays in that case aren't so bad.
     97 NS_IMETHODIMP
     98 Service::CollectReports(nsIHandleReportCallback* aHandleReport,
     99                        nsISupports* aData, bool aAnonymize) {
    100  size_t totalConnSize = 0;
    101  {
    102    nsTArray<RefPtr<Connection>> connections;
    103    getConnections(connections);
    104 
    105    for (uint32_t i = 0; i < connections.Length(); i++) {
    106      RefPtr<Connection>& conn = connections[i];
    107 
    108      // Someone may have closed the Connection, in which case we skip it.
    109      // Note that we have consumers of the synchronous API that are off the
    110      // main-thread, like the DOM Cache and IndexedDB, and as such we must be
    111      // sure that we have a connection.
    112      MutexAutoLock lockedAsyncScope(conn->sharedAsyncExecutionMutex);
    113      if (!conn->connectionReady()) {
    114        continue;
    115      }
    116 
    117      nsCString pathHead("explicit/storage/sqlite/");
    118      // This filename isn't privacy-sensitive, and so is never anonymized.
    119      pathHead.Append(conn->getFilename());
    120      pathHead.Append('/');
    121 
    122      SQLiteMutexAutoLock lockedScope(conn->sharedDBMutex);
    123 
    124      constexpr auto stmtDesc =
    125          "Memory (approximate) used by all prepared statements used by "
    126          "connections to this database."_ns;
    127      ReportConn(aHandleReport, aData, conn, pathHead, "stmt"_ns, stmtDesc,
    128                 SQLITE_DBSTATUS_STMT_USED, &totalConnSize);
    129 
    130      constexpr auto cacheDesc =
    131          "Memory (approximate) used by all pager caches used by connections "
    132          "to this database."_ns;
    133      ReportConn(aHandleReport, aData, conn, pathHead, "cache"_ns, cacheDesc,
    134                 SQLITE_DBSTATUS_CACHE_USED_SHARED, &totalConnSize);
    135 
    136      constexpr auto schemaDesc =
    137          "Memory (approximate) used to store the schema for all databases "
    138          "associated with connections to this database."_ns;
    139      ReportConn(aHandleReport, aData, conn, pathHead, "schema"_ns, schemaDesc,
    140                 SQLITE_DBSTATUS_SCHEMA_USED, &totalConnSize);
    141    }
    142 
    143 #ifdef MOZ_DMD
    144    if (::sqlite3_memory_used() != int64_t(gSqliteMemoryUsed)) {
    145      NS_WARNING(
    146          "memory consumption reported by SQLite doesn't match "
    147          "our measurements");
    148    }
    149 #endif
    150  }
    151 
    152  int64_t other = static_cast<int64_t>(::sqlite3_memory_used() - totalConnSize);
    153 
    154  MOZ_COLLECT_REPORT("explicit/storage/sqlite/other", KIND_HEAP, UNITS_BYTES,
    155                     other, "All unclassified sqlite memory.");
    156 
    157  return NS_OK;
    158 }
    159 
    160 ////////////////////////////////////////////////////////////////////////////////
    161 //// Service
    162 
    163 NS_IMPL_ISUPPORTS(Service, mozIStorageService, nsIObserver, nsIMemoryReporter)
    164 
    165 Service* Service::gService = nullptr;
    166 
    167 already_AddRefed<Service> Service::getSingleton() {
    168  if (gService) {
    169    return do_AddRef(gService);
    170  }
    171 
    172  // The first reference to the storage service must be obtained on the
    173  // main thread.
    174  NS_ENSURE_TRUE(NS_IsMainThread(), nullptr);
    175  RefPtr<Service> service = new Service();
    176  if (NS_SUCCEEDED(service->initialize())) {
    177    // Note: This is cleared in the Service destructor.
    178    gService = service.get();
    179    return service.forget();
    180  }
    181 
    182  return nullptr;
    183 }
    184 
    185 int Service::AutoVFSRegistration::Init(UniquePtr<sqlite3_vfs> aVFS) {
    186  MOZ_ASSERT(!mVFS);
    187  if (aVFS) {
    188    mVFS = std::move(aVFS);
    189    return sqlite3_vfs_register(mVFS.get(), 0);
    190  }
    191  NS_WARNING("Failed to register VFS");
    192  return SQLITE_OK;
    193 }
    194 
    195 Service::AutoVFSRegistration::~AutoVFSRegistration() {
    196  if (mVFS) {
    197    int rc = sqlite3_vfs_unregister(mVFS.get());
    198    if (rc != SQLITE_OK) {
    199      NS_WARNING("Failed to unregister sqlite vfs wrapper.");
    200    }
    201  }
    202 }
    203 
    204 Service::Service()
    205    : mMutex("Service::mMutex"),
    206      mRegistrationMutex("Service::mRegistrationMutex"),
    207      mLastSensitivity(mozilla::intl::Collator::Sensitivity::Base) {}
    208 
    209 Service::~Service() {
    210  mozilla::UnregisterWeakMemoryReporter(this);
    211  mozilla::UnregisterStorageSQLiteDistinguishedAmount();
    212 
    213  gService = nullptr;
    214 }
    215 
    216 void Service::registerConnection(Connection* aConnection) {
    217  mRegistrationMutex.AssertNotCurrentThreadOwns();
    218  MutexAutoLock mutex(mRegistrationMutex);
    219  (void)mConnections.AppendElement(aConnection);
    220 }
    221 
    222 void Service::unregisterConnection(Connection* aConnection) {
    223  // If this is the last Connection it might be the only thing keeping Service
    224  // alive.  So ensure that Service is destroyed only after the Connection is
    225  // cleanly unregistered and destroyed.
    226  RefPtr<Service> kungFuDeathGrip(this);
    227  RefPtr<Connection> forgettingRef;
    228  {
    229    mRegistrationMutex.AssertNotCurrentThreadOwns();
    230    MutexAutoLock mutex(mRegistrationMutex);
    231 
    232    for (uint32_t i = 0; i < mConnections.Length(); ++i) {
    233      if (mConnections[i] == aConnection) {
    234        // Because dropping the final reference can potentially result in
    235        // spinning a nested event loop if the connection was not properly
    236        // shutdown, we want to do that outside this loop so that we can finish
    237        // mutating the array and drop our mutex.
    238        forgettingRef = std::move(mConnections[i]);
    239        mConnections.RemoveElementAt(i);
    240        break;
    241      }
    242    }
    243  }
    244 
    245  MOZ_ASSERT(forgettingRef,
    246             "Attempt to unregister unknown storage connection!");
    247 
    248  // Do not proxy the release anywhere, just let this reference drop here. (We
    249  // previously did proxy the release, but that was because we invoked Close()
    250  // in the destructor and Close() likes to complain if it's not invoked on the
    251  // opener event target, so it was essential that the last reference be dropped
    252  // on the opener event target.  We now enqueue Close() inside our caller,
    253  // Release(), so it doesn't actually matter what thread our reference drops
    254  // on.)
    255 }
    256 
    257 void Service::getConnections(
    258    /* inout */ nsTArray<RefPtr<Connection>>& aConnections) {
    259  mRegistrationMutex.AssertNotCurrentThreadOwns();
    260  MutexAutoLock mutex(mRegistrationMutex);
    261  aConnections.Clear();
    262  aConnections.AppendElements(mConnections);
    263 }
    264 
    265 void Service::minimizeMemory() {
    266  nsTArray<RefPtr<Connection>> connections;
    267  getConnections(connections);
    268 
    269  for (uint32_t i = 0; i < connections.Length(); i++) {
    270    RefPtr<Connection> conn = connections[i];
    271    // For non-main-thread owning/opening threads, we may be racing against them
    272    // closing their connection or their thread.  That's okay, see below.
    273    if (!conn->connectionReady()) {
    274      continue;
    275    }
    276 
    277    constexpr auto shrinkPragma = "PRAGMA shrink_memory"_ns;
    278 
    279    if (!conn->operationSupported(Connection::SYNCHRONOUS)) {
    280      // This is a mozIStorageAsyncConnection, it can only be used on the main
    281      // thread, so we can do a straight API call.
    282      nsCOMPtr<mozIStoragePendingStatement> ps;
    283      DebugOnly<nsresult> rv = conn->ExecuteSimpleSQLAsync(
    284          shrinkPragma, nullptr, getter_AddRefs(ps));
    285      MOZ_ASSERT(NS_SUCCEEDED(rv), "Should have purged sqlite caches");
    286    } else if (IsOnCurrentSerialEventTarget(conn->eventTargetOpenedOn)) {
    287      if (conn->isAsyncExecutionThreadAvailable()) {
    288        nsCOMPtr<mozIStoragePendingStatement> ps;
    289        DebugOnly<nsresult> rv = conn->ExecuteSimpleSQLAsync(
    290            shrinkPragma, nullptr, getter_AddRefs(ps));
    291        MOZ_ASSERT(NS_SUCCEEDED(rv), "Should have purged sqlite caches");
    292      } else {
    293        conn->ExecuteSimpleSQL(shrinkPragma);
    294      }
    295    } else {
    296      // We are on the wrong event target, the query should be executed on the
    297      // opener event target, so we must dispatch to it.
    298      // It's possible the connection is already closed or will be closed by the
    299      // time our runnable runs. ExecuteSimpleSQL will safely return with a
    300      // failure in that case. If the event target is shutting down or shut
    301      // down, the dispatch will fail and that's okay.
    302      nsCOMPtr<nsIRunnable> event = NewRunnableMethod<const nsCString>(
    303          "Connection::ExecuteSimpleSQL", conn, &Connection::ExecuteSimpleSQL,
    304          shrinkPragma);
    305      (void)conn->eventTargetOpenedOn->Dispatch(event, NS_DISPATCH_NORMAL);
    306    }
    307  }
    308 }
    309 
    310 UniquePtr<sqlite3_vfs> ConstructReadOnlyNoLockVFS();
    311 
    312 static const char* sObserverTopics[] = {"memory-pressure",
    313                                        "xpcom-shutdown-threads"};
    314 
    315 nsresult Service::initialize() {
    316  MOZ_ASSERT(NS_IsMainThread(), "Must be initialized on the main thread");
    317 
    318  int rc = AutoSQLiteLifetime::getInitResult();
    319  if (rc != SQLITE_OK) {
    320    return convertResultCode(rc);
    321  }
    322 
    323  /**
    324   *                    The virtual file system hierarchy
    325   *
    326   *                                 obfsvfs
    327   *                                    |
    328   *                                    |
    329   *                                    |
    330   *                                 quotavfs
    331   *                                   / \
    332   *                                 /     \
    333   *                               /         \
    334   *                             /             \
    335   *                           /                 \
    336   *                     base-vfs-excl        base-vfs
    337   *                          / \               / \
    338   *                        /     \           /     \
    339   *                      /         \       /         \
    340   *                 unix-excl     win32  unix       win32
    341   */
    342 
    343  rc = mBaseSqliteVFS.Init(basevfs::ConstructVFS(false));
    344  if (rc != SQLITE_OK) {
    345    return convertResultCode(rc);
    346  }
    347 
    348  rc = mBaseExclSqliteVFS.Init(basevfs::ConstructVFS(true));
    349  if (rc != SQLITE_OK) {
    350    return convertResultCode(rc);
    351  }
    352 
    353  rc = mQuotaSqliteVFS.Init(quotavfs::ConstructVFS(basevfs::GetVFSName(
    354      StaticPrefs::storage_sqlite_exclusiveLock_enabled())));
    355  if (rc != SQLITE_OK) {
    356    return convertResultCode(rc);
    357  }
    358 
    359  rc =
    360      mObfuscatingSqliteVFS.Init(obfsvfs::ConstructVFS(quotavfs::GetVFSName()));
    361  if (rc != SQLITE_OK) {
    362    return convertResultCode(rc);
    363  }
    364 
    365  rc = mReadOnlyNoLockSqliteVFS.Init(ConstructReadOnlyNoLockVFS());
    366  if (rc != SQLITE_OK) {
    367    return convertResultCode(rc);
    368  }
    369 
    370  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
    371  NS_ENSURE_TRUE(os, NS_ERROR_FAILURE);
    372 
    373  for (auto& sObserverTopic : sObserverTopics) {
    374    nsresult rv = os->AddObserver(this, sObserverTopic, false);
    375    if (NS_WARN_IF(NS_FAILED(rv))) {
    376      return rv;
    377    }
    378  }
    379 
    380  mozilla::RegisterWeakMemoryReporter(this);
    381  mozilla::RegisterStorageSQLiteDistinguishedAmount(
    382      StorageSQLiteDistinguishedAmount);
    383 
    384  return NS_OK;
    385 }
    386 
    387 int Service::localeCompareStrings(const nsAString& aStr1,
    388                                  const nsAString& aStr2,
    389                                  Collator::Sensitivity aSensitivity) {
    390  // The mozilla::intl::Collator is not thread safe, since the Collator::Options
    391  // can be changed.
    392  MutexAutoLock mutex(mMutex);
    393 
    394  Collator* collator = getCollator();
    395  if (!collator) {
    396    NS_ERROR("Storage service has no collation");
    397    return 0;
    398  }
    399 
    400  if (aSensitivity != mLastSensitivity) {
    401    Collator::Options options{};
    402    options.sensitivity = aSensitivity;
    403    auto result = mCollator->SetOptions(options);
    404 
    405    if (result.isErr()) {
    406      NS_WARNING("Could not configure the mozilla::intl::Collation.");
    407      return 0;
    408    }
    409    mLastSensitivity = aSensitivity;
    410  }
    411 
    412  return collator->CompareStrings(aStr1, aStr2);
    413 }
    414 
    415 Collator* Service::getCollator() {
    416  mMutex.AssertCurrentThreadOwns();
    417 
    418  if (mCollator) {
    419    return mCollator.get();
    420  }
    421 
    422  auto result = mozilla::intl::LocaleService::TryCreateComponent<Collator>();
    423  if (result.isErr()) {
    424    NS_WARNING("Could not create mozilla::intl::Collation.");
    425    return nullptr;
    426  }
    427 
    428  mCollator = result.unwrap();
    429 
    430  // Sort in a case-insensitive way, where "base" letters are considered
    431  // equal, e.g: a = á, a = A, a ≠ b.
    432  Collator::Options options{};
    433  options.sensitivity = Collator::Sensitivity::Base;
    434  auto optResult = mCollator->SetOptions(options);
    435 
    436  if (optResult.isErr()) {
    437    NS_WARNING("Could not configure the mozilla::intl::Collation.");
    438    mCollator = nullptr;
    439    return nullptr;
    440  }
    441 
    442  return mCollator.get();
    443 }
    444 
    445 ////////////////////////////////////////////////////////////////////////////////
    446 //// mozIStorageService
    447 
    448 NS_IMETHODIMP
    449 Service::OpenSpecialDatabase(const nsACString& aStorageKey,
    450                             const nsACString& aName, uint32_t aConnectionFlags,
    451                             mozIStorageConnection** _connection) {
    452  if (!aStorageKey.Equals(kMozStorageMemoryStorageKey)) {
    453    return NS_ERROR_INVALID_ARG;
    454  }
    455 
    456  const bool interruptible =
    457      aConnectionFlags & mozIStorageService::CONNECTION_INTERRUPTIBLE;
    458 
    459  int flags = SQLITE_OPEN_READWRITE;
    460 
    461  if (!aName.IsEmpty()) {
    462    flags |= SQLITE_OPEN_URI;
    463  }
    464 
    465  RefPtr<Connection> msc =
    466      new Connection(this, flags, Connection::SYNCHRONOUS,
    467                     kMozStorageMemoryStorageKey, interruptible);
    468  const nsresult rv = msc->initialize(aStorageKey, aName);
    469  NS_ENSURE_SUCCESS(rv, rv);
    470 
    471  msc.forget(_connection);
    472  return NS_OK;
    473 }
    474 
    475 namespace {
    476 
    477 class AsyncInitDatabase final : public Runnable {
    478 public:
    479  AsyncInitDatabase(Connection* aConnection, nsIFile* aStorageFile,
    480                    int32_t aGrowthIncrement,
    481                    mozIStorageCompletionCallback* aCallback)
    482      : Runnable("storage::AsyncInitDatabase"),
    483        mConnection(aConnection),
    484        mStorageFile(aStorageFile),
    485        mGrowthIncrement(aGrowthIncrement),
    486        mCallback(aCallback) {
    487    MOZ_ASSERT(NS_IsMainThread());
    488  }
    489 
    490  NS_IMETHOD Run() override {
    491    MOZ_ASSERT(!NS_IsMainThread());
    492    nsresult rv = mConnection->initializeOnAsyncThread(mStorageFile);
    493    if (NS_FAILED(rv)) {
    494      return DispatchResult(rv, nullptr);
    495    }
    496 
    497    if (mGrowthIncrement >= 0) {
    498      // Ignore errors. In the future, we might wish to log them.
    499      (void)mConnection->SetGrowthIncrement(mGrowthIncrement, ""_ns);
    500    }
    501 
    502    return DispatchResult(
    503        NS_OK, NS_ISUPPORTS_CAST(mozIStorageAsyncConnection*, mConnection));
    504  }
    505 
    506 private:
    507  nsresult DispatchResult(nsresult aStatus, nsISupports* aValue) {
    508    RefPtr<CallbackComplete> event =
    509        new CallbackComplete(aStatus, aValue, mCallback.forget());
    510    return NS_DispatchToMainThread(event);
    511  }
    512 
    513  ~AsyncInitDatabase() {
    514    NS_ReleaseOnMainThread("AsyncInitDatabase::mStorageFile",
    515                           mStorageFile.forget());
    516    NS_ReleaseOnMainThread("AsyncInitDatabase::mConnection",
    517                           mConnection.forget());
    518 
    519    // Generally, the callback will be released by CallbackComplete.
    520    // However, if for some reason Run() is not executed, we still
    521    // need to ensure that it is released here.
    522    NS_ReleaseOnMainThread("AsyncInitDatabase::mCallback", mCallback.forget());
    523  }
    524 
    525  RefPtr<Connection> mConnection;
    526  nsCOMPtr<nsIFile> mStorageFile;
    527  int32_t mGrowthIncrement;
    528  RefPtr<mozIStorageCompletionCallback> mCallback;
    529 };
    530 
    531 }  // namespace
    532 
    533 NS_IMETHODIMP
    534 Service::OpenAsyncDatabase(nsIVariant* aDatabaseStore, uint32_t aOpenFlags,
    535                           uint32_t /* aConnectionFlags */,
    536                           mozIStorageCompletionCallback* aCallback) {
    537  if (!NS_IsMainThread()) {
    538    return NS_ERROR_NOT_SAME_THREAD;
    539  }
    540  NS_ENSURE_ARG(aDatabaseStore);
    541  NS_ENSURE_ARG(aCallback);
    542 
    543  const bool shared = aOpenFlags & mozIStorageService::OPEN_SHARED;
    544  const bool ignoreLockingMode =
    545      aOpenFlags & mozIStorageService::OPEN_IGNORE_LOCKING_MODE;
    546  // Specifying ignoreLockingMode will force use of the readOnly flag:
    547  const bool readOnly =
    548      ignoreLockingMode || (aOpenFlags & mozIStorageService::OPEN_READONLY);
    549  const bool openNotExclusive =
    550      aOpenFlags & mozIStorageService::OPEN_NOT_EXCLUSIVE;
    551  int flags = readOnly ? SQLITE_OPEN_READONLY : SQLITE_OPEN_READWRITE;
    552 
    553  nsCOMPtr<nsIFile> storageFile;
    554  nsCOMPtr<nsISupports> dbStore;
    555  nsresult rv = aDatabaseStore->GetAsISupports(getter_AddRefs(dbStore));
    556  if (NS_SUCCEEDED(rv)) {
    557    // Generally, aDatabaseStore holds the database nsIFile.
    558    storageFile = do_QueryInterface(dbStore, &rv);
    559    if (NS_FAILED(rv)) {
    560      return NS_ERROR_INVALID_ARG;
    561    }
    562 
    563    nsCOMPtr<nsIFile> cloned;
    564    rv = storageFile->Clone(getter_AddRefs(cloned));
    565    MOZ_ASSERT(NS_SUCCEEDED(rv));
    566    storageFile = std::move(cloned);
    567 
    568    if (!readOnly) {
    569      // Ensure that SQLITE_OPEN_CREATE is passed in for compatibility reasons.
    570      flags |= SQLITE_OPEN_CREATE;
    571    }
    572 
    573    // Apply the shared-cache option.
    574    flags |= shared ? SQLITE_OPEN_SHAREDCACHE : SQLITE_OPEN_PRIVATECACHE;
    575  } else {
    576    // Sometimes, however, it's a special database name.
    577    nsAutoCString keyString;
    578    rv = aDatabaseStore->GetAsACString(keyString);
    579    if (NS_FAILED(rv) || !keyString.Equals(kMozStorageMemoryStorageKey)) {
    580      return NS_ERROR_INVALID_ARG;
    581    }
    582 
    583    // Just fall through with nullptr storageFile, this will cause the storage
    584    // connection to use a memory DB.
    585  }
    586 
    587  // Create connection on this thread, but initialize it on its helper thread.
    588  nsAutoCString telemetryFilename;
    589  if (!storageFile) {
    590    telemetryFilename.Assign(kMozStorageMemoryStorageKey);
    591  } else {
    592    rv = storageFile->GetNativeLeafName(telemetryFilename);
    593    NS_ENSURE_SUCCESS(rv, rv);
    594  }
    595  RefPtr<Connection> msc = new Connection(
    596      this, flags, Connection::ASYNCHRONOUS, telemetryFilename,
    597      /* interruptible */ true, ignoreLockingMode, openNotExclusive);
    598  nsCOMPtr<nsIEventTarget> target = msc->getAsyncExecutionTarget();
    599  MOZ_ASSERT(target,
    600             "Cannot initialize a connection that has been closed already");
    601 
    602  RefPtr<AsyncInitDatabase> asyncInit = new AsyncInitDatabase(
    603      msc, storageFile, /* growthIncrement */ -1, aCallback);
    604  return target->Dispatch(asyncInit, nsIEventTarget::DISPATCH_NORMAL);
    605 }
    606 
    607 NS_IMETHODIMP
    608 Service::OpenDatabase(nsIFile* aDatabaseFile, uint32_t aConnectionFlags,
    609                      mozIStorageConnection** _connection) {
    610  NS_ENSURE_ARG(aDatabaseFile);
    611 
    612  const bool interruptible =
    613      aConnectionFlags & mozIStorageService::CONNECTION_INTERRUPTIBLE;
    614 
    615  // Always ensure that SQLITE_OPEN_CREATE is passed in for compatibility
    616  // reasons.
    617  const int flags =
    618      SQLITE_OPEN_READWRITE | SQLITE_OPEN_SHAREDCACHE | SQLITE_OPEN_CREATE;
    619  nsAutoCString telemetryFilename;
    620  nsresult rv = aDatabaseFile->GetNativeLeafName(telemetryFilename);
    621  NS_ENSURE_SUCCESS(rv, rv);
    622  RefPtr<Connection> msc = new Connection(this, flags, Connection::SYNCHRONOUS,
    623                                          telemetryFilename, interruptible);
    624  rv = msc->initialize(aDatabaseFile);
    625  NS_ENSURE_SUCCESS(rv, rv);
    626 
    627  msc.forget(_connection);
    628  return NS_OK;
    629 }
    630 
    631 NS_IMETHODIMP
    632 Service::OpenUnsharedDatabase(nsIFile* aDatabaseFile, uint32_t aConnectionFlags,
    633                              mozIStorageConnection** _connection) {
    634  NS_ENSURE_ARG(aDatabaseFile);
    635 
    636  const bool interruptible =
    637      aConnectionFlags & mozIStorageService::CONNECTION_INTERRUPTIBLE;
    638 
    639  // Always ensure that SQLITE_OPEN_CREATE is passed in for compatibility
    640  // reasons.
    641  const int flags =
    642      SQLITE_OPEN_READWRITE | SQLITE_OPEN_PRIVATECACHE | SQLITE_OPEN_CREATE;
    643  nsAutoCString telemetryFilename;
    644  nsresult rv = aDatabaseFile->GetNativeLeafName(telemetryFilename);
    645  NS_ENSURE_SUCCESS(rv, rv);
    646  RefPtr<Connection> msc = new Connection(this, flags, Connection::SYNCHRONOUS,
    647                                          telemetryFilename, interruptible);
    648  rv = msc->initialize(aDatabaseFile);
    649  NS_ENSURE_SUCCESS(rv, rv);
    650 
    651  msc.forget(_connection);
    652  return NS_OK;
    653 }
    654 
    655 NS_IMETHODIMP
    656 Service::OpenDatabaseWithFileURL(nsIFileURL* aFileURL,
    657                                 const nsACString& aTelemetryFilename,
    658                                 uint32_t aConnectionFlags,
    659                                 mozIStorageConnection** _connection) {
    660  NS_ENSURE_ARG(aFileURL);
    661 
    662  const bool interruptible =
    663      aConnectionFlags & mozIStorageService::CONNECTION_INTERRUPTIBLE;
    664 
    665  // Always ensure that SQLITE_OPEN_CREATE is passed in for compatibility
    666  // reasons.
    667  const int flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_SHAREDCACHE |
    668                    SQLITE_OPEN_CREATE | SQLITE_OPEN_URI;
    669  nsresult rv;
    670  nsAutoCString telemetryFilename;
    671  if (!aTelemetryFilename.IsEmpty()) {
    672    telemetryFilename = aTelemetryFilename;
    673  } else {
    674    nsCOMPtr<nsIFile> databaseFile;
    675    rv = aFileURL->GetFile(getter_AddRefs(databaseFile));
    676    NS_ENSURE_SUCCESS(rv, rv);
    677    rv = databaseFile->GetNativeLeafName(telemetryFilename);
    678    NS_ENSURE_SUCCESS(rv, rv);
    679  }
    680  RefPtr<Connection> msc = new Connection(this, flags, Connection::SYNCHRONOUS,
    681                                          telemetryFilename, interruptible);
    682  rv = msc->initialize(aFileURL);
    683  NS_ENSURE_SUCCESS(rv, rv);
    684 
    685  msc.forget(_connection);
    686  return NS_OK;
    687 }
    688 
    689 ////////////////////////////////////////////////////////////////////////////////
    690 //// nsIObserver
    691 
    692 NS_IMETHODIMP
    693 Service::Observe(nsISupports*, const char* aTopic, const char16_t*) {
    694  if (strcmp(aTopic, "memory-pressure") == 0) {
    695    minimizeMemory();
    696  } else if (strcmp(aTopic, "xpcom-shutdown-threads") == 0) {
    697    // The Service is kept alive by our strong observer references and
    698    // references held by Connection instances.  Since we're about to remove the
    699    // former and then wait for the latter ones to go away, it behooves us to
    700    // hold a strong reference to ourselves so our calls to getConnections() do
    701    // not happen on a deleted object.
    702    RefPtr<Service> kungFuDeathGrip = this;
    703 
    704    nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
    705 
    706    for (auto& sObserverTopic : sObserverTopics) {
    707      (void)os->RemoveObserver(this, sObserverTopic);
    708    }
    709 
    710    SpinEventLoopUntil("storage::Service::Observe(xpcom-shutdown-threads)"_ns,
    711                       [&]() -> bool {
    712                         // We must wait until all the closing connections are
    713                         // closed.
    714                         nsTArray<RefPtr<Connection>> connections;
    715                         getConnections(connections);
    716                         for (auto& conn : connections) {
    717                           if (conn->isClosing()) {
    718                             return false;
    719                           }
    720                         }
    721                         return true;
    722                       });
    723 
    724 #ifdef DEBUG
    725    nsTArray<RefPtr<Connection>> connections;
    726    getConnections(connections);
    727    for (uint32_t i = 0, n = connections.Length(); i < n; i++) {
    728      if (!connections[i]->isClosed()) {
    729        // getFilename is only the leaf name for the database file,
    730        // so it shouldn't contain privacy-sensitive information.
    731        CrashReporter::RecordAnnotationNSCString(
    732            CrashReporter::Annotation::StorageConnectionNotClosed,
    733            connections[i]->getFilename());
    734        printf_stderr("Storage connection not closed: %s",
    735                      connections[i]->getFilename().get());
    736        MOZ_CRASH();
    737      }
    738    }
    739 #endif
    740  }
    741 
    742  return NS_OK;
    743 }
    744 
    745 }  // namespace mozilla::storage