/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* 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 "IDBTransaction.h"
#include "js/RootingAPI.h"
#include "mozilla/Attributes.h"
#include "mozilla/dom/filehandle/ActorsChild.h"
#include "mozilla/dom/indexedDB/PBackgroundIDBCursorChild.h"
#include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseChild.h"
#include "mozilla/dom/indexedDB/PBackgroundIDBDatabaseRequestChild.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 "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsTArray.h"

class nsIEventTarget;
struct nsID;
struct PRThread;

namespace JS {
struct WasmModule;
} // namespace JS

namespace mozilla {
namespace ipc {

class BackgroundChildImpl;

} // namespace ipc

namespace dom {

class IDBCursor;
class IDBDatabase;
class IDBFactory;
class IDBMutableFile;
class IDBOpenDBRequest;
class IDBRequest;
class IndexedDatabaseManager;

namespace indexedDB {

class Key;
class PermissionRequestChild;
class PermissionRequestParent;
class SerializedStructuredCloneReadInfo;

class ThreadLocal
{
  friend class nsAutoPtr<ThreadLocal>;
  friend IDBFactory;

  LoggingInfo mLoggingInfo;
  IDBTransaction* mCurrentTransaction;
  nsCString mLoggingIdString;

#ifdef DEBUG
  PRThread* mOwningThread;
#endif

public:
  void
  AssertIsOnOwningThread() const
#ifdef DEBUG
  ;
#else
  { }
#endif

  const LoggingInfo&
  GetLoggingInfo() const
  {
    AssertIsOnOwningThread();

    return mLoggingInfo;
  }

  const nsID&
  Id() const
  {
    AssertIsOnOwningThread();

    return mLoggingInfo.backgroundChildLoggingId();
  }

  const nsCString&
  IdString() const
  {
    AssertIsOnOwningThread();

    return mLoggingIdString;
  }

  int64_t
  NextTransactionSN(IDBTransaction::Mode aMode)
  {
    AssertIsOnOwningThread();
    MOZ_ASSERT(mLoggingInfo.nextTransactionSerialNumber() < INT64_MAX);
    MOZ_ASSERT(mLoggingInfo.nextVersionChangeTransactionSerialNumber() >
                 INT64_MIN);

    if (aMode == IDBTransaction::Mode::VersionChange) {
      return mLoggingInfo.nextVersionChangeTransactionSerialNumber()--;
    }

    return mLoggingInfo.nextTransactionSerialNumber()++;
  }

  uint64_t
  NextRequestSN()
  {
    AssertIsOnOwningThread();
    MOZ_ASSERT(mLoggingInfo.nextRequestSerialNumber() < UINT64_MAX);

    return mLoggingInfo.nextRequestSerialNumber()++;
  }

  void
  SetCurrentTransaction(IDBTransaction* aCurrentTransaction)
  {
    AssertIsOnOwningThread();

    mCurrentTransaction = aCurrentTransaction;
  }

  IDBTransaction*
  GetCurrentTransaction() const
  {
    AssertIsOnOwningThread();

    return mCurrentTransaction;
  }

private:
  explicit ThreadLocal(const nsID& aBackgroundChildLoggingId);
  ~ThreadLocal();

  ThreadLocal() = delete;
  ThreadLocal(const ThreadLocal& aOther) = delete;
};

class BackgroundFactoryChild final
  : public PBackgroundIDBFactoryChild
{
  friend class mozilla::ipc::BackgroundChildImpl;
  friend IDBFactory;

  IDBFactory* mFactory;

#ifdef DEBUG
  nsCOMPtr<nsIEventTarget> mOwningThread;
#endif

public:
#ifdef DEBUG
  void
  AssertIsOnOwningThread() const;

  nsIEventTarget*
  OwningThread() const;
#else
  void
  AssertIsOnOwningThread() const
  { }
#endif

  IDBFactory*
  GetDOMObject() const
  {
    AssertIsOnOwningThread();
    return mFactory;
  }

private:
  // Only created by IDBFactory.
  explicit BackgroundFactoryChild(IDBFactory* aFactory);

  // Only destroyed by mozilla::ipc::BackgroundChildImpl.
  ~BackgroundFactoryChild();

  void
  SendDeleteMeInternal();

  // IPDL methods are only called by IPDL.
  virtual void
  ActorDestroy(ActorDestroyReason aWhy) override;

  virtual PBackgroundIDBFactoryRequestChild*
  AllocPBackgroundIDBFactoryRequestChild(const FactoryRequestParams& aParams)
                                         override;

  virtual bool
  DeallocPBackgroundIDBFactoryRequestChild(
                                      PBackgroundIDBFactoryRequestChild* aActor)
                                      override;

  virtual PBackgroundIDBDatabaseChild*
  AllocPBackgroundIDBDatabaseChild(const DatabaseSpec& aSpec,
                                   PBackgroundIDBFactoryRequestChild* aRequest)
                                   override;

  virtual bool
  DeallocPBackgroundIDBDatabaseChild(PBackgroundIDBDatabaseChild* aActor)
                                     override;

  bool
  SendDeleteMe() = delete;
};

class BackgroundDatabaseChild;

class BackgroundRequestChildBase
{
protected:
  RefPtr<IDBRequest> mRequest;

public:
  void
  AssertIsOnOwningThread() const
#ifdef DEBUG
  ;
#else
  { }
#endif

protected:
  explicit BackgroundRequestChildBase(IDBRequest* aRequest);

  virtual
  ~BackgroundRequestChildBase();
};

class BackgroundFactoryRequestChild final
  : public BackgroundRequestChildBase
  , public PBackgroundIDBFactoryRequestChild
{
  typedef mozilla::dom::quota::PersistenceType PersistenceType;

  friend IDBFactory;
  friend class BackgroundFactoryChild;
  friend class BackgroundDatabaseChild;
  friend class PermissionRequestChild;
  friend class PermissionRequestParent;

  RefPtr<IDBFactory> mFactory;
  const uint64_t mRequestedVersion;
  const bool mIsDeleteOp;

public:
  IDBOpenDBRequest*
  GetOpenDBRequest() const;

private:
  // Only created by IDBFactory.
  BackgroundFactoryRequestChild(IDBFactory* aFactory,
                                IDBOpenDBRequest* aOpenRequest,
                                bool aIsDeleteOp,
                                uint64_t aRequestedVersion);

  // Only destroyed by BackgroundFactoryChild.
  ~BackgroundFactoryRequestChild();

  bool
  HandleResponse(nsresult aResponse);

  bool
  HandleResponse(const OpenDatabaseRequestResponse& aResponse);

  bool
  HandleResponse(const DeleteDatabaseRequestResponse& aResponse);

  // IPDL methods are only called by IPDL.
  virtual void
  ActorDestroy(ActorDestroyReason aWhy) override;

  virtual bool
  Recv__delete__(const FactoryRequestResponse& aResponse) override;

  virtual bool
  RecvPermissionChallenge(const PrincipalInfo& aPrincipalInfo) override;

  virtual bool
  RecvBlocked(const uint64_t& aCurrentVersion) override;
};

class BackgroundDatabaseChild final
  : public PBackgroundIDBDatabaseChild
{
  friend class BackgroundFactoryChild;
  friend class BackgroundFactoryRequestChild;
  friend IDBDatabase;

  nsAutoPtr<DatabaseSpec> mSpec;
  RefPtr<IDBDatabase> mTemporaryStrongDatabase;
  BackgroundFactoryRequestChild* mOpenRequestActor;
  IDBDatabase* mDatabase;

public:
  void
  AssertIsOnOwningThread() const
  {
    static_cast<BackgroundFactoryChild*>(Manager())->AssertIsOnOwningThread();
  }

#ifdef DEBUG
  nsIEventTarget*
  OwningThread() const
  {
    return static_cast<BackgroundFactoryChild*>(Manager())->OwningThread();
  }
#endif

  const DatabaseSpec*
  Spec() const
  {
    AssertIsOnOwningThread();
    return mSpec;
  }

  IDBDatabase*
  GetDOMObject() const
  {
    AssertIsOnOwningThread();
    return mDatabase;
  }

private:
  // Only constructed by BackgroundFactoryChild.
  BackgroundDatabaseChild(const DatabaseSpec& aSpec,
                          BackgroundFactoryRequestChild* aOpenRequest);

  // Only destroyed by BackgroundFactoryChild.
  ~BackgroundDatabaseChild();

  void
  SendDeleteMeInternal();

  void
  EnsureDOMObject();

  void
  ReleaseDOMObject();

  // IPDL methods are only called by IPDL.
  virtual void
  ActorDestroy(ActorDestroyReason aWhy) override;

  virtual PBackgroundIDBDatabaseFileChild*
  AllocPBackgroundIDBDatabaseFileChild(PBlobChild* aBlobChild)
                                       override;

  virtual bool
  DeallocPBackgroundIDBDatabaseFileChild(
                                        PBackgroundIDBDatabaseFileChild* aActor)
                                        override;

  virtual PBackgroundIDBDatabaseRequestChild*
  AllocPBackgroundIDBDatabaseRequestChild(const DatabaseRequestParams& aParams)
                                          override;

  virtual bool
  DeallocPBackgroundIDBDatabaseRequestChild(
                                     PBackgroundIDBDatabaseRequestChild* aActor)
                                     override;

  virtual PBackgroundIDBTransactionChild*
  AllocPBackgroundIDBTransactionChild(
                                    const nsTArray<nsString>& aObjectStoreNames,
                                    const Mode& aMode)
                                    override;

  virtual bool
  DeallocPBackgroundIDBTransactionChild(PBackgroundIDBTransactionChild* aActor)
                                        override;

  virtual PBackgroundIDBVersionChangeTransactionChild*
  AllocPBackgroundIDBVersionChangeTransactionChild(
                                              const uint64_t& aCurrentVersion,
                                              const uint64_t& aRequestedVersion,
                                              const int64_t& aNextObjectStoreId,
                                              const int64_t& aNextIndexId)
                                              override;

  virtual bool
  RecvPBackgroundIDBVersionChangeTransactionConstructor(
                            PBackgroundIDBVersionChangeTransactionChild* aActor,
                            const uint64_t& aCurrentVersion,
                            const uint64_t& aRequestedVersion,
                            const int64_t& aNextObjectStoreId,
                            const int64_t& aNextIndexId)
                            override;

  virtual bool
  DeallocPBackgroundIDBVersionChangeTransactionChild(
                            PBackgroundIDBVersionChangeTransactionChild* aActor)
                            override;

  virtual PBackgroundMutableFileChild*
  AllocPBackgroundMutableFileChild(const nsString& aName,
                                   const nsString& aType) override;

  virtual bool
  DeallocPBackgroundMutableFileChild(PBackgroundMutableFileChild* aActor)
                                     override;

  virtual bool
  RecvVersionChange(const uint64_t& aOldVersion,
                    const NullableVersion& aNewVersion)
                    override;

  virtual bool
  RecvInvalidate() override;

  virtual bool
  RecvCloseAfterInvalidationComplete() override;

  bool
  SendDeleteMe() = delete;
};

class BackgroundDatabaseRequestChild final
  : public BackgroundRequestChildBase
  , public PBackgroundIDBDatabaseRequestChild
{
  friend class BackgroundDatabaseChild;
  friend IDBDatabase;

  RefPtr<IDBDatabase> mDatabase;

private:
  // Only created by IDBDatabase.
  BackgroundDatabaseRequestChild(IDBDatabase* aDatabase,
                                 IDBRequest* aRequest);

  // Only destroyed by BackgroundDatabaseChild.
  ~BackgroundDatabaseRequestChild();

  bool
  HandleResponse(nsresult aResponse);

  bool
  HandleResponse(const CreateFileRequestResponse& aResponse);

  // IPDL methods are only called by IPDL.
  virtual bool
  Recv__delete__(const DatabaseRequestResponse& aResponse) override;
};

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.
  RefPtr<IDBTransaction> mTemporaryStrongTransaction;

protected:
  // mTransaction is weak and is valid until the NoteActorDestroyed() member
  // function is called.
  IDBTransaction* mTransaction;

public:
#ifdef DEBUG
  virtual void
  AssertIsOnOwningThread() const = 0;
#else
  void
  AssertIsOnOwningThread() const
  { }
#endif

  IDBTransaction*
  GetDOMObject() const
  {
    AssertIsOnOwningThread();
    return mTransaction;
  }

protected:
  BackgroundTransactionBase();
  explicit BackgroundTransactionBase(IDBTransaction* aTransaction);

  virtual
  ~BackgroundTransactionBase();

  void
  NoteActorDestroyed();

  void
  NoteComplete();

private:
  // Only called by BackgroundVersionChangeTransactionChild.
  void
  SetDOMTransaction(IDBTransaction* aDOMObject);
};

class BackgroundTransactionChild final
  : public BackgroundTransactionBase
  , public PBackgroundIDBTransactionChild
{
  friend class BackgroundDatabaseChild;
  friend IDBDatabase;

public:
#ifdef DEBUG
  virtual void
  AssertIsOnOwningThread() const override;
#endif

  void
  SendDeleteMeInternal();

private:
  // Only created by IDBDatabase.
  explicit BackgroundTransactionChild(IDBTransaction* aTransaction);

  // Only destroyed by BackgroundDatabaseChild.
  ~BackgroundTransactionChild();

  // IPDL methods are only called by IPDL.
  virtual void
  ActorDestroy(ActorDestroyReason aWhy) override;

  bool
  RecvComplete(const nsresult& aResult) override;

  virtual PBackgroundIDBRequestChild*
  AllocPBackgroundIDBRequestChild(const RequestParams& aParams) override;

  virtual bool
  DeallocPBackgroundIDBRequestChild(PBackgroundIDBRequestChild* aActor)
                                    override;

  virtual PBackgroundIDBCursorChild*
  AllocPBackgroundIDBCursorChild(const OpenCursorParams& aParams) override;

  virtual bool
  DeallocPBackgroundIDBCursorChild(PBackgroundIDBCursorChild* aActor)
                                   override;

  bool
  SendDeleteMe() = delete;
};

class BackgroundVersionChangeTransactionChild final
  : public BackgroundTransactionBase
  , public PBackgroundIDBVersionChangeTransactionChild
{
  friend class BackgroundDatabaseChild;

  IDBOpenDBRequest* mOpenDBRequest;

public:
#ifdef DEBUG
  virtual void
  AssertIsOnOwningThread() const override;
#endif

  void
  SendDeleteMeInternal(bool aFailedConstructor);

private:
  // Only created by BackgroundDatabaseChild.
  explicit BackgroundVersionChangeTransactionChild(IDBOpenDBRequest* aOpenDBRequest);

  // Only destroyed by BackgroundDatabaseChild.
  ~BackgroundVersionChangeTransactionChild();

  // Only called by BackgroundDatabaseChild.
  void
  SetDOMTransaction(IDBTransaction* aDOMObject)
  {
    BackgroundTransactionBase::SetDOMTransaction(aDOMObject);
  }

  // IPDL methods are only called by IPDL.
  virtual void
  ActorDestroy(ActorDestroyReason aWhy) override;

  bool
  RecvComplete(const nsresult& aResult) override;

  virtual PBackgroundIDBRequestChild*
  AllocPBackgroundIDBRequestChild(const RequestParams& aParams) override;

  virtual bool
  DeallocPBackgroundIDBRequestChild(PBackgroundIDBRequestChild* aActor)
                                    override;

  virtual PBackgroundIDBCursorChild*
  AllocPBackgroundIDBCursorChild(const OpenCursorParams& aParams) override;

  virtual bool
  DeallocPBackgroundIDBCursorChild(PBackgroundIDBCursorChild* aActor)
                                   override;

  bool
  SendDeleteMe() = delete;
};

class BackgroundMutableFileChild final
  : public mozilla::dom::BackgroundMutableFileChildBase
{
  friend class BackgroundDatabaseChild;

  nsString mName;
  nsString mType;

private:
  // Only constructed by BackgroundDatabaseChild.
  BackgroundMutableFileChild(DEBUGONLY(PRThread* aOwningThread,)
                             const nsAString& aName,
                             const nsAString& aType);

  // Only destroyed by BackgroundDatabaseChild.
  ~BackgroundMutableFileChild();

  // BackgroundMutableFileChildBase
  virtual already_AddRefed<MutableFileBase>
  CreateMutableFile() override;
};

class BackgroundRequestChild final
  : public BackgroundRequestChildBase
  , public PBackgroundIDBRequestChild
{
  friend class BackgroundTransactionChild;
  friend class BackgroundVersionChangeTransactionChild;
  friend IDBTransaction;

  class PreprocessHelper;

  RefPtr<IDBTransaction> mTransaction;
  nsTArray<RefPtr<PreprocessHelper>> mPreprocessHelpers;
  nsTArray<nsTArray<RefPtr<JS::WasmModule>>> mModuleSets;
  uint32_t mRunningPreprocessHelpers;
  uint32_t mCurrentModuleSetIndex;
  nsresult mPreprocessResultCode;
  bool mGetAll;

private:
  // Only created by IDBTransaction.
  explicit
  BackgroundRequestChild(IDBRequest* aRequest);

  // Only destroyed by BackgroundTransactionChild or
  // BackgroundVersionChangeTransactionChild.
  ~BackgroundRequestChild();

  void
  MaybeSendContinue();

  void
  OnPreprocessFinished(uint32_t aModuleSetIndex,
                       nsTArray<RefPtr<JS::WasmModule>>& aModuleSet);

  void
  OnPreprocessFailed(uint32_t aModuleSetIndex, nsresult aErrorCode);

  const nsTArray<RefPtr<JS::WasmModule>>*
  GetNextModuleSet(const StructuredCloneReadInfo& aInfo);

  void
  HandleResponse(nsresult aResponse);

  void
  HandleResponse(const Key& aResponse);

  void
  HandleResponse(const nsTArray<Key>& aResponse);

  void
  HandleResponse(const SerializedStructuredCloneReadInfo& aResponse);

  void
  HandleResponse(const nsTArray<SerializedStructuredCloneReadInfo>& aResponse);

  void
  HandleResponse(JS::Handle<JS::Value> aResponse);

  void
  HandleResponse(uint64_t aResponse);

  nsresult
  HandlePreprocess(const WasmModulePreprocessInfo& aPreprocessInfo);

  nsresult
  HandlePreprocess(const nsTArray<WasmModulePreprocessInfo>& aPreprocessInfos);

  // IPDL methods are only called by IPDL.
  virtual void
  ActorDestroy(ActorDestroyReason aWhy) override;

  virtual bool
  Recv__delete__(const RequestResponse& aResponse) override;

  virtual bool
  RecvPreprocess(const PreprocessParams& aParams) override;
};

class BackgroundCursorChild final
  : public PBackgroundIDBCursorChild
{
  friend class BackgroundTransactionChild;
  friend class BackgroundVersionChangeTransactionChild;

  class DelayedActionRunnable;

  IDBRequest* mRequest;
  IDBTransaction* mTransaction;
  IDBObjectStore* mObjectStore;
  IDBIndex* mIndex;
  IDBCursor* mCursor;

  // These are only set while a request is in progress.
  RefPtr<IDBRequest> mStrongRequest;
  RefPtr<IDBCursor> mStrongCursor;

  Direction mDirection;

#ifdef DEBUG
  PRThread* mOwningThread;
#endif

public:
  BackgroundCursorChild(IDBRequest* aRequest,
                        IDBObjectStore* aObjectStore,
                        Direction aDirection);

  BackgroundCursorChild(IDBRequest* aRequest,
                        IDBIndex* aIndex,
                        Direction aDirection);

  void
  AssertIsOnOwningThread() const
#ifdef DEBUG
  ;
#else
  { }
#endif

  void
  SendContinueInternal(const CursorRequestParams& aParams);

  void
  SendDeleteMeInternal();

  IDBRequest*
  GetRequest() const
  {
    AssertIsOnOwningThread();

    return mRequest;
  }

  IDBObjectStore*
  GetObjectStore() const
  {
    AssertIsOnOwningThread();

    return mObjectStore;
  }

  IDBIndex*
  GetIndex() const
  {
    AssertIsOnOwningThread();

    return mIndex;
  }

  Direction
  GetDirection() const
  {
    AssertIsOnOwningThread();

    return mDirection;
  }

private:
  // Only destroyed by BackgroundTransactionChild or
  // BackgroundVersionChangeTransactionChild.
  ~BackgroundCursorChild();

  void
  HandleResponse(nsresult aResponse);

  void
  HandleResponse(const void_t& aResponse);

  void
  HandleResponse(const nsTArray<ObjectStoreCursorResponse>& aResponse);

  void
  HandleResponse(const ObjectStoreKeyCursorResponse& aResponse);

  void
  HandleResponse(const IndexCursorResponse& aResponse);

  void
  HandleResponse(const IndexKeyCursorResponse& aResponse);

  // IPDL methods are only called by IPDL.
  virtual void
  ActorDestroy(ActorDestroyReason aWhy) override;

  virtual bool
  RecvResponse(const CursorResponse& aResponse) override;

  // Force callers to use SendContinueInternal.
  bool
  SendContinue(const CursorRequestParams& aParams) = delete;

  bool
  SendDeleteMe() = delete;
};

class BackgroundUtilsChild final
  : public PBackgroundIndexedDBUtilsChild
{
  friend class mozilla::ipc::BackgroundChildImpl;
  friend IndexedDatabaseManager;

  IndexedDatabaseManager* mManager;

#ifdef DEBUG
  nsCOMPtr<nsIEventTarget> mOwningThread;
#endif

public:
  void
  AssertIsOnOwningThread() const
#ifdef DEBUG
  ;
#else
  { }
#endif

private:
  // Only created by IndexedDatabaseManager.
  explicit BackgroundUtilsChild(IndexedDatabaseManager* aManager);

  // Only destroyed by mozilla::ipc::BackgroundChildImpl.
  ~BackgroundUtilsChild();

  void
  SendDeleteMeInternal();

  // IPDL methods are only called by IPDL.
  virtual void
  ActorDestroy(ActorDestroyReason aWhy) override;

  bool
  SendDeleteMe() = delete;
};

} // namespace indexedDB
} // namespace dom
} // namespace mozilla

#endif // mozilla_dom_indexeddb_actorschild_h__
