tor-browser

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

IDBDatabase.cpp (32012B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "IDBDatabase.h"
      8 
      9 #include "IDBEvents.h"
     10 #include "IDBFactory.h"
     11 #include "IDBIndex.h"
     12 #include "IDBObjectStore.h"
     13 #include "IDBRequest.h"
     14 #include "IDBTransaction.h"
     15 #include "IndexedDBCommon.h"
     16 #include "IndexedDatabaseInlines.h"
     17 #include "IndexedDatabaseManager.h"
     18 #include "MainThreadUtils.h"
     19 #include "ProfilerHelpers.h"
     20 #include "ReportInternalError.h"
     21 #include "ScriptErrorHelper.h"
     22 #include "mozilla/ErrorResult.h"
     23 #include "mozilla/EventDispatcher.h"
     24 #include "mozilla/Services.h"
     25 #include "mozilla/dom/BindingDeclarations.h"
     26 #include "mozilla/dom/DOMStringListBinding.h"
     27 #include "mozilla/dom/Document.h"
     28 #include "mozilla/dom/Exceptions.h"
     29 #include "mozilla/dom/File.h"
     30 #include "mozilla/dom/IDBDatabaseBinding.h"
     31 #include "mozilla/dom/IDBObjectStoreBinding.h"
     32 #include "mozilla/dom/IDBTransactionBinding.h"
     33 #include "mozilla/dom/IPCBlobUtils.h"
     34 #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseFileChild.h"
     35 #include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h"
     36 #include "mozilla/dom/quota/QuotaManager.h"
     37 #include "mozilla/dom/quota/ResultExtensions.h"
     38 #include "mozilla/ipc/BackgroundChild.h"
     39 #include "mozilla/ipc/BackgroundUtils.h"
     40 #include "mozilla/ipc/FileDescriptor.h"
     41 #include "mozilla/ipc/InputStreamParams.h"
     42 #include "mozilla/ipc/InputStreamUtils.h"
     43 #include "mozilla/storage.h"
     44 #include "nsCOMPtr.h"
     45 #include "nsGlobalWindowInner.h"
     46 #include "nsIObserver.h"
     47 #include "nsIObserverService.h"
     48 #include "nsIScriptError.h"
     49 #include "nsISupportsPrimitives.h"
     50 #include "nsIWeakReferenceUtils.h"
     51 #include "nsQueryObject.h"
     52 #include "nsThreadUtils.h"
     53 
     54 // Include this last to avoid path problems on Windows.
     55 #include "ActorsChild.h"
     56 
     57 namespace mozilla::dom {
     58 
     59 using namespace mozilla::dom::indexedDB;
     60 using namespace mozilla::dom::quota;
     61 using namespace mozilla::ipc;
     62 using namespace mozilla::services;
     63 
     64 namespace {
     65 
     66 const char kCycleCollectionObserverTopic[] = "cycle-collector-end";
     67 const char kMemoryPressureObserverTopic[] = "memory-pressure";
     68 const char kWindowObserverTopic[] = "inner-window-destroyed";
     69 
     70 class CancelableRunnableWrapper final : public CancelableRunnable {
     71  nsCOMPtr<nsIRunnable> mRunnable;
     72 
     73 public:
     74  explicit CancelableRunnableWrapper(nsCOMPtr<nsIRunnable> aRunnable)
     75      : CancelableRunnable("dom::CancelableRunnableWrapper"),
     76        mRunnable(std::move(aRunnable)) {
     77    MOZ_ASSERT(mRunnable);
     78  }
     79 
     80 private:
     81  ~CancelableRunnableWrapper() = default;
     82 
     83  NS_DECL_NSIRUNNABLE
     84  nsresult Cancel() override;
     85 };
     86 
     87 class DatabaseFile final : public PBackgroundIDBDatabaseFileChild {
     88  IDBDatabase* mDatabase;
     89 
     90 public:
     91  explicit DatabaseFile(IDBDatabase* aDatabase) : mDatabase(aDatabase) {
     92    MOZ_ASSERT(aDatabase);
     93    aDatabase->AssertIsOnOwningThread();
     94 
     95    MOZ_COUNT_CTOR(DatabaseFile);
     96  }
     97 
     98 private:
     99  ~DatabaseFile() {
    100    MOZ_ASSERT(!mDatabase);
    101 
    102    MOZ_COUNT_DTOR(DatabaseFile);
    103  }
    104 
    105  virtual void ActorDestroy(ActorDestroyReason aWhy) override {
    106    MOZ_ASSERT(mDatabase);
    107    mDatabase->AssertIsOnOwningThread();
    108 
    109    if (aWhy != Deletion) {
    110      RefPtr<IDBDatabase> database = mDatabase;
    111      database->NoteFinishedFileActor(this);
    112    }
    113 
    114 #ifdef DEBUG
    115    mDatabase = nullptr;
    116 #endif
    117  }
    118 };
    119 
    120 }  // namespace
    121 
    122 class IDBDatabase::Observer final : public nsIObserver {
    123  IDBDatabase* mWeakDatabase;
    124  const uint64_t mWindowId;
    125 
    126 public:
    127  Observer(IDBDatabase* aDatabase, uint64_t aWindowId)
    128      : mWeakDatabase(aDatabase), mWindowId(aWindowId) {
    129    MOZ_ASSERT(NS_IsMainThread());
    130    MOZ_ASSERT(aDatabase);
    131  }
    132 
    133  void Revoke() {
    134    MOZ_ASSERT(NS_IsMainThread());
    135 
    136    mWeakDatabase = nullptr;
    137  }
    138 
    139  NS_DECL_ISUPPORTS
    140 
    141 private:
    142  ~Observer() {
    143    MOZ_ASSERT(NS_IsMainThread());
    144    MOZ_ASSERT(!mWeakDatabase);
    145  }
    146 
    147  NS_DECL_NSIOBSERVER
    148 };
    149 
    150 IDBDatabase::IDBDatabase(IDBOpenDBRequest* aRequest,
    151                         SafeRefPtr<IDBFactory> aFactory,
    152                         BackgroundDatabaseChild* aActor,
    153                         UniquePtr<DatabaseSpec> aSpec)
    154    : DOMEventTargetHelper(aRequest),
    155      mFactory(std::move(aFactory)),
    156      mSpec(std::move(aSpec)),
    157      mBackgroundActor(aActor),
    158      mClosed(false),
    159      mInvalidated(false),
    160      mQuotaExceeded(false),
    161      mIncreasedActiveDatabaseCount(false) {
    162  MOZ_ASSERT(aRequest);
    163  MOZ_ASSERT(mFactory);
    164  mFactory->AssertIsOnOwningThread();
    165  MOZ_ASSERT(aActor);
    166  MOZ_ASSERT(mSpec);
    167 }
    168 
    169 IDBDatabase::~IDBDatabase() {
    170  AssertIsOnOwningThread();
    171  MOZ_ASSERT(!mBackgroundActor);
    172  MOZ_ASSERT(!mIncreasedActiveDatabaseCount);
    173 }
    174 
    175 // static
    176 RefPtr<IDBDatabase> IDBDatabase::Create(IDBOpenDBRequest* aRequest,
    177                                        SafeRefPtr<IDBFactory> aFactory,
    178                                        BackgroundDatabaseChild* aActor,
    179                                        UniquePtr<DatabaseSpec> aSpec) {
    180  MOZ_ASSERT(aRequest);
    181  MOZ_ASSERT(aFactory);
    182  aFactory->AssertIsOnOwningThread();
    183  MOZ_ASSERT(aActor);
    184  MOZ_ASSERT(aSpec);
    185 
    186  RefPtr<IDBDatabase> db =
    187      new IDBDatabase(aRequest, aFactory.clonePtr(), aActor, std::move(aSpec));
    188 
    189  if (nsCOMPtr<nsPIDOMWindowInner> window = aFactory->GetOwnerWindow()) {
    190    MOZ_ASSERT(NS_IsMainThread());
    191    uint64_t windowId = window->WindowID();
    192 
    193    RefPtr<Observer> observer = new Observer(db, windowId);
    194 
    195    nsCOMPtr<nsIObserverService> obsSvc = GetObserverService();
    196    MOZ_ASSERT(obsSvc);
    197 
    198    // This topic must be successfully registered.
    199    MOZ_ALWAYS_SUCCEEDS(
    200        obsSvc->AddObserver(observer, kWindowObserverTopic, false));
    201 
    202    // These topics are not crucial.
    203    QM_WARNONLY_TRY(QM_TO_RESULT(
    204        obsSvc->AddObserver(observer, kCycleCollectionObserverTopic, false)));
    205    QM_WARNONLY_TRY(QM_TO_RESULT(
    206        obsSvc->AddObserver(observer, kMemoryPressureObserverTopic, false)));
    207 
    208    db->mObserver = std::move(observer);
    209  }
    210 
    211  db->IncreaseActiveDatabaseCount();
    212 
    213  return db;
    214 }
    215 
    216 #ifdef DEBUG
    217 
    218 void IDBDatabase::AssertIsOnOwningThread() const {
    219  MOZ_ASSERT(mFactory);
    220  mFactory->AssertIsOnOwningThread();
    221 }
    222 
    223 #endif  // DEBUG
    224 
    225 nsIEventTarget* IDBDatabase::EventTarget() const {
    226  AssertIsOnOwningThread();
    227  return mFactory->EventTarget();
    228 }
    229 
    230 void IDBDatabase::CloseInternal() {
    231  AssertIsOnOwningThread();
    232 
    233  if (!mClosed) {
    234    mClosed = true;
    235 
    236    ExpireFileActors(/* aExpireAll */ true);
    237 
    238    if (mObserver) {
    239      mObserver->Revoke();
    240 
    241      nsCOMPtr<nsIObserverService> obsSvc = GetObserverService();
    242      if (obsSvc) {
    243        // These might not have been registered.
    244        obsSvc->RemoveObserver(mObserver, kCycleCollectionObserverTopic);
    245        obsSvc->RemoveObserver(mObserver, kMemoryPressureObserverTopic);
    246 
    247        MOZ_ALWAYS_SUCCEEDS(
    248            obsSvc->RemoveObserver(mObserver, kWindowObserverTopic));
    249      }
    250 
    251      mObserver = nullptr;
    252    }
    253 
    254    if (mBackgroundActor && !mInvalidated) {
    255      mBackgroundActor->SendClose();
    256    }
    257 
    258    // Decrease the number of active databases right after the database is
    259    // closed.
    260    MaybeDecreaseActiveDatabaseCount();
    261  }
    262 }
    263 
    264 void IDBDatabase::InvalidateInternal() {
    265  AssertIsOnOwningThread();
    266 
    267  AbortTransactions(/* aShouldWarn */ true);
    268 
    269  CloseInternal();
    270 }
    271 
    272 void IDBDatabase::EnterSetVersionTransaction(uint64_t aNewVersion) {
    273  AssertIsOnOwningThread();
    274  MOZ_ASSERT(aNewVersion);
    275  MOZ_ASSERT(!RunningVersionChangeTransaction());
    276  MOZ_ASSERT(mSpec);
    277  MOZ_ASSERT(!mPreviousSpec);
    278 
    279  mPreviousSpec = MakeUnique<DatabaseSpec>(*mSpec);
    280 
    281  mSpec->metadata().version() = aNewVersion;
    282 }
    283 
    284 void IDBDatabase::ExitSetVersionTransaction() {
    285  AssertIsOnOwningThread();
    286 
    287  if (mPreviousSpec) {
    288    mPreviousSpec = nullptr;
    289  }
    290 }
    291 
    292 void IDBDatabase::RevertToPreviousState() {
    293  AssertIsOnOwningThread();
    294  MOZ_ASSERT(RunningVersionChangeTransaction());
    295  MOZ_ASSERT(mPreviousSpec);
    296 
    297  // Hold the current spec alive until RefreshTransactionsSpecEnumerator has
    298  // finished!
    299  auto currentSpec = std::move(mSpec);
    300 
    301  mSpec = std::move(mPreviousSpec);
    302 
    303  RefreshSpec(/* aMayDelete */ true);
    304 }
    305 
    306 void IDBDatabase::RefreshSpec(bool aMayDelete) {
    307  AssertIsOnOwningThread();
    308 
    309  for (auto* weakTransaction : mTransactions) {
    310    const auto transaction =
    311        SafeRefPtr{weakTransaction, AcquireStrongRefFromRawPtr{}};
    312    MOZ_ASSERT(transaction);
    313    transaction->AssertIsOnOwningThread();
    314    transaction->RefreshSpec(aMayDelete);
    315  }
    316 }
    317 
    318 const nsString& IDBDatabase::Name() const {
    319  AssertIsOnOwningThread();
    320  MOZ_ASSERT(mSpec);
    321 
    322  return mSpec->metadata().name();
    323 }
    324 
    325 uint64_t IDBDatabase::Version() const {
    326  AssertIsOnOwningThread();
    327  MOZ_ASSERT(mSpec);
    328 
    329  return mSpec->metadata().version();
    330 }
    331 
    332 RefPtr<DOMStringList> IDBDatabase::ObjectStoreNames() const {
    333  AssertIsOnOwningThread();
    334  MOZ_ASSERT(mSpec);
    335 
    336  return CreateSortedDOMStringList(
    337      mSpec->objectStores(),
    338      [](const auto& objectStore) { return objectStore.metadata().name(); });
    339 }
    340 
    341 RefPtr<Document> IDBDatabase::GetOwnerDocument() const {
    342  if (nsPIDOMWindowInner* window = GetOwnerWindow()) {
    343    return window->GetExtantDoc();
    344  }
    345  return nullptr;
    346 }
    347 
    348 RefPtr<IDBObjectStore> IDBDatabase::CreateObjectStore(
    349    const nsAString& aName, const IDBObjectStoreParameters& aOptionalParameters,
    350    ErrorResult& aRv) {
    351  AssertIsOnOwningThread();
    352 
    353  const auto transaction = IDBTransaction::MaybeCurrent();
    354  if (!transaction || transaction->Database() != this ||
    355      transaction->GetMode() != IDBTransaction::Mode::VersionChange) {
    356    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
    357    return nullptr;
    358  }
    359 
    360  if (!transaction->IsActive()) {
    361    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
    362    return nullptr;
    363  }
    364 
    365  QM_INFOONLY_TRY_UNWRAP(const auto maybeKeyPath,
    366                         KeyPath::Parse(aOptionalParameters.mKeyPath));
    367  if (!maybeKeyPath) {
    368    aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
    369    return nullptr;
    370  }
    371 
    372  const auto& keyPath = maybeKeyPath.ref();
    373 
    374  auto& objectStores = mSpec->objectStores();
    375  const auto end = objectStores.cend();
    376  const auto foundIt = std::find_if(
    377      objectStores.cbegin(), end, [&aName](const auto& objectStore) {
    378        return aName == objectStore.metadata().name();
    379      });
    380  if (foundIt != end) {
    381    aRv.ThrowConstraintError(nsPrintfCString(
    382        "Object store named '%s' already exists at index '%zu'",
    383        NS_ConvertUTF16toUTF8(aName).get(), foundIt.GetIndex()));
    384    return nullptr;
    385  }
    386 
    387  if (!keyPath.IsAllowedForObjectStore(aOptionalParameters.mAutoIncrement)) {
    388    aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
    389    return nullptr;
    390  }
    391 
    392  const ObjectStoreSpec* oldSpecElements =
    393      objectStores.IsEmpty() ? nullptr : objectStores.Elements();
    394 
    395  ObjectStoreSpec* newSpec = objectStores.AppendElement();
    396  newSpec->metadata() =
    397      ObjectStoreMetadata(transaction->NextObjectStoreId(), nsString(aName),
    398                          keyPath, aOptionalParameters.mAutoIncrement);
    399 
    400  if (oldSpecElements && oldSpecElements != objectStores.Elements()) {
    401    MOZ_ASSERT(objectStores.Length() > 1);
    402 
    403    // Array got moved, update the spec pointers for all live objectStores and
    404    // indexes.
    405    RefreshSpec(/* aMayDelete */ false);
    406  }
    407 
    408  auto objectStore = transaction->CreateObjectStore(*newSpec);
    409  MOZ_ASSERT(objectStore);
    410 
    411  // Don't do this in the macro because we always need to increment the serial
    412  // number to keep in sync with the parent.
    413  const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
    414 
    415  IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
    416      "database(%s).transaction(%s).createObjectStore(%s)",
    417      "IDBDatabase.createObjectStore(%.0s%.0s%.0s)",
    418      transaction->LoggingSerialNumber(), requestSerialNumber,
    419      IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(*transaction),
    420      IDB_LOG_STRINGIFY(objectStore));
    421 
    422  return objectStore;
    423 }
    424 
    425 void IDBDatabase::DeleteObjectStore(const nsAString& aName, ErrorResult& aRv) {
    426  AssertIsOnOwningThread();
    427 
    428  const auto transaction = IDBTransaction::MaybeCurrent();
    429  if (!transaction || transaction->Database() != this ||
    430      transaction->GetMode() != IDBTransaction::Mode::VersionChange) {
    431    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
    432    return;
    433  }
    434 
    435  if (!transaction->IsActive()) {
    436    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
    437    return;
    438  }
    439 
    440  auto& specArray = mSpec->objectStores();
    441  const auto end = specArray.end();
    442  const auto foundIt =
    443      std::find_if(specArray.begin(), end, [&aName](const auto& objectStore) {
    444        const ObjectStoreMetadata& metadata = objectStore.metadata();
    445        MOZ_ASSERT(metadata.id());
    446 
    447        return aName == metadata.name();
    448      });
    449 
    450  if (foundIt == end) {
    451    aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR);
    452    return;
    453  }
    454 
    455  // Must do this before altering the metadata array!
    456  transaction->DeleteObjectStore(foundIt->metadata().id());
    457 
    458  specArray.RemoveElementAt(foundIt);
    459  RefreshSpec(/* aMayDelete */ false);
    460 
    461  // Don't do this in the macro because we always need to increment the serial
    462  // number to keep in sync with the parent.
    463  const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
    464 
    465  IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
    466      "database(%s).transaction(%s).deleteObjectStore(\"%s\")",
    467      "IDBDatabase.deleteObjectStore(%.0s%.0s%.0s)",
    468      transaction->LoggingSerialNumber(), requestSerialNumber,
    469      IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(*transaction),
    470      NS_ConvertUTF16toUTF8(aName).get());
    471 }
    472 
    473 RefPtr<IDBTransaction> IDBDatabase::Transaction(
    474    JSContext* aCx, const StringOrStringSequence& aStoreNames,
    475    IDBTransactionMode aMode, const IDBTransactionOptions& aOptions,
    476    ErrorResult& aRv) {
    477  AssertIsOnOwningThread();
    478 
    479  if ((aMode == IDBTransactionMode::Readwriteflush ||
    480       aMode == IDBTransactionMode::Cleanup) &&
    481      !StaticPrefs::dom_indexedDB_experimental()) {
    482    // Pretend that this mode doesn't exist. We don't have a way to annotate
    483    // certain enum values as depending on preferences so we just duplicate the
    484    // normal exception generation here.
    485    aRv.ThrowTypeError<MSG_INVALID_ENUM_VALUE>("argument 2", "readwriteflush",
    486                                               "IDBTransactionMode");
    487    return nullptr;
    488  }
    489 
    490  if (QuotaManager::IsShuttingDown()) {
    491    IDB_REPORT_INTERNAL_ERR();
    492    aRv.ThrowUnknownError("Can't start IndexedDB transaction during shutdown");
    493    return nullptr;
    494  }
    495 
    496  // https://w3c.github.io/IndexedDB/#dom-idbdatabase-transaction
    497  // Step 1.
    498  if (RunningVersionChangeTransaction()) {
    499    aRv.ThrowInvalidStateError(
    500        "Can't start a transaction while running an upgrade transaction");
    501    return nullptr;
    502  }
    503 
    504  // Step 2.
    505  if (mClosed) {
    506    aRv.ThrowInvalidStateError(
    507        "Can't start a transaction on a closed database");
    508    return nullptr;
    509  }
    510 
    511  // Step 3.
    512  AutoTArray<nsString, 1> stackSequence;
    513 
    514  if (aStoreNames.IsString()) {
    515    stackSequence.AppendElement(aStoreNames.GetAsString());
    516  } else {
    517    MOZ_ASSERT(aStoreNames.IsStringSequence());
    518    // Step 5, but it can be done before step 4 because those steps
    519    // can't both throw.
    520    if (aStoreNames.GetAsStringSequence().IsEmpty()) {
    521      aRv.ThrowInvalidAccessError("Empty scope passed in");
    522      return nullptr;
    523    }
    524  }
    525 
    526  // Step 4.
    527  const nsTArray<nsString>& storeNames =
    528      aStoreNames.IsString() ? stackSequence
    529                             : static_cast<const nsTArray<nsString>&>(
    530                                   aStoreNames.GetAsStringSequence());
    531  MOZ_ASSERT(!storeNames.IsEmpty());
    532 
    533  const nsTArray<ObjectStoreSpec>& objectStores = mSpec->objectStores();
    534  const uint32_t nameCount = storeNames.Length();
    535 
    536  nsTArray<nsString> sortedStoreNames;
    537  sortedStoreNames.SetCapacity(nameCount);
    538 
    539  // While collecting object store names, check if the corresponding object
    540  // stores actually exist.
    541  const auto begin = objectStores.cbegin();
    542  const auto end = objectStores.cend();
    543  for (const auto& name : storeNames) {
    544    const auto foundIt =
    545        std::find_if(begin, end, [&name](const auto& objectStore) {
    546          return objectStore.metadata().name() == name;
    547        });
    548    if (foundIt == end) {
    549      // Not using nsPrintfCString in case "name" has embedded nulls.
    550      aRv.ThrowNotFoundError("'"_ns + NS_ConvertUTF16toUTF8(name) +
    551                             "' is not a known object store name"_ns);
    552      return nullptr;
    553    }
    554 
    555    sortedStoreNames.EmplaceBack(name);
    556  }
    557  sortedStoreNames.Sort();
    558 
    559  // Remove any duplicates.
    560  sortedStoreNames.SetLength(
    561      std::unique(sortedStoreNames.begin(), sortedStoreNames.end()).GetIndex());
    562 
    563  IDBTransaction::Mode mode;
    564  switch (aMode) {
    565    case IDBTransactionMode::Readonly:
    566      mode = IDBTransaction::Mode::ReadOnly;
    567      break;
    568    case IDBTransactionMode::Readwrite:
    569      if (mQuotaExceeded) {
    570        mode = IDBTransaction::Mode::Cleanup;
    571        mQuotaExceeded = false;
    572      } else {
    573        mode = IDBTransaction::Mode::ReadWrite;
    574      }
    575      break;
    576    case IDBTransactionMode::Readwriteflush:
    577      mode = IDBTransaction::Mode::ReadWriteFlush;
    578      break;
    579    case IDBTransactionMode::Cleanup:
    580      mode = IDBTransaction::Mode::Cleanup;
    581      mQuotaExceeded = false;
    582      break;
    583    case IDBTransactionMode::Versionchange:
    584      // Step 6.
    585      aRv.ThrowTypeError("Invalid transaction mode");
    586      return nullptr;
    587 
    588    default:
    589      MOZ_CRASH("Unknown mode!");
    590  }
    591 
    592  auto durability = IDBTransaction::Durability::Default;
    593  if (aOptions.IsAnyMemberPresent()) {
    594    switch (aOptions.mDurability) {
    595      case mozilla::dom::IDBTransactionDurability::Default:
    596        durability = IDBTransaction::Durability::Default;
    597        break;
    598      case mozilla::dom::IDBTransactionDurability::Strict:
    599        durability = IDBTransaction::Durability::Strict;
    600        break;
    601      case mozilla::dom::IDBTransactionDurability::Relaxed:
    602        durability = IDBTransaction::Durability::Relaxed;
    603        break;
    604 
    605      default:
    606        MOZ_CRASH("Unknown durability hint!");
    607    }
    608  }
    609 
    610  SafeRefPtr<IDBTransaction> transaction =
    611      IDBTransaction::Create(aCx, this, sortedStoreNames, mode, durability);
    612  if (NS_WARN_IF(!transaction)) {
    613    IDB_REPORT_INTERNAL_ERR();
    614    MOZ_ASSERT(!NS_IsMainThread(),
    615               "Transaction creation can only fail on workers");
    616    aRv.ThrowUnknownError("Failed to create IndexedDB transaction on worker");
    617    return nullptr;
    618  }
    619 
    620  RefPtr<BackgroundTransactionChild> actor =
    621      new BackgroundTransactionChild(transaction.clonePtr());
    622 
    623  IDB_LOG_MARK_CHILD_TRANSACTION(
    624      "database(%s).transaction(%s)", "IDBDatabase.transaction(%.0s%.0s)",
    625      transaction->LoggingSerialNumber(), IDB_LOG_STRINGIFY(this),
    626      IDB_LOG_STRINGIFY(*transaction));
    627 
    628  if (!mBackgroundActor->SendPBackgroundIDBTransactionConstructor(
    629          actor, sortedStoreNames, mode, durability)) {
    630    IDB_REPORT_INTERNAL_ERR();
    631    aRv.ThrowUnknownError("Failed to create IndexedDB transaction");
    632    return nullptr;
    633  }
    634 
    635  transaction->SetBackgroundActor(actor);
    636 
    637  if (mode == IDBTransaction::Mode::Cleanup) {
    638    ExpireFileActors(/* aExpireAll */ true);
    639  }
    640 
    641  return AsRefPtr(std::move(transaction));
    642 }
    643 
    644 void IDBDatabase::RegisterTransaction(IDBTransaction& aTransaction) {
    645  AssertIsOnOwningThread();
    646  aTransaction.AssertIsOnOwningThread();
    647  MOZ_ASSERT(!mTransactions.Contains(&aTransaction));
    648 
    649  mTransactions.Insert(&aTransaction);
    650 }
    651 
    652 void IDBDatabase::UnregisterTransaction(IDBTransaction& aTransaction) {
    653  AssertIsOnOwningThread();
    654  aTransaction.AssertIsOnOwningThread();
    655  MOZ_ASSERT(mTransactions.Contains(&aTransaction));
    656 
    657  mTransactions.Remove(&aTransaction);
    658 }
    659 
    660 void IDBDatabase::AbortTransactions(bool aShouldWarn) {
    661  AssertIsOnOwningThread();
    662 
    663  constexpr size_t StackExceptionLimit = 20;
    664  using StrongTransactionArray =
    665      AutoTArray<SafeRefPtr<IDBTransaction>, StackExceptionLimit>;
    666  using WeakTransactionArray = AutoTArray<IDBTransaction*, StackExceptionLimit>;
    667 
    668  if (!mTransactions.Count()) {
    669    // Return early as an optimization, the remainder is a no-op in this
    670    // case.
    671    return;
    672  }
    673 
    674  // XXX TransformIfIntoNewArray might be generalized to allow specifying the
    675  // type of nsTArray to create, so that it can create an AutoTArray as well; an
    676  // TransformIf (without AbortOnErr) might be added, which could be used here.
    677  StrongTransactionArray transactionsToAbort;
    678  transactionsToAbort.SetCapacity(mTransactions.Count());
    679 
    680  for (IDBTransaction* const transaction : mTransactions) {
    681    MOZ_ASSERT(transaction);
    682 
    683    transaction->AssertIsOnOwningThread();
    684 
    685    // Transactions that are already done can simply be ignored. Otherwise
    686    // there is a race here and it's possible that the transaction has not
    687    // been successfully committed yet so we will warn the user.
    688    if (!transaction->IsFinished()) {
    689      transactionsToAbort.EmplaceBack(transaction,
    690                                      AcquireStrongRefFromRawPtr{});
    691    }
    692  }
    693  MOZ_ASSERT(transactionsToAbort.Length() <= mTransactions.Count());
    694 
    695  if (transactionsToAbort.IsEmpty()) {
    696    // Return early as an optimization, the remainder is a no-op in this
    697    // case.
    698    return;
    699  }
    700 
    701  // We want to abort transactions as soon as possible so we iterate the
    702  // transactions once and abort them all first, collecting the transactions
    703  // that need to have a warning issued along the way. Those that need a
    704  // warning will be a subset of those that are aborted, so we don't need
    705  // additional strong references here.
    706  WeakTransactionArray transactionsThatNeedWarning;
    707 
    708  for (const auto& transaction : transactionsToAbort) {
    709    MOZ_ASSERT(transaction);
    710    MOZ_ASSERT(!transaction->IsFinished());
    711 
    712    // We warn for any transactions that could have written data, but
    713    // ignore read-only transactions.
    714    if (aShouldWarn && transaction->IsWriteAllowed()) {
    715      transactionsThatNeedWarning.AppendElement(transaction.unsafeGetRawPtr());
    716    }
    717 
    718    transaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
    719  }
    720 
    721  static const char kWarningMessage[] = "IndexedDBTransactionAbortNavigation";
    722 
    723  for (IDBTransaction* transaction : transactionsThatNeedWarning) {
    724    MOZ_ASSERT(transaction);
    725    LogWarning(kWarningMessage, transaction->GetCallerLocation());
    726  }
    727 }
    728 
    729 PBackgroundIDBDatabaseFileChild* IDBDatabase::GetOrCreateFileActorForBlob(
    730    Blob& aBlob) {
    731  AssertIsOnOwningThread();
    732  MOZ_ASSERT(mBackgroundActor);
    733 
    734  // We use the File's nsIWeakReference as the key to the table because
    735  // a) it is unique per blob, b) it is reference-counted so that we can
    736  // guarantee that it stays alive, and c) it doesn't hold the actual File
    737  // alive.
    738  nsWeakPtr weakRef = do_GetWeakReference(&aBlob);
    739  MOZ_ASSERT(weakRef);
    740 
    741  PBackgroundIDBDatabaseFileChild* actor = nullptr;
    742 
    743  if (!mFileActors.Get(weakRef, &actor)) {
    744    BlobImpl* blobImpl = aBlob.Impl();
    745    MOZ_ASSERT(blobImpl);
    746 
    747    IPCBlob ipcBlob;
    748    nsresult rv = IPCBlobUtils::Serialize(blobImpl, ipcBlob);
    749    if (NS_WARN_IF(NS_FAILED(rv))) {
    750      return nullptr;
    751    }
    752 
    753    auto* dbFile = new DatabaseFile(this);
    754 
    755    actor = mBackgroundActor->SendPBackgroundIDBDatabaseFileConstructor(
    756        dbFile, ipcBlob);
    757    if (NS_WARN_IF(!actor)) {
    758      return nullptr;
    759    }
    760 
    761    mFileActors.InsertOrUpdate(weakRef, actor);
    762  }
    763 
    764  MOZ_ASSERT(actor);
    765 
    766  return actor;
    767 }
    768 
    769 void IDBDatabase::NoteFinishedFileActor(
    770    PBackgroundIDBDatabaseFileChild* aFileActor) {
    771  AssertIsOnOwningThread();
    772  MOZ_ASSERT(aFileActor);
    773 
    774  mFileActors.RemoveIf([aFileActor](const auto& iter) {
    775    MOZ_ASSERT(iter.Key());
    776    PBackgroundIDBDatabaseFileChild* actor = iter.Data();
    777    MOZ_ASSERT(actor);
    778 
    779    return actor == aFileActor;
    780  });
    781 }
    782 
    783 void IDBDatabase::NoteActiveTransaction() {
    784  AssertIsOnOwningThread();
    785  MOZ_ASSERT(mFactory);
    786 
    787  // Increase the number of active transactions.
    788  mFactory->UpdateActiveTransactionCount(1);
    789 }
    790 
    791 void IDBDatabase::NoteInactiveTransaction() {
    792  AssertIsOnOwningThread();
    793 
    794  if (!mBackgroundActor || !mFileActors.Count()) {
    795    MOZ_ASSERT(mFactory);
    796    mFactory->UpdateActiveTransactionCount(-1);
    797    return;
    798  }
    799 
    800  RefPtr<Runnable> runnable =
    801      NewRunnableMethod("IDBDatabase::NoteInactiveTransactionDelayed", this,
    802                        &IDBDatabase::NoteInactiveTransactionDelayed);
    803  MOZ_ASSERT(runnable);
    804 
    805  if (!NS_IsMainThread()) {
    806    // Wrap as a nsICancelableRunnable to make workers happy.
    807    runnable = MakeRefPtr<CancelableRunnableWrapper>(runnable.forget());
    808  }
    809 
    810  MOZ_ALWAYS_SUCCEEDS(
    811      EventTarget()->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL));
    812 }
    813 
    814 void IDBDatabase::ExpireFileActors(bool aExpireAll) {
    815  AssertIsOnOwningThread();
    816 
    817  if (mBackgroundActor && mFileActors.Count()) {
    818    for (auto iter = mFileActors.Iter(); !iter.Done(); iter.Next()) {
    819      nsISupports* key = iter.Key();
    820      PBackgroundIDBDatabaseFileChild* actor = iter.Data();
    821      MOZ_ASSERT(key);
    822      MOZ_ASSERT(actor);
    823 
    824      bool shouldExpire = aExpireAll;
    825      if (!shouldExpire) {
    826        nsWeakPtr weakRef = do_QueryInterface(key);
    827        MOZ_ASSERT(weakRef);
    828 
    829        nsCOMPtr<nsISupports> referent = do_QueryReferent(weakRef);
    830        shouldExpire = !referent;
    831      }
    832 
    833      if (shouldExpire) {
    834        PBackgroundIDBDatabaseFileChild::Send__delete__(actor);
    835 
    836        if (!aExpireAll) {
    837          iter.Remove();
    838        }
    839      }
    840    }
    841    if (aExpireAll) {
    842      mFileActors.Clear();
    843    }
    844  } else {
    845    MOZ_ASSERT(!mFileActors.Count());
    846  }
    847 }
    848 
    849 void IDBDatabase::Invalidate() {
    850  AssertIsOnOwningThread();
    851 
    852  if (!mInvalidated) {
    853    mInvalidated = true;
    854 
    855    InvalidateInternal();
    856  }
    857 }
    858 
    859 void IDBDatabase::NoteInactiveTransactionDelayed() {
    860  ExpireFileActors(/* aExpireAll */ false);
    861 
    862  MOZ_ASSERT(mFactory);
    863  mFactory->UpdateActiveTransactionCount(-1);
    864 }
    865 
    866 void IDBDatabase::LogWarning(const char* aMessageName,
    867                             const JSCallingLocation& aLoc) {
    868  AssertIsOnOwningThread();
    869  MOZ_ASSERT(aMessageName);
    870 
    871  ScriptErrorHelper::DumpLocalizedMessage(
    872      nsDependentCString(aMessageName), aLoc, nsIScriptError::warningFlag,
    873      mFactory->IsChrome(), mFactory->InnerWindowID());
    874 }
    875 
    876 NS_IMPL_ADDREF_INHERITED(IDBDatabase, DOMEventTargetHelper)
    877 NS_IMPL_RELEASE_INHERITED(IDBDatabase, DOMEventTargetHelper)
    878 
    879 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBDatabase)
    880 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
    881 
    882 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBDatabase)
    883 
    884 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBDatabase,
    885                                                  DOMEventTargetHelper)
    886  tmp->AssertIsOnOwningThread();
    887  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFactory)
    888 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    889 
    890 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBDatabase,
    891                                                DOMEventTargetHelper)
    892  tmp->AssertIsOnOwningThread();
    893 
    894  // Don't unlink mFactory!
    895 
    896  // We've been unlinked, at the very least we should be able to prevent further
    897  // transactions from starting and unblock any other SetVersion callers.
    898  tmp->CloseInternal();
    899 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    900 
    901 void IDBDatabase::DisconnectFromOwner() {
    902  InvalidateInternal();
    903  DOMEventTargetHelper::DisconnectFromOwner();
    904 }
    905 
    906 void IDBDatabase::LastRelease() {
    907  AssertIsOnOwningThread();
    908 
    909  CloseInternal();
    910 
    911  ExpireFileActors(/* aExpireAll */ true);
    912 
    913  if (mBackgroundActor) {
    914    mBackgroundActor->SendDeleteMeInternal();
    915    MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!");
    916  }
    917 }
    918 
    919 JSObject* IDBDatabase::WrapObject(JSContext* aCx,
    920                                  JS::Handle<JSObject*> aGivenProto) {
    921  return IDBDatabase_Binding::Wrap(aCx, this, aGivenProto);
    922 }
    923 
    924 NS_IMETHODIMP
    925 CancelableRunnableWrapper::Run() {
    926  const nsCOMPtr<nsIRunnable> runnable = std::move(mRunnable);
    927 
    928  if (runnable) {
    929    return runnable->Run();
    930  }
    931 
    932  return NS_OK;
    933 }
    934 
    935 nsresult CancelableRunnableWrapper::Cancel() {
    936  if (mRunnable) {
    937    mRunnable = nullptr;
    938    return NS_OK;
    939  }
    940 
    941  return NS_ERROR_UNEXPECTED;
    942 }
    943 
    944 NS_IMPL_ISUPPORTS(IDBDatabase::Observer, nsIObserver)
    945 
    946 NS_IMETHODIMP
    947 IDBDatabase::Observer::Observe(nsISupports* aSubject, const char* aTopic,
    948                               const char16_t* aData) {
    949  MOZ_ASSERT(NS_IsMainThread());
    950  MOZ_ASSERT(aTopic);
    951 
    952  if (!strcmp(aTopic, kWindowObserverTopic)) {
    953    if (mWeakDatabase) {
    954      nsCOMPtr<nsISupportsPRUint64> supportsInt = do_QueryInterface(aSubject);
    955      MOZ_ASSERT(supportsInt);
    956 
    957      uint64_t windowId;
    958      MOZ_ALWAYS_SUCCEEDS(supportsInt->GetData(&windowId));
    959 
    960      if (windowId == mWindowId) {
    961        RefPtr<IDBDatabase> database = mWeakDatabase;
    962        mWeakDatabase = nullptr;
    963 
    964        database->InvalidateInternal();
    965      }
    966    }
    967 
    968    return NS_OK;
    969  }
    970 
    971  if (!strcmp(aTopic, kCycleCollectionObserverTopic) ||
    972      !strcmp(aTopic, kMemoryPressureObserverTopic)) {
    973    if (mWeakDatabase) {
    974      RefPtr<IDBDatabase> database = mWeakDatabase;
    975 
    976      database->ExpireFileActors(/* aExpireAll */ false);
    977    }
    978 
    979    return NS_OK;
    980  }
    981 
    982  NS_WARNING("Unknown observer topic!");
    983  return NS_OK;
    984 }
    985 
    986 nsresult IDBDatabase::RenameObjectStore(int64_t aObjectStoreId,
    987                                        const nsAString& aName) {
    988  MOZ_ASSERT(mSpec);
    989 
    990  nsTArray<ObjectStoreSpec>& objectStores = mSpec->objectStores();
    991  ObjectStoreSpec* foundObjectStoreSpec = nullptr;
    992 
    993  // Find the matched object store spec and check if 'aName' is already used by
    994  // another object store.
    995 
    996  for (auto& objSpec : objectStores) {
    997    const bool idIsCurrent = objSpec.metadata().id() == aObjectStoreId;
    998 
    999    if (idIsCurrent) {
   1000      MOZ_ASSERT(!foundObjectStoreSpec);
   1001      foundObjectStoreSpec = &objSpec;
   1002    }
   1003 
   1004    if (objSpec.metadata().name() == aName) {
   1005      if (idIsCurrent) {
   1006        return NS_OK;
   1007      }
   1008      return NS_ERROR_DOM_INDEXEDDB_RENAME_OBJECT_STORE_ERR;
   1009    }
   1010  }
   1011 
   1012  MOZ_ASSERT(foundObjectStoreSpec);
   1013 
   1014  // Update the name of the matched object store.
   1015  foundObjectStoreSpec->metadata().name().Assign(aName);
   1016 
   1017  return NS_OK;
   1018 }
   1019 
   1020 nsresult IDBDatabase::RenameIndex(int64_t aObjectStoreId, int64_t aIndexId,
   1021                                  const nsAString& aName) {
   1022  MOZ_ASSERT(mSpec);
   1023 
   1024  nsTArray<ObjectStoreSpec>& objectStores = mSpec->objectStores();
   1025 
   1026  ObjectStoreSpec* foundObjectStoreSpec = nullptr;
   1027  // Find the matched index metadata and check if 'aName' is already used by
   1028  // another index.
   1029  for (uint32_t objCount = objectStores.Length(), objIndex = 0;
   1030       objIndex < objCount; objIndex++) {
   1031    const ObjectStoreSpec& objSpec = objectStores[objIndex];
   1032    if (objSpec.metadata().id() == aObjectStoreId) {
   1033      foundObjectStoreSpec = &objectStores[objIndex];
   1034      break;
   1035    }
   1036  }
   1037 
   1038  MOZ_ASSERT(foundObjectStoreSpec);
   1039 
   1040  nsTArray<IndexMetadata>& indexes = foundObjectStoreSpec->indexes();
   1041  IndexMetadata* foundIndexMetadata = nullptr;
   1042  for (uint32_t idxCount = indexes.Length(), idxIndex = 0; idxIndex < idxCount;
   1043       idxIndex++) {
   1044    const IndexMetadata& metadata = indexes[idxIndex];
   1045    if (metadata.id() == aIndexId) {
   1046      MOZ_ASSERT(!foundIndexMetadata);
   1047      foundIndexMetadata = &indexes[idxIndex];
   1048      continue;
   1049    }
   1050    if (aName == metadata.name()) {
   1051      return NS_ERROR_DOM_INDEXEDDB_RENAME_INDEX_ERR;
   1052    }
   1053  }
   1054 
   1055  MOZ_ASSERT(foundIndexMetadata);
   1056 
   1057  // Update the name of the matched object store.
   1058  foundIndexMetadata->name() = nsString(aName);
   1059 
   1060  return NS_OK;
   1061 }
   1062 
   1063 void IDBDatabase::IncreaseActiveDatabaseCount() {
   1064  AssertIsOnOwningThread();
   1065  MOZ_ASSERT(mFactory);
   1066  MOZ_ASSERT(!mIncreasedActiveDatabaseCount);
   1067 
   1068  mFactory->UpdateActiveDatabaseCount(1);
   1069  mIncreasedActiveDatabaseCount = true;
   1070 }
   1071 
   1072 void IDBDatabase::MaybeDecreaseActiveDatabaseCount() {
   1073  AssertIsOnOwningThread();
   1074 
   1075  if (mIncreasedActiveDatabaseCount) {
   1076    // Decrease the number of active databases.
   1077    MOZ_ASSERT(mFactory);
   1078    mFactory->UpdateActiveDatabaseCount(-1);
   1079    mIncreasedActiveDatabaseCount = false;
   1080  }
   1081 }
   1082 
   1083 }  // namespace mozilla::dom