summaryrefslogtreecommitdiffstats
path: root/dom/indexedDB
diff options
context:
space:
mode:
Diffstat (limited to 'dom/indexedDB')
-rw-r--r--dom/indexedDB/ActorsChild.cpp12
-rw-r--r--dom/indexedDB/ActorsChild.h14
-rw-r--r--dom/indexedDB/ActorsParent.cpp1128
-rw-r--r--dom/indexedDB/ActorsParentCommon.cpp2
-rw-r--r--dom/indexedDB/DatabaseFileManager.h14
-rw-r--r--dom/indexedDB/IDBCursor.cpp8
-rw-r--r--dom/indexedDB/IDBDatabase.cpp26
-rw-r--r--dom/indexedDB/IDBDatabase.h3
-rw-r--r--dom/indexedDB/IDBFactory.cpp236
-rw-r--r--dom/indexedDB/IDBFactory.h9
-rw-r--r--dom/indexedDB/IDBObjectStore.cpp29
-rw-r--r--dom/indexedDB/IDBObjectStore.h12
-rw-r--r--dom/indexedDB/IDBTransaction.cpp73
-rw-r--r--dom/indexedDB/IDBTransaction.h32
-rw-r--r--dom/indexedDB/IndexedDatabase.cpp3
-rw-r--r--dom/indexedDB/IndexedDatabaseManager.cpp84
-rw-r--r--dom/indexedDB/IndexedDatabaseManager.h8
-rw-r--r--dom/indexedDB/Key.cpp52
-rw-r--r--dom/indexedDB/Key.h12
-rw-r--r--dom/indexedDB/KeyPath.cpp16
-rw-r--r--dom/indexedDB/KeyPath.h7
-rw-r--r--dom/indexedDB/PBackgroundIDBCursor.ipdl4
-rw-r--r--dom/indexedDB/PBackgroundIDBDatabase.ipdl5
-rw-r--r--dom/indexedDB/PBackgroundIDBFactory.ipdl10
-rw-r--r--dom/indexedDB/PBackgroundIDBTransaction.ipdl4
-rw-r--r--dom/indexedDB/PBackgroundIDBVersionChangeTransaction.ipdl4
-rw-r--r--dom/indexedDB/SchemaUpgrades.cpp2
-rw-r--r--dom/indexedDB/SerializationHelpers.h11
-rw-r--r--dom/indexedDB/test/mochitest-common.toml3
-rw-r--r--dom/indexedDB/test/test_open_and_databases.html19
-rw-r--r--dom/indexedDB/test/test_third_party.html8
-rw-r--r--dom/indexedDB/test/unit/test_connection_idle_maintenance.js2
-rw-r--r--dom/indexedDB/test/unit/test_connection_idle_maintenance_stop.js2
-rw-r--r--dom/indexedDB/test/unit/test_open_and_databases.js76
-rw-r--r--dom/indexedDB/test/unit/xpcshell-head-parent-process.js8
-rw-r--r--dom/indexedDB/test/unit/xpcshell-shared.toml2
36 files changed, 1505 insertions, 435 deletions
diff --git a/dom/indexedDB/ActorsChild.cpp b/dom/indexedDB/ActorsChild.cpp
index a6f3ef7789..1a122ea3c1 100644
--- a/dom/indexedDB/ActorsChild.cpp
+++ b/dom/indexedDB/ActorsChild.cpp
@@ -1429,7 +1429,7 @@ mozilla::ipc::IPCResult BackgroundTransactionChild::RecvComplete(
PBackgroundIDBRequestChild*
BackgroundTransactionChild::AllocPBackgroundIDBRequestChild(
- const RequestParams& aParams) {
+ const int64_t& aRequestId, const RequestParams& aParams) {
MOZ_CRASH(
"PBackgroundIDBRequestChild actors should be manually "
"constructed!");
@@ -1445,7 +1445,7 @@ bool BackgroundTransactionChild::DeallocPBackgroundIDBRequestChild(
PBackgroundIDBCursorChild*
BackgroundTransactionChild::AllocPBackgroundIDBCursorChild(
- const OpenCursorParams& aParams) {
+ const int64_t& aRequestId, const OpenCursorParams& aParams) {
AssertIsOnOwningThread();
MOZ_CRASH("PBackgroundIDBCursorChild actors should be manually constructed!");
@@ -1547,7 +1547,7 @@ mozilla::ipc::IPCResult BackgroundVersionChangeTransactionChild::RecvComplete(
PBackgroundIDBRequestChild*
BackgroundVersionChangeTransactionChild::AllocPBackgroundIDBRequestChild(
- const RequestParams& aParams) {
+ const int64_t& aRequestId, const RequestParams& aParams) {
MOZ_CRASH(
"PBackgroundIDBRequestChild actors should be manually "
"constructed!");
@@ -1563,7 +1563,7 @@ bool BackgroundVersionChangeTransactionChild::DeallocPBackgroundIDBRequestChild(
PBackgroundIDBCursorChild*
BackgroundVersionChangeTransactionChild::AllocPBackgroundIDBCursorChild(
- const OpenCursorParams& aParams) {
+ const int64_t& aRequestId, const OpenCursorParams& aParams) {
AssertIsOnOwningThread();
MOZ_CRASH("PBackgroundIDBCursorChild actors should be manually constructed!");
@@ -2211,7 +2211,7 @@ BackgroundCursorChild<CursorType>::SafeRefPtrFromThis() {
template <IDBCursorType CursorType>
void BackgroundCursorChild<CursorType>::SendContinueInternal(
- const CursorRequestParams& aParams,
+ const int64_t aRequestId, const CursorRequestParams& aParams,
const CursorData<CursorType>& aCurrentData) {
AssertIsOnOwningThread();
MOZ_ASSERT(mRequest);
@@ -2394,7 +2394,7 @@ void BackgroundCursorChild<CursorType>::SendContinueInternal(
// handling model disallow this?
} else {
MOZ_ALWAYS_TRUE(PBackgroundIDBCursorChild::SendContinue(
- params, currentKey, currentObjectStoreKey));
+ aRequestId, params, currentKey, currentObjectStoreKey));
}
}
diff --git a/dom/indexedDB/ActorsChild.h b/dom/indexedDB/ActorsChild.h
index e3e204be0b..194ed28878 100644
--- a/dom/indexedDB/ActorsChild.h
+++ b/dom/indexedDB/ActorsChild.h
@@ -347,12 +347,12 @@ class BackgroundTransactionChild final : public BackgroundTransactionBase,
mozilla::ipc::IPCResult RecvComplete(nsresult aResult);
PBackgroundIDBRequestChild* AllocPBackgroundIDBRequestChild(
- const RequestParams& aParams);
+ const int64_t& aRequestId, const RequestParams& aParams);
bool DeallocPBackgroundIDBRequestChild(PBackgroundIDBRequestChild* aActor);
PBackgroundIDBCursorChild* AllocPBackgroundIDBCursorChild(
- const OpenCursorParams& aParams);
+ const int64_t& aRequestId, const OpenCursorParams& aParams);
bool DeallocPBackgroundIDBCursorChild(PBackgroundIDBCursorChild* aActor);
};
@@ -393,12 +393,12 @@ class BackgroundVersionChangeTransactionChild final
mozilla::ipc::IPCResult RecvComplete(nsresult aResult);
PBackgroundIDBRequestChild* AllocPBackgroundIDBRequestChild(
- const RequestParams& aParams);
+ const int64_t& aRequestId, const RequestParams& aParams);
bool DeallocPBackgroundIDBRequestChild(PBackgroundIDBRequestChild* aActor);
PBackgroundIDBCursorChild* AllocPBackgroundIDBCursorChild(
- const OpenCursorParams& aParams);
+ const int64_t& aRequestId, const OpenCursorParams& aParams);
bool DeallocPBackgroundIDBCursorChild(PBackgroundIDBCursorChild* aActor);
};
@@ -548,7 +548,8 @@ class BackgroundCursorChild final : public BackgroundCursorChildBase {
BackgroundCursorChild(NotNull<IDBRequest*> aRequest, SourceType* aSource,
Direction aDirection);
- void SendContinueInternal(const CursorRequestParams& aParams,
+ void SendContinueInternal(const int64_t aRequestId,
+ const CursorRequestParams& aParams,
const CursorData<CursorType>& aCurrentData);
void InvalidateCachedResponses();
@@ -594,7 +595,8 @@ class BackgroundCursorChild final : public BackgroundCursorChildBase {
mozilla::ipc::IPCResult RecvResponse(CursorResponse&& aResponse) override;
// Force callers to use SendContinueInternal.
- bool SendContinue(const CursorRequestParams& aParams, const Key& aCurrentKey,
+ bool SendContinue(const int64_t& aRequestId,
+ const CursorRequestParams& aParams, const Key& aCurrentKey,
const Key& aCurrentObjectStoreKey) = delete;
bool SendDeleteMe() = delete;
diff --git a/dom/indexedDB/ActorsParent.cpp b/dom/indexedDB/ActorsParent.cpp
index 47360094ca..6609ea5b81 100644
--- a/dom/indexedDB/ActorsParent.cpp
+++ b/dom/indexedDB/ActorsParent.cpp
@@ -94,6 +94,7 @@
#include "mozilla/dom/FileBlobImpl.h"
#include "mozilla/dom/FlippedOnce.h"
#include "mozilla/dom/IDBCursorBinding.h"
+#include "mozilla/dom/IDBFactory.h"
#include "mozilla/dom/IPCBlob.h"
#include "mozilla/dom/IPCBlobUtils.h"
#include "mozilla/dom/IndexedDatabase.h"
@@ -122,6 +123,7 @@
#include "mozilla/dom/quota/CheckedUnsafePtr.h"
#include "mozilla/dom/quota/Client.h"
#include "mozilla/dom/quota/ClientImpl.h"
+#include "mozilla/dom/quota/DebugOnlyMacro.h"
#include "mozilla/dom/quota/DirectoryLock.h"
#include "mozilla/dom/quota/DecryptingInputStream_impl.h"
#include "mozilla/dom/quota/EncryptingOutputStream_impl.h"
@@ -1973,6 +1975,10 @@ class TransactionDatabaseOperationBase : public DatabaseOperationBase {
};
InitializedOnce<const NotNull<SafeRefPtr<TransactionBase>>> mTransaction;
+ // Unique request id within the context of the transaction, allocated by the
+ // transaction in the content process starting from 0. Values less than 0 are
+ // impossible and forbidden. Used to support the explicit commit() request.
+ const int64_t mRequestId;
InternalState mInternalState = InternalState::Initial;
bool mWaitingForContinue = false;
const bool mTransactionIsAborted;
@@ -2034,10 +2040,11 @@ class TransactionDatabaseOperationBase : public DatabaseOperationBase {
virtual void Cleanup();
protected:
- explicit TransactionDatabaseOperationBase(
- SafeRefPtr<TransactionBase> aTransaction);
+ TransactionDatabaseOperationBase(SafeRefPtr<TransactionBase> aTransaction,
+ int64_t aRequestId);
TransactionDatabaseOperationBase(SafeRefPtr<TransactionBase> aTransaction,
+ const int64_t aRequestId,
uint64_t aLoggingSerialNumber);
~TransactionDatabaseOperationBase() override;
@@ -2141,6 +2148,14 @@ class Factory final : public PBackgroundIDBFactoryParent,
bool DeallocPBackgroundIDBFactoryRequestParent(
PBackgroundIDBFactoryRequestParent* aActor) override;
+
+ mozilla::ipc::IPCResult RecvGetDatabases(
+ const PersistenceType& aPersistenceType,
+ const PrincipalInfo& aPrincipalInfo,
+ GetDatabasesResolver&& aResolve) override;
+
+ private:
+ Maybe<ContentParentId> GetContentParentId() const;
};
class WaitForTransactionsHelper final : public Runnable {
@@ -2395,11 +2410,13 @@ class Database final
already_AddRefed<PBackgroundIDBTransactionParent>
AllocPBackgroundIDBTransactionParent(
- const nsTArray<nsString>& aObjectStoreNames, const Mode& aMode) override;
+ const nsTArray<nsString>& aObjectStoreNames, const Mode& aMode,
+ const Durability& aDurability) override;
mozilla::ipc::IPCResult RecvPBackgroundIDBTransactionConstructor(
PBackgroundIDBTransactionParent* aActor,
- nsTArray<nsString>&& aObjectStoreNames, const Mode& aMode) override;
+ nsTArray<nsString>&& aObjectStoreNames, const Mode& aMode,
+ const Durability& aDurability) override;
mozilla::ipc::IPCResult RecvDeleteMe() override;
@@ -2418,6 +2435,7 @@ class Database::StartTransactionOp final
private:
explicit StartTransactionOp(SafeRefPtr<TransactionBase> aTransaction)
: TransactionDatabaseOperationBase(std::move(aTransaction),
+ /* aRequestId */ 0,
/* aLoggingSerialNumber */ 0) {}
~StartTransactionOp() override = default;
@@ -2591,6 +2609,7 @@ class TransactionBase : public AtomicSafeRefCounted<TransactionBase> {
protected:
using Mode = IDBTransaction::Mode;
+ using Durability = IDBTransaction::Durability;
private:
const SafeRefPtr<Database> mDatabase;
@@ -2602,6 +2621,7 @@ class TransactionBase : public AtomicSafeRefCounted<TransactionBase> {
uint64_t mActiveRequestCount;
Atomic<bool> mInvalidatedOnAnyThread;
const Mode mMode;
+ const Durability mDurability; // TODO: See bug 1883045
FlippedOnce<false> mInitialized;
FlippedOnce<false> mHasBeenActiveOnConnectionThread;
FlippedOnce<false> mActorDestroyed;
@@ -2661,6 +2681,8 @@ class TransactionBase : public AtomicSafeRefCounted<TransactionBase> {
Mode GetMode() const { return mMode; }
+ Durability GetDurability() const { return mDurability; }
+
const Database& GetDatabase() const {
MOZ_ASSERT(mDatabase);
@@ -2723,7 +2745,8 @@ class TransactionBase : public AtomicSafeRefCounted<TransactionBase> {
virtual ~TransactionBase();
protected:
- TransactionBase(SafeRefPtr<Database> aDatabase, Mode aMode);
+ TransactionBase(SafeRefPtr<Database> aDatabase, Mode aMode,
+ Durability aDurability);
void NoteActorDestroyed() {
AssertIsOnBackgroundThread();
@@ -2765,7 +2788,8 @@ class TransactionBase : public AtomicSafeRefCounted<TransactionBase> {
CommitOrAbort();
}
- PBackgroundIDBRequestParent* AllocRequest(RequestParams&& aParams,
+ PBackgroundIDBRequestParent* AllocRequest(const int64_t aRequestId,
+ RequestParams&& aParams,
bool aTrustParams);
bool StartRequest(PBackgroundIDBRequestParent* aActor);
@@ -2775,7 +2799,7 @@ class TransactionBase : public AtomicSafeRefCounted<TransactionBase> {
already_AddRefed<PBackgroundIDBCursorParent> AllocCursor(
const OpenCursorParams& aParams, bool aTrustParams);
- bool StartCursor(PBackgroundIDBCursorParent* aActor,
+ bool StartCursor(PBackgroundIDBCursorParent* aActor, const int64_t aRequestId,
const OpenCursorParams& aParams);
virtual void UpdateMetadata(nsresult aResult) {}
@@ -2858,26 +2882,27 @@ class NormalTransaction final : public TransactionBase,
mozilla::ipc::IPCResult RecvAbort(const nsresult& aResultCode) override;
PBackgroundIDBRequestParent* AllocPBackgroundIDBRequestParent(
- const RequestParams& aParams) override;
+ const int64_t& aRequestId, const RequestParams& aParams) override;
mozilla::ipc::IPCResult RecvPBackgroundIDBRequestConstructor(
- PBackgroundIDBRequestParent* aActor,
+ PBackgroundIDBRequestParent* aActor, const int64_t& aRequestId,
const RequestParams& aParams) override;
bool DeallocPBackgroundIDBRequestParent(
PBackgroundIDBRequestParent* aActor) override;
already_AddRefed<PBackgroundIDBCursorParent> AllocPBackgroundIDBCursorParent(
- const OpenCursorParams& aParams) override;
+ const int64_t& aRequestId, const OpenCursorParams& aParams) override;
mozilla::ipc::IPCResult RecvPBackgroundIDBCursorConstructor(
- PBackgroundIDBCursorParent* aActor,
+ PBackgroundIDBCursorParent* aActor, const int64_t& aRequestId,
const OpenCursorParams& aParams) override;
public:
// This constructor is only called by Database.
NormalTransaction(
SafeRefPtr<Database> aDatabase, TransactionBase::Mode aMode,
+ TransactionBase::Durability aDurability,
nsTArray<SafeRefPtr<FullObjectStoreMetadata>>&& aObjectStores);
MOZ_INLINE_DECL_SAFEREFCOUNTING_INHERITED(NormalTransaction, TransactionBase)
@@ -2950,26 +2975,25 @@ class VersionChangeTransaction final
const IndexOrObjectStoreId& aIndexId, const nsAString& aName) override;
PBackgroundIDBRequestParent* AllocPBackgroundIDBRequestParent(
- const RequestParams& aParams) override;
+ const int64_t& aRequestId, const RequestParams& aParams) override;
mozilla::ipc::IPCResult RecvPBackgroundIDBRequestConstructor(
- PBackgroundIDBRequestParent* aActor,
+ PBackgroundIDBRequestParent* aActor, const int64_t& aRequestId,
const RequestParams& aParams) override;
bool DeallocPBackgroundIDBRequestParent(
PBackgroundIDBRequestParent* aActor) override;
already_AddRefed<PBackgroundIDBCursorParent> AllocPBackgroundIDBCursorParent(
- const OpenCursorParams& aParams) override;
+ const int64_t& aRequestId, const OpenCursorParams& aParams) override;
mozilla::ipc::IPCResult RecvPBackgroundIDBCursorConstructor(
- PBackgroundIDBCursorParent* aActor,
+ PBackgroundIDBCursorParent* aActor, const int64_t& aRequestId,
const OpenCursorParams& aParams) override;
};
class FactoryOp
: public DatabaseOperationBase,
- public PBackgroundIDBFactoryRequestParent,
public SupportsCheckedUnsafePtr<CheckIf<DiagnosticAssertEnabled>> {
public:
struct MaybeBlockedDatabaseInfo final {
@@ -3008,9 +3032,20 @@ class FactoryOp
// Waiting for directory open allowed on the PBackground thread. The next
// step is either SendingResults if directory lock failed to acquire, or
- // DatabaseOpenPending if directory lock is acquired.
+ // DirectoryWorkOpen if the factory operation is not tied up to a specific
+ // database, or DatabaseOpenPending otherwise.
DirectoryOpenPending,
+ // Waiting to do/doing directory work on the QuotaManager IO thread. Its
+ // next step is DirectoryWorkDone if directory work was successful or
+ // SendingResults if directory work failed.
+ DirectoryWorkOpen,
+
+ // Checking if database work can be started. If the database is not blocked
+ // by other factory operations then the next step is DatabaseWorkOpen.
+ // Otherwise the next step is DatabaseOpenPending.
+ DirectoryWorkDone,
+
// Waiting for database open allowed on the PBackground thread. The next
// step is DatabaseWorkOpen.
DatabaseOpenPending,
@@ -3060,14 +3095,18 @@ class FactoryOp
// Must be released on the main thread!
RefPtr<DirectoryLock> mDirectoryLock;
- RefPtr<FactoryOp> mDelayedOp;
+ nsTArray<NotNull<RefPtr<FactoryOp>>> mBlocking;
+ nsTArray<NotNull<RefPtr<FactoryOp>>> mBlockedOn;
+
nsTArray<MaybeBlockedDatabaseInfo> mMaybeBlockedDatabases;
- const CommonFactoryRequestParams mCommonParams;
+ const PrincipalInfo mPrincipalInfo;
OriginMetadata mOriginMetadata;
- nsCString mDatabaseId;
- nsString mDatabaseFilePath;
+ Maybe<nsString> mDatabaseName;
+ Maybe<nsCString> mDatabaseId;
+ Maybe<nsString> mDatabaseFilePath;
int64_t mDirectoryLockId;
+ const PersistenceType mPersistenceType;
State mState;
bool mWaitingForPermissionRetry;
bool mEnforcingQuota;
@@ -3081,17 +3120,23 @@ class FactoryOp
return mOriginMetadata.mOrigin;
}
+ const Maybe<nsString>& DatabaseNameRef() const {
+ AssertIsOnOwningThread();
+
+ return mDatabaseName;
+ }
+
bool DatabaseFilePathIsKnown() const {
AssertIsOnOwningThread();
- return !mDatabaseFilePath.IsEmpty();
+ return mDatabaseFilePath.isSome();
}
const nsAString& DatabaseFilePath() const {
AssertIsOnOwningThread();
- MOZ_ASSERT(!mDatabaseFilePath.IsEmpty());
+ MOZ_ASSERT(mDatabaseFilePath);
- return mDatabaseFilePath;
+ return mDatabaseFilePath.ref();
}
void NoteDatabaseBlocked(Database* aDatabase);
@@ -3109,7 +3154,9 @@ class FactoryOp
protected:
FactoryOp(SafeRefPtr<Factory> aFactory,
const Maybe<ContentParentId>& aContentParentId,
- const CommonFactoryRequestParams& aCommonParams, bool aDeleting);
+ const PersistenceType aPersistenceType,
+ const PrincipalInfo& aPrincipalInfo,
+ const Maybe<nsString>& aDatabaseName, bool aDeleting);
~FactoryOp() override {
// Normally this would be out-of-line since it is a virtual function but
@@ -3122,6 +3169,8 @@ class FactoryOp
nsresult DirectoryOpen();
+ nsresult DirectoryWorkDone();
+
nsresult SendToIOThread();
void WaitForTransactions();
@@ -3136,6 +3185,8 @@ class FactoryOp
const Maybe<uint64_t>& aNewVersion);
// Methods that subclasses must implement.
+ virtual nsresult DoDirectoryWork() = 0;
+
virtual nsresult DatabaseOpen() = 0;
virtual nsresult DoDatabaseWork() = 0;
@@ -3157,17 +3208,56 @@ class FactoryOp
void DirectoryLockFailed();
- // IPDL methods.
- void ActorDestroy(ActorDestroyReason aWhy) override;
-
virtual void SendBlockedNotification() = 0;
private:
// Test whether this FactoryOp needs to wait for the given op.
bool MustWaitFor(const FactoryOp& aExistingOp);
+
+ void AddBlockingOp(FactoryOp& aOp) {
+ AssertIsOnOwningThread();
+
+ mBlocking.AppendElement(WrapNotNull(&aOp));
+ }
+
+ void AddBlockedOnOp(FactoryOp& aOp) {
+ AssertIsOnOwningThread();
+
+ mBlockedOn.AppendElement(WrapNotNull(&aOp));
+ }
+
+ void MaybeUnblock(FactoryOp& aOp) {
+ AssertIsOnOwningThread();
+
+ mBlockedOn.RemoveElement(&aOp);
+ if (mBlockedOn.IsEmpty()) {
+ MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(this));
+ }
+ }
+};
+
+class FactoryRequestOp : public FactoryOp,
+ public PBackgroundIDBFactoryRequestParent {
+ protected:
+ const CommonFactoryRequestParams mCommonParams;
+
+ FactoryRequestOp(SafeRefPtr<Factory> aFactory,
+ const Maybe<ContentParentId>& aContentParentId,
+ const CommonFactoryRequestParams& aCommonParams,
+ bool aDeleting)
+ : FactoryOp(std::move(aFactory), aContentParentId,
+ aCommonParams.metadata().persistenceType(),
+ aCommonParams.principalInfo(),
+ Some(aCommonParams.metadata().name()), aDeleting),
+ mCommonParams(aCommonParams) {}
+
+ nsresult DoDirectoryWork() override;
+
+ // IPDL methods.
+ void ActorDestroy(ActorDestroyReason aWhy) override;
};
-class OpenDatabaseOp final : public FactoryOp {
+class OpenDatabaseOp final : public FactoryRequestOp {
friend class Database;
friend class VersionChangeTransaction;
@@ -3249,7 +3339,7 @@ class OpenDatabaseOp::VersionChangeOp final
explicit VersionChangeOp(OpenDatabaseOp* aOpenDatabaseOp)
: TransactionDatabaseOperationBase(
aOpenDatabaseOp->mVersionChangeTransaction.clonePtr(),
- aOpenDatabaseOp->LoggingSerialNumber()),
+ /* aRequestId */ 0, aOpenDatabaseOp->LoggingSerialNumber()),
mOpenDatabaseOp(aOpenDatabaseOp),
mRequestedVersion(aOpenDatabaseOp->mRequestedVersion),
mPreviousVersion(
@@ -3269,7 +3359,7 @@ class OpenDatabaseOp::VersionChangeOp final
void Cleanup() override;
};
-class DeleteDatabaseOp final : public FactoryOp {
+class DeleteDatabaseOp final : public FactoryRequestOp {
class VersionChangeOp;
nsString mDatabaseDirectoryPath;
@@ -3280,8 +3370,8 @@ class DeleteDatabaseOp final : public FactoryOp {
DeleteDatabaseOp(SafeRefPtr<Factory> aFactory,
const Maybe<ContentParentId>& aContentParentId,
const CommonFactoryRequestParams& aParams)
- : FactoryOp(std::move(aFactory), aContentParentId, aParams,
- /* aDeleting */ true),
+ : FactoryRequestOp(std::move(aFactory), aContentParentId, aParams,
+ /* aDeleting */ true),
mPreviousVersion(0) {}
private:
@@ -3327,6 +3417,43 @@ class DeleteDatabaseOp::VersionChangeOp final : public DatabaseOperationBase {
NS_DECL_NSIRUNNABLE
};
+class GetDatabasesOp final : public FactoryOp {
+ nsTHashMap<nsStringHashKey, DatabaseMetadata> mDatabaseMetadataTable;
+ nsTArray<DatabaseMetadata> mDatabaseMetadataArray;
+ Factory::GetDatabasesResolver mResolver;
+
+ public:
+ GetDatabasesOp(SafeRefPtr<Factory> aFactory,
+ const Maybe<ContentParentId>& aContentParentId,
+ const PersistenceType aPersistenceType,
+ const PrincipalInfo& aPrincipalInfo,
+ Factory::GetDatabasesResolver&& aResolver)
+ : FactoryOp(std::move(aFactory), aContentParentId, aPersistenceType,
+ aPrincipalInfo, Nothing(), /* aDeleting */ false),
+ mResolver(std::move(aResolver)) {}
+
+ private:
+ ~GetDatabasesOp() override = default;
+
+ nsresult DatabasesNotAvailable();
+
+ nsresult DoDirectoryWork() override;
+
+ nsresult DatabaseOpen() override;
+
+ nsresult DoDatabaseWork() override;
+
+ nsresult BeginVersionChange() override;
+
+ bool AreActorsAlive() override;
+
+ void SendBlockedNotification() override;
+
+ nsresult DispatchToWorkThread() override;
+
+ void SendResults() override;
+};
+
class VersionChangeTransactionOp : public TransactionDatabaseOperationBase {
public:
void Cleanup() override;
@@ -3334,7 +3461,8 @@ class VersionChangeTransactionOp : public TransactionDatabaseOperationBase {
protected:
explicit VersionChangeTransactionOp(
SafeRefPtr<VersionChangeTransaction> aTransaction)
- : TransactionDatabaseOperationBase(std::move(aTransaction)) {}
+ : TransactionDatabaseOperationBase(std::move(aTransaction),
+ /* aRequestId */ 0) {}
~VersionChangeTransactionOp() override = default;
@@ -3519,8 +3647,9 @@ class NormalTransactionOp : public TransactionDatabaseOperationBase,
void Cleanup() override;
protected:
- explicit NormalTransactionOp(SafeRefPtr<TransactionBase> aTransaction)
- : TransactionDatabaseOperationBase(std::move(aTransaction))
+ NormalTransactionOp(SafeRefPtr<TransactionBase> aTransaction,
+ const int64_t aRequestId)
+ : TransactionDatabaseOperationBase(std::move(aTransaction), aRequestId)
#ifdef DEBUG
,
mResponseSent(false)
@@ -3628,7 +3757,7 @@ class ObjectStoreAddOrPutRequestOp final : public NormalTransactionOp {
};
class SCInputStream;
- const ObjectStoreAddPutParams mParams;
+ ObjectStoreAddPutParams mParams;
Maybe<UniqueIndexTable> mUniqueIndexTable;
// This must be non-const so that we can update the mNextAutoIncrementId field
@@ -3647,6 +3776,7 @@ class ObjectStoreAddOrPutRequestOp final : public NormalTransactionOp {
private:
// Only created by TransactionBase.
ObjectStoreAddOrPutRequestOp(SafeRefPtr<TransactionBase> aTransaction,
+ const int64_t aRequestId,
RequestParams&& aParams);
~ObjectStoreAddOrPutRequestOp() override = default;
@@ -3883,6 +4013,7 @@ class ObjectStoreGetRequestOp final : public NormalTransactionOp {
private:
// Only created by TransactionBase.
ObjectStoreGetRequestOp(SafeRefPtr<TransactionBase> aTransaction,
+ const int64_t aRequestId,
const RequestParams& aParams, bool aGetAll);
~ObjectStoreGetRequestOp() override = default;
@@ -3912,6 +4043,7 @@ class ObjectStoreGetKeyRequestOp final : public NormalTransactionOp {
private:
// Only created by TransactionBase.
ObjectStoreGetKeyRequestOp(SafeRefPtr<TransactionBase> aTransaction,
+ const int64_t aRequestId,
const RequestParams& aParams, bool aGetAll);
~ObjectStoreGetKeyRequestOp() override = default;
@@ -3930,6 +4062,7 @@ class ObjectStoreDeleteRequestOp final : public NormalTransactionOp {
private:
ObjectStoreDeleteRequestOp(SafeRefPtr<TransactionBase> aTransaction,
+ const int64_t aRequestId,
const ObjectStoreDeleteParams& aParams);
~ObjectStoreDeleteRequestOp() override = default;
@@ -3951,6 +4084,7 @@ class ObjectStoreClearRequestOp final : public NormalTransactionOp {
private:
ObjectStoreClearRequestOp(SafeRefPtr<TransactionBase> aTransaction,
+ const int64_t aRequestId,
const ObjectStoreClearParams& aParams);
~ObjectStoreClearRequestOp() override = default;
@@ -3971,8 +4105,10 @@ class ObjectStoreCountRequestOp final : public NormalTransactionOp {
private:
ObjectStoreCountRequestOp(SafeRefPtr<TransactionBase> aTransaction,
+ const int64_t aRequestId,
const ObjectStoreCountParams& aParams)
- : NormalTransactionOp(std::move(aTransaction)), mParams(aParams) {}
+ : NormalTransactionOp(std::move(aTransaction), aRequestId),
+ mParams(aParams) {}
~ObjectStoreCountRequestOp() override = default;
@@ -3990,8 +4126,8 @@ class IndexRequestOpBase : public NormalTransactionOp {
protected:
IndexRequestOpBase(SafeRefPtr<TransactionBase> aTransaction,
- const RequestParams& aParams)
- : NormalTransactionOp(std::move(aTransaction)),
+ const int64_t aRequestId, const RequestParams& aParams)
+ : NormalTransactionOp(std::move(aTransaction), aRequestId),
mMetadata(IndexMetadataForParams(Transaction(), aParams)) {}
~IndexRequestOpBase() override = default;
@@ -4014,7 +4150,8 @@ class IndexGetRequestOp final : public IndexRequestOpBase {
private:
// Only created by TransactionBase.
IndexGetRequestOp(SafeRefPtr<TransactionBase> aTransaction,
- const RequestParams& aParams, bool aGetAll);
+ const int64_t aRequestId, const RequestParams& aParams,
+ bool aGetAll);
~IndexGetRequestOp() override = default;
@@ -4034,7 +4171,8 @@ class IndexGetKeyRequestOp final : public IndexRequestOpBase {
private:
// Only created by TransactionBase.
IndexGetKeyRequestOp(SafeRefPtr<TransactionBase> aTransaction,
- const RequestParams& aParams, bool aGetAll);
+ const int64_t aRequestId, const RequestParams& aParams,
+ bool aGetAll);
~IndexGetKeyRequestOp() override = default;
@@ -4052,8 +4190,8 @@ class IndexCountRequestOp final : public IndexRequestOpBase {
private:
// Only created by TransactionBase.
IndexCountRequestOp(SafeRefPtr<TransactionBase> aTransaction,
- const RequestParams& aParams)
- : IndexRequestOpBase(std::move(aTransaction), aParams),
+ const int64_t aRequestId, const RequestParams& aParams)
+ : IndexRequestOpBase(std::move(aTransaction), aRequestId, aParams),
mParams(aParams.get_IndexCountParams()) {}
~IndexCountRequestOp() override = default;
@@ -4153,7 +4291,8 @@ class CursorBase : public PBackgroundIDBCursorParent {
~CursorBase() override { MOZ_ASSERT(!mObjectStoreMetadata); }
private:
- virtual bool Start(const OpenCursorParams& aParams) = 0;
+ virtual bool Start(const int64_t aRequestId,
+ const OpenCursorParams& aParams) = 0;
};
class IndexCursorBase : public CursorBase {
@@ -4311,7 +4450,7 @@ class Cursor final
LazyInitializedOnce<const typename Base::ContinueQueries> mContinueQueries;
// Only called by TransactionBase.
- bool Start(const OpenCursorParams& aParams) final;
+ bool Start(const int64_t aRequestId, const OpenCursorParams& aParams) final;
void SendResponseInternal(CursorResponse& aResponse,
const FilesArrayT<CursorType>& aFiles);
@@ -4325,8 +4464,8 @@ class Cursor final
mozilla::ipc::IPCResult RecvDeleteMe() override;
mozilla::ipc::IPCResult RecvContinue(
- const CursorRequestParams& aParams, const Key& aCurrentKey,
- const Key& aCurrentObjectStoreKey) override;
+ const int64_t& aRequestId, const CursorRequestParams& aParams,
+ const Key& aCurrentKey, const Key& aCurrentObjectStoreKey) override;
public:
Cursor(SafeRefPtr<TransactionBase> aTransaction,
@@ -4373,8 +4512,9 @@ class Cursor<CursorType>::CursorOpBase
#endif
protected:
- explicit CursorOpBase(Cursor* aCursor)
- : TransactionDatabaseOperationBase(aCursor->mTransaction.clonePtr()),
+ explicit CursorOpBase(Cursor* aCursor, const int64_t aRequestId)
+ : TransactionDatabaseOperationBase(aCursor->mTransaction.clonePtr(),
+ /* aRequestId */ aRequestId),
mCursor(aCursor)
#ifdef DEBUG
,
@@ -4552,9 +4692,10 @@ class Cursor<CursorType>::OpenOp final : public CursorOpBase {
using CursorOpBase::mResponse;
// Only created by Cursor.
- OpenOp(Cursor* const aCursor,
+ OpenOp(Cursor* const aCursor, const int64_t aRequestId,
const Maybe<SerializedKeyRange>& aOptionalKeyRange)
- : CursorOpBase(aCursor), mOptionalKeyRange(aOptionalKeyRange) {}
+ : CursorOpBase(aCursor, aRequestId),
+ mOptionalKeyRange(aOptionalKeyRange) {}
// Reference counted.
~OpenOp() override = default;
@@ -4572,9 +4713,9 @@ class Cursor<CursorType>::ContinueOp final
const CursorRequestParams mParams;
// Only created by Cursor.
- ContinueOp(Cursor* const aCursor, CursorRequestParams aParams,
- CursorPosition<CursorType> aPosition)
- : CursorOpBase(aCursor),
+ ContinueOp(Cursor* const aCursor, int64_t aRequestId,
+ CursorRequestParams aParams, CursorPosition<CursorType> aPosition)
+ : CursorOpBase(aCursor, aRequestId),
mParams(std::move(aParams)),
mCurrentPosition{std::move(aPosition)} {
MOZ_ASSERT(mParams.type() != CursorRequestParams::T__None);
@@ -4691,6 +4832,8 @@ class DatabaseLoggingInfo final {
};
class QuotaClient final : public mozilla::dom::quota::Client {
+ friend class GetDatabasesOp;
+
static QuotaClient* sInstance;
nsCOMPtr<nsIEventTarget> mBackgroundThread;
@@ -4824,8 +4967,9 @@ class QuotaClient final : public mozilla::dom::quota::Client {
// checks those unfinished deletion and clean them up after that.
template <ObsoleteFilenamesHandling ObsoleteFilenames =
ObsoleteFilenamesHandling::Omit>
- Result<GetDatabaseFilenamesResult<ObsoleteFilenames>, nsresult>
- GetDatabaseFilenames(nsIFile& aDirectory, const AtomicBool& aCanceled);
+ Result<GetDatabaseFilenamesResult<ObsoleteFilenames>,
+ nsresult> static GetDatabaseFilenames(nsIFile& aDirectory,
+ const AtomicBool& aCanceled);
nsresult GetUsageForOriginInternal(PersistenceType aPersistenceType,
const OriginMetadata& aOriginMetadata,
@@ -4866,6 +5010,9 @@ class DeleteFilesRunnable final : public Runnable {
RefPtr<DirectoryLock> mDirectoryLock;
nsTArray<int64_t> mFileIds;
State mState;
+ DEBUGONLY(bool mDEBUGCountsAsPending = false);
+
+ static uint64_t sPendingRunnables;
public:
DeleteFilesRunnable(SafeRefPtr<DatabaseFileManager> aFileManager,
@@ -4873,8 +5020,14 @@ class DeleteFilesRunnable final : public Runnable {
void RunImmediately();
+ static bool IsDeletionPending() { return sPendingRunnables > 0; }
+
private:
+#ifdef DEBUG
+ ~DeleteFilesRunnable();
+#else
~DeleteFilesRunnable() = default;
+#endif
void Open();
@@ -4953,6 +5106,7 @@ class Maintenance final : public Runnable {
PRTime mStartTime;
RefPtr<UniversalDirectoryLock> mPendingDirectoryLock;
RefPtr<UniversalDirectoryLock> mDirectoryLock;
+ nsTArray<nsCOMPtr<nsIRunnable>> mCompleteCallbacks;
nsTArray<DirectoryInfo> mDirectoryInfos;
nsTHashMap<nsStringHashKey, DatabaseMaintenance*> mDatabaseMaintenances;
nsresult mResultCode;
@@ -4994,6 +5148,8 @@ class Maintenance final : public Runnable {
void UnregisterDatabaseMaintenance(DatabaseMaintenance* aDatabaseMaintenance);
+ bool HasDatabaseMaintenances() const { return mDatabaseMaintenances.Count(); }
+
RefPtr<DatabaseMaintenance> GetDatabaseMaintenance(
const nsAString& aDatabasePath) const {
AssertIsOnBackgroundThread();
@@ -5001,6 +5157,13 @@ class Maintenance final : public Runnable {
return mDatabaseMaintenances.Get(aDatabasePath);
}
+ void WaitForCompletion(nsIRunnable* aCallback) {
+ AssertIsOnBackgroundThread();
+ MOZ_ASSERT(mDatabaseMaintenances.Count());
+
+ mCompleteCallbacks.AppendElement(aCallback);
+ }
+
void Stringify(nsACString& aResult) const;
private:
@@ -6410,9 +6573,9 @@ class DeserializeIndexValueHelper final : public Runnable {
value.setUndefined();
ErrorResult rv;
- IDBObjectStore::AppendIndexUpdateInfo(mIndexID, mKeyPath, mMultiEntry,
- mLocale, jsapi.cx(), value,
- &mUpdateInfoArray, &rv);
+ IDBObjectStore::AppendIndexUpdateInfo(
+ mIndexID, mKeyPath, mMultiEntry, &mUpdateInfoArray,
+ /* aAutoIncrementedObjectStoreKeyPath */ VoidString(), &rv);
return rv.Failed() ? rv.StealNSResult() : NS_OK;
}
#endif
@@ -6450,9 +6613,9 @@ class DeserializeIndexValueHelper final : public Runnable {
[this](const nsresult rv) { OperationCompleted(rv); });
ErrorResult errorResult;
- IDBObjectStore::AppendIndexUpdateInfo(mIndexID, mKeyPath, mMultiEntry,
- mLocale, cx, value, &mUpdateInfoArray,
- &errorResult);
+ IDBObjectStore::AppendIndexUpdateInfo(
+ mIndexID, mKeyPath, mMultiEntry, mLocale, cx, value, &mUpdateInfoArray,
+ /* aAutoIncrementedObjectStoreKeyPath */ VoidString(), &errorResult);
QM_TRY(OkIf(!errorResult.Failed()), NS_OK,
([this, &errorResult](const NotOk) {
OperationCompleted(errorResult.StealNSResult());
@@ -9111,20 +9274,9 @@ Factory::AllocPBackgroundIDBFactoryRequestParent(
return nullptr;
}
- Maybe<ContentParentId> contentParentId;
-
- uint64_t childID = BackgroundParent::GetChildID(Manager());
- if (childID) {
- // If childID is not zero we are dealing with an other-process actor. We
- // want to initialize OpenDatabaseOp/DeleteDatabaseOp here with the ID
- // (and later also Database) in that case, so Database::IsOwnedByProcess
- // can find Databases belonging to a particular content process when
- // QuotaClient::AbortOperationsForProcess is called which is currently used
- // to abort operations for content processes only.
- contentParentId = Some(ContentParentId(childID));
- }
+ Maybe<ContentParentId> contentParentId = GetContentParentId();
- auto actor = [&]() -> RefPtr<FactoryOp> {
+ auto actor = [&]() -> RefPtr<FactoryRequestOp> {
if (aParams.type() == FactoryRequestParams::TOpenDatabaseRequestParams) {
return MakeRefPtr<OpenDatabaseOp>(SafeRefPtrFromThis(), contentParentId,
*commonParams);
@@ -9151,7 +9303,7 @@ mozilla::ipc::IPCResult Factory::RecvPBackgroundIDBFactoryRequestConstructor(
MOZ_ASSERT(aParams.type() != FactoryRequestParams::T__None);
MOZ_ASSERT(!QuotaClient::IsShuttingDownOnBackgroundThread());
- auto* op = static_cast<FactoryOp*>(aActor);
+ auto* op = static_cast<FactoryRequestOp*>(aActor);
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(op));
return IPC_OK();
@@ -9163,10 +9315,69 @@ bool Factory::DeallocPBackgroundIDBFactoryRequestParent(
MOZ_ASSERT(aActor);
// Transfer ownership back from IPDL.
- RefPtr<FactoryOp> op = dont_AddRef(static_cast<FactoryOp*>(aActor));
+ RefPtr<FactoryRequestOp> op =
+ dont_AddRef(static_cast<FactoryRequestOp*>(aActor));
return true;
}
+mozilla::ipc::IPCResult Factory::RecvGetDatabases(
+ const PersistenceType& aPersistenceType,
+ const PrincipalInfo& aPrincipalInfo, GetDatabasesResolver&& aResolve) {
+ AssertIsOnBackgroundThread();
+
+ auto ResolveGetDatabasesAndReturn = [&aResolve](const nsresult rv) {
+ aResolve(rv);
+ return IPC_OK();
+ };
+
+ QM_TRY(MOZ_TO_RESULT(!QuotaClient::IsShuttingDownOnBackgroundThread()),
+ ResolveGetDatabasesAndReturn);
+
+ QM_TRY(MOZ_TO_RESULT(IsValidPersistenceType(aPersistenceType)),
+ QM_IPC_FAIL(this));
+
+ QM_TRY(MOZ_TO_RESULT(QuotaManager::IsPrincipalInfoValid(aPrincipalInfo)),
+ QM_IPC_FAIL(this));
+
+ MOZ_ASSERT(aPrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo ||
+ aPrincipalInfo.type() == PrincipalInfo::TContentPrincipalInfo);
+
+ PersistenceType persistenceType =
+ IDBFactory::GetPersistenceType(aPrincipalInfo);
+
+ QM_TRY(MOZ_TO_RESULT(aPersistenceType == persistenceType), QM_IPC_FAIL(this));
+
+ Maybe<ContentParentId> contentParentId = GetContentParentId();
+
+ auto op = MakeRefPtr<GetDatabasesOp>(SafeRefPtrFromThis(), contentParentId,
+ aPersistenceType, aPrincipalInfo,
+ std::move(aResolve));
+
+ gFactoryOps->AppendElement(op);
+
+ // Balanced in CleanupMetadata() which is/must always called by SendResults().
+ IncreaseBusyCount();
+
+ MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(op));
+
+ return IPC_OK();
+}
+
+Maybe<ContentParentId> Factory::GetContentParentId() const {
+ uint64_t childID = BackgroundParent::GetChildID(Manager());
+ if (childID) {
+ // If childID is not zero we are dealing with an other-process actor. We
+ // want to initialize OpenDatabaseOp/DeleteDatabaseOp here with the ID
+ // (and later also Database) in that case, so Database::IsOwnedByProcess
+ // can find Databases belonging to a particular content process when
+ // QuotaClient::AbortOperationsForProcess is called which is currently used
+ // to abort operations for content processes only.
+ return Some(ContentParentId(childID));
+ }
+
+ return Nothing();
+}
+
/*******************************************************************************
* WaitForTransactionsHelper
******************************************************************************/
@@ -9608,7 +9819,8 @@ bool Database::DeallocPBackgroundIDBDatabaseFileParent(
already_AddRefed<PBackgroundIDBTransactionParent>
Database::AllocPBackgroundIDBTransactionParent(
- const nsTArray<nsString>& aObjectStoreNames, const Mode& aMode) {
+ const nsTArray<nsString>& aObjectStoreNames, const Mode& aMode,
+ const Durability& aDurability) {
AssertIsOnBackgroundThread();
// Once a database is closed it must not try to open new transactions.
@@ -9628,6 +9840,12 @@ Database::AllocPBackgroundIDBTransactionParent(
return nullptr;
}
+ if (NS_AUUF_OR_WARN_IF(aDurability != IDBTransaction::Durability::Default &&
+ aDurability != IDBTransaction::Durability::Strict &&
+ aDurability != IDBTransaction::Durability::Relaxed)) {
+ return nullptr;
+ }
+
const ObjectStoreTable& objectStores = mMetadata->mObjectStores;
const uint32_t nameCount = aObjectStoreNames.Length();
@@ -9670,13 +9888,15 @@ Database::AllocPBackgroundIDBTransactionParent(
nullptr);
return MakeSafeRefPtr<NormalTransaction>(SafeRefPtrFromThis(), aMode,
+ aDurability,
std::move(objectStoreMetadatas))
.forget();
}
mozilla::ipc::IPCResult Database::RecvPBackgroundIDBTransactionConstructor(
PBackgroundIDBTransactionParent* aActor,
- nsTArray<nsString>&& aObjectStoreNames, const Mode& aMode) {
+ nsTArray<nsString>&& aObjectStoreNames, const Mode& aMode,
+ const Durability& aDurability) { // TODO: See bug 1883045
AssertIsOnBackgroundThread();
MOZ_ASSERT(aActor);
MOZ_ASSERT(!aObjectStoreNames.IsEmpty());
@@ -9684,6 +9904,9 @@ mozilla::ipc::IPCResult Database::RecvPBackgroundIDBTransactionConstructor(
aMode == IDBTransaction::Mode::ReadWrite ||
aMode == IDBTransaction::Mode::ReadWriteFlush ||
aMode == IDBTransaction::Mode::Cleanup);
+ MOZ_ASSERT(aDurability == IDBTransaction::Durability::Default ||
+ aDurability == IDBTransaction::Durability::Strict ||
+ aDurability == IDBTransaction::Durability::Relaxed);
MOZ_ASSERT(!mClosed);
if (IsInvalidated()) {
@@ -9815,7 +10038,8 @@ void Database::StartTransactionOp::Cleanup() {
* TransactionBase
******************************************************************************/
-TransactionBase::TransactionBase(SafeRefPtr<Database> aDatabase, Mode aMode)
+TransactionBase::TransactionBase(SafeRefPtr<Database> aDatabase, Mode aMode,
+ Durability aDurability)
: mDatabase(std::move(aDatabase)),
mDatabaseId(mDatabase->Id()),
mLoggingSerialNumber(
@@ -9823,6 +10047,7 @@ TransactionBase::TransactionBase(SafeRefPtr<Database> aDatabase, Mode aMode)
mActiveRequestCount(0),
mInvalidatedOnAnyThread(false),
mMode(aMode),
+ mDurability(aDurability),
mResultCode(NS_OK) {
AssertIsOnBackgroundThread();
MOZ_ASSERT(mDatabase);
@@ -9899,14 +10124,13 @@ void TransactionBase::CommitOrAbort() {
return;
}
- // In case of a failed request that was started after committing was
- // initiated, abort (cf.
- // https://w3c.github.io/IndexedDB/#async-execute-request step 5.3 vs. 5.4).
- // Note this can only happen here when we are committing explicitly, otherwise
- // the decision is made by the child.
+ // In case of a failed request and explicitly committed transaction, abort
+ // (cf. https://w3c.github.io/IndexedDB/#async-execute-request step 5.3
+ // vs. 5.4). It's worth emphasizing this can only happen here when we are
+ // committing explicitly, otherwise the decision is made by the child.
if (NS_SUCCEEDED(mResultCode) && mLastFailedRequest &&
*mLastRequestBeforeCommit &&
- *mLastFailedRequest >= **mLastRequestBeforeCommit) {
+ *mLastFailedRequest == **mLastRequestBeforeCommit) {
mResultCode = NS_ERROR_DOM_INDEXEDDB_ABORT_ERR;
}
@@ -10360,7 +10584,7 @@ void TransactionBase::Invalidate() {
}
PBackgroundIDBRequestParent* TransactionBase::AllocRequest(
- RequestParams&& aParams, bool aTrustParams) {
+ const int64_t aRequestId, RequestParams&& aParams, bool aTrustParams) {
AssertIsOnBackgroundThread();
MOZ_ASSERT(aParams.type() != RequestParams::T__None);
@@ -10382,67 +10606,77 @@ PBackgroundIDBRequestParent* TransactionBase::AllocRequest(
switch (aParams.type()) {
case RequestParams::TObjectStoreAddParams:
case RequestParams::TObjectStorePutParams:
- actor = new ObjectStoreAddOrPutRequestOp(SafeRefPtrFromThis(),
+ actor = new ObjectStoreAddOrPutRequestOp(SafeRefPtrFromThis(), aRequestId,
std::move(aParams));
break;
case RequestParams::TObjectStoreGetParams:
- actor = new ObjectStoreGetRequestOp(SafeRefPtrFromThis(), aParams,
- /* aGetAll */ false);
+ actor =
+ new ObjectStoreGetRequestOp(SafeRefPtrFromThis(), aRequestId, aParams,
+ /* aGetAll */ false);
break;
case RequestParams::TObjectStoreGetAllParams:
- actor = new ObjectStoreGetRequestOp(SafeRefPtrFromThis(), aParams,
- /* aGetAll */ true);
+ actor =
+ new ObjectStoreGetRequestOp(SafeRefPtrFromThis(), aRequestId, aParams,
+ /* aGetAll */ true);
break;
case RequestParams::TObjectStoreGetKeyParams:
- actor = new ObjectStoreGetKeyRequestOp(SafeRefPtrFromThis(), aParams,
+ actor = new ObjectStoreGetKeyRequestOp(SafeRefPtrFromThis(), aRequestId,
+ aParams,
/* aGetAll */ false);
break;
case RequestParams::TObjectStoreGetAllKeysParams:
- actor = new ObjectStoreGetKeyRequestOp(SafeRefPtrFromThis(), aParams,
+ actor = new ObjectStoreGetKeyRequestOp(SafeRefPtrFromThis(), aRequestId,
+ aParams,
/* aGetAll */ true);
break;
case RequestParams::TObjectStoreDeleteParams:
- actor = new ObjectStoreDeleteRequestOp(
- SafeRefPtrFromThis(), aParams.get_ObjectStoreDeleteParams());
+ actor =
+ new ObjectStoreDeleteRequestOp(SafeRefPtrFromThis(), aRequestId,
+ aParams.get_ObjectStoreDeleteParams());
break;
case RequestParams::TObjectStoreClearParams:
- actor = new ObjectStoreClearRequestOp(
- SafeRefPtrFromThis(), aParams.get_ObjectStoreClearParams());
+ actor =
+ new ObjectStoreClearRequestOp(SafeRefPtrFromThis(), aRequestId,
+ aParams.get_ObjectStoreClearParams());
break;
case RequestParams::TObjectStoreCountParams:
- actor = new ObjectStoreCountRequestOp(
- SafeRefPtrFromThis(), aParams.get_ObjectStoreCountParams());
+ actor =
+ new ObjectStoreCountRequestOp(SafeRefPtrFromThis(), aRequestId,
+ aParams.get_ObjectStoreCountParams());
break;
case RequestParams::TIndexGetParams:
- actor = new IndexGetRequestOp(SafeRefPtrFromThis(), aParams,
+ actor = new IndexGetRequestOp(SafeRefPtrFromThis(), aRequestId, aParams,
/* aGetAll */ false);
break;
case RequestParams::TIndexGetKeyParams:
- actor = new IndexGetKeyRequestOp(SafeRefPtrFromThis(), aParams,
- /* aGetAll */ false);
+ actor =
+ new IndexGetKeyRequestOp(SafeRefPtrFromThis(), aRequestId, aParams,
+ /* aGetAll */ false);
break;
case RequestParams::TIndexGetAllParams:
- actor = new IndexGetRequestOp(SafeRefPtrFromThis(), aParams,
+ actor = new IndexGetRequestOp(SafeRefPtrFromThis(), aRequestId, aParams,
/* aGetAll */ true);
break;
case RequestParams::TIndexGetAllKeysParams:
- actor = new IndexGetKeyRequestOp(SafeRefPtrFromThis(), aParams,
- /* aGetAll */ true);
+ actor =
+ new IndexGetKeyRequestOp(SafeRefPtrFromThis(), aRequestId, aParams,
+ /* aGetAll */ true);
break;
case RequestParams::TIndexCountParams:
- actor = new IndexCountRequestOp(SafeRefPtrFromThis(), aParams);
+ actor =
+ new IndexCountRequestOp(SafeRefPtrFromThis(), aRequestId, aParams);
break;
default:
@@ -10552,6 +10786,7 @@ already_AddRefed<PBackgroundIDBCursorParent> TransactionBase::AllocCursor(
}
bool TransactionBase::StartCursor(PBackgroundIDBCursorParent* const aActor,
+ const int64_t aRequestId,
const OpenCursorParams& aParams) {
AssertIsOnBackgroundThread();
MOZ_ASSERT(aActor);
@@ -10559,7 +10794,7 @@ bool TransactionBase::StartCursor(PBackgroundIDBCursorParent* const aActor,
auto* const op = static_cast<CursorBase*>(aActor);
- if (NS_WARN_IF(!op->Start(aParams))) {
+ if (NS_WARN_IF(!op->Start(aRequestId, aParams))) {
return false;
}
@@ -10572,8 +10807,9 @@ bool TransactionBase::StartCursor(PBackgroundIDBCursorParent* const aActor,
NormalTransaction::NormalTransaction(
SafeRefPtr<Database> aDatabase, TransactionBase::Mode aMode,
+ TransactionBase::Durability aDurability,
nsTArray<SafeRefPtr<FullObjectStoreMetadata>>&& aObjectStores)
- : TransactionBase(std::move(aDatabase), aMode),
+ : TransactionBase(std::move(aDatabase), aMode, aDurability),
mObjectStores{std::move(aObjectStores)} {
AssertIsOnBackgroundThread();
MOZ_ASSERT(!mObjectStores.IsEmpty());
@@ -10638,16 +10874,18 @@ mozilla::ipc::IPCResult NormalTransaction::RecvAbort(
PBackgroundIDBRequestParent*
NormalTransaction::AllocPBackgroundIDBRequestParent(
- const RequestParams& aParams) {
+ const int64_t& aRequestId, const RequestParams& aParams) {
AssertIsOnBackgroundThread();
MOZ_ASSERT(aParams.type() != RequestParams::T__None);
- return AllocRequest(std::move(const_cast<RequestParams&>(aParams)),
+ return AllocRequest(aRequestId,
+ std::move(const_cast<RequestParams&>(aParams)),
IsSameProcessActor());
}
mozilla::ipc::IPCResult NormalTransaction::RecvPBackgroundIDBRequestConstructor(
- PBackgroundIDBRequestParent* const aActor, const RequestParams& aParams) {
+ PBackgroundIDBRequestParent* const aActor, const int64_t& aRequestId,
+ const RequestParams& aParams) {
AssertIsOnBackgroundThread();
MOZ_ASSERT(aActor);
MOZ_ASSERT(aParams.type() != RequestParams::T__None);
@@ -10668,19 +10906,20 @@ bool NormalTransaction::DeallocPBackgroundIDBRequestParent(
already_AddRefed<PBackgroundIDBCursorParent>
NormalTransaction::AllocPBackgroundIDBCursorParent(
- const OpenCursorParams& aParams) {
+ const int64_t& aRequestId, const OpenCursorParams& aParams) {
AssertIsOnBackgroundThread();
return AllocCursor(aParams, IsSameProcessActor());
}
mozilla::ipc::IPCResult NormalTransaction::RecvPBackgroundIDBCursorConstructor(
- PBackgroundIDBCursorParent* const aActor, const OpenCursorParams& aParams) {
+ PBackgroundIDBCursorParent* const aActor, const int64_t& aRequestId,
+ const OpenCursorParams& aParams) {
AssertIsOnBackgroundThread();
MOZ_ASSERT(aActor);
MOZ_ASSERT(aParams.type() != OpenCursorParams::T__None);
- if (!StartCursor(aActor, aParams)) {
+ if (!StartCursor(aActor, aRequestId, aParams)) {
return IPC_FAIL(this, "StartCursor failed!");
}
return IPC_OK();
@@ -10693,7 +10932,9 @@ mozilla::ipc::IPCResult NormalTransaction::RecvPBackgroundIDBCursorConstructor(
VersionChangeTransaction::VersionChangeTransaction(
OpenDatabaseOp* aOpenDatabaseOp)
: TransactionBase(aOpenDatabaseOp->mDatabase.clonePtr(),
- IDBTransaction::Mode::VersionChange),
+ IDBTransaction::Mode::VersionChange,
+ // VersionChange must not change durability.
+ IDBTransaction::Durability::Default), // Not used.
mOpenDatabaseOp(aOpenDatabaseOp) {
AssertIsOnBackgroundThread();
MOZ_ASSERT(aOpenDatabaseOp);
@@ -10755,7 +10996,7 @@ void VersionChangeTransaction::UpdateMetadata(nsresult aResult) {
AssertIsOnBackgroundThread();
MOZ_ASSERT(mOpenDatabaseOp);
MOZ_ASSERT(!!mActorWasAlive == !!mOpenDatabaseOp->mDatabase);
- MOZ_ASSERT_IF(mActorWasAlive, !mOpenDatabaseOp->mDatabaseId.IsEmpty());
+ MOZ_ASSERT_IF(mActorWasAlive, !mOpenDatabaseOp->mDatabaseId.ref().IsEmpty());
if (IsActorDestroyed() || !mActorWasAlive) {
return;
@@ -11224,17 +11465,19 @@ mozilla::ipc::IPCResult VersionChangeTransaction::RecvRenameIndex(
PBackgroundIDBRequestParent*
VersionChangeTransaction::AllocPBackgroundIDBRequestParent(
- const RequestParams& aParams) {
+ const int64_t& aRequestId, const RequestParams& aParams) {
AssertIsOnBackgroundThread();
MOZ_ASSERT(aParams.type() != RequestParams::T__None);
- return AllocRequest(std::move(const_cast<RequestParams&>(aParams)),
+ return AllocRequest(aRequestId,
+ std::move(const_cast<RequestParams&>(aParams)),
IsSameProcessActor());
}
mozilla::ipc::IPCResult
VersionChangeTransaction::RecvPBackgroundIDBRequestConstructor(
- PBackgroundIDBRequestParent* aActor, const RequestParams& aParams) {
+ PBackgroundIDBRequestParent* aActor, const int64_t& aRequestId,
+ const RequestParams& aParams) {
AssertIsOnBackgroundThread();
MOZ_ASSERT(aActor);
MOZ_ASSERT(aParams.type() != RequestParams::T__None);
@@ -11255,7 +11498,7 @@ bool VersionChangeTransaction::DeallocPBackgroundIDBRequestParent(
already_AddRefed<PBackgroundIDBCursorParent>
VersionChangeTransaction::AllocPBackgroundIDBCursorParent(
- const OpenCursorParams& aParams) {
+ const int64_t& aRequestId, const OpenCursorParams& aParams) {
AssertIsOnBackgroundThread();
return AllocCursor(aParams, IsSameProcessActor());
@@ -11263,12 +11506,13 @@ VersionChangeTransaction::AllocPBackgroundIDBCursorParent(
mozilla::ipc::IPCResult
VersionChangeTransaction::RecvPBackgroundIDBCursorConstructor(
- PBackgroundIDBCursorParent* aActor, const OpenCursorParams& aParams) {
+ PBackgroundIDBCursorParent* aActor, const int64_t& aRequestId,
+ const OpenCursorParams& aParams) {
AssertIsOnBackgroundThread();
MOZ_ASSERT(aActor);
MOZ_ASSERT(aParams.type() != OpenCursorParams::T__None);
- if (!StartCursor(aActor, aParams)) {
+ if (!StartCursor(aActor, aRequestId, aParams)) {
return IPC_FAIL(this, "StartCursor failed!");
}
return IPC_OK();
@@ -11417,7 +11661,8 @@ bool Cursor<CursorType>::VerifyRequestParams(
}
template <IDBCursorType CursorType>
-bool Cursor<CursorType>::Start(const OpenCursorParams& aParams) {
+bool Cursor<CursorType>::Start(const int64_t aRequestId,
+ const OpenCursorParams& aParams) {
AssertIsOnBackgroundThread();
MOZ_ASSERT(aParams.type() == ToOpenCursorParamsType(CursorType));
MOZ_ASSERT(this->mObjectStoreMetadata);
@@ -11429,7 +11674,7 @@ bool Cursor<CursorType>::Start(const OpenCursorParams& aParams) {
const Maybe<SerializedKeyRange>& optionalKeyRange =
GetCommonOpenCursorParams(aParams).optionalKeyRange();
- const RefPtr<OpenOp> openOp = new OpenOp(this, optionalKeyRange);
+ const RefPtr<OpenOp> openOp = new OpenOp(this, aRequestId, optionalKeyRange);
if (NS_WARN_IF(!openOp->Init(*mTransaction))) {
openOp->Cleanup();
@@ -11553,8 +11798,8 @@ mozilla::ipc::IPCResult Cursor<CursorType>::RecvDeleteMe() {
template <IDBCursorType CursorType>
mozilla::ipc::IPCResult Cursor<CursorType>::RecvContinue(
- const CursorRequestParams& aParams, const Key& aCurrentKey,
- const Key& aCurrentObjectStoreKey) {
+ const int64_t& aRequestId, const CursorRequestParams& aParams,
+ const Key& aCurrentKey, const Key& aCurrentObjectStoreKey) {
AssertIsOnBackgroundThread();
MOZ_ASSERT(aParams.type() != CursorRequestParams::T__None);
MOZ_ASSERT(this->mObjectStoreMetadata);
@@ -11604,7 +11849,7 @@ mozilla::ipc::IPCResult Cursor<CursorType>::RecvContinue(
}
const RefPtr<ContinueOp> continueOp =
- new ContinueOp(this, aParams, std::move(position));
+ new ContinueOp(this, aRequestId, aParams, std::move(position));
if (NS_WARN_IF(!continueOp->Init(*mTransaction))) {
continueOp->Cleanup();
return IPC_FAIL(this, "ContinueOp initialization failed!");
@@ -11626,15 +11871,18 @@ DatabaseFileManager::DatabaseFileManager(
PersistenceType aPersistenceType,
const quota::OriginMetadata& aOriginMetadata,
const nsAString& aDatabaseName, const nsCString& aDatabaseID,
- bool aEnforcingQuota, bool aIsInPrivateBrowsingMode)
+ const nsAString& aDatabaseFilePath, bool aEnforcingQuota,
+ bool aIsInPrivateBrowsingMode)
: mPersistenceType(aPersistenceType),
mOriginMetadata(aOriginMetadata),
mDatabaseName(aDatabaseName),
mDatabaseID(aDatabaseID),
+ mDatabaseFilePath(aDatabaseFilePath),
mCipherKeyManager(
aIsInPrivateBrowsingMode
? new IndexedDBCipherKeyManager("IndexedDBCipherKeyManager")
: nullptr),
+ mDatabaseVersion(0),
mEnforcingQuota(aEnforcingQuota),
mIsInPrivateBrowsingMode(aIsInPrivateBrowsingMode) {}
@@ -12488,6 +12736,17 @@ void QuotaClient::StopIdleMaintenance() {
void QuotaClient::InitiateShutdown() {
AssertIsOnBackgroundThread();
+ MOZ_ASSERT(IsShuttingDownOnBackgroundThread());
+
+ if (mDeleteTimer) {
+ // QuotaClient::AsyncDeleteFile will not schedule new timers beyond
+ // shutdown. And we expect all critical (PBM) deletions to have been
+ // triggered before this point via ClearPrivateRepository (w/out using
+ // DeleteFilesRunnable at all).
+ mDeleteTimer->Cancel();
+ mDeleteTimer = nullptr;
+ mPendingDeleteInfos.Clear();
+ }
AbortAllOperations();
}
@@ -12495,7 +12754,7 @@ void QuotaClient::InitiateShutdown() {
bool QuotaClient::IsShutdownCompleted() const {
return (!gFactoryOps || gFactoryOps->IsEmpty()) &&
(!gLiveDatabaseHashtable || !gLiveDatabaseHashtable->Count()) &&
- !mCurrentMaintenance;
+ !mCurrentMaintenance && !DeleteFilesRunnable::IsDeletionPending();
}
void QuotaClient::ForceKillActors() {
@@ -12575,17 +12834,20 @@ void QuotaClient::FinalizeShutdown() {
mMaintenanceThreadPool->Shutdown();
mMaintenanceThreadPool = nullptr;
}
-
- if (mDeleteTimer) {
- MOZ_ALWAYS_SUCCEEDS(mDeleteTimer->Cancel());
- mDeleteTimer = nullptr;
- }
}
void QuotaClient::DeleteTimerCallback(nsITimer* aTimer, void* aClosure) {
AssertIsOnBackgroundThread();
MOZ_ASSERT(aTimer);
+ // Even though we do not schedule new timers after shutdown has started,
+ // an already existing one might fire afterwards (actually we think it
+ // shouldn't, but there is no reason to enforce this invariant). We can
+ // just ignore it, the cleanup work is done in InitiateShutdown.
+ if (NS_WARN_IF(IsShuttingDownOnBackgroundThread())) {
+ return;
+ }
+
auto* const self = static_cast<QuotaClient*>(aClosure);
MOZ_ASSERT(self);
MOZ_ASSERT(self->mDeleteTimer);
@@ -12728,6 +12990,8 @@ void QuotaClient::ProcessMaintenanceQueue() {
* DeleteFilesRunnable
******************************************************************************/
+uint64_t DeleteFilesRunnable::sPendingRunnables = 0;
+
DeleteFilesRunnable::DeleteFilesRunnable(
SafeRefPtr<DatabaseFileManager> aFileManager, nsTArray<int64_t>&& aFileIds)
: Runnable("dom::indexeddb::DeleteFilesRunnable"),
@@ -12736,6 +13000,12 @@ DeleteFilesRunnable::DeleteFilesRunnable(
mFileIds(std::move(aFileIds)),
mState(State_Initial) {}
+#ifdef DEBUG
+DeleteFilesRunnable::~DeleteFilesRunnable() {
+ MOZ_ASSERT(!mDEBUGCountsAsPending);
+}
+#endif
+
void DeleteFilesRunnable::RunImmediately() {
AssertIsOnBackgroundThread();
MOZ_ASSERT(mState == State_Initial);
@@ -12747,6 +13017,10 @@ void DeleteFilesRunnable::Open() {
AssertIsOnBackgroundThread();
MOZ_ASSERT(mState == State_Initial);
+ MOZ_ASSERT(!mDEBUGCountsAsPending);
+ sPendingRunnables++;
+ DEBUGONLY(mDEBUGCountsAsPending = true);
+
QuotaManager* const quotaManager = QuotaManager::Get();
if (NS_WARN_IF(!quotaManager)) {
Finish();
@@ -12800,6 +13074,9 @@ void DeleteFilesRunnable::UnblockOpen() {
MOZ_ASSERT(mState == State_UnblockingOpen);
mDirectoryLock = nullptr;
+ MOZ_ASSERT(mDEBUGCountsAsPending);
+ sPendingRunnables--;
+ DEBUGONLY(mDEBUGCountsAsPending = false);
mState = State_Completed;
}
@@ -12892,6 +13169,11 @@ void Maintenance::UnregisterDatabaseMaintenance(
return;
}
+ for (const auto& completeCallback : mCompleteCallbacks) {
+ MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(completeCallback));
+ }
+ mCompleteCallbacks.Clear();
+
mState = State::Finishing;
Finish();
}
@@ -13289,6 +13571,10 @@ nsresult Maintenance::BeginDatabaseMaintenance() {
for (uint32_t index = gFactoryOps->Length(); index > 0; index--) {
CheckedUnsafePtr<FactoryOp>& existingOp = (*gFactoryOps)[index - 1];
+ if (existingOp->DatabaseNameRef().isNothing()) {
+ return false;
+ }
+
if (!existingOp->DatabaseFilePathIsKnown()) {
continue;
}
@@ -14448,14 +14734,17 @@ void DatabaseOperationBase::AutoSetProgressHandler::Unregister() {
FactoryOp::FactoryOp(SafeRefPtr<Factory> aFactory,
const Maybe<ContentParentId>& aContentParentId,
- const CommonFactoryRequestParams& aCommonParams,
- bool aDeleting)
+ const PersistenceType aPersistenceType,
+ const PrincipalInfo& aPrincipalInfo,
+ const Maybe<nsString>& aDatabaseName, bool aDeleting)
: DatabaseOperationBase(aFactory->GetLoggingInfo()->Id(),
aFactory->GetLoggingInfo()->NextRequestSN()),
mFactory(std::move(aFactory)),
mContentParentId(aContentParentId),
- mCommonParams(aCommonParams),
+ mPrincipalInfo(aPrincipalInfo),
+ mDatabaseName(aDatabaseName),
mDirectoryLockId(-1),
+ mPersistenceType(aPersistenceType),
mState(State::Initial),
mWaitingForPermissionRetry(false),
mEnforcingQuota(true),
@@ -14506,7 +14795,7 @@ void FactoryOp::NoteDatabaseClosed(Database* const aDatabase) {
}
DatabaseActorInfo* info;
- MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(mDatabaseId, &info));
+ MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(mDatabaseId.ref(), &info));
MOZ_ASSERT(info->mWaitingFactoryOp == this);
if (AreActorsAlive()) {
@@ -14547,6 +14836,14 @@ void FactoryOp::StringifyState(nsACString& aResult) const {
aResult.AppendLiteral("DirectoryOpenPending");
return;
+ case State::DirectoryWorkOpen:
+ aResult.AppendLiteral("DirectoryWorkOpen");
+ return;
+
+ case State::DirectoryWorkDone:
+ aResult.AppendLiteral("DirectoryWorkDone");
+ return;
+
case State::DatabaseOpenPending:
aResult.AppendLiteral("DatabaseOpenPending");
return;
@@ -14588,8 +14885,7 @@ void FactoryOp::Stringify(nsACString& aResult) const {
AssertIsOnOwningThread();
aResult.AppendLiteral("PersistenceType:");
- aResult.Append(
- PersistenceTypeToString(mCommonParams.metadata().persistenceType()));
+ aResult.Append(PersistenceTypeToString(mPersistenceType));
aResult.Append(kQuotaGenericDelimiter);
aResult.AppendLiteral("Origin:");
@@ -14617,32 +14913,25 @@ nsresult FactoryOp::Open() {
QuotaManager* const quotaManager = QuotaManager::Get();
MOZ_ASSERT(quotaManager);
- const DatabaseMetadata& metadata = mCommonParams.metadata();
-
- const PersistenceType persistenceType = metadata.persistenceType();
-
- const PrincipalInfo& principalInfo = mCommonParams.principalInfo();
-
- QM_TRY_UNWRAP(auto principalMetadata,
- quotaManager->GetInfoFromValidatedPrincipalInfo(principalInfo));
+ QM_TRY_UNWRAP(
+ auto principalMetadata,
+ quotaManager->GetInfoFromValidatedPrincipalInfo(mPrincipalInfo));
- mOriginMetadata = {std::move(principalMetadata), persistenceType};
+ mOriginMetadata = {std::move(principalMetadata), mPersistenceType};
- if (principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
- MOZ_ASSERT(mCommonParams.metadata().persistenceType() ==
- PERSISTENCE_TYPE_PERSISTENT);
+ if (mPrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
+ MOZ_ASSERT(mPersistenceType == PERSISTENCE_TYPE_PERSISTENT);
mEnforcingQuota = false;
- } else if (principalInfo.type() == PrincipalInfo::TContentPrincipalInfo) {
+ } else if (mPrincipalInfo.type() == PrincipalInfo::TContentPrincipalInfo) {
const ContentPrincipalInfo& contentPrincipalInfo =
- principalInfo.get_ContentPrincipalInfo();
+ mPrincipalInfo.get_ContentPrincipalInfo();
MOZ_ASSERT_IF(
QuotaManager::IsOriginInternal(contentPrincipalInfo.originNoSuffix()),
- mCommonParams.metadata().persistenceType() ==
- PERSISTENCE_TYPE_PERSISTENT);
+ mPersistenceType == PERSISTENCE_TYPE_PERSISTENT);
- mEnforcingQuota = persistenceType != PERSISTENCE_TYPE_PERSISTENT;
+ mEnforcingQuota = mPersistenceType != PERSISTENCE_TYPE_PERSISTENT;
if (mOriginMetadata.mIsPrivate) {
if (StaticPrefs::dom_indexedDB_privateBrowsing_enabled()) {
@@ -14663,31 +14952,39 @@ nsresult FactoryOp::Open() {
MOZ_ASSERT(false);
}
- QuotaManager::GetStorageId(persistenceType, mOriginMetadata.mOrigin,
- Client::IDB, mDatabaseId);
+ if (mDatabaseName.isSome()) {
+ nsCString databaseId;
- mDatabaseId.Append('*');
- mDatabaseId.Append(NS_ConvertUTF16toUTF8(metadata.name()));
+ QuotaManager::GetStorageId(mPersistenceType, mOriginMetadata.mOrigin,
+ Client::IDB, databaseId);
- // Need to get database file path before opening the directory.
- // XXX: For what reason?
- QM_TRY_UNWRAP(
- mDatabaseFilePath,
- ([this, metadata, quotaManager]() -> mozilla::Result<nsString, nsresult> {
- QM_TRY_INSPECT(const auto& dbFile,
- quotaManager->GetOriginDirectory(mOriginMetadata));
+ databaseId.Append('*');
+ databaseId.Append(NS_ConvertUTF16toUTF8(mDatabaseName.ref()));
- QM_TRY(MOZ_TO_RESULT(dbFile->Append(
- NS_LITERAL_STRING_FROM_CSTRING(IDB_DIRECTORY_NAME))));
+ mDatabaseId = Some(std::move(databaseId));
- QM_TRY(MOZ_TO_RESULT(
- dbFile->Append(GetDatabaseFilenameBase(metadata.name(),
- mOriginMetadata.mIsPrivate) +
- kSQLiteSuffix)));
+ // Need to get database file path before opening the directory.
+ // XXX: For what reason?
+ QM_TRY_UNWRAP(
+ auto databaseFilePath,
+ ([this, quotaManager]() -> mozilla::Result<nsString, nsresult> {
+ QM_TRY_INSPECT(const auto& dbFile,
+ quotaManager->GetOriginDirectory(mOriginMetadata));
- QM_TRY_RETURN(
- MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsString, dbFile, GetPath));
- }()));
+ QM_TRY(MOZ_TO_RESULT(dbFile->Append(
+ NS_LITERAL_STRING_FROM_CSTRING(IDB_DIRECTORY_NAME))));
+
+ QM_TRY(MOZ_TO_RESULT(dbFile->Append(
+ GetDatabaseFilenameBase(mDatabaseName.ref(),
+ mOriginMetadata.mIsPrivate) +
+ kSQLiteSuffix)));
+
+ QM_TRY_RETURN(
+ MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsString, dbFile, GetPath));
+ }()));
+
+ mDatabaseFilePath = Some(std::move(databaseFilePath));
+ }
// Open directory
mState = State::DirectoryOpenPending;
@@ -14711,47 +15008,77 @@ nsresult FactoryOp::DirectoryOpen() {
AssertIsOnOwningThread();
MOZ_ASSERT(mState == State::DirectoryOpenPending);
MOZ_ASSERT(mDirectoryLock);
- MOZ_ASSERT(!mDatabaseFilePath.IsEmpty());
+
+ if (mDatabaseName.isNothing()) {
+ QuotaManager* const quotaManager = QuotaManager::Get();
+ MOZ_ASSERT(quotaManager);
+
+ // Must set this before dispatching otherwise we will race with the IO
+ // thread.
+ mState = State::DirectoryWorkOpen;
+
+ QM_TRY(MOZ_TO_RESULT(
+ quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL)),
+ NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR, IDB_REPORT_INTERNAL_ERR_LAMBDA);
+
+ return NS_OK;
+ }
+
+ mState = State::DirectoryWorkDone;
+ MOZ_ALWAYS_SUCCEEDS(Run());
+
+ return NS_OK;
+}
+
+nsresult FactoryOp::DirectoryWorkDone() {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mState == State::DirectoryWorkDone);
+ MOZ_ASSERT(mDirectoryLock);
MOZ_ASSERT(gFactoryOps);
// See if this FactoryOp needs to wait.
- const bool delayed =
- std::any_of(
- gFactoryOps->rbegin(), gFactoryOps->rend(),
- [foundThis = false, &self = *this](const auto& existingOp) mutable {
- if (existingOp == &self) {
- foundThis = true;
- return false;
- }
+ const bool blocked = [&self = *this] {
+ bool foundThis = false;
+ bool blocked = false;
- if (foundThis && self.MustWaitFor(*existingOp)) {
- // Only one op can be delayed.
- MOZ_ASSERT(!existingOp->mDelayedOp);
- existingOp->mDelayedOp = &self;
- return true;
- }
+ for (const auto& existingOp : Reversed(*gFactoryOps)) {
+ if (existingOp == &self) {
+ foundThis = true;
+ continue;
+ }
- return false;
- }) ||
- [&self = *this] {
- QuotaClient* quotaClient = QuotaClient::GetInstance();
- MOZ_ASSERT(quotaClient);
-
- if (RefPtr<Maintenance> currentMaintenance =
- quotaClient->GetCurrentMaintenance()) {
- if (RefPtr<DatabaseMaintenance> databaseMaintenance =
- currentMaintenance->GetDatabaseMaintenance(
- self.mDatabaseFilePath)) {
- databaseMaintenance->WaitForCompletion(&self);
- return true;
- }
+ if (foundThis && self.MustWaitFor(*existingOp)) {
+ existingOp->AddBlockingOp(self);
+ self.AddBlockedOnOp(*existingOp);
+ blocked = true;
+ }
+ }
+
+ return blocked;
+ }() || [&self = *this] {
+ QuotaClient* quotaClient = QuotaClient::GetInstance();
+ MOZ_ASSERT(quotaClient);
+
+ if (RefPtr<Maintenance> currentMaintenance =
+ quotaClient->GetCurrentMaintenance()) {
+ if (self.mDatabaseName.isSome()) {
+ if (RefPtr<DatabaseMaintenance> databaseMaintenance =
+ currentMaintenance->GetDatabaseMaintenance(
+ self.mDatabaseFilePath.ref())) {
+ databaseMaintenance->WaitForCompletion(&self);
+ return true;
}
+ } else if (currentMaintenance->HasDatabaseMaintenances()) {
+ currentMaintenance->WaitForCompletion(&self);
+ return true;
+ }
+ }
- return false;
- }();
+ return false;
+ }();
mState = State::DatabaseOpenPending;
- if (!delayed) {
+ if (!blocked) {
QM_TRY(MOZ_TO_RESULT(DatabaseOpen()));
}
@@ -14785,22 +15112,23 @@ void FactoryOp::WaitForTransactions() {
AssertIsOnOwningThread();
MOZ_ASSERT(mState == State::BeginVersionChange ||
mState == State::WaitingForOtherDatabasesToClose);
- MOZ_ASSERT(!mDatabaseId.IsEmpty());
+ MOZ_ASSERT(!mDatabaseId.ref().IsEmpty());
MOZ_ASSERT(!IsActorDestroyed());
mState = State::WaitingForTransactionsToComplete;
RefPtr<WaitForTransactionsHelper> helper =
- new WaitForTransactionsHelper(mDatabaseId, this);
+ new WaitForTransactionsHelper(mDatabaseId.ref(), this);
helper->WaitForTransactions();
}
void FactoryOp::CleanupMetadata() {
AssertIsOnOwningThread();
- if (mDelayedOp) {
- MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(mDelayedOp.forget()));
+ for (const NotNull<RefPtr<FactoryOp>>& blockingOp : mBlocking) {
+ blockingOp->MaybeUnblock(*this);
}
+ mBlocking.Clear();
MOZ_ASSERT(gFactoryOps);
gFactoryOps->RemoveElement(this);
@@ -14868,12 +15196,23 @@ nsresult FactoryOp::SendVersionChangeMessages(
bool FactoryOp::MustWaitFor(const FactoryOp& aExistingOp) {
AssertIsOnOwningThread();
- // Things for the same persistence type, the same origin and the same
- // database must wait.
- return aExistingOp.mCommonParams.metadata().persistenceType() ==
- mCommonParams.metadata().persistenceType() &&
- aExistingOp.mOriginMetadata.mOrigin == mOriginMetadata.mOrigin &&
- aExistingOp.mDatabaseId == mDatabaseId;
+ // If the persistence types don't overlap, the op can proceed.
+ if (aExistingOp.mPersistenceType != mPersistenceType) {
+ return false;
+ }
+
+ // If the origins don't overlap, the op can proceed.
+ if (aExistingOp.mOriginMetadata.mOrigin != mOriginMetadata.mOrigin) {
+ return false;
+ }
+
+ // If the database ids don't overlap, the op can proceed.
+ if (!aExistingOp.mDatabaseId.isNothing() && !mDatabaseId.isNothing() &&
+ aExistingOp.mDatabaseId.ref() != mDatabaseId.ref()) {
+ return false;
+ }
+
+ return true;
}
// Run() assumes that the caller holds a strong reference to the object that
@@ -14905,6 +15244,14 @@ FactoryOp::Run() {
QM_WARNONLY_TRY(MOZ_TO_RESULT(Open()), handleError);
break;
+ case State::DirectoryWorkOpen:
+ QM_WARNONLY_TRY(MOZ_TO_RESULT(DoDirectoryWork()), handleError);
+ break;
+
+ case State::DirectoryWorkDone:
+ QM_WARNONLY_TRY(MOZ_TO_RESULT(DirectoryWorkDone()), handleError);
+ break;
+
case State::DatabaseOpenPending:
QM_WARNONLY_TRY(MOZ_TO_RESULT(DatabaseOpen()), handleError);
break;
@@ -14971,7 +15318,11 @@ void FactoryOp::DirectoryLockFailed() {
MOZ_ALWAYS_SUCCEEDS(Run());
}
-void FactoryOp::ActorDestroy(ActorDestroyReason aWhy) {
+nsresult FactoryRequestOp::DoDirectoryWork() {
+ MOZ_CRASH("Not implemented because this should be unreachable.");
+}
+
+void FactoryRequestOp::ActorDestroy(ActorDestroyReason aWhy) {
AssertIsOnBackgroundThread();
NoteActorDestroyed();
@@ -14980,8 +15331,8 @@ void FactoryOp::ActorDestroy(ActorDestroyReason aWhy) {
OpenDatabaseOp::OpenDatabaseOp(SafeRefPtr<Factory> aFactory,
const Maybe<ContentParentId>& aContentParentId,
const CommonFactoryRequestParams& aParams)
- : FactoryOp(std::move(aFactory), aContentParentId, aParams,
- /* aDeleting */ false),
+ : FactoryRequestOp(std::move(aFactory), aContentParentId, aParams,
+ /* aDeleting */ false),
mMetadata(MakeSafeRefPtr<FullDatabaseMetadata>(aParams.metadata())),
mRequestedVersion(aParams.metadata().version()),
mVersionChangeOp(nullptr),
@@ -14990,7 +15341,7 @@ OpenDatabaseOp::OpenDatabaseOp(SafeRefPtr<Factory> aFactory,
void OpenDatabaseOp::ActorDestroy(ActorDestroyReason aWhy) {
AssertIsOnOwningThread();
- FactoryOp::ActorDestroy(aWhy);
+ FactoryRequestOp::ActorDestroy(aWhy);
if (mVersionChangeOp) {
mVersionChangeOp->NoteActorDestroyed();
@@ -15097,7 +15448,7 @@ nsresult OpenDatabaseOp::DoDatabaseWork() {
const auto& databaseFilePath,
MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsString, dbFile, GetPath));
- MOZ_ASSERT(databaseFilePath == mDatabaseFilePath);
+ MOZ_ASSERT(databaseFilePath == mDatabaseFilePath.ref());
}
#endif
@@ -15114,8 +15465,8 @@ nsresult OpenDatabaseOp::DoDatabaseWork() {
if (!fileManager) {
fileManager = MakeSafeRefPtr<DatabaseFileManager>(
- persistenceType, mOriginMetadata, databaseName, mDatabaseId,
- mEnforcingQuota, mInPrivateBrowsing);
+ persistenceType, mOriginMetadata, databaseName, mDatabaseId.ref(),
+ mDatabaseFilePath.ref(), mEnforcingQuota, mInPrivateBrowsing);
}
Maybe<const CipherKey> maybeKey =
@@ -15555,7 +15906,7 @@ nsresult OpenDatabaseOp::BeginVersionChange() {
MOZ_ASSERT(!mDatabase->IsClosed());
DatabaseActorInfo* info;
- MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(mDatabaseId, &info));
+ MOZ_ALWAYS_TRUE(gLiveDatabaseHashtable->Get(mDatabaseId.ref(), &info));
MOZ_ASSERT(info->mLiveDatabases.Contains(mDatabase.unsafeGetRawPtr()));
MOZ_ASSERT(!info->mWaitingFactoryOp);
@@ -15699,9 +16050,9 @@ void OpenDatabaseOp::SendResults() {
MOZ_ASSERT_IF(!HasFailed(), !mVersionChangeTransaction);
DebugOnly<DatabaseActorInfo*> info = nullptr;
- MOZ_ASSERT_IF(
- gLiveDatabaseHashtable && gLiveDatabaseHashtable->Get(mDatabaseId, &info),
- !info->mWaitingFactoryOp);
+ MOZ_ASSERT_IF(mDatabaseId.isSome() && gLiveDatabaseHashtable &&
+ gLiveDatabaseHashtable->Get(mDatabaseId.ref(), &info),
+ !info->mWaitingFactoryOp);
if (mVersionChangeTransaction) {
MOZ_ASSERT(HasFailed());
@@ -15720,6 +16071,8 @@ void OpenDatabaseOp::SendResults() {
// need to update the version in our metadata.
mMetadata->mCommonMetadata.version() = mRequestedVersion;
+ mFileManager->UpdateDatabaseVersion(mRequestedVersion);
+
nsresult rv = EnsureDatabaseActorIsAlive();
if (NS_SUCCEEDED(rv)) {
// We successfully opened a database so use its actor as the success
@@ -15767,7 +16120,7 @@ void OpenDatabaseOp::SendResults() {
&OpenDatabaseOp::ConnectionClosedCallback);
RefPtr<WaitForTransactionsHelper> helper =
- new WaitForTransactionsHelper(mDatabaseId, callback);
+ new WaitForTransactionsHelper(mDatabaseId.ref(), callback);
helper->WaitForTransactions();
} else {
CleanupMetadata();
@@ -15792,7 +16145,7 @@ void OpenDatabaseOp::EnsureDatabaseActor() {
mState == State::DatabaseWorkVersionChange ||
mState == State::SendingResults);
MOZ_ASSERT(!HasFailed());
- MOZ_ASSERT(!mDatabaseFilePath.IsEmpty());
+ MOZ_ASSERT(mDatabaseFilePath.isSome());
MOZ_ASSERT(!IsActorDestroyed());
if (mDatabase) {
@@ -15800,13 +16153,13 @@ void OpenDatabaseOp::EnsureDatabaseActor() {
}
MOZ_ASSERT(mMetadata->mDatabaseId.IsEmpty());
- mMetadata->mDatabaseId = mDatabaseId;
+ mMetadata->mDatabaseId = mDatabaseId.ref();
MOZ_ASSERT(mMetadata->mFilePath.IsEmpty());
- mMetadata->mFilePath = mDatabaseFilePath;
+ mMetadata->mFilePath = mDatabaseFilePath.ref();
DatabaseActorInfo* info;
- if (gLiveDatabaseHashtable->Get(mDatabaseId, &info)) {
+ if (gLiveDatabaseHashtable->Get(mDatabaseId.ref(), &info)) {
AssertMetadataConsistency(*info->mMetadata);
mMetadata = info->mMetadata.clonePtr();
}
@@ -15833,7 +16186,7 @@ void OpenDatabaseOp::EnsureDatabaseActor() {
// XXX Maybe use LookupOrInsertWith above, to avoid a second lookup here?
info = gLiveDatabaseHashtable
->InsertOrUpdate(
- mDatabaseId,
+ mDatabaseId.ref(),
MakeUnique<DatabaseActorInfo>(
mMetadata.clonePtr(),
WrapNotNullUnchecked(mDatabase.unsafeGetRawPtr())))
@@ -16128,8 +16481,8 @@ void DeleteDatabaseOp::LoadPreviousVersion(nsIFile& aDatabaseFile) {
if (!fileManager) {
fileManager = MakeSafeRefPtr<DatabaseFileManager>(
- persistenceType, mOriginMetadata, databaseName, mDatabaseId,
- mEnforcingQuota, mInPrivateBrowsing);
+ persistenceType, mOriginMetadata, databaseName, mDatabaseId.ref(),
+ mDatabaseFilePath.ref(), mEnforcingQuota, mInPrivateBrowsing);
}
const auto maybeKey =
@@ -16237,7 +16590,7 @@ nsresult DeleteDatabaseOp::DoDatabaseWork() {
const auto& databaseFilePath,
MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsString, dbFile, GetPath));
- MOZ_ASSERT(databaseFilePath == mDatabaseFilePath);
+ MOZ_ASSERT(databaseFilePath == mDatabaseFilePath.ref());
}
#endif
@@ -16271,7 +16624,7 @@ nsresult DeleteDatabaseOp::BeginVersionChange() {
}
DatabaseActorInfo* info;
- if (gLiveDatabaseHashtable->Get(mDatabaseId, &info)) {
+ if (gLiveDatabaseHashtable->Get(mDatabaseId.ref(), &info)) {
MOZ_ASSERT(!info->mWaitingFactoryOp);
nsresult rv =
@@ -16345,9 +16698,9 @@ void DeleteDatabaseOp::SendResults() {
MOZ_ASSERT(mMaybeBlockedDatabases.IsEmpty());
DebugOnly<DatabaseActorInfo*> info = nullptr;
- MOZ_ASSERT_IF(
- gLiveDatabaseHashtable && gLiveDatabaseHashtable->Get(mDatabaseId, &info),
- !info->mWaitingFactoryOp);
+ MOZ_ASSERT_IF(mDatabaseId.isSome() && gLiveDatabaseHashtable &&
+ gLiveDatabaseHashtable->Get(mDatabaseId.ref(), &info),
+ !info->mWaitingFactoryOp);
if (!IsActorDestroyed()) {
FactoryRequestResponse response;
@@ -16428,7 +16781,7 @@ void DeleteDatabaseOp::VersionChangeOp::RunOnOwningThread() {
// Inform all the other databases that they are now invalidated. That
// should remove the previous metadata from our table.
- if (gLiveDatabaseHashtable->Get(deleteOp->mDatabaseId, &info)) {
+ if (gLiveDatabaseHashtable->Get(deleteOp->mDatabaseId.ref(), &info)) {
MOZ_ASSERT(!info->mLiveDatabases.IsEmpty());
MOZ_ASSERT(!info->mWaitingFactoryOp);
@@ -16454,7 +16807,7 @@ void DeleteDatabaseOp::VersionChangeOp::RunOnOwningThread() {
database->Invalidate();
}
- MOZ_ASSERT(!gLiveDatabaseHashtable->Get(deleteOp->mDatabaseId));
+ MOZ_ASSERT(!gLiveDatabaseHashtable->Get(deleteOp->mDatabaseId.ref()));
}
}
}
@@ -16491,21 +16844,257 @@ nsresult DeleteDatabaseOp::VersionChangeOp::Run() {
return NS_OK;
}
+nsresult GetDatabasesOp::DatabasesNotAvailable() {
+ AssertIsOnIOThread();
+ MOZ_ASSERT(mState == State::DatabaseWorkOpen);
+
+ mState = State::SendingResults;
+
+ QM_TRY(MOZ_TO_RESULT(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL)));
+
+ return NS_OK;
+}
+
+nsresult GetDatabasesOp::DoDirectoryWork() {
+ AssertIsOnIOThread();
+ MOZ_ASSERT(mState == State::DirectoryWorkOpen);
+
+ // This state (DirectoryWorkOpen) runs immediately on the I/O thread, before
+ // waiting for existing factory operations to complete (at which point
+ // DoDatabaseWork will be invoked). To match the spec, we must snapshot the
+ // current state of any databases that are being created (version = 0) or
+ // upgraded (version >= 1) now. If we only sampled these values in
+ // DoDatabaseWork, we would only see their post-creation/post-upgrade
+ // versions, which would be incorrect.
+
+ IndexedDatabaseManager* const idm = IndexedDatabaseManager::Get();
+ MOZ_ASSERT(idm);
+
+ const auto& fileManagers =
+ idm->GetFileManagers(mPersistenceType, mOriginMetadata.mOrigin);
+
+ for (const auto& fileManager : fileManagers) {
+ auto& metadata =
+ mDatabaseMetadataTable.LookupOrInsert(fileManager->DatabaseFilePath());
+ metadata.name() = fileManager->DatabaseName();
+ metadata.version() = fileManager->DatabaseVersion();
+ }
+
+ // Must set this before dispatching otherwise we will race with the IO thread.
+ mState = State::DirectoryWorkDone;
+
+ QM_TRY(MOZ_TO_RESULT(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL)));
+
+ return NS_OK;
+}
+
+nsresult GetDatabasesOp::DatabaseOpen() {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mState == State::DatabaseOpenPending);
+
+ nsresult rv = SendToIOThread();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+nsresult GetDatabasesOp::DoDatabaseWork() {
+ AssertIsOnIOThread();
+ MOZ_ASSERT(mState == State::DatabaseWorkOpen);
+
+ AUTO_PROFILER_LABEL("GetDatabasesOp::DoDatabaseWork", DOM);
+
+ if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
+ !OperationMayProceed()) {
+ IDB_REPORT_INTERNAL_ERR();
+ return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
+ }
+
+ QuotaManager* const quotaManager = QuotaManager::Get();
+ MOZ_ASSERT(quotaManager);
+
+ if (mPersistenceType != PERSISTENCE_TYPE_PERSISTENT) {
+ QM_TRY(MOZ_TO_RESULT(
+ quotaManager->EnsureTemporaryStorageIsInitializedInternal()));
+ }
+
+ {
+ QM_TRY_INSPECT(const bool& exists,
+ quotaManager->DoesOriginDirectoryExist(mOriginMetadata));
+ if (!exists) {
+ return DatabasesNotAvailable();
+ }
+ }
+
+ QM_TRY(([&quotaManager, this]()
+ -> mozilla::Result<std::pair<nsCOMPtr<nsIFile>, bool>, nsresult> {
+ if (mPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
+ QM_TRY_RETURN(
+ quotaManager->EnsurePersistentOriginIsInitialized(mOriginMetadata));
+ }
+
+ QM_TRY_RETURN(quotaManager->EnsureTemporaryOriginIsInitialized(
+ mPersistenceType, mOriginMetadata));
+ }()
+ .map([](const auto& res) { return Ok{}; })));
+
+ {
+ QM_TRY_INSPECT(const bool& exists,
+ quotaManager->DoesClientDirectoryExist(
+ ClientMetadata{mOriginMetadata, Client::IDB}));
+ if (!exists) {
+ return DatabasesNotAvailable();
+ }
+ }
+
+ QM_TRY_INSPECT(
+ const auto& clientDirectory,
+ ([&quotaManager, this]()
+ -> mozilla::Result<std::pair<nsCOMPtr<nsIFile>, bool>, nsresult> {
+ if (mPersistenceType == PERSISTENCE_TYPE_PERSISTENT) {
+ QM_TRY_RETURN(quotaManager->EnsurePersistentClientIsInitialized(
+ ClientMetadata{mOriginMetadata, Client::IDB}));
+ }
+
+ QM_TRY_RETURN(quotaManager->EnsureTemporaryClientIsInitialized(
+ ClientMetadata{mOriginMetadata, Client::IDB}));
+ }()
+ .map([](const auto& res) { return res.first; })));
+
+ QM_TRY_INSPECT(
+ (const auto& [subdirsToProcess, databaseFilenames]),
+ QuotaClient::GetDatabaseFilenames(*clientDirectory,
+ /* aCanceled */ Atomic<bool>{false}));
+
+ for (const auto& databaseFilename : databaseFilenames) {
+ QM_TRY_INSPECT(
+ const auto& databaseFile,
+ CloneFileAndAppend(*clientDirectory, databaseFilename + kSQLiteSuffix));
+
+ nsString path;
+ databaseFile->GetPath(path);
+
+ // Use the snapshotted values from DoDirectoryWork which correctly
+ // snapshotted the state of any pending creations/upgrades. This does mean
+ // that we need to skip reporting databases that had a version of 0 at that
+ // time because they were still being created. In the event that any other
+ // creation or upgrade requests are made after our operation is created,
+ // this operation will block those, so it's not possible for this set of
+ // data to get out of sync. The snapshotting (using cached database name
+ // and version in DatabaseFileManager) also guarantees that we are not
+ // touching the SQLite database here on the QuotaManager I/O thread which
+ // is already open on the connection thread.
+
+ auto metadata = mDatabaseMetadataTable.Lookup(path);
+ if (metadata) {
+ if (metadata->version() != 0) {
+ mDatabaseMetadataArray.AppendElement(DatabaseMetadata(
+ metadata->name(), metadata->version(), mPersistenceType));
+ }
+
+ continue;
+ }
+
+ // Since the database is not already open (there was no DatabaseFileManager
+ // for snapshotting in DoDirectoryWork which could provide us with the
+ // database name and version without needing to open the SQLite database),
+ // it is safe and necessary for us to open the database on this thread and
+ // retrieve its name and version. We do not need to worry about racing a
+ // database open because database opens can only be processed on this
+ // thread and we are performing the steps below synchronously.
+
+ QM_TRY_INSPECT(
+ const auto& fmDirectory,
+ CloneFileAndAppend(*clientDirectory,
+ databaseFilename + kFileManagerDirectoryNameSuffix));
+
+ QM_TRY_UNWRAP(
+ const NotNull<nsCOMPtr<mozIStorageConnection>> connection,
+ CreateStorageConnection(*databaseFile, *fmDirectory, VoidString(),
+ mOriginMetadata.mOrigin, mDirectoryLockId,
+ TelemetryIdForFile(databaseFile), Nothing{}));
+
+ {
+ // Load version information.
+ QM_TRY_INSPECT(const auto& stmt,
+ CreateAndExecuteSingleStepStatement<
+ SingleStepResult::ReturnNullIfNoResult>(
+ *connection, "SELECT name, version FROM database"_ns));
+
+ QM_TRY(OkIf(stmt), NS_ERROR_FILE_CORRUPTED);
+
+ QM_TRY_INSPECT(
+ const auto& databaseName,
+ MOZ_TO_RESULT_INVOKE_MEMBER_TYPED(nsString, stmt, GetString, 0));
+
+ QM_TRY_INSPECT(const int64_t& version,
+ MOZ_TO_RESULT_INVOKE_MEMBER(stmt, GetInt64, 1));
+
+ mDatabaseMetadataArray.AppendElement(
+ DatabaseMetadata(databaseName, version, mPersistenceType));
+ }
+ }
+
+ mState = State::SendingResults;
+
+ QM_TRY(MOZ_TO_RESULT(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL)));
+
+ return NS_OK;
+}
+
+nsresult GetDatabasesOp::BeginVersionChange() {
+ MOZ_CRASH("Not implemented because this should be unreachable.");
+}
+
+bool GetDatabasesOp::AreActorsAlive() {
+ MOZ_CRASH("Not implemented because this should be unreachable.");
+}
+
+void GetDatabasesOp::SendBlockedNotification() {
+ MOZ_CRASH("Not implemented because this should be unreachable.");
+}
+
+nsresult GetDatabasesOp::DispatchToWorkThread() {
+ MOZ_CRASH("Not implemented because this should be unreachable.");
+}
+
+void GetDatabasesOp::SendResults() {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mState == State::SendingResults);
+
+#ifdef DEBUG
+ NoteActorDestroyed();
+#endif
+
+ mResolver(mDatabaseMetadataArray);
+
+ mDirectoryLock = nullptr;
+
+ CleanupMetadata();
+
+ FinishSendResults();
+}
+
TransactionDatabaseOperationBase::TransactionDatabaseOperationBase(
- SafeRefPtr<TransactionBase> aTransaction)
+ SafeRefPtr<TransactionBase> aTransaction, const int64_t aRequestId)
: DatabaseOperationBase(aTransaction->GetLoggingInfo()->Id(),
aTransaction->GetLoggingInfo()->NextRequestSN()),
mTransaction(WrapNotNull(std::move(aTransaction))),
+ mRequestId(aRequestId),
mTransactionIsAborted((*mTransaction)->IsAborted()),
mTransactionLoggingSerialNumber((*mTransaction)->LoggingSerialNumber()) {
MOZ_ASSERT(LoggingSerialNumber());
}
TransactionDatabaseOperationBase::TransactionDatabaseOperationBase(
- SafeRefPtr<TransactionBase> aTransaction, uint64_t aLoggingSerialNumber)
+ SafeRefPtr<TransactionBase> aTransaction, const int64_t aRequestId,
+ uint64_t aLoggingSerialNumber)
: DatabaseOperationBase(aTransaction->GetLoggingInfo()->Id(),
aLoggingSerialNumber),
mTransaction(WrapNotNull(std::move(aTransaction))),
+ mRequestId(aRequestId),
mTransactionIsAborted((*mTransaction)->IsAborted()),
mTransactionLoggingSerialNumber((*mTransaction)->LoggingSerialNumber()) {}
@@ -16729,7 +17318,7 @@ void TransactionDatabaseOperationBase::SendPreprocessInfoOrResults(
mWaitingForContinue = true;
} else {
if (mLoggingSerialNumber) {
- (*mTransaction)->NoteFinishedRequest(mLoggingSerialNumber, ResultCode());
+ (*mTransaction)->NoteFinishedRequest(mRequestId, ResultCode());
}
Cleanup();
@@ -18199,8 +18788,9 @@ mozilla::ipc::IPCResult NormalTransactionOp::RecvContinue(
}
ObjectStoreAddOrPutRequestOp::ObjectStoreAddOrPutRequestOp(
- SafeRefPtr<TransactionBase> aTransaction, RequestParams&& aParams)
- : NormalTransactionOp(std::move(aTransaction)),
+ SafeRefPtr<TransactionBase> aTransaction, const int64_t aRequestId,
+ RequestParams&& aParams)
+ : NormalTransactionOp(std::move(aTransaction), aRequestId),
mParams(
std::move(aParams.type() == RequestParams::TObjectStoreAddParams
? aParams.get_ObjectStoreAddParams().commonParams()
@@ -18429,6 +19019,11 @@ nsresult ObjectStoreAddOrPutRequestOp::DoDatabaseWork(
}
QM_TRY(key.SetFromInteger(autoIncrementNum));
+
+ // Update index keys if primary key is preserved in child.
+ for (auto& updateInfo : mParams.indexUpdateInfos()) {
+ updateInfo.value().MaybeUpdateAutoIncrementKey(autoIncrementNum);
+ }
} else if (key.IsFloat()) {
double numericKey = key.ToFloat();
numericKey = std::min(numericKey, double(1LL << 53));
@@ -18715,9 +19310,9 @@ ObjectStoreAddOrPutRequestOp::SCInputStream::IsNonBlocking(bool* _retval) {
}
ObjectStoreGetRequestOp::ObjectStoreGetRequestOp(
- SafeRefPtr<TransactionBase> aTransaction, const RequestParams& aParams,
- bool aGetAll)
- : NormalTransactionOp(std::move(aTransaction)),
+ SafeRefPtr<TransactionBase> aTransaction, const int64_t aRequestId,
+ const RequestParams& aParams, bool aGetAll)
+ : NormalTransactionOp(std::move(aTransaction), aRequestId),
mObjectStoreId(aGetAll
? aParams.get_ObjectStoreGetAllParams().objectStoreId()
: aParams.get_ObjectStoreGetParams().objectStoreId()),
@@ -18887,9 +19482,9 @@ void ObjectStoreGetRequestOp::GetResponse(RequestResponse& aResponse,
}
ObjectStoreGetKeyRequestOp::ObjectStoreGetKeyRequestOp(
- SafeRefPtr<TransactionBase> aTransaction, const RequestParams& aParams,
- bool aGetAll)
- : NormalTransactionOp(std::move(aTransaction)),
+ SafeRefPtr<TransactionBase> aTransaction, const int64_t aRequestId,
+ const RequestParams& aParams, bool aGetAll)
+ : NormalTransactionOp(std::move(aTransaction), aRequestId),
mObjectStoreId(
aGetAll ? aParams.get_ObjectStoreGetAllKeysParams().objectStoreId()
: aParams.get_ObjectStoreGetKeyParams().objectStoreId()),
@@ -18975,9 +19570,9 @@ void ObjectStoreGetKeyRequestOp::GetResponse(RequestResponse& aResponse,
}
ObjectStoreDeleteRequestOp::ObjectStoreDeleteRequestOp(
- SafeRefPtr<TransactionBase> aTransaction,
+ SafeRefPtr<TransactionBase> aTransaction, const int64_t aRequestId,
const ObjectStoreDeleteParams& aParams)
- : NormalTransactionOp(std::move(aTransaction)),
+ : NormalTransactionOp(std::move(aTransaction), aRequestId),
mParams(aParams),
mObjectStoreMayHaveIndexes(false) {
AssertIsOnBackgroundThread();
@@ -19036,9 +19631,9 @@ nsresult ObjectStoreDeleteRequestOp::DoDatabaseWork(
}
ObjectStoreClearRequestOp::ObjectStoreClearRequestOp(
- SafeRefPtr<TransactionBase> aTransaction,
+ SafeRefPtr<TransactionBase> aTransaction, const int64_t aRequestId,
const ObjectStoreClearParams& aParams)
- : NormalTransactionOp(std::move(aTransaction)),
+ : NormalTransactionOp(std::move(aTransaction), aRequestId),
mParams(aParams),
mObjectStoreMayHaveIndexes(false) {
AssertIsOnBackgroundThread();
@@ -19209,8 +19804,9 @@ SafeRefPtr<FullIndexMetadata> IndexRequestOpBase::IndexMetadataForParams(
}
IndexGetRequestOp::IndexGetRequestOp(SafeRefPtr<TransactionBase> aTransaction,
+ const int64_t aRequestId,
const RequestParams& aParams, bool aGetAll)
- : IndexRequestOpBase(std::move(aTransaction), aParams),
+ : IndexRequestOpBase(std::move(aTransaction), aRequestId, aParams),
mDatabase(Transaction().GetDatabasePtr()),
mOptionalKeyRange(aGetAll
? aParams.get_IndexGetAllParams().optionalKeyRange()
@@ -19339,9 +19935,9 @@ void IndexGetRequestOp::GetResponse(RequestResponse& aResponse,
}
IndexGetKeyRequestOp::IndexGetKeyRequestOp(
- SafeRefPtr<TransactionBase> aTransaction, const RequestParams& aParams,
- bool aGetAll)
- : IndexRequestOpBase(std::move(aTransaction), aParams),
+ SafeRefPtr<TransactionBase> aTransaction, const int64_t aRequestId,
+ const RequestParams& aParams, bool aGetAll)
+ : IndexRequestOpBase(std::move(aTransaction), aRequestId, aParams),
mOptionalKeyRange(
aGetAll ? aParams.get_IndexGetAllKeysParams().optionalKeyRange()
: Some(aParams.get_IndexGetKeyParams().keyRange())),
diff --git a/dom/indexedDB/ActorsParentCommon.cpp b/dom/indexedDB/ActorsParentCommon.cpp
index 1b92e15dad..5f25bd8ad6 100644
--- a/dom/indexedDB/ActorsParentCommon.cpp
+++ b/dom/indexedDB/ActorsParentCommon.cpp
@@ -66,7 +66,7 @@ class nsIFile;
namespace mozilla::dom::indexedDB {
-static_assert(SNAPPY_VERSION == 0x010109);
+static_assert(SNAPPY_VERSION == 0x010200);
using mozilla::ipc::IsOnBackgroundThread;
diff --git a/dom/indexedDB/DatabaseFileManager.h b/dom/indexedDB/DatabaseFileManager.h
index 62d11e95a5..fdfea06ba1 100644
--- a/dom/indexedDB/DatabaseFileManager.h
+++ b/dom/indexedDB/DatabaseFileManager.h
@@ -31,12 +31,15 @@ class DatabaseFileManager final
const quota::OriginMetadata mOriginMetadata;
const nsString mDatabaseName;
const nsCString mDatabaseID;
+ const nsString mDatabaseFilePath;
RefPtr<IndexedDBCipherKeyManager> mCipherKeyManager;
LazyInitializedOnce<const nsString> mDirectoryPath;
LazyInitializedOnce<const nsString> mJournalDirectoryPath;
+ uint64_t mDatabaseVersion;
+
const bool mEnforcingQuota;
const bool mIsInPrivateBrowsingMode;
@@ -68,7 +71,8 @@ class DatabaseFileManager final
DatabaseFileManager(PersistenceType aPersistenceType,
const quota::OriginMetadata& aOriginMetadata,
const nsAString& aDatabaseName,
- const nsCString& aDatabaseID, bool aEnforcingQuota,
+ const nsCString& aDatabaseID,
+ const nsAString& aDatabaseFilePath, bool aEnforcingQuota,
bool aIsInPrivateBrowsingMode);
PersistenceType Type() const { return mPersistenceType; }
@@ -83,6 +87,14 @@ class DatabaseFileManager final
const nsCString& DatabaseID() const { return mDatabaseID; }
+ const nsAString& DatabaseFilePath() const { return mDatabaseFilePath; }
+
+ uint64_t DatabaseVersion() const { return mDatabaseVersion; }
+
+ void UpdateDatabaseVersion(uint64_t aDatabaseVersion) {
+ mDatabaseVersion = aDatabaseVersion;
+ }
+
IndexedDBCipherKeyManager& MutableCipherKeyManagerRef() const {
MOZ_ASSERT(mIsInPrivateBrowsingMode);
MOZ_ASSERT(mCipherKeyManager);
diff --git a/dom/indexedDB/IDBCursor.cpp b/dom/indexedDB/IDBCursor.cpp
index 2fb6e98715..65234bf792 100644
--- a/dom/indexedDB/IDBCursor.cpp
+++ b/dom/indexedDB/IDBCursor.cpp
@@ -417,7 +417,8 @@ void IDBTypedCursor<CursorType>::Continue(JSContext* const aCx,
IDB_LOG_STRINGIFY(key));
}
- GetTypedBackgroundActorRef().SendContinueInternal(ContinueParams(key), mData);
+ GetTypedBackgroundActorRef().SendContinueInternal(
+ mTransaction->NextRequestId(), ContinueParams(key), mData);
mContinueCalled = true;
}
@@ -523,6 +524,7 @@ void IDBTypedCursor<CursorType>::ContinuePrimaryKey(
IDB_LOG_STRINGIFY(key), IDB_LOG_STRINGIFY(primaryKey));
GetTypedBackgroundActorRef().SendContinueInternal(
+ mTransaction->NextRequestId(),
ContinuePrimaryKeyParams(key, primaryKey), mData);
mContinueCalled = true;
@@ -573,8 +575,8 @@ void IDBTypedCursor<CursorType>::Advance(const uint32_t aCount,
IDB_LOG_STRINGIFY(mSource), IDB_LOG_STRINGIFY(mDirection), aCount);
}
- GetTypedBackgroundActorRef().SendContinueInternal(AdvanceParams(aCount),
- mData);
+ GetTypedBackgroundActorRef().SendContinueInternal(
+ mTransaction->NextRequestId(), AdvanceParams(aCount), mData);
mContinueCalled = true;
}
diff --git a/dom/indexedDB/IDBDatabase.cpp b/dom/indexedDB/IDBDatabase.cpp
index 4bbc0fc68a..c1a70e9401 100644
--- a/dom/indexedDB/IDBDatabase.cpp
+++ b/dom/indexedDB/IDBDatabase.cpp
@@ -20,6 +20,7 @@
#include "MainThreadUtils.h"
#include "mozilla/ResultExtensions.h"
#include "mozilla/Services.h"
+#include "mozilla/dom/IDBTransactionBinding.h"
#include "mozilla/storage.h"
#include "mozilla/Telemetry.h"
#include "mozilla/dom/BindingDeclarations.h"
@@ -474,7 +475,8 @@ void IDBDatabase::DeleteObjectStore(const nsAString& aName, ErrorResult& aRv) {
RefPtr<IDBTransaction> IDBDatabase::Transaction(
JSContext* aCx, const StringOrStringSequence& aStoreNames,
- IDBTransactionMode aMode, ErrorResult& aRv) {
+ IDBTransactionMode aMode, const IDBTransactionOptions& aOptions,
+ ErrorResult& aRv) {
AssertIsOnOwningThread();
if ((aMode == IDBTransactionMode::Readwriteflush ||
@@ -598,8 +600,26 @@ RefPtr<IDBTransaction> IDBDatabase::Transaction(
MOZ_CRASH("Unknown mode!");
}
+ auto durability = IDBTransaction::Durability::Default;
+ if (aOptions.IsAnyMemberPresent()) {
+ switch (aOptions.mDurability) {
+ case mozilla::dom::IDBTransactionDurability::Default:
+ durability = IDBTransaction::Durability::Default;
+ break;
+ case mozilla::dom::IDBTransactionDurability::Strict:
+ durability = IDBTransaction::Durability::Strict;
+ break;
+ case mozilla::dom::IDBTransactionDurability::Relaxed:
+ durability = IDBTransaction::Durability::Relaxed;
+ break;
+
+ default:
+ MOZ_CRASH("Unknown durability hint!");
+ }
+ }
+
SafeRefPtr<IDBTransaction> transaction =
- IDBTransaction::Create(aCx, this, sortedStoreNames, mode);
+ IDBTransaction::Create(aCx, this, sortedStoreNames, mode, durability);
if (NS_WARN_IF(!transaction)) {
IDB_REPORT_INTERNAL_ERR();
MOZ_ASSERT(!NS_IsMainThread(),
@@ -617,7 +637,7 @@ RefPtr<IDBTransaction> IDBDatabase::Transaction(
IDB_LOG_STRINGIFY(*transaction));
if (!mBackgroundActor->SendPBackgroundIDBTransactionConstructor(
- actor, sortedStoreNames, mode)) {
+ actor, sortedStoreNames, mode, durability)) {
IDB_REPORT_INTERNAL_ERR();
aRv.ThrowUnknownError("Failed to create IndexedDB transaction");
return nullptr;
diff --git a/dom/indexedDB/IDBDatabase.h b/dom/indexedDB/IDBDatabase.h
index 3f7c4aa63c..6d96aaa35d 100644
--- a/dom/indexedDB/IDBDatabase.h
+++ b/dom/indexedDB/IDBDatabase.h
@@ -166,7 +166,8 @@ class IDBDatabase final : public DOMEventTargetHelper {
// This will be called from the DOM.
[[nodiscard]] RefPtr<IDBTransaction> Transaction(
JSContext* aCx, const StringOrStringSequence& aStoreNames,
- IDBTransactionMode aMode, ErrorResult& aRv);
+ IDBTransactionMode aMode, const IDBTransactionOptions& aOptions,
+ ErrorResult& aRv);
IMPL_EVENT_HANDLER(abort)
IMPL_EVENT_HANDLER(close)
diff --git a/dom/indexedDB/IDBFactory.cpp b/dom/indexedDB/IDBFactory.cpp
index 81dce07d7e..51d4c4df23 100644
--- a/dom/indexedDB/IDBFactory.cpp
+++ b/dom/indexedDB/IDBFactory.cpp
@@ -15,8 +15,9 @@
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/IDBFactoryBinding.h"
-#include "mozilla/dom/quota/PersistenceType.h"
+#include "mozilla/dom/Promise.h"
#include "mozilla/dom/quota/QuotaManager.h"
+#include "mozilla/dom/quota/ResultExtensions.h"
#include "mozilla/dom/BrowserChild.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/ipc/BackgroundChild.h"
@@ -378,6 +379,32 @@ bool IDBFactory::AllowedForPrincipal(nsIPrincipal* aPrincipal,
return !aPrincipal->GetIsNullPrincipal();
}
+// static
+PersistenceType IDBFactory::GetPersistenceType(
+ const PrincipalInfo& aPrincipalInfo) {
+ if (aPrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
+ // Chrome privilege always gets persistent storage.
+ return PERSISTENCE_TYPE_PERSISTENT;
+ }
+
+ if (aPrincipalInfo.type() == PrincipalInfo::TContentPrincipalInfo) {
+ nsCString origin =
+ aPrincipalInfo.get_ContentPrincipalInfo().originNoSuffix();
+
+ if (QuotaManager::IsOriginInternal(origin)) {
+ // Internal origins always get persistent storage.
+ return PERSISTENCE_TYPE_PERSISTENT;
+ }
+
+ if (aPrincipalInfo.get_ContentPrincipalInfo().attrs().mPrivateBrowsingId >
+ 0) {
+ return PERSISTENCE_TYPE_PRIVATE;
+ }
+ }
+
+ return PERSISTENCE_TYPE_DEFAULT;
+}
+
void IDBFactory::UpdateActiveTransactionCount(int32_t aDelta) {
AssertIsOnOwningThread();
MOZ_DIAGNOSTIC_ASSERT(aDelta > 0 || (mActiveTransactionCount + aDelta) <
@@ -443,6 +470,71 @@ RefPtr<IDBOpenDBRequest> IDBFactory::DeleteDatabase(
/* aDeleting */ true, aCallerType, aRv);
}
+already_AddRefed<Promise> IDBFactory::Databases(JSContext* const aCx) {
+ RefPtr<Promise> promise = Promise::CreateInfallible(GetOwnerGlobal());
+
+ // Nothing can be done here if we have previously failed to create a
+ // background actor.
+ if (mBackgroundActorFailed) {
+ promise->MaybeReject(NS_ERROR_FAILURE);
+ return promise.forget();
+ }
+
+ PersistenceType persistenceType = GetPersistenceType(*mPrincipalInfo);
+
+ QM_TRY(MOZ_TO_RESULT(EnsureBackgroundActor()), [&promise](const nsresult rv) {
+ promise->MaybeReject(rv);
+ return promise.forget();
+ });
+
+ mBackgroundActor->SendGetDatabases(persistenceType, *mPrincipalInfo)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [promise](const PBackgroundIDBFactoryChild::GetDatabasesPromise::
+ ResolveOrRejectValue& aValue) {
+ if (aValue.IsReject()) {
+ promise->MaybeReject(NS_ERROR_FAILURE);
+ return;
+ }
+
+ const GetDatabasesResponse& response = aValue.ResolveValue();
+
+ switch (response.type()) {
+ case GetDatabasesResponse::Tnsresult:
+ promise->MaybeReject(response.get_nsresult());
+
+ break;
+
+ case GetDatabasesResponse::TArrayOfDatabaseMetadata: {
+ const auto& array = response.get_ArrayOfDatabaseMetadata();
+
+ Sequence<IDBDatabaseInfo> databaseInfos;
+
+ for (const auto& databaseMetadata : array) {
+ IDBDatabaseInfo databaseInfo;
+
+ databaseInfo.mName.Construct(databaseMetadata.name());
+ databaseInfo.mVersion.Construct(databaseMetadata.version());
+
+ if (!databaseInfos.AppendElement(std::move(databaseInfo),
+ fallible)) {
+ promise->MaybeRejectWithTypeError("Out of memory");
+ return;
+ }
+ }
+
+ promise->MaybeResolve(databaseInfos);
+
+ break;
+ }
+ default:
+ MOZ_CRASH("Unknown response type!");
+ }
+ });
+
+ return promise.forget();
+}
+
int16_t IDBFactory::Cmp(JSContext* aCx, JS::Handle<JS::Value> aFirst,
JS::Handle<JS::Value> aSecond, ErrorResult& aRv) {
Key first, second;
@@ -512,6 +604,63 @@ RefPtr<IDBOpenDBRequest> IDBFactory::DeleteForPrincipal(
/* aDeleting */ true, aGuarantee, aRv);
}
+nsresult IDBFactory::EnsureBackgroundActor() {
+ if (mBackgroundActor) {
+ return NS_OK;
+ }
+
+ BackgroundChildImpl::ThreadLocal* threadLocal =
+ BackgroundChildImpl::GetThreadLocalForCurrentThread();
+
+ UniquePtr<ThreadLocal> newIDBThreadLocal;
+ ThreadLocal* idbThreadLocal;
+
+ if (threadLocal && threadLocal->mIndexedDBThreadLocal) {
+ idbThreadLocal = threadLocal->mIndexedDBThreadLocal.get();
+ } else {
+ nsCOMPtr<nsIUUIDGenerator> uuidGen =
+ do_GetService("@mozilla.org/uuid-generator;1");
+ MOZ_ASSERT(uuidGen);
+
+ nsID id{};
+ MOZ_ALWAYS_SUCCEEDS(uuidGen->GenerateUUIDInPlace(&id));
+
+ newIDBThreadLocal = WrapUnique(new ThreadLocal(id));
+ idbThreadLocal = newIDBThreadLocal.get();
+ }
+
+ PBackgroundChild* backgroundActor =
+ BackgroundChild::GetOrCreateForCurrentThread();
+ if (NS_WARN_IF(!backgroundActor)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ {
+ BackgroundFactoryChild* actor = new BackgroundFactoryChild(*this);
+
+ mBackgroundActor = static_cast<BackgroundFactoryChild*>(
+ backgroundActor->SendPBackgroundIDBFactoryConstructor(
+ actor, idbThreadLocal->GetLoggingInfo(),
+ IndexedDatabaseManager::GetLocale()));
+
+ if (NS_WARN_IF(!mBackgroundActor)) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ if (newIDBThreadLocal) {
+ if (!threadLocal) {
+ threadLocal = BackgroundChildImpl::GetThreadLocalForCurrentThread();
+ }
+ MOZ_ASSERT(threadLocal);
+ MOZ_ASSERT(!threadLocal->mIndexedDBThreadLocal);
+
+ threadLocal->mIndexedDBThreadLocal = std::move(newIDBThreadLocal);
+ }
+
+ return NS_OK;
+}
+
RefPtr<IDBOpenDBRequest> IDBFactory::OpenInternal(
JSContext* aCx, nsIPrincipal* aPrincipal, const nsAString& aName,
const Optional<uint64_t>& aVersion, bool aDeleting, CallerType aCallerType,
@@ -590,29 +739,7 @@ RefPtr<IDBOpenDBRequest> IDBFactory::OpenInternal(
return nullptr;
}
- PersistenceType persistenceType;
-
- bool isPersistent =
- principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo;
- if (!isPersistent &&
- principalInfo.type() == PrincipalInfo::TContentPrincipalInfo) {
- nsCString origin =
- principalInfo.get_ContentPrincipalInfo().originNoSuffix();
- isPersistent = QuotaManager::IsOriginInternal(origin);
- }
-
- const bool isPrivate =
- principalInfo.type() == PrincipalInfo::TContentPrincipalInfo &&
- principalInfo.get_ContentPrincipalInfo().attrs().mPrivateBrowsingId > 0;
-
- if (isPersistent) {
- // Chrome privilege and internal origins always get persistent storage.
- persistenceType = PERSISTENCE_TYPE_PERSISTENT;
- } else if (isPrivate) {
- persistenceType = PERSISTENCE_TYPE_PRIVATE;
- } else {
- persistenceType = PERSISTENCE_TYPE_DEFAULT;
- }
+ PersistenceType persistenceType = GetPersistenceType(principalInfo);
DatabaseMetadata& metadata = commonParams.metadata();
metadata.name() = aName;
@@ -627,60 +754,11 @@ RefPtr<IDBOpenDBRequest> IDBFactory::OpenInternal(
params = OpenDatabaseRequestParams(commonParams);
}
- if (!mBackgroundActor) {
- BackgroundChildImpl::ThreadLocal* threadLocal =
- BackgroundChildImpl::GetThreadLocalForCurrentThread();
-
- UniquePtr<ThreadLocal> newIDBThreadLocal;
- ThreadLocal* idbThreadLocal;
-
- if (threadLocal && threadLocal->mIndexedDBThreadLocal) {
- idbThreadLocal = threadLocal->mIndexedDBThreadLocal.get();
- } else {
- nsCOMPtr<nsIUUIDGenerator> uuidGen =
- do_GetService("@mozilla.org/uuid-generator;1");
- MOZ_ASSERT(uuidGen);
-
- nsID id;
- MOZ_ALWAYS_SUCCEEDS(uuidGen->GenerateUUIDInPlace(&id));
-
- newIDBThreadLocal = WrapUnique(new ThreadLocal(id));
- idbThreadLocal = newIDBThreadLocal.get();
- }
-
- PBackgroundChild* backgroundActor =
- BackgroundChild::GetOrCreateForCurrentThread();
- if (NS_WARN_IF(!backgroundActor)) {
- IDB_REPORT_INTERNAL_ERR();
- aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
- return nullptr;
- }
-
- {
- BackgroundFactoryChild* actor = new BackgroundFactoryChild(*this);
-
- mBackgroundActor = static_cast<BackgroundFactoryChild*>(
- backgroundActor->SendPBackgroundIDBFactoryConstructor(
- actor, idbThreadLocal->GetLoggingInfo(),
- IndexedDatabaseManager::GetLocale()));
-
- if (NS_WARN_IF(!mBackgroundActor)) {
- mBackgroundActorFailed = true;
- IDB_REPORT_INTERNAL_ERR();
- aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
- return nullptr;
- }
- }
-
- if (newIDBThreadLocal) {
- if (!threadLocal) {
- threadLocal = BackgroundChildImpl::GetThreadLocalForCurrentThread();
- }
- MOZ_ASSERT(threadLocal);
- MOZ_ASSERT(!threadLocal->mIndexedDBThreadLocal);
-
- threadLocal->mIndexedDBThreadLocal = std::move(newIDBThreadLocal);
- }
+ nsresult rv = EnsureBackgroundActor();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ IDB_REPORT_INTERNAL_ERR();
+ aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+ return nullptr;
}
RefPtr<IDBOpenDBRequest> request = IDBOpenDBRequest::Create(
@@ -704,7 +782,7 @@ RefPtr<IDBOpenDBRequest> IDBFactory::OpenInternal(
IDB_LOG_STRINGIFY(aVersion));
}
- nsresult rv = InitiateRequest(WrapNotNull(request), params);
+ rv = InitiateRequest(WrapNotNull(request), params);
if (NS_WARN_IF(NS_FAILED(rv))) {
IDB_REPORT_INTERNAL_ERR();
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
diff --git a/dom/indexedDB/IDBFactory.h b/dom/indexedDB/IDBFactory.h
index 488f885ae7..7139b26f9c 100644
--- a/dom/indexedDB/IDBFactory.h
+++ b/dom/indexedDB/IDBFactory.h
@@ -9,6 +9,7 @@
#include "mozilla/Attributes.h"
#include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/quota/PersistenceType.h"
#include "mozilla/GlobalTeardownObserver.h"
#include "mozilla/UniquePtr.h"
#include "nsCOMPtr.h"
@@ -59,6 +60,7 @@ class IDBFactory final : public GlobalTeardownObserver, public nsWrapperCache {
UniquePtr<PrincipalInfo> mPrincipalInfo;
+ // TODO: Unused, remove me!
nsCOMPtr<nsIGlobalObject> mGlobal;
// This will only be set if the factory belongs to a window in a child
@@ -96,6 +98,9 @@ class IDBFactory final : public GlobalTeardownObserver, public nsWrapperCache {
static bool AllowedForPrincipal(nsIPrincipal* aPrincipal,
bool* aIsSystemPrincipal = nullptr);
+ static quota::PersistenceType GetPersistenceType(
+ const PrincipalInfo& aPrincipalInfo);
+
void AssertIsOnOwningThread() const { NS_ASSERT_OWNINGTHREAD(IDBFactory); }
nsISerialEventTarget* EventTarget() const {
@@ -157,6 +162,8 @@ class IDBFactory final : public GlobalTeardownObserver, public nsWrapperCache {
JSContext* aCx, const nsAString& aName, const IDBOpenDBOptions& aOptions,
CallerType aCallerType, ErrorResult& aRv);
+ already_AddRefed<Promise> Databases(JSContext* aCx);
+
int16_t Cmp(JSContext* aCx, JS::Handle<JS::Value> aFirst,
JS::Handle<JS::Value> aSecond, ErrorResult& aRv);
@@ -194,6 +201,8 @@ class IDBFactory final : public GlobalTeardownObserver, public nsWrapperCache {
static nsresult AllowedForWindowInternal(nsPIDOMWindowInner* aWindow,
nsCOMPtr<nsIPrincipal>* aPrincipal);
+ nsresult EnsureBackgroundActor();
+
[[nodiscard]] RefPtr<IDBOpenDBRequest> OpenInternal(
JSContext* aCx, nsIPrincipal* aPrincipal, const nsAString& aName,
const Optional<uint64_t>& aVersion, bool aDeleting,
diff --git a/dom/indexedDB/IDBObjectStore.cpp b/dom/indexedDB/IDBObjectStore.cpp
index e6464d2410..728f10b105 100644
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -410,13 +410,20 @@ RefPtr<IDBObjectStore> IDBObjectStore::Create(
void IDBObjectStore::AppendIndexUpdateInfo(
const int64_t aIndexID, const KeyPath& aKeyPath, const bool aMultiEntry,
const nsCString& aLocale, JSContext* const aCx, JS::Handle<JS::Value> aVal,
- nsTArray<IndexUpdateInfo>* const aUpdateInfoArray, ErrorResult* const aRv) {
+ nsTArray<IndexUpdateInfo>* const aUpdateInfoArray,
+ const VoidOrObjectStoreKeyPathString& aAutoIncrementedObjectStoreKeyPath,
+ ErrorResult* const aRv) {
// This precondition holds when `aVal` is the result of a structured clone.
js::AutoAssertNoContentJS noContentJS(aCx);
+ static_assert(std::is_same_v<IDBObjectStore::VoidOrObjectStoreKeyPathString,
+ KeyPath::VoidOrObjectStoreKeyPathString>,
+ "Inconsistent types");
+
if (!aMultiEntry) {
Key key;
- *aRv = aKeyPath.ExtractKey(aCx, aVal, key);
+ *aRv =
+ aKeyPath.ExtractKey(aCx, aVal, key, aAutoIncrementedObjectStoreKeyPath);
// If an index's keyPath doesn't match an object, we ignore that object.
if (aRv->ErrorCodeIs(NS_ERROR_DOM_INDEXEDDB_DATA_ERR) || key.IsUnset()) {
@@ -625,6 +632,21 @@ void IDBObjectStore::GetAddInfo(JSContext* aCx, ValueWrapper& aValueWrapper,
const nsTArray<IndexMetadata>& indexes = mSpec->indexes();
const uint32_t idxCount = indexes.Length();
+ const auto& autoIncrementedObjectStoreKeyPath =
+ [this]() -> const nsAString& {
+ if (AutoIncrement() && GetKeyPath().IsValid()) {
+ // By https://w3c.github.io/IndexedDB/#database-interface ,
+ // createObjectStore algorithm, step 8, neither arrays nor empty paths
+ // are allowed for autoincremented object stores.
+ // See also KeyPath::IsAllowedForObjectStore.
+ MOZ_ASSERT(GetKeyPath().IsString());
+ MOZ_ASSERT(!GetKeyPath().IsEmpty());
+ return GetKeyPath().mStrings[0];
+ }
+
+ return VoidString();
+ }();
+
aUpdateInfoArray.SetCapacity(idxCount); // Pretty good estimate
for (uint32_t idxIndex = 0; idxIndex < idxCount; idxIndex++) {
@@ -632,7 +654,8 @@ void IDBObjectStore::GetAddInfo(JSContext* aCx, ValueWrapper& aValueWrapper,
AppendIndexUpdateInfo(metadata.id(), metadata.keyPath(),
metadata.multiEntry(), metadata.locale(), aCx,
- aValueWrapper.Value(), &aUpdateInfoArray, &aRv);
+ aValueWrapper.Value(), &aUpdateInfoArray,
+ autoIncrementedObjectStoreKeyPath, &aRv);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
diff --git a/dom/indexedDB/IDBObjectStore.h b/dom/indexedDB/IDBObjectStore.h
index dc79fa3616..99bec23327 100644
--- a/dom/indexedDB/IDBObjectStore.h
+++ b/dom/indexedDB/IDBObjectStore.h
@@ -48,6 +48,7 @@ class IDBObjectStore final : public nsISupports, public nsWrapperCache {
using KeyPath = indexedDB::KeyPath;
using ObjectStoreSpec = indexedDB::ObjectStoreSpec;
using StructuredCloneReadInfoChild = indexedDB::StructuredCloneReadInfoChild;
+ using VoidOrObjectStoreKeyPathString = nsAString;
// For AddOrPut() and DeleteInternal().
// TODO Consider removing this, and making the functions public?
@@ -98,11 +99,12 @@ class IDBObjectStore final : public nsISupports, public nsWrapperCache {
[[nodiscard]] static RefPtr<IDBObjectStore> Create(
SafeRefPtr<IDBTransaction> aTransaction, ObjectStoreSpec& aSpec);
- static void AppendIndexUpdateInfo(int64_t aIndexID, const KeyPath& aKeyPath,
- bool aMultiEntry, const nsCString& aLocale,
- JSContext* aCx, JS::Handle<JS::Value> aVal,
- nsTArray<IndexUpdateInfo>* aUpdateInfoArray,
- ErrorResult* aRv);
+ static void AppendIndexUpdateInfo(
+ int64_t aIndexID, const KeyPath& aKeyPath, bool aMultiEntry,
+ const nsCString& aLocale, JSContext* aCx, JS::Handle<JS::Value> aVal,
+ nsTArray<IndexUpdateInfo>* aUpdateInfoArray,
+ const VoidOrObjectStoreKeyPathString& aAutoIncrementedObjectStoreKeyPath,
+ ErrorResult* aRv);
static void ClearCloneReadInfo(
indexedDB::StructuredCloneReadInfoChild& aReadInfo);
diff --git a/dom/indexedDB/IDBTransaction.cpp b/dom/indexedDB/IDBTransaction.cpp
index d984bcacdf..b35bea6eff 100644
--- a/dom/indexedDB/IDBTransaction.cpp
+++ b/dom/indexedDB/IDBTransaction.cpp
@@ -89,8 +89,9 @@ auto IDBTransaction::DoWithTransactionChild(const Func& aFunc) const {
IDBTransaction::IDBTransaction(IDBDatabase* const aDatabase,
const nsTArray<nsString>& aObjectStoreNames,
- const Mode aMode, nsString aFilename,
- const uint32_t aLineNo, const uint32_t aColumn,
+ const Mode aMode, const Durability aDurability,
+ nsString aFilename, const uint32_t aLineNo,
+ const uint32_t aColumn,
CreatedFromFactoryFunction /*aDummy*/)
: DOMEventTargetHelper(aDatabase),
mDatabase(aDatabase),
@@ -98,12 +99,14 @@ IDBTransaction::IDBTransaction(IDBDatabase* const aDatabase,
mLoggingSerialNumber(GetIndexedDBThreadLocal()->NextTransactionSN(aMode)),
mNextObjectStoreId(0),
mNextIndexId(0),
+ mNextRequestId(0),
mAbortCode(NS_OK),
mPendingRequestCount(0),
mFilename(std::move(aFilename)),
mLineNo(aLineNo),
mColumn(aColumn),
mMode(aMode),
+ mDurability(aDurability),
mRegistered(false),
mNotedActiveTransaction(false) {
MOZ_ASSERT(aDatabase);
@@ -177,9 +180,11 @@ SafeRefPtr<IDBTransaction> IDBTransaction::CreateVersionChange(
nsString filename;
uint32_t lineNo, column;
aOpenRequest->GetCallerLocation(filename, &lineNo, &column);
+ // XXX: What should we have as durability hint here?
auto transaction = MakeSafeRefPtr<IDBTransaction>(
aDatabase, emptyObjectStoreNames, Mode::VersionChange,
- std::move(filename), lineNo, column, CreatedFromFactoryFunction{});
+ Durability::Default, std::move(filename), lineNo, column,
+ CreatedFromFactoryFunction{});
transaction->NoteActiveTransaction();
@@ -196,7 +201,8 @@ SafeRefPtr<IDBTransaction> IDBTransaction::CreateVersionChange(
// static
SafeRefPtr<IDBTransaction> IDBTransaction::Create(
JSContext* const aCx, IDBDatabase* const aDatabase,
- const nsTArray<nsString>& aObjectStoreNames, const Mode aMode) {
+ const nsTArray<nsString>& aObjectStoreNames, const Mode aMode,
+ const Durability aDurability) {
MOZ_ASSERT(aDatabase);
aDatabase->AssertIsOnOwningThread();
MOZ_ASSERT(!aObjectStoreNames.IsEmpty());
@@ -207,8 +213,8 @@ SafeRefPtr<IDBTransaction> IDBTransaction::Create(
uint32_t lineNo, column;
IDBRequest::CaptureCaller(aCx, filename, &lineNo, &column);
auto transaction = MakeSafeRefPtr<IDBTransaction>(
- aDatabase, aObjectStoreNames, aMode, std::move(filename), lineNo, column,
- CreatedFromFactoryFunction{});
+ aDatabase, aObjectStoreNames, aMode, aDurability, std::move(filename),
+ lineNo, column, CreatedFromFactoryFunction{});
if (!NS_IsMainThread()) {
WorkerPrivate* const workerPrivate = GetCurrentThreadWorkerPrivate();
@@ -287,8 +293,9 @@ BackgroundRequestChild* IDBTransaction::StartRequest(
BackgroundRequestChild* const actor =
new BackgroundRequestChild(std::move(aRequest));
- DoWithTransactionChild([actor, &aParams](auto& transactionChild) {
- transactionChild.SendPBackgroundIDBRequestConstructor(actor, aParams);
+ DoWithTransactionChild([this, actor, &aParams](auto& transactionChild) {
+ transactionChild.SendPBackgroundIDBRequestConstructor(
+ actor, NextRequestId(), aParams);
});
// Balanced in BackgroundRequestChild::Recv__delete__().
@@ -302,8 +309,9 @@ void IDBTransaction::OpenCursor(PBackgroundIDBCursorChild& aBackgroundActor,
AssertIsOnOwningThread();
MOZ_ASSERT(aParams.type() != OpenCursorParams::T__None);
- DoWithTransactionChild([&aBackgroundActor, &aParams](auto& actor) {
- actor.SendPBackgroundIDBCursorConstructor(&aBackgroundActor, aParams);
+ DoWithTransactionChild([this, &aBackgroundActor, &aParams](auto& actor) {
+ actor.SendPBackgroundIDBCursorConstructor(&aBackgroundActor,
+ NextRequestId(), aParams);
});
// Balanced in BackgroundCursorChild::RecvResponse().
@@ -383,15 +391,16 @@ void IDBTransaction::SendCommit(const bool aAutoCommit) {
LoggingSerialNumber(), requestSerialNumber,
aAutoCommit ? "automatically" : "explicitly");
- const auto lastRequestSerialNumber =
- [this, aAutoCommit,
- requestSerialNumber]() -> Maybe<decltype(requestSerialNumber)> {
+ const int64_t requestId = NextRequestId();
+
+ const auto lastRequestId = [this, aAutoCommit,
+ requestId]() -> Maybe<decltype(requestId)> {
if (aAutoCommit) {
return Nothing();
}
- // In case of an explicit commit, we need to note the serial number of the
- // last request to check if a request submitted before the commit request
+ // In case of an explicit commit, we need to note the id of the last
+ // request to check if a request submitted before the commit request
// failed. If we are currently in an event handler for a request on this
// transaction, ignore this request. This is used to synchronize the
// transaction's committing state with the parent side, to abort the
@@ -405,15 +414,13 @@ void IDBTransaction::SendCommit(const bool aAutoCommit) {
const bool dispatchingEventForThisTransaction =
maybeCurrentTransaction && &maybeCurrentTransaction.ref() == this;
- return Some(requestSerialNumber
- ? (requestSerialNumber -
- (dispatchingEventForThisTransaction ? 0 : 1))
+ return Some(requestId
+ ? (requestId - (dispatchingEventForThisTransaction ? 0 : 1))
: 0);
}();
- DoWithTransactionChild([lastRequestSerialNumber](auto& actor) {
- actor.SendCommit(lastRequestSerialNumber);
- });
+ DoWithTransactionChild(
+ [lastRequestId](auto& actor) { actor.SendCommit(lastRequestId); });
mSentCommitOrAbort.Flip();
}
@@ -817,6 +824,12 @@ int64_t IDBTransaction::NextIndexId() {
return mNextIndexId++;
}
+int64_t IDBTransaction::NextRequestId() {
+ AssertIsOnOwningThread();
+
+ return mNextRequestId++;
+}
+
void IDBTransaction::InvalidateCursorCaches() {
AssertIsOnOwningThread();
@@ -869,6 +882,24 @@ IDBTransactionMode IDBTransaction::GetMode(ErrorResult& aRv) const {
}
}
+IDBTransactionDurability IDBTransaction::GetDurability(ErrorResult& aRv) const {
+ AssertIsOnOwningThread();
+
+ switch (mDurability) {
+ case Durability::Default:
+ return IDBTransactionDurability::Default;
+
+ case Durability::Strict:
+ return IDBTransactionDurability::Strict;
+
+ case Durability::Relaxed:
+ return IDBTransactionDurability::Relaxed;
+
+ default:
+ MOZ_CRASH("Bad mode!");
+ }
+}
+
DOMException* IDBTransaction::GetError() const {
AssertIsOnOwningThread();
diff --git a/dom/indexedDB/IDBTransaction.h b/dom/indexedDB/IDBTransaction.h
index 65fefddfe5..9feafb3c68 100644
--- a/dom/indexedDB/IDBTransaction.h
+++ b/dom/indexedDB/IDBTransaction.h
@@ -63,6 +63,15 @@ class IDBTransaction final
Invalid
};
+ enum struct Durability {
+ Default = 0,
+ Strict,
+ Relaxed,
+
+ // Only needed for IPC serialization helper, should never be used in code.
+ Invalid
+ };
+
enum struct ReadyState { Active, Inactive, Committing, Finished };
private:
@@ -90,6 +99,11 @@ class IDBTransaction final
int64_t mNextObjectStoreId;
int64_t mNextIndexId;
+ // Request ids are issued starting from 0 and incremented by one as we send
+ // actor creation messages to the parent process. Used to support the
+ // explicit commit() request.
+ int64_t mNextRequestId;
+
nsresult mAbortCode; ///< The result that caused the transaction to be
///< aborted, or NS_OK if not aborted.
///< NS_ERROR_DOM_INDEXEDDB_ABORT_ERR indicates that the
@@ -108,6 +122,7 @@ class IDBTransaction final
ReadyState mReadyState = ReadyState::Active;
FlippedOnce<false> mStarted;
const Mode mMode;
+ const Durability mDurability;
bool mRegistered; ///< Whether mDatabase->RegisterTransaction() has been
///< called (which may not be the case if construction was
@@ -130,7 +145,8 @@ class IDBTransaction final
[[nodiscard]] static SafeRefPtr<IDBTransaction> Create(
JSContext* aCx, IDBDatabase* aDatabase,
- const nsTArray<nsString>& aObjectStoreNames, Mode aMode);
+ const nsTArray<nsString>& aObjectStoreNames, Mode aMode,
+ Durability aDurability);
static Maybe<IDBTransaction&> MaybeCurrent();
@@ -232,6 +248,11 @@ class IDBTransaction final
return mMode;
}
+ Durability GetDurability() const {
+ AssertIsOnOwningThread();
+ return mDurability;
+ }
+
uint32_t GetPendingRequestCount() const { return mPendingRequestCount; }
IDBDatabase* Database() const {
@@ -280,6 +301,9 @@ class IDBTransaction final
// Only for Mode::VersionChange transactions.
int64_t NextIndexId();
+ // See the comment for mNextRequestId.
+ int64_t NextRequestId();
+
void InvalidateCursorCaches();
void RegisterCursor(IDBCursor& aCursor);
void UnregisterCursor(IDBCursor& aCursor);
@@ -299,6 +323,8 @@ class IDBTransaction final
IDBTransactionMode GetMode(ErrorResult& aRv) const;
+ IDBTransactionDurability GetDurability(ErrorResult& aRv) const;
+
DOMException* GetError() const;
[[nodiscard]] RefPtr<IDBObjectStore> ObjectStore(const nsAString& aName,
@@ -323,8 +349,8 @@ class IDBTransaction final
public:
IDBTransaction(IDBDatabase* aDatabase,
const nsTArray<nsString>& aObjectStoreNames, Mode aMode,
- nsString aFilename, uint32_t aLineNo, uint32_t aColumn,
- CreatedFromFactoryFunction aDummy);
+ Durability aDurability, nsString aFilename, uint32_t aLineNo,
+ uint32_t aColumn, CreatedFromFactoryFunction aDummy);
private:
~IDBTransaction();
diff --git a/dom/indexedDB/IndexedDatabase.cpp b/dom/indexedDB/IndexedDatabase.cpp
index a73d9fbf5a..538aea5280 100644
--- a/dom/indexedDB/IndexedDatabase.cpp
+++ b/dom/indexedDB/IndexedDatabase.cpp
@@ -391,7 +391,8 @@ JSObject* CommonStructuredCloneReadCallback(
!StructuredCloneHolder::ReadString(aReader, value)) {
return nullptr;
}
- params->Append(key, value);
+ params->Append(NS_ConvertUTF16toUTF8(key),
+ NS_ConvertUTF16toUTF8(value));
}
if (!WrapAsJSObject(aCx, params, &result)) {
diff --git a/dom/indexedDB/IndexedDatabaseManager.cpp b/dom/indexedDB/IndexedDatabaseManager.cpp
index 0558c38826..e63dd793cf 100644
--- a/dom/indexedDB/IndexedDatabaseManager.cpp
+++ b/dom/indexedDB/IndexedDatabaseManager.cpp
@@ -67,6 +67,13 @@ class FileManagerInfo {
[[nodiscard]] SafeRefPtr<DatabaseFileManager> GetFileManager(
PersistenceType aPersistenceType, const nsAString& aName) const;
+ [[nodiscard]] SafeRefPtr<DatabaseFileManager>
+ GetFileManagerByDatabaseFilePath(PersistenceType aPersistenceType,
+ const nsAString& aDatabaseFilePath) const;
+
+ const nsTArray<SafeRefPtr<DatabaseFileManager>>& GetFileManagers(
+ PersistenceType aPersistenceType) const;
+
void AddFileManager(SafeRefPtr<DatabaseFileManager> aFileManager);
bool HasFileManagers() const {
@@ -86,18 +93,18 @@ class FileManagerInfo {
const nsAString& aName);
private:
- nsTArray<SafeRefPtr<DatabaseFileManager> >& GetArray(
+ nsTArray<SafeRefPtr<DatabaseFileManager>>& GetArray(
PersistenceType aPersistenceType);
- const nsTArray<SafeRefPtr<DatabaseFileManager> >& GetImmutableArray(
+ const nsTArray<SafeRefPtr<DatabaseFileManager>>& GetImmutableArray(
PersistenceType aPersistenceType) const {
return const_cast<FileManagerInfo*>(this)->GetArray(aPersistenceType);
}
- nsTArray<SafeRefPtr<DatabaseFileManager> > mPersistentStorageFileManagers;
- nsTArray<SafeRefPtr<DatabaseFileManager> > mTemporaryStorageFileManagers;
- nsTArray<SafeRefPtr<DatabaseFileManager> > mDefaultStorageFileManagers;
- nsTArray<SafeRefPtr<DatabaseFileManager> > mPrivateStorageFileManagers;
+ nsTArray<SafeRefPtr<DatabaseFileManager>> mPersistentStorageFileManagers;
+ nsTArray<SafeRefPtr<DatabaseFileManager>> mTemporaryStorageFileManagers;
+ nsTArray<SafeRefPtr<DatabaseFileManager>> mDefaultStorageFileManagers;
+ nsTArray<SafeRefPtr<DatabaseFileManager>> mPrivateStorageFileManagers;
};
} // namespace indexedDB
@@ -199,6 +206,13 @@ auto DatabaseNameMatchPredicate(const nsAString* const aName) {
};
}
+auto DatabaseFilePathMatchPredicate(const nsAString* const aDatabaseFilePath) {
+ MOZ_ASSERT(aDatabaseFilePath);
+ return [aDatabaseFilePath](const auto& fileManager) {
+ return fileManager->DatabaseFilePath() == *aDatabaseFilePath;
+ };
+}
+
} // namespace
IndexedDatabaseManager::IndexedDatabaseManager() : mBackgroundActor(nullptr) {
@@ -476,6 +490,35 @@ SafeRefPtr<DatabaseFileManager> IndexedDatabaseManager::GetFileManager(
return info->GetFileManager(aPersistenceType, aDatabaseName);
}
+SafeRefPtr<DatabaseFileManager>
+IndexedDatabaseManager::GetFileManagerByDatabaseFilePath(
+ PersistenceType aPersistenceType, const nsACString& aOrigin,
+ const nsAString& aDatabaseFilePath) {
+ AssertIsOnIOThread();
+
+ FileManagerInfo* info;
+ if (!mFileManagerInfos.Get(aOrigin, &info)) {
+ return nullptr;
+ }
+
+ return info->GetFileManagerByDatabaseFilePath(aPersistenceType,
+ aDatabaseFilePath);
+}
+
+const nsTArray<SafeRefPtr<DatabaseFileManager>>&
+IndexedDatabaseManager::GetFileManagers(PersistenceType aPersistenceType,
+ const nsACString& aOrigin) {
+ AssertIsOnIOThread();
+
+ FileManagerInfo* info;
+ if (!mFileManagerInfos.Get(aOrigin, &info)) {
+ static nsTArray<SafeRefPtr<DatabaseFileManager>> emptyArray;
+ return emptyArray;
+ }
+
+ return info->GetFileManagers(aPersistenceType);
+}
+
void IndexedDatabaseManager::AddFileManager(
SafeRefPtr<DatabaseFileManager> aFileManager) {
AssertIsOnIOThread();
@@ -680,11 +723,34 @@ SafeRefPtr<DatabaseFileManager> FileManagerInfo::GetFileManager(
return foundIt != end ? foundIt->clonePtr() : nullptr;
}
+SafeRefPtr<DatabaseFileManager>
+FileManagerInfo::GetFileManagerByDatabaseFilePath(
+ PersistenceType aPersistenceType,
+ const nsAString& aDatabaseFilePath) const {
+ AssertIsOnIOThread();
+
+ const auto& managers = GetImmutableArray(aPersistenceType);
+
+ const auto end = managers.cend();
+ const auto foundIt =
+ std::find_if(managers.cbegin(), end,
+ DatabaseFilePathMatchPredicate(&aDatabaseFilePath));
+
+ return foundIt != end ? foundIt->clonePtr() : nullptr;
+}
+
+const nsTArray<SafeRefPtr<DatabaseFileManager>>&
+FileManagerInfo::GetFileManagers(PersistenceType aPersistenceType) const {
+ AssertIsOnIOThread();
+
+ return GetImmutableArray(aPersistenceType);
+}
+
void FileManagerInfo::AddFileManager(
SafeRefPtr<DatabaseFileManager> aFileManager) {
AssertIsOnIOThread();
- nsTArray<SafeRefPtr<DatabaseFileManager> >& managers =
+ nsTArray<SafeRefPtr<DatabaseFileManager>>& managers =
GetArray(aFileManager->Type());
NS_ASSERTION(!managers.Contains(aFileManager), "Adding more than once?!");
@@ -718,7 +784,7 @@ void FileManagerInfo::InvalidateAndRemoveFileManagers(
PersistenceType aPersistenceType) {
AssertIsOnIOThread();
- nsTArray<SafeRefPtr<DatabaseFileManager> >& managers =
+ nsTArray<SafeRefPtr<DatabaseFileManager>>& managers =
GetArray(aPersistenceType);
for (uint32_t i = 0; i < managers.Length(); i++) {
@@ -743,7 +809,7 @@ void FileManagerInfo::InvalidateAndRemoveFileManager(
}
}
-nsTArray<SafeRefPtr<DatabaseFileManager> >& FileManagerInfo::GetArray(
+nsTArray<SafeRefPtr<DatabaseFileManager>>& FileManagerInfo::GetArray(
PersistenceType aPersistenceType) {
switch (aPersistenceType) {
case PERSISTENCE_TYPE_PERSISTENT:
diff --git a/dom/indexedDB/IndexedDatabaseManager.h b/dom/indexedDB/IndexedDatabaseManager.h
index 0766143dd6..3d0f467368 100644
--- a/dom/indexedDB/IndexedDatabaseManager.h
+++ b/dom/indexedDB/IndexedDatabaseManager.h
@@ -99,6 +99,14 @@ class IndexedDatabaseManager final {
PersistenceType aPersistenceType, const nsACString& aOrigin,
const nsAString& aDatabaseName);
+ [[nodiscard]] SafeRefPtr<DatabaseFileManager>
+ GetFileManagerByDatabaseFilePath(PersistenceType aPersistenceType,
+ const nsACString& aOrigin,
+ const nsAString& aDatabaseFilePath);
+
+ const nsTArray<SafeRefPtr<DatabaseFileManager>>& GetFileManagers(
+ PersistenceType aPersistenceType, const nsACString& aOrigin);
+
void AddFileManager(SafeRefPtr<DatabaseFileManager> aFileManager);
void InvalidateAllFileManagers();
diff --git a/dom/indexedDB/Key.cpp b/dom/indexedDB/Key.cpp
index 6f96023a54..7e0c607617 100644
--- a/dom/indexedDB/Key.cpp
+++ b/dom/indexedDB/Key.cpp
@@ -560,6 +560,51 @@ Result<Ok, nsresult> Key::EncodeString(const Span<const T> aInput,
#define KEY_MAXIMUM_BUFFER_LENGTH \
::mozilla::detail::nsTStringLengthStorage<char>::kMax
+void Key::ReserveAutoIncrementKey(bool aFirstOfArray) {
+ // Allocate memory for the new size
+ uint32_t oldLen = mBuffer.Length();
+ char* buffer;
+ if (!mBuffer.GetMutableData(&buffer, oldLen + 1 + sizeof(double))) {
+ return;
+ }
+
+ // Remember the offset of the buffer to be updated later.
+ mAutoIncrementKeyOffsets.AppendElement(oldLen + 1);
+
+ // Fill the type.
+ buffer += oldLen;
+ *(buffer++) = aFirstOfArray ? (eMaxType + eFloat) : eFloat;
+
+ // Fill up with 0xFF to reserve the buffer in fixed size because the encoded
+ // string could be trimmed if ended with padding zeros.
+ mozilla::BigEndian::writeUint64(buffer, UINT64_MAX);
+}
+
+void Key::MaybeUpdateAutoIncrementKey(int64_t aKey) {
+ if (mAutoIncrementKeyOffsets.IsEmpty()) {
+ return;
+ }
+
+ for (uint32_t offset : mAutoIncrementKeyOffsets) {
+ char* buffer;
+ MOZ_ALWAYS_TRUE(mBuffer.GetMutableData(&buffer));
+ buffer += offset;
+ WriteDoubleToUint64(buffer, double(aKey));
+ }
+
+ TrimBuffer();
+}
+
+void Key::WriteDoubleToUint64(char* aBuffer, double aValue) {
+ MOZ_ASSERT(aBuffer);
+
+ uint64_t bits = BitwiseCast<uint64_t>(aValue);
+ const uint64_t signbit = FloatingPoint<double>::kSignBit;
+ uint64_t number = bits & signbit ? (-bits) : (bits | signbit);
+
+ mozilla::BigEndian::writeUint64(aBuffer, number);
+}
+
template <typename T>
Result<Ok, nsresult> Key::EncodeAsString(const Span<const T> aInput,
uint8_t aType) {
@@ -797,13 +842,8 @@ Result<Ok, nsresult> Key::EncodeNumber(double aFloat, uint8_t aType) {
*(buffer++) = aType;
- uint64_t bits = BitwiseCast<uint64_t>(aFloat);
- // Note: The subtraction from 0 below is necessary to fix
- // MSVC build warning C4146 (negating an unsigned value).
- const uint64_t signbit = FloatingPoint<double>::kSignBit;
- uint64_t number = bits & signbit ? (0 - bits) : (bits | signbit);
+ WriteDoubleToUint64(buffer, aFloat);
- mozilla::BigEndian::writeUint64(buffer, number);
return Ok();
}
diff --git a/dom/indexedDB/Key.h b/dom/indexedDB/Key.h
index d19bc94e53..25ddd3e0b1 100644
--- a/dom/indexedDB/Key.h
+++ b/dom/indexedDB/Key.h
@@ -25,6 +25,7 @@ class Key {
friend struct IPC::ParamTraits<Key>;
nsCString mBuffer;
+ CopyableTArray<uint32_t> mAutoIncrementKeyOffsets;
public:
enum {
@@ -86,7 +87,10 @@ class Key {
return Compare(mBuffer, aOther.mBuffer) >= 0;
}
- void Unset() { mBuffer.SetIsVoid(true); }
+ void Unset() {
+ mBuffer.SetIsVoid(true);
+ mAutoIncrementKeyOffsets.Clear();
+ }
bool IsUnset() const { return mBuffer.IsVoid(); }
@@ -174,6 +178,10 @@ class Key {
return 0;
}
+ void ReserveAutoIncrementKey(bool aFirstOfArray);
+
+ void MaybeUpdateAutoIncrementKey(int64_t aKey);
+
private:
class MOZ_STACK_CLASS ArrayValueEncoder;
@@ -273,6 +281,8 @@ class Key {
template <typename T>
nsresult SetFromSource(T* aSource, uint32_t aIndex);
+
+ void WriteDoubleToUint64(char* aBuffer, double aValue);
};
} // namespace mozilla::dom::indexedDB
diff --git a/dom/indexedDB/KeyPath.cpp b/dom/indexedDB/KeyPath.cpp
index 5cad164296..f27ddd007a 100644
--- a/dom/indexedDB/KeyPath.cpp
+++ b/dom/indexedDB/KeyPath.cpp
@@ -329,8 +329,9 @@ bool KeyPath::AppendStringWithValidation(const nsAString& aString) {
return false;
}
-nsresult KeyPath::ExtractKey(JSContext* aCx, const JS::Value& aValue,
- Key& aKey) const {
+nsresult KeyPath::ExtractKey(JSContext* aCx, const JS::Value& aValue, Key& aKey,
+ const VoidOrObjectStoreKeyPathString&
+ aAutoIncrementedObjectStoreKeyPath) const {
uint32_t len = mStrings.Length();
JS::Rooted<JS::Value> value(aCx);
@@ -341,6 +342,17 @@ nsresult KeyPath::ExtractKey(JSContext* aCx, const JS::Value& aValue,
GetJSValFromKeyPathString(aCx, aValue, mStrings[i], value.address(),
DoNotCreateProperties, nullptr, nullptr);
if (NS_FAILED(rv)) {
+ if (!aAutoIncrementedObjectStoreKeyPath.IsVoid() &&
+ mStrings[i].Equals(aAutoIncrementedObjectStoreKeyPath)) {
+ // We are extracting index keys of an object to be added if
+ // object store key path for a string key is provided.
+ // Because the autoIncrement primary key is part of
+ // this index key but is not defined in |aValue|, so we reserve
+ // the space here to update the key later in parent.
+ aKey.ReserveAutoIncrementKey(IsArray() && i == 0);
+ continue;
+ }
+
return rv;
}
diff --git a/dom/indexedDB/KeyPath.h b/dom/indexedDB/KeyPath.h
index 4e1042650f..5fac30ecf8 100644
--- a/dom/indexedDB/KeyPath.h
+++ b/dom/indexedDB/KeyPath.h
@@ -45,6 +45,8 @@ class KeyPath {
KeyPath() : mType(KeyPathType::NonExistent) { MOZ_COUNT_CTOR(KeyPath); }
public:
+ using VoidOrObjectStoreKeyPathString = nsAString;
+
enum class KeyPathType { NonExistent, String, Array, EndGuard };
void SetType(KeyPathType aType);
@@ -76,7 +78,10 @@ class KeyPath {
static Result<KeyPath, nsresult> Parse(
const Nullable<OwningStringOrStringSequence>& aValue);
- nsresult ExtractKey(JSContext* aCx, const JS::Value& aValue, Key& aKey) const;
+ nsresult ExtractKey(
+ JSContext* aCx, const JS::Value& aValue, Key& aKey,
+ const VoidOrObjectStoreKeyPathString& aAutoIncrementedObjectStoreKeyPath =
+ VoidString()) const;
nsresult ExtractKeyAsJSVal(JSContext* aCx, const JS::Value& aValue,
JS::Value* aOutVal) const;
diff --git a/dom/indexedDB/PBackgroundIDBCursor.ipdl b/dom/indexedDB/PBackgroundIDBCursor.ipdl
index 60c8d2ca7b..e7669f2554 100644
--- a/dom/indexedDB/PBackgroundIDBCursor.ipdl
+++ b/dom/indexedDB/PBackgroundIDBCursor.ipdl
@@ -87,8 +87,8 @@ protocol PBackgroundIDBCursor
parent:
async DeleteMe();
- async Continue(CursorRequestParams params, Key currentKey,
- Key currentObjectStoreKey);
+ async Continue(int64_t requestId, CursorRequestParams params,
+ Key currentKey, Key currentObjectStoreKey);
child:
async __delete__();
diff --git a/dom/indexedDB/PBackgroundIDBDatabase.ipdl b/dom/indexedDB/PBackgroundIDBDatabase.ipdl
index f9262ebadf..88d5e79392 100644
--- a/dom/indexedDB/PBackgroundIDBDatabase.ipdl
+++ b/dom/indexedDB/PBackgroundIDBDatabase.ipdl
@@ -19,6 +19,9 @@ using struct mozilla::null_t from "mozilla/ipc/IPCCore.h";
using mozilla::dom::IDBTransaction::Mode
from "mozilla/dom/IDBTransaction.h";
+using mozilla::dom::IDBTransaction::Durability
+ from "mozilla/dom/IDBTransaction.h";
+
namespace mozilla {
namespace dom {
namespace indexedDB {
@@ -41,7 +44,7 @@ parent:
async PBackgroundIDBDatabaseFile(IPCBlob blob);
- async PBackgroundIDBTransaction(nsString[] objectStoreNames, Mode mode);
+ async PBackgroundIDBTransaction(nsString[] objectStoreNames, Mode mode, Durability durability);
child:
async __delete__();
diff --git a/dom/indexedDB/PBackgroundIDBFactory.ipdl b/dom/indexedDB/PBackgroundIDBFactory.ipdl
index ef2a88e55b..c838189b2b 100644
--- a/dom/indexedDB/PBackgroundIDBFactory.ipdl
+++ b/dom/indexedDB/PBackgroundIDBFactory.ipdl
@@ -40,6 +40,12 @@ union FactoryRequestParams
DeleteDatabaseRequestParams;
};
+union GetDatabasesResponse
+{
+ nsresult;
+ DatabaseMetadata[];
+};
+
[ChildImpl="indexedDB::BackgroundFactoryChild", ParentImpl=virtual]
sync protocol PBackgroundIDBFactory
{
@@ -53,6 +59,10 @@ parent:
async PBackgroundIDBFactoryRequest(FactoryRequestParams params);
+ async GetDatabases(PersistenceType persistenceType,
+ PrincipalInfo principalInfo)
+ returns(GetDatabasesResponse response);
+
child:
async __delete__();
diff --git a/dom/indexedDB/PBackgroundIDBTransaction.ipdl b/dom/indexedDB/PBackgroundIDBTransaction.ipdl
index 85a9f76265..071e031cb2 100644
--- a/dom/indexedDB/PBackgroundIDBTransaction.ipdl
+++ b/dom/indexedDB/PBackgroundIDBTransaction.ipdl
@@ -34,9 +34,9 @@ parent:
async Commit(int64_t? lastRequest);
async Abort(nsresult resultCode);
- async PBackgroundIDBCursor(OpenCursorParams params);
+ async PBackgroundIDBCursor(int64_t requestId, OpenCursorParams params);
- async PBackgroundIDBRequest(RequestParams params);
+ async PBackgroundIDBRequest(int64_t requestId, RequestParams params);
child:
async __delete__();
diff --git a/dom/indexedDB/PBackgroundIDBVersionChangeTransaction.ipdl b/dom/indexedDB/PBackgroundIDBVersionChangeTransaction.ipdl
index e139f0f1c3..c096c28996 100644
--- a/dom/indexedDB/PBackgroundIDBVersionChangeTransaction.ipdl
+++ b/dom/indexedDB/PBackgroundIDBVersionChangeTransaction.ipdl
@@ -36,9 +36,9 @@ parent:
async DeleteIndex(int64_t objectStoreId, int64_t indexId);
async RenameIndex(int64_t objectStoreId, int64_t indexId, nsString name);
- async PBackgroundIDBCursor(OpenCursorParams params);
+ async PBackgroundIDBCursor(int64_t requestId, OpenCursorParams params);
- async PBackgroundIDBRequest(RequestParams params);
+ async PBackgroundIDBRequest(int64_t requestId, RequestParams params);
child:
async __delete__();
diff --git a/dom/indexedDB/SchemaUpgrades.cpp b/dom/indexedDB/SchemaUpgrades.cpp
index 234c4c9f04..17230e247f 100644
--- a/dom/indexedDB/SchemaUpgrades.cpp
+++ b/dom/indexedDB/SchemaUpgrades.cpp
@@ -2863,7 +2863,7 @@ nsresult UpgradeFileIdsFunction::Init(nsIFile* aFMDirectory,
auto fileManager = MakeSafeRefPtr<DatabaseFileManager>(
PERSISTENCE_TYPE_INVALID, quota::OriginMetadata{},
/* aDatabaseName */ u""_ns, /* aDatabaseID */ ""_ns,
- /* aEnforcingQuota */ false,
+ /* aDatabaseFilePath */ u""_ns, /* aEnforcingQuota */ false,
/* aIsInPrivateBrowsingMode */ false);
nsresult rv = fileManager->Init(aFMDirectory, aConnection);
diff --git a/dom/indexedDB/SerializationHelpers.h b/dom/indexedDB/SerializationHelpers.h
index 488c1eb044..a79c6cefb4 100644
--- a/dom/indexedDB/SerializationHelpers.h
+++ b/dom/indexedDB/SerializationHelpers.h
@@ -31,10 +31,12 @@ struct ParamTraits<mozilla::dom::indexedDB::Key> {
static void Write(MessageWriter* aWriter, const paramType& aParam) {
WriteParam(aWriter, aParam.mBuffer);
+ WriteParam(aWriter, aParam.mAutoIncrementKeyOffsets);
}
static bool Read(MessageReader* aReader, paramType* aResult) {
- return ReadParam(aReader, &aResult->mBuffer);
+ return ReadParam(aReader, &aResult->mBuffer) &&
+ ReadParam(aReader, &aResult->mAutoIncrementKeyOffsets);
}
};
@@ -72,6 +74,13 @@ struct ParamTraits<mozilla::dom::IDBTransaction::Mode>
mozilla::dom::IDBTransaction::Mode::ReadOnly,
mozilla::dom::IDBTransaction::Mode::Invalid> {};
+template <>
+struct ParamTraits<mozilla::dom::IDBTransaction::Durability>
+ : public ContiguousEnumSerializer<
+ mozilla::dom::IDBTransaction::Durability,
+ mozilla::dom::IDBTransaction::Durability::Default,
+ mozilla::dom::IDBTransaction::Durability::Invalid> {};
+
} // namespace IPC
#endif // mozilla_dom_indexeddb_serializationhelpers_h__
diff --git a/dom/indexedDB/test/mochitest-common.toml b/dom/indexedDB/test/mochitest-common.toml
index d42228da45..0959f36e13 100644
--- a/dom/indexedDB/test/mochitest-common.toml
+++ b/dom/indexedDB/test/mochitest-common.toml
@@ -73,6 +73,7 @@ support-files = [
"unit/test_objectStore_remove_values.js",
"unit/test_object_identity.js",
"unit/test_odd_result_order.js",
+ "unit/test_open_and_databases.js",
"unit/test_open_empty_db.js",
"unit/test_open_for_principal.js",
"unit/test_open_objectStore.js",
@@ -299,6 +300,8 @@ skip-if = [
["test_odd_result_order.html"]
+["test_open_and_databases.html"]
+
["test_open_empty_db.html"]
["test_open_for_principal.html"]
diff --git a/dom/indexedDB/test/test_open_and_databases.html b/dom/indexedDB/test/test_open_and_databases.html
new file mode 100644
index 0000000000..9d0113136e
--- /dev/null
+++ b/dom/indexedDB/test/test_open_and_databases.html
@@ -0,0 +1,19 @@
+<!--
+ Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html>
+<head>
+ <title>Indexed Database Property Test</title>
+
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+
+ <script type="text/javascript" src="unit/test_open_and_databases.js"></script>
+ <script type="text/javascript" src="helpers.js"></script>
+
+</head>
+
+<body onload="runTest();"></body>
+
+</html>
diff --git a/dom/indexedDB/test/test_third_party.html b/dom/indexedDB/test/test_third_party.html
index ee90de8dac..439f50d754 100644
--- a/dom/indexedDB/test/test_third_party.html
+++ b/dom/indexedDB/test/test_third_party.html
@@ -25,12 +25,12 @@
{ host: "http://sub1.test2.example.org:8000", cookieBehavior: BEHAVIOR_REJECT, expectedResultFrame1: false, expectedResultFrame2: false },
{ host: "http://" + window.location.host, cookieBehavior: BEHAVIOR_REJECTFOREIGN, expectedResultFrame1: true, expectedResultFrame2: true },
- { host: "http://example.com", cookieBehavior: BEHAVIOR_REJECTFOREIGN, expectedResultFrame1: false, expectedResultFrame2: true },
- { host: "http://sub1.test2.example.org:8000", cookieBehavior: BEHAVIOR_REJECTFOREIGN, expectedResultFrame1: false, expectedResultFrame2: true },
+ { host: "http://example.com", cookieBehavior: BEHAVIOR_REJECTFOREIGN, expectedResultFrame1: false, expectedResultFrame2: false },
+ { host: "http://sub1.test2.example.org:8000", cookieBehavior: BEHAVIOR_REJECTFOREIGN, expectedResultFrame1: false, expectedResultFrame2: false },
{ host: "http://" + window.location.host, cookieBehavior: BEHAVIOR_LIMITFOREIGN, expectedResultFrame1: true, expectedResultFrame2: true },
- { host: "http://example.com", cookieBehavior: BEHAVIOR_LIMITFOREIGN, expectedResultFrame1: false, expectedResultFrame2: true },
- { host: "http://sub1.test2.example.org:8000", cookieBehavior: BEHAVIOR_LIMITFOREIGN, expectedResultFrame1: false, expectedResultFrame2: true },
+ { host: "http://example.com", cookieBehavior: BEHAVIOR_LIMITFOREIGN, expectedResultFrame1: false, expectedResultFrame2: false },
+ { host: "http://sub1.test2.example.org:8000", cookieBehavior: BEHAVIOR_LIMITFOREIGN, expectedResultFrame1: false, expectedResultFrame2: false },
];
const iframe1Path =
diff --git a/dom/indexedDB/test/unit/test_connection_idle_maintenance.js b/dom/indexedDB/test/unit/test_connection_idle_maintenance.js
index 288f656c65..819b0b61bb 100644
--- a/dom/indexedDB/test/unit/test_connection_idle_maintenance.js
+++ b/dom/indexedDB/test/unit/test_connection_idle_maintenance.js
@@ -8,7 +8,7 @@ async function testSteps() {
// A constant used to deal with small decrease in usage when transactions are
// transferred from the WAL file back into the original database, also called
// as checkpointing.
- const cosmologicalConstant = 31768;
+ const cosmologicalConstant = 20000;
// The length of time that database connections will be held open after all
// transactions have completed before doing idle maintenance.
diff --git a/dom/indexedDB/test/unit/test_connection_idle_maintenance_stop.js b/dom/indexedDB/test/unit/test_connection_idle_maintenance_stop.js
index 33dc69b210..5453e4dc4e 100644
--- a/dom/indexedDB/test/unit/test_connection_idle_maintenance_stop.js
+++ b/dom/indexedDB/test/unit/test_connection_idle_maintenance_stop.js
@@ -8,7 +8,7 @@ async function testSteps() {
// A constant used to deal with small decrease in usage when transactions are
// transferred from the WAL file back into the original database, also called
// as checkpointing.
- const cosmologicalConstant = 32768;
+ const cosmologicalConstant = 35000;
// The maximum number of threads that can be used for database activity at a
// single time.
diff --git a/dom/indexedDB/test/unit/test_open_and_databases.js b/dom/indexedDB/test/unit/test_open_and_databases.js
new file mode 100644
index 0000000000..b13cba067a
--- /dev/null
+++ b/dom/indexedDB/test/unit/test_open_and_databases.js
@@ -0,0 +1,76 @@
+/**
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+/* exported testSteps */
+async function testSteps() {
+ const openInfos = [
+ { name: "foo-a", version: 1 },
+ { name: "foo-b", version: 1 },
+ ];
+
+ info("Creating databases");
+
+ for (let index = 0; index < openInfos.length; index++) {
+ const openInfo = openInfos[index];
+
+ const request = indexedDB.open(openInfo.name, openInfo.version);
+
+ await expectingUpgrade(request);
+
+ const event = await expectingSuccess(request);
+
+ const database = event.target.result;
+
+ database.close();
+ }
+
+ info("Getting databases");
+
+ const databasesPromise = indexedDB.databases();
+
+ info("Opening databases");
+
+ const openPromises = [];
+
+ for (let index = 0; index < openInfos.length; index++) {
+ const openInfo = openInfos[index];
+
+ const request = indexedDB.open(openInfo.name, openInfo.version);
+
+ const promise = expectingSuccess(request);
+
+ openPromises.push(promise);
+ }
+
+ info("Waiting for databases operation to complete");
+
+ const databaseInfos = await databasesPromise;
+
+ info("Verifying databases");
+
+ is(
+ databaseInfos.length,
+ openInfos.length,
+ "The result of databases() should contain one result per database"
+ );
+
+ for (let index = 0; index < openInfos.length; index++) {
+ const openInfo = openInfos[index];
+
+ ok(
+ databaseInfos.some(function (element) {
+ return (
+ element.name === openInfo.name && element.version === openInfo.version
+ );
+ }),
+ "The result of databases() should be a sequence of the correct names " +
+ "and versions of all databases for the origin"
+ );
+ }
+
+ info("Waiting for open operations to complete");
+
+ await Promise.all(openPromises);
+}
diff --git a/dom/indexedDB/test/unit/xpcshell-head-parent-process.js b/dom/indexedDB/test/unit/xpcshell-head-parent-process.js
index f6bd2608ef..840eef7908 100644
--- a/dom/indexedDB/test/unit/xpcshell-head-parent-process.js
+++ b/dom/indexedDB/test/unit/xpcshell-head-parent-process.js
@@ -56,7 +56,11 @@ if (!this.runTest) {
if (testSteps.constructor.name === "AsyncFunction") {
// Do run our existing cleanup function that would normally be called by
// the generator's call to finishTest().
- registerCleanupFunction(resetTesting);
+ registerCleanupFunction(function () {
+ if (SpecialPowers.isMainProcess()) {
+ resetTesting();
+ }
+ });
add_task(testSteps);
@@ -644,7 +648,7 @@ var SpecialPowers = {
clearUserPref(prefName) {
Services.prefs.clearUserPref(prefName);
},
- // Copied (and slightly adjusted) from testing/specialpowers/content/SpecialPowersAPI.jsm
+ // Copied (and slightly adjusted) from testing/specialpowers/api.js
exactGC(callback) {
let count = 0;
diff --git a/dom/indexedDB/test/unit/xpcshell-shared.toml b/dom/indexedDB/test/unit/xpcshell-shared.toml
index ac1183ab2d..f967236a9e 100644
--- a/dom/indexedDB/test/unit/xpcshell-shared.toml
+++ b/dom/indexedDB/test/unit/xpcshell-shared.toml
@@ -111,6 +111,8 @@ skip-if = ["os == 'android'"] # bug 864843
["test_odd_result_order.js"]
+["test_open_and_databases.js"]
+
["test_open_empty_db.js"]
["test_open_for_principal.js"]