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"
10 #include "IDBFactory.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"
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
;
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
;
75 explicit CancelableRunnableWrapper(nsCOMPtr
<nsIRunnable
> aRunnable
)
76 : CancelableRunnable("dom::CancelableRunnableWrapper"),
77 mRunnable(std::move(aRunnable
)) {
78 MOZ_ASSERT(mRunnable
);
82 ~CancelableRunnableWrapper() = default;
85 nsresult
Cancel() override
;
88 class DatabaseFile final
: public PBackgroundIDBDatabaseFileChild
{
89 IDBDatabase
* mDatabase
;
92 explicit DatabaseFile(IDBDatabase
* aDatabase
) : mDatabase(aDatabase
) {
93 MOZ_ASSERT(aDatabase
);
94 aDatabase
->AssertIsOnOwningThread();
96 MOZ_COUNT_CTOR(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);
123 class IDBDatabase::Observer final
: public nsIObserver
{
124 IDBDatabase
* mWeakDatabase
;
125 const uint64_t mWindowId
;
128 Observer(IDBDatabase
* aDatabase
, uint64_t aWindowId
)
129 : mWeakDatabase(aDatabase
), mWindowId(aWindowId
) {
130 MOZ_ASSERT(NS_IsMainThread());
131 MOZ_ASSERT(aDatabase
);
135 MOZ_ASSERT(NS_IsMainThread());
137 mWeakDatabase
= nullptr;
144 MOZ_ASSERT(NS_IsMainThread());
145 MOZ_ASSERT(!mWeakDatabase
);
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
),
161 mQuotaExceeded(false),
162 mIncreasedActiveDatabaseCount(false) {
163 MOZ_ASSERT(aRequest
);
164 MOZ_ASSERT(mFactory
);
165 mFactory
->AssertIsOnOwningThread();
170 IDBDatabase::~IDBDatabase() {
171 AssertIsOnOwningThread();
172 MOZ_ASSERT(!mBackgroundActor
);
173 MOZ_ASSERT(!mIncreasedActiveDatabaseCount
);
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();
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();
199 // This topic must be successfully registered.
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();
219 void IDBDatabase::AssertIsOnOwningThread() const {
220 MOZ_ASSERT(mFactory
);
221 mFactory
->AssertIsOnOwningThread();
226 nsIEventTarget
* IDBDatabase::EventTarget() const {
227 AssertIsOnOwningThread();
228 return mFactory
->EventTarget();
231 void IDBDatabase::CloseInternal() {
232 AssertIsOnOwningThread();
237 ExpireFileActors(/* aExpireAll */ true);
242 nsCOMPtr
<nsIObserverService
> obsSvc
= GetObserverService();
244 // These might not have been registered.
245 obsSvc
->RemoveObserver(mObserver
, kCycleCollectionObserverTopic
);
246 obsSvc
->RemoveObserver(mObserver
, kMemoryPressureObserverTopic
);
249 obsSvc
->RemoveObserver(mObserver
, kWindowObserverTopic
));
255 if (mBackgroundActor
&& !mInvalidated
) {
256 mBackgroundActor
->SendClose();
259 // Decrease the number of active databases right after the database is
261 MaybeDecreaseActiveDatabaseCount();
265 void IDBDatabase::InvalidateInternal() {
266 AssertIsOnOwningThread();
268 AbortTransactions(/* aShouldWarn */ true);
273 void IDBDatabase::EnterSetVersionTransaction(uint64_t aNewVersion
) {
274 AssertIsOnOwningThread();
275 MOZ_ASSERT(aNewVersion
);
276 MOZ_ASSERT(!RunningVersionChangeTransaction());
278 MOZ_ASSERT(!mPreviousSpec
);
280 mPreviousSpec
= MakeUnique
<DatabaseSpec
>(*mSpec
);
282 mSpec
->metadata().version() = aNewVersion
;
285 void IDBDatabase::ExitSetVersionTransaction() {
286 AssertIsOnOwningThread();
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
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();
323 return mSpec
->metadata().name();
326 uint64_t IDBDatabase::Version() const {
327 AssertIsOnOwningThread();
330 return mSpec
->metadata().version();
333 RefPtr
<DOMStringList
> IDBDatabase::ObjectStoreNames() const {
334 AssertIsOnOwningThread();
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();
349 RefPtr
<IDBObjectStore
> IDBDatabase::CreateObjectStore(
350 const nsAString
& aName
, const IDBObjectStoreParameters
& aOptionalParameters
,
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
);
361 if (!transaction
->IsActive()) {
362 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR
);
366 QM_INFOONLY_TRY_UNWRAP(const auto maybeKeyPath
,
367 KeyPath::Parse(aOptionalParameters
.mKeyPath
));
369 aRv
.Throw(NS_ERROR_DOM_SYNTAX_ERR
);
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()));
388 if (!keyPath
.IsAllowedForObjectStore(aOptionalParameters
.mAutoIncrement
)) {
389 aRv
.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR
);
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
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
));
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
);
436 if (!transaction
->IsActive()) {
437 aRv
.Throw(NS_ERROR_DOM_INDEXEDDB_TRANSACTION_INACTIVE_ERR
);
441 auto& specArray
= mSpec
->objectStores();
442 const auto end
= specArray
.end();
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
);
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
,
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");
491 if (QuotaManager::IsShuttingDown()) {
492 IDB_REPORT_INTERNAL_ERR();
493 aRv
.ThrowUnknownError("Can't start IndexedDB transaction during shutdown");
497 // https://w3c.github.io/IndexedDB/#dom-idbdatabase-transaction
499 if (RunningVersionChangeTransaction()) {
500 aRv
.ThrowInvalidStateError(
501 "Can't start a transaction while running an upgrade transaction");
507 aRv
.ThrowInvalidStateError(
508 "Can't start a transaction on a closed database");
513 AutoTArray
<nsString
, 1> stackSequence
;
515 if (aStoreNames
.IsString()) {
516 stackSequence
.AppendElement(aStoreNames
.GetAsString());
518 MOZ_ASSERT(aStoreNames
.IsStringSequence());
519 // Step 5, but it can be done before step 4 because those steps
521 if (aStoreNames
.GetAsStringSequence().IsEmpty()) {
522 aRv
.ThrowInvalidAccessError("Empty scope passed in");
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
) {
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
);
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
;
566 case IDBTransactionMode::Readonly
:
567 mode
= IDBTransaction::Mode::ReadOnly
;
569 case IDBTransactionMode::Readwrite
:
570 if (mQuotaExceeded
) {
571 mode
= IDBTransaction::Mode::Cleanup
;
572 mQuotaExceeded
= false;
574 mode
= IDBTransaction::Mode::ReadWrite
;
577 case IDBTransactionMode::Readwriteflush
:
578 mode
= IDBTransaction::Mode::ReadWriteFlush
;
580 case IDBTransactionMode::Cleanup
:
581 mode
= IDBTransaction::Mode::Cleanup
;
582 mQuotaExceeded
= false;
584 case IDBTransactionMode::Versionchange
:
586 aRv
.ThrowTypeError("Invalid transaction mode");
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
;
599 case mozilla::dom::IDBTransactionDurability::Strict
:
600 durability
= IDBTransaction::Durability::Strict
;
602 case mozilla::dom::IDBTransactionDurability::Relaxed
:
603 durability
= IDBTransaction::Durability::Relaxed
;
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");
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");
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
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
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(
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
739 nsWeakPtr weakRef
= do_GetWeakReference(&aBlob
);
742 PBackgroundIDBDatabaseFileChild
* actor
= nullptr;
744 if (!mFileActors
.Get(weakRef
, &actor
)) {
745 BlobImpl
* blobImpl
= aBlob
.Impl();
746 MOZ_ASSERT(blobImpl
);
749 nsresult rv
= IPCBlobUtils::Serialize(blobImpl
, ipcBlob
);
750 if (NS_WARN_IF(NS_FAILED(rv
))) {
754 auto* dbFile
= new DatabaseFile(this);
756 actor
= mBackgroundActor
->SendPBackgroundIDBDatabaseFileConstructor(
758 if (NS_WARN_IF(!actor
)) {
762 mFileActors
.InsertOrUpdate(weakRef
, 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();
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);
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());
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();
825 bool shouldExpire
= aExpireAll
;
827 nsWeakPtr weakRef
= do_QueryInterface(key
);
830 nsCOMPtr
<nsISupports
> referent
= do_QueryReferent(weakRef
);
831 shouldExpire
= !referent
;
835 PBackgroundIDBDatabaseFileChild::Send__delete__(actor
);
846 MOZ_ASSERT(!mFileActors
.Count());
850 void IDBDatabase::Invalidate() {
851 AssertIsOnOwningThread();
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();
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
);
926 CancelableRunnableWrapper::Run() {
927 const nsCOMPtr
<nsIRunnable
> runnable
= std::move(mRunnable
);
930 return runnable
->Run();
936 nsresult
CancelableRunnableWrapper::Cancel() {
942 return NS_ERROR_UNEXPECTED
;
945 NS_IMPL_ISUPPORTS(IDBDatabase::Observer
, nsIObserver
)
948 IDBDatabase::Observer::Observe(nsISupports
* aSubject
, const char* aTopic
,
949 const char16_t
* aData
) {
950 MOZ_ASSERT(NS_IsMainThread());
953 if (!strcmp(aTopic
, kWindowObserverTopic
)) {
955 nsCOMPtr
<nsISupportsPRUint64
> supportsInt
= do_QueryInterface(aSubject
);
956 MOZ_ASSERT(supportsInt
);
959 MOZ_ALWAYS_SUCCEEDS(supportsInt
->GetData(&windowId
));
961 if (windowId
== mWindowId
) {
962 RefPtr
<IDBDatabase
> database
= mWeakDatabase
;
963 mWeakDatabase
= nullptr;
965 database
->InvalidateInternal();
972 if (!strcmp(aTopic
, kCycleCollectionObserverTopic
) ||
973 !strcmp(aTopic
, kMemoryPressureObserverTopic
)) {
975 RefPtr
<IDBDatabase
> database
= mWeakDatabase
;
977 database
->ExpireFileActors(/* aExpireAll */ false);
983 NS_WARNING("Unknown observer topic!");
987 nsresult
IDBDatabase::RenameObjectStore(int64_t aObjectStoreId
,
988 const nsAString
& aName
) {
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
;
1001 MOZ_ASSERT(!foundObjectStoreSpec
);
1002 foundObjectStoreSpec
= &objSpec
;
1005 if (objSpec
.metadata().name() == aName
) {
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
);
1021 nsresult
IDBDatabase::RenameIndex(int64_t aObjectStoreId
, int64_t aIndexId
,
1022 const nsAString
& aName
) {
1025 nsTArray
<ObjectStoreSpec
>& objectStores
= mSpec
->objectStores();
1027 ObjectStoreSpec
* foundObjectStoreSpec
= nullptr;
1028 // Find the matched index metadata and check if 'aName' is already used by
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
];
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
;
1045 const IndexMetadata
& metadata
= indexes
[idxIndex
];
1046 if (metadata
.id() == aIndexId
) {
1047 MOZ_ASSERT(!foundIndexMetadata
);
1048 foundIndexMetadata
= &indexes
[idxIndex
];
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
);
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