From d8bbc7858622b6d9c278469aab701ca0b609cddf Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 15 May 2024 05:35:49 +0200 Subject: Merging upstream version 126.0. Signed-off-by: Daniel Baumann --- dom/indexedDB/ActorsParent.cpp | 1128 ++++++++++++++++++++++++++++++---------- 1 file changed, 862 insertions(+), 266 deletions(-) (limited to 'dom/indexedDB/ActorsParent.cpp') 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>> 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 aTransaction); + TransactionDatabaseOperationBase(SafeRefPtr aTransaction, + int64_t aRequestId); TransactionDatabaseOperationBase(SafeRefPtr 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 GetContentParentId() const; }; class WaitForTransactionsHelper final : public Runnable { @@ -2395,11 +2410,13 @@ class Database final already_AddRefed AllocPBackgroundIDBTransactionParent( - const nsTArray& aObjectStoreNames, const Mode& aMode) override; + const nsTArray& aObjectStoreNames, const Mode& aMode, + const Durability& aDurability) override; mozilla::ipc::IPCResult RecvPBackgroundIDBTransactionConstructor( PBackgroundIDBTransactionParent* aActor, - nsTArray&& aObjectStoreNames, const Mode& aMode) override; + nsTArray&& 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 aTransaction) : TransactionDatabaseOperationBase(std::move(aTransaction), + /* aRequestId */ 0, /* aLoggingSerialNumber */ 0) {} ~StartTransactionOp() override = default; @@ -2591,6 +2609,7 @@ class TransactionBase : public AtomicSafeRefCounted { protected: using Mode = IDBTransaction::Mode; + using Durability = IDBTransaction::Durability; private: const SafeRefPtr mDatabase; @@ -2602,6 +2621,7 @@ class TransactionBase : public AtomicSafeRefCounted { uint64_t mActiveRequestCount; Atomic mInvalidatedOnAnyThread; const Mode mMode; + const Durability mDurability; // TODO: See bug 1883045 FlippedOnce mInitialized; FlippedOnce mHasBeenActiveOnConnectionThread; FlippedOnce mActorDestroyed; @@ -2661,6 +2681,8 @@ class TransactionBase : public AtomicSafeRefCounted { 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 { virtual ~TransactionBase(); protected: - TransactionBase(SafeRefPtr aDatabase, Mode aMode); + TransactionBase(SafeRefPtr aDatabase, Mode aMode, + Durability aDurability); void NoteActorDestroyed() { AssertIsOnBackgroundThread(); @@ -2765,7 +2788,8 @@ class TransactionBase : public AtomicSafeRefCounted { 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 { already_AddRefed 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 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 aDatabase, TransactionBase::Mode aMode, + TransactionBase::Durability aDurability, nsTArray>&& 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 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> { 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 mDirectoryLock; - RefPtr mDelayedOp; + nsTArray>> mBlocking; + nsTArray>> mBlockedOn; + nsTArray mMaybeBlockedDatabases; - const CommonFactoryRequestParams mCommonParams; + const PrincipalInfo mPrincipalInfo; OriginMetadata mOriginMetadata; - nsCString mDatabaseId; - nsString mDatabaseFilePath; + Maybe mDatabaseName; + Maybe mDatabaseId; + Maybe mDatabaseFilePath; int64_t mDirectoryLockId; + const PersistenceType mPersistenceType; State mState; bool mWaitingForPermissionRetry; bool mEnforcingQuota; @@ -3081,17 +3120,23 @@ class FactoryOp return mOriginMetadata.mOrigin; } + const Maybe& 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 aFactory, const Maybe& aContentParentId, - const CommonFactoryRequestParams& aCommonParams, bool aDeleting); + const PersistenceType aPersistenceType, + const PrincipalInfo& aPrincipalInfo, + const Maybe& 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& 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 aFactory, + const Maybe& 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 aFactory, const Maybe& 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 mDatabaseMetadataTable; + nsTArray mDatabaseMetadataArray; + Factory::GetDatabasesResolver mResolver; + + public: + GetDatabasesOp(SafeRefPtr aFactory, + const Maybe& 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 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 aTransaction) - : TransactionDatabaseOperationBase(std::move(aTransaction)) + NormalTransactionOp(SafeRefPtr 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 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 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 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 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 aTransaction, + const int64_t aRequestId, const ObjectStoreDeleteParams& aParams); ~ObjectStoreDeleteRequestOp() override = default; @@ -3951,6 +4084,7 @@ class ObjectStoreClearRequestOp final : public NormalTransactionOp { private: ObjectStoreClearRequestOp(SafeRefPtr aTransaction, + const int64_t aRequestId, const ObjectStoreClearParams& aParams); ~ObjectStoreClearRequestOp() override = default; @@ -3971,8 +4105,10 @@ class ObjectStoreCountRequestOp final : public NormalTransactionOp { private: ObjectStoreCountRequestOp(SafeRefPtr 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 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 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 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 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 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& 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 aTransaction, @@ -4373,8 +4512,9 @@ class Cursor::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::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& aOptionalKeyRange) - : CursorOpBase(aCursor), mOptionalKeyRange(aOptionalKeyRange) {} + : CursorOpBase(aCursor, aRequestId), + mOptionalKeyRange(aOptionalKeyRange) {} // Reference counted. ~OpenOp() override = default; @@ -4572,9 +4713,9 @@ class Cursor::ContinueOp final const CursorRequestParams mParams; // Only created by Cursor. - ContinueOp(Cursor* const aCursor, CursorRequestParams aParams, - CursorPosition aPosition) - : CursorOpBase(aCursor), + ContinueOp(Cursor* const aCursor, int64_t aRequestId, + CursorRequestParams aParams, CursorPosition 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 mBackgroundThread; @@ -4824,8 +4967,9 @@ class QuotaClient final : public mozilla::dom::quota::Client { // checks those unfinished deletion and clean them up after that. template - Result, nsresult> - GetDatabaseFilenames(nsIFile& aDirectory, const AtomicBool& aCanceled); + Result, + 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 mDirectoryLock; nsTArray mFileIds; State mState; + DEBUGONLY(bool mDEBUGCountsAsPending = false); + + static uint64_t sPendingRunnables; public: DeleteFilesRunnable(SafeRefPtr 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 mPendingDirectoryLock; RefPtr mDirectoryLock; + nsTArray> mCompleteCallbacks; nsTArray mDirectoryInfos; nsTHashMap mDatabaseMaintenances; nsresult mResultCode; @@ -4994,6 +5148,8 @@ class Maintenance final : public Runnable { void UnregisterDatabaseMaintenance(DatabaseMaintenance* aDatabaseMaintenance); + bool HasDatabaseMaintenances() const { return mDatabaseMaintenances.Count(); } + RefPtr 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; - - 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 = GetContentParentId(); - auto actor = [&]() -> RefPtr { + auto actor = [&]() -> RefPtr { if (aParams.type() == FactoryRequestParams::TOpenDatabaseRequestParams) { return MakeRefPtr(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(aActor); + auto* op = static_cast(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 op = dont_AddRef(static_cast(aActor)); + RefPtr op = + dont_AddRef(static_cast(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 = GetContentParentId(); + + auto op = MakeRefPtr(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 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 Database::AllocPBackgroundIDBTransactionParent( - const nsTArray& aObjectStoreNames, const Mode& aMode) { + const nsTArray& 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(SafeRefPtrFromThis(), aMode, + aDurability, std::move(objectStoreMetadatas)) .forget(); } mozilla::ipc::IPCResult Database::RecvPBackgroundIDBTransactionConstructor( PBackgroundIDBTransactionParent* aActor, - nsTArray&& aObjectStoreNames, const Mode& aMode) { + nsTArray&& 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 aDatabase, Mode aMode) +TransactionBase::TransactionBase(SafeRefPtr aDatabase, Mode aMode, + Durability aDurability) : mDatabase(std::move(aDatabase)), mDatabaseId(mDatabase->Id()), mLoggingSerialNumber( @@ -9823,6 +10047,7 @@ TransactionBase::TransactionBase(SafeRefPtr 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 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(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 aDatabase, TransactionBase::Mode aMode, + TransactionBase::Durability aDurability, nsTArray>&& 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(aParams)), + return AllocRequest(aRequestId, + std::move(const_cast(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 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(aParams)), + return AllocRequest(aRequestId, + std::move(const_cast(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 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::VerifyRequestParams( } template -bool Cursor::Start(const OpenCursorParams& aParams) { +bool Cursor::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::Start(const OpenCursorParams& aParams) { const Maybe& optionalKeyRange = GetCommonOpenCursorParams(aParams).optionalKeyRange(); - const RefPtr openOp = new OpenOp(this, optionalKeyRange); + const RefPtr openOp = new OpenOp(this, aRequestId, optionalKeyRange); if (NS_WARN_IF(!openOp->Init(*mTransaction))) { openOp->Cleanup(); @@ -11553,8 +11798,8 @@ mozilla::ipc::IPCResult Cursor::RecvDeleteMe() { template mozilla::ipc::IPCResult Cursor::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::RecvContinue( } const RefPtr 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(aClosure); MOZ_ASSERT(self); MOZ_ASSERT(self->mDeleteTimer); @@ -12728,6 +12990,8 @@ void QuotaClient::ProcessMaintenanceQueue() { * DeleteFilesRunnable ******************************************************************************/ +uint64_t DeleteFilesRunnable::sPendingRunnables = 0; + DeleteFilesRunnable::DeleteFilesRunnable( SafeRefPtr aFileManager, nsTArray&& 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& 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 aFactory, const Maybe& aContentParentId, - const CommonFactoryRequestParams& aCommonParams, - bool aDeleting) + const PersistenceType aPersistenceType, + const PrincipalInfo& aPrincipalInfo, + const Maybe& 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 { - 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 { + 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 currentMaintenance = - quotaClient->GetCurrentMaintenance()) { - if (RefPtr 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 currentMaintenance = + quotaClient->GetCurrentMaintenance()) { + if (self.mDatabaseName.isSome()) { + if (RefPtr 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 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>& 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 aFactory, const Maybe& aContentParentId, const CommonFactoryRequestParams& aParams) - : FactoryOp(std::move(aFactory), aContentParentId, aParams, - /* aDeleting */ false), + : FactoryRequestOp(std::move(aFactory), aContentParentId, aParams, + /* aDeleting */ false), mMetadata(MakeSafeRefPtr(aParams.metadata())), mRequestedVersion(aParams.metadata().version()), mVersionChangeOp(nullptr), @@ -14990,7 +15341,7 @@ OpenDatabaseOp::OpenDatabaseOp(SafeRefPtr 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( - persistenceType, mOriginMetadata, databaseName, mDatabaseId, - mEnforcingQuota, mInPrivateBrowsing); + persistenceType, mOriginMetadata, databaseName, mDatabaseId.ref(), + mDatabaseFilePath.ref(), mEnforcingQuota, mInPrivateBrowsing); } Maybe 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 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 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( mMetadata.clonePtr(), WrapNotNullUnchecked(mDatabase.unsafeGetRawPtr()))) @@ -16128,8 +16481,8 @@ void DeleteDatabaseOp::LoadPreviousVersion(nsIFile& aDatabaseFile) { if (!fileManager) { fileManager = MakeSafeRefPtr( - 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 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((["aManager, this]() + -> mozilla::Result, 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, + (["aManager, this]() + -> mozilla::Result, 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{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> 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 aTransaction) + SafeRefPtr 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 aTransaction, uint64_t aLoggingSerialNumber) + SafeRefPtr 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 aTransaction, RequestParams&& aParams) - : NormalTransactionOp(std::move(aTransaction)), + SafeRefPtr 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 aTransaction, const RequestParams& aParams, - bool aGetAll) - : NormalTransactionOp(std::move(aTransaction)), + SafeRefPtr 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 aTransaction, const RequestParams& aParams, - bool aGetAll) - : NormalTransactionOp(std::move(aTransaction)), + SafeRefPtr 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 aTransaction, + SafeRefPtr 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 aTransaction, + SafeRefPtr 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 IndexRequestOpBase::IndexMetadataForParams( } IndexGetRequestOp::IndexGetRequestOp(SafeRefPtr 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 aTransaction, const RequestParams& aParams, - bool aGetAll) - : IndexRequestOpBase(std::move(aTransaction), aParams), + SafeRefPtr 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())), -- cgit v1.2.3