Bug 1941128 - Turn off network.dns.native_https_query on Mac again
[gecko.git] / dom / indexedDB / IDBDatabase.cpp
blob2ffbddffd1b21f81c37736c76b1b1e72df6e843b
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/. */
7 #include "IDBDatabase.h"
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 "IndexedDatabaseInlines.h"
16 #include "IndexedDatabaseManager.h"
17 #include "IndexedDBCommon.h"
18 #include "mozilla/ErrorResult.h"
19 #include "mozilla/EventDispatcher.h"
20 #include "MainThreadUtils.h"
21 #include "mozilla/ResultExtensions.h"
22 #include "mozilla/Services.h"
23 #include "mozilla/dom/IDBTransactionBinding.h"
24 #include "mozilla/storage.h"
25 #include "mozilla/dom/BindingDeclarations.h"
26 #include "mozilla/dom/DOMStringListBinding.h"
27 #include "mozilla/dom/Exceptions.h"
28 #include "mozilla/dom/File.h"
29 #include "mozilla/dom/IDBDatabaseBinding.h"
30 #include "mozilla/dom/IDBObjectStoreBinding.h"
31 #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseFileChild.h"
32 #include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h"
33 #include "mozilla/dom/IPCBlobUtils.h"
34 #include "mozilla/dom/quota/QuotaManager.h"
35 #include "mozilla/dom/quota/ResultExtensions.h"
36 #include "mozilla/ipc/BackgroundChild.h"
37 #include "mozilla/ipc/BackgroundUtils.h"
38 #include "mozilla/ipc/FileDescriptor.h"
39 #include "mozilla/ipc/InputStreamParams.h"
40 #include "mozilla/ipc/InputStreamUtils.h"
41 #include "nsCOMPtr.h"
42 #include "mozilla/dom/Document.h"
43 #include "nsIObserver.h"
44 #include "nsIObserverService.h"
45 #include "nsIScriptError.h"
46 #include "nsISupportsPrimitives.h"
47 #include "nsThreadUtils.h"
48 #include "nsIWeakReferenceUtils.h"
49 #include "ProfilerHelpers.h"
50 #include "ReportInternalError.h"
51 #include "ScriptErrorHelper.h"
52 #include "nsGlobalWindowInner.h"
53 #include "nsQueryObject.h"
55 // Include this last to avoid path problems on Windows.
56 #include "ActorsChild.h"
58 namespace mozilla::dom {
60 using namespace mozilla::dom::indexedDB;
61 using namespace mozilla::dom::quota;
62 using namespace mozilla::ipc;
63 using namespace mozilla::services;
65 namespace {
67 const char kCycleCollectionObserverTopic[] = "cycle-collector-end";
68 const char kMemoryPressureObserverTopic[] = "memory-pressure";
69 const char kWindowObserverTopic[] = "inner-window-destroyed";
71 class CancelableRunnableWrapper final : public CancelableRunnable {
72 nsCOMPtr<nsIRunnable> mRunnable;
74 public:
75 explicit CancelableRunnableWrapper(nsCOMPtr<nsIRunnable> aRunnable)
76 : CancelableRunnable("dom::CancelableRunnableWrapper"),
77 mRunnable(std::move(aRunnable)) {
78 MOZ_ASSERT(mRunnable);
81 private:
82 ~CancelableRunnableWrapper() = default;
84 NS_DECL_NSIRUNNABLE
85 nsresult Cancel() override;
88 class DatabaseFile final : public PBackgroundIDBDatabaseFileChild {
89 IDBDatabase* mDatabase;
91 public:
92 explicit DatabaseFile(IDBDatabase* aDatabase) : mDatabase(aDatabase) {
93 MOZ_ASSERT(aDatabase);
94 aDatabase->AssertIsOnOwningThread();
96 MOZ_COUNT_CTOR(DatabaseFile);
99 private:
100 ~DatabaseFile() {
101 MOZ_ASSERT(!mDatabase);
103 MOZ_COUNT_DTOR(DatabaseFile);
106 virtual void ActorDestroy(ActorDestroyReason aWhy) override {
107 MOZ_ASSERT(mDatabase);
108 mDatabase->AssertIsOnOwningThread();
110 if (aWhy != Deletion) {
111 RefPtr<IDBDatabase> database = mDatabase;
112 database->NoteFinishedFileActor(this);
115 #ifdef DEBUG
116 mDatabase = nullptr;
117 #endif
121 } // namespace
123 class IDBDatabase::Observer final : public nsIObserver {
124 IDBDatabase* mWeakDatabase;
125 const uint64_t mWindowId;
127 public:
128 Observer(IDBDatabase* aDatabase, uint64_t aWindowId)
129 : mWeakDatabase(aDatabase), mWindowId(aWindowId) {
130 MOZ_ASSERT(NS_IsMainThread());
131 MOZ_ASSERT(aDatabase);
134 void Revoke() {
135 MOZ_ASSERT(NS_IsMainThread());
137 mWeakDatabase = nullptr;
140 NS_DECL_ISUPPORTS
142 private:
143 ~Observer() {
144 MOZ_ASSERT(NS_IsMainThread());
145 MOZ_ASSERT(!mWeakDatabase);
148 NS_DECL_NSIOBSERVER
151 IDBDatabase::IDBDatabase(IDBOpenDBRequest* aRequest,
152 SafeRefPtr<IDBFactory> aFactory,
153 BackgroundDatabaseChild* aActor,
154 UniquePtr<DatabaseSpec> aSpec)
155 : DOMEventTargetHelper(aRequest),
156 mFactory(std::move(aFactory)),
157 mSpec(std::move(aSpec)),
158 mBackgroundActor(aActor),
159 mClosed(false),
160 mInvalidated(false),
161 mQuotaExceeded(false),
162 mIncreasedActiveDatabaseCount(false) {
163 MOZ_ASSERT(aRequest);
164 MOZ_ASSERT(mFactory);
165 mFactory->AssertIsOnOwningThread();
166 MOZ_ASSERT(aActor);
167 MOZ_ASSERT(mSpec);
170 IDBDatabase::~IDBDatabase() {
171 AssertIsOnOwningThread();
172 MOZ_ASSERT(!mBackgroundActor);
173 MOZ_ASSERT(!mIncreasedActiveDatabaseCount);
176 // static
177 RefPtr<IDBDatabase> IDBDatabase::Create(IDBOpenDBRequest* aRequest,
178 SafeRefPtr<IDBFactory> aFactory,
179 BackgroundDatabaseChild* aActor,
180 UniquePtr<DatabaseSpec> aSpec) {
181 MOZ_ASSERT(aRequest);
182 MOZ_ASSERT(aFactory);
183 aFactory->AssertIsOnOwningThread();
184 MOZ_ASSERT(aActor);
185 MOZ_ASSERT(aSpec);
187 RefPtr<IDBDatabase> db =
188 new IDBDatabase(aRequest, aFactory.clonePtr(), aActor, std::move(aSpec));
190 if (nsCOMPtr<nsPIDOMWindowInner> window = aFactory->GetOwnerWindow()) {
191 MOZ_ASSERT(NS_IsMainThread());
192 uint64_t windowId = window->WindowID();
194 RefPtr<Observer> observer = new Observer(db, windowId);
196 nsCOMPtr<nsIObserverService> obsSvc = GetObserverService();
197 MOZ_ASSERT(obsSvc);
199 // This topic must be successfully registered.
200 MOZ_ALWAYS_SUCCEEDS(
201 obsSvc->AddObserver(observer, kWindowObserverTopic, false));
203 // These topics are not crucial.
204 QM_WARNONLY_TRY(QM_TO_RESULT(
205 obsSvc->AddObserver(observer, kCycleCollectionObserverTopic, false)));
206 QM_WARNONLY_TRY(QM_TO_RESULT(
207 obsSvc->AddObserver(observer, kMemoryPressureObserverTopic, false)));
209 db->mObserver = std::move(observer);
212 db->IncreaseActiveDatabaseCount();
214 return db;
217 #ifdef DEBUG
219 void IDBDatabase::AssertIsOnOwningThread() const {
220 MOZ_ASSERT(mFactory);
221 mFactory->AssertIsOnOwningThread();
224 #endif // DEBUG
226 nsIEventTarget* IDBDatabase::EventTarget() const {
227 AssertIsOnOwningThread();
228 return mFactory->EventTarget();
231 void IDBDatabase::CloseInternal() {
232 AssertIsOnOwningThread();
234 if (!mClosed) {
235 mClosed = true;
237 ExpireFileActors(/* aExpireAll */ true);
239 if (mObserver) {
240 mObserver->Revoke();
242 nsCOMPtr<nsIObserverService> obsSvc = GetObserverService();
243 if (obsSvc) {
244 // These might not have been registered.
245 obsSvc->RemoveObserver(mObserver, kCycleCollectionObserverTopic);
246 obsSvc->RemoveObserver(mObserver, kMemoryPressureObserverTopic);
248 MOZ_ALWAYS_SUCCEEDS(
249 obsSvc->RemoveObserver(mObserver, kWindowObserverTopic));
252 mObserver = nullptr;
255 if (mBackgroundActor && !mInvalidated) {
256 mBackgroundActor->SendClose();
259 // Decrease the number of active databases right after the database is
260 // closed.
261 MaybeDecreaseActiveDatabaseCount();
265 void IDBDatabase::InvalidateInternal() {
266 AssertIsOnOwningThread();
268 AbortTransactions(/* aShouldWarn */ true);
270 CloseInternal();
273 void IDBDatabase::EnterSetVersionTransaction(uint64_t aNewVersion) {
274 AssertIsOnOwningThread();
275 MOZ_ASSERT(aNewVersion);
276 MOZ_ASSERT(!RunningVersionChangeTransaction());
277 MOZ_ASSERT(mSpec);
278 MOZ_ASSERT(!mPreviousSpec);
280 mPreviousSpec = MakeUnique<DatabaseSpec>(*mSpec);
282 mSpec->metadata().version() = aNewVersion;
285 void IDBDatabase::ExitSetVersionTransaction() {
286 AssertIsOnOwningThread();
288 if (mPreviousSpec) {
289 mPreviousSpec = nullptr;
293 void IDBDatabase::RevertToPreviousState() {
294 AssertIsOnOwningThread();
295 MOZ_ASSERT(RunningVersionChangeTransaction());
296 MOZ_ASSERT(mPreviousSpec);
298 // Hold the current spec alive until RefreshTransactionsSpecEnumerator has
299 // finished!
300 auto currentSpec = std::move(mSpec);
302 mSpec = std::move(mPreviousSpec);
304 RefreshSpec(/* aMayDelete */ true);
307 void IDBDatabase::RefreshSpec(bool aMayDelete) {
308 AssertIsOnOwningThread();
310 for (auto* weakTransaction : mTransactions) {
311 const auto transaction =
312 SafeRefPtr{weakTransaction, AcquireStrongRefFromRawPtr{}};
313 MOZ_ASSERT(transaction);
314 transaction->AssertIsOnOwningThread();
315 transaction->RefreshSpec(aMayDelete);
319 const nsString& IDBDatabase::Name() const {
320 AssertIsOnOwningThread();
321 MOZ_ASSERT(mSpec);
323 return mSpec->metadata().name();
326 uint64_t IDBDatabase::Version() const {
327 AssertIsOnOwningThread();
328 MOZ_ASSERT(mSpec);
330 return mSpec->metadata().version();
333 RefPtr<DOMStringList> IDBDatabase::ObjectStoreNames() const {
334 AssertIsOnOwningThread();
335 MOZ_ASSERT(mSpec);
337 return CreateSortedDOMStringList(
338 mSpec->objectStores(),
339 [](const auto& objectStore) { return objectStore.metadata().name(); });
342 RefPtr<Document> IDBDatabase::GetOwnerDocument() const {
343 if (nsPIDOMWindowInner* window = GetOwnerWindow()) {
344 return window->GetExtantDoc();
346 return nullptr;
349 RefPtr<IDBObjectStore> IDBDatabase::CreateObjectStore(
350 const nsAString& aName, const IDBObjectStoreParameters& aOptionalParameters,
351 ErrorResult& aRv) {
352 AssertIsOnOwningThread();
354 const auto transaction = IDBTransaction::MaybeCurrent();
355 if (!transaction || transaction->Database() != this ||
356 transaction->GetMode() != IDBTransaction::Mode::VersionChange) {
357 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
358 return nullptr;
361 if (!transaction->IsActive()) {
362 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
363 return nullptr;
366 QM_INFOONLY_TRY_UNWRAP(const auto maybeKeyPath,
367 KeyPath::Parse(aOptionalParameters.mKeyPath));
368 if (!maybeKeyPath) {
369 aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
370 return nullptr;
373 const auto& keyPath = maybeKeyPath.ref();
375 auto& objectStores = mSpec->objectStores();
376 const auto end = objectStores.cend();
377 const auto foundIt = std::find_if(
378 objectStores.cbegin(), end, [&aName](const auto& objectStore) {
379 return aName == objectStore.metadata().name();
381 if (foundIt != end) {
382 aRv.ThrowConstraintError(nsPrintfCString(
383 "Object store named '%s' already exists at index '%zu'",
384 NS_ConvertUTF16toUTF8(aName).get(), foundIt.GetIndex()));
385 return nullptr;
388 if (!keyPath.IsAllowedForObjectStore(aOptionalParameters.mAutoIncrement)) {
389 aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
390 return nullptr;
393 const ObjectStoreSpec* oldSpecElements =
394 objectStores.IsEmpty() ? nullptr : objectStores.Elements();
396 ObjectStoreSpec* newSpec = objectStores.AppendElement();
397 newSpec->metadata() =
398 ObjectStoreMetadata(transaction->NextObjectStoreId(), nsString(aName),
399 keyPath, aOptionalParameters.mAutoIncrement);
401 if (oldSpecElements && oldSpecElements != objectStores.Elements()) {
402 MOZ_ASSERT(objectStores.Length() > 1);
404 // Array got moved, update the spec pointers for all live objectStores and
405 // indexes.
406 RefreshSpec(/* aMayDelete */ false);
409 auto objectStore = transaction->CreateObjectStore(*newSpec);
410 MOZ_ASSERT(objectStore);
412 // Don't do this in the macro because we always need to increment the serial
413 // number to keep in sync with the parent.
414 const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
416 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
417 "database(%s).transaction(%s).createObjectStore(%s)",
418 "IDBDatabase.createObjectStore(%.0s%.0s%.0s)",
419 transaction->LoggingSerialNumber(), requestSerialNumber,
420 IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(*transaction),
421 IDB_LOG_STRINGIFY(objectStore));
423 return objectStore;
426 void IDBDatabase::DeleteObjectStore(const nsAString& aName, ErrorResult& aRv) {
427 AssertIsOnOwningThread();
429 const auto transaction = IDBTransaction::MaybeCurrent();
430 if (!transaction || transaction->Database() != this ||
431 transaction->GetMode() != IDBTransaction::Mode::VersionChange) {
432 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_ALLOWED_ERR);
433 return;
436 if (!transaction->IsActive()) {
437 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR);
438 return;
441 auto& specArray = mSpec->objectStores();
442 const auto end = specArray.end();
443 const auto foundIt =
444 std::find_if(specArray.begin(), end, [&aName](const auto& objectStore) {
445 const ObjectStoreMetadata& metadata = objectStore.metadata();
446 MOZ_ASSERT(metadata.id());
448 return aName == metadata.name();
451 if (foundIt == end) {
452 aRv.Throw(NS_ERROR_DOM_INDEXEDDB_NOT_FOUND_ERR);
453 return;
456 // Must do this before altering the metadata array!
457 transaction->DeleteObjectStore(foundIt->metadata().id());
459 specArray.RemoveElementAt(foundIt);
460 RefreshSpec(/* aMayDelete */ false);
462 // Don't do this in the macro because we always need to increment the serial
463 // number to keep in sync with the parent.
464 const uint64_t requestSerialNumber = IDBRequest::NextSerialNumber();
466 IDB_LOG_MARK_CHILD_TRANSACTION_REQUEST(
467 "database(%s).transaction(%s).deleteObjectStore(\"%s\")",
468 "IDBDatabase.deleteObjectStore(%.0s%.0s%.0s)",
469 transaction->LoggingSerialNumber(), requestSerialNumber,
470 IDB_LOG_STRINGIFY(this), IDB_LOG_STRINGIFY(*transaction),
471 NS_ConvertUTF16toUTF8(aName).get());
474 RefPtr<IDBTransaction> IDBDatabase::Transaction(
475 JSContext* aCx, const StringOrStringSequence& aStoreNames,
476 IDBTransactionMode aMode, const IDBTransactionOptions& aOptions,
477 ErrorResult& aRv) {
478 AssertIsOnOwningThread();
480 if ((aMode == IDBTransactionMode::Readwriteflush ||
481 aMode == IDBTransactionMode::Cleanup) &&
482 !StaticPrefs::dom_indexedDB_experimental()) {
483 // Pretend that this mode doesn't exist. We don't have a way to annotate
484 // certain enum values as depending on preferences so we just duplicate the
485 // normal exception generation here.
486 aRv.ThrowTypeError<MSG_INVALID_ENUM_VALUE>("argument 2", "readwriteflush",
487 "IDBTransactionMode");
488 return nullptr;
491 if (QuotaManager::IsShuttingDown()) {
492 IDB_REPORT_INTERNAL_ERR();
493 aRv.ThrowUnknownError("Can't start IndexedDB transaction during shutdown");
494 return nullptr;
497 // https://w3c.github.io/IndexedDB/#dom-idbdatabase-transaction
498 // Step 1.
499 if (RunningVersionChangeTransaction()) {
500 aRv.ThrowInvalidStateError(
501 "Can't start a transaction while running an upgrade transaction");
502 return nullptr;
505 // Step 2.
506 if (mClosed) {
507 aRv.ThrowInvalidStateError(
508 "Can't start a transaction on a closed database");
509 return nullptr;
512 // Step 3.
513 AutoTArray<nsString, 1> stackSequence;
515 if (aStoreNames.IsString()) {
516 stackSequence.AppendElement(aStoreNames.GetAsString());
517 } else {
518 MOZ_ASSERT(aStoreNames.IsStringSequence());
519 // Step 5, but it can be done before step 4 because those steps
520 // can't both throw.
521 if (aStoreNames.GetAsStringSequence().IsEmpty()) {
522 aRv.ThrowInvalidAccessError("Empty scope passed in");
523 return nullptr;
527 // Step 4.
528 const nsTArray<nsString>& storeNames =
529 aStoreNames.IsString() ? stackSequence
530 : static_cast<const nsTArray<nsString>&>(
531 aStoreNames.GetAsStringSequence());
532 MOZ_ASSERT(!storeNames.IsEmpty());
534 const nsTArray<ObjectStoreSpec>& objectStores = mSpec->objectStores();
535 const uint32_t nameCount = storeNames.Length();
537 nsTArray<nsString> sortedStoreNames;
538 sortedStoreNames.SetCapacity(nameCount);
540 // While collecting object store names, check if the corresponding object
541 // stores actually exist.
542 const auto begin = objectStores.cbegin();
543 const auto end = objectStores.cend();
544 for (const auto& name : storeNames) {
545 const auto foundIt =
546 std::find_if(begin, end, [&name](const auto& objectStore) {
547 return objectStore.metadata().name() == name;
549 if (foundIt == end) {
550 // Not using nsPrintfCString in case "name" has embedded nulls.
551 aRv.ThrowNotFoundError("'"_ns + NS_ConvertUTF16toUTF8(name) +
552 "' is not a known object store name"_ns);
553 return nullptr;
556 sortedStoreNames.EmplaceBack(name);
558 sortedStoreNames.Sort();
560 // Remove any duplicates.
561 sortedStoreNames.SetLength(
562 std::unique(sortedStoreNames.begin(), sortedStoreNames.end()).GetIndex());
564 IDBTransaction::Mode mode;
565 switch (aMode) {
566 case IDBTransactionMode::Readonly:
567 mode = IDBTransaction::Mode::ReadOnly;
568 break;
569 case IDBTransactionMode::Readwrite:
570 if (mQuotaExceeded) {
571 mode = IDBTransaction::Mode::Cleanup;
572 mQuotaExceeded = false;
573 } else {
574 mode = IDBTransaction::Mode::ReadWrite;
576 break;
577 case IDBTransactionMode::Readwriteflush:
578 mode = IDBTransaction::Mode::ReadWriteFlush;
579 break;
580 case IDBTransactionMode::Cleanup:
581 mode = IDBTransaction::Mode::Cleanup;
582 mQuotaExceeded = false;
583 break;
584 case IDBTransactionMode::Versionchange:
585 // Step 6.
586 aRv.ThrowTypeError("Invalid transaction mode");
587 return nullptr;
589 default:
590 MOZ_CRASH("Unknown mode!");
593 auto durability = IDBTransaction::Durability::Default;
594 if (aOptions.IsAnyMemberPresent()) {
595 switch (aOptions.mDurability) {
596 case mozilla::dom::IDBTransactionDurability::Default:
597 durability = IDBTransaction::Durability::Default;
598 break;
599 case mozilla::dom::IDBTransactionDurability::Strict:
600 durability = IDBTransaction::Durability::Strict;
601 break;
602 case mozilla::dom::IDBTransactionDurability::Relaxed:
603 durability = IDBTransaction::Durability::Relaxed;
604 break;
606 default:
607 MOZ_CRASH("Unknown durability hint!");
611 SafeRefPtr<IDBTransaction> transaction =
612 IDBTransaction::Create(aCx, this, sortedStoreNames, mode, durability);
613 if (NS_WARN_IF(!transaction)) {
614 IDB_REPORT_INTERNAL_ERR();
615 MOZ_ASSERT(!NS_IsMainThread(),
616 "Transaction creation can only fail on workers");
617 aRv.ThrowUnknownError("Failed to create IndexedDB transaction on worker");
618 return nullptr;
621 RefPtr<BackgroundTransactionChild> actor =
622 new BackgroundTransactionChild(transaction.clonePtr());
624 IDB_LOG_MARK_CHILD_TRANSACTION(
625 "database(%s).transaction(%s)", "IDBDatabase.transaction(%.0s%.0s)",
626 transaction->LoggingSerialNumber(), IDB_LOG_STRINGIFY(this),
627 IDB_LOG_STRINGIFY(*transaction));
629 if (!mBackgroundActor->SendPBackgroundIDBTransactionConstructor(
630 actor, sortedStoreNames, mode, durability)) {
631 IDB_REPORT_INTERNAL_ERR();
632 aRv.ThrowUnknownError("Failed to create IndexedDB transaction");
633 return nullptr;
636 transaction->SetBackgroundActor(actor);
638 if (mode == IDBTransaction::Mode::Cleanup) {
639 ExpireFileActors(/* aExpireAll */ true);
642 return AsRefPtr(std::move(transaction));
645 void IDBDatabase::RegisterTransaction(IDBTransaction& aTransaction) {
646 AssertIsOnOwningThread();
647 aTransaction.AssertIsOnOwningThread();
648 MOZ_ASSERT(!mTransactions.Contains(&aTransaction));
650 mTransactions.Insert(&aTransaction);
653 void IDBDatabase::UnregisterTransaction(IDBTransaction& aTransaction) {
654 AssertIsOnOwningThread();
655 aTransaction.AssertIsOnOwningThread();
656 MOZ_ASSERT(mTransactions.Contains(&aTransaction));
658 mTransactions.Remove(&aTransaction);
661 void IDBDatabase::AbortTransactions(bool aShouldWarn) {
662 AssertIsOnOwningThread();
664 constexpr size_t StackExceptionLimit = 20;
665 using StrongTransactionArray =
666 AutoTArray<SafeRefPtr<IDBTransaction>, StackExceptionLimit>;
667 using WeakTransactionArray = AutoTArray<IDBTransaction*, StackExceptionLimit>;
669 if (!mTransactions.Count()) {
670 // Return early as an optimization, the remainder is a no-op in this
671 // case.
672 return;
675 // XXX TransformIfIntoNewArray might be generalized to allow specifying the
676 // type of nsTArray to create, so that it can create an AutoTArray as well; an
677 // TransformIf (without AbortOnErr) might be added, which could be used here.
678 StrongTransactionArray transactionsToAbort;
679 transactionsToAbort.SetCapacity(mTransactions.Count());
681 for (IDBTransaction* const transaction : mTransactions) {
682 MOZ_ASSERT(transaction);
684 transaction->AssertIsOnOwningThread();
686 // Transactions that are already done can simply be ignored. Otherwise
687 // there is a race here and it's possible that the transaction has not
688 // been successfully committed yet so we will warn the user.
689 if (!transaction->IsFinished()) {
690 transactionsToAbort.EmplaceBack(transaction,
691 AcquireStrongRefFromRawPtr{});
694 MOZ_ASSERT(transactionsToAbort.Length() <= mTransactions.Count());
696 if (transactionsToAbort.IsEmpty()) {
697 // Return early as an optimization, the remainder is a no-op in this
698 // case.
699 return;
702 // We want to abort transactions as soon as possible so we iterate the
703 // transactions once and abort them all first, collecting the transactions
704 // that need to have a warning issued along the way. Those that need a
705 // warning will be a subset of those that are aborted, so we don't need
706 // additional strong references here.
707 WeakTransactionArray transactionsThatNeedWarning;
709 for (const auto& transaction : transactionsToAbort) {
710 MOZ_ASSERT(transaction);
711 MOZ_ASSERT(!transaction->IsFinished());
713 // We warn for any transactions that could have written data, but
714 // ignore read-only transactions.
715 if (aShouldWarn && transaction->IsWriteAllowed()) {
716 transactionsThatNeedWarning.AppendElement(transaction.unsafeGetRawPtr());
719 transaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
722 static const char kWarningMessage[] = "IndexedDBTransactionAbortNavigation";
724 for (IDBTransaction* transaction : transactionsThatNeedWarning) {
725 MOZ_ASSERT(transaction);
726 LogWarning(kWarningMessage, transaction->GetCallerLocation());
730 PBackgroundIDBDatabaseFileChild* IDBDatabase::GetOrCreateFileActorForBlob(
731 Blob& aBlob) {
732 AssertIsOnOwningThread();
733 MOZ_ASSERT(mBackgroundActor);
735 // We use the File's nsIWeakReference as the key to the table because
736 // a) it is unique per blob, b) it is reference-counted so that we can
737 // guarantee that it stays alive, and c) it doesn't hold the actual File
738 // alive.
739 nsWeakPtr weakRef = do_GetWeakReference(&aBlob);
740 MOZ_ASSERT(weakRef);
742 PBackgroundIDBDatabaseFileChild* actor = nullptr;
744 if (!mFileActors.Get(weakRef, &actor)) {
745 BlobImpl* blobImpl = aBlob.Impl();
746 MOZ_ASSERT(blobImpl);
748 IPCBlob ipcBlob;
749 nsresult rv = IPCBlobUtils::Serialize(blobImpl, ipcBlob);
750 if (NS_WARN_IF(NS_FAILED(rv))) {
751 return nullptr;
754 auto* dbFile = new DatabaseFile(this);
756 actor = mBackgroundActor->SendPBackgroundIDBDatabaseFileConstructor(
757 dbFile, ipcBlob);
758 if (NS_WARN_IF(!actor)) {
759 return nullptr;
762 mFileActors.InsertOrUpdate(weakRef, actor);
765 MOZ_ASSERT(actor);
767 return actor;
770 void IDBDatabase::NoteFinishedFileActor(
771 PBackgroundIDBDatabaseFileChild* aFileActor) {
772 AssertIsOnOwningThread();
773 MOZ_ASSERT(aFileActor);
775 mFileActors.RemoveIf([aFileActor](const auto& iter) {
776 MOZ_ASSERT(iter.Key());
777 PBackgroundIDBDatabaseFileChild* actor = iter.Data();
778 MOZ_ASSERT(actor);
780 return actor == aFileActor;
784 void IDBDatabase::NoteActiveTransaction() {
785 AssertIsOnOwningThread();
786 MOZ_ASSERT(mFactory);
788 // Increase the number of active transactions.
789 mFactory->UpdateActiveTransactionCount(1);
792 void IDBDatabase::NoteInactiveTransaction() {
793 AssertIsOnOwningThread();
795 if (!mBackgroundActor || !mFileActors.Count()) {
796 MOZ_ASSERT(mFactory);
797 mFactory->UpdateActiveTransactionCount(-1);
798 return;
801 RefPtr<Runnable> runnable =
802 NewRunnableMethod("IDBDatabase::NoteInactiveTransactionDelayed", this,
803 &IDBDatabase::NoteInactiveTransactionDelayed);
804 MOZ_ASSERT(runnable);
806 if (!NS_IsMainThread()) {
807 // Wrap as a nsICancelableRunnable to make workers happy.
808 runnable = MakeRefPtr<CancelableRunnableWrapper>(runnable.forget());
811 MOZ_ALWAYS_SUCCEEDS(
812 EventTarget()->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL));
815 void IDBDatabase::ExpireFileActors(bool aExpireAll) {
816 AssertIsOnOwningThread();
818 if (mBackgroundActor && mFileActors.Count()) {
819 for (auto iter = mFileActors.Iter(); !iter.Done(); iter.Next()) {
820 nsISupports* key = iter.Key();
821 PBackgroundIDBDatabaseFileChild* actor = iter.Data();
822 MOZ_ASSERT(key);
823 MOZ_ASSERT(actor);
825 bool shouldExpire = aExpireAll;
826 if (!shouldExpire) {
827 nsWeakPtr weakRef = do_QueryInterface(key);
828 MOZ_ASSERT(weakRef);
830 nsCOMPtr<nsISupports> referent = do_QueryReferent(weakRef);
831 shouldExpire = !referent;
834 if (shouldExpire) {
835 PBackgroundIDBDatabaseFileChild::Send__delete__(actor);
837 if (!aExpireAll) {
838 iter.Remove();
842 if (aExpireAll) {
843 mFileActors.Clear();
845 } else {
846 MOZ_ASSERT(!mFileActors.Count());
850 void IDBDatabase::Invalidate() {
851 AssertIsOnOwningThread();
853 if (!mInvalidated) {
854 mInvalidated = true;
856 InvalidateInternal();
860 void IDBDatabase::NoteInactiveTransactionDelayed() {
861 ExpireFileActors(/* aExpireAll */ false);
863 MOZ_ASSERT(mFactory);
864 mFactory->UpdateActiveTransactionCount(-1);
867 void IDBDatabase::LogWarning(const char* aMessageName,
868 const JSCallingLocation& aLoc) {
869 AssertIsOnOwningThread();
870 MOZ_ASSERT(aMessageName);
872 ScriptErrorHelper::DumpLocalizedMessage(
873 nsDependentCString(aMessageName), aLoc, nsIScriptError::warningFlag,
874 mFactory->IsChrome(), mFactory->InnerWindowID());
877 NS_IMPL_ADDREF_INHERITED(IDBDatabase, DOMEventTargetHelper)
878 NS_IMPL_RELEASE_INHERITED(IDBDatabase, DOMEventTargetHelper)
880 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBDatabase)
881 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
883 NS_IMPL_CYCLE_COLLECTION_CLASS(IDBDatabase)
885 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBDatabase,
886 DOMEventTargetHelper)
887 tmp->AssertIsOnOwningThread();
888 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFactory)
889 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
891 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBDatabase,
892 DOMEventTargetHelper)
893 tmp->AssertIsOnOwningThread();
895 // Don't unlink mFactory!
897 // We've been unlinked, at the very least we should be able to prevent further
898 // transactions from starting and unblock any other SetVersion callers.
899 tmp->CloseInternal();
900 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
902 void IDBDatabase::DisconnectFromOwner() {
903 InvalidateInternal();
904 DOMEventTargetHelper::DisconnectFromOwner();
907 void IDBDatabase::LastRelease() {
908 AssertIsOnOwningThread();
910 CloseInternal();
912 ExpireFileActors(/* aExpireAll */ true);
914 if (mBackgroundActor) {
915 mBackgroundActor->SendDeleteMeInternal();
916 MOZ_ASSERT(!mBackgroundActor, "SendDeleteMeInternal should have cleared!");
920 JSObject* IDBDatabase::WrapObject(JSContext* aCx,
921 JS::Handle<JSObject*> aGivenProto) {
922 return IDBDatabase_Binding::Wrap(aCx, this, aGivenProto);
925 NS_IMETHODIMP
926 CancelableRunnableWrapper::Run() {
927 const nsCOMPtr<nsIRunnable> runnable = std::move(mRunnable);
929 if (runnable) {
930 return runnable->Run();
933 return NS_OK;
936 nsresult CancelableRunnableWrapper::Cancel() {
937 if (mRunnable) {
938 mRunnable = nullptr;
939 return NS_OK;
942 return NS_ERROR_UNEXPECTED;
945 NS_IMPL_ISUPPORTS(IDBDatabase::Observer, nsIObserver)
947 NS_IMETHODIMP
948 IDBDatabase::Observer::Observe(nsISupports* aSubject, const char* aTopic,
949 const char16_t* aData) {
950 MOZ_ASSERT(NS_IsMainThread());
951 MOZ_ASSERT(aTopic);
953 if (!strcmp(aTopic, kWindowObserverTopic)) {
954 if (mWeakDatabase) {
955 nsCOMPtr<nsISupportsPRUint64> supportsInt = do_QueryInterface(aSubject);
956 MOZ_ASSERT(supportsInt);
958 uint64_t windowId;
959 MOZ_ALWAYS_SUCCEEDS(supportsInt->GetData(&windowId));
961 if (windowId == mWindowId) {
962 RefPtr<IDBDatabase> database = mWeakDatabase;
963 mWeakDatabase = nullptr;
965 database->InvalidateInternal();
969 return NS_OK;
972 if (!strcmp(aTopic, kCycleCollectionObserverTopic) ||
973 !strcmp(aTopic, kMemoryPressureObserverTopic)) {
974 if (mWeakDatabase) {
975 RefPtr<IDBDatabase> database = mWeakDatabase;
977 database->ExpireFileActors(/* aExpireAll */ false);
980 return NS_OK;
983 NS_WARNING("Unknown observer topic!");
984 return NS_OK;
987 nsresult IDBDatabase::RenameObjectStore(int64_t aObjectStoreId,
988 const nsAString& aName) {
989 MOZ_ASSERT(mSpec);
991 nsTArray<ObjectStoreSpec>& objectStores = mSpec->objectStores();
992 ObjectStoreSpec* foundObjectStoreSpec = nullptr;
994 // Find the matched object store spec and check if 'aName' is already used by
995 // another object store.
997 for (auto& objSpec : objectStores) {
998 const bool idIsCurrent = objSpec.metadata().id() == aObjectStoreId;
1000 if (idIsCurrent) {
1001 MOZ_ASSERT(!foundObjectStoreSpec);
1002 foundObjectStoreSpec = &objSpec;
1005 if (objSpec.metadata().name() == aName) {
1006 if (idIsCurrent) {
1007 return NS_OK;
1009 return NS_ERROR_DOM_INDEXEDDB_RENAME_OBJECT_STORE_ERR;
1013 MOZ_ASSERT(foundObjectStoreSpec);
1015 // Update the name of the matched object store.
1016 foundObjectStoreSpec->metadata().name().Assign(aName);
1018 return NS_OK;
1021 nsresult IDBDatabase::RenameIndex(int64_t aObjectStoreId, int64_t aIndexId,
1022 const nsAString& aName) {
1023 MOZ_ASSERT(mSpec);
1025 nsTArray<ObjectStoreSpec>& objectStores = mSpec->objectStores();
1027 ObjectStoreSpec* foundObjectStoreSpec = nullptr;
1028 // Find the matched index metadata and check if 'aName' is already used by
1029 // another index.
1030 for (uint32_t objCount = objectStores.Length(), objIndex = 0;
1031 objIndex < objCount; objIndex++) {
1032 const ObjectStoreSpec& objSpec = objectStores[objIndex];
1033 if (objSpec.metadata().id() == aObjectStoreId) {
1034 foundObjectStoreSpec = &objectStores[objIndex];
1035 break;
1039 MOZ_ASSERT(foundObjectStoreSpec);
1041 nsTArray<IndexMetadata>& indexes = foundObjectStoreSpec->indexes();
1042 IndexMetadata* foundIndexMetadata = nullptr;
1043 for (uint32_t idxCount = indexes.Length(), idxIndex = 0; idxIndex < idxCount;
1044 idxIndex++) {
1045 const IndexMetadata& metadata = indexes[idxIndex];
1046 if (metadata.id() == aIndexId) {
1047 MOZ_ASSERT(!foundIndexMetadata);
1048 foundIndexMetadata = &indexes[idxIndex];
1049 continue;
1051 if (aName == metadata.name()) {
1052 return NS_ERROR_DOM_INDEXEDDB_RENAME_INDEX_ERR;
1056 MOZ_ASSERT(foundIndexMetadata);
1058 // Update the name of the matched object store.
1059 foundIndexMetadata->name() = nsString(aName);
1061 return NS_OK;
1064 void IDBDatabase::IncreaseActiveDatabaseCount() {
1065 AssertIsOnOwningThread();
1066 MOZ_ASSERT(mFactory);
1067 MOZ_ASSERT(!mIncreasedActiveDatabaseCount);
1069 mFactory->UpdateActiveDatabaseCount(1);
1070 mIncreasedActiveDatabaseCount = true;
1073 void IDBDatabase::MaybeDecreaseActiveDatabaseCount() {
1074 AssertIsOnOwningThread();
1076 if (mIncreasedActiveDatabaseCount) {
1077 // Decrease the number of active databases.
1078 MOZ_ASSERT(mFactory);
1079 mFactory->UpdateActiveDatabaseCount(-1);
1080 mIncreasedActiveDatabaseCount = false;
1084 } // namespace mozilla::dom