/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_dom_indexeddb_actorschild_h__ #define mozilla_dom_indexeddb_actorschild_h__ #include "js/RootingAPI.h" #include "mozilla/Attributes.h" #include "mozilla/dom/IDBCursorType.h" #include "mozilla/dom/IDBTransaction.h" #include "mozilla/dom/indexedDB/PBackgroundIDBCursorChild.h" #include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseChild.h" #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryChild.h" #include "mozilla/dom/indexedDB/PBackgroundIDBFactoryRequestChild.h" #include "mozilla/dom/indexedDB/PBackgroundIDBRequestChild.h" #include "mozilla/dom/indexedDB/PBackgroundIDBSharedTypes.h" #include "mozilla/dom/indexedDB/PBackgroundIDBTransactionChild.h" #include "mozilla/dom/indexedDB/PBackgroundIDBVersionChangeTransactionChild.h" #include "mozilla/dom/indexedDB/PBackgroundIndexedDBUtilsChild.h" #include "mozilla/InitializedOnce.h" #include "mozilla/UniquePtr.h" #include "nsCOMPtr.h" #include "nsTArray.h" class nsIEventTarget; struct nsID; namespace mozilla { namespace ipc { class BackgroundChildImpl; } // namespace ipc namespace dom { class IDBCursor; class IDBDatabase; class IDBFactory; class IDBOpenDBRequest; class IDBRequest; class IndexedDatabaseManager; namespace indexedDB { class Key; class PermissionRequestChild; class PermissionRequestParent; class SerializedStructuredCloneReadInfo; struct CloneInfo; } // namespace indexedDB } // namespace dom } // namespace mozilla MOZ_DECLARE_RELOCATE_USING_MOVE_CONSTRUCTOR(mozilla::dom::indexedDB::CloneInfo) namespace mozilla::dom::indexedDB { class BackgroundFactoryChild final : public PBackgroundIDBFactoryChild { friend class mozilla::ipc::BackgroundChildImpl; friend IDBFactory; // TODO: This long-lived raw pointer is very suspicious, in particular as it // is used in BackgroundDatabaseChild::EnsureDOMObject to reacquire a strong // reference. What ensures it is kept alive, and why can't we store a strong // reference here? IDBFactory* mFactory; public: NS_INLINE_DECL_REFCOUNTING(BackgroundFactoryChild, override) void AssertIsOnOwningThread() const { NS_ASSERT_OWNINGTHREAD(BackgroundFactoryChild); } IDBFactory& GetDOMObject() const { AssertIsOnOwningThread(); MOZ_ASSERT(mFactory); return *mFactory; } bool SendDeleteMe() = delete; private: // Only created by IDBFactory. explicit BackgroundFactoryChild(IDBFactory& aFactory); // Only destroyed by mozilla::ipc::BackgroundChildImpl. ~BackgroundFactoryChild(); void SendDeleteMeInternal(); public: // IPDL methods are only called by IPDL. void ActorDestroy(ActorDestroyReason aWhy) override; PBackgroundIDBFactoryRequestChild* AllocPBackgroundIDBFactoryRequestChild( const FactoryRequestParams& aParams); bool DeallocPBackgroundIDBFactoryRequestChild( PBackgroundIDBFactoryRequestChild* aActor); PBackgroundIDBDatabaseChild* AllocPBackgroundIDBDatabaseChild( const DatabaseSpec& aSpec, PBackgroundIDBFactoryRequestChild* aRequest) const; bool DeallocPBackgroundIDBDatabaseChild(PBackgroundIDBDatabaseChild* aActor); mozilla::ipc::IPCResult RecvPBackgroundIDBDatabaseConstructor( PBackgroundIDBDatabaseChild* aActor, const DatabaseSpec& aSpec, NotNull aRequest) override; }; class BackgroundDatabaseChild; class BackgroundRequestChildBase { protected: const NotNull> mRequest; public: void AssertIsOnOwningThread() const #ifdef DEBUG ; #else { } #endif protected: explicit BackgroundRequestChildBase( MovingNotNull> aRequest); virtual ~BackgroundRequestChildBase(); }; class BackgroundFactoryRequestChild final : public BackgroundRequestChildBase, public PBackgroundIDBFactoryRequestChild { using PersistenceType = mozilla::dom::quota::PersistenceType; friend IDBFactory; friend class BackgroundFactoryChild; friend class BackgroundDatabaseChild; friend class PermissionRequestChild; friend class PermissionRequestParent; const SafeRefPtr mFactory; // Normally when opening of a database is successful, we receive a database // actor in request response, so we can use it to call ReleaseDOMObject() // which clears temporary strong reference to IDBDatabase. // However, when there's an error, we don't receive a database actor and // IDBRequest::mTransaction is already cleared (must be). So the only way how // to call ReleaseDOMObject() is to have a back-reference to database actor. // This creates a weak ref cycle between // BackgroundFactoryRequestChild (using mDatabaseActor member) and // BackgroundDatabaseChild actor (using mOpenRequestActor member). // mDatabaseActor is set in EnsureDOMObject() and cleared in // ReleaseDOMObject(). BackgroundDatabaseChild* mDatabaseActor; const uint64_t mRequestedVersion; const bool mIsDeleteOp; public: NotNull GetOpenDBRequest() const; private: // Only created by IDBFactory. BackgroundFactoryRequestChild( SafeRefPtr aFactory, MovingNotNull> aOpenRequest, bool aIsDeleteOp, uint64_t aRequestedVersion); // Only destroyed by BackgroundFactoryChild. ~BackgroundFactoryRequestChild(); void SetDatabaseActor(BackgroundDatabaseChild* aActor); void HandleResponse(nsresult aResponse); void HandleResponse(const OpenDatabaseRequestResponse& aResponse); void HandleResponse(const DeleteDatabaseRequestResponse& aResponse); public: // IPDL methods are only called by IPDL. void ActorDestroy(ActorDestroyReason aWhy) override; mozilla::ipc::IPCResult Recv__delete__( const FactoryRequestResponse& aResponse); mozilla::ipc::IPCResult RecvPermissionChallenge( PrincipalInfo&& aPrincipalInfo); mozilla::ipc::IPCResult RecvBlocked(uint64_t aCurrentVersion); }; class BackgroundDatabaseChild final : public PBackgroundIDBDatabaseChild { friend class BackgroundFactoryChild; friend class BackgroundFactoryRequestChild; friend IDBDatabase; UniquePtr mSpec; RefPtr mTemporaryStrongDatabase; BackgroundFactoryRequestChild* mOpenRequestActor; IDBDatabase* mDatabase; public: void AssertIsOnOwningThread() const #ifdef DEBUG ; #else { } #endif const DatabaseSpec* Spec() const { AssertIsOnOwningThread(); return mSpec.get(); } IDBDatabase* GetDOMObject() const { AssertIsOnOwningThread(); return mDatabase; } bool SendDeleteMe() = delete; private: // Only constructed by BackgroundFactoryChild. BackgroundDatabaseChild(const DatabaseSpec& aSpec, BackgroundFactoryRequestChild* aOpenRequest); // Only destroyed by BackgroundFactoryChild. ~BackgroundDatabaseChild(); void SendDeleteMeInternal(); [[nodiscard]] bool EnsureDOMObject(); void ReleaseDOMObject(); public: // IPDL methods are only called by IPDL. void ActorDestroy(ActorDestroyReason aWhy) override; PBackgroundIDBDatabaseFileChild* AllocPBackgroundIDBDatabaseFileChild( const IPCBlob& aIPCBlob); bool DeallocPBackgroundIDBDatabaseFileChild( PBackgroundIDBDatabaseFileChild* aActor) const; already_AddRefed AllocPBackgroundIDBVersionChangeTransactionChild(uint64_t aCurrentVersion, uint64_t aRequestedVersion, int64_t aNextObjectStoreId, int64_t aNextIndexId); mozilla::ipc::IPCResult RecvPBackgroundIDBVersionChangeTransactionConstructor( PBackgroundIDBVersionChangeTransactionChild* aActor, const uint64_t& aCurrentVersion, const uint64_t& aRequestedVersion, const int64_t& aNextObjectStoreId, const int64_t& aNextIndexId) override; mozilla::ipc::IPCResult RecvVersionChange(uint64_t aOldVersion, Maybe aNewVersion); mozilla::ipc::IPCResult RecvInvalidate(); mozilla::ipc::IPCResult RecvCloseAfterInvalidationComplete(); }; class BackgroundVersionChangeTransactionChild; class BackgroundTransactionBase { friend class BackgroundVersionChangeTransactionChild; // mTemporaryStrongTransaction is strong and is only valid until the end of // NoteComplete() member function or until the NoteActorDestroyed() member // function is called. SafeRefPtr mTemporaryStrongTransaction; protected: // mTransaction is weak and is valid until the NoteActorDestroyed() member // function is called. IDBTransaction* mTransaction = nullptr; public: #ifdef DEBUG virtual void AssertIsOnOwningThread() const = 0; #else void AssertIsOnOwningThread() const {} #endif IDBTransaction* GetDOMObject() const { AssertIsOnOwningThread(); return mTransaction; } protected: MOZ_COUNTED_DEFAULT_CTOR(BackgroundTransactionBase); explicit BackgroundTransactionBase(SafeRefPtr aTransaction); MOZ_COUNTED_DTOR_VIRTUAL(BackgroundTransactionBase); void NoteActorDestroyed(); void NoteComplete(); private: // Only called by BackgroundVersionChangeTransactionChild. void SetDOMTransaction(SafeRefPtr aTransaction); }; class BackgroundTransactionChild final : public BackgroundTransactionBase, public PBackgroundIDBTransactionChild { friend class BackgroundDatabaseChild; friend IDBDatabase; public: NS_INLINE_DECL_REFCOUNTING(BackgroundTransactionChild, override) #ifdef DEBUG void AssertIsOnOwningThread() const override; #endif void SendDeleteMeInternal(); bool SendDeleteMe() = delete; private: // Only created by IDBDatabase. explicit BackgroundTransactionChild(SafeRefPtr aTransaction); // Only destroyed by BackgroundDatabaseChild. ~BackgroundTransactionChild(); public: // IPDL methods are only called by IPDL. void ActorDestroy(ActorDestroyReason aWhy) override; mozilla::ipc::IPCResult RecvComplete(nsresult aResult); PBackgroundIDBRequestChild* AllocPBackgroundIDBRequestChild( const RequestParams& aParams); bool DeallocPBackgroundIDBRequestChild(PBackgroundIDBRequestChild* aActor); PBackgroundIDBCursorChild* AllocPBackgroundIDBCursorChild( const OpenCursorParams& aParams); bool DeallocPBackgroundIDBCursorChild(PBackgroundIDBCursorChild* aActor); }; class BackgroundVersionChangeTransactionChild final : public BackgroundTransactionBase, public PBackgroundIDBVersionChangeTransactionChild { friend class BackgroundDatabaseChild; IDBOpenDBRequest* mOpenDBRequest; public: NS_INLINE_DECL_REFCOUNTING(BackgroundVersionChangeTransactionChild, override) #ifdef DEBUG void AssertIsOnOwningThread() const override; #endif void SendDeleteMeInternal(bool aFailedConstructor); bool SendDeleteMe() = delete; private: // Only created by BackgroundDatabaseChild. explicit BackgroundVersionChangeTransactionChild( IDBOpenDBRequest* aOpenDBRequest); // Only destroyed by BackgroundDatabaseChild. ~BackgroundVersionChangeTransactionChild(); // Only called by BackgroundDatabaseChild. using BackgroundTransactionBase::SetDOMTransaction; public: // IPDL methods are only called by IPDL. void ActorDestroy(ActorDestroyReason aWhy) override; mozilla::ipc::IPCResult RecvComplete(nsresult aResult); PBackgroundIDBRequestChild* AllocPBackgroundIDBRequestChild( const RequestParams& aParams); bool DeallocPBackgroundIDBRequestChild(PBackgroundIDBRequestChild* aActor); PBackgroundIDBCursorChild* AllocPBackgroundIDBCursorChild( const OpenCursorParams& aParams); bool DeallocPBackgroundIDBCursorChild(PBackgroundIDBCursorChild* aActor); }; class BackgroundRequestChild final : public BackgroundRequestChildBase, public PBackgroundIDBRequestChild { friend class BackgroundTransactionChild; friend class BackgroundVersionChangeTransactionChild; friend struct CloneInfo; friend IDBTransaction; class PreprocessHelper; SafeRefPtr mTransaction; nsTArray mCloneInfos; uint32_t mRunningPreprocessHelpers; uint32_t mCurrentCloneDataIndex; nsresult mPreprocessResultCode; bool mGetAll; private: // Only created by IDBTransaction. explicit BackgroundRequestChild(MovingNotNull> aRequest); // Only destroyed by BackgroundTransactionChild or // BackgroundVersionChangeTransactionChild. ~BackgroundRequestChild(); void MaybeSendContinue(); void OnPreprocessFinished(uint32_t aCloneDataIndex, UniquePtr aCloneData); void OnPreprocessFailed(uint32_t aCloneDataIndex, nsresult aErrorCode); UniquePtr GetNextCloneData(); void HandleResponse(nsresult aResponse); void HandleResponse(const Key& aResponse); void HandleResponse(const nsTArray& aResponse); void HandleResponse(SerializedStructuredCloneReadInfo&& aResponse); void HandleResponse(nsTArray&& aResponse); void HandleResponse(JS::Handle aResponse); void HandleResponse(uint64_t aResponse); nsresult HandlePreprocess(const PreprocessInfo& aPreprocessInfo); nsresult HandlePreprocess(const nsTArray& aPreprocessInfos); nsresult HandlePreprocessInternal( const nsTArray& aPreprocessInfos); SafeRefPtr AcquireTransaction() const { return mTransaction.clonePtr(); } public: // IPDL methods are only called by IPDL. void ActorDestroy(ActorDestroyReason aWhy) override; mozilla::ipc::IPCResult Recv__delete__(RequestResponse&& aResponse); mozilla::ipc::IPCResult RecvPreprocess(const PreprocessParams& aParams); }; struct CloneInfo { RefPtr mPreprocessHelper; UniquePtr mCloneData; }; class BackgroundCursorChildBase : public PBackgroundIDBCursorChild, public SafeRefCounted { private: NS_DECL_OWNINGTHREAD public: MOZ_DECLARE_REFCOUNTED_TYPENAME( mozilla::dom::indexedDB::BackgroundCursorChildBase) MOZ_INLINE_DECL_SAFEREFCOUNTING_INHERITED(BackgroundCursorChildBase, SafeRefCounted) protected: ~BackgroundCursorChildBase(); InitializedOnce> mRequest; Maybe mTransaction; // These are only set while a request is in progress. RefPtr mStrongRequest; RefPtr mStrongCursor; const Direction mDirection; BackgroundCursorChildBase(NotNull aRequest, Direction aDirection); void HandleResponse(nsresult aResponse); public: void AssertIsOnOwningThread() const { NS_ASSERT_OWNINGTHREAD(BackgroundCursorChildBase); } MovingNotNull> AcquireRequest() const; NotNull GetRequest() const { AssertIsOnOwningThread(); return *mRequest; } Direction GetDirection() const { AssertIsOnOwningThread(); return mDirection; } virtual void SendDeleteMeInternal() = 0; virtual mozilla::ipc::IPCResult RecvResponse(CursorResponse&& aResponse) = 0; }; template class BackgroundCursorChild final : public BackgroundCursorChildBase { public: using SourceType = CursorSourceType; using ResponseType = typename CursorTypeTraits::ResponseType; private: friend class BackgroundTransactionChild; friend class BackgroundVersionChangeTransactionChild; InitializedOnce> mSource; IDBCursorImpl* mCursor; std::deque> mCachedResponses, mDelayedResponses; bool mInFlightResponseInvalidationNeeded; public: BackgroundCursorChild(NotNull aRequest, SourceType* aSource, Direction aDirection); void SendContinueInternal(const CursorRequestParams& aParams, const CursorData& aCurrentData); void InvalidateCachedResponses(); template void DiscardCachedResponses(const Condition& aConditionFunc); SourceType* GetSource() const { AssertIsOnOwningThread(); return *mSource; } void SendDeleteMeInternal() final; private: // Only destroyed by BackgroundTransactionChild or // BackgroundVersionChangeTransactionChild. ~BackgroundCursorChild(); void CompleteContinueRequestFromCache(); using BackgroundCursorChildBase::HandleResponse; void HandleResponse(const void_t& aResponse); void HandleResponse(nsTArray&& aResponses); template void HandleMultipleCursorResponses(nsTArray&& aResponses, const Func& aHandleRecord); template [[nodiscard]] RefPtr HandleIndividualCursorResponse( bool aUseAsCurrentResult, Args&&... aArgs); SafeRefPtr SafeRefPtrFromThis(); public: // IPDL methods are only called by IPDL. void ActorDestroy(ActorDestroyReason aWhy) override; mozilla::ipc::IPCResult RecvResponse(CursorResponse&& aResponse) override; // Force callers to use SendContinueInternal. bool SendContinue(const CursorRequestParams& aParams, const Key& aCurrentKey, const Key& aCurrentObjectStoreKey) = delete; bool SendDeleteMe() = delete; }; class BackgroundUtilsChild final : public PBackgroundIndexedDBUtilsChild { friend class mozilla::ipc::BackgroundChildImpl; friend IndexedDatabaseManager; IndexedDatabaseManager* mManager; NS_DECL_OWNINGTHREAD public: void AssertIsOnOwningThread() const { NS_ASSERT_OWNINGTHREAD(BackgroundUtilsChild); } bool SendDeleteMe() = delete; private: // Only created by IndexedDatabaseManager. explicit BackgroundUtilsChild(IndexedDatabaseManager* aManager); // Only destroyed by mozilla::ipc::BackgroundChildImpl. ~BackgroundUtilsChild(); void SendDeleteMeInternal(); public: // IPDL methods are only called by IPDL. void ActorDestroy(ActorDestroyReason aWhy) override; }; } // namespace mozilla::dom::indexedDB #endif // mozilla_dom_indexeddb_actorschild_h__