/* -*- 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/. */

#include "BackgroundChild.h"
#include "BackgroundParent.h"

#include "BackgroundChildImpl.h"
#include "BackgroundParentImpl.h"
#include "base/process_util.h"
#include "base/task.h"
#include "FileDescriptor.h"
#include "GeckoProfiler.h"
#include "InputStreamUtils.h"
#include "mozilla/Assertions.h"
#include "mozilla/Atomics.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/Unused.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/ipc/BlobChild.h"
#include "mozilla/dom/ipc/BlobParent.h"
#include "mozilla/dom/ipc/nsIRemoteBlob.h"
#include "mozilla/ipc/ProtocolTypes.h"
#include "nsAutoPtr.h"
#include "nsCOMPtr.h"
#include "nsIEventTarget.h"
#include "nsIIPCBackgroundChildCreateCallback.h"
#include "nsIMutable.h"
#include "nsIObserver.h"
#include "nsIObserverService.h"
#include "nsIRunnable.h"
#include "nsISupportsImpl.h"
#include "nsIThread.h"
#include "nsITimer.h"
#include "nsTArray.h"
#include "nsThreadUtils.h"
#include "nsTraceRefcnt.h"
#include "nsXULAppAPI.h"
#include "nsXPCOMPrivate.h"
#include "prthread.h"

#ifdef RELEASE_OR_BETA
#define THREADSAFETY_ASSERT MOZ_ASSERT
#else
#define THREADSAFETY_ASSERT MOZ_RELEASE_ASSERT
#endif

#define CRASH_IN_CHILD_PROCESS(_msg)                                           \
  do {                                                                         \
    if (XRE_IsParentProcess()) {                                                     \
      MOZ_ASSERT(false, _msg);                                                 \
    } else {                                                                   \
      MOZ_CRASH(_msg);                                                         \
    }                                                                          \
  }                                                                            \
  while (0)

using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::ipc;

namespace {

// -----------------------------------------------------------------------------
// Utility Functions
// -----------------------------------------------------------------------------


void
AssertIsInMainProcess()
{
  MOZ_ASSERT(XRE_IsParentProcess());
}

void
AssertIsInChildProcess()
{
  MOZ_ASSERT(!XRE_IsParentProcess());
}

void
AssertIsOnMainThread()
{
  THREADSAFETY_ASSERT(NS_IsMainThread());
}

// -----------------------------------------------------------------------------
// ParentImpl Declaration
// -----------------------------------------------------------------------------

class ParentImpl final : public BackgroundParentImpl
{
  friend class mozilla::ipc::BackgroundParent;

public:
  class CreateCallback;

private:
  class ShutdownObserver;
  class RequestMessageLoopRunnable;
  class ShutdownBackgroundThreadRunnable;
  class ForceCloseBackgroundActorsRunnable;
  class CreateCallbackRunnable;
  class ConnectActorRunnable;

  struct MOZ_STACK_CLASS TimerCallbackClosure
  {
    nsIThread* mThread;
    nsTArray<ParentImpl*>* mLiveActors;

    TimerCallbackClosure(nsIThread* aThread, nsTArray<ParentImpl*>* aLiveActors)
      : mThread(aThread), mLiveActors(aLiveActors)
    {
      AssertIsInMainProcess();
      AssertIsOnMainThread();
      MOZ_ASSERT(aThread);
      MOZ_ASSERT(aLiveActors);
    }
  };

  // The length of time we will wait at shutdown for all actors to clean
  // themselves up before forcing them to be destroyed.
  static const uint32_t kShutdownTimerDelayMS = 10000;

  // This is only modified on the main thread. It is null if the thread does not
  // exist or is shutting down.
  static StaticRefPtr<nsIThread> sBackgroundThread;

  // This is created and destroyed on the main thread but only modified on the
  // background thread. It is specific to each instance of sBackgroundThread.
  static nsTArray<ParentImpl*>* sLiveActorsForBackgroundThread;

  // This is only modified on the main thread.
  static StaticRefPtr<nsITimer> sShutdownTimer;

  // This exists so that that [Assert]IsOnBackgroundThread() can continue to
  // work during shutdown.
  static Atomic<PRThread*> sBackgroundPRThread;

  // This is only modified on the main thread. It is null if the thread does not
  // exist or is shutting down.
  static MessageLoop* sBackgroundThreadMessageLoop;

  // This is only modified on the main thread. It maintains a count of live
  // actors so that the background thread can be shut down when it is no longer
  // needed.
  static uint64_t sLiveActorCount;

  // This is only modified on the main thread. It is true after the shutdown
  // observer is registered and is never unset thereafter.
  static bool sShutdownObserverRegistered;

  // This is only modified on the main thread. It prevents us from trying to
  // create the background thread after application shutdown has started.
  static bool sShutdownHasStarted;

  // This is only modified on the main thread. It is a FIFO queue for callbacks
  // waiting for the background thread to be created.
  static StaticAutoPtr<nsTArray<RefPtr<CreateCallback>>> sPendingCallbacks;

  // Only touched on the main thread, null if this is a same-process actor.
  RefPtr<ContentParent> mContent;

  // Set when the actor is opened successfully and used to handle shutdown
  // hangs. Only touched on the background thread.
  nsTArray<ParentImpl*>* mLiveActorArray;

  // Set at construction to indicate whether this parent actor corresponds to a
  // child actor in another process or to a child actor from a different thread
  // in the same process.
  const bool mIsOtherProcessActor;

  // Set after ActorDestroy has been called. Only touched on the background
  // thread.
  bool mActorDestroyed;

public:
  static bool
  CreateActorForSameProcess(CreateCallback* aCallback);

  static bool
  IsOnBackgroundThread()
  {
    return PR_GetCurrentThread() == sBackgroundPRThread;
  }

  static void
  AssertIsOnBackgroundThread()
  {
    THREADSAFETY_ASSERT(IsOnBackgroundThread());
  }

  NS_INLINE_DECL_REFCOUNTING(ParentImpl)

  void
  Destroy();

private:
  // Forwarded from BackgroundParent.
  static bool
  IsOtherProcessActor(PBackgroundParent* aBackgroundActor);

  // Forwarded from BackgroundParent.
  static already_AddRefed<ContentParent>
  GetContentParent(PBackgroundParent* aBackgroundActor);

  // Forwarded from BackgroundParent.
  static intptr_t
  GetRawContentParentForComparison(PBackgroundParent* aBackgroundActor);

  // Forwarded from BackgroundParent.
  static PBackgroundParent*
  Alloc(ContentParent* aContent,
        Transport* aTransport,
        ProcessId aOtherPid);

  static bool
  CreateBackgroundThread();

  static void
  ShutdownBackgroundThread();

  static void
  ShutdownTimerCallback(nsITimer* aTimer, void* aClosure);

  // For same-process actors.
  ParentImpl()
  : mLiveActorArray(nullptr), mIsOtherProcessActor(false),
    mActorDestroyed(false)
  {
    AssertIsInMainProcess();
    AssertIsOnMainThread();
  }

  // For other-process actors.
  explicit ParentImpl(ContentParent* aContent)
  : mContent(aContent), mLiveActorArray(nullptr),
    mIsOtherProcessActor(true), mActorDestroyed(false)
  {
    AssertIsInMainProcess();
    AssertIsOnMainThread();
    MOZ_ASSERT(aContent);
  }

  ~ParentImpl()
  {
    AssertIsInMainProcess();
    AssertIsOnMainThread();
    MOZ_ASSERT(!mContent);
  }

  void
  MainThreadActorDestroy();

  void
  SetLiveActorArray(nsTArray<ParentImpl*>* aLiveActorArray)
  {
    AssertIsInMainProcess();
    AssertIsOnBackgroundThread();
    MOZ_ASSERT(aLiveActorArray);
    MOZ_ASSERT(!aLiveActorArray->Contains(this));
    MOZ_ASSERT(!mLiveActorArray);
    MOZ_ASSERT(mIsOtherProcessActor);

    mLiveActorArray = aLiveActorArray;
    mLiveActorArray->AppendElement(this);
  }

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

// -----------------------------------------------------------------------------
// ChildImpl Declaration
// -----------------------------------------------------------------------------

class ChildImpl final : public BackgroundChildImpl
{
  friend class mozilla::ipc::BackgroundChild;
  friend class mozilla::ipc::BackgroundChildImpl;

  typedef base::ProcessId ProcessId;
  typedef mozilla::ipc::Transport Transport;

  class ShutdownObserver;
  class CreateActorRunnable;
  class ParentCreateCallback;
  class AlreadyCreatedCallbackRunnable;
  class FailedCreateCallbackRunnable;
  class OpenChildProcessActorRunnable;
  class OpenMainProcessActorRunnable;

  // A thread-local index that is not valid.
  static const unsigned int kBadThreadLocalIndex =
    static_cast<unsigned int>(-1);

  // This is only modified on the main thread. It is the thread-local index that
  // we use to store the BackgroundChild for each thread.
  static unsigned int sThreadLocalIndex;

  struct ThreadLocalInfo
  {
    explicit ThreadLocalInfo(nsIIPCBackgroundChildCreateCallback* aCallback)
#ifdef DEBUG
      : mClosed(false)
#endif
    {
      mCallbacks.AppendElement(aCallback);
    }

    RefPtr<ChildImpl> mActor;
    nsTArray<nsCOMPtr<nsIIPCBackgroundChildCreateCallback>> mCallbacks;
    nsAutoPtr<BackgroundChildImpl::ThreadLocal> mConsumerThreadLocal;
#ifdef DEBUG
    bool mClosed;
#endif
  };

  // This is only modified on the main thread. It is a FIFO queue for actors
  // that are in the process of construction.
  static StaticAutoPtr<nsTArray<nsCOMPtr<nsIEventTarget>>> sPendingTargets;

  // This is only modified on the main thread. It prevents us from trying to
  // create the background thread after application shutdown has started.
  static bool sShutdownHasStarted;

#if defined(DEBUG) || !defined(RELEASE_OR_BETA)
  nsIThread* mBoundThread;
#endif

#ifdef DEBUG
  bool mActorDestroyed;
#endif

public:
  static bool
  OpenProtocolOnMainThread(nsIEventTarget* aEventTarget);

  static void
  Shutdown();

  void
  AssertIsOnBoundThread()
  {
    THREADSAFETY_ASSERT(mBoundThread);

#ifdef RELEASE_OR_BETA
    DebugOnly<bool> current;
#else
    bool current;
#endif
    THREADSAFETY_ASSERT(
      NS_SUCCEEDED(mBoundThread->IsOnCurrentThread(&current)));
    THREADSAFETY_ASSERT(current);
  }

  void
  AssertActorDestroyed()
  {
    MOZ_ASSERT(mActorDestroyed, "ChildImpl::ActorDestroy not called in time");
  }

  ChildImpl()
#if defined(DEBUG) || !defined(RELEASE_OR_BETA)
  : mBoundThread(nullptr)
#endif
#ifdef DEBUG
  , mActorDestroyed(false)
#endif
  {
    AssertIsOnMainThread();
  }

  NS_INLINE_DECL_REFCOUNTING(ChildImpl)

private:
  // Forwarded from BackgroundChild.
  static void
  Startup();

  // Forwarded from BackgroundChild.
  static PBackgroundChild*
  Alloc(Transport* aTransport, ProcessId aOtherPid);

  // Forwarded from BackgroundChild.
  static PBackgroundChild*
  GetForCurrentThread();

  // Forwarded from BackgroundChild.
  static bool
  GetOrCreateForCurrentThread(nsIIPCBackgroundChildCreateCallback* aCallback);

  // Forwarded from BackgroundChild.
  static PBackgroundChild*
  SynchronouslyCreateForCurrentThread();

  // Forwarded from BackgroundChild.
  static void
  CloseForCurrentThread();

  // Forwarded from BackgroundChildImpl.
  static BackgroundChildImpl::ThreadLocal*
  GetThreadLocalForCurrentThread();

  static void
  ThreadLocalDestructor(void* aThreadLocal)
  {
    auto threadLocalInfo = static_cast<ThreadLocalInfo*>(aThreadLocal);

    if (threadLocalInfo) {
      MOZ_ASSERT(threadLocalInfo->mClosed);

      if (threadLocalInfo->mActor) {
        threadLocalInfo->mActor->Close();
        threadLocalInfo->mActor->AssertActorDestroyed();

        // Since the actor is created on the main thread it must only
        // be released on the main thread as well.
        if (!NS_IsMainThread()) {
          ChildImpl* actor;
          threadLocalInfo->mActor.forget(&actor);

          MOZ_ALWAYS_SUCCEEDS(
            NS_DispatchToMainThread(NewNonOwningRunnableMethod(actor, &ChildImpl::Release)));
        }
      }
      delete threadLocalInfo;
    }
  }

  static void
  DispatchFailureCallback(nsIEventTarget* aEventTarget);

  // This class is reference counted.
  ~ChildImpl()
  {
    AssertActorDestroyed();
  }

  void
  SetBoundThread()
  {
    THREADSAFETY_ASSERT(!mBoundThread);

#if defined(DEBUG) || !defined(RELEASE_OR_BETA)
    mBoundThread = NS_GetCurrentThread();
#endif

    THREADSAFETY_ASSERT(mBoundThread);
  }

  // Only called by IPDL.
  virtual void
  ActorDestroy(ActorDestroyReason aWhy) override;

  static already_AddRefed<nsIIPCBackgroundChildCreateCallback>
  GetNextCallback();
};

// -----------------------------------------------------------------------------
// ParentImpl Helper Declarations
// -----------------------------------------------------------------------------

class ParentImpl::ShutdownObserver final : public nsIObserver
{
public:
  ShutdownObserver()
  {
    AssertIsOnMainThread();
  }

  NS_DECL_ISUPPORTS
  NS_DECL_NSIOBSERVER

private:
  ~ShutdownObserver()
  {
    AssertIsOnMainThread();
  }
};

class ParentImpl::RequestMessageLoopRunnable final : public Runnable
{
  nsCOMPtr<nsIThread> mTargetThread;
  MessageLoop* mMessageLoop;

public:
  explicit RequestMessageLoopRunnable(nsIThread* aTargetThread)
  : mTargetThread(aTargetThread), mMessageLoop(nullptr)
  {
    AssertIsInMainProcess();
    AssertIsOnMainThread();
    MOZ_ASSERT(aTargetThread);
  }

private:
  ~RequestMessageLoopRunnable()
  { }

  NS_DECL_NSIRUNNABLE
};

class ParentImpl::ShutdownBackgroundThreadRunnable final : public Runnable
{
public:
  ShutdownBackgroundThreadRunnable()
  {
    AssertIsInMainProcess();
    AssertIsOnMainThread();
  }

private:
  ~ShutdownBackgroundThreadRunnable()
  { }

  NS_DECL_NSIRUNNABLE
};

class ParentImpl::ForceCloseBackgroundActorsRunnable final : public Runnable
{
  nsTArray<ParentImpl*>* mActorArray;

public:
  explicit ForceCloseBackgroundActorsRunnable(nsTArray<ParentImpl*>* aActorArray)
  : mActorArray(aActorArray)
  {
    AssertIsInMainProcess();
    AssertIsOnMainThread();
    MOZ_ASSERT(aActorArray);
  }

private:
  ~ForceCloseBackgroundActorsRunnable()
  { }

  NS_DECL_NSIRUNNABLE
};

class ParentImpl::CreateCallbackRunnable final : public Runnable
{
  RefPtr<CreateCallback> mCallback;

public:
  explicit CreateCallbackRunnable(CreateCallback* aCallback)
  : mCallback(aCallback)
  {
    AssertIsInMainProcess();
    AssertIsOnMainThread();
    MOZ_ASSERT(aCallback);
  }

private:
  ~CreateCallbackRunnable()
  { }

  NS_DECL_NSIRUNNABLE
};

class ParentImpl::ConnectActorRunnable final : public Runnable
{
  RefPtr<ParentImpl> mActor;
  Transport* mTransport;
  ProcessId mOtherPid;
  nsTArray<ParentImpl*>* mLiveActorArray;

public:
  ConnectActorRunnable(ParentImpl* aActor,
                       Transport* aTransport,
                       ProcessId aOtherPid,
                       nsTArray<ParentImpl*>* aLiveActorArray)
  : mActor(aActor), mTransport(aTransport), mOtherPid(aOtherPid),
    mLiveActorArray(aLiveActorArray)
  {
    AssertIsInMainProcess();
    AssertIsOnMainThread();
    MOZ_ASSERT(aActor);
    MOZ_ASSERT(aTransport);
    MOZ_ASSERT(aLiveActorArray);
  }

private:
  ~ConnectActorRunnable()
  {
    AssertIsInMainProcess();
  }

  NS_DECL_NSIRUNNABLE
};

class NS_NO_VTABLE ParentImpl::CreateCallback
{
public:
  NS_INLINE_DECL_REFCOUNTING(CreateCallback)

  virtual void
  Success(already_AddRefed<ParentImpl> aActor, MessageLoop* aMessageLoop) = 0;

  virtual void
  Failure() = 0;

protected:
  virtual ~CreateCallback()
  { }
};

// -----------------------------------------------------------------------------
// ChildImpl Helper Declarations
// -----------------------------------------------------------------------------

class ChildImpl::ShutdownObserver final : public nsIObserver
{
public:
  ShutdownObserver()
  {
    AssertIsOnMainThread();
  }

  NS_DECL_ISUPPORTS
  NS_DECL_NSIOBSERVER

private:
  ~ShutdownObserver()
  {
    AssertIsOnMainThread();
  }
};

class ChildImpl::CreateActorRunnable final : public Runnable
{
  nsCOMPtr<nsIEventTarget> mEventTarget;

public:
  CreateActorRunnable()
  : mEventTarget(NS_GetCurrentThread())
  {
    MOZ_ASSERT(mEventTarget);
  }

private:
  ~CreateActorRunnable()
  { }

  NS_DECL_NSIRUNNABLE
};

class ChildImpl::ParentCreateCallback final :
  public ParentImpl::CreateCallback
{
  nsCOMPtr<nsIEventTarget> mEventTarget;

public:
  explicit ParentCreateCallback(nsIEventTarget* aEventTarget)
  : mEventTarget(aEventTarget)
  {
    AssertIsInMainProcess();
    AssertIsOnMainThread();
    MOZ_ASSERT(aEventTarget);
  }

private:
  ~ParentCreateCallback()
  { }

  virtual void
  Success(already_AddRefed<ParentImpl> aActor, MessageLoop* aMessageLoop)
          override;

  virtual void
  Failure() override;
};

// Must be cancelable in order to dispatch on active worker threads
class ChildImpl::AlreadyCreatedCallbackRunnable final :
  public CancelableRunnable
{
public:
  AlreadyCreatedCallbackRunnable()
  {
    // May be created on any thread!
  }

protected:
  virtual ~AlreadyCreatedCallbackRunnable()
  { }

  NS_DECL_NSIRUNNABLE
  nsresult Cancel() override;
};

class ChildImpl::FailedCreateCallbackRunnable final : public Runnable
{
public:
  FailedCreateCallbackRunnable()
  {
    // May be created on any thread!
  }

protected:
  virtual ~FailedCreateCallbackRunnable()
  { }

  NS_DECL_NSIRUNNABLE
};

class ChildImpl::OpenChildProcessActorRunnable final : public Runnable
{
  RefPtr<ChildImpl> mActor;
  nsAutoPtr<Transport> mTransport;
  ProcessId mOtherPid;

public:
  OpenChildProcessActorRunnable(already_AddRefed<ChildImpl>&& aActor,
                                Transport* aTransport,
                                ProcessId aOtherPid)
  : mActor(aActor), mTransport(aTransport),
    mOtherPid(aOtherPid)
  {
    AssertIsOnMainThread();
    MOZ_ASSERT(mActor);
    MOZ_ASSERT(aTransport);
  }

private:
  ~OpenChildProcessActorRunnable()
  {
    if (mTransport) {
      CRASH_IN_CHILD_PROCESS("Leaking transport!");
      Unused << mTransport.forget();
    }
  }

  NS_DECL_NSIRUNNABLE
};

class ChildImpl::OpenMainProcessActorRunnable final : public Runnable
{
  RefPtr<ChildImpl> mActor;
  RefPtr<ParentImpl> mParentActor;
  MessageLoop* mParentMessageLoop;

public:
  OpenMainProcessActorRunnable(already_AddRefed<ChildImpl>&& aChildActor,
                               already_AddRefed<ParentImpl> aParentActor,
                               MessageLoop* aParentMessageLoop)
  : mActor(aChildActor), mParentActor(aParentActor),
    mParentMessageLoop(aParentMessageLoop)
  {
    AssertIsOnMainThread();
    MOZ_ASSERT(mParentActor);
    MOZ_ASSERT(aParentMessageLoop);
  }

private:
  ~OpenMainProcessActorRunnable()
  { }

  NS_DECL_NSIRUNNABLE
};

} // namespace

namespace mozilla {
namespace ipc {

bool
IsOnBackgroundThread()
{
  return ParentImpl::IsOnBackgroundThread();
}

#ifdef DEBUG

void
AssertIsOnBackgroundThread()
{
  ParentImpl::AssertIsOnBackgroundThread();
}

#endif // DEBUG

} // namespace ipc
} // namespace mozilla

// -----------------------------------------------------------------------------
// BackgroundParent Public Methods
// -----------------------------------------------------------------------------

// static
bool
BackgroundParent::IsOtherProcessActor(PBackgroundParent* aBackgroundActor)
{
  return ParentImpl::IsOtherProcessActor(aBackgroundActor);
}

// static
already_AddRefed<ContentParent>
BackgroundParent::GetContentParent(PBackgroundParent* aBackgroundActor)
{
  return ParentImpl::GetContentParent(aBackgroundActor);
}

// static
PBlobParent*
BackgroundParent::GetOrCreateActorForBlobImpl(
                                            PBackgroundParent* aBackgroundActor,
                                            BlobImpl* aBlobImpl)
{
  AssertIsOnBackgroundThread();
  MOZ_ASSERT(aBackgroundActor);
  MOZ_ASSERT(aBlobImpl);

  BlobParent* actor = BlobParent::GetOrCreate(aBackgroundActor, aBlobImpl);
  if (NS_WARN_IF(!actor)) {
    return nullptr;
  }

  return actor;
}

// static
intptr_t
BackgroundParent::GetRawContentParentForComparison(
                                            PBackgroundParent* aBackgroundActor)
{
  return ParentImpl::GetRawContentParentForComparison(aBackgroundActor);
}

// static
PBackgroundParent*
BackgroundParent::Alloc(ContentParent* aContent,
                        Transport* aTransport,
                        ProcessId aOtherPid)
{
  return ParentImpl::Alloc(aContent, aTransport, aOtherPid);
}

// -----------------------------------------------------------------------------
// BackgroundChild Public Methods
// -----------------------------------------------------------------------------

// static
void
BackgroundChild::Startup()
{
  ChildImpl::Startup();
}

// static
PBackgroundChild*
BackgroundChild::Alloc(Transport* aTransport, ProcessId aOtherPid)
{
  return ChildImpl::Alloc(aTransport, aOtherPid);
}

// static
PBackgroundChild*
BackgroundChild::GetForCurrentThread()
{
  return ChildImpl::GetForCurrentThread();
}

// static
bool
BackgroundChild::GetOrCreateForCurrentThread(
                                 nsIIPCBackgroundChildCreateCallback* aCallback)
{
  return ChildImpl::GetOrCreateForCurrentThread(aCallback);
}

// static
PBackgroundChild*
BackgroundChild::SynchronouslyCreateForCurrentThread()
{
  return ChildImpl::SynchronouslyCreateForCurrentThread();
}

// static
PBlobChild*
BackgroundChild::GetOrCreateActorForBlob(PBackgroundChild* aBackgroundActor,
                                         nsIDOMBlob* aBlob)
{
  MOZ_ASSERT(aBlob);

  RefPtr<BlobImpl> blobImpl = static_cast<Blob*>(aBlob)->Impl();
  MOZ_ASSERT(blobImpl);

  return GetOrCreateActorForBlobImpl(aBackgroundActor, blobImpl);
}

// static
PBlobChild*
BackgroundChild::GetOrCreateActorForBlobImpl(PBackgroundChild* aBackgroundActor,
                                             BlobImpl* aBlobImpl)
{
  MOZ_ASSERT(aBackgroundActor);
  MOZ_ASSERT(aBlobImpl);
  MOZ_ASSERT(GetForCurrentThread(),
             "BackgroundChild not created on this thread yet!");
  MOZ_ASSERT(aBackgroundActor == GetForCurrentThread(),
             "BackgroundChild is bound to a different thread!");

  BlobChild* actor = BlobChild::GetOrCreate(aBackgroundActor, aBlobImpl);
  if (NS_WARN_IF(!actor)) {
    return nullptr;
  }

  return actor;
}

// static
void
BackgroundChild::CloseForCurrentThread()
{
  ChildImpl::CloseForCurrentThread();
}

// -----------------------------------------------------------------------------
// BackgroundChildImpl Public Methods
// -----------------------------------------------------------------------------

// static
BackgroundChildImpl::ThreadLocal*
BackgroundChildImpl::GetThreadLocalForCurrentThread()
{
  return ChildImpl::GetThreadLocalForCurrentThread();
}

// -----------------------------------------------------------------------------
// ParentImpl Static Members
// -----------------------------------------------------------------------------

StaticRefPtr<nsIThread> ParentImpl::sBackgroundThread;

nsTArray<ParentImpl*>* ParentImpl::sLiveActorsForBackgroundThread;

StaticRefPtr<nsITimer> ParentImpl::sShutdownTimer;

Atomic<PRThread*> ParentImpl::sBackgroundPRThread;

MessageLoop* ParentImpl::sBackgroundThreadMessageLoop = nullptr;

uint64_t ParentImpl::sLiveActorCount = 0;

bool ParentImpl::sShutdownObserverRegistered = false;

bool ParentImpl::sShutdownHasStarted = false;

StaticAutoPtr<nsTArray<RefPtr<ParentImpl::CreateCallback>>>
  ParentImpl::sPendingCallbacks;

// -----------------------------------------------------------------------------
// ChildImpl Static Members
// -----------------------------------------------------------------------------

unsigned int ChildImpl::sThreadLocalIndex = kBadThreadLocalIndex;

StaticAutoPtr<nsTArray<nsCOMPtr<nsIEventTarget>>> ChildImpl::sPendingTargets;

bool ChildImpl::sShutdownHasStarted = false;

// -----------------------------------------------------------------------------
// ParentImpl Implementation
// -----------------------------------------------------------------------------

// static
bool
ParentImpl::IsOtherProcessActor(PBackgroundParent* aBackgroundActor)
{
  AssertIsOnBackgroundThread();
  MOZ_ASSERT(aBackgroundActor);

  return static_cast<ParentImpl*>(aBackgroundActor)->mIsOtherProcessActor;
}

// static
already_AddRefed<ContentParent>
ParentImpl::GetContentParent(PBackgroundParent* aBackgroundActor)
{
  AssertIsOnBackgroundThread();
  MOZ_ASSERT(aBackgroundActor);

  auto actor = static_cast<ParentImpl*>(aBackgroundActor);
  if (actor->mActorDestroyed) {
    MOZ_ASSERT(false, "GetContentParent called after ActorDestroy was called!");
    return nullptr;
  }

  if (actor->mContent) {
    // We need to hand out a reference to our ContentParent but we also need to
    // keep the one we have. We can't call AddRef here because ContentParent is
    // not threadsafe so instead we dispatch a runnable to the main thread to do
    // it for us. This is safe since we are guaranteed that our AddRef runnable
    // will run before the reference we hand out can be released, and the
    // ContentParent can't die as long as the existing reference is maintained.
    MOZ_ALWAYS_SUCCEEDS(
      NS_DispatchToMainThread(NewNonOwningRunnableMethod(actor->mContent, &ContentParent::AddRef)));
  }

  return already_AddRefed<ContentParent>(actor->mContent.get());
}

// static
intptr_t
ParentImpl::GetRawContentParentForComparison(
                                            PBackgroundParent* aBackgroundActor)
{
  AssertIsOnBackgroundThread();
  MOZ_ASSERT(aBackgroundActor);

  auto actor = static_cast<ParentImpl*>(aBackgroundActor);
  if (actor->mActorDestroyed) {
    MOZ_ASSERT(false,
               "GetRawContentParentForComparison called after ActorDestroy was "
               "called!");
    return intptr_t(-1);
  }

  return intptr_t(static_cast<nsIContentParent*>(actor->mContent.get()));
}

// static
PBackgroundParent*
ParentImpl::Alloc(ContentParent* aContent,
                  Transport* aTransport,
                  ProcessId aOtherPid)
{
  AssertIsInMainProcess();
  AssertIsOnMainThread();
  MOZ_ASSERT(aTransport);

  if (!sBackgroundThread && !CreateBackgroundThread()) {
    NS_WARNING("Failed to create background thread!");
    return nullptr;
  }

  MOZ_ASSERT(sLiveActorsForBackgroundThread);

  sLiveActorCount++;

  RefPtr<ParentImpl> actor = new ParentImpl(aContent);

  nsCOMPtr<nsIRunnable> connectRunnable =
    new ConnectActorRunnable(actor, aTransport, aOtherPid,
                             sLiveActorsForBackgroundThread);

  if (NS_FAILED(sBackgroundThread->Dispatch(connectRunnable,
                                            NS_DISPATCH_NORMAL))) {
    NS_WARNING("Failed to dispatch connect runnable!");

    MOZ_ASSERT(sLiveActorCount);
    sLiveActorCount--;

    return nullptr;
  }

  return actor;
}

// static
bool
ParentImpl::CreateActorForSameProcess(CreateCallback* aCallback)
{
  AssertIsInMainProcess();
  AssertIsOnMainThread();
  MOZ_ASSERT(aCallback);

  if (!sBackgroundThread && !CreateBackgroundThread()) {
    NS_WARNING("Failed to create background thread!");
    return false;
  }

  MOZ_ASSERT(!sShutdownHasStarted);

  sLiveActorCount++;

  if (sBackgroundThreadMessageLoop) {
    nsCOMPtr<nsIRunnable> callbackRunnable =
      new CreateCallbackRunnable(aCallback);
    MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(callbackRunnable));
    return true;
  }

  if (!sPendingCallbacks) {
    sPendingCallbacks = new nsTArray<RefPtr<CreateCallback>>();
  }

  sPendingCallbacks->AppendElement(aCallback);
  return true;
}

// static
bool
ParentImpl::CreateBackgroundThread()
{
  AssertIsInMainProcess();
  AssertIsOnMainThread();
  MOZ_ASSERT(!sBackgroundThread);
  MOZ_ASSERT(!sLiveActorsForBackgroundThread);

  if (sShutdownHasStarted) {
    NS_WARNING("Trying to create background thread after shutdown has "
               "already begun!");
    return false;
  }

  nsCOMPtr<nsITimer> newShutdownTimer;

  if (!sShutdownTimer) {
    nsresult rv;
    newShutdownTimer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return false;
    }
  }

  if (!sShutdownObserverRegistered) {
    nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
    if (NS_WARN_IF(!obs)) {
      return false;
    }

    nsCOMPtr<nsIObserver> observer = new ShutdownObserver();

    nsresult rv =
      obs->AddObserver(observer, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID, false);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return false;
    }

    sShutdownObserverRegistered = true;
  }

  nsCOMPtr<nsIThread> thread;
  if (NS_FAILED(NS_NewNamedThread("IPDL Background", getter_AddRefs(thread)))) {
    NS_WARNING("NS_NewNamedThread failed!");
    return false;
  }

  nsCOMPtr<nsIRunnable> messageLoopRunnable =
    new RequestMessageLoopRunnable(thread);
  if (NS_FAILED(thread->Dispatch(messageLoopRunnable, NS_DISPATCH_NORMAL))) {
    NS_WARNING("Failed to dispatch RequestMessageLoopRunnable!");
    return false;
  }

  sBackgroundThread = thread;
  sLiveActorsForBackgroundThread = new nsTArray<ParentImpl*>(1);

  if (!sShutdownTimer) {
    MOZ_ASSERT(newShutdownTimer);
    sShutdownTimer = newShutdownTimer;
  }

  return true;
}

// static
void
ParentImpl::ShutdownBackgroundThread()
{
  AssertIsInMainProcess();
  AssertIsOnMainThread();
  MOZ_ASSERT_IF(!sBackgroundThread, !sBackgroundThreadMessageLoop);
  MOZ_ASSERT(sShutdownHasStarted);
  MOZ_ASSERT_IF(!sBackgroundThread, !sLiveActorCount);
  MOZ_ASSERT_IF(sBackgroundThread, sShutdownTimer);

  if (sPendingCallbacks) {
    if (!sPendingCallbacks->IsEmpty()) {
      nsTArray<RefPtr<CreateCallback>> callbacks;
      sPendingCallbacks->SwapElements(callbacks);

      for (uint32_t index = 0; index < callbacks.Length(); index++) {
        RefPtr<CreateCallback> callback;
        callbacks[index].swap(callback);
        MOZ_ASSERT(callback);

        callback->Failure();
      }
    }

    sPendingCallbacks = nullptr;
  }

  nsCOMPtr<nsITimer> shutdownTimer = sShutdownTimer.get();
  sShutdownTimer = nullptr;

  if (sBackgroundThread) {
    nsCOMPtr<nsIThread> thread = sBackgroundThread.get();
    sBackgroundThread = nullptr;

    nsAutoPtr<nsTArray<ParentImpl*>> liveActors(sLiveActorsForBackgroundThread);
    sLiveActorsForBackgroundThread = nullptr;

    sBackgroundThreadMessageLoop = nullptr;

    MOZ_ASSERT_IF(!sShutdownHasStarted, !sLiveActorCount);

    if (sLiveActorCount) {
      // We need to spin the event loop while we wait for all the actors to be
      // cleaned up. We also set a timeout to force-kill any hanging actors.
      TimerCallbackClosure closure(thread, liveActors);

      MOZ_ALWAYS_SUCCEEDS(
        shutdownTimer->InitWithFuncCallback(&ShutdownTimerCallback,
                                            &closure,
                                            kShutdownTimerDelayMS,
                                            nsITimer::TYPE_ONE_SHOT));

      nsIThread* currentThread = NS_GetCurrentThread();
      MOZ_ASSERT(currentThread);

      while (sLiveActorCount) {
        NS_ProcessNextEvent(currentThread);
      }

      MOZ_ASSERT(liveActors->IsEmpty());

      MOZ_ALWAYS_SUCCEEDS(shutdownTimer->Cancel());
    }

    // Dispatch this runnable to unregister the thread from the profiler.
    nsCOMPtr<nsIRunnable> shutdownRunnable =
      new ShutdownBackgroundThreadRunnable();
    MOZ_ALWAYS_SUCCEEDS(thread->Dispatch(shutdownRunnable, NS_DISPATCH_NORMAL));

    MOZ_ALWAYS_SUCCEEDS(thread->Shutdown());
  }
}

// static
void
ParentImpl::ShutdownTimerCallback(nsITimer* aTimer, void* aClosure)
{
  AssertIsInMainProcess();
  AssertIsOnMainThread();
  MOZ_ASSERT(sShutdownHasStarted);
  MOZ_ASSERT(sLiveActorCount);

  auto closure = static_cast<TimerCallbackClosure*>(aClosure);
  MOZ_ASSERT(closure);

  // Don't let the stack unwind until the ForceCloseBackgroundActorsRunnable has
  // finished.
  sLiveActorCount++;

  nsCOMPtr<nsIRunnable> forceCloseRunnable =
    new ForceCloseBackgroundActorsRunnable(closure->mLiveActors);
  MOZ_ALWAYS_SUCCEEDS(closure->mThread->Dispatch(forceCloseRunnable,
                                                 NS_DISPATCH_NORMAL));
}

void
ParentImpl::Destroy()
{
  // May be called on any thread!

  AssertIsInMainProcess();

  MOZ_ALWAYS_SUCCEEDS(
    NS_DispatchToMainThread(NewNonOwningRunnableMethod(this, &ParentImpl::MainThreadActorDestroy)));
}

void
ParentImpl::MainThreadActorDestroy()
{
  AssertIsInMainProcess();
  AssertIsOnMainThread();
  MOZ_ASSERT_IF(mIsOtherProcessActor, mContent);
  MOZ_ASSERT_IF(!mIsOtherProcessActor, !mContent);

  mContent = nullptr;

  MOZ_ASSERT(sLiveActorCount);
  sLiveActorCount--;

  // This may be the last reference!
  Release();
}

void
ParentImpl::ActorDestroy(ActorDestroyReason aWhy)
{
  AssertIsInMainProcess();
  AssertIsOnBackgroundThread();
  MOZ_ASSERT(!mActorDestroyed);
  MOZ_ASSERT_IF(mIsOtherProcessActor, mLiveActorArray);

  BackgroundParentImpl::ActorDestroy(aWhy);

  mActorDestroyed = true;

  if (mLiveActorArray) {
    MOZ_ALWAYS_TRUE(mLiveActorArray->RemoveElement(this));
    mLiveActorArray = nullptr;
  }

  // This is tricky. We should be able to call Destroy() here directly because
  // we're not going to touch 'this' or our MessageChannel any longer on this
  // thread. Destroy() dispatches the MainThreadActorDestroy runnable and when
  // it runs it will destroy 'this' and our associated MessageChannel. However,
  // IPDL is about to call MessageChannel::Clear() on this thread! To avoid
  // racing with the main thread we must ensure that the MessageChannel lives
  // long enough to be cleared in this call stack.

  MOZ_ALWAYS_SUCCEEDS(
    NS_DispatchToCurrentThread(NewNonOwningRunnableMethod(this, &ParentImpl::Destroy)));
}

NS_IMPL_ISUPPORTS(ParentImpl::ShutdownObserver, nsIObserver)

NS_IMETHODIMP
ParentImpl::ShutdownObserver::Observe(nsISupports* aSubject,
                                      const char* aTopic,
                                      const char16_t* aData)
{
  AssertIsInMainProcess();
  AssertIsOnMainThread();
  MOZ_ASSERT(!sShutdownHasStarted);
  MOZ_ASSERT(!strcmp(aTopic, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID));

  sShutdownHasStarted = true;

  // Do this first before calling (and spinning the event loop in)
  // ShutdownBackgroundThread().
  ChildImpl::Shutdown();

  ShutdownBackgroundThread();

  return NS_OK;
}

NS_IMETHODIMP
ParentImpl::RequestMessageLoopRunnable::Run()
{
  AssertIsInMainProcess();
  MOZ_ASSERT(mTargetThread);

  char stackBaseGuess;

  if (NS_IsMainThread()) {
    MOZ_ASSERT(mMessageLoop);

    if (!sBackgroundThread ||
        !SameCOMIdentity(mTargetThread.get(), sBackgroundThread.get())) {
      return NS_OK;
    }

    MOZ_ASSERT(!sBackgroundThreadMessageLoop);
    sBackgroundThreadMessageLoop = mMessageLoop;

    if (sPendingCallbacks && !sPendingCallbacks->IsEmpty()) {
      nsTArray<RefPtr<CreateCallback>> callbacks;
      sPendingCallbacks->SwapElements(callbacks);

      for (uint32_t index = 0; index < callbacks.Length(); index++) {
        MOZ_ASSERT(callbacks[index]);

        nsCOMPtr<nsIRunnable> callbackRunnable =
          new CreateCallbackRunnable(callbacks[index]);
        if (NS_FAILED(NS_DispatchToCurrentThread(callbackRunnable))) {
          NS_WARNING("Failed to dispatch callback runnable!");
        }
      }
    }

    return NS_OK;
  }

  profiler_register_thread("IPDL Background", &stackBaseGuess);

#ifdef DEBUG
  {
    bool correctThread;
    MOZ_ASSERT(NS_SUCCEEDED(mTargetThread->IsOnCurrentThread(&correctThread)));
    MOZ_ASSERT(correctThread);
  }
#endif

  DebugOnly<PRThread*> oldBackgroundThread =
    sBackgroundPRThread.exchange(PR_GetCurrentThread());

  MOZ_ASSERT_IF(oldBackgroundThread,
                PR_GetCurrentThread() != oldBackgroundThread);

  MOZ_ASSERT(!mMessageLoop);

  mMessageLoop = MessageLoop::current();
  MOZ_ASSERT(mMessageLoop);

  if (NS_FAILED(NS_DispatchToMainThread(this))) {
    NS_WARNING("Failed to dispatch RequestMessageLoopRunnable to main thread!");
    return NS_ERROR_FAILURE;
  }

  return NS_OK;
}

NS_IMETHODIMP
ParentImpl::ShutdownBackgroundThreadRunnable::Run()
{
  AssertIsInMainProcess();

  // It is possible that another background thread was created while this thread
  // was shutting down. In that case we can't assert anything about
  // sBackgroundPRThread and we should not modify it here.
  sBackgroundPRThread.compareExchange(PR_GetCurrentThread(), nullptr);

  profiler_unregister_thread();

  return NS_OK;
}

NS_IMETHODIMP
ParentImpl::ForceCloseBackgroundActorsRunnable::Run()
{
  AssertIsInMainProcess();
  MOZ_ASSERT(mActorArray);

  if (NS_IsMainThread()) {
    MOZ_ASSERT(sLiveActorCount);
    sLiveActorCount--;
    return NS_OK;
  }

  AssertIsOnBackgroundThread();

  if (!mActorArray->IsEmpty()) {
    // Copy the array since calling Close() could mutate the actual array.
    nsTArray<ParentImpl*> actorsToClose(*mActorArray);

    for (uint32_t index = 0; index < actorsToClose.Length(); index++) {
      actorsToClose[index]->Close();
    }
  }

  MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));

  return NS_OK;
}

NS_IMETHODIMP
ParentImpl::CreateCallbackRunnable::Run()
{
  AssertIsInMainProcess();
  AssertIsOnMainThread();
  MOZ_ASSERT(sBackgroundThreadMessageLoop);
  MOZ_ASSERT(mCallback);

  RefPtr<CreateCallback> callback;
  mCallback.swap(callback);

  RefPtr<ParentImpl> actor = new ParentImpl();

  callback->Success(actor.forget(), sBackgroundThreadMessageLoop);

  return NS_OK;
}

NS_IMETHODIMP
ParentImpl::ConnectActorRunnable::Run()
{
  AssertIsInMainProcess();
  AssertIsOnBackgroundThread();

  // Transfer ownership to this thread. If Open() fails then we will release
  // this reference in Destroy.
  ParentImpl* actor;
  mActor.forget(&actor);

  if (!actor->Open(mTransport, mOtherPid, XRE_GetIOMessageLoop(), ParentSide)) {
    actor->Destroy();
    return NS_ERROR_FAILURE;
  }

  actor->SetLiveActorArray(mLiveActorArray);

  return NS_OK;
}

// -----------------------------------------------------------------------------
// ChildImpl Implementation
// -----------------------------------------------------------------------------

// static
void
ChildImpl::Startup()
{
  // This happens on the main thread but before XPCOM has started so we can't
  // assert that we're being called on the main thread here.

  MOZ_ASSERT(sThreadLocalIndex == kBadThreadLocalIndex,
             "BackgroundChild::Startup() called more than once!");

  PRStatus status =
    PR_NewThreadPrivateIndex(&sThreadLocalIndex, ThreadLocalDestructor);
  MOZ_RELEASE_ASSERT(status == PR_SUCCESS, "PR_NewThreadPrivateIndex failed!");

  MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex);

  nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
  MOZ_RELEASE_ASSERT(observerService);

  nsCOMPtr<nsIObserver> observer = new ShutdownObserver();

  nsresult rv =
    observerService->AddObserver(observer,
                                 NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID,
                                 false);
  MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
}

// static
void
ChildImpl::Shutdown()
{
  AssertIsOnMainThread();

  if (sShutdownHasStarted) {
    MOZ_ASSERT_IF(sThreadLocalIndex != kBadThreadLocalIndex,
                  !PR_GetThreadPrivate(sThreadLocalIndex));
    return;
  }

  sShutdownHasStarted = true;

#ifdef DEBUG
  MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex);

  auto threadLocalInfo =
    static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));

  if (threadLocalInfo) {
    MOZ_ASSERT(!threadLocalInfo->mClosed);
    threadLocalInfo->mClosed = true;
  }
#endif

  DebugOnly<PRStatus> status = PR_SetThreadPrivate(sThreadLocalIndex, nullptr);
  MOZ_ASSERT(status == PR_SUCCESS);
}

// static
PBackgroundChild*
ChildImpl::Alloc(Transport* aTransport, ProcessId aOtherPid)
{
  AssertIsInChildProcess();
  AssertIsOnMainThread();
  MOZ_ASSERT(aTransport);
  MOZ_ASSERT(sPendingTargets);
  MOZ_ASSERT(!sPendingTargets->IsEmpty());

  nsCOMPtr<nsIEventTarget> eventTarget;
  sPendingTargets->ElementAt(0).swap(eventTarget);

  sPendingTargets->RemoveElementAt(0);

  RefPtr<ChildImpl> actor = new ChildImpl();

  ChildImpl* weakActor = actor;

  nsCOMPtr<nsIRunnable> openRunnable =
    new OpenChildProcessActorRunnable(actor.forget(), aTransport,
                                      aOtherPid);
  if (NS_FAILED(eventTarget->Dispatch(openRunnable, NS_DISPATCH_NORMAL))) {
    MOZ_CRASH("Failed to dispatch OpenActorRunnable!");
  }

  // This value is only checked against null to determine success/failure, so
  // there is no need to worry about the reference count here.
  return weakActor;
}

// static
PBackgroundChild*
ChildImpl::GetForCurrentThread()
{
  MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex);

  auto threadLocalInfo =
    static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));

  if (!threadLocalInfo) {
    return nullptr;
  }

  return threadLocalInfo->mActor;
}

// static
bool
ChildImpl::GetOrCreateForCurrentThread(
                                 nsIIPCBackgroundChildCreateCallback* aCallback)
{
  MOZ_ASSERT(aCallback);
  MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex,
             "BackgroundChild::Startup() was never called!");

  bool created = false;

  auto threadLocalInfo =
    static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));

  if (threadLocalInfo) {
    threadLocalInfo->mCallbacks.AppendElement(aCallback);
  } else {
    nsAutoPtr<ThreadLocalInfo> newInfo(new ThreadLocalInfo(aCallback));

    if (PR_SetThreadPrivate(sThreadLocalIndex, newInfo) != PR_SUCCESS) {
      CRASH_IN_CHILD_PROCESS("PR_SetThreadPrivate failed!");
      return false;
    }

    created = true;
    threadLocalInfo = newInfo.forget();
  }

  if (threadLocalInfo->mActor) {
    // Runnable will use GetForCurrentThread() to retrieve actor again.  This
    // allows us to avoid addref'ing on the wrong thread.
    nsCOMPtr<nsIRunnable> runnable = new AlreadyCreatedCallbackRunnable();
    MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(runnable));

    return true;
  }

  if (!created) {
    // We have already started the sequence for opening the actor so there's
    // nothing else we need to do here. This callback will be called after the
    // first callback in the schedule runnable.
    return true;
  }

  if (NS_IsMainThread()) {
    if (NS_WARN_IF(!OpenProtocolOnMainThread(NS_GetCurrentThread()))) {
      return false;
    }

    return true;
  }

  RefPtr<CreateActorRunnable> runnable = new CreateActorRunnable();
  if (NS_FAILED(NS_DispatchToMainThread(runnable))) {
    CRASH_IN_CHILD_PROCESS("Failed to dispatch to main thread!");
    return false;
  }

  return true;
}

namespace {

class Callback final : public nsIIPCBackgroundChildCreateCallback
{
  bool* mDone;

public:
  explicit Callback(bool* aDone)
    : mDone(aDone)
  {
    MOZ_ASSERT(mDone);
  }

  NS_DECL_ISUPPORTS

private:
  ~Callback()
  { }

  virtual void
  ActorCreated(PBackgroundChild* aActor) override
  {
    *mDone = true;
  }

  virtual void
  ActorFailed() override
  {
    *mDone = true;
  }
};

NS_IMPL_ISUPPORTS(Callback, nsIIPCBackgroundChildCreateCallback)

} // anonymous namespace

/* static */
PBackgroundChild*
ChildImpl::SynchronouslyCreateForCurrentThread()
{
  MOZ_ASSERT(!GetForCurrentThread());

  bool done = false;
  nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback = new Callback(&done);

  if (NS_WARN_IF(!GetOrCreateForCurrentThread(callback))) {
    return nullptr;
  }

  nsIThread* currentThread = NS_GetCurrentThread();
  MOZ_ASSERT(currentThread);

  while (!done) {
    if (NS_WARN_IF(!NS_ProcessNextEvent(currentThread, true /* aMayWait */))) {
      return nullptr;
    }
  }

  return GetForCurrentThread();
}

// static
void
ChildImpl::CloseForCurrentThread()
{
  if (sThreadLocalIndex == kBadThreadLocalIndex) {
    return;
  }

  auto threadLocalInfo =
    static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));

  if (!threadLocalInfo) {
    return;
  }

#ifdef DEBUG
  MOZ_ASSERT(!threadLocalInfo->mClosed);
  threadLocalInfo->mClosed = true;
#endif

  // Clearing the thread local will synchronously close the actor.
  DebugOnly<PRStatus> status = PR_SetThreadPrivate(sThreadLocalIndex, nullptr);
  MOZ_ASSERT(status == PR_SUCCESS);
}

// static
BackgroundChildImpl::ThreadLocal*
ChildImpl::GetThreadLocalForCurrentThread()
{
  MOZ_ASSERT(sThreadLocalIndex != kBadThreadLocalIndex,
             "BackgroundChild::Startup() was never called!");

  auto threadLocalInfo =
    static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));

  if (!threadLocalInfo) {
    return nullptr;
  }

  if (!threadLocalInfo->mConsumerThreadLocal) {
    threadLocalInfo->mConsumerThreadLocal =
      new BackgroundChildImpl::ThreadLocal();
  }

  return threadLocalInfo->mConsumerThreadLocal;
}

// static
already_AddRefed<nsIIPCBackgroundChildCreateCallback>
ChildImpl::GetNextCallback()
{
  // May run on any thread!

  auto threadLocalInfo =
    static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));
  MOZ_ASSERT(threadLocalInfo);

  if (threadLocalInfo->mCallbacks.IsEmpty()) {
    return nullptr;
  }

  nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback;
  threadLocalInfo->mCallbacks[0].swap(callback);

  threadLocalInfo->mCallbacks.RemoveElementAt(0);

  return callback.forget();
}

NS_IMETHODIMP
ChildImpl::AlreadyCreatedCallbackRunnable::Run()
{
  // May run on any thread!

  // Report the current actor back in the callback.
  PBackgroundChild* actor = ChildImpl::GetForCurrentThread();

  // If the current actor is null, do not create a new actor here.  This likely
  // means we are in the process of cleaning up a worker thread and do not want
  // a new actor created.  Unfortunately we cannot report back to the callback
  // because the thread local is gone at this point.  Instead simply do nothing
  // and return.
  if (NS_WARN_IF(!actor)) {
    return NS_OK;
  }

  nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback =
    ChildImpl::GetNextCallback();
  while (callback) {
    callback->ActorCreated(actor);
    callback = ChildImpl::GetNextCallback();
  }

  return NS_OK;
}

nsresult
ChildImpl::AlreadyCreatedCallbackRunnable::Cancel()
{
  // These are IPC infrastructure objects and need to run unconditionally.
  Run();
  return NS_OK;
}

NS_IMETHODIMP
ChildImpl::FailedCreateCallbackRunnable::Run()
{
  // May run on any thread!

  nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback =
    ChildImpl::GetNextCallback();
  while (callback) {
    callback->ActorFailed();
    callback = ChildImpl::GetNextCallback();
  }

  return NS_OK;
}

NS_IMETHODIMP
ChildImpl::OpenChildProcessActorRunnable::Run()
{
  // May be run on any thread!

  AssertIsInChildProcess();
  MOZ_ASSERT(mActor);
  MOZ_ASSERT(mTransport);

  nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback =
    ChildImpl::GetNextCallback();
  MOZ_ASSERT(callback,
             "There should be at least one callback when first creating the "
             "actor!");

  RefPtr<ChildImpl> strongActor;
  mActor.swap(strongActor);

  if (!strongActor->Open(mTransport.forget(), mOtherPid,
                         XRE_GetIOMessageLoop(), ChildSide)) {
    CRASH_IN_CHILD_PROCESS("Failed to open ChildImpl!");

    while (callback) {
      callback->ActorFailed();
      callback = ChildImpl::GetNextCallback();
    }

    return NS_OK;
  }

  // Now that Open() has succeeded transfer the ownership of the actor to IPDL.
  auto threadLocalInfo =
    static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));

  MOZ_ASSERT(threadLocalInfo);
  MOZ_ASSERT(!threadLocalInfo->mActor);

  RefPtr<ChildImpl>& actor = threadLocalInfo->mActor;
  strongActor.swap(actor);

  actor->SetBoundThread();

  while (callback) {
    callback->ActorCreated(actor);
    callback = ChildImpl::GetNextCallback();
  }

  return NS_OK;
}

NS_IMETHODIMP
ChildImpl::OpenMainProcessActorRunnable::Run()
{
  // May run on any thread!

  AssertIsInMainProcess();
  MOZ_ASSERT(mActor);
  MOZ_ASSERT(mParentActor);
  MOZ_ASSERT(mParentMessageLoop);

  nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback =
    ChildImpl::GetNextCallback();
  MOZ_ASSERT(callback,
             "There should be at least one callback when first creating the "
             "actor!");

  RefPtr<ChildImpl> strongChildActor;
  mActor.swap(strongChildActor);

  RefPtr<ParentImpl> parentActor;
  mParentActor.swap(parentActor);

  MessageChannel* parentChannel = parentActor->GetIPCChannel();
  MOZ_ASSERT(parentChannel);

  if (!strongChildActor->Open(parentChannel, mParentMessageLoop, ChildSide)) {
    NS_WARNING("Failed to open ChildImpl!");

    parentActor->Destroy();

    while (callback) {
      callback->ActorFailed();
      callback = ChildImpl::GetNextCallback();
    }

    return NS_OK;
  }

  // Make sure the parent knows it is same process.
  parentActor->SetOtherProcessId(base::GetCurrentProcId());

  // Now that Open() has succeeded transfer the ownership of the actors to IPDL.
  Unused << parentActor.forget();

  auto threadLocalInfo =
    static_cast<ThreadLocalInfo*>(PR_GetThreadPrivate(sThreadLocalIndex));

  MOZ_ASSERT(threadLocalInfo);
  MOZ_ASSERT(!threadLocalInfo->mActor);

  RefPtr<ChildImpl>& childActor = threadLocalInfo->mActor;
  strongChildActor.swap(childActor);

  childActor->SetBoundThread();

  while (callback) {
    callback->ActorCreated(childActor);
    callback = ChildImpl::GetNextCallback();
  }

  return NS_OK;
}

NS_IMETHODIMP
ChildImpl::CreateActorRunnable::Run()
{
  AssertIsOnMainThread();

  if (!OpenProtocolOnMainThread(mEventTarget)) {
    NS_WARNING("OpenProtocolOnMainThread failed!");
    return NS_ERROR_FAILURE;
  }

  return NS_OK;
}

void
ChildImpl::ParentCreateCallback::Success(
                                      already_AddRefed<ParentImpl> aParentActor,
                                      MessageLoop* aParentMessageLoop)
{
  AssertIsInMainProcess();
  AssertIsOnMainThread();

  RefPtr<ParentImpl> parentActor = aParentActor;
  MOZ_ASSERT(parentActor);
  MOZ_ASSERT(aParentMessageLoop);
  MOZ_ASSERT(mEventTarget);

  RefPtr<ChildImpl> childActor = new ChildImpl();

  nsCOMPtr<nsIEventTarget> target;
  mEventTarget.swap(target);

  nsCOMPtr<nsIRunnable> openRunnable =
    new OpenMainProcessActorRunnable(childActor.forget(), parentActor.forget(),
                                     aParentMessageLoop);
  if (NS_FAILED(target->Dispatch(openRunnable, NS_DISPATCH_NORMAL))) {
    NS_WARNING("Failed to dispatch open runnable!");
  }
}

void
ChildImpl::ParentCreateCallback::Failure()
{
  AssertIsInMainProcess();
  AssertIsOnMainThread();
  MOZ_ASSERT(mEventTarget);

  nsCOMPtr<nsIEventTarget> target;
  mEventTarget.swap(target);

  DispatchFailureCallback(target);
}

// static
bool
ChildImpl::OpenProtocolOnMainThread(nsIEventTarget* aEventTarget)
{
  AssertIsOnMainThread();
  MOZ_ASSERT(aEventTarget);

  if (sShutdownHasStarted) {
    MOZ_CRASH("Called BackgroundChild::GetOrCreateForCurrentThread after "
              "shutdown has started!");
  }

  if (XRE_IsParentProcess()) {
    RefPtr<ParentImpl::CreateCallback> parentCallback =
      new ParentCreateCallback(aEventTarget);

    if (!ParentImpl::CreateActorForSameProcess(parentCallback)) {
      NS_WARNING("BackgroundParent::CreateActor() failed!");
      DispatchFailureCallback(aEventTarget);
      return false;
    }

    return true;
  }

  ContentChild* content = ContentChild::GetSingleton();
  MOZ_ASSERT(content);

  if (content->IsShuttingDown()) {
    // The transport for ContentChild is shut down and can't be used to open
    // PBackground.
    DispatchFailureCallback(aEventTarget);
    return false;
  }

  if (!PBackground::Open(content)) {
    MOZ_CRASH("Failed to create top level actor!");
    return false;
  }

  if (!sPendingTargets) {
    sPendingTargets = new nsTArray<nsCOMPtr<nsIEventTarget>>(1);
    ClearOnShutdown(&sPendingTargets);
  }

  sPendingTargets->AppendElement(aEventTarget);

  return true;
}

// static
void
ChildImpl::DispatchFailureCallback(nsIEventTarget* aEventTarget)
{
  MOZ_ASSERT(aEventTarget);

  nsCOMPtr<nsIRunnable> callbackRunnable = new FailedCreateCallbackRunnable();
  if (NS_FAILED(aEventTarget->Dispatch(callbackRunnable, NS_DISPATCH_NORMAL))) {
    NS_WARNING("Failed to dispatch CreateCallbackRunnable!");
  }
}

void
ChildImpl::ActorDestroy(ActorDestroyReason aWhy)
{
  AssertIsOnBoundThread();

#ifdef DEBUG
  MOZ_ASSERT(!mActorDestroyed);
  mActorDestroyed = true;
#endif

  BackgroundChildImpl::ActorDestroy(aWhy);
}

NS_IMPL_ISUPPORTS(ChildImpl::ShutdownObserver, nsIObserver)

NS_IMETHODIMP
ChildImpl::ShutdownObserver::Observe(nsISupports* aSubject,
                                     const char* aTopic,
                                     const char16_t* aData)
{
  AssertIsOnMainThread();
  MOZ_ASSERT(!strcmp(aTopic, NS_XPCOM_SHUTDOWN_THREADS_OBSERVER_ID));

  ChildImpl::Shutdown();

  return NS_OK;
}