tor-browser

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

mozStorageConnection.cpp (100262B)


      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 "ErrorList.h"
      9 #include "nsError.h"
     10 #include "nsThreadUtils.h"
     11 #include "nsIFile.h"
     12 #include "nsIFileURL.h"
     13 #include "nsIXPConnect.h"
     14 #include "mozilla/AppShutdown.h"
     15 #include "mozilla/CheckedInt.h"
     16 #include "mozilla/glean/StorageMetrics.h"
     17 #include "mozilla/Telemetry.h"
     18 #include "mozilla/Mutex.h"
     19 #include "mozilla/CondVar.h"
     20 #include "mozilla/Attributes.h"
     21 #include "mozilla/ErrorNames.h"
     22 #include "mozilla/dom/quota/QuotaObject.h"
     23 #include "mozilla/ScopeExit.h"
     24 #include "mozilla/SpinEventLoopUntil.h"
     25 #include "mozilla/StaticPrefs_storage.h"
     26 
     27 #include "mozIStorageCompletionCallback.h"
     28 #include "mozIStorageFunction.h"
     29 
     30 #include "mozStorageAsyncStatementExecution.h"
     31 #include "mozStorageSQLFunctions.h"
     32 #include "mozStorageConnection.h"
     33 #include "mozStorageService.h"
     34 #include "mozStorageStatement.h"
     35 #include "mozStorageAsyncStatement.h"
     36 #include "mozStorageArgValueArray.h"
     37 #include "mozStoragePrivateHelpers.h"
     38 #include "mozStorageStatementData.h"
     39 #include "ObfuscatingVFS.h"
     40 #include "QuotaVFS.h"
     41 #include "StorageBaseStatementInternal.h"
     42 #include "SQLCollations.h"
     43 #include "FileSystemModule.h"
     44 #include "mozStorageHelper.h"
     45 #include "sqlite3_static_ext.h"
     46 
     47 #include "mozilla/Assertions.h"
     48 #include "mozilla/Logging.h"
     49 #include "mozilla/Printf.h"
     50 #include "mozilla/ProfilerLabels.h"
     51 #include "mozilla/RefPtr.h"
     52 #include "nsComponentManagerUtils.h"
     53 #include "nsProxyRelease.h"
     54 #include "nsStringFwd.h"
     55 #include "nsURLHelper.h"
     56 
     57 #define MIN_AVAILABLE_BYTES_PER_CHUNKED_GROWTH 524288000  // 500 MiB
     58 
     59 // Maximum size of the pages cache per connection.
     60 #define MAX_CACHE_SIZE_KIBIBYTES 2048  // 2 MiB
     61 
     62 mozilla::LazyLogModule gStorageLog("mozStorage");
     63 
     64 // Checks that the protected code is running on the main-thread only if the
     65 // connection was also opened on it.
     66 #ifdef DEBUG
     67 #  define CHECK_MAINTHREAD_ABUSE()                                   \
     68    do {                                                             \
     69      NS_WARNING_ASSERTION(                                          \
     70          eventTargetOpenedOn == GetMainThreadSerialEventTarget() || \
     71              !NS_IsMainThread(),                                    \
     72          "Using Storage synchronous API on main-thread, but "       \
     73          "the connection was opened on another thread.");           \
     74    } while (0)
     75 #else
     76 #  define CHECK_MAINTHREAD_ABUSE() \
     77    do { /* Nothing */             \
     78    } while (0)
     79 #endif
     80 
     81 namespace mozilla::storage {
     82 
     83 using mozilla::dom::quota::QuotaObject;
     84 
     85 namespace {
     86 
     87 int nsresultToSQLiteResult(nsresult aXPCOMResultCode) {
     88  if (NS_SUCCEEDED(aXPCOMResultCode)) {
     89    return SQLITE_OK;
     90  }
     91 
     92  switch (aXPCOMResultCode) {
     93    case NS_ERROR_FILE_CORRUPTED:
     94      return SQLITE_CORRUPT;
     95    case NS_ERROR_FILE_ACCESS_DENIED:
     96      return SQLITE_CANTOPEN;
     97    case NS_ERROR_STORAGE_BUSY:
     98      return SQLITE_BUSY;
     99    case NS_ERROR_FILE_IS_LOCKED:
    100      return SQLITE_LOCKED;
    101    case NS_ERROR_FILE_READ_ONLY:
    102      return SQLITE_READONLY;
    103    case NS_ERROR_STORAGE_IOERR:
    104      return SQLITE_IOERR;
    105    case NS_ERROR_FILE_NO_DEVICE_SPACE:
    106      return SQLITE_FULL;
    107    case NS_ERROR_OUT_OF_MEMORY:
    108      return SQLITE_NOMEM;
    109    case NS_ERROR_UNEXPECTED:
    110      return SQLITE_MISUSE;
    111    case NS_ERROR_ABORT:
    112      return SQLITE_ABORT;
    113    case NS_ERROR_STORAGE_CONSTRAINT:
    114      return SQLITE_CONSTRAINT;
    115    default:
    116      return SQLITE_ERROR;
    117  }
    118 
    119  MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Must return in switch above!");
    120 }
    121 
    122 ////////////////////////////////////////////////////////////////////////////////
    123 //// Variant Specialization Functions (variantToSQLiteT)
    124 
    125 int sqlite3_T_int(sqlite3_context* aCtx, int aValue) {
    126  ::sqlite3_result_int(aCtx, aValue);
    127  return SQLITE_OK;
    128 }
    129 
    130 int sqlite3_T_int64(sqlite3_context* aCtx, sqlite3_int64 aValue) {
    131  ::sqlite3_result_int64(aCtx, aValue);
    132  return SQLITE_OK;
    133 }
    134 
    135 int sqlite3_T_double(sqlite3_context* aCtx, double aValue) {
    136  ::sqlite3_result_double(aCtx, aValue);
    137  return SQLITE_OK;
    138 }
    139 
    140 int sqlite3_T_text(sqlite3_context* aCtx, const nsCString& aValue) {
    141  CheckedInt<int32_t> length(aValue.Length());
    142  if (!length.isValid()) {
    143    return SQLITE_MISUSE;
    144  }
    145  ::sqlite3_result_text(aCtx, aValue.get(), length.value(), SQLITE_TRANSIENT);
    146  return SQLITE_OK;
    147 }
    148 
    149 int sqlite3_T_text16(sqlite3_context* aCtx, const nsString& aValue) {
    150  CheckedInt<int32_t> n_bytes =
    151      CheckedInt<int32_t>(aValue.Length()) * sizeof(char16_t);
    152  if (!n_bytes.isValid()) {
    153    return SQLITE_MISUSE;
    154  }
    155  ::sqlite3_result_text16(aCtx, aValue.get(), n_bytes.value(),
    156                          SQLITE_TRANSIENT);
    157  return SQLITE_OK;
    158 }
    159 
    160 int sqlite3_T_null(sqlite3_context* aCtx) {
    161  ::sqlite3_result_null(aCtx);
    162  return SQLITE_OK;
    163 }
    164 
    165 int sqlite3_T_blob(sqlite3_context* aCtx, const void* aData, int aSize) {
    166  ::sqlite3_result_blob(aCtx, aData, aSize, free);
    167  return SQLITE_OK;
    168 }
    169 
    170 int sqlite3_T_array(sqlite3_context* aCtx, const void* aData, int aSize,
    171                    int aType) {
    172  // Not supported for now.
    173  return SQLITE_MISUSE;
    174 }
    175 
    176 #include "variantToSQLiteT_impl.h"
    177 
    178 ////////////////////////////////////////////////////////////////////////////////
    179 //// Modules
    180 
    181 struct Module {
    182  const char* name;
    183  int (*registerFunc)(sqlite3*, const char*);
    184 };
    185 
    186 Module gModules[] = {{"filesystem", RegisterFileSystemModule}};
    187 
    188 ////////////////////////////////////////////////////////////////////////////////
    189 //// Local Functions
    190 
    191 int tracefunc(unsigned aReason, void* aClosure, void* aP, void* aX) {
    192  switch (aReason) {
    193    case SQLITE_TRACE_STMT: {
    194      // aP is a pointer to the prepared statement.
    195      sqlite3_stmt* stmt = static_cast<sqlite3_stmt*>(aP);
    196      // aX is a pointer to a string containing the unexpanded SQL or a comment,
    197      // starting with "--"" in case of a trigger.
    198      char* expanded = static_cast<char*>(aX);
    199      // Simulate what sqlite_trace was doing.
    200      if (!::strncmp(expanded, "--", 2)) {
    201        MOZ_LOG(gStorageLog, LogLevel::Debug,
    202                ("TRACE_STMT on %p: '%s'", aClosure, expanded));
    203      } else {
    204        char* sql = ::sqlite3_expanded_sql(stmt);
    205        MOZ_LOG(gStorageLog, LogLevel::Debug,
    206                ("TRACE_STMT on %p: '%s'", aClosure, sql));
    207        ::sqlite3_free(sql);
    208      }
    209      break;
    210    }
    211    case SQLITE_TRACE_PROFILE: {
    212      // aX is pointer to a 64bit integer containing nanoseconds it took to
    213      // execute the last command.
    214      sqlite_int64 time = *(static_cast<sqlite_int64*>(aX)) / 1000000;
    215      if (time > 0) {
    216        MOZ_LOG(gStorageLog, LogLevel::Debug,
    217                ("TRACE_TIME on %p: %lldms", aClosure, time));
    218      }
    219      break;
    220    }
    221  }
    222  return 0;
    223 }
    224 
    225 void basicFunctionHelper(sqlite3_context* aCtx, int aArgc,
    226                         sqlite3_value** aArgv) {
    227  void* userData = ::sqlite3_user_data(aCtx);
    228 
    229  mozIStorageFunction* func = static_cast<mozIStorageFunction*>(userData);
    230 
    231  RefPtr<ArgValueArray> arguments(new ArgValueArray(aArgc, aArgv));
    232  if (!arguments) return;
    233 
    234  nsCOMPtr<nsIVariant> result;
    235  nsresult rv = func->OnFunctionCall(arguments, getter_AddRefs(result));
    236  if (NS_FAILED(rv)) {
    237    nsAutoCString errorMessage;
    238    GetErrorName(rv, errorMessage);
    239    errorMessage.InsertLiteral("User function returned ", 0);
    240    errorMessage.Append('!');
    241 
    242    NS_WARNING(errorMessage.get());
    243 
    244    ::sqlite3_result_error(aCtx, errorMessage.get(), -1);
    245    ::sqlite3_result_error_code(aCtx, nsresultToSQLiteResult(rv));
    246    return;
    247  }
    248  int retcode = variantToSQLiteT(aCtx, result);
    249  if (retcode != SQLITE_OK) {
    250    NS_WARNING("User function returned invalid data type!");
    251    ::sqlite3_result_error(aCtx, "User function returned invalid data type",
    252                           -1);
    253  }
    254 }
    255 
    256 RefPtr<QuotaObject> GetQuotaObject(sqlite3_file* aFile, bool obfuscatingVFS) {
    257  return obfuscatingVFS
    258             ? mozilla::storage::obfsvfs::GetQuotaObjectForFile(aFile)
    259             : mozilla::storage::quotavfs::GetQuotaObjectForFile(aFile);
    260 }
    261 
    262 /**
    263 * This code is heavily based on the sample at:
    264 *   http://www.sqlite.org/unlock_notify.html
    265 */
    266 class UnlockNotification {
    267 public:
    268  UnlockNotification()
    269      : mMutex("UnlockNotification mMutex"),
    270        mCondVar(mMutex, "UnlockNotification condVar"),
    271        mSignaled(false) {}
    272 
    273  void Wait() {
    274    MutexAutoLock lock(mMutex);
    275    while (!mSignaled) {
    276      (void)mCondVar.Wait();
    277    }
    278  }
    279 
    280  void Signal() {
    281    MutexAutoLock lock(mMutex);
    282    mSignaled = true;
    283    (void)mCondVar.Notify();
    284  }
    285 
    286 private:
    287  Mutex mMutex MOZ_UNANNOTATED;
    288  CondVar mCondVar;
    289  bool mSignaled;
    290 };
    291 
    292 void UnlockNotifyCallback(void** aArgs, int aArgsSize) {
    293  for (int i = 0; i < aArgsSize; i++) {
    294    UnlockNotification* notification =
    295        static_cast<UnlockNotification*>(aArgs[i]);
    296    notification->Signal();
    297  }
    298 }
    299 
    300 int WaitForUnlockNotify(sqlite3* aDatabase) {
    301  UnlockNotification notification;
    302  int srv =
    303      ::sqlite3_unlock_notify(aDatabase, UnlockNotifyCallback, &notification);
    304  MOZ_ASSERT(srv == SQLITE_LOCKED || srv == SQLITE_OK);
    305  if (srv == SQLITE_OK) {
    306    notification.Wait();
    307  }
    308 
    309  return srv;
    310 }
    311 
    312 ////////////////////////////////////////////////////////////////////////////////
    313 //// Local Classes
    314 
    315 class AsyncCloseConnection final : public Runnable {
    316 public:
    317  AsyncCloseConnection(Connection* aConnection, sqlite3* aNativeConnection,
    318                       nsIRunnable* aCallbackEvent)
    319      : Runnable("storage::AsyncCloseConnection"),
    320        mConnection(aConnection),
    321        mNativeConnection(aNativeConnection),
    322        mCallbackEvent(aCallbackEvent) {}
    323 
    324  NS_IMETHOD Run() override {
    325    // Make sure we don't dispatch to the current thread.
    326    MOZ_ASSERT(!IsOnCurrentSerialEventTarget(mConnection->eventTargetOpenedOn));
    327 
    328    nsCOMPtr<nsIRunnable> event =
    329        NewRunnableMethod("storage::Connection::shutdownAsyncThread",
    330                          mConnection, &Connection::shutdownAsyncThread);
    331    MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(event));
    332 
    333    // Internal close.
    334    (void)mConnection->internalClose(mNativeConnection);
    335 
    336    // Callback
    337    if (mCallbackEvent) {
    338      nsCOMPtr<nsIThread> thread;
    339      (void)NS_GetMainThread(getter_AddRefs(thread));
    340      (void)thread->Dispatch(mCallbackEvent, NS_DISPATCH_NORMAL);
    341    }
    342 
    343    return NS_OK;
    344  }
    345 
    346  ~AsyncCloseConnection() override {
    347    NS_ReleaseOnMainThread("AsyncCloseConnection::mConnection",
    348                           mConnection.forget());
    349    NS_ReleaseOnMainThread("AsyncCloseConnection::mCallbackEvent",
    350                           mCallbackEvent.forget());
    351  }
    352 
    353 private:
    354  RefPtr<Connection> mConnection;
    355  sqlite3* mNativeConnection;
    356  nsCOMPtr<nsIRunnable> mCallbackEvent;
    357 };
    358 
    359 /**
    360 * An event used to initialize the clone of a connection.
    361 *
    362 * Must be executed on the clone's async execution thread.
    363 */
    364 class AsyncInitializeClone final : public Runnable {
    365 public:
    366  /**
    367   * @param aConnection The connection being cloned.
    368   * @param aClone The clone.
    369   * @param aReadOnly If |true|, the clone is read only.
    370   * @param aCallback A callback to trigger once initialization
    371   *                  is complete. This event will be called on
    372   *                  aClone->eventTargetOpenedOn.
    373   */
    374  AsyncInitializeClone(Connection* aConnection, Connection* aClone,
    375                       const bool aReadOnly,
    376                       mozIStorageCompletionCallback* aCallback)
    377      : Runnable("storage::AsyncInitializeClone"),
    378        mConnection(aConnection),
    379        mClone(aClone),
    380        mReadOnly(aReadOnly),
    381        mCallback(aCallback) {
    382    MOZ_ASSERT(NS_IsMainThread());
    383  }
    384 
    385  NS_IMETHOD Run() override {
    386    MOZ_ASSERT(!NS_IsMainThread());
    387    nsresult rv = mConnection->initializeClone(mClone, mReadOnly);
    388    if (NS_FAILED(rv)) {
    389      return Dispatch(rv, nullptr);
    390    }
    391    return Dispatch(NS_OK,
    392                    NS_ISUPPORTS_CAST(mozIStorageAsyncConnection*, mClone));
    393  }
    394 
    395 private:
    396  nsresult Dispatch(nsresult aResult, nsISupports* aValue) {
    397    RefPtr<CallbackComplete> event =
    398        new CallbackComplete(aResult, aValue, mCallback.forget());
    399    return mClone->eventTargetOpenedOn->Dispatch(event, NS_DISPATCH_NORMAL);
    400  }
    401 
    402  ~AsyncInitializeClone() override {
    403    nsCOMPtr<nsIThread> thread;
    404    DebugOnly<nsresult> rv = NS_GetMainThread(getter_AddRefs(thread));
    405    MOZ_ASSERT(NS_SUCCEEDED(rv));
    406 
    407    // Handle ambiguous nsISupports inheritance.
    408    NS_ProxyRelease("AsyncInitializeClone::mConnection", thread,
    409                    mConnection.forget());
    410    NS_ProxyRelease("AsyncInitializeClone::mClone", thread, mClone.forget());
    411 
    412    // Generally, the callback will be released by CallbackComplete.
    413    // However, if for some reason Run() is not executed, we still
    414    // need to ensure that it is released here.
    415    NS_ProxyRelease("AsyncInitializeClone::mCallback", thread,
    416                    mCallback.forget());
    417  }
    418 
    419  RefPtr<Connection> mConnection;
    420  RefPtr<Connection> mClone;
    421  const bool mReadOnly;
    422  nsCOMPtr<mozIStorageCompletionCallback> mCallback;
    423 };
    424 
    425 /**
    426 * A listener for async connection closing.
    427 */
    428 class CloseListener final : public mozIStorageCompletionCallback {
    429 public:
    430  NS_DECL_ISUPPORTS
    431  CloseListener() : mClosed(false) {}
    432 
    433  NS_IMETHOD Complete(nsresult, nsISupports*) override {
    434    mClosed = true;
    435    return NS_OK;
    436  }
    437 
    438  bool mClosed;
    439 
    440 private:
    441  ~CloseListener() = default;
    442 };
    443 
    444 NS_IMPL_ISUPPORTS(CloseListener, mozIStorageCompletionCallback)
    445 
    446 class AsyncVacuumEvent final : public Runnable {
    447 public:
    448  AsyncVacuumEvent(Connection* aConnection,
    449                   mozIStorageCompletionCallback* aCallback,
    450                   bool aUseIncremental, int32_t aSetPageSize)
    451      : Runnable("storage::AsyncVacuum"),
    452        mConnection(aConnection),
    453        mCallback(aCallback),
    454        mUseIncremental(aUseIncremental),
    455        mSetPageSize(aSetPageSize),
    456        mStatus(NS_ERROR_UNEXPECTED) {}
    457 
    458  NS_IMETHOD Run() override {
    459    // This is initially dispatched to the helper thread, then re-dispatched
    460    // to the opener thread, where it will callback.
    461    if (IsOnCurrentSerialEventTarget(mConnection->eventTargetOpenedOn)) {
    462      // Send the completion event.
    463      if (mCallback) {
    464        (void)mCallback->Complete(mStatus, nullptr);
    465      }
    466      return NS_OK;
    467    }
    468 
    469    // Ensure to invoke the callback regardless of errors.
    470    auto guard = MakeScopeExit([&]() {
    471      mConnection->mIsStatementOnHelperThreadInterruptible = false;
    472      (void)mConnection->eventTargetOpenedOn->Dispatch(this,
    473                                                       NS_DISPATCH_NORMAL);
    474    });
    475 
    476    // Get list of attached databases.
    477    nsCOMPtr<mozIStorageStatement> stmt;
    478    nsresult rv = mConnection->CreateStatement(MOZ_STORAGE_UNIQUIFY_QUERY_STR
    479                                               "PRAGMA database_list"_ns,
    480                                               getter_AddRefs(stmt));
    481    NS_ENSURE_SUCCESS(rv, rv);
    482    // We must accumulate names and loop through them later, otherwise VACUUM
    483    // will see an ongoing statement and bail out.
    484    nsTArray<nsCString> schemaNames;
    485    bool hasResult = false;
    486    while (stmt && NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
    487      nsAutoCString name;
    488      rv = stmt->GetUTF8String(1, name);
    489      if (NS_SUCCEEDED(rv) && !name.EqualsLiteral("temp")) {
    490        schemaNames.AppendElement(name);
    491      }
    492    }
    493    mStatus = NS_OK;
    494    // Mark this vacuum as an interruptible operation, so it can be interrupted
    495    // if the connection closes during shutdown.
    496    mConnection->mIsStatementOnHelperThreadInterruptible = true;
    497    for (const nsCString& schemaName : schemaNames) {
    498      rv = this->Vacuum(schemaName);
    499      if (NS_FAILED(rv)) {
    500        // This is sub-optimal since it's only keeping the last error reason,
    501        // but it will do for now.
    502        mStatus = rv;
    503      }
    504    }
    505    return mStatus;
    506  }
    507 
    508  nsresult Vacuum(const nsACString& aSchemaName) {
    509    // Abort if we're in shutdown.
    510    if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
    511      return NS_ERROR_ABORT;
    512    }
    513    int32_t removablePages = mConnection->RemovablePagesInFreeList(aSchemaName);
    514    if (!removablePages) {
    515      // There's no empty pages to remove, so skip this vacuum for now.
    516      return NS_OK;
    517    }
    518    nsresult rv;
    519    bool needsFullVacuum = true;
    520 
    521    if (mSetPageSize) {
    522      nsAutoCString query(MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA ");
    523      query.Append(aSchemaName);
    524      query.AppendLiteral(".page_size = ");
    525      query.AppendInt(mSetPageSize);
    526      nsCOMPtr<mozIStorageStatement> stmt;
    527      rv = mConnection->ExecuteSimpleSQL(query);
    528      NS_ENSURE_SUCCESS(rv, rv);
    529    }
    530 
    531    // Check auto_vacuum.
    532    {
    533      nsAutoCString query(MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA ");
    534      query.Append(aSchemaName);
    535      query.AppendLiteral(".auto_vacuum");
    536      nsCOMPtr<mozIStorageStatement> stmt;
    537      rv = mConnection->CreateStatement(query, getter_AddRefs(stmt));
    538      NS_ENSURE_SUCCESS(rv, rv);
    539      bool hasResult = false;
    540      bool changeAutoVacuum = false;
    541      if (stmt && NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
    542        bool isIncrementalVacuum = stmt->AsInt32(0) == 2;
    543        changeAutoVacuum = isIncrementalVacuum != mUseIncremental;
    544        if (isIncrementalVacuum && !changeAutoVacuum) {
    545          needsFullVacuum = false;
    546        }
    547      }
    548      // Changing auto_vacuum is only supported on the main schema.
    549      if (aSchemaName.EqualsLiteral("main") && changeAutoVacuum) {
    550        nsAutoCString query(MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA ");
    551        query.Append(aSchemaName);
    552        query.AppendLiteral(".auto_vacuum = ");
    553        query.AppendInt(mUseIncremental ? 2 : 0);
    554        rv = mConnection->ExecuteSimpleSQL(query);
    555        NS_ENSURE_SUCCESS(rv, rv);
    556      }
    557    }
    558 
    559    if (needsFullVacuum) {
    560      nsAutoCString query(MOZ_STORAGE_UNIQUIFY_QUERY_STR "VACUUM ");
    561      query.Append(aSchemaName);
    562      rv = mConnection->ExecuteSimpleSQL(query);
    563      // TODO (Bug 1818039): Report failed vacuum telemetry.
    564      NS_ENSURE_SUCCESS(rv, rv);
    565    } else {
    566      nsAutoCString query(MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA ");
    567      query.Append(aSchemaName);
    568      query.AppendLiteral(".incremental_vacuum(");
    569      query.AppendInt(removablePages);
    570      query.AppendLiteral(")");
    571      rv = mConnection->ExecuteSimpleSQL(query);
    572      NS_ENSURE_SUCCESS(rv, rv);
    573    }
    574 
    575    return NS_OK;
    576  }
    577 
    578  ~AsyncVacuumEvent() override {
    579    NS_ReleaseOnMainThread("AsyncVacuum::mConnection", mConnection.forget());
    580    NS_ReleaseOnMainThread("AsyncVacuum::mCallback", mCallback.forget());
    581  }
    582 
    583 private:
    584  RefPtr<Connection> mConnection;
    585  nsCOMPtr<mozIStorageCompletionCallback> mCallback;
    586  bool mUseIncremental;
    587  int32_t mSetPageSize;
    588  Atomic<nsresult> mStatus;
    589 };
    590 
    591 /**
    592 * A runnable to perform an SQLite database backup when there may be one or more
    593 * open connections on that database.
    594 */
    595 class AsyncBackupDatabaseFile final : public Runnable, public nsITimerCallback {
    596 public:
    597  NS_DECL_ISUPPORTS_INHERITED
    598 
    599  /**
    600   * @param aConnection The connection to the database being backed up.
    601   * @param aNativeConnection The native connection to the database being backed
    602   *                          up.
    603   * @param aDestinationFile The destination file for the created backup.
    604   * @param aCallback A callback to trigger once the backup process has
    605   *                  completed. The callback will be supplied with an nsresult
    606   *                  indicating whether or not the backup was successfully
    607   *                  created. This callback will be called on the
    608   *                  mConnection->eventTargetOpenedOn thread.
    609   * @throws
    610   */
    611  AsyncBackupDatabaseFile(Connection* aConnection, sqlite3* aNativeConnection,
    612                          nsIFile* aDestinationFile,
    613                          mozIStorageCompletionCallback* aCallback,
    614                          int32_t aPagesPerStep, uint32_t aStepDelayMs)
    615      : Runnable("storage::AsyncBackupDatabaseFile"),
    616        mConnection(aConnection),
    617        mNativeConnection(aNativeConnection),
    618        mDestinationFile(aDestinationFile),
    619        mCallback(aCallback),
    620        mPagesPerStep(aPagesPerStep),
    621        mStepDelayMs(aStepDelayMs),
    622        mBackupFile(nullptr),
    623        mBackupHandle(nullptr) {
    624    MOZ_ASSERT(NS_IsMainThread());
    625  }
    626 
    627  NS_IMETHOD Run() override {
    628    MOZ_ASSERT(!NS_IsMainThread());
    629 
    630    nsAutoString path;
    631    nsresult rv = mDestinationFile->GetPath(path);
    632    if (NS_FAILED(rv)) {
    633      return Dispatch(rv, nullptr);
    634    }
    635    // Put a .tmp on the end of the destination while the backup is underway.
    636    // This extension will be stripped off after the backup successfully
    637    // completes.
    638    path.AppendLiteral(".tmp");
    639 
    640    int srv = ::sqlite3_open(NS_ConvertUTF16toUTF8(path).get(), &mBackupFile);
    641    if (srv != SQLITE_OK) {
    642      ::sqlite3_close(mBackupFile);
    643      mBackupFile = nullptr;
    644      return Dispatch(NS_ERROR_FAILURE, nullptr);
    645    }
    646 
    647    static const char* mainDBName = "main";
    648 
    649    mBackupHandle = ::sqlite3_backup_init(mBackupFile, mainDBName,
    650                                          mNativeConnection, mainDBName);
    651    if (!mBackupHandle) {
    652      MOZ_ALWAYS_TRUE(::sqlite3_close(mBackupFile) == SQLITE_OK);
    653      return Dispatch(NS_ERROR_FAILURE, nullptr);
    654    }
    655 
    656    return DoStep();
    657  }
    658 
    659  NS_IMETHOD
    660  Notify(nsITimer* aTimer) override { return DoStep(); }
    661 
    662 private:
    663  nsresult DoStep() {
    664 #define DISPATCH_AND_RETURN_IF_FAILED(rv) \
    665  if (NS_FAILED(rv)) {                    \
    666    return Dispatch(rv, nullptr);         \
    667  }
    668 
    669    // This guard is used to close the backup database in the event of
    670    // some failure throughout this process. We release the exit guard
    671    // only if we complete the backup successfully, or defer to another
    672    // later call to DoStep.
    673    auto guard = MakeScopeExit([&]() {
    674      MOZ_ALWAYS_TRUE(::sqlite3_close(mBackupFile) == SQLITE_OK);
    675      mBackupFile = nullptr;
    676    });
    677 
    678    MOZ_ASSERT(!NS_IsMainThread());
    679    nsAutoString originalPath;
    680    nsresult rv = mDestinationFile->GetPath(originalPath);
    681    DISPATCH_AND_RETURN_IF_FAILED(rv);
    682 
    683    nsAutoString tempPath = originalPath;
    684    tempPath.AppendLiteral(".tmp");
    685 
    686    nsCOMPtr<nsIFile> file;
    687    rv = NS_NewLocalFile(tempPath, getter_AddRefs(file));
    688    DISPATCH_AND_RETURN_IF_FAILED(rv);
    689 
    690    int srv = ::sqlite3_backup_step(mBackupHandle, mPagesPerStep);
    691    if (srv == SQLITE_OK || srv == SQLITE_BUSY || srv == SQLITE_LOCKED) {
    692      // We're continuing the backup later. Release the guard to avoid closing
    693      // the database.
    694      guard.release();
    695      // Queue up the next step
    696      return NS_NewTimerWithCallback(getter_AddRefs(mTimer), this, mStepDelayMs,
    697                                     nsITimer::TYPE_ONE_SHOT,
    698                                     GetCurrentSerialEventTarget());
    699    }
    700 #ifdef DEBUG
    701    if (srv != SQLITE_DONE) {
    702      nsCString warnMsg;
    703      warnMsg.AppendLiteral(
    704          "The SQLite database copy could not be completed due to an error: ");
    705      warnMsg.Append(::sqlite3_errmsg(mBackupFile));
    706      NS_WARNING(warnMsg.get());
    707    }
    708 #endif
    709 
    710    (void)::sqlite3_backup_finish(mBackupHandle);
    711    MOZ_ALWAYS_TRUE(::sqlite3_close(mBackupFile) == SQLITE_OK);
    712    mBackupFile = nullptr;
    713 
    714    // The database is already closed, so we can release this guard now.
    715    guard.release();
    716 
    717    if (srv != SQLITE_DONE) {
    718      NS_WARNING("Failed to create database copy.");
    719 
    720      // The partially created database file is not useful. Let's remove it.
    721      rv = file->Remove(false);
    722      if (NS_FAILED(rv)) {
    723        NS_WARNING(
    724            "Removing a partially backed up SQLite database file failed.");
    725      }
    726 
    727      return Dispatch(convertResultCode(srv), nullptr);
    728    }
    729 
    730    // Now that we've successfully created the copy, we'll strip off the .tmp
    731    // extension.
    732 
    733    nsAutoString leafName;
    734    rv = mDestinationFile->GetLeafName(leafName);
    735    DISPATCH_AND_RETURN_IF_FAILED(rv);
    736 
    737    rv = file->RenameTo(nullptr, leafName);
    738    DISPATCH_AND_RETURN_IF_FAILED(rv);
    739 
    740 #undef DISPATCH_AND_RETURN_IF_FAILED
    741    return Dispatch(NS_OK, nullptr);
    742  }
    743 
    744  nsresult Dispatch(nsresult aResult, nsISupports* aValue) {
    745    RefPtr<CallbackComplete> event =
    746        new CallbackComplete(aResult, aValue, mCallback.forget());
    747    return mConnection->eventTargetOpenedOn->Dispatch(event,
    748                                                      NS_DISPATCH_NORMAL);
    749  }
    750 
    751  ~AsyncBackupDatabaseFile() override {
    752    nsresult rv;
    753    nsCOMPtr<nsIThread> thread =
    754        do_QueryInterface(mConnection->eventTargetOpenedOn, &rv);
    755    MOZ_ASSERT(NS_SUCCEEDED(rv));
    756 
    757    // Handle ambiguous nsISupports inheritance.
    758    NS_ProxyRelease("AsyncBackupDatabaseFile::mConnection", thread,
    759                    mConnection.forget());
    760    NS_ProxyRelease("AsyncBackupDatabaseFile::mDestinationFile", thread,
    761                    mDestinationFile.forget());
    762 
    763    // Generally, the callback will be released by CallbackComplete.
    764    // However, if for some reason Run() is not executed, we still
    765    // need to ensure that it is released here.
    766    NS_ProxyRelease("AsyncInitializeClone::mCallback", thread,
    767                    mCallback.forget());
    768  }
    769 
    770  RefPtr<Connection> mConnection;
    771  sqlite3* mNativeConnection;
    772  nsCOMPtr<nsITimer> mTimer;
    773  nsCOMPtr<nsIFile> mDestinationFile;
    774  nsCOMPtr<mozIStorageCompletionCallback> mCallback;
    775  int32_t mPagesPerStep;
    776  uint32_t mStepDelayMs;
    777  sqlite3* mBackupFile;
    778  sqlite3_backup* mBackupHandle;
    779 };
    780 
    781 NS_IMPL_ISUPPORTS_INHERITED(AsyncBackupDatabaseFile, Runnable, nsITimerCallback)
    782 
    783 }  // namespace
    784 
    785 ////////////////////////////////////////////////////////////////////////////////
    786 //// Connection
    787 
    788 Connection::Connection(Service* aService, int aFlags,
    789                       ConnectionOperation aSupportedOperations,
    790                       const nsCString& aTelemetryFilename, bool aInterruptible,
    791                       bool aIgnoreLockingMode, bool aOpenNotExclusive)
    792    : sharedAsyncExecutionMutex("Connection::sharedAsyncExecutionMutex"),
    793      sharedDBMutex("Connection::sharedDBMutex"),
    794      eventTargetOpenedOn(WrapNotNull(GetCurrentSerialEventTarget())),
    795      mIsStatementOnHelperThreadInterruptible(false),
    796      mDBConn(nullptr),
    797      mDefaultTransactionType(mozIStorageConnection::TRANSACTION_DEFERRED),
    798      mDestroying(false),
    799      mProgressHandler(nullptr),
    800      mStorageService(aService),
    801      mFlags(aFlags),
    802      mTransactionNestingLevel(0),
    803      mSupportedOperations(aSupportedOperations),
    804      mInterruptible(aSupportedOperations == Connection::ASYNCHRONOUS ||
    805                     aInterruptible),
    806      mIgnoreLockingMode(aIgnoreLockingMode),
    807      mOpenNotExclusive(aOpenNotExclusive),
    808      mAsyncExecutionThreadShuttingDown(false),
    809      mConnectionClosed(false),
    810      mGrowthChunkSize(0) {
    811  MOZ_ASSERT(!mIgnoreLockingMode || mFlags & SQLITE_OPEN_READONLY,
    812             "Can't ignore locking for a non-readonly connection!");
    813  mStorageService->registerConnection(this);
    814  MOZ_ASSERT(!aTelemetryFilename.IsEmpty(),
    815             "A telemetry filename should have been passed-in.");
    816  mTelemetryFilename.Assign(aTelemetryFilename);
    817 }
    818 
    819 Connection::~Connection() {
    820  // Failsafe Close() occurs in our custom Release method because of
    821  // complications related to Close() potentially invoking AsyncClose() which
    822  // will increment our refcount.
    823  MOZ_ASSERT(!mAsyncExecutionThread,
    824             "The async thread has not been shutdown properly!");
    825 }
    826 
    827 NS_IMPL_ADDREF(Connection)
    828 
    829 NS_INTERFACE_MAP_BEGIN(Connection)
    830  NS_INTERFACE_MAP_ENTRY(mozIStorageAsyncConnection)
    831  NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
    832  NS_INTERFACE_MAP_ENTRY(mozIStorageConnection)
    833  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, mozIStorageConnection)
    834 NS_INTERFACE_MAP_END
    835 
    836 // This is identical to what NS_IMPL_RELEASE provides, but with the
    837 // extra |1 == count| case.
    838 NS_IMETHODIMP_(MozExternalRefCountType) Connection::Release(void) {
    839  MOZ_ASSERT(0 != mRefCnt, "dup release");
    840  nsrefcnt count = --mRefCnt;
    841  NS_LOG_RELEASE(this, count, "Connection");
    842  if (1 == count) {
    843    // If the refcount went to 1, the single reference must be from
    844    // gService->mConnections (in class |Service|).  And the code calling
    845    // Release is either:
    846    // - The "user" code that had created the connection, releasing on any
    847    //   thread.
    848    // - One of Service's getConnections() callers had acquired a strong
    849    //   reference to the Connection that out-lived the last "user" reference,
    850    //   and now that just got dropped.  Note that this reference could be
    851    //   getting dropped on the main thread or Connection->eventTargetOpenedOn
    852    //   (because of the NewRunnableMethod used by minimizeMemory).
    853    //
    854    // Either way, we should now perform our failsafe Close() and unregister.
    855    // However, we only want to do this once, and the reality is that our
    856    // refcount could go back up above 1 and down again at any time if we are
    857    // off the main thread and getConnections() gets called on the main thread,
    858    // so we use an atomic here to do this exactly once.
    859    if (mDestroying.compareExchange(false, true)) {
    860      // Close the connection, dispatching to the opening event target if we're
    861      // not on that event target already and that event target is still
    862      // accepting runnables. We do this because it's possible we're on the main
    863      // thread because of getConnections(), and we REALLY don't want to
    864      // transfer I/O to the main thread if we can avoid it.
    865      if (IsOnCurrentSerialEventTarget(eventTargetOpenedOn)) {
    866        // This could cause SpinningSynchronousClose() to be invoked and AddRef
    867        // triggered for AsyncCloseConnection's strong ref if the conn was ever
    868        // use for async purposes.  (Main-thread only, though.)
    869        (void)synchronousClose();
    870      } else {
    871        nsCOMPtr<nsIRunnable> event =
    872            NewRunnableMethod("storage::Connection::synchronousClose", this,
    873                              &Connection::synchronousClose);
    874        if (NS_FAILED(eventTargetOpenedOn->Dispatch(event.forget(),
    875                                                    NS_DISPATCH_NORMAL))) {
    876          // The event target was dead and so we've just leaked our runnable.
    877          // This should not happen because our non-main-thread consumers should
    878          // be explicitly closing their connections, not relying on us to close
    879          // them for them.  (It's okay to let a statement go out of scope for
    880          // automatic cleanup, but not a Connection.)
    881          MOZ_ASSERT(false,
    882                     "Leaked Connection::synchronousClose(), ownership fail.");
    883          (void)synchronousClose();
    884        }
    885      }
    886 
    887      // This will drop its strong reference right here, right now.
    888      mStorageService->unregisterConnection(this);
    889    }
    890  } else if (0 == count) {
    891    mRefCnt = 1; /* stabilize */
    892 #if 0            /* enable this to find non-threadsafe destructors: */
    893    NS_ASSERT_OWNINGTHREAD(Connection);
    894 #endif
    895    delete (this);
    896    return 0;
    897  }
    898  return count;
    899 }
    900 
    901 int32_t Connection::getSqliteRuntimeStatus(int32_t aStatusOption,
    902                                           int32_t* aMaxValue) {
    903  MOZ_ASSERT(connectionReady(), "A connection must exist at this point");
    904  int curr = 0, max = 0;
    905  DebugOnly<int> rc =
    906      ::sqlite3_db_status(mDBConn, aStatusOption, &curr, &max, 0);
    907  MOZ_ASSERT(NS_SUCCEEDED(convertResultCode(rc)));
    908  if (aMaxValue) *aMaxValue = max;
    909  return curr;
    910 }
    911 
    912 nsIEventTarget* Connection::getAsyncExecutionTarget() {
    913  NS_ENSURE_TRUE(IsOnCurrentSerialEventTarget(eventTargetOpenedOn), nullptr);
    914 
    915  // Don't return the asynchronous event target if we are shutting down.
    916  if (mAsyncExecutionThreadShuttingDown) {
    917    return nullptr;
    918  }
    919 
    920  // Create the async event target if there's none yet.
    921  if (!mAsyncExecutionThread) {
    922    // Names start with "sqldb:" followed by a recognizable name, like the
    923    // database file name, or a specially crafted name like "memory".
    924    // This name will be surfaced on https://crash-stats.mozilla.org, so any
    925    // sensitive part of the file name (e.g. an URL origin) should be replaced
    926    // by passing an explicit telemetryName to openDatabaseWithFileURL.
    927    nsAutoCString name("sqldb:"_ns);
    928    name.Append(mTelemetryFilename);
    929    static nsThreadPoolNaming naming;
    930    nsresult rv = NS_NewNamedThread(naming.GetNextThreadName(name),
    931                                    getter_AddRefs(mAsyncExecutionThread));
    932    if (NS_FAILED(rv)) {
    933      NS_WARNING("Failed to create async thread.");
    934      return nullptr;
    935    }
    936  }
    937 
    938  return mAsyncExecutionThread;
    939 }
    940 
    941 void Connection::RecordOpenStatus(nsresult rv) {
    942  nsCString histogramKey = mTelemetryFilename;
    943 
    944  if (histogramKey.IsEmpty()) {
    945    histogramKey.AssignLiteral("unknown");
    946  }
    947 
    948  if (NS_SUCCEEDED(rv)) {
    949    return;
    950  }
    951 
    952  switch (rv) {
    953    case NS_ERROR_FILE_CORRUPTED:
    954      mozilla::glean::sqlite_store::open.Get(histogramKey, "corrupt"_ns).Add();
    955      break;
    956    case NS_ERROR_STORAGE_IOERR:
    957      mozilla::glean::sqlite_store::open.Get(histogramKey, "diskio"_ns).Add();
    958      break;
    959    case NS_ERROR_FILE_ACCESS_DENIED:
    960    case NS_ERROR_FILE_IS_LOCKED:
    961    case NS_ERROR_FILE_READ_ONLY:
    962      mozilla::glean::sqlite_store::open.Get(histogramKey, "access"_ns).Add();
    963      break;
    964    case NS_ERROR_FILE_NO_DEVICE_SPACE:
    965      mozilla::glean::sqlite_store::open.Get(histogramKey, "diskspace"_ns)
    966          .Add();
    967      break;
    968    default:
    969      mozilla::glean::sqlite_store::open.Get(histogramKey, "failure"_ns).Add();
    970  }
    971 }
    972 
    973 void Connection::RecordQueryStatus(int srv) {
    974  nsCString histogramKey = mTelemetryFilename;
    975 
    976  if (histogramKey.IsEmpty()) {
    977    histogramKey.AssignLiteral("unknown");
    978  }
    979 
    980  switch (srv) {
    981    case SQLITE_OK:
    982    case SQLITE_ROW:
    983    case SQLITE_DONE:
    984 
    985    // Note that these are returned when we intentionally cancel a statement so
    986    // they aren't indicating a failure.
    987    case SQLITE_ABORT:
    988    case SQLITE_INTERRUPT:
    989      break;
    990    case SQLITE_CORRUPT:
    991    case SQLITE_NOTADB:
    992      mozilla::glean::sqlite_store::query.Get(histogramKey, "corrupt"_ns).Add();
    993      break;
    994    case SQLITE_PERM:
    995    case SQLITE_CANTOPEN:
    996    case SQLITE_LOCKED:
    997    case SQLITE_READONLY:
    998      mozilla::glean::sqlite_store::query.Get(histogramKey, "access"_ns).Add();
    999      break;
   1000    case SQLITE_IOERR:
   1001    case SQLITE_NOLFS:
   1002      mozilla::glean::sqlite_store::query.Get(histogramKey, "diskio"_ns).Add();
   1003      break;
   1004    case SQLITE_FULL:
   1005    case SQLITE_TOOBIG:
   1006      mozilla::glean::sqlite_store::query.Get(histogramKey, "diskspace"_ns)
   1007          .Add();
   1008      break;
   1009    case SQLITE_CONSTRAINT:
   1010    case SQLITE_RANGE:
   1011    case SQLITE_MISMATCH:
   1012    case SQLITE_MISUSE:
   1013      mozilla::glean::sqlite_store::query.Get(histogramKey, "misuse"_ns).Add();
   1014      break;
   1015    case SQLITE_BUSY:
   1016      mozilla::glean::sqlite_store::query.Get(histogramKey, "busy"_ns).Add();
   1017      break;
   1018    default:
   1019      mozilla::glean::sqlite_store::query.Get(histogramKey, "failure"_ns).Add();
   1020  }
   1021 }
   1022 
   1023 nsresult Connection::initialize(const nsACString& aStorageKey,
   1024                                const nsACString& aName) {
   1025  MOZ_ASSERT(aStorageKey.Equals(kMozStorageMemoryStorageKey));
   1026  NS_ASSERTION(!connectionReady(),
   1027               "Initialize called on already opened database!");
   1028  MOZ_ASSERT(!mIgnoreLockingMode, "Can't ignore locking on an in-memory db.");
   1029  AUTO_PROFILER_LABEL("Connection::initialize", OTHER);
   1030 
   1031  mStorageKey = aStorageKey;
   1032  mName = aName;
   1033 
   1034  // in memory database requested, sqlite uses a magic file name
   1035 
   1036  const nsAutoCString path =
   1037      mName.IsEmpty() ? nsAutoCString(":memory:"_ns)
   1038                      : "file:"_ns + mName + "?mode=memory&cache=shared"_ns;
   1039 
   1040  int srv = ::sqlite3_open_v2(path.get(), &mDBConn, mFlags,
   1041                              basevfs::GetVFSName(true));
   1042  if (srv != SQLITE_OK) {
   1043    ::sqlite3_close(mDBConn);
   1044    mDBConn = nullptr;
   1045    nsresult rv = convertResultCode(srv);
   1046    RecordOpenStatus(rv);
   1047    return rv;
   1048  }
   1049 
   1050 #ifdef MOZ_SQLITE_FTS3_TOKENIZER
   1051  srv =
   1052      ::sqlite3_db_config(mDBConn, SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER, 1, 0);
   1053  MOZ_ASSERT(srv == SQLITE_OK,
   1054             "SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER should be enabled");
   1055 #endif
   1056 
   1057  // Do not set mDatabaseFile or mFileURL here since this is a "memory"
   1058  // database.
   1059 
   1060  nsresult rv = initializeInternal();
   1061  RecordOpenStatus(rv);
   1062  NS_ENSURE_SUCCESS(rv, rv);
   1063 
   1064  return NS_OK;
   1065 }
   1066 
   1067 nsresult Connection::initialize(nsIFile* aDatabaseFile) {
   1068  NS_ASSERTION(aDatabaseFile, "Passed null file!");
   1069  NS_ASSERTION(!connectionReady(),
   1070               "Initialize called on already opened database!");
   1071  AUTO_PROFILER_LABEL("Connection::initialize", OTHER);
   1072 
   1073  // Do not set mFileURL here since this is database does not have an associated
   1074  // URL.
   1075  mDatabaseFile = aDatabaseFile;
   1076 
   1077  nsAutoString path;
   1078  nsresult rv = aDatabaseFile->GetPath(path);
   1079  NS_ENSURE_SUCCESS(rv, rv);
   1080 
   1081  bool exclusive =
   1082      StaticPrefs::storage_sqlite_exclusiveLock_enabled() && !mOpenNotExclusive;
   1083  int srv;
   1084  if (mIgnoreLockingMode) {
   1085    exclusive = false;
   1086    srv = ::sqlite3_open_v2(NS_ConvertUTF16toUTF8(path).get(), &mDBConn, mFlags,
   1087                            "readonly-immutable-nolock");
   1088  } else {
   1089    srv = ::sqlite3_open_v2(NS_ConvertUTF16toUTF8(path).get(), &mDBConn, mFlags,
   1090                            basevfs::GetVFSName(exclusive));
   1091    if (exclusive && (srv == SQLITE_LOCKED || srv == SQLITE_BUSY)) {
   1092      ::sqlite3_close(mDBConn);
   1093      // Retry without trying to get an exclusive lock.
   1094      exclusive = false;
   1095      srv = ::sqlite3_open_v2(NS_ConvertUTF16toUTF8(path).get(), &mDBConn,
   1096                              mFlags, basevfs::GetVFSName(false));
   1097    }
   1098  }
   1099  if (srv != SQLITE_OK) {
   1100    ::sqlite3_close(mDBConn);
   1101    mDBConn = nullptr;
   1102    rv = convertResultCode(srv);
   1103    RecordOpenStatus(rv);
   1104    return rv;
   1105  }
   1106 
   1107  rv = initializeInternal();
   1108  if (exclusive &&
   1109      (rv == NS_ERROR_STORAGE_BUSY || rv == NS_ERROR_FILE_IS_LOCKED)) {
   1110    // Usually SQLite will fail to acquire an exclusive lock on opening, but in
   1111    // some cases it may successfully open the database and then lock on the
   1112    // first query execution. When initializeInternal fails it closes the
   1113    // connection, so we can try to restart it in non-exclusive mode.
   1114    srv = ::sqlite3_open_v2(NS_ConvertUTF16toUTF8(path).get(), &mDBConn, mFlags,
   1115                            basevfs::GetVFSName(false));
   1116    if (srv == SQLITE_OK) {
   1117      rv = initializeInternal();
   1118    } else {
   1119      ::sqlite3_close(mDBConn);
   1120      mDBConn = nullptr;
   1121    }
   1122  }
   1123 
   1124  RecordOpenStatus(rv);
   1125  NS_ENSURE_SUCCESS(rv, rv);
   1126 
   1127  return NS_OK;
   1128 }
   1129 
   1130 nsresult Connection::initialize(nsIFileURL* aFileURL) {
   1131  NS_ASSERTION(aFileURL, "Passed null file URL!");
   1132  NS_ASSERTION(!connectionReady(),
   1133               "Initialize called on already opened database!");
   1134  AUTO_PROFILER_LABEL("Connection::initialize", OTHER);
   1135 
   1136  nsCOMPtr<nsIFile> databaseFile;
   1137  nsresult rv = aFileURL->GetFile(getter_AddRefs(databaseFile));
   1138  NS_ENSURE_SUCCESS(rv, rv);
   1139 
   1140  // Set both mDatabaseFile and mFileURL here.
   1141  mFileURL = aFileURL;
   1142  mDatabaseFile = databaseFile;
   1143 
   1144  nsAutoCString spec;
   1145  rv = aFileURL->GetSpec(spec);
   1146  NS_ENSURE_SUCCESS(rv, rv);
   1147 
   1148  // If there is a key specified, we need to use the obfuscating VFS.
   1149  nsAutoCString query;
   1150  rv = aFileURL->GetQuery(query);
   1151  NS_ENSURE_SUCCESS(rv, rv);
   1152 
   1153  bool hasKey = false;
   1154  bool hasDirectoryLockId = false;
   1155 
   1156  MOZ_ALWAYS_TRUE(
   1157      URLParams::Parse(query, true,
   1158                       [&hasKey, &hasDirectoryLockId](
   1159                           const nsACString& aName, const nsACString& aValue) {
   1160                         if (aName.EqualsLiteral("key")) {
   1161                           hasKey = true;
   1162                           return true;
   1163                         }
   1164                         if (aName.EqualsLiteral("directoryLockId")) {
   1165                           hasDirectoryLockId = true;
   1166                           return true;
   1167                         }
   1168                         return true;
   1169                       }));
   1170 
   1171  bool exclusive =
   1172      StaticPrefs::storage_sqlite_exclusiveLock_enabled() && !mOpenNotExclusive;
   1173 
   1174  const char* const vfs = hasKey               ? obfsvfs::GetVFSName()
   1175                          : hasDirectoryLockId ? quotavfs::GetVFSName()
   1176                                               : basevfs::GetVFSName(exclusive);
   1177 
   1178  int srv = ::sqlite3_open_v2(spec.get(), &mDBConn, mFlags, vfs);
   1179  if (srv != SQLITE_OK) {
   1180    ::sqlite3_close(mDBConn);
   1181    mDBConn = nullptr;
   1182    rv = convertResultCode(srv);
   1183    RecordOpenStatus(rv);
   1184    return rv;
   1185  }
   1186 
   1187  rv = initializeInternal();
   1188  RecordOpenStatus(rv);
   1189  NS_ENSURE_SUCCESS(rv, rv);
   1190 
   1191  return NS_OK;
   1192 }
   1193 
   1194 nsresult Connection::initializeInternal() {
   1195  MOZ_ASSERT(mDBConn);
   1196  auto guard = MakeScopeExit([&]() { initializeFailed(); });
   1197 
   1198  mConnectionClosed = false;
   1199 
   1200 #ifdef MOZ_SQLITE_FTS3_TOKENIZER
   1201  DebugOnly<int> srv2 =
   1202      ::sqlite3_db_config(mDBConn, SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER, 1, 0);
   1203  MOZ_ASSERT(srv2 == SQLITE_OK,
   1204             "SQLITE_DBCONFIG_ENABLE_FTS3_TOKENIZER should be enabled");
   1205 #endif
   1206 
   1207  // Properly wrap the database handle's mutex.
   1208  sharedDBMutex.initWithMutex(sqlite3_db_mutex(mDBConn));
   1209 
   1210  // SQLite tracing can slow down queries (especially long queries)
   1211  // significantly. Don't trace unless the user is actively monitoring SQLite.
   1212  if (MOZ_LOG_TEST(gStorageLog, LogLevel::Debug)) {
   1213    ::sqlite3_trace_v2(mDBConn, SQLITE_TRACE_STMT | SQLITE_TRACE_PROFILE,
   1214                       tracefunc, this);
   1215 
   1216    MOZ_LOG(
   1217        gStorageLog, LogLevel::Debug,
   1218        ("Opening connection to '%s' (%p)", mTelemetryFilename.get(), this));
   1219  }
   1220 
   1221  int64_t pageSize = Service::kDefaultPageSize;
   1222 
   1223  // Set page_size to the preferred default value.  This is effective only if
   1224  // the database has just been created, otherwise, if the database does not
   1225  // use WAL journal mode, a VACUUM operation will updated its page_size.
   1226  nsAutoCString pageSizeQuery(MOZ_STORAGE_UNIQUIFY_QUERY_STR
   1227                              "PRAGMA page_size = ");
   1228  pageSizeQuery.AppendInt(pageSize);
   1229  int srv = executeSql(mDBConn, pageSizeQuery.get());
   1230  if (srv != SQLITE_OK) {
   1231    return convertResultCode(srv);
   1232  }
   1233 
   1234  // Setting the cache_size forces the database open, verifying if it is valid
   1235  // or corrupt.  So this is executed regardless it being actually needed.
   1236  // The cache_size is calculated from the actual page_size, to save memory.
   1237  nsAutoCString cacheSizeQuery(MOZ_STORAGE_UNIQUIFY_QUERY_STR
   1238                               "PRAGMA cache_size = ");
   1239  cacheSizeQuery.AppendInt(-MAX_CACHE_SIZE_KIBIBYTES);
   1240  srv = executeSql(mDBConn, cacheSizeQuery.get());
   1241  if (srv != SQLITE_OK) {
   1242    return convertResultCode(srv);
   1243  }
   1244 
   1245  // Register our built-in SQL functions.
   1246  srv = registerFunctions(mDBConn);
   1247  if (srv != SQLITE_OK) {
   1248    return convertResultCode(srv);
   1249  }
   1250 
   1251  // Register our built-in SQL collating sequences.
   1252  srv = registerCollations(mDBConn, mStorageService);
   1253  if (srv != SQLITE_OK) {
   1254    return convertResultCode(srv);
   1255  }
   1256 
   1257  // Set the default synchronous value. Each consumer can switch this
   1258  // accordingly to their needs.
   1259 #if defined(ANDROID)
   1260  // Android prefers synchronous = OFF for performance reasons.
   1261  (void)ExecuteSimpleSQL("PRAGMA synchronous = OFF;"_ns);
   1262 #else
   1263  // Normal is the suggested value for WAL journals.
   1264  (void)ExecuteSimpleSQL("PRAGMA synchronous = NORMAL;"_ns);
   1265 #endif
   1266 
   1267  // Initialization succeeded, we can stop guarding for failures.
   1268  guard.release();
   1269  return NS_OK;
   1270 }
   1271 
   1272 nsresult Connection::initializeOnAsyncThread(nsIFile* aStorageFile) {
   1273  MOZ_ASSERT(!IsOnCurrentSerialEventTarget(eventTargetOpenedOn));
   1274  nsresult rv = aStorageFile
   1275                    ? initialize(aStorageFile)
   1276                    : initialize(kMozStorageMemoryStorageKey, VoidCString());
   1277  if (NS_FAILED(rv)) {
   1278    // Shutdown the async thread, since initialization failed.
   1279    MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
   1280    mAsyncExecutionThreadShuttingDown = true;
   1281    nsCOMPtr<nsIRunnable> event =
   1282        NewRunnableMethod("Connection::shutdownAsyncThread", this,
   1283                          &Connection::shutdownAsyncThread);
   1284    (void)NS_DispatchToMainThread(event);
   1285  }
   1286  return rv;
   1287 }
   1288 
   1289 void Connection::initializeFailed() {
   1290  {
   1291    MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
   1292    mConnectionClosed = true;
   1293  }
   1294  MOZ_ALWAYS_TRUE(::sqlite3_close(mDBConn) == SQLITE_OK);
   1295  mDBConn = nullptr;
   1296  sharedDBMutex.destroy();
   1297 }
   1298 
   1299 nsresult Connection::databaseElementExists(
   1300    enum DatabaseElementType aElementType, const nsACString& aElementName,
   1301    bool* _exists) {
   1302  if (!connectionReady()) {
   1303    return NS_ERROR_NOT_AVAILABLE;
   1304  }
   1305  nsresult rv = ensureOperationSupported(SYNCHRONOUS);
   1306  if (NS_FAILED(rv)) {
   1307    return rv;
   1308  }
   1309 
   1310  // When constructing the query, make sure to SELECT the correct db's
   1311  // sqlite_master if the user is prefixing the element with a specific db. ex:
   1312  // sample.test
   1313  nsCString query("SELECT name FROM (SELECT * FROM ");
   1314  nsDependentCSubstring element;
   1315  int32_t ind = aElementName.FindChar('.');
   1316  if (ind == kNotFound) {
   1317    element.Assign(aElementName);
   1318  } else {
   1319    nsDependentCSubstring db(Substring(aElementName, 0, ind + 1));
   1320    element.Assign(Substring(aElementName, ind + 1, aElementName.Length()));
   1321    query.Append(db);
   1322  }
   1323  query.AppendLiteral(
   1324      "sqlite_master UNION ALL SELECT * FROM sqlite_temp_master) WHERE type = "
   1325      "'");
   1326 
   1327  switch (aElementType) {
   1328    case INDEX:
   1329      query.AppendLiteral("index");
   1330      break;
   1331    case TABLE:
   1332      query.AppendLiteral("table");
   1333      break;
   1334  }
   1335  query.AppendLiteral("' AND name ='");
   1336  query.Append(element);
   1337  query.Append('\'');
   1338 
   1339  sqlite3_stmt* stmt;
   1340  int srv = prepareStatement(mDBConn, query, &stmt);
   1341  if (srv != SQLITE_OK) {
   1342    RecordQueryStatus(srv);
   1343    return convertResultCode(srv);
   1344  }
   1345 
   1346  srv = stepStatement(mDBConn, stmt);
   1347  // we just care about the return value from step
   1348  (void)::sqlite3_finalize(stmt);
   1349 
   1350  RecordQueryStatus(srv);
   1351 
   1352  if (srv == SQLITE_ROW) {
   1353    *_exists = true;
   1354    return NS_OK;
   1355  }
   1356  if (srv == SQLITE_DONE) {
   1357    *_exists = false;
   1358    return NS_OK;
   1359  }
   1360 
   1361  return convertResultCode(srv);
   1362 }
   1363 
   1364 bool Connection::findFunctionByInstance(mozIStorageFunction* aInstance) {
   1365  sharedDBMutex.assertCurrentThreadOwns();
   1366 
   1367  for (const auto& data : mFunctions.Values()) {
   1368    if (data.function == aInstance) {
   1369      return true;
   1370    }
   1371  }
   1372  return false;
   1373 }
   1374 
   1375 /* static */
   1376 int Connection::sProgressHelper(void* aArg) {
   1377  Connection* _this = static_cast<Connection*>(aArg);
   1378  return _this->progressHandler();
   1379 }
   1380 
   1381 int Connection::progressHandler() {
   1382  sharedDBMutex.assertCurrentThreadOwns();
   1383  if (mProgressHandler) {
   1384    bool result;
   1385    nsresult rv = mProgressHandler->OnProgress(this, &result);
   1386    if (NS_FAILED(rv)) return 0;  // Don't break request
   1387    return result ? 1 : 0;
   1388  }
   1389  return 0;
   1390 }
   1391 
   1392 nsresult Connection::setClosedState() {
   1393  // Flag that we are shutting down the async thread, so that
   1394  // getAsyncExecutionTarget knows not to expose/create the async thread.
   1395  MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
   1396  NS_ENSURE_FALSE(mAsyncExecutionThreadShuttingDown, NS_ERROR_UNEXPECTED);
   1397 
   1398  mAsyncExecutionThreadShuttingDown = true;
   1399 
   1400  // Set the property to null before closing the connection, otherwise the
   1401  // other functions in the module may try to use the connection after it is
   1402  // closed.
   1403  mDBConn = nullptr;
   1404 
   1405  return NS_OK;
   1406 }
   1407 
   1408 bool Connection::operationSupported(ConnectionOperation aOperationType) {
   1409  if (aOperationType == ASYNCHRONOUS) {
   1410    // Async operations are supported for all connections, on any thread.
   1411    return true;
   1412  }
   1413  // Sync operations are supported for sync connections (on any thread), and
   1414  // async connections on a background thread.
   1415  MOZ_ASSERT(aOperationType == SYNCHRONOUS);
   1416  return mSupportedOperations == SYNCHRONOUS || !NS_IsMainThread();
   1417 }
   1418 
   1419 nsresult Connection::ensureOperationSupported(
   1420    ConnectionOperation aOperationType) {
   1421  if (NS_WARN_IF(!operationSupported(aOperationType))) {
   1422 #ifdef DEBUG
   1423    if (NS_IsMainThread()) {
   1424      nsCOMPtr<nsIXPConnect> xpc = nsIXPConnect::XPConnect();
   1425      (void)xpc->DebugDumpJSStack(false, false, false);
   1426    }
   1427 #endif
   1428    MOZ_ASSERT(false,
   1429               "Don't use async connections synchronously on the main thread");
   1430    return NS_ERROR_NOT_AVAILABLE;
   1431  }
   1432  return NS_OK;
   1433 }
   1434 
   1435 bool Connection::isConnectionReadyOnThisThread() {
   1436  MOZ_ASSERT_IF(connectionReady(), !mConnectionClosed);
   1437  if (mAsyncExecutionThread && mAsyncExecutionThread->IsOnCurrentThread()) {
   1438    return true;
   1439  }
   1440  return connectionReady();
   1441 }
   1442 
   1443 bool Connection::isClosing() {
   1444  MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
   1445  return mAsyncExecutionThreadShuttingDown && !mConnectionClosed;
   1446 }
   1447 
   1448 bool Connection::isClosed() {
   1449  MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
   1450  return mConnectionClosed;
   1451 }
   1452 
   1453 bool Connection::isClosed(MutexAutoLock& lock) { return mConnectionClosed; }
   1454 
   1455 bool Connection::isAsyncExecutionThreadAvailable() {
   1456  MOZ_ASSERT(IsOnCurrentSerialEventTarget(eventTargetOpenedOn));
   1457  return mAsyncExecutionThread && !mAsyncExecutionThreadShuttingDown;
   1458 }
   1459 
   1460 void Connection::shutdownAsyncThread() {
   1461  MOZ_ASSERT(IsOnCurrentSerialEventTarget(eventTargetOpenedOn));
   1462  MOZ_ASSERT(mAsyncExecutionThread);
   1463  MOZ_ASSERT(mAsyncExecutionThreadShuttingDown);
   1464 
   1465  MOZ_ALWAYS_SUCCEEDS(mAsyncExecutionThread->Shutdown());
   1466  mAsyncExecutionThread = nullptr;
   1467 }
   1468 
   1469 nsresult Connection::internalClose(sqlite3* aNativeConnection) {
   1470 #ifdef DEBUG
   1471  {  // Make sure we have marked our async thread as shutting down.
   1472    MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
   1473    MOZ_ASSERT(mAsyncExecutionThreadShuttingDown,
   1474               "Did not call setClosedState!");
   1475    MOZ_ASSERT(!isClosed(lockedScope), "Unexpected closed state");
   1476  }
   1477 #endif  // DEBUG
   1478 
   1479  if (MOZ_LOG_TEST(gStorageLog, LogLevel::Debug)) {
   1480    nsAutoCString leafName(":memory");
   1481    if (mDatabaseFile) (void)mDatabaseFile->GetNativeLeafName(leafName);
   1482    MOZ_LOG(gStorageLog, LogLevel::Debug,
   1483            ("Closing connection to '%s'", leafName.get()));
   1484  }
   1485 
   1486  // At this stage, we may still have statements that need to be
   1487  // finalized. Attempt to close the database connection. This will
   1488  // always disconnect any virtual tables and cleanly finalize their
   1489  // internal statements. Once this is done, closing may fail due to
   1490  // unfinalized client statements, in which case we need to finalize
   1491  // these statements and close again.
   1492  {
   1493    MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
   1494    mConnectionClosed = true;
   1495  }
   1496 
   1497  // Nothing else needs to be done if we don't have a connection here.
   1498  if (!aNativeConnection) return NS_OK;
   1499 
   1500  int srv = ::sqlite3_close(aNativeConnection);
   1501 
   1502  if (srv == SQLITE_BUSY) {
   1503    {
   1504      // Nothing else should change the connection or statements status until we
   1505      // are done here.
   1506      SQLiteMutexAutoLock lockedScope(sharedDBMutex);
   1507      // We still have non-finalized statements. Finalize them.
   1508      sqlite3_stmt* stmt = nullptr;
   1509      while ((stmt = ::sqlite3_next_stmt(aNativeConnection, stmt))) {
   1510        MOZ_LOG(gStorageLog, LogLevel::Debug,
   1511                ("Auto-finalizing SQL statement '%s' (%p)", ::sqlite3_sql(stmt),
   1512                 stmt));
   1513 
   1514 #ifdef DEBUG
   1515        SmprintfPointer msg = ::mozilla::Smprintf(
   1516            "SQL statement '%s' (%p) should have been finalized before closing "
   1517            "the connection",
   1518            ::sqlite3_sql(stmt), stmt);
   1519        NS_WARNING(msg.get());
   1520 #endif  // DEBUG
   1521 
   1522        srv = ::sqlite3_finalize(stmt);
   1523 
   1524 #ifdef DEBUG
   1525        if (srv != SQLITE_OK) {
   1526          SmprintfPointer msg = ::mozilla::Smprintf(
   1527              "Could not finalize SQL statement (%p)", stmt);
   1528          NS_WARNING(msg.get());
   1529        }
   1530 #endif  // DEBUG
   1531 
   1532        // Ensure that the loop continues properly, whether closing has
   1533        // succeeded or not.
   1534        if (srv == SQLITE_OK) {
   1535          stmt = nullptr;
   1536        }
   1537      }
   1538      // Scope exiting will unlock the mutex before we invoke sqlite3_close()
   1539      // again, since Sqlite will try to acquire it.
   1540    }
   1541 
   1542    // Now that all statements have been finalized, we
   1543    // should be able to close.
   1544    srv = ::sqlite3_close(aNativeConnection);
   1545    MOZ_ASSERT(false,
   1546               "Had to forcibly close the database connection because not all "
   1547               "the statements have been finalized.");
   1548  }
   1549 
   1550  if (srv == SQLITE_OK) {
   1551    sharedDBMutex.destroy();
   1552  } else {
   1553    MOZ_ASSERT(false,
   1554               "sqlite3_close failed. There are probably outstanding "
   1555               "statements that are listed above!");
   1556  }
   1557 
   1558  return convertResultCode(srv);
   1559 }
   1560 
   1561 nsCString Connection::getFilename() { return mTelemetryFilename; }
   1562 
   1563 int Connection::stepStatement(sqlite3* aNativeConnection,
   1564                              sqlite3_stmt* aStatement) {
   1565  MOZ_ASSERT(aStatement);
   1566 
   1567  AUTO_PROFILER_LABEL_DYNAMIC_CSTR("Connection::stepStatement", OTHER,
   1568                                   ::sqlite3_sql(aStatement));
   1569 
   1570  bool checkedMainThread = false;
   1571  TimeStamp startTime = TimeStamp::Now();
   1572 
   1573  // The connection may have been closed if the executing statement has been
   1574  // created and cached after a call to asyncClose() but before the actual
   1575  // sqlite3_close().  This usually happens when other tasks using cached
   1576  // statements are asynchronously scheduled for execution and any of them ends
   1577  // up after asyncClose. See bug 728653 for details.
   1578  if (!isConnectionReadyOnThisThread()) return SQLITE_MISUSE;
   1579 
   1580  (void)::sqlite3_extended_result_codes(aNativeConnection, 1);
   1581 
   1582  int srv;
   1583  while ((srv = ::sqlite3_step(aStatement)) == SQLITE_LOCKED_SHAREDCACHE) {
   1584    if (!checkedMainThread) {
   1585      checkedMainThread = true;
   1586      if (::NS_IsMainThread()) {
   1587        NS_WARNING("We won't allow blocking on the main thread!");
   1588        break;
   1589      }
   1590    }
   1591 
   1592    srv = WaitForUnlockNotify(aNativeConnection);
   1593    if (srv != SQLITE_OK) {
   1594      break;
   1595    }
   1596 
   1597    ::sqlite3_reset(aStatement);
   1598  }
   1599 
   1600  // Report very slow SQL statements to Telemetry
   1601  TimeDuration duration = TimeStamp::Now() - startTime;
   1602  const uint32_t threshold = NS_IsMainThread()
   1603                                 ? Telemetry::kSlowSQLThresholdForMainThread
   1604                                 : Telemetry::kSlowSQLThresholdForHelperThreads;
   1605  if (duration.ToMilliseconds() >= threshold) {
   1606    nsDependentCString statementString(::sqlite3_sql(aStatement));
   1607    Telemetry::RecordSlowSQLStatement(
   1608        statementString, mTelemetryFilename,
   1609        static_cast<uint32_t>(duration.ToMilliseconds()));
   1610  }
   1611 
   1612  (void)::sqlite3_extended_result_codes(aNativeConnection, 0);
   1613  // Drop off the extended result bits of the result code.
   1614  return srv & 0xFF;
   1615 }
   1616 
   1617 int Connection::prepareStatement(sqlite3* aNativeConnection,
   1618                                 const nsCString& aSQL, sqlite3_stmt** _stmt) {
   1619  // We should not even try to prepare statements after the connection has
   1620  // been closed.
   1621  if (!isConnectionReadyOnThisThread()) return SQLITE_MISUSE;
   1622 
   1623  bool checkedMainThread = false;
   1624 
   1625  (void)::sqlite3_extended_result_codes(aNativeConnection, 1);
   1626 
   1627  int srv;
   1628  while ((srv = ::sqlite3_prepare_v2(aNativeConnection, aSQL.get(), -1, _stmt,
   1629                                     nullptr)) == SQLITE_LOCKED_SHAREDCACHE) {
   1630    if (!checkedMainThread) {
   1631      checkedMainThread = true;
   1632      if (::NS_IsMainThread()) {
   1633        NS_WARNING("We won't allow blocking on the main thread!");
   1634        break;
   1635      }
   1636    }
   1637 
   1638    srv = WaitForUnlockNotify(aNativeConnection);
   1639    if (srv != SQLITE_OK) {
   1640      break;
   1641    }
   1642  }
   1643 
   1644  if (srv != SQLITE_OK) {
   1645    nsCString warnMsg;
   1646    warnMsg.AppendLiteral("The SQL statement '");
   1647    warnMsg.Append(aSQL);
   1648    warnMsg.AppendLiteral("' could not be compiled due to an error: ");
   1649    warnMsg.Append(::sqlite3_errmsg(aNativeConnection));
   1650 
   1651 #ifdef DEBUG
   1652    NS_WARNING(warnMsg.get());
   1653 #endif
   1654    MOZ_LOG(gStorageLog, LogLevel::Error, ("%s", warnMsg.get()));
   1655  }
   1656 
   1657  (void)::sqlite3_extended_result_codes(aNativeConnection, 0);
   1658  // Drop off the extended result bits of the result code.
   1659  int rc = srv & 0xFF;
   1660  // sqlite will return OK on a comment only string and set _stmt to nullptr.
   1661  // The callers of this function are used to only checking the return value,
   1662  // so it is safer to return an error code.
   1663  if (rc == SQLITE_OK && *_stmt == nullptr) {
   1664    return SQLITE_MISUSE;
   1665  }
   1666 
   1667  return rc;
   1668 }
   1669 
   1670 int Connection::executeSql(sqlite3* aNativeConnection, const char* aSqlString) {
   1671  if (!isConnectionReadyOnThisThread()) return SQLITE_MISUSE;
   1672 
   1673  AUTO_PROFILER_LABEL_DYNAMIC_CSTR("Connection::executeSql", OTHER, aSqlString);
   1674 
   1675  TimeStamp startTime = TimeStamp::Now();
   1676  int srv =
   1677      ::sqlite3_exec(aNativeConnection, aSqlString, nullptr, nullptr, nullptr);
   1678  RecordQueryStatus(srv);
   1679 
   1680  // Report very slow SQL statements to Telemetry
   1681  TimeDuration duration = TimeStamp::Now() - startTime;
   1682  const uint32_t threshold = NS_IsMainThread()
   1683                                 ? Telemetry::kSlowSQLThresholdForMainThread
   1684                                 : Telemetry::kSlowSQLThresholdForHelperThreads;
   1685  if (duration.ToMilliseconds() >= threshold) {
   1686    nsDependentCString statementString(aSqlString);
   1687    Telemetry::RecordSlowSQLStatement(
   1688        statementString, mTelemetryFilename,
   1689        static_cast<uint32_t>(duration.ToMilliseconds()));
   1690  }
   1691 
   1692  return srv;
   1693 }
   1694 
   1695 ////////////////////////////////////////////////////////////////////////////////
   1696 //// nsIInterfaceRequestor
   1697 
   1698 NS_IMETHODIMP
   1699 Connection::GetInterface(const nsIID& aIID, void** _result) {
   1700  if (aIID.Equals(NS_GET_IID(nsIEventTarget))) {
   1701    nsIEventTarget* background = getAsyncExecutionTarget();
   1702    NS_IF_ADDREF(background);
   1703    *_result = background;
   1704    return NS_OK;
   1705  }
   1706  return NS_ERROR_NO_INTERFACE;
   1707 }
   1708 
   1709 ////////////////////////////////////////////////////////////////////////////////
   1710 //// mozIStorageConnection
   1711 
   1712 NS_IMETHODIMP
   1713 Connection::Close() {
   1714  nsresult rv = ensureOperationSupported(SYNCHRONOUS);
   1715  if (NS_FAILED(rv)) {
   1716    return rv;
   1717  }
   1718  return synchronousClose();
   1719 }
   1720 
   1721 nsresult Connection::synchronousClose() {
   1722  if (!connectionReady()) {
   1723    return NS_ERROR_NOT_INITIALIZED;
   1724  }
   1725 
   1726 #ifdef DEBUG
   1727  // Since we're accessing mAsyncExecutionThread, we need to be on the opener
   1728  // event target. We make this check outside of debug code below in
   1729  // setClosedState, but this is here to be explicit.
   1730  MOZ_ASSERT(IsOnCurrentSerialEventTarget(eventTargetOpenedOn));
   1731 #endif  // DEBUG
   1732 
   1733  // Make sure we have not executed any asynchronous statements.
   1734  // If this fails, the mDBConn may be left open, resulting in a leak.
   1735  // We'll try to finalize the pending statements and close the connection.
   1736  if (isAsyncExecutionThreadAvailable()) {
   1737 #ifdef DEBUG
   1738    if (NS_IsMainThread()) {
   1739      nsCOMPtr<nsIXPConnect> xpc = nsIXPConnect::XPConnect();
   1740      (void)xpc->DebugDumpJSStack(false, false, false);
   1741    }
   1742 #endif
   1743    MOZ_ASSERT(false,
   1744               "Close() was invoked on a connection that executed asynchronous "
   1745               "statements. "
   1746               "Should have used asyncClose().");
   1747    // Try to close the database regardless, to free up resources.
   1748    (void)SpinningSynchronousClose();
   1749    return NS_ERROR_UNEXPECTED;
   1750  }
   1751 
   1752  // setClosedState nullifies our connection pointer, so we take a raw pointer
   1753  // off it, to pass it through the close procedure.
   1754  sqlite3* nativeConn = mDBConn;
   1755  nsresult rv = setClosedState();
   1756  NS_ENSURE_SUCCESS(rv, rv);
   1757 
   1758  return internalClose(nativeConn);
   1759 }
   1760 
   1761 NS_IMETHODIMP
   1762 Connection::SpinningSynchronousClose() {
   1763  nsresult rv = ensureOperationSupported(SYNCHRONOUS);
   1764  if (NS_FAILED(rv)) {
   1765    return rv;
   1766  }
   1767  if (!IsOnCurrentSerialEventTarget(eventTargetOpenedOn)) {
   1768    return NS_ERROR_NOT_SAME_THREAD;
   1769  }
   1770 
   1771  // As currently implemented, we can't spin to wait for an existing AsyncClose.
   1772  // Our only existing caller will never have called close; assert if misused
   1773  // so that no new callers assume this works after an AsyncClose.
   1774  MOZ_DIAGNOSTIC_ASSERT(connectionReady());
   1775  if (!connectionReady()) {
   1776    return NS_ERROR_UNEXPECTED;
   1777  }
   1778 
   1779  RefPtr<CloseListener> listener = new CloseListener();
   1780  rv = AsyncClose(listener);
   1781  NS_ENSURE_SUCCESS(rv, rv);
   1782  MOZ_ALWAYS_TRUE(
   1783      SpinEventLoopUntil("storage::Connection::SpinningSynchronousClose"_ns,
   1784                         [&]() { return listener->mClosed; }));
   1785  MOZ_ASSERT(isClosed(), "The connection should be closed at this point");
   1786 
   1787  return rv;
   1788 }
   1789 
   1790 NS_IMETHODIMP
   1791 Connection::AsyncClose(mozIStorageCompletionCallback* aCallback) {
   1792  NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_NOT_SAME_THREAD);
   1793  // Check if AsyncClose or Close were already invoked.
   1794  if (!connectionReady()) {
   1795    return NS_ERROR_NOT_INITIALIZED;
   1796  }
   1797  nsresult rv = ensureOperationSupported(ASYNCHRONOUS);
   1798  if (NS_FAILED(rv)) {
   1799    return rv;
   1800  }
   1801 
   1802  // The two relevant factors at this point are whether we have a database
   1803  // connection and whether we have an async execution thread.  Here's what the
   1804  // states mean and how we handle them:
   1805  //
   1806  // - (mDBConn && asyncThread): The expected case where we are either an
   1807  //   async connection or a sync connection that has been used asynchronously.
   1808  //   Either way the caller must call us and not Close().  Nothing surprising
   1809  //   about this.  We'll dispatch AsyncCloseConnection to the already-existing
   1810  //   async thread.
   1811  //
   1812  // - (mDBConn && !asyncThread): A somewhat unusual case where the caller
   1813  //   opened the connection synchronously and was planning to use it
   1814  //   asynchronously, but never got around to using it asynchronously before
   1815  //   needing to shutdown.  This has been observed to happen for the cookie
   1816  //   service in a case where Firefox shuts itself down almost immediately
   1817  //   after startup (for unknown reasons).  In the Firefox shutdown case,
   1818  //   we may also fail to create a new async execution thread if one does not
   1819  //   already exist.  (nsThreadManager will refuse to create new threads when
   1820  //   it has already been told to shutdown.)  As such, we need to handle a
   1821  //   failure to create the async execution thread by falling back to
   1822  //   synchronous Close() and also dispatching the completion callback because
   1823  //   at least Places likes to spin a nested event loop that depends on the
   1824  //   callback being invoked.
   1825  //
   1826  //   Note that we have considered not trying to spin up the async execution
   1827  //   thread in this case if it does not already exist, but the overhead of
   1828  //   thread startup (if successful) is significantly less expensive than the
   1829  //   worst-case potential I/O hit of synchronously closing a database when we
   1830  //   could close it asynchronously.
   1831  //
   1832  // - (!mDBConn && asyncThread): This happens in some but not all cases where
   1833  //   OpenAsyncDatabase encountered a problem opening the database.  If it
   1834  //   happened in all cases AsyncInitDatabase would just shut down the thread
   1835  //   directly and we would avoid this case.  But it doesn't, so for simplicity
   1836  //   and consistency AsyncCloseConnection knows how to handle this and we
   1837  //   act like this was the (mDBConn && asyncThread) case in this method.
   1838  //
   1839  // - (!mDBConn && !asyncThread): The database was never successfully opened or
   1840  //   Close() or AsyncClose() has already been called (at least) once.  This is
   1841  //   undeniably a misuse case by the caller.  We could optimize for this
   1842  //   case by adding an additional check of mAsyncExecutionThread without using
   1843  //   getAsyncExecutionTarget() to avoid wastefully creating a thread just to
   1844  //   shut it down.  But this complicates the method for broken caller code
   1845  //   whereas we're still correct and safe without the special-case.
   1846  nsIEventTarget* asyncThread = getAsyncExecutionTarget();
   1847 
   1848  // Create our callback event if we were given a callback.  This will
   1849  // eventually be dispatched in all cases, even if we fall back to Close() and
   1850  // the database wasn't open and we return an error.  The rationale is that
   1851  // no existing consumer checks our return value and several of them like to
   1852  // spin nested event loops until the callback fires.  Given that, it seems
   1853  // preferable for us to dispatch the callback in all cases.  (Except the
   1854  // wrong thread misuse case we bailed on up above.  But that's okay because
   1855  // that is statically wrong whereas these edge cases are dynamic.)
   1856  nsCOMPtr<nsIRunnable> completeEvent;
   1857  if (aCallback) {
   1858    completeEvent = newCompletionEvent(aCallback);
   1859  }
   1860 
   1861  if (!asyncThread) {
   1862    // We were unable to create an async thread, so we need to fall back to
   1863    // using normal Close().  Since there is no async thread, Close() will
   1864    // not complain about that.  (Close() may, however, complain if the
   1865    // connection is closed, but that's okay.)
   1866    if (completeEvent) {
   1867      // Closing the database is more important than returning an error code
   1868      // about a failure to dispatch, especially because all existing native
   1869      // callers ignore our return value.
   1870      (void)NS_DispatchToMainThread(completeEvent.forget());
   1871    }
   1872    MOZ_ALWAYS_SUCCEEDS(synchronousClose());
   1873    // Return a success inconditionally here, since Close() is unlikely to fail
   1874    // and we want to reassure the consumer that its callback will be invoked.
   1875    return NS_OK;
   1876  }
   1877 
   1878  // If we're closing the connection during shutdown, and there is an
   1879  // interruptible statement running on the helper thread, issue a
   1880  // sqlite3_interrupt() to avoid crashing when that statement takes a long
   1881  // time (for example a vacuum).
   1882  if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed) &&
   1883      mInterruptible && mIsStatementOnHelperThreadInterruptible) {
   1884    MOZ_ASSERT(!isClosing(), "Must not be closing, see Interrupt()");
   1885    DebugOnly<nsresult> rv2 = Interrupt();
   1886    MOZ_ASSERT(NS_SUCCEEDED(rv2));
   1887  }
   1888 
   1889  // setClosedState nullifies our connection pointer, so we take a raw pointer
   1890  // off it, to pass it through the close procedure.
   1891  sqlite3* nativeConn = mDBConn;
   1892  rv = setClosedState();
   1893  NS_ENSURE_SUCCESS(rv, rv);
   1894 
   1895  // Create and dispatch our close event to the background thread.
   1896  nsCOMPtr<nsIRunnable> closeEvent =
   1897      new AsyncCloseConnection(this, nativeConn, completeEvent);
   1898  rv = asyncThread->Dispatch(closeEvent, NS_DISPATCH_NORMAL);
   1899  NS_ENSURE_SUCCESS(rv, rv);
   1900 
   1901  return NS_OK;
   1902 }
   1903 
   1904 NS_IMETHODIMP
   1905 Connection::AsyncClone(bool aReadOnly,
   1906                       mozIStorageCompletionCallback* aCallback) {
   1907  AUTO_PROFILER_LABEL("Connection::AsyncClone", OTHER);
   1908 
   1909  NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_NOT_SAME_THREAD);
   1910  if (!connectionReady()) {
   1911    return NS_ERROR_NOT_INITIALIZED;
   1912  }
   1913  nsresult rv = ensureOperationSupported(ASYNCHRONOUS);
   1914  if (NS_FAILED(rv)) {
   1915    return rv;
   1916  }
   1917  if (!mDatabaseFile) return NS_ERROR_UNEXPECTED;
   1918 
   1919  int flags = mFlags;
   1920  if (aReadOnly) {
   1921    // Turn off SQLITE_OPEN_READWRITE, and set SQLITE_OPEN_READONLY.
   1922    flags = (~SQLITE_OPEN_READWRITE & flags) | SQLITE_OPEN_READONLY;
   1923    // Turn off SQLITE_OPEN_CREATE.
   1924    flags = (~SQLITE_OPEN_CREATE & flags);
   1925  }
   1926 
   1927  // The cloned connection will still implement the synchronous API, but throw
   1928  // if any synchronous methods are called on the main thread.
   1929  RefPtr<Connection> clone =
   1930      new Connection(mStorageService, flags, ASYNCHRONOUS, mTelemetryFilename,
   1931                     mInterruptible, mIgnoreLockingMode, mOpenNotExclusive);
   1932 
   1933  RefPtr<AsyncInitializeClone> initEvent =
   1934      new AsyncInitializeClone(this, clone, aReadOnly, aCallback);
   1935  // Dispatch to our async thread, since the originating connection must remain
   1936  // valid and open for the whole cloning process.  This also ensures we are
   1937  // properly serialized with a `close` operation, rather than race with it.
   1938  nsCOMPtr<nsIEventTarget> target = getAsyncExecutionTarget();
   1939  if (!target) {
   1940    return NS_ERROR_UNEXPECTED;
   1941  }
   1942  return target->Dispatch(initEvent, NS_DISPATCH_NORMAL);
   1943 }
   1944 
   1945 nsresult Connection::initializeClone(Connection* aClone, bool aReadOnly) {
   1946  nsresult rv;
   1947  if (!mStorageKey.IsEmpty()) {
   1948    rv = aClone->initialize(mStorageKey, mName);
   1949  } else if (mFileURL) {
   1950    rv = aClone->initialize(mFileURL);
   1951  } else {
   1952    rv = aClone->initialize(mDatabaseFile);
   1953  }
   1954  if (NS_FAILED(rv)) {
   1955    return rv;
   1956  }
   1957 
   1958  auto guard = MakeScopeExit([&]() { aClone->initializeFailed(); });
   1959 
   1960  rv = aClone->SetDefaultTransactionType(mDefaultTransactionType);
   1961  NS_ENSURE_SUCCESS(rv, rv);
   1962 
   1963  // Re-attach on-disk databases that were attached to the original connection.
   1964  {
   1965    nsCOMPtr<mozIStorageStatement> stmt;
   1966    rv = CreateStatement("PRAGMA database_list"_ns, getter_AddRefs(stmt));
   1967    NS_ENSURE_SUCCESS(rv, rv);
   1968    bool hasResult = false;
   1969    while (stmt && NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
   1970      nsAutoCString name;
   1971      rv = stmt->GetUTF8String(1, name);
   1972      if (NS_SUCCEEDED(rv) && !name.EqualsLiteral("main") &&
   1973          !name.EqualsLiteral("temp")) {
   1974        nsCString path;
   1975        rv = stmt->GetUTF8String(2, path);
   1976        if (NS_SUCCEEDED(rv) && !path.IsEmpty()) {
   1977          nsCOMPtr<mozIStorageStatement> attachStmt;
   1978          rv = aClone->CreateStatement("ATTACH DATABASE :path AS "_ns + name,
   1979                                       getter_AddRefs(attachStmt));
   1980          NS_ENSURE_SUCCESS(rv, rv);
   1981          rv = attachStmt->BindUTF8StringByName("path"_ns, path);
   1982          NS_ENSURE_SUCCESS(rv, rv);
   1983          rv = attachStmt->Execute();
   1984          NS_ENSURE_SUCCESS(rv, rv);
   1985        }
   1986      }
   1987    }
   1988  }
   1989 
   1990  // Copy over pragmas from the original connection.
   1991  // LIMITATION WARNING!  Many of these pragmas are actually scoped to the
   1992  // schema ("main" and any other attached databases), and this implmentation
   1993  // fails to propagate them.  This is being addressed on trunk.
   1994  static const char* pragmas[] = {
   1995      "cache_size",  "temp_store",         "foreign_keys", "journal_size_limit",
   1996      "synchronous", "wal_autocheckpoint", "busy_timeout"};
   1997  for (auto& pragma : pragmas) {
   1998    // Read-only connections just need cache_size and temp_store pragmas.
   1999    if (aReadOnly && ::strcmp(pragma, "cache_size") != 0 &&
   2000        ::strcmp(pragma, "temp_store") != 0) {
   2001      continue;
   2002    }
   2003 
   2004    nsAutoCString pragmaQuery("PRAGMA ");
   2005    pragmaQuery.Append(pragma);
   2006    nsCOMPtr<mozIStorageStatement> stmt;
   2007    rv = CreateStatement(pragmaQuery, getter_AddRefs(stmt));
   2008    NS_ENSURE_SUCCESS(rv, rv);
   2009    bool hasResult = false;
   2010    if (stmt && NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
   2011      pragmaQuery.AppendLiteral(" = ");
   2012      pragmaQuery.AppendInt(stmt->AsInt32(0));
   2013      rv = aClone->ExecuteSimpleSQL(pragmaQuery);
   2014      NS_ENSURE_SUCCESS(rv, rv);
   2015    }
   2016  }
   2017 
   2018  // Copy over temporary tables, triggers, and views from the original
   2019  // connections. Entities in `sqlite_temp_master` are only visible to the
   2020  // connection that created them.
   2021  if (!aReadOnly) {
   2022    rv = aClone->ExecuteSimpleSQL("BEGIN TRANSACTION"_ns);
   2023    NS_ENSURE_SUCCESS(rv, rv);
   2024 
   2025    nsCOMPtr<mozIStorageStatement> stmt;
   2026    rv = CreateStatement(nsLiteralCString("SELECT sql FROM sqlite_temp_master "
   2027                                          "WHERE type IN ('table', 'view', "
   2028                                          "'index', 'trigger')"),
   2029                         getter_AddRefs(stmt));
   2030    // Propagate errors, because failing to copy triggers might cause schema
   2031    // coherency issues when writing to the database from the cloned connection.
   2032    NS_ENSURE_SUCCESS(rv, rv);
   2033    bool hasResult = false;
   2034    while (stmt && NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
   2035      nsAutoCString query;
   2036      rv = stmt->GetUTF8String(0, query);
   2037      NS_ENSURE_SUCCESS(rv, rv);
   2038 
   2039      // The `CREATE` SQL statements in `sqlite_temp_master` omit the `TEMP`
   2040      // keyword. We need to add it back, or we'll recreate temporary entities
   2041      // as persistent ones. `sqlite_temp_master` also holds `CREATE INDEX`
   2042      // statements, but those don't need `TEMP` keywords.
   2043      if (StringBeginsWith(query, "CREATE TABLE "_ns) ||
   2044          StringBeginsWith(query, "CREATE TRIGGER "_ns) ||
   2045          StringBeginsWith(query, "CREATE VIEW "_ns)) {
   2046        query.Replace(0, 6, "CREATE TEMP");
   2047      }
   2048 
   2049      rv = aClone->ExecuteSimpleSQL(query);
   2050      NS_ENSURE_SUCCESS(rv, rv);
   2051    }
   2052 
   2053    rv = aClone->ExecuteSimpleSQL("COMMIT"_ns);
   2054    NS_ENSURE_SUCCESS(rv, rv);
   2055  }
   2056 
   2057  // Copy any functions that have been added to this connection.
   2058  SQLiteMutexAutoLock lockedScope(sharedDBMutex);
   2059  for (const auto& entry : mFunctions) {
   2060    const nsACString& key = entry.GetKey();
   2061    Connection::FunctionInfo data = entry.GetData();
   2062 
   2063    rv = aClone->CreateFunction(key, data.numArgs, data.function);
   2064    if (NS_FAILED(rv)) {
   2065      NS_WARNING("Failed to copy function to cloned connection");
   2066    }
   2067  }
   2068 
   2069  // Load SQLite extensions that were on this connection.
   2070  // Copy into an array rather than holding the mutex while we load extensions.
   2071  nsTArray<nsCString> loadedExtensions;
   2072  {
   2073    MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
   2074    AppendToArray(loadedExtensions, mLoadedExtensions);
   2075  }
   2076  for (const auto& extension : loadedExtensions) {
   2077    (void)aClone->LoadExtension(extension, nullptr);
   2078  }
   2079 
   2080  guard.release();
   2081  return NS_OK;
   2082 }
   2083 
   2084 NS_IMETHODIMP
   2085 Connection::Clone(bool aReadOnly, mozIStorageConnection** _connection) {
   2086  MOZ_ASSERT(IsOnCurrentSerialEventTarget(eventTargetOpenedOn));
   2087 
   2088  AUTO_PROFILER_LABEL("Connection::Clone", OTHER);
   2089 
   2090  if (!connectionReady()) {
   2091    return NS_ERROR_NOT_INITIALIZED;
   2092  }
   2093  nsresult rv = ensureOperationSupported(SYNCHRONOUS);
   2094  if (NS_FAILED(rv)) {
   2095    return rv;
   2096  }
   2097 
   2098  int flags = mFlags;
   2099  if (aReadOnly) {
   2100    // Turn off SQLITE_OPEN_READWRITE, and set SQLITE_OPEN_READONLY.
   2101    flags = (~SQLITE_OPEN_READWRITE & flags) | SQLITE_OPEN_READONLY;
   2102    // Turn off SQLITE_OPEN_CREATE.
   2103    flags = (~SQLITE_OPEN_CREATE & flags);
   2104  }
   2105 
   2106  RefPtr<Connection> clone =
   2107      new Connection(mStorageService, flags, mSupportedOperations,
   2108                     mTelemetryFilename, mInterruptible);
   2109 
   2110  rv = initializeClone(clone, aReadOnly);
   2111  if (NS_FAILED(rv)) {
   2112    return rv;
   2113  }
   2114 
   2115  NS_IF_ADDREF(*_connection = clone);
   2116  return NS_OK;
   2117 }
   2118 
   2119 NS_IMETHODIMP
   2120 Connection::Interrupt() {
   2121  MOZ_ASSERT(mInterruptible, "Interrupt method not allowed");
   2122  MOZ_ASSERT_IF(SYNCHRONOUS == mSupportedOperations,
   2123                !IsOnCurrentSerialEventTarget(eventTargetOpenedOn));
   2124  MOZ_ASSERT_IF(ASYNCHRONOUS == mSupportedOperations,
   2125                IsOnCurrentSerialEventTarget(eventTargetOpenedOn));
   2126 
   2127  if (!connectionReady()) {
   2128    return NS_ERROR_NOT_INITIALIZED;
   2129  }
   2130 
   2131  if (isClosing()) {  // Closing already in asynchronous case
   2132    return NS_OK;
   2133  }
   2134 
   2135  {
   2136    // As stated on https://www.sqlite.org/c3ref/interrupt.html,
   2137    // it is not safe to call sqlite3_interrupt() when
   2138    // database connection is closed or might close before
   2139    // sqlite3_interrupt() returns.
   2140    MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
   2141    if (!isClosed(lockedScope)) {
   2142      MOZ_ASSERT(mDBConn);
   2143      ::sqlite3_interrupt(mDBConn);
   2144    }
   2145  }
   2146 
   2147  return NS_OK;
   2148 }
   2149 
   2150 NS_IMETHODIMP
   2151 Connection::AsyncVacuum(mozIStorageCompletionCallback* aCallback,
   2152                        bool aUseIncremental, int32_t aSetPageSize) {
   2153  NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_NOT_SAME_THREAD);
   2154  // Abort if we're shutting down.
   2155  if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
   2156    return NS_ERROR_ABORT;
   2157  }
   2158  // Check if AsyncClose or Close were already invoked.
   2159  if (!connectionReady()) {
   2160    return NS_ERROR_NOT_INITIALIZED;
   2161  }
   2162  nsresult rv = ensureOperationSupported(ASYNCHRONOUS);
   2163  if (NS_FAILED(rv)) {
   2164    return rv;
   2165  }
   2166  nsIEventTarget* asyncThread = getAsyncExecutionTarget();
   2167  if (!asyncThread) {
   2168    return NS_ERROR_NOT_INITIALIZED;
   2169  }
   2170 
   2171  // Create and dispatch our vacuum event to the background thread.
   2172  nsCOMPtr<nsIRunnable> vacuumEvent =
   2173      new AsyncVacuumEvent(this, aCallback, aUseIncremental, aSetPageSize);
   2174  rv = asyncThread->Dispatch(vacuumEvent, NS_DISPATCH_NORMAL);
   2175  NS_ENSURE_SUCCESS(rv, rv);
   2176 
   2177  return NS_OK;
   2178 }
   2179 
   2180 NS_IMETHODIMP
   2181 Connection::GetDefaultPageSize(int32_t* _defaultPageSize) {
   2182  *_defaultPageSize = Service::kDefaultPageSize;
   2183  return NS_OK;
   2184 }
   2185 
   2186 NS_IMETHODIMP
   2187 Connection::GetConnectionReady(bool* _ready) {
   2188  MOZ_ASSERT(IsOnCurrentSerialEventTarget(eventTargetOpenedOn));
   2189  *_ready = connectionReady();
   2190  return NS_OK;
   2191 }
   2192 
   2193 NS_IMETHODIMP
   2194 Connection::GetDatabaseFile(nsIFile** _dbFile) {
   2195  if (!connectionReady()) {
   2196    return NS_ERROR_NOT_INITIALIZED;
   2197  }
   2198  nsresult rv = ensureOperationSupported(ASYNCHRONOUS);
   2199  if (NS_FAILED(rv)) {
   2200    return rv;
   2201  }
   2202 
   2203  NS_IF_ADDREF(*_dbFile = mDatabaseFile);
   2204 
   2205  return NS_OK;
   2206 }
   2207 
   2208 NS_IMETHODIMP
   2209 Connection::GetLastInsertRowID(int64_t* _id) {
   2210  if (!connectionReady()) {
   2211    return NS_ERROR_NOT_INITIALIZED;
   2212  }
   2213  nsresult rv = ensureOperationSupported(SYNCHRONOUS);
   2214  if (NS_FAILED(rv)) {
   2215    return rv;
   2216  }
   2217 
   2218  sqlite_int64 id = ::sqlite3_last_insert_rowid(mDBConn);
   2219  *_id = id;
   2220 
   2221  return NS_OK;
   2222 }
   2223 
   2224 NS_IMETHODIMP
   2225 Connection::GetAffectedRows(int32_t* _rows) {
   2226  if (!connectionReady()) {
   2227    return NS_ERROR_NOT_INITIALIZED;
   2228  }
   2229  nsresult rv = ensureOperationSupported(SYNCHRONOUS);
   2230  if (NS_FAILED(rv)) {
   2231    return rv;
   2232  }
   2233 
   2234  *_rows = ::sqlite3_changes(mDBConn);
   2235 
   2236  return NS_OK;
   2237 }
   2238 
   2239 NS_IMETHODIMP
   2240 Connection::GetLastError(int32_t* _error) {
   2241  if (!connectionReady()) {
   2242    return NS_ERROR_NOT_INITIALIZED;
   2243  }
   2244  nsresult rv = ensureOperationSupported(SYNCHRONOUS);
   2245  if (NS_FAILED(rv)) {
   2246    return rv;
   2247  }
   2248 
   2249  *_error = ::sqlite3_errcode(mDBConn);
   2250 
   2251  return NS_OK;
   2252 }
   2253 
   2254 NS_IMETHODIMP
   2255 Connection::GetLastErrorString(nsACString& _errorString) {
   2256  if (!connectionReady()) {
   2257    return NS_ERROR_NOT_INITIALIZED;
   2258  }
   2259  nsresult rv = ensureOperationSupported(SYNCHRONOUS);
   2260  if (NS_FAILED(rv)) {
   2261    return rv;
   2262  }
   2263 
   2264  const char* serr = ::sqlite3_errmsg(mDBConn);
   2265  _errorString.Assign(serr);
   2266 
   2267  return NS_OK;
   2268 }
   2269 
   2270 NS_IMETHODIMP
   2271 Connection::GetSchemaVersion(int32_t* _version) {
   2272  if (!connectionReady()) {
   2273    return NS_ERROR_NOT_INITIALIZED;
   2274  }
   2275  nsresult rv = ensureOperationSupported(SYNCHRONOUS);
   2276  if (NS_FAILED(rv)) {
   2277    return rv;
   2278  }
   2279 
   2280  nsCOMPtr<mozIStorageStatement> stmt;
   2281  (void)CreateStatement("PRAGMA user_version"_ns, getter_AddRefs(stmt));
   2282  NS_ENSURE_TRUE(stmt, NS_ERROR_OUT_OF_MEMORY);
   2283 
   2284  *_version = 0;
   2285  bool hasResult;
   2286  if (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
   2287    *_version = stmt->AsInt32(0);
   2288  }
   2289 
   2290  return NS_OK;
   2291 }
   2292 
   2293 NS_IMETHODIMP
   2294 Connection::SetSchemaVersion(int32_t aVersion) {
   2295  if (!connectionReady()) {
   2296    return NS_ERROR_NOT_INITIALIZED;
   2297  }
   2298  nsresult rv = ensureOperationSupported(SYNCHRONOUS);
   2299  if (NS_FAILED(rv)) {
   2300    return rv;
   2301  }
   2302 
   2303  nsAutoCString stmt("PRAGMA user_version = "_ns);
   2304  stmt.AppendInt(aVersion);
   2305 
   2306  return ExecuteSimpleSQL(stmt);
   2307 }
   2308 
   2309 NS_IMETHODIMP
   2310 Connection::CreateStatement(const nsACString& aSQLStatement,
   2311                            mozIStorageStatement** _stmt) {
   2312  NS_ENSURE_ARG_POINTER(_stmt);
   2313  if (!connectionReady()) {
   2314    return NS_ERROR_NOT_INITIALIZED;
   2315  }
   2316  nsresult rv = ensureOperationSupported(SYNCHRONOUS);
   2317  if (NS_FAILED(rv)) {
   2318    return rv;
   2319  }
   2320 
   2321  RefPtr<Statement> statement(new Statement());
   2322  NS_ENSURE_TRUE(statement, NS_ERROR_OUT_OF_MEMORY);
   2323 
   2324  rv = statement->initialize(this, mDBConn, aSQLStatement);
   2325  NS_ENSURE_SUCCESS(rv, rv);
   2326 
   2327  Statement* rawPtr;
   2328  statement.forget(&rawPtr);
   2329  *_stmt = rawPtr;
   2330  return NS_OK;
   2331 }
   2332 
   2333 NS_IMETHODIMP
   2334 Connection::CreateAsyncStatement(const nsACString& aSQLStatement,
   2335                                 mozIStorageAsyncStatement** _stmt) {
   2336  NS_ENSURE_ARG_POINTER(_stmt);
   2337  if (!connectionReady()) {
   2338    return NS_ERROR_NOT_INITIALIZED;
   2339  }
   2340  nsresult rv = ensureOperationSupported(ASYNCHRONOUS);
   2341  if (NS_FAILED(rv)) {
   2342    return rv;
   2343  }
   2344 
   2345  RefPtr<AsyncStatement> statement(new AsyncStatement());
   2346  NS_ENSURE_TRUE(statement, NS_ERROR_OUT_OF_MEMORY);
   2347 
   2348  rv = statement->initialize(this, mDBConn, aSQLStatement);
   2349  NS_ENSURE_SUCCESS(rv, rv);
   2350 
   2351  AsyncStatement* rawPtr;
   2352  statement.forget(&rawPtr);
   2353  *_stmt = rawPtr;
   2354  return NS_OK;
   2355 }
   2356 
   2357 NS_IMETHODIMP
   2358 Connection::ExecuteSimpleSQL(const nsACString& aSQLStatement) {
   2359  CHECK_MAINTHREAD_ABUSE();
   2360  if (!connectionReady()) {
   2361    return NS_ERROR_NOT_INITIALIZED;
   2362  }
   2363  nsresult rv = ensureOperationSupported(SYNCHRONOUS);
   2364  if (NS_FAILED(rv)) {
   2365    return rv;
   2366  }
   2367 
   2368  int srv = executeSql(mDBConn, PromiseFlatCString(aSQLStatement).get());
   2369  return convertResultCode(srv);
   2370 }
   2371 
   2372 NS_IMETHODIMP
   2373 Connection::ExecuteAsync(
   2374    const nsTArray<RefPtr<mozIStorageBaseStatement>>& aStatements,
   2375    mozIStorageStatementCallback* aCallback,
   2376    mozIStoragePendingStatement** _handle) {
   2377  nsTArray<StatementData> stmts(aStatements.Length());
   2378  for (uint32_t i = 0; i < aStatements.Length(); i++) {
   2379    nsCOMPtr<StorageBaseStatementInternal> stmt =
   2380        do_QueryInterface(aStatements[i]);
   2381    NS_ENSURE_STATE(stmt);
   2382 
   2383    // Obtain our StatementData.
   2384    StatementData data;
   2385    nsresult rv = stmt->getAsynchronousStatementData(data);
   2386    NS_ENSURE_SUCCESS(rv, rv);
   2387 
   2388    NS_ASSERTION(stmt->getOwner() == this,
   2389                 "Statement must be from this database connection!");
   2390 
   2391    // Now append it to our array.
   2392    stmts.AppendElement(data);
   2393  }
   2394 
   2395  // Dispatch to the background
   2396  return AsyncExecuteStatements::execute(std::move(stmts), this, mDBConn,
   2397                                         aCallback, _handle);
   2398 }
   2399 
   2400 NS_IMETHODIMP
   2401 Connection::ExecuteSimpleSQLAsync(const nsACString& aSQLStatement,
   2402                                  mozIStorageStatementCallback* aCallback,
   2403                                  mozIStoragePendingStatement** _handle) {
   2404  NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_NOT_SAME_THREAD);
   2405 
   2406  nsCOMPtr<mozIStorageAsyncStatement> stmt;
   2407  nsresult rv = CreateAsyncStatement(aSQLStatement, getter_AddRefs(stmt));
   2408  if (NS_FAILED(rv)) {
   2409    return rv;
   2410  }
   2411 
   2412  nsCOMPtr<mozIStoragePendingStatement> pendingStatement;
   2413  rv = stmt->ExecuteAsync(aCallback, getter_AddRefs(pendingStatement));
   2414  if (NS_FAILED(rv)) {
   2415    return rv;
   2416  }
   2417 
   2418  pendingStatement.forget(_handle);
   2419  return rv;
   2420 }
   2421 
   2422 NS_IMETHODIMP
   2423 Connection::TableExists(const nsACString& aTableName, bool* _exists) {
   2424  return databaseElementExists(TABLE, aTableName, _exists);
   2425 }
   2426 
   2427 NS_IMETHODIMP
   2428 Connection::IndexExists(const nsACString& aIndexName, bool* _exists) {
   2429  return databaseElementExists(INDEX, aIndexName, _exists);
   2430 }
   2431 
   2432 NS_IMETHODIMP
   2433 Connection::GetTransactionInProgress(bool* _inProgress) {
   2434  if (!connectionReady()) {
   2435    return NS_ERROR_NOT_INITIALIZED;
   2436  }
   2437  nsresult rv = ensureOperationSupported(ASYNCHRONOUS);
   2438  if (NS_FAILED(rv)) {
   2439    return rv;
   2440  }
   2441 
   2442  SQLiteMutexAutoLock lockedScope(sharedDBMutex);
   2443  *_inProgress = transactionInProgress(lockedScope);
   2444  return NS_OK;
   2445 }
   2446 
   2447 NS_IMETHODIMP
   2448 Connection::GetDefaultTransactionType(int32_t* _type) {
   2449  *_type = mDefaultTransactionType;
   2450  return NS_OK;
   2451 }
   2452 
   2453 NS_IMETHODIMP
   2454 Connection::SetDefaultTransactionType(int32_t aType) {
   2455  NS_ENSURE_ARG_RANGE(aType, TRANSACTION_DEFERRED, TRANSACTION_EXCLUSIVE);
   2456  mDefaultTransactionType = aType;
   2457  return NS_OK;
   2458 }
   2459 
   2460 NS_IMETHODIMP
   2461 Connection::GetVariableLimit(int32_t* _limit) {
   2462  if (!connectionReady()) {
   2463    return NS_ERROR_NOT_INITIALIZED;
   2464  }
   2465  int limit = ::sqlite3_limit(mDBConn, SQLITE_LIMIT_VARIABLE_NUMBER, -1);
   2466  if (limit < 0) {
   2467    return NS_ERROR_UNEXPECTED;
   2468  }
   2469  *_limit = limit;
   2470  return NS_OK;
   2471 }
   2472 
   2473 NS_IMETHODIMP
   2474 Connection::SetVariableLimit(int32_t limit) {
   2475  if (!connectionReady()) {
   2476    return NS_ERROR_NOT_INITIALIZED;
   2477  }
   2478  int oldLimit = ::sqlite3_limit(mDBConn, SQLITE_LIMIT_VARIABLE_NUMBER, limit);
   2479  if (oldLimit < 0) {
   2480    return NS_ERROR_UNEXPECTED;
   2481  }
   2482  return NS_OK;
   2483 }
   2484 
   2485 NS_IMETHODIMP
   2486 Connection::BeginTransaction() {
   2487  if (!connectionReady()) {
   2488    return NS_ERROR_NOT_INITIALIZED;
   2489  }
   2490  nsresult rv = ensureOperationSupported(SYNCHRONOUS);
   2491  if (NS_FAILED(rv)) {
   2492    return rv;
   2493  }
   2494 
   2495  SQLiteMutexAutoLock lockedScope(sharedDBMutex);
   2496  return beginTransactionInternal(lockedScope, mDBConn,
   2497                                  mDefaultTransactionType);
   2498 }
   2499 
   2500 nsresult Connection::beginTransactionInternal(
   2501    const SQLiteMutexAutoLock& aProofOfLock, sqlite3* aNativeConnection,
   2502    int32_t aTransactionType) {
   2503  if (transactionInProgress(aProofOfLock)) {
   2504    return NS_ERROR_FAILURE;
   2505  }
   2506  nsresult rv;
   2507  switch (aTransactionType) {
   2508    case TRANSACTION_DEFERRED:
   2509      rv = convertResultCode(executeSql(aNativeConnection, "BEGIN DEFERRED"));
   2510      break;
   2511    case TRANSACTION_IMMEDIATE:
   2512      rv = convertResultCode(executeSql(aNativeConnection, "BEGIN IMMEDIATE"));
   2513      break;
   2514    case TRANSACTION_EXCLUSIVE:
   2515      rv = convertResultCode(executeSql(aNativeConnection, "BEGIN EXCLUSIVE"));
   2516      break;
   2517    default:
   2518      return NS_ERROR_ILLEGAL_VALUE;
   2519  }
   2520  return rv;
   2521 }
   2522 
   2523 NS_IMETHODIMP
   2524 Connection::CommitTransaction() {
   2525  if (!connectionReady()) {
   2526    return NS_ERROR_NOT_INITIALIZED;
   2527  }
   2528  nsresult rv = ensureOperationSupported(SYNCHRONOUS);
   2529  if (NS_FAILED(rv)) {
   2530    return rv;
   2531  }
   2532 
   2533  SQLiteMutexAutoLock lockedScope(sharedDBMutex);
   2534  return commitTransactionInternal(lockedScope, mDBConn);
   2535 }
   2536 
   2537 nsresult Connection::commitTransactionInternal(
   2538    const SQLiteMutexAutoLock& aProofOfLock, sqlite3* aNativeConnection) {
   2539  if (!transactionInProgress(aProofOfLock)) {
   2540    return NS_ERROR_UNEXPECTED;
   2541  }
   2542  nsresult rv =
   2543      convertResultCode(executeSql(aNativeConnection, "COMMIT TRANSACTION"));
   2544  return rv;
   2545 }
   2546 
   2547 NS_IMETHODIMP
   2548 Connection::RollbackTransaction() {
   2549  if (!connectionReady()) {
   2550    return NS_ERROR_NOT_INITIALIZED;
   2551  }
   2552  nsresult rv = ensureOperationSupported(SYNCHRONOUS);
   2553  if (NS_FAILED(rv)) {
   2554    return rv;
   2555  }
   2556 
   2557  SQLiteMutexAutoLock lockedScope(sharedDBMutex);
   2558  return rollbackTransactionInternal(lockedScope, mDBConn);
   2559 }
   2560 
   2561 nsresult Connection::rollbackTransactionInternal(
   2562    const SQLiteMutexAutoLock& aProofOfLock, sqlite3* aNativeConnection) {
   2563  if (!transactionInProgress(aProofOfLock)) {
   2564    return NS_ERROR_UNEXPECTED;
   2565  }
   2566 
   2567  nsresult rv =
   2568      convertResultCode(executeSql(aNativeConnection, "ROLLBACK TRANSACTION"));
   2569  return rv;
   2570 }
   2571 
   2572 NS_IMETHODIMP
   2573 Connection::CreateTable(const char* aTableName, const char* aTableSchema) {
   2574  if (!connectionReady()) {
   2575    return NS_ERROR_NOT_INITIALIZED;
   2576  }
   2577  nsresult rv = ensureOperationSupported(SYNCHRONOUS);
   2578  if (NS_FAILED(rv)) {
   2579    return rv;
   2580  }
   2581 
   2582  SmprintfPointer buf =
   2583      ::mozilla::Smprintf("CREATE TABLE %s (%s)", aTableName, aTableSchema);
   2584  if (!buf) return NS_ERROR_OUT_OF_MEMORY;
   2585 
   2586  int srv = executeSql(mDBConn, buf.get());
   2587 
   2588  return convertResultCode(srv);
   2589 }
   2590 
   2591 NS_IMETHODIMP
   2592 Connection::CreateFunction(const nsACString& aFunctionName,
   2593                           int32_t aNumArguments,
   2594                           mozIStorageFunction* aFunction) {
   2595  if (!connectionReady()) {
   2596    return NS_ERROR_NOT_INITIALIZED;
   2597  }
   2598  nsresult rv = ensureOperationSupported(ASYNCHRONOUS);
   2599  if (NS_FAILED(rv)) {
   2600    return rv;
   2601  }
   2602 
   2603  // Check to see if this function is already defined.  We only check the name
   2604  // because a function can be defined with the same body but different names.
   2605  SQLiteMutexAutoLock lockedScope(sharedDBMutex);
   2606  NS_ENSURE_FALSE(mFunctions.Contains(aFunctionName), NS_ERROR_FAILURE);
   2607 
   2608  int srv = ::sqlite3_create_function(
   2609      mDBConn, nsPromiseFlatCString(aFunctionName).get(), aNumArguments,
   2610      SQLITE_ANY, aFunction, basicFunctionHelper, nullptr, nullptr);
   2611  if (srv != SQLITE_OK) return convertResultCode(srv);
   2612 
   2613  FunctionInfo info = {aFunction, aNumArguments};
   2614  mFunctions.InsertOrUpdate(aFunctionName, info);
   2615 
   2616  return NS_OK;
   2617 }
   2618 
   2619 NS_IMETHODIMP
   2620 Connection::RemoveFunction(const nsACString& aFunctionName) {
   2621  if (!connectionReady()) {
   2622    return NS_ERROR_NOT_INITIALIZED;
   2623  }
   2624  nsresult rv = ensureOperationSupported(ASYNCHRONOUS);
   2625  if (NS_FAILED(rv)) {
   2626    return rv;
   2627  }
   2628 
   2629  SQLiteMutexAutoLock lockedScope(sharedDBMutex);
   2630  NS_ENSURE_TRUE(mFunctions.Get(aFunctionName, nullptr), NS_ERROR_FAILURE);
   2631 
   2632  int srv = ::sqlite3_create_function(
   2633      mDBConn, nsPromiseFlatCString(aFunctionName).get(), 0, SQLITE_ANY,
   2634      nullptr, nullptr, nullptr, nullptr);
   2635  if (srv != SQLITE_OK) return convertResultCode(srv);
   2636 
   2637  mFunctions.Remove(aFunctionName);
   2638 
   2639  return NS_OK;
   2640 }
   2641 
   2642 NS_IMETHODIMP
   2643 Connection::SetProgressHandler(int32_t aGranularity,
   2644                               mozIStorageProgressHandler* aHandler,
   2645                               mozIStorageProgressHandler** _oldHandler) {
   2646  if (!connectionReady()) {
   2647    return NS_ERROR_NOT_INITIALIZED;
   2648  }
   2649  nsresult rv = ensureOperationSupported(ASYNCHRONOUS);
   2650  if (NS_FAILED(rv)) {
   2651    return rv;
   2652  }
   2653 
   2654  // Return previous one
   2655  SQLiteMutexAutoLock lockedScope(sharedDBMutex);
   2656  NS_IF_ADDREF(*_oldHandler = mProgressHandler);
   2657 
   2658  if (!aHandler || aGranularity <= 0) {
   2659    aHandler = nullptr;
   2660    aGranularity = 0;
   2661  }
   2662  mProgressHandler = aHandler;
   2663  ::sqlite3_progress_handler(mDBConn, aGranularity, sProgressHelper, this);
   2664 
   2665  return NS_OK;
   2666 }
   2667 
   2668 NS_IMETHODIMP
   2669 Connection::RemoveProgressHandler(mozIStorageProgressHandler** _oldHandler) {
   2670  if (!connectionReady()) {
   2671    return NS_ERROR_NOT_INITIALIZED;
   2672  }
   2673  nsresult rv = ensureOperationSupported(ASYNCHRONOUS);
   2674  if (NS_FAILED(rv)) {
   2675    return rv;
   2676  }
   2677 
   2678  // Return previous one
   2679  SQLiteMutexAutoLock lockedScope(sharedDBMutex);
   2680  NS_IF_ADDREF(*_oldHandler = mProgressHandler);
   2681 
   2682  mProgressHandler = nullptr;
   2683  ::sqlite3_progress_handler(mDBConn, 0, nullptr, nullptr);
   2684 
   2685  return NS_OK;
   2686 }
   2687 
   2688 NS_IMETHODIMP
   2689 Connection::SetGrowthIncrement(int32_t aChunkSize,
   2690                               const nsACString& aDatabaseName) {
   2691  if (!connectionReady()) {
   2692    return NS_ERROR_NOT_INITIALIZED;
   2693  }
   2694  nsresult rv = ensureOperationSupported(SYNCHRONOUS);
   2695  if (NS_FAILED(rv)) {
   2696    return rv;
   2697  }
   2698 
   2699  // Bug 597215: Disk space is extremely limited on Android
   2700  // so don't preallocate space. This is also not effective
   2701  // on log structured file systems used by Android devices
   2702 #if !defined(ANDROID) && !defined(MOZ_PLATFORM_MAEMO)
   2703  // Don't preallocate if less than 500MiB is available.
   2704  int64_t bytesAvailable;
   2705  rv = mDatabaseFile->GetDiskSpaceAvailable(&bytesAvailable);
   2706  NS_ENSURE_SUCCESS(rv, rv);
   2707  if (bytesAvailable < MIN_AVAILABLE_BYTES_PER_CHUNKED_GROWTH) {
   2708    return NS_ERROR_FILE_TOO_BIG;
   2709  }
   2710 
   2711  int srv = ::sqlite3_file_control(
   2712      mDBConn,
   2713      aDatabaseName.Length() ? nsPromiseFlatCString(aDatabaseName).get()
   2714                             : nullptr,
   2715      SQLITE_FCNTL_CHUNK_SIZE, &aChunkSize);
   2716  if (srv == SQLITE_OK) {
   2717    mGrowthChunkSize = aChunkSize;
   2718  }
   2719 #endif
   2720  return NS_OK;
   2721 }
   2722 
   2723 int32_t Connection::RemovablePagesInFreeList(const nsACString& aSchemaName) {
   2724  int32_t freeListPagesCount = 0;
   2725  if (!isConnectionReadyOnThisThread()) {
   2726    MOZ_ASSERT(false, "Database connection is not ready");
   2727    return freeListPagesCount;
   2728  }
   2729  {
   2730    nsAutoCString query(MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA ");
   2731    query.Append(aSchemaName);
   2732    query.AppendLiteral(".freelist_count");
   2733    nsCOMPtr<mozIStorageStatement> stmt;
   2734    DebugOnly<nsresult> rv = CreateStatement(query, getter_AddRefs(stmt));
   2735    MOZ_ASSERT(NS_SUCCEEDED(rv));
   2736    bool hasResult = false;
   2737    if (stmt && NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
   2738      freeListPagesCount = stmt->AsInt32(0);
   2739    }
   2740  }
   2741  // If there's no chunk size set, any page is good to be removed.
   2742  if (mGrowthChunkSize == 0 || freeListPagesCount == 0) {
   2743    return freeListPagesCount;
   2744  }
   2745  int32_t pageSize;
   2746  {
   2747    nsAutoCString query(MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA ");
   2748    query.Append(aSchemaName);
   2749    query.AppendLiteral(".page_size");
   2750    nsCOMPtr<mozIStorageStatement> stmt;
   2751    DebugOnly<nsresult> rv = CreateStatement(query, getter_AddRefs(stmt));
   2752    MOZ_ASSERT(NS_SUCCEEDED(rv));
   2753    bool hasResult = false;
   2754    if (stmt && NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
   2755      pageSize = stmt->AsInt32(0);
   2756    } else {
   2757      MOZ_ASSERT(false, "Couldn't get page_size");
   2758      return 0;
   2759    }
   2760  }
   2761  return std::max(0, freeListPagesCount - (mGrowthChunkSize / pageSize));
   2762 }
   2763 
   2764 NS_IMETHODIMP
   2765 Connection::LoadExtension(const nsACString& aExtensionName,
   2766                          mozIStorageCompletionCallback* aCallback) {
   2767  AUTO_PROFILER_LABEL("Connection::LoadExtension", OTHER);
   2768 
   2769  // This is a static list of extensions we can load.
   2770  // Please use lowercase ASCII names and keep this list alphabetically ordered.
   2771  static constexpr nsLiteralCString sSupportedExtensions[] = {
   2772      // clang-format off
   2773      "fts5"_ns,
   2774  #ifdef MOZ_SQLITE_VEC0_EXT
   2775      "vec"_ns,
   2776  #endif
   2777      // clang-format on
   2778  };
   2779  if (std::find(std::begin(sSupportedExtensions),
   2780                std::end(sSupportedExtensions),
   2781                aExtensionName) == std::end(sSupportedExtensions)) {
   2782    return NS_ERROR_INVALID_ARG;
   2783  }
   2784 
   2785  if (!connectionReady()) {
   2786    return NS_ERROR_NOT_INITIALIZED;
   2787  }
   2788 
   2789  int srv = ::sqlite3_db_config(mDBConn, SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION,
   2790                                1, nullptr);
   2791  if (srv != SQLITE_OK) {
   2792    return NS_ERROR_UNEXPECTED;
   2793  }
   2794 
   2795  // Track the loaded extension for later connection cloning operations.
   2796  {
   2797    MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
   2798    if (!mLoadedExtensions.EnsureInserted(aExtensionName)) {
   2799      // Already loaded, bail out but issue a warning.
   2800      NS_WARNING(nsPrintfCString(
   2801                     "Tried to register '%s' SQLite extension multiple times!",
   2802                     PromiseFlatCString(aExtensionName).get())
   2803                     .get());
   2804      return NS_OK;
   2805    }
   2806  }
   2807 
   2808  nsAutoCString entryPoint("sqlite3_");
   2809  entryPoint.Append(aExtensionName);
   2810  entryPoint.AppendLiteral("_init");
   2811 
   2812  RefPtr<Runnable> loadTask = NS_NewRunnableFunction(
   2813      "mozStorageConnection::LoadExtension",
   2814      [this, self = RefPtr(this), entryPoint,
   2815       callback = RefPtr(aCallback)]() mutable {
   2816        MOZ_ASSERT(
   2817            !NS_IsMainThread() ||
   2818                (operationSupported(Connection::SYNCHRONOUS) &&
   2819                 eventTargetOpenedOn == GetMainThreadSerialEventTarget()),
   2820            "Should happen on main-thread only for synchronous connections "
   2821            "opened on the main thread");
   2822 #ifdef MOZ_FOLD_LIBS
   2823        int srv = ::sqlite3_load_extension(mDBConn,
   2824                                           MOZ_DLL_PREFIX "nss3" MOZ_DLL_SUFFIX,
   2825                                           entryPoint.get(), nullptr);
   2826 #else
   2827        int srv = ::sqlite3_load_extension(
   2828            mDBConn, MOZ_DLL_PREFIX "mozsqlite3" MOZ_DLL_SUFFIX,
   2829            entryPoint.get(), nullptr);
   2830 #endif
   2831        if (!callback) {
   2832          return;
   2833        };
   2834        RefPtr<Runnable> callbackTask = NS_NewRunnableFunction(
   2835            "mozStorageConnection::LoadExtension_callback",
   2836            [callback = std::move(callback), srv]() {
   2837              (void)callback->Complete(convertResultCode(srv), nullptr);
   2838            });
   2839        if (IsOnCurrentSerialEventTarget(eventTargetOpenedOn)) {
   2840          MOZ_ALWAYS_SUCCEEDS(callbackTask->Run());
   2841        } else {
   2842          // Redispatch the callback to the calling thread.
   2843          MOZ_ALWAYS_SUCCEEDS(eventTargetOpenedOn->Dispatch(
   2844              callbackTask.forget(), NS_DISPATCH_NORMAL));
   2845        }
   2846      });
   2847 
   2848  if (NS_IsMainThread() && !operationSupported(Connection::SYNCHRONOUS)) {
   2849    // This is a main-thread call to an async-only connection, thus we should
   2850    // load the library in the helper thread.
   2851    nsIEventTarget* helperThread = getAsyncExecutionTarget();
   2852    if (!helperThread) {
   2853      return NS_ERROR_NOT_INITIALIZED;
   2854    }
   2855    MOZ_ALWAYS_SUCCEEDS(
   2856        helperThread->Dispatch(loadTask.forget(), NS_DISPATCH_NORMAL));
   2857  } else {
   2858    // In any other case we just load the extension on the current thread.
   2859    MOZ_ALWAYS_SUCCEEDS(loadTask->Run());
   2860  }
   2861  return NS_OK;
   2862 }
   2863 
   2864 NS_IMETHODIMP
   2865 Connection::EnableModule(const nsACString& aModuleName) {
   2866  if (!connectionReady()) {
   2867    return NS_ERROR_NOT_INITIALIZED;
   2868  }
   2869  nsresult rv = ensureOperationSupported(SYNCHRONOUS);
   2870  if (NS_FAILED(rv)) {
   2871    return rv;
   2872  }
   2873 
   2874  for (auto& gModule : gModules) {
   2875    struct Module* m = &gModule;
   2876    if (aModuleName.Equals(m->name)) {
   2877      int srv = m->registerFunc(mDBConn, m->name);
   2878      if (srv != SQLITE_OK) return convertResultCode(srv);
   2879 
   2880      return NS_OK;
   2881    }
   2882  }
   2883 
   2884  return NS_ERROR_FAILURE;
   2885 }
   2886 
   2887 NS_IMETHODIMP
   2888 Connection::GetQuotaObjects(QuotaObject** aDatabaseQuotaObject,
   2889                            QuotaObject** aJournalQuotaObject) {
   2890  MOZ_ASSERT(aDatabaseQuotaObject);
   2891  MOZ_ASSERT(aJournalQuotaObject);
   2892 
   2893  if (!connectionReady()) {
   2894    return NS_ERROR_NOT_INITIALIZED;
   2895  }
   2896  nsresult rv = ensureOperationSupported(SYNCHRONOUS);
   2897  if (NS_FAILED(rv)) {
   2898    return rv;
   2899  }
   2900 
   2901  sqlite3_file* file;
   2902  int srv = ::sqlite3_file_control(mDBConn, nullptr, SQLITE_FCNTL_FILE_POINTER,
   2903                                   &file);
   2904  if (srv != SQLITE_OK) {
   2905    return convertResultCode(srv);
   2906  }
   2907 
   2908  sqlite3_vfs* vfs;
   2909  srv =
   2910      ::sqlite3_file_control(mDBConn, nullptr, SQLITE_FCNTL_VFS_POINTER, &vfs);
   2911  if (srv != SQLITE_OK) {
   2912    return convertResultCode(srv);
   2913  }
   2914 
   2915  bool obfusactingVFS = false;
   2916 
   2917  {
   2918    const nsDependentCString vfsName{vfs->zName};
   2919 
   2920    if (vfsName == obfsvfs::GetVFSName()) {
   2921      obfusactingVFS = true;
   2922    } else if (vfsName != quotavfs::GetVFSName()) {
   2923      NS_WARNING("Got unexpected vfs");
   2924      return NS_ERROR_FAILURE;
   2925    }
   2926  }
   2927 
   2928  RefPtr<QuotaObject> databaseQuotaObject =
   2929      GetQuotaObject(file, obfusactingVFS);
   2930  if (NS_WARN_IF(!databaseQuotaObject)) {
   2931    return NS_ERROR_FAILURE;
   2932  }
   2933 
   2934  srv = ::sqlite3_file_control(mDBConn, nullptr, SQLITE_FCNTL_JOURNAL_POINTER,
   2935                               &file);
   2936  if (srv != SQLITE_OK) {
   2937    return convertResultCode(srv);
   2938  }
   2939 
   2940  RefPtr<QuotaObject> journalQuotaObject = GetQuotaObject(file, obfusactingVFS);
   2941  if (NS_WARN_IF(!journalQuotaObject)) {
   2942    return NS_ERROR_FAILURE;
   2943  }
   2944 
   2945  databaseQuotaObject.forget(aDatabaseQuotaObject);
   2946  journalQuotaObject.forget(aJournalQuotaObject);
   2947  return NS_OK;
   2948 }
   2949 
   2950 SQLiteMutex& Connection::GetSharedDBMutex() { return sharedDBMutex; }
   2951 
   2952 uint32_t Connection::GetTransactionNestingLevel(
   2953    const mozilla::storage::SQLiteMutexAutoLock& aProofOfLock) {
   2954  return mTransactionNestingLevel;
   2955 }
   2956 
   2957 uint32_t Connection::IncreaseTransactionNestingLevel(
   2958    const mozilla::storage::SQLiteMutexAutoLock& aProofOfLock) {
   2959  return ++mTransactionNestingLevel;
   2960 }
   2961 
   2962 uint32_t Connection::DecreaseTransactionNestingLevel(
   2963    const mozilla::storage::SQLiteMutexAutoLock& aProofOfLock) {
   2964  return --mTransactionNestingLevel;
   2965 }
   2966 
   2967 NS_IMETHODIMP
   2968 Connection::BackupToFileAsync(nsIFile* aDestinationFile,
   2969                              mozIStorageCompletionCallback* aCallback,
   2970                              uint32_t aPagesPerStep, uint32_t aStepDelayMs) {
   2971  NS_ENSURE_ARG(aDestinationFile);
   2972  NS_ENSURE_ARG(aCallback);
   2973  NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_NOT_SAME_THREAD);
   2974 
   2975  // Abort if we're shutting down.
   2976  if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
   2977    return NS_ERROR_ABORT;
   2978  }
   2979  // Check if AsyncClose or Close were already invoked.
   2980  if (!connectionReady()) {
   2981    return NS_ERROR_NOT_INITIALIZED;
   2982  }
   2983  nsresult rv = ensureOperationSupported(ASYNCHRONOUS);
   2984  if (NS_FAILED(rv)) {
   2985    return rv;
   2986  }
   2987  nsIEventTarget* asyncThread = getAsyncExecutionTarget();
   2988  if (!asyncThread) {
   2989    return NS_ERROR_NOT_INITIALIZED;
   2990  }
   2991 
   2992  // The number of pages of the database to copy per step
   2993  static constexpr int32_t DEFAULT_PAGES_PER_STEP = 5;
   2994  // The number of milliseconds to wait between each step.
   2995  static constexpr uint32_t DEFAULT_STEP_DELAY_MS = 250;
   2996 
   2997  CheckedInt<int32_t> pagesPerStep(aPagesPerStep);
   2998  if (!pagesPerStep.isValid()) {
   2999    return NS_ERROR_INVALID_ARG;
   3000  }
   3001 
   3002  if (!pagesPerStep.value()) {
   3003    pagesPerStep = DEFAULT_PAGES_PER_STEP;
   3004  }
   3005 
   3006  if (!aStepDelayMs) {
   3007    aStepDelayMs = DEFAULT_STEP_DELAY_MS;
   3008  }
   3009 
   3010  // Create and dispatch our backup event to the execution thread.
   3011  nsCOMPtr<nsIRunnable> backupEvent =
   3012      new AsyncBackupDatabaseFile(this, mDBConn, aDestinationFile, aCallback,
   3013                                  pagesPerStep.value(), aStepDelayMs);
   3014  rv = asyncThread->Dispatch(backupEvent, NS_DISPATCH_NORMAL);
   3015  return rv;
   3016 }
   3017 
   3018 }  // namespace mozilla::storage