diff options
Diffstat (limited to '')
-rw-r--r-- | dom/indexedDB/ActorsChild.h | 634 |
1 files changed, 634 insertions, 0 deletions
diff --git a/dom/indexedDB/ActorsChild.h b/dom/indexedDB/ActorsChild.h new file mode 100644 index 0000000000..e3e204be0b --- /dev/null +++ b/dom/indexedDB/ActorsChild.h @@ -0,0 +1,634 @@ +/* -*- 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); + + already_AddRefed<PBackgroundIDBDatabaseChild> + AllocPBackgroundIDBDatabaseChild( + const DatabaseSpec& aSpec, + PBackgroundIDBFactoryRequestChild* aRequest) const; + + mozilla::ipc::IPCResult RecvPBackgroundIDBDatabaseConstructor( + PBackgroundIDBDatabaseChild* aActor, const DatabaseSpec& aSpec, + NotNull<PBackgroundIDBFactoryRequestChild*> aRequest) override; +}; + +class BackgroundDatabaseChild; + +class BackgroundRequestChildBase { + protected: + const NotNull<RefPtr<IDBRequest>> mRequest; + + public: + void AssertIsOnOwningThread() const +#ifdef DEBUG + ; +#else + { + } +#endif + + protected: + explicit BackgroundRequestChildBase( + MovingNotNull<RefPtr<IDBRequest>> 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<IDBFactory> 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<IDBOpenDBRequest*> GetOpenDBRequest() const; + + private: + // Only created by IDBFactory. + BackgroundFactoryRequestChild( + SafeRefPtr<IDBFactory> aFactory, + MovingNotNull<RefPtr<IDBOpenDBRequest>> 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<DatabaseSpec> mSpec; + RefPtr<IDBDatabase> mTemporaryStrongDatabase; + BackgroundFactoryRequestChild* mOpenRequestActor; + IDBDatabase* mDatabase; + + public: + NS_INLINE_DECL_REFCOUNTING(BackgroundDatabaseChild, override) + + 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); + + ~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<PBackgroundIDBVersionChangeTransactionChild> + 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<uint64_t> 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<IDBTransaction> 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<IDBTransaction> aTransaction); + + MOZ_COUNTED_DTOR_VIRTUAL(BackgroundTransactionBase); + + void NoteActorDestroyed(); + + void NoteComplete(); + + private: + // Only called by BackgroundVersionChangeTransactionChild. + void SetDOMTransaction(SafeRefPtr<IDBTransaction> 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<IDBTransaction> 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<IDBTransaction> mTransaction; + nsTArray<CloneInfo> mCloneInfos; + uint32_t mRunningPreprocessHelpers; + uint32_t mCurrentCloneDataIndex; + nsresult mPreprocessResultCode; + bool mGetAll; + + private: + // Only created by IDBTransaction. + explicit BackgroundRequestChild(MovingNotNull<RefPtr<IDBRequest>> aRequest); + + // Only destroyed by BackgroundTransactionChild or + // BackgroundVersionChangeTransactionChild. + ~BackgroundRequestChild(); + + void MaybeSendContinue(); + + void OnPreprocessFinished(uint32_t aCloneDataIndex, + UniquePtr<JSStructuredCloneData> aCloneData); + + void OnPreprocessFailed(uint32_t aCloneDataIndex, nsresult aErrorCode); + + UniquePtr<JSStructuredCloneData> GetNextCloneData(); + + void HandleResponse(nsresult aResponse); + + void HandleResponse(const Key& aResponse); + + void HandleResponse(const nsTArray<Key>& aResponse); + + void HandleResponse(SerializedStructuredCloneReadInfo&& aResponse); + + void HandleResponse(nsTArray<SerializedStructuredCloneReadInfo>&& aResponse); + + void HandleResponse(JS::Handle<JS::Value> aResponse); + + void HandleResponse(uint64_t aResponse); + + nsresult HandlePreprocess(const PreprocessInfo& aPreprocessInfo); + + nsresult HandlePreprocess(const nsTArray<PreprocessInfo>& aPreprocessInfos); + + nsresult HandlePreprocessInternal( + const nsTArray<PreprocessInfo>& aPreprocessInfos); + + SafeRefPtr<IDBTransaction> 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<BackgroundRequestChild::PreprocessHelper> mPreprocessHelper; + UniquePtr<JSStructuredCloneData> mCloneData; +}; + +class BackgroundCursorChildBase + : public PBackgroundIDBCursorChild, + public SafeRefCounted<BackgroundCursorChildBase> { + private: + NS_DECL_OWNINGTHREAD + + public: + MOZ_DECLARE_REFCOUNTED_TYPENAME( + mozilla::dom::indexedDB::BackgroundCursorChildBase) + MOZ_INLINE_DECL_SAFEREFCOUNTING_INHERITED(BackgroundCursorChildBase, + SafeRefCounted) + + protected: + ~BackgroundCursorChildBase(); + + InitializedOnce<const NotNull<IDBRequest*>> mRequest; + Maybe<IDBTransaction&> mTransaction; + + // These are only set while a request is in progress. + RefPtr<IDBRequest> mStrongRequest; + RefPtr<IDBCursor> mStrongCursor; + + const Direction mDirection; + + BackgroundCursorChildBase(NotNull<IDBRequest*> aRequest, + Direction aDirection); + + void HandleResponse(nsresult aResponse); + + public: + void AssertIsOnOwningThread() const { + NS_ASSERT_OWNINGTHREAD(BackgroundCursorChildBase); + } + + MovingNotNull<RefPtr<IDBRequest>> AcquireRequest() const; + + NotNull<IDBRequest*> GetRequest() const { + AssertIsOnOwningThread(); + + return *mRequest; + } + + Direction GetDirection() const { + AssertIsOnOwningThread(); + + return mDirection; + } + + virtual void SendDeleteMeInternal() = 0; + + virtual mozilla::ipc::IPCResult RecvResponse(CursorResponse&& aResponse) = 0; +}; + +template <IDBCursorType CursorType> +class BackgroundCursorChild final : public BackgroundCursorChildBase { + public: + using SourceType = CursorSourceType<CursorType>; + using ResponseType = typename CursorTypeTraits<CursorType>::ResponseType; + + private: + friend class BackgroundTransactionChild; + friend class BackgroundVersionChangeTransactionChild; + + InitializedOnce<const NotNull<SourceType*>> mSource; + IDBCursorImpl<CursorType>* mCursor; + + std::deque<CursorData<CursorType>> mCachedResponses, mDelayedResponses; + bool mInFlightResponseInvalidationNeeded; + + public: + BackgroundCursorChild(NotNull<IDBRequest*> aRequest, SourceType* aSource, + Direction aDirection); + + void SendContinueInternal(const CursorRequestParams& aParams, + const CursorData<CursorType>& aCurrentData); + + void InvalidateCachedResponses(); + + template <typename Condition> + 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<ResponseType>&& aResponses); + + template <typename Func> + void HandleMultipleCursorResponses(nsTArray<ResponseType>&& aResponses, + const Func& aHandleRecord); + + template <typename... Args> + [[nodiscard]] RefPtr<IDBCursor> HandleIndividualCursorResponse( + bool aUseAsCurrentResult, Args&&... aArgs); + + SafeRefPtr<BackgroundCursorChild> 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__ |