/* -*- 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 "mozilla/DebugOnly.h"

#include "base/basictypes.h"

#include "ContentParent.h"
#include "TabParent.h"

#if defined(ANDROID) || defined(LINUX)
# include <sys/time.h>
# include <sys/resource.h>
#endif

#ifdef MOZ_WIDGET_GONK
#include <sys/types.h>
#include <sys/wait.h>
#endif

#include "chrome/common/process_watcher.h"

#include "mozilla/a11y/PDocAccessible.h"
#include "AppProcessChecker.h"
#include "AudioChannelService.h"
#include "BlobParent.h"
#include "CrashReporterParent.h"
#include "GMPServiceParent.h"
#include "HandlerServiceParent.h"
#include "IHistory.h"
#include "imgIContainer.h"
#include "mozIApplication.h"
#if defined(XP_WIN) && defined(ACCESSIBILITY)
#include "mozilla/a11y/AccessibleWrap.h"
#endif
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/StyleSheetInlines.h"
#include "mozilla/DataStorage.h"
#include "mozilla/devtools/HeapSnapshotTempFileHelperParent.h"
#include "mozilla/docshell/OfflineCacheUpdateParent.h"
#include "mozilla/dom/DataTransfer.h"
#include "mozilla/dom/DOMStorageIPC.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/FileSystemSecurity.h"
#include "mozilla/dom/ExternalHelperAppParent.h"
#include "mozilla/dom/GetFilesHelper.h"
#include "mozilla/dom/GeolocationBinding.h"
#include "mozilla/dom/Notification.h"
#include "mozilla/dom/PContentBridgeParent.h"
#include "mozilla/dom/PContentPermissionRequestParent.h"
#include "mozilla/dom/PCycleCollectWithLogsParent.h"
#include "mozilla/dom/PMemoryReportRequestParent.h"
#include "mozilla/dom/ServiceWorkerRegistrar.h"
#include "mozilla/dom/power/PowerManagerService.h"
#include "mozilla/dom/Permissions.h"
#include "mozilla/dom/PresentationParent.h"
#include "mozilla/dom/PPresentationParent.h"
#include "mozilla/dom/PushNotifier.h"
#include "mozilla/dom/FlyWebPublishedServerIPC.h"
#include "mozilla/dom/quota/QuotaManagerService.h"
#include "mozilla/dom/time/DateCacheCleaner.h"
#include "mozilla/embedding/printingui/PrintingParent.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/gfx/GPUProcessManager.h"
#include "mozilla/hal_sandbox/PHalParent.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/BackgroundParent.h"
#include "mozilla/ipc/FileDescriptorUtils.h"
#include "mozilla/ipc/PSendStreamParent.h"
#include "mozilla/ipc/TestShellParent.h"
#include "mozilla/ipc/InputStreamUtils.h"
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
#include "mozilla/layers/PAPZParent.h"
#include "mozilla/layers/CompositorThread.h"
#include "mozilla/layers/ImageBridgeParent.h"
#include "mozilla/layers/LayerTreeOwnerTracker.h"
#include "mozilla/layout/RenderFrameParent.h"
#include "mozilla/LookAndFeel.h"
#include "mozilla/media/MediaParent.h"
#include "mozilla/Move.h"
#include "mozilla/net/NeckoParent.h"
#include "mozilla/plugins/PluginBridge.h"
#include "mozilla/Preferences.h"
#include "mozilla/ProcessHangMonitor.h"
#include "mozilla/ProcessHangMonitorIPC.h"
#ifdef MOZ_ENABLE_PROFILER_SPS
#include "mozilla/ProfileGatherer.h"
#endif
#include "mozilla/ScopeExit.h"
#include "mozilla/Services.h"
#include "mozilla/StaticPtr.h"
#include "mozilla/Telemetry.h"
#include "mozilla/WebBrowserPersistDocumentParent.h"
#include "mozilla/Unused.h"
#include "nsAnonymousTemporaryFile.h"
#include "nsAppRunner.h"
#include "nsCDefaultURIFixup.h"
#include "nsCExternalHandlerService.h"
#include "nsCOMPtr.h"
#include "nsChromeRegistryChrome.h"
#include "nsConsoleMessage.h"
#include "nsConsoleService.h"
#include "nsContentUtils.h"
#include "nsDebugImpl.h"
#include "nsFrameMessageManager.h"
#include "nsHashPropertyBag.h"
#include "nsIAlertsService.h"
#include "nsIAppsService.h"
#include "nsIClipboard.h"
#include "nsContentPermissionHelper.h"
#include "nsICycleCollectorListener.h"
#include "nsIDocShellTreeOwner.h"
#include "nsIDocument.h"
#include "nsIDOMGeoGeolocation.h"
#include "nsIDOMGeoPositionError.h"
#include "nsIDragService.h"
#include "mozilla/dom/WakeLock.h"
#include "nsIDOMWindow.h"
#include "nsIExternalProtocolService.h"
#include "nsIFormProcessor.h"
#include "nsIGfxInfo.h"
#include "nsIIdleService.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsIMemoryInfoDumper.h"
#include "nsIMemoryReporter.h"
#include "nsIMozBrowserFrame.h"
#include "nsIMutable.h"
#include "nsINSSU2FToken.h"
#include "nsIObserverService.h"
#include "nsIParentChannel.h"
#include "nsIPresShell.h"
#include "nsIRemoteWindowContext.h"
#include "nsIScriptError.h"
#include "nsIScriptSecurityManager.h"
#include "nsISiteSecurityService.h"
#include "nsISpellChecker.h"
#include "nsISupportsPrimitives.h"
#include "nsITimer.h"
#include "nsIURIFixup.h"
#include "nsIDocShellTreeOwner.h"
#include "nsIXULWindow.h"
#include "nsIDOMChromeWindow.h"
#include "nsIWindowWatcher.h"
#include "nsPIWindowWatcher.h"
#include "nsWindowWatcher.h"
#include "nsIXULRuntime.h"
#include "mozilla/dom/nsMixedContentBlocker.h"
#include "nsMemoryInfoDumper.h"
#include "nsMemoryReporterManager.h"
#include "nsServiceManagerUtils.h"
#include "nsStyleSheetService.h"
#include "nsThreadUtils.h"
#include "nsToolkitCompsCID.h"
#include "nsWidgetsCID.h"
#include "PreallocatedProcessManager.h"
#include "ProcessPriorityManager.h"
#include "SandboxHal.h"
#include "ScreenManagerParent.h"
#include "SourceSurfaceRawData.h"
#include "TabParent.h"
#include "URIUtils.h"
#include "nsIWebBrowserChrome.h"
#include "nsIDocShell.h"
#include "nsDocShell.h"
#include "nsOpenURIInFrameParams.h"
#include "mozilla/net/NeckoMessageUtils.h"
#include "gfxPrefs.h"
#include "prio.h"
#include "private/pprio.h"
#include "ContentProcessManager.h"
#include "mozilla/dom/ipc/StructuredCloneData.h"
#include "mozilla/psm/PSMContentListener.h"
#include "nsPluginHost.h"
#include "nsPluginTags.h"
#include "nsIBlocklistService.h"
#include "mozilla/StyleSheet.h"
#include "mozilla/StyleSheetInlines.h"
#include "nsHostObjectProtocolHandler.h"
#include "nsICaptivePortalService.h"

#include "nsIBidiKeyboard.h"

#include "nsLayoutStylesheetCache.h"

#ifdef MOZ_WEBRTC
#include "signaling/src/peerconnection/WebrtcGlobalParent.h"
#endif

#if defined(ANDROID) || defined(LINUX)
#include "nsSystemInfo.h"
#endif

#if defined(XP_LINUX)
#include "mozilla/Hal.h"
#endif

#ifdef ANDROID
# include "gfxAndroidPlatform.h"
#endif

#ifdef MOZ_PERMISSIONS
# include "nsPermissionManager.h"
#endif

#ifdef MOZ_WIDGET_ANDROID
# include "AndroidBridge.h"
#endif

#ifdef MOZ_WIDGET_GONK
#include "nsIVolume.h"
#include "nsVolumeService.h"
#include "nsIVolumeService.h"
#include "SpeakerManagerService.h"
using namespace mozilla::system;
#endif

#ifdef MOZ_WIDGET_GTK
#include <gdk/gdk.h>
#endif

#include "mozilla/RemoteSpellCheckEngineParent.h"

#include "Crypto.h"

#ifdef MOZ_WEBSPEECH
#include "mozilla/dom/SpeechSynthesisParent.h"
#endif

#ifdef MOZ_TOOLKIT_SEARCH
#include "nsIBrowserSearchService.h"
#endif

#ifdef MOZ_ENABLE_PROFILER_SPS
#include "nsIProfiler.h"
#include "nsIProfileSaveEvent.h"
#endif

#ifdef XP_WIN
#include "mozilla/widget/AudioSession.h"
#endif

#ifdef ACCESSIBILITY
#include "nsAccessibilityService.h"
#endif

// For VP9Benchmark::sBenchmarkFpsPref
#include "Benchmark.h"

static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);

#if defined(XP_WIN)
// e10s forced enable pref, defined in nsAppRunner.cpp
extern const char* kForceEnableE10sPref;
#endif

using base::ChildPrivileges;
using base::KillProcess;
#ifdef MOZ_ENABLE_PROFILER_SPS
using mozilla::ProfileGatherer;
#endif

using namespace mozilla::dom::power;
using namespace mozilla::media;
using namespace mozilla::embedding;
using namespace mozilla::gfx;
using namespace mozilla::gmp;
using namespace mozilla::hal;
using namespace mozilla::ipc;
using namespace mozilla::layers;
using namespace mozilla::layout;
using namespace mozilla::net;
using namespace mozilla::jsipc;
using namespace mozilla::psm;
using namespace mozilla::widget;

// XXX Workaround for bug 986973 to maintain the existing broken semantics
template<>
struct nsIConsoleService::COMTypeInfo<nsConsoleService, void> {
  static const nsIID kIID;
};
const nsIID nsIConsoleService::COMTypeInfo<nsConsoleService, void>::kIID = NS_ICONSOLESERVICE_IID;

namespace mozilla {
namespace dom {

#define NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC "ipc:network:set-offline"
#define NS_IPC_IOSERVICE_SET_CONNECTIVITY_TOPIC "ipc:network:set-connectivity"

class MemoryReportRequestParent : public PMemoryReportRequestParent
{
public:
  explicit MemoryReportRequestParent(uint32_t aGeneration);

  virtual ~MemoryReportRequestParent();

  virtual void ActorDestroy(ActorDestroyReason aWhy) override;

  virtual bool RecvReport(const MemoryReport& aReport) override;
  virtual bool Recv__delete__() override;

private:
  const uint32_t mGeneration;
  // Non-null if we haven't yet called EndProcessReport() on it.
  RefPtr<nsMemoryReporterManager> mReporterManager;

  ContentParent* Owner()
  {
    return static_cast<ContentParent*>(Manager());
  }
};

MemoryReportRequestParent::MemoryReportRequestParent(uint32_t aGeneration)
  : mGeneration(aGeneration)
{
  MOZ_COUNT_CTOR(MemoryReportRequestParent);
  mReporterManager = nsMemoryReporterManager::GetOrCreate();
  NS_WARNING_ASSERTION(mReporterManager, "GetOrCreate failed");
}

bool
MemoryReportRequestParent::RecvReport(const MemoryReport& aReport)
{
  if (mReporterManager) {
    mReporterManager->HandleChildReport(mGeneration, aReport);
  }
  return true;
}

bool
MemoryReportRequestParent::Recv__delete__()
{
  // Notifying the reporter manager is done in ActorDestroy, because
  // it needs to happen even if the child process exits mid-report.
  // (The reporter manager will time out eventually, but let's avoid
  // that if possible.)
  return true;
}

void
MemoryReportRequestParent::ActorDestroy(ActorDestroyReason aWhy)
{
  if (mReporterManager) {
    mReporterManager->EndProcessReport(mGeneration, aWhy == Deletion);
    mReporterManager = nullptr;
  }
}

MemoryReportRequestParent::~MemoryReportRequestParent()
{
  MOZ_ASSERT(!mReporterManager);
  MOZ_COUNT_DTOR(MemoryReportRequestParent);
}

// IPC receiver for remote GC/CC logging.
class CycleCollectWithLogsParent final : public PCycleCollectWithLogsParent
{
public:
  ~CycleCollectWithLogsParent()
  {
    MOZ_COUNT_DTOR(CycleCollectWithLogsParent);
  }

  static bool AllocAndSendConstructor(ContentParent* aManager,
                                      bool aDumpAllTraces,
                                      nsICycleCollectorLogSink* aSink,
                                      nsIDumpGCAndCCLogsCallback* aCallback)
  {
    CycleCollectWithLogsParent *actor;
    FILE* gcLog;
    FILE* ccLog;
    nsresult rv;

    actor = new CycleCollectWithLogsParent(aSink, aCallback);
    rv = actor->mSink->Open(&gcLog, &ccLog);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      delete actor;
      return false;
    }

    return aManager->
      SendPCycleCollectWithLogsConstructor(actor,
                                           aDumpAllTraces,
                                           FILEToFileDescriptor(gcLog),
                                           FILEToFileDescriptor(ccLog));
  }

private:
  virtual bool RecvCloseGCLog() override
  {
    Unused << mSink->CloseGCLog();
    return true;
  }

  virtual bool RecvCloseCCLog() override
  {
    Unused << mSink->CloseCCLog();
    return true;
  }

  virtual bool Recv__delete__() override
  {
    // Report completion to mCallback only on successful
    // completion of the protocol.
    nsCOMPtr<nsIFile> gcLog, ccLog;
    mSink->GetGcLog(getter_AddRefs(gcLog));
    mSink->GetCcLog(getter_AddRefs(ccLog));
    Unused << mCallback->OnDump(gcLog, ccLog, /* parent = */ false);
    return true;
  }

  virtual void ActorDestroy(ActorDestroyReason aReason) override
  {
    // If the actor is unexpectedly destroyed, we deliberately
    // don't call Close[GC]CLog on the sink, because the logs may
    // be incomplete.  See also the nsCycleCollectorLogSinkToFile
    // implementaiton of those methods, and its destructor.
  }

  CycleCollectWithLogsParent(nsICycleCollectorLogSink *aSink,
                             nsIDumpGCAndCCLogsCallback *aCallback)
    : mSink(aSink), mCallback(aCallback)
  {
    MOZ_COUNT_CTOR(CycleCollectWithLogsParent);
  }

  nsCOMPtr<nsICycleCollectorLogSink> mSink;
  nsCOMPtr<nsIDumpGCAndCCLogsCallback> mCallback;
};

// A memory reporter for ContentParent objects themselves.
class ContentParentsMemoryReporter final : public nsIMemoryReporter
{
  ~ContentParentsMemoryReporter() {}
public:
  NS_DECL_ISUPPORTS
  NS_DECL_NSIMEMORYREPORTER
};

NS_IMPL_ISUPPORTS(ContentParentsMemoryReporter, nsIMemoryReporter)

NS_IMETHODIMP
ContentParentsMemoryReporter::CollectReports(
  nsIHandleReportCallback* aHandleReport,
  nsISupports* aData,
  bool aAnonymize)
{
  AutoTArray<ContentParent*, 16> cps;
  ContentParent::GetAllEvenIfDead(cps);

  for (uint32_t i = 0; i < cps.Length(); i++) {
    ContentParent* cp = cps[i];
    MessageChannel* channel = cp->GetIPCChannel();

    nsString friendlyName;
    cp->FriendlyName(friendlyName, aAnonymize);

    cp->AddRef();
    nsrefcnt refcnt = cp->Release();

    const char* channelStr = "no channel";
    uint32_t numQueuedMessages = 0;
    if (channel) {
      if (channel->Unsound_IsClosed()) {
        channelStr = "closed channel";
      } else {
        channelStr = "open channel";
      }
      numQueuedMessages = channel->Unsound_NumQueuedMessages();
    }

    nsPrintfCString path("queued-ipc-messages/content-parent"
                         "(%s, pid=%d, %s, 0x%p, refcnt=%d)",
                         NS_ConvertUTF16toUTF8(friendlyName).get(),
                         cp->Pid(), channelStr,
                         static_cast<nsIContentParent*>(cp), refcnt);

    NS_NAMED_LITERAL_CSTRING(desc,
      "The number of unset IPC messages held in this ContentParent's "
      "channel.  A large value here might indicate that we're leaking "
      "messages.  Similarly, a ContentParent object for a process that's no "
      "longer running could indicate that we're leaking ContentParents.");

    aHandleReport->Callback(/* process */ EmptyCString(), path,
                            KIND_OTHER, UNITS_COUNT,
                            numQueuedMessages, desc, aData);
  }

  return NS_OK;
}

nsDataHashtable<nsStringHashKey, ContentParent*>* ContentParent::sAppContentParents;
nsTArray<ContentParent*>* ContentParent::sNonAppContentParents;
nsTArray<ContentParent*>* ContentParent::sLargeAllocationContentParents;
nsTArray<ContentParent*>* ContentParent::sPrivateContent;
StaticAutoPtr<LinkedList<ContentParent> > ContentParent::sContentParents;

// This is true when subprocess launching is enabled.  This is the
// case between StartUp() and ShutDown() or JoinAllSubprocesses().
static bool sCanLaunchSubprocesses;

// Set to true if the DISABLE_UNSAFE_CPOW_WARNINGS environment variable is
// set.
static bool sDisableUnsafeCPOWWarnings = false;

// The first content child has ID 1, so the chrome process can have ID 0.
static uint64_t gContentChildID = 1;

// We want the prelaunched process to know that it's for apps, but not
// actually for any app in particular.  Use a magic manifest URL.
// Can't be a static constant.
#define MAGIC_PREALLOCATED_APP_MANIFEST_URL NS_LITERAL_STRING("{{template}}")

static const char* sObserverTopics[] = {
  "xpcom-shutdown",
  "profile-before-change",
  NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC,
  NS_IPC_IOSERVICE_SET_CONNECTIVITY_TOPIC,
  NS_IPC_CAPTIVE_PORTAL_SET_STATE,
  "memory-pressure",
  "child-gc-request",
  "child-cc-request",
  "child-mmu-request",
  "last-pb-context-exited",
  "file-watcher-update",
#ifdef MOZ_WIDGET_GONK
  NS_VOLUME_STATE_CHANGED,
  NS_VOLUME_REMOVED,
  "phone-state-changed",
#endif
#ifdef ACCESSIBILITY
  "a11y-init-or-shutdown",
#endif
#ifdef MOZ_ENABLE_PROFILER_SPS
  "profiler-started",
  "profiler-stopped",
  "profiler-paused",
  "profiler-resumed",
  "profiler-subprocess-gather",
  "profiler-subprocess",
#endif
  "cacheservice:empty-cache",
};

// PreallocateAppProcess is called by the PreallocatedProcessManager.
// ContentParent then takes this process back within
// GetNewOrPreallocatedAppProcess.
/*static*/ already_AddRefed<ContentParent>
ContentParent::PreallocateAppProcess()
{
  RefPtr<ContentParent> process =
    new ContentParent(/* app = */ nullptr,
                      /* aOpener = */ nullptr,
                      /* isForBrowserElement = */ false,
                      /* isForPreallocated = */ true);

  if (!process->LaunchSubprocess(PROCESS_PRIORITY_PREALLOC)) {
    return nullptr;
  }

  process->Init();
  return process.forget();
}

/*static*/ already_AddRefed<ContentParent>
ContentParent::GetNewOrPreallocatedAppProcess(mozIApplication* aApp,
                                              ProcessPriority aInitialPriority,
                                              ContentParent* aOpener,
                                              /*out*/ bool* aTookPreAllocated)
{
  MOZ_ASSERT(aApp);
  RefPtr<ContentParent> process = PreallocatedProcessManager::Take();

  if (process) {
    if (!process->SetPriorityAndCheckIsAlive(aInitialPriority)) {
      // Kill the process just in case it's not actually dead; we don't want
      // to "leak" this process!
      process->KillHard("GetNewOrPreallocatedAppProcess");
    }
    else {
      nsAutoString manifestURL;
      if (NS_FAILED(aApp->GetManifestURL(manifestURL))) {
        NS_ERROR("Failed to get manifest URL");
        return nullptr;
      }
      process->TransformPreallocatedIntoApp(aOpener, manifestURL);
      process->ForwardKnownInfo();

      if (aTookPreAllocated) {
        *aTookPreAllocated = true;
      }
      return process.forget();
    }
  }

  NS_WARNING("Unable to use pre-allocated app process");
  process = new ContentParent(aApp,
                              /* aOpener = */ aOpener,
                              /* isForBrowserElement = */ false,
                              /* isForPreallocated = */ false);

  if (!process->LaunchSubprocess(aInitialPriority)) {
    return nullptr;
  }

  process->Init();
  process->ForwardKnownInfo();

  if (aTookPreAllocated) {
    *aTookPreAllocated = false;
  }

  return process.forget();
}

/*static*/ void
ContentParent::StartUp()
{
  // We could launch sub processes from content process
  // FIXME Bug 1023701 - Stop using ContentParent static methods in
  // child process
  sCanLaunchSubprocesses = true;

  if (!XRE_IsParentProcess()) {
    return;
  }

  // Note: This reporter measures all ContentParents.
  RegisterStrongMemoryReporter(new ContentParentsMemoryReporter());

  mozilla::dom::time::InitializeDateCacheCleaner();

  BlobParent::Startup(BlobParent::FriendKey());

  BackgroundChild::Startup();

  // Try to preallocate a process that we can transform into an app later.
  PreallocatedProcessManager::AllocateAfterDelay();

  sDisableUnsafeCPOWWarnings = PR_GetEnv("DISABLE_UNSAFE_CPOW_WARNINGS");
}

/*static*/ void
ContentParent::ShutDown()
{
  // No-op for now.  We rely on normal process shutdown and
  // ClearOnShutdown() to clean up our state.
  sCanLaunchSubprocesses = false;
}

/*static*/ void
ContentParent::JoinProcessesIOThread(const nsTArray<ContentParent*>* aProcesses,
                                     Monitor* aMonitor, bool* aDone)
{
  const nsTArray<ContentParent*>& processes = *aProcesses;
  for (uint32_t i = 0; i < processes.Length(); ++i) {
    if (GeckoChildProcessHost* process = processes[i]->mSubprocess) {
      process->Join();
    }
  }
  {
    MonitorAutoLock lock(*aMonitor);
    *aDone = true;
    lock.Notify();
  }
  // Don't touch any arguments to this function from now on.
}

/*static*/ void
ContentParent::JoinAllSubprocesses()
{
  MOZ_ASSERT(NS_IsMainThread());

  AutoTArray<ContentParent*, 8> processes;
  GetAll(processes);
  if (processes.IsEmpty()) {
    printf_stderr("There are no live subprocesses.");
    return;
  }

  printf_stderr("Subprocesses are still alive.  Doing emergency join.\n");

  bool done = false;
  Monitor monitor("mozilla.dom.ContentParent.JoinAllSubprocesses");
  XRE_GetIOMessageLoop()->PostTask(NewRunnableFunction(
                                     &ContentParent::JoinProcessesIOThread,
                                     &processes, &monitor, &done));
  {
    MonitorAutoLock lock(monitor);
    while (!done) {
      lock.Wait();
    }
  }

  sCanLaunchSubprocesses = false;
}

/*static*/ already_AddRefed<ContentParent>
ContentParent::GetNewOrUsedBrowserProcess(bool aForBrowserElement,
                                          ProcessPriority aPriority,
                                          ContentParent* aOpener,
                                          bool aLargeAllocationProcess)
{
  nsTArray<ContentParent*>* contentParents;
  int32_t maxContentParents;

  // Decide which pool of content parents we are going to be pulling from based
  // on the aLargeAllocationProcess flag.
  if (aLargeAllocationProcess) {
    if (!sLargeAllocationContentParents) {
      sLargeAllocationContentParents = new nsTArray<ContentParent*>();
    }
    contentParents = sLargeAllocationContentParents;

    maxContentParents = Preferences::GetInt("dom.ipc.dedicatedProcessCount", 2);
  } else {
    if (!sNonAppContentParents) {
      sNonAppContentParents = new nsTArray<ContentParent*>();
    }
    contentParents = sNonAppContentParents;

    maxContentParents = Preferences::GetInt("dom.ipc.processCount", 1);
  }

  if (maxContentParents < 1) {
    maxContentParents = 1;
  }

  if (contentParents->Length() >= uint32_t(maxContentParents)) {
    uint32_t maxSelectable = std::min(static_cast<uint32_t>(contentParents->Length()),
                                      static_cast<uint32_t>(maxContentParents));
    uint32_t startIdx = rand() % maxSelectable;
    uint32_t currIdx = startIdx;
    do {
      RefPtr<ContentParent> p = (*contentParents)[currIdx];
      NS_ASSERTION(p->IsAlive(), "Non-alive contentparent in sNonAppContntParents?");
      if (p->mOpener == aOpener) {
        return p.forget();
      }
      currIdx = (currIdx + 1) % maxSelectable;
    } while (currIdx != startIdx);
  }

  // Try to take and transform the preallocated process into browser.
  RefPtr<ContentParent> p = PreallocatedProcessManager::Take();
  if (p) {
    p->TransformPreallocatedIntoBrowser(aOpener);
  } else {
    // Failed in using the preallocated process: fork from the chrome process.
    p = new ContentParent(/* app = */ nullptr,
                          aOpener,
                          aForBrowserElement,
                          /* isForPreallocated = */ false);

    if (!p->LaunchSubprocess(aPriority)) {
      return nullptr;
    }

    p->Init();
  }

  p->mLargeAllocationProcess = aLargeAllocationProcess;

  p->ForwardKnownInfo();

  contentParents->AppendElement(p);
  return p.forget();
}

/*static*/ ProcessPriority
ContentParent::GetInitialProcessPriority(Element* aFrameElement)
{
  // Frames with mozapptype == critical which are expecting a system message
  // get FOREGROUND_HIGH priority.

  if (!aFrameElement) {
    return PROCESS_PRIORITY_FOREGROUND;
  }

  if (aFrameElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::mozapptype,
                                 NS_LITERAL_STRING("inputmethod"), eCaseMatters)) {
    return PROCESS_PRIORITY_FOREGROUND_KEYBOARD;
  } else if (!aFrameElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::mozapptype,
                                        NS_LITERAL_STRING("critical"), eCaseMatters)) {
    return PROCESS_PRIORITY_FOREGROUND;
  }

  nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(aFrameElement);
  if (!browserFrame) {
    return PROCESS_PRIORITY_FOREGROUND;
  }

  return PROCESS_PRIORITY_FOREGROUND;
}

#if defined(XP_WIN)
extern const wchar_t* kPluginWidgetContentParentProperty;

/*static*/ void
ContentParent::SendAsyncUpdate(nsIWidget* aWidget)
{
  if (!aWidget || aWidget->Destroyed()) {
    return;
  }
  // Fire off an async request to the plugin to paint its window
  HWND hwnd = (HWND)aWidget->GetNativeData(NS_NATIVE_WINDOW);
  NS_ASSERTION(hwnd, "Expected valid hwnd value.");
  ContentParent* cp = reinterpret_cast<ContentParent*>(
    ::GetPropW(hwnd, kPluginWidgetContentParentProperty));
  if (cp && !cp->IsDestroyed()) {
    Unused << cp->SendUpdateWindow((uintptr_t)hwnd);
  }
}
#endif // defined(XP_WIN)

bool
ContentParent::PreallocatedProcessReady()
{
  return true;
}

bool
ContentParent::RecvCreateChildProcess(const IPCTabContext& aContext,
                                      const hal::ProcessPriority& aPriority,
                                      const TabId& aOpenerTabId,
                                      ContentParentId* aCpId,
                                      bool* aIsForApp,
                                      bool* aIsForBrowser,
                                      TabId* aTabId)
{
#if 0
  if (!CanOpenBrowser(aContext)) {
      return false;
  }
#endif
  RefPtr<ContentParent> cp;
  MaybeInvalidTabContext tc(aContext);
  if (!tc.IsValid()) {
    NS_ERROR(nsPrintfCString("Received an invalid TabContext from "
                             "the child process. (%s)",
                             tc.GetInvalidReason()).get());
    return false;
  }

  nsCOMPtr<mozIApplication> ownApp = tc.GetTabContext().GetOwnApp();
  if (ownApp) {
    cp = GetNewOrPreallocatedAppProcess(ownApp, aPriority, this);
  }
  else {
   cp = GetNewOrUsedBrowserProcess(/* isBrowserElement = */ true,
                                   aPriority, this);
  }

  if (!cp) {
    *aCpId = 0;
    *aIsForApp = false;
    *aIsForBrowser = false;
    return true;
  }

  *aCpId = cp->ChildID();
  *aIsForApp = cp->IsForApp();
  *aIsForBrowser = cp->IsForBrowser();

  ContentProcessManager *cpm = ContentProcessManager::GetSingleton();
  cpm->AddContentProcess(cp, this->ChildID());

  if (cpm->AddGrandchildProcess(this->ChildID(), cp->ChildID())) {
    // Pre-allocate a TabId here to save one time IPC call at app startup.
    *aTabId = AllocateTabId(aOpenerTabId, aContext, cp->ChildID());
    return (*aTabId != 0);
  }

  return false;
}

bool
ContentParent::RecvBridgeToChildProcess(const ContentParentId& aCpId)
{
  ContentProcessManager *cpm = ContentProcessManager::GetSingleton();
  ContentParent* cp = cpm->GetContentProcessById(aCpId);

  if (cp) {
    ContentParentId parentId;
    if (cpm->GetParentProcessId(cp->ChildID(), &parentId) &&
      parentId == this->ChildID()) {
      return NS_SUCCEEDED(PContentBridge::Bridge(this, cp));
    }
  }

  // You can't bridge to a process you didn't open!
  KillHard("BridgeToChildProcess");
  return false;
}

static nsIDocShell* GetOpenerDocShellHelper(Element* aFrameElement)
{
  // Propagate the private-browsing status of the element's parent
  // docshell to the remote docshell, via the chrome flags.
  nsCOMPtr<Element> frameElement = do_QueryInterface(aFrameElement);
  MOZ_ASSERT(frameElement);
  nsPIDOMWindowOuter* win = frameElement->OwnerDoc()->GetWindow();
  if (!win) {
    NS_WARNING("Remote frame has no window");
    return nullptr;
  }
  nsIDocShell* docShell = win->GetDocShell();
  if (!docShell) {
    NS_WARNING("Remote frame has no docshell");
    return nullptr;
  }

  return docShell;
}

bool
ContentParent::RecvCreateGMPService()
{
  return PGMPService::Open(this);
}

bool
ContentParent::RecvLoadPlugin(const uint32_t& aPluginId, nsresult* aRv, uint32_t* aRunID)
{
  *aRv = NS_OK;
  return mozilla::plugins::SetupBridge(aPluginId, this, false, aRv, aRunID);
}

bool
ContentParent::RecvUngrabPointer(const uint32_t& aTime)
{
#if !defined(MOZ_WIDGET_GTK)
  NS_RUNTIMEABORT("This message only makes sense on GTK platforms");
  return false;
#else
  gdk_pointer_ungrab(aTime);
  return true;
#endif
}

bool
ContentParent::RecvRemovePermission(const IPC::Principal& aPrincipal,
                                    const nsCString& aPermissionType,
                                    nsresult* aRv) {
  *aRv = Permissions::RemovePermission(aPrincipal, aPermissionType.get());
  return true;
}

bool
ContentParent::RecvConnectPluginBridge(const uint32_t& aPluginId, nsresult* aRv)
{
  *aRv = NS_OK;
  // We don't need to get the run ID for the plugin, since we already got it
  // in the first call to SetupBridge in RecvLoadPlugin, so we pass in a dummy
  // pointer and just throw it away.
  uint32_t dummy = 0;
  return mozilla::plugins::SetupBridge(aPluginId, this, true, aRv, &dummy);
}

bool
ContentParent::RecvGetBlocklistState(const uint32_t& aPluginId,
                                     uint32_t* aState)
{
  *aState = nsIBlocklistService::STATE_BLOCKED;

  RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
  if (!pluginHost) {
    NS_WARNING("Plugin host not found");
    return false;
  }
  nsPluginTag* tag =  pluginHost->PluginWithId(aPluginId);

  if (!tag) {
    // Default state is blocked anyway
    NS_WARNING("Plugin tag not found. This should never happen, but to avoid a crash we're forcibly blocking it");
    return true;
  }

  return NS_SUCCEEDED(tag->GetBlocklistState(aState));
}

bool
ContentParent::RecvFindPlugins(const uint32_t& aPluginEpoch,
                               nsresult* aRv,
                               nsTArray<PluginTag>* aPlugins,
                               uint32_t* aNewPluginEpoch)
{
  *aRv = mozilla::plugins::FindPluginsForContent(aPluginEpoch, aPlugins, aNewPluginEpoch);
  return true;
}

/*static*/ TabParent*
ContentParent::CreateBrowserOrApp(const TabContext& aContext,
                                  Element* aFrameElement,
                                  ContentParent* aOpenerContentParent,
                                  bool aFreshProcess)
{
  PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);

  if (!sCanLaunchSubprocesses) {
    return nullptr;
  }

  if (TabParent* parent = TabParent::GetNextTabParent()) {
    parent->SetOwnerElement(aFrameElement);
    return parent;
  }

  ProcessPriority initialPriority = GetInitialProcessPriority(aFrameElement);
  bool isInContentProcess = !XRE_IsParentProcess();
  TabId tabId;

  nsIDocShell* docShell = GetOpenerDocShellHelper(aFrameElement);
  TabId openerTabId;
  if (docShell) {
    openerTabId = TabParent::GetTabIdFrom(docShell);
  }

  if (aContext.IsMozBrowserElement() || !aContext.HasOwnApp()) {
    RefPtr<nsIContentParent> constructorSender;
    if (isInContentProcess) {
      MOZ_ASSERT(aContext.IsMozBrowserElement());
      constructorSender = CreateContentBridgeParent(aContext, initialPriority,
                                                    openerTabId, &tabId);
    } else {
      if (aOpenerContentParent) {
        constructorSender = aOpenerContentParent;
      } else {
        constructorSender =
          GetNewOrUsedBrowserProcess(aContext.IsMozBrowserElement(),
                                     initialPriority,
                                     nullptr,
                                     aFreshProcess);
        if (!constructorSender) {
          return nullptr;
        }
      }
      tabId = AllocateTabId(openerTabId,
                            aContext.AsIPCTabContext(),
                            constructorSender->ChildID());
    }
    if (constructorSender) {
      nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
      docShell->GetTreeOwner(getter_AddRefs(treeOwner));
      if (!treeOwner) {
        return nullptr;
      }

      nsCOMPtr<nsIWebBrowserChrome> wbc = do_GetInterface(treeOwner);
      if (!wbc) {
        return nullptr;
      }
      uint32_t chromeFlags = 0;
      wbc->GetChromeFlags(&chromeFlags);

      nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
      if (loadContext && loadContext->UsePrivateBrowsing()) {
        chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW;
      }
      if (docShell->GetAffectPrivateSessionLifetime()) {
        chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME;
      }

      if (tabId == 0) {
        return nullptr;
      }
      RefPtr<TabParent> tp(new TabParent(constructorSender, tabId,
                                         aContext, chromeFlags));
      tp->SetInitedByParent();

      PBrowserParent* browser =
      constructorSender->SendPBrowserConstructor(
        // DeallocPBrowserParent() releases this ref.
        tp.forget().take(), tabId,
        aContext.AsIPCTabContext(),
        chromeFlags,
        constructorSender->ChildID(),
        constructorSender->IsForApp(),
        constructorSender->IsForBrowser());

      if (aFreshProcess) {
        Unused << browser->SendSetFreshProcess();
      }

      if (browser) {
        RefPtr<TabParent> constructedTabParent = TabParent::GetFrom(browser);
        constructedTabParent->SetOwnerElement(aFrameElement);
        return constructedTabParent;
      }
    }
    return nullptr;
  }

  // If we got here, we have an app and we're not a browser element.  ownApp
  // shouldn't be null, because we otherwise would have gone into the
  // !HasOwnApp() branch above.
  RefPtr<nsIContentParent> parent;
  bool reused = false;
  bool tookPreallocated = false;
  nsAutoString manifestURL;

  if (isInContentProcess) {
    parent = CreateContentBridgeParent(aContext,
                                       initialPriority,
                                       openerTabId,
                                       &tabId);
  }
  else {
    nsCOMPtr<mozIApplication> ownApp = aContext.GetOwnApp();

    if (!sAppContentParents) {
      sAppContentParents =
        new nsDataHashtable<nsStringHashKey, ContentParent*>();
    }

    // Each app gets its own ContentParent instance unless it shares it with
    // a parent app.
    if (NS_FAILED(ownApp->GetManifestURL(manifestURL))) {
      NS_ERROR("Failed to get manifest URL");
      return nullptr;
    }

    RefPtr<ContentParent> p = sAppContentParents->Get(manifestURL);

    if (!p && Preferences::GetBool("dom.ipc.reuse_parent_app")) {
      nsAutoString parentAppManifestURL;
      aFrameElement->GetAttr(kNameSpaceID_None,
                             nsGkAtoms::parentapp, parentAppManifestURL);
      nsAdoptingString systemAppManifestURL =
        Preferences::GetString("b2g.system_manifest_url");
      nsCOMPtr<nsIAppsService> appsService =
        do_GetService(APPS_SERVICE_CONTRACTID);
      if (!parentAppManifestURL.IsEmpty() &&
        !parentAppManifestURL.Equals(systemAppManifestURL) &&
        appsService) {
        nsCOMPtr<mozIApplication> parentApp;
        nsCOMPtr<mozIApplication> app;
        appsService->GetAppByManifestURL(parentAppManifestURL,
                                         getter_AddRefs(parentApp));
        appsService->GetAppByManifestURL(manifestURL,
                                         getter_AddRefs(app));

        // Only let certified apps re-use the same process.
        unsigned short parentAppStatus = 0;
        unsigned short appStatus = 0;
        if (app &&
          NS_SUCCEEDED(app->GetAppStatus(&appStatus)) &&
          appStatus == nsIPrincipal::APP_STATUS_CERTIFIED &&
          parentApp &&
          NS_SUCCEEDED(parentApp->GetAppStatus(&parentAppStatus)) &&
          parentAppStatus == nsIPrincipal::APP_STATUS_CERTIFIED) {
          // Check if we can re-use the process of the parent app.
          p = sAppContentParents->Get(parentAppManifestURL);
        }
      }
    }

    if (p) {
      // Check that the process is still alive and set its priority.
      // Hopefully the process won't die after this point, if this call
      // succeeds.
      if (!p->SetPriorityAndCheckIsAlive(initialPriority)) {
        p = nullptr;
      }
    }

    reused = !!p;
    if (!p) {
      p = GetNewOrPreallocatedAppProcess(ownApp, initialPriority, nullptr,
                                         &tookPreallocated);
      MOZ_ASSERT(p);
      sAppContentParents->Put(manifestURL, p);
    }
    tabId = AllocateTabId(openerTabId, aContext.AsIPCTabContext(),
                          p->ChildID());
    parent = static_cast<nsIContentParent*>(p);
  }

  if (!parent || (tabId == 0)) {
    return nullptr;
  }

  uint32_t chromeFlags = 0;

  RefPtr<TabParent> tp = new TabParent(parent, tabId, aContext, chromeFlags);
  tp->SetInitedByParent();
  PBrowserParent* browser = parent->SendPBrowserConstructor(
    // DeallocPBrowserParent() releases this ref.
    RefPtr<TabParent>(tp).forget().take(),
    tabId,
    aContext.AsIPCTabContext(),
    chromeFlags,
    parent->ChildID(),
    parent->IsForApp(),
    parent->IsForBrowser());

  if (aFreshProcess) {
    Unused << browser->SendSetFreshProcess();
  }

  if (browser) {
    RefPtr<TabParent> constructedTabParent = TabParent::GetFrom(browser);
    constructedTabParent->SetOwnerElement(aFrameElement);
  }

  if (isInContentProcess) {
    // Just return directly without the following check in content process.
    return TabParent::GetFrom(browser);
  }

  if (!browser) {
    // We failed to actually start the PBrowser.  This can happen if the
    // other process has already died.
    if (!reused) {
      // Don't leave a broken ContentParent in the hashtable.
      parent->AsContentParent()->KillHard("CreateBrowserOrApp");
      sAppContentParents->Remove(manifestURL);
      parent = nullptr;
    }

    // If we took the preallocated process and it was already dead, try
    // again with a non-preallocated process.  We can be sure this won't
    // loop forever, because the next time through there will be no
    // preallocated process to take.
    if (tookPreallocated) {
      return ContentParent::CreateBrowserOrApp(aContext, aFrameElement,
                                               aOpenerContentParent);
    }

    // Otherwise just give up.
    return nullptr;
  }

  return TabParent::GetFrom(browser);
}

/*static*/ ContentBridgeParent*
ContentParent::CreateContentBridgeParent(const TabContext& aContext,
                                         const hal::ProcessPriority& aPriority,
                                         const TabId& aOpenerTabId,
                                         /*out*/ TabId* aTabId)
{
  MOZ_ASSERT(aTabId);

  ContentChild* child = ContentChild::GetSingleton();
  ContentParentId cpId;
  bool isForApp;
  bool isForBrowser;
  if (!child->SendCreateChildProcess(aContext.AsIPCTabContext(),
                                     aPriority,
                                     aOpenerTabId,
                                     &cpId,
                                     &isForApp,
                                     &isForBrowser,
                                     aTabId)) {
    return nullptr;
  }
  if (cpId == 0) {
    return nullptr;
  }
  if (!child->SendBridgeToChildProcess(cpId)) {
    return nullptr;
  }
  ContentBridgeParent* parent = child->GetLastBridge();
  parent->SetChildID(cpId);
  parent->SetIsForApp(isForApp);
  parent->SetIsForBrowser(isForBrowser);
  return parent;
}

void
ContentParent::GetAll(nsTArray<ContentParent*>& aArray)
{
  aArray.Clear();

  for (auto* cp : AllProcesses(eLive)) {
    aArray.AppendElement(cp);
  }
}

void
ContentParent::GetAllEvenIfDead(nsTArray<ContentParent*>& aArray)
{
  aArray.Clear();

  for (auto* cp : AllProcesses(eAll)) {
    aArray.AppendElement(cp);
  }
}

void
ContentParent::Init()
{
  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
  if (obs) {
    size_t length = ArrayLength(sObserverTopics);
    for (size_t i = 0; i < length; ++i) {
      obs->AddObserver(this, sObserverTopics[i], false);
    }
  }
  Preferences::AddStrongObserver(this, "");
  if (obs) {
    nsAutoString cpId;
    cpId.AppendInt(static_cast<uint64_t>(this->ChildID()));
    obs->NotifyObservers(static_cast<nsIObserver*>(this), "ipc:content-created", cpId.get());
  }

#ifdef ACCESSIBILITY
  // If accessibility is running in chrome process then start it in content
  // process.
  if (nsIPresShell::IsAccessibilityActive()) {
#if !defined(XP_WIN)
      Unused << SendActivateA11y(0);
#else
    // On Windows we currently only enable a11y in the content process
    // for testing purposes.
    if (Preferences::GetBool(kForceEnableE10sPref, false)) {
      Unused << SendActivateA11y(a11y::AccessibleWrap::GetContentProcessIdFor(ChildID()));
    }
#endif
  }
#endif

#ifdef MOZ_ENABLE_PROFILER_SPS
  nsCOMPtr<nsIProfiler> profiler(do_GetService("@mozilla.org/tools/profiler;1"));
  bool profilerActive = false;
  DebugOnly<nsresult> rv = profiler->IsActive(&profilerActive);
  MOZ_ASSERT(NS_SUCCEEDED(rv));

  if (profilerActive) {
    nsCOMPtr<nsIProfilerStartParams> currentProfilerParams;
    rv = profiler->GetStartParams(getter_AddRefs(currentProfilerParams));
    MOZ_ASSERT(NS_SUCCEEDED(rv));

    nsCOMPtr<nsISupports> gatherer;
    rv = profiler->GetProfileGatherer(getter_AddRefs(gatherer));
    MOZ_ASSERT(NS_SUCCEEDED(rv));
    mGatherer = static_cast<ProfileGatherer*>(gatherer.get());

    StartProfiler(currentProfilerParams);
  }
#endif

  RefPtr<GeckoMediaPluginServiceParent> gmps(GeckoMediaPluginServiceParent::GetSingleton());
  gmps->UpdateContentProcessGMPCapabilities();
}

void
ContentParent::ForwardKnownInfo()
{
  MOZ_ASSERT(mMetamorphosed);
  if (!mMetamorphosed) {
    return;
  }
#ifdef MOZ_WIDGET_GONK
  InfallibleTArray<VolumeInfo> volumeInfo;
  RefPtr<nsVolumeService> vs = nsVolumeService::GetSingleton();
  if (vs) {
    vs->GetVolumesForIPC(&volumeInfo);
    Unused << SendVolumes(volumeInfo);
  }
#endif /* MOZ_WIDGET_GONK */
}

namespace {

class RemoteWindowContext final : public nsIRemoteWindowContext
                                , public nsIInterfaceRequestor
{
public:
  explicit RemoteWindowContext(TabParent* aTabParent)
  : mTabParent(aTabParent)
  {
  }

  NS_DECL_ISUPPORTS
  NS_DECL_NSIINTERFACEREQUESTOR
  NS_DECL_NSIREMOTEWINDOWCONTEXT

private:
  ~RemoteWindowContext();
  RefPtr<TabParent> mTabParent;
};

NS_IMPL_ISUPPORTS(RemoteWindowContext, nsIRemoteWindowContext, nsIInterfaceRequestor)

RemoteWindowContext::~RemoteWindowContext()
{
}

NS_IMETHODIMP
RemoteWindowContext::GetInterface(const nsIID& aIID, void** aSink)
{
  return QueryInterface(aIID, aSink);
}

NS_IMETHODIMP
RemoteWindowContext::OpenURI(nsIURI* aURI)
{
  mTabParent->LoadURL(aURI);
  return NS_OK;
}

} // namespace

bool
ContentParent::SetPriorityAndCheckIsAlive(ProcessPriority aPriority)
{
  ProcessPriorityManager::SetProcessPriority(this, aPriority);

  // Now that we've set this process's priority, check whether the process is
  // still alive.  Hopefully we've set the priority to FOREGROUND*, so the
  // process won't unexpectedly crash after this point!
  //
  // Bug 943174: use waitid() with WNOWAIT so that, if the process
  // did exit, we won't consume its zombie and confuse the
  // GeckoChildProcessHost dtor.
#ifdef MOZ_WIDGET_GONK
  siginfo_t info;
  info.si_pid = 0;
  if (waitid(P_PID, Pid(), &info, WNOWAIT | WNOHANG | WEXITED) == 0
    && info.si_pid != 0) {
    return false;
  }
#endif

  return true;
}

// Helper for ContentParent::TransformPreallocatedIntoApp.
static void
TryGetNameFromManifestURL(const nsAString& aManifestURL,
              nsAString& aName)
{
  aName.Truncate();
  if (aManifestURL.IsEmpty() ||
    aManifestURL == MAGIC_PREALLOCATED_APP_MANIFEST_URL) {
    return;
  }

  nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
  NS_ENSURE_TRUE_VOID(appsService);

  nsCOMPtr<mozIApplication> app;
  appsService->GetAppByManifestURL(aManifestURL, getter_AddRefs(app));

  if (!app) {
    return;
  }

  app->GetName(aName);
}

void
ContentParent::TransformPreallocatedIntoApp(ContentParent* aOpener,
                                            const nsAString& aAppManifestURL)
{
  MOZ_ASSERT(IsPreallocated());
  mMetamorphosed = true;
  mOpener = aOpener;
  mAppManifestURL = aAppManifestURL;
  TryGetNameFromManifestURL(aAppManifestURL, mAppName);
}

void
ContentParent::TransformPreallocatedIntoBrowser(ContentParent* aOpener)
{
  // Reset mAppManifestURL, mIsForBrowser and mOSPrivileges for browser.
  mMetamorphosed = true;
  mOpener = aOpener;
  mAppManifestURL.Truncate();
  mIsForBrowser = true;
}

void
ContentParent::ShutDownProcess(ShutDownMethod aMethod)
{
  // Shutting down by sending a shutdown message works differently than the
  // other methods. We first call Shutdown() in the child. After the child is
  // ready, it calls FinishShutdown() on us. Then we close the channel.
  if (aMethod == SEND_SHUTDOWN_MESSAGE) {
    if (mIPCOpen && !mShutdownPending && SendShutdown()) {
      mShutdownPending = true;
      // Start the force-kill timer if we haven't already.
      StartForceKillTimer();
    }

    // If call was not successful, the channel must have been broken
    // somehow, and we will clean up the error in ActorDestroy.
    return;
  }

  using mozilla::dom::quota::QuotaManagerService;

  if (QuotaManagerService* quotaManagerService = QuotaManagerService::Get()) {
    quotaManagerService->AbortOperationsForProcess(mChildID);
  }

  // If Close() fails with an error, we'll end up back in this function, but
  // with aMethod = CLOSE_CHANNEL_WITH_ERROR.

  if (aMethod == CLOSE_CHANNEL && !mCalledClose) {
    // Close() can only be called once: It kicks off the destruction
    // sequence.
    mCalledClose = true;
    Close();
  }

  const ManagedContainer<POfflineCacheUpdateParent>& ocuParents =
    ManagedPOfflineCacheUpdateParent();
  for (auto iter = ocuParents.ConstIter(); !iter.Done(); iter.Next()) {
    RefPtr<mozilla::docshell::OfflineCacheUpdateParent> ocuParent =
      static_cast<mozilla::docshell::OfflineCacheUpdateParent*>(iter.Get()->GetKey());
    ocuParent->StopSendingMessagesToChild();
  }

  // NB: must MarkAsDead() here so that this isn't accidentally
  // returned from Get*() while in the midst of shutdown.
  MarkAsDead();

  // A ContentParent object might not get freed until after XPCOM shutdown has
  // shut down the cycle collector.  But by then it's too late to release any
  // CC'ed objects, so we need to null them out here, while we still can.  See
  // bug 899761.
  ShutDownMessageManager();
}

bool
ContentParent::RecvFinishShutdown()
{
  // At this point, we already called ShutDownProcess once with
  // SEND_SHUTDOWN_MESSAGE. To actually close the channel, we call
  // ShutDownProcess again with CLOSE_CHANNEL.
  MOZ_ASSERT(mShutdownPending);
  ShutDownProcess(CLOSE_CHANNEL);
  return true;
}

void
ContentParent::ShutDownMessageManager()
{
  if (!mMessageManager) {
  return;
  }

  mMessageManager->ReceiveMessage(
      static_cast<nsIContentFrameMessageManager*>(mMessageManager.get()), nullptr,
      CHILD_PROCESS_SHUTDOWN_MESSAGE, false,
      nullptr, nullptr, nullptr, nullptr);

  mMessageManager->Disconnect();
  mMessageManager = nullptr;
}

void
ContentParent::MarkAsDead()
{
  if (!mAppManifestURL.IsEmpty()) {
    if (sAppContentParents) {
      sAppContentParents->Remove(mAppManifestURL);
      if (!sAppContentParents->Count()) {
        delete sAppContentParents;
        sAppContentParents = nullptr;
      }
    }
  } else {
    if (sNonAppContentParents) {
      sNonAppContentParents->RemoveElement(this);
      if (!sNonAppContentParents->Length()) {
        delete sNonAppContentParents;
        sNonAppContentParents = nullptr;
      }
    }

    if (sLargeAllocationContentParents) {
      sLargeAllocationContentParents->RemoveElement(this);
      if (!sLargeAllocationContentParents->Length()) {
        delete sLargeAllocationContentParents;
        sLargeAllocationContentParents = nullptr;
      }
    }
  }

  if (sPrivateContent) {
    sPrivateContent->RemoveElement(this);
    if (!sPrivateContent->Length()) {
      delete sPrivateContent;
      sPrivateContent = nullptr;
    }
  }

  mIsAlive = false;
}

void
ContentParent::OnChannelError()
{
  RefPtr<ContentParent> content(this);
  PContentParent::OnChannelError();
}

void
ContentParent::OnChannelConnected(int32_t pid)
{
  SetOtherProcessId(pid);

#if defined(ANDROID) || defined(LINUX)
  // Check nice preference
  int32_t nice = Preferences::GetInt("dom.ipc.content.nice", 0);

  // Environment variable overrides preference
  char* relativeNicenessStr = getenv("MOZ_CHILD_PROCESS_RELATIVE_NICENESS");
  if (relativeNicenessStr) {
    nice = atoi(relativeNicenessStr);
  }

  /* make the GUI thread have higher priority on single-cpu devices */
  nsCOMPtr<nsIPropertyBag2> infoService = do_GetService(NS_SYSTEMINFO_CONTRACTID);
  if (infoService) {
    int32_t cpus;
    nsresult rv = infoService->GetPropertyAsInt32(NS_LITERAL_STRING("cpucount"), &cpus);
    if (NS_FAILED(rv)) {
      cpus = 1;
    }
    if (nice != 0 && cpus == 1) {
      setpriority(PRIO_PROCESS, pid, getpriority(PRIO_PROCESS, pid) + nice);
    }
  }
#endif
}

void
ContentParent::ProcessingError(Result aCode, const char* aReason)
{
  if (MsgDropped == aCode) {
    return;
  }
  // Other errors are big deals.
  KillHard(aReason);
}

/* static */
bool
ContentParent::AllocateLayerTreeId(TabParent* aTabParent, uint64_t* aId)
{
  return AllocateLayerTreeId(aTabParent->Manager()->AsContentParent(),
                             aTabParent, aTabParent->GetTabId(), aId);
}

/* static */
bool
ContentParent::AllocateLayerTreeId(ContentParent* aContent,
                                   TabParent* aTopLevel, const TabId& aTabId,
                                   uint64_t* aId)
{
  GPUProcessManager* gpu = GPUProcessManager::Get();

  *aId = gpu->AllocateLayerTreeId();

  if (!aContent || !aTopLevel) {
    return false;
  }

  gpu->MapLayerTreeId(*aId, aContent->OtherPid());

  if (!gfxPlatform::AsyncPanZoomEnabled()) {
    return true;
  }

  return aContent->SendNotifyLayerAllocated(aTabId, *aId);
}

bool
ContentParent::RecvAllocateLayerTreeId(const ContentParentId& aCpId,
                                       const TabId& aTabId, uint64_t* aId)
{
  // Protect against spoofing by a compromised child. aCpId must either
  // correspond to the process that this ContentParent represents or be a
  // child of it.
  ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
  if (ChildID() != aCpId) {
    ContentParentId parent;
    if (!cpm->GetParentProcessId(aCpId, &parent) ||
        ChildID() != parent) {
      return false;
    }
  }

  // GetTopLevelTabParentByProcessAndTabId will make sure that aTabId
  // lives in the process for aCpId.
  RefPtr<ContentParent> contentParent = cpm->GetContentProcessById(aCpId);
  RefPtr<TabParent> browserParent =
    cpm->GetTopLevelTabParentByProcessAndTabId(aCpId, aTabId);
  MOZ_ASSERT(contentParent && browserParent);

  return AllocateLayerTreeId(contentParent, browserParent, aTabId, aId);
}

bool
ContentParent::RecvDeallocateLayerTreeId(const uint64_t& aId)
{
  GPUProcessManager* gpu = GPUProcessManager::Get();

  if (!gpu->IsLayerTreeIdMapped(aId, OtherPid()))
  {
    // You can't deallocate layer tree ids that you didn't allocate
    KillHard("DeallocateLayerTreeId");
  }

  gpu->UnmapLayerTreeId(aId, OtherPid());

  return true;
}

namespace {

void
DelayedDeleteSubprocess(GeckoChildProcessHost* aSubprocess)
{
  RefPtr<DeleteTask<GeckoChildProcessHost>> task = new DeleteTask<GeckoChildProcessHost>(aSubprocess);
  XRE_GetIOMessageLoop()->PostTask(task.forget());
}

// This runnable only exists to delegate ownership of the
// ContentParent to this runnable, until it's deleted by the event
// system.
struct DelayedDeleteContentParentTask : public Runnable
{
  explicit DelayedDeleteContentParentTask(ContentParent* aObj) : mObj(aObj) { }

  // No-op
  NS_IMETHOD Run() override { return NS_OK; }

  RefPtr<ContentParent> mObj;
};

} // namespace

void
ContentParent::ActorDestroy(ActorDestroyReason why)
{
  if (mForceKillTimer) {
    mForceKillTimer->Cancel();
    mForceKillTimer = nullptr;
  }

  // Signal shutdown completion regardless of error state, so we can
  // finish waiting in the xpcom-shutdown/profile-before-change observer.
  mIPCOpen = false;

  if (mHangMonitorActor) {
    ProcessHangMonitor::RemoveProcess(mHangMonitorActor);
    mHangMonitorActor = nullptr;
  }

  RefPtr<FileSystemSecurity> fss = FileSystemSecurity::Get();
  if (fss) {
    fss->Forget(ChildID());
  }

  if (why == NormalShutdown && !mCalledClose) {
    // If we shut down normally but haven't called Close, assume somebody
    // else called Close on us. In that case, we still need to call
    // ShutDownProcess below to perform other necessary clean up.
    mCalledClose = true;
  }

  // Make sure we always clean up.
  ShutDownProcess(why == NormalShutdown ? CLOSE_CHANNEL
                                        : CLOSE_CHANNEL_WITH_ERROR);

  RefPtr<ContentParent> kungFuDeathGrip(this);
  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
  if (obs) {
    size_t length = ArrayLength(sObserverTopics);
    for (size_t i = 0; i < length; ++i) {
      obs->RemoveObserver(static_cast<nsIObserver*>(this),
                          sObserverTopics[i]);
    }
  }

  // remove the global remote preferences observers
  Preferences::RemoveObserver(this, "");
  gfxVars::RemoveReceiver(this);

  if (GPUProcessManager* gpu = GPUProcessManager::Get()) {
    // Note: the manager could have shutdown already.
    gpu->RemoveListener(this);
  }

  RecvRemoveGeolocationListener();

  mConsoleService = nullptr;

#ifdef MOZ_ENABLE_PROFILER_SPS
  if (mGatherer && !mProfile.IsEmpty()) {
    mGatherer->OOPExitProfile(mProfile);
  }
#endif

  if (obs) {
    RefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();

    props->SetPropertyAsUint64(NS_LITERAL_STRING("childID"), mChildID);

    if (AbnormalShutdown == why) {
      Telemetry::Accumulate(Telemetry::SUBPROCESS_ABNORMAL_ABORT,
                            NS_LITERAL_CSTRING("content"), 1);

      props->SetPropertyAsBool(NS_LITERAL_STRING("abnormal"), true);
    }
    nsAutoString cpId;
    cpId.AppendInt(static_cast<uint64_t>(this->ChildID()));
    obs->NotifyObservers((nsIPropertyBag2*) props, "ipc:content-shutdown", cpId.get());
  }

  // Remove any and all idle listeners.
  nsCOMPtr<nsIIdleService> idleService =
    do_GetService("@mozilla.org/widget/idleservice;1");
  MOZ_ASSERT(idleService);
  RefPtr<ParentIdleListener> listener;
  for (int32_t i = mIdleListeners.Length() - 1; i >= 0; --i) {
    listener = static_cast<ParentIdleListener*>(mIdleListeners[i].get());
    idleService->RemoveIdleObserver(listener, listener->mTime);
  }
  mIdleListeners.Clear();

  MessageLoop::current()->
    PostTask(NewRunnableFunction(DelayedDeleteSubprocess, mSubprocess));
  mSubprocess = nullptr;

  // IPDL rules require actors to live on past ActorDestroy, but it
  // may be that the kungFuDeathGrip above is the last reference to
  // |this|.  If so, when we go out of scope here, we're deleted and
  // all hell breaks loose.
  //
  // This runnable ensures that a reference to |this| lives on at
  // least until after the current task finishes running.
  NS_DispatchToCurrentThread(new DelayedDeleteContentParentTask(this));

  ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
  nsTArray<ContentParentId> childIDArray =
    cpm->GetAllChildProcessById(this->ChildID());

  // Destroy any processes created by this ContentParent
  for(uint32_t i = 0; i < childIDArray.Length(); i++) {
    ContentParent* cp = cpm->GetContentProcessById(childIDArray[i]);
    MessageLoop::current()->PostTask(NewRunnableMethod
                                     <ShutDownMethod>(cp,
                                                      &ContentParent::ShutDownProcess,
                                                      SEND_SHUTDOWN_MESSAGE));
  }
  cpm->RemoveContentProcess(this->ChildID());

  if (mDriverCrashGuard) {
    mDriverCrashGuard->NotifyCrashed();
  }

  // Unregister all the BlobURLs registered by the ContentChild.
  for (uint32_t i = 0; i < mBlobURLs.Length(); ++i) {
    nsHostObjectProtocolHandler::RemoveDataEntry(mBlobURLs[i]);
  }

  mBlobURLs.Clear();

#if defined(XP_WIN32) && defined(ACCESSIBILITY)
  a11y::AccessibleWrap::ReleaseContentProcessIdFor(ChildID());
#endif
}

void
ContentParent::NotifyTabDestroying(const TabId& aTabId,
                                   const ContentParentId& aCpId)
{
  if (XRE_IsParentProcess()) {
    // There can be more than one PBrowser for a given app process
    // because of popup windows.  PBrowsers can also destroy
    // concurrently.  When all the PBrowsers are destroying, kick off
    // another task to ensure the child process *really* shuts down,
    // even if the PBrowsers themselves never finish destroying.
    ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
    ContentParent* cp = cpm->GetContentProcessById(aCpId);
    if (!cp) {
        return;
    }
    ++cp->mNumDestroyingTabs;
    nsTArray<TabId> tabIds = cpm->GetTabParentsByProcessId(aCpId);
    if (static_cast<size_t>(cp->mNumDestroyingTabs) != tabIds.Length()) {
        return;
    }

    uint32_t numberOfParents = sNonAppContentParents ? sNonAppContentParents->Length() : 0;
    int32_t processesToKeepAlive = Preferences::GetInt("dom.ipc.keepProcessesAlive", 0);
    if (!cp->mLargeAllocationProcess && static_cast<int32_t>(numberOfParents) <= processesToKeepAlive) {
      return;
    }

    // We're dying now, so prevent this content process from being
    // recycled during its shutdown procedure.
    cp->MarkAsDead();
    cp->StartForceKillTimer();
  } else {
    ContentChild::GetSingleton()->SendNotifyTabDestroying(aTabId, aCpId);
  }
}

void
ContentParent::StartForceKillTimer()
{
  if (mForceKillTimer || !mIPCOpen) {
    return;
  }

  int32_t timeoutSecs = Preferences::GetInt("dom.ipc.tabs.shutdownTimeoutSecs", 5);
  if (timeoutSecs > 0) {
    mForceKillTimer = do_CreateInstance("@mozilla.org/timer;1");
    MOZ_ASSERT(mForceKillTimer);
    mForceKillTimer->InitWithFuncCallback(ContentParent::ForceKillTimerCallback,
                                          this,
                                          timeoutSecs * 1000,
                                          nsITimer::TYPE_ONE_SHOT);
  }
}

void
ContentParent::NotifyTabDestroyed(const TabId& aTabId,
                                  bool aNotifiedDestroying)
{
  if (aNotifiedDestroying) {
    --mNumDestroyingTabs;
  }

  nsTArray<PContentPermissionRequestParent*> parentArray =
    nsContentPermissionUtils::GetContentPermissionRequestParentById(aTabId);

  // Need to close undeleted ContentPermissionRequestParents before tab is closed.
  for (auto& permissionRequestParent : parentArray) {
    Unused << PContentPermissionRequestParent::Send__delete__(permissionRequestParent);
  }

  // There can be more than one PBrowser for a given app process
  // because of popup windows.  When the last one closes, shut
  // us down.
  ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
  nsTArray<TabId> tabIds = cpm->GetTabParentsByProcessId(this->ChildID());

  // We might want to keep alive some content processes for testing, because of performance
  // reasons, but we don't want to alter behavior if the pref is not set.
  uint32_t numberOfParents = sNonAppContentParents ? sNonAppContentParents->Length() : 0;
  int32_t processesToKeepAlive = Preferences::GetInt("dom.ipc.keepProcessesAlive", 0);
  bool shouldKeepAliveAny = !mLargeAllocationProcess && processesToKeepAlive > 0;
  bool shouldKeepAliveThis = shouldKeepAliveAny && static_cast<int32_t>(numberOfParents) <= processesToKeepAlive;

  if (tabIds.Length() == 1 && !shouldKeepAliveThis) {
    // In the case of normal shutdown, send a shutdown message to child to
    // allow it to perform shutdown tasks.
    MessageLoop::current()->PostTask(NewRunnableMethod
                                     <ShutDownMethod>(this,
                                                      &ContentParent::ShutDownProcess,
                                                      SEND_SHUTDOWN_MESSAGE));
  }
}

jsipc::CPOWManager*
ContentParent::GetCPOWManager()
{
  if (PJavaScriptParent* p = LoneManagedOrNullAsserts(ManagedPJavaScriptParent())) {
    return CPOWManagerFor(p);
  }
  return nullptr;
}

TestShellParent*
ContentParent::CreateTestShell()
{
  return static_cast<TestShellParent*>(SendPTestShellConstructor());
}

bool
ContentParent::DestroyTestShell(TestShellParent* aTestShell)
{
  return PTestShellParent::Send__delete__(aTestShell);
}

TestShellParent*
ContentParent::GetTestShellSingleton()
{
  PTestShellParent* p = LoneManagedOrNullAsserts(ManagedPTestShellParent());
  return static_cast<TestShellParent*>(p);
}

void
ContentParent::InitializeMembers()
{
  mSubprocess = nullptr;
  mChildID = gContentChildID++;
  mGeolocationWatchID = -1;
  mNumDestroyingTabs = 0;
  mIsAlive = true;
  mMetamorphosed = false;
  mSendPermissionUpdates = false;
  mCalledClose = false;
  mCalledKillHard = false;
  mCreatedPairedMinidumps = false;
  mShutdownPending = false;
  mIPCOpen = true;
  mHangMonitorActor = nullptr;
}

bool
ContentParent::LaunchSubprocess(ProcessPriority aInitialPriority /* = PROCESS_PRIORITY_FOREGROUND */)
{
  PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);

  std::vector<std::string> extraArgs;

  if (gSafeMode) {
    extraArgs.push_back("-safeMode");
  }

  if (!mSubprocess->LaunchAndWaitForProcessHandle(extraArgs)) {
    MarkAsDead();
    return false;
  }

  Open(mSubprocess->GetChannel(),
     base::GetProcId(mSubprocess->GetChildProcessHandle()));

  InitInternal(aInitialPriority,
               true, /* Setup off-main thread compositing */
               true  /* Send registered chrome */);

  ContentProcessManager::GetSingleton()->AddContentProcess(this);

  ProcessHangMonitor::AddProcess(this);

  // Set a reply timeout for CPOWs.
  SetReplyTimeoutMs(Preferences::GetInt("dom.ipc.cpow.timeout", 0));

  return true;
}

ContentParent::ContentParent(mozIApplication* aApp,
                             ContentParent* aOpener,
                             bool aIsForBrowser,
                             bool aIsForPreallocated)
  : nsIContentParent()
  , mOpener(aOpener)
  , mIsForBrowser(aIsForBrowser)
  , mLargeAllocationProcess(false)
{
  InitializeMembers();  // Perform common initialization.

  // No more than one of !!aApp, aIsForBrowser, aIsForPreallocated should be
  // true.
  MOZ_ASSERT(!!aApp + aIsForBrowser + aIsForPreallocated <= 1);

  mMetamorphosed = true;

  // Insert ourselves into the global linked list of ContentParent objects.
  if (!sContentParents) {
    sContentParents = new LinkedList<ContentParent>();
  }
  sContentParents->insertBack(this);

  if (aApp) {
    aApp->GetManifestURL(mAppManifestURL);
    aApp->GetName(mAppName);
  } else if (aIsForPreallocated) {
    mAppManifestURL = MAGIC_PREALLOCATED_APP_MANIFEST_URL;
  }

  // From this point on, NS_WARNING, NS_ASSERTION, etc. should print out the
  // PID along with the warning.
  nsDebugImpl::SetMultiprocessMode("Parent");

#if defined(XP_WIN) && !defined(MOZ_B2G)
  // Request Windows message deferral behavior on our side of the PContent
  // channel. Generally only applies to the situation where we get caught in
  // a deadlock with the plugin process when sending CPOWs.
  GetIPCChannel()->SetChannelFlags(MessageChannel::REQUIRE_DEFERRED_MESSAGE_PROTECTION);
#endif

  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
  ChildPrivileges privs = base::PRIVILEGES_DEFAULT;
  mSubprocess = new GeckoChildProcessHost(GeckoProcessType_Content, privs);
}

ContentParent::~ContentParent()
{
  if (mForceKillTimer) {
    mForceKillTimer->Cancel();
  }

  NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");

  // We should be removed from all these lists in ActorDestroy.
  MOZ_ASSERT(!sPrivateContent || !sPrivateContent->Contains(this));
  if (mAppManifestURL.IsEmpty()) {
    MOZ_ASSERT((!sNonAppContentParents ||
                !sNonAppContentParents->Contains(this)) &&
               (!sLargeAllocationContentParents ||
                !sLargeAllocationContentParents->Contains(this)));
  } else {
    // In general, we expect sAppContentParents->Get(mAppManifestURL) to be
    // nullptr.  But it could be that we created another ContentParent for
    // this app after we did this->ActorDestroy(), so the right check is
    // that sAppContentParents->Get(mAppManifestURL) != this.
    MOZ_ASSERT(!sAppContentParents ||
               sAppContentParents->Get(mAppManifestURL) != this);
  }
}

void
ContentParent::InitInternal(ProcessPriority aInitialPriority,
                            bool aSetupOffMainThreadCompositing,
                            bool aSendRegisteredChrome)
{
  if (aSendRegisteredChrome) {
    nsCOMPtr<nsIChromeRegistry> registrySvc = nsChromeRegistry::GetService();
    nsChromeRegistryChrome* chromeRegistry =
      static_cast<nsChromeRegistryChrome*>(registrySvc.get());
    chromeRegistry->SendRegisteredChrome(this);
  }

  if (gAppData) {
    nsCString version(gAppData->version);
    nsCString buildID(gAppData->buildID);
    nsCString name(gAppData->name);
    nsCString UAName(gAppData->UAName);
    nsCString ID(gAppData->ID);
    nsCString vendor(gAppData->vendor);

    // Sending all information to content process.
    Unused << SendAppInfo(version, buildID, name, UAName, ID, vendor);
  }

  // Initialize the message manager (and load delayed scripts) now that we
  // have established communications with the child.
  mMessageManager->InitWithCallback(this);

  // Set the subprocess's priority.  We do this early on because we're likely
  // /lowering/ the process's CPU and memory priority, which it has inherited
  // from this process.
  //
  // This call can cause us to send IPC messages to the child process, so it
  // must come after the Open() call above.
  ProcessPriorityManager::SetProcessPriority(this, aInitialPriority);

  if (aSetupOffMainThreadCompositing) {
    // NB: internally, this will send an IPC message to the child
    // process to get it to create the CompositorBridgeChild.  This
    // message goes through the regular IPC queue for this
    // channel, so delivery will happen-before any other messages
    // we send.  The CompositorBridgeChild must be created before any
    // PBrowsers are created, because they rely on the Compositor
    // already being around.  (Creation is async, so can't happen
    // on demand.)
    bool useOffMainThreadCompositing = !!CompositorThreadHolder::Loop();
    if (useOffMainThreadCompositing) {
      GPUProcessManager* gpm = GPUProcessManager::Get();

      Endpoint<PCompositorBridgeChild> compositor;
      Endpoint<PImageBridgeChild> imageBridge;
      Endpoint<PVRManagerChild> vrBridge;
      Endpoint<PVideoDecoderManagerChild> videoManager;

      DebugOnly<bool> opened = gpm->CreateContentBridges(
        OtherPid(),
        &compositor,
        &imageBridge,
        &vrBridge,
        &videoManager);
      MOZ_ASSERT(opened);

      Unused << SendInitRendering(
        Move(compositor),
        Move(imageBridge),
        Move(vrBridge),
        Move(videoManager));

      gpm->AddListener(this);
    }
  }

  if (gAppData) {
    // Sending all information to content process.
    Unused << SendAppInit();
  }

  nsStyleSheetService *sheetService = nsStyleSheetService::GetInstance();
  if (sheetService) {
    // This looks like a lot of work, but in a normal browser session we just
    // send two loads.

    for (StyleSheet* sheet : *sheetService->AgentStyleSheets()) {
      URIParams uri;
      SerializeURI(sheet->GetSheetURI(), uri);
      Unused << SendLoadAndRegisterSheet(uri, nsIStyleSheetService::AGENT_SHEET);
    }

    for (StyleSheet* sheet : *sheetService->UserStyleSheets()) {
      URIParams uri;
      SerializeURI(sheet->GetSheetURI(), uri);
      Unused << SendLoadAndRegisterSheet(uri, nsIStyleSheetService::USER_SHEET);
    }

    for (StyleSheet* sheet : *sheetService->AuthorStyleSheets()) {
      URIParams uri;
      SerializeURI(sheet->GetSheetURI(), uri);
      Unused << SendLoadAndRegisterSheet(uri, nsIStyleSheetService::AUTHOR_SHEET);
    }
  }

#if defined(XP_WIN)
  // Send the info needed to join the browser process's audio session.
  nsID id;
  nsString sessionName;
  nsString iconPath;
  if (NS_SUCCEEDED(mozilla::widget::GetAudioSessionData(id, sessionName,
                                                        iconPath))) {
    Unused << SendSetAudioSessionData(id, sessionName, iconPath);
  }
#endif

  {
    RefPtr<ServiceWorkerRegistrar> swr = ServiceWorkerRegistrar::Get();
    MOZ_ASSERT(swr);

    nsTArray<ServiceWorkerRegistrationData> registrations;
    swr->GetRegistrations(registrations);
    Unused << SendInitServiceWorkers(ServiceWorkerConfiguration(registrations));
  }

  {
    nsTArray<BlobURLRegistrationData> registrations;
    if (nsHostObjectProtocolHandler::GetAllBlobURLEntries(registrations,
                                                          this)) {
      Unused << SendInitBlobURLs(registrations);
    }
  }
}

bool
ContentParent::IsAlive() const
{
  return mIsAlive;
}

bool
ContentParent::IsForApp() const
{
  return !mAppManifestURL.IsEmpty();
}

int32_t
ContentParent::Pid() const
{
  if (!mSubprocess || !mSubprocess->GetChildProcessHandle()) {
    return -1;
  }
  return base::GetProcId(mSubprocess->GetChildProcessHandle());
}

bool
ContentParent::RecvReadPrefsArray(InfallibleTArray<PrefSetting>* aPrefs)
{
  Preferences::GetPreferences(aPrefs);
  return true;
}

bool
ContentParent::RecvGetGfxVars(InfallibleTArray<GfxVarUpdate>* aVars)
{
  // Ensure gfxVars is initialized (for xpcshell tests).
  gfxVars::Initialize();

  *aVars = gfxVars::FetchNonDefaultVars();

  // Now that content has initialized gfxVars, we can start listening for
  // updates.
  gfxVars::AddReceiver(this);
  return true;
}

void
ContentParent::OnCompositorUnexpectedShutdown()
{
  GPUProcessManager* gpm = GPUProcessManager::Get();

  Endpoint<PCompositorBridgeChild> compositor;
  Endpoint<PImageBridgeChild> imageBridge;
  Endpoint<PVRManagerChild> vrBridge;
  Endpoint<PVideoDecoderManagerChild> videoManager;

  DebugOnly<bool> opened = gpm->CreateContentBridges(
    OtherPid(),
    &compositor,
    &imageBridge,
    &vrBridge,
    &videoManager);
  MOZ_ASSERT(opened);

  Unused << SendReinitRendering(
    Move(compositor),
    Move(imageBridge),
    Move(vrBridge),
    Move(videoManager));
}

void
ContentParent::OnVarChanged(const GfxVarUpdate& aVar)
{
  if (!mIPCOpen) {
    return;
  }
  Unused << SendVarUpdate(aVar);
}

bool
ContentParent::RecvReadFontList(InfallibleTArray<FontListEntry>* retValue)
{
#ifdef ANDROID
  gfxAndroidPlatform::GetPlatform()->GetSystemFontList(retValue);
#endif
  return true;
}

bool
ContentParent::RecvReadDataStorageArray(const nsString& aFilename,
                                        InfallibleTArray<DataStorageItem>* aValues)
{
  // If we're shutting down, the DataStorage object may have been cleared
  // already, and setting it up is pointless anyways since we're about to die.
  if (mShutdownPending) {
    return true;
  }

  // Ensure the SSS is initialized before we try to use its storage.
  nsCOMPtr<nsISiteSecurityService> sss = do_GetService("@mozilla.org/ssservice;1");

  RefPtr<DataStorage> storage = DataStorage::Get(aFilename);
  storage->GetAll(aValues);
  return true;
}

bool
ContentParent::RecvReadPermissions(InfallibleTArray<IPC::Permission>* aPermissions)
{
#ifdef MOZ_PERMISSIONS
  nsCOMPtr<nsIPermissionManager> permissionManagerIface =
    services::GetPermissionManager();
  nsPermissionManager* permissionManager =
    static_cast<nsPermissionManager*>(permissionManagerIface.get());
  MOZ_ASSERT(permissionManager,
             "We have no permissionManager in the Chrome process !");

  nsCOMPtr<nsISimpleEnumerator> enumerator;
  DebugOnly<nsresult> rv = permissionManager->GetEnumerator(getter_AddRefs(enumerator));
  MOZ_ASSERT(NS_SUCCEEDED(rv), "Could not get enumerator!");
  while(1) {
    bool hasMore;
    enumerator->HasMoreElements(&hasMore);
    if (!hasMore)
      break;

    nsCOMPtr<nsISupports> supp;
    enumerator->GetNext(getter_AddRefs(supp));
    nsCOMPtr<nsIPermission> perm = do_QueryInterface(supp);

    nsCOMPtr<nsIPrincipal> principal;
    perm->GetPrincipal(getter_AddRefs(principal));
    nsCString origin;
    if (principal) {
      principal->GetOrigin(origin);
    }
    nsCString type;
    perm->GetType(type);
    uint32_t capability;
    perm->GetCapability(&capability);
    uint32_t expireType;
    perm->GetExpireType(&expireType);
    int64_t expireTime;
    perm->GetExpireTime(&expireTime);

    aPermissions->AppendElement(IPC::Permission(origin, type,
                                                capability, expireType,
                                                expireTime));
  }

  // Ask for future changes
  mSendPermissionUpdates = true;
#endif

  return true;
}

bool
ContentParent::RecvSetClipboard(const IPCDataTransfer& aDataTransfer,
                                const bool& aIsPrivateData,
                                const IPC::Principal& aRequestingPrincipal,
                                const int32_t& aWhichClipboard)
{
  nsresult rv;
  nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
  NS_ENSURE_SUCCESS(rv, true);

  nsCOMPtr<nsITransferable> trans =
    do_CreateInstance("@mozilla.org/widget/transferable;1", &rv);
  NS_ENSURE_SUCCESS(rv, true);
  trans->Init(nullptr);

  rv = nsContentUtils::IPCTransferableToTransferable(aDataTransfer,
                                                     aIsPrivateData,
                                                     aRequestingPrincipal,
                                                     trans, this, nullptr);
  NS_ENSURE_SUCCESS(rv, true);

  clipboard->SetData(trans, nullptr, aWhichClipboard);
  return true;
}

bool
ContentParent::RecvGetClipboard(nsTArray<nsCString>&& aTypes,
                                const int32_t& aWhichClipboard,
                                IPCDataTransfer* aDataTransfer)
{
  nsresult rv;
  nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
  NS_ENSURE_SUCCESS(rv, true);

  nsCOMPtr<nsITransferable> trans = do_CreateInstance("@mozilla.org/widget/transferable;1", &rv);
  NS_ENSURE_SUCCESS(rv, true);
  trans->Init(nullptr);

  for (uint32_t t = 0; t < aTypes.Length(); t++) {
    trans->AddDataFlavor(aTypes[t].get());
  }

  clipboard->GetData(trans, aWhichClipboard);
  nsContentUtils::TransferableToIPCTransferable(trans, aDataTransfer,
                                                true, nullptr, this);
  return true;
}

bool
ContentParent::RecvEmptyClipboard(const int32_t& aWhichClipboard)
{
  nsresult rv;
  nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
  NS_ENSURE_SUCCESS(rv, true);

  clipboard->EmptyClipboard(aWhichClipboard);

  return true;
}

bool
ContentParent::RecvClipboardHasType(nsTArray<nsCString>&& aTypes,
                                    const int32_t& aWhichClipboard,
                                    bool* aHasType)
{
  nsresult rv;
  nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
  NS_ENSURE_SUCCESS(rv, true);

  const char** typesChrs = new const char *[aTypes.Length()];
  for (uint32_t t = 0; t < aTypes.Length(); t++) {
    typesChrs[t] = aTypes[t].get();
  }

  clipboard->HasDataMatchingFlavors(typesChrs, aTypes.Length(),
                                    aWhichClipboard, aHasType);

  delete [] typesChrs;
  return true;
}

bool
ContentParent::RecvGetSystemColors(const uint32_t& colorsCount,
                                   InfallibleTArray<uint32_t>* colors)
{
#ifdef MOZ_WIDGET_ANDROID
  NS_ASSERTION(AndroidBridge::Bridge() != nullptr, "AndroidBridge is not available");
  if (AndroidBridge::Bridge() == nullptr) {
    // Do not fail - the colors won't be right, but it's not critical
    return true;
  }

  colors->AppendElements(colorsCount);

  // The array elements correspond to the members of AndroidSystemColors structure,
  // so just pass the pointer to the elements buffer
  AndroidBridge::Bridge()->GetSystemColors((AndroidSystemColors*)colors->Elements());
#endif
  return true;
}

bool
ContentParent::RecvGetIconForExtension(const nsCString& aFileExt,
                                       const uint32_t& aIconSize,
                                       InfallibleTArray<uint8_t>* bits)
{
#ifdef MOZ_WIDGET_ANDROID
  NS_ASSERTION(AndroidBridge::Bridge() != nullptr, "AndroidBridge is not available");
  if (AndroidBridge::Bridge() == nullptr) {
    // Do not fail - just no icon will be shown
    return true;
  }

  bits->AppendElements(aIconSize * aIconSize * 4);

  AndroidBridge::Bridge()->GetIconForExtension(aFileExt, aIconSize, bits->Elements());
#endif
  return true;
}

bool
ContentParent::RecvGetShowPasswordSetting(bool* showPassword)
{
  // default behavior is to show the last password character
  *showPassword = true;
#ifdef MOZ_WIDGET_ANDROID
  NS_ASSERTION(AndroidBridge::Bridge() != nullptr, "AndroidBridge is not available");

  *showPassword = java::GeckoAppShell::GetShowPasswordSetting();
#endif
  return true;
}

bool
ContentParent::RecvFirstIdle()
{
  // When the ContentChild goes idle, it sends us a FirstIdle message
  // which we use as a good time to prelaunch another process. If we
  // prelaunch any sooner than this, then we'll be competing with the
  // child process and slowing it down.
  PreallocatedProcessManager::AllocateAfterDelay();
  return true;
}

bool
ContentParent::RecvAudioChannelChangeDefVolChannel(const int32_t& aChannel,
                                                   const bool& aHidden)
{
  RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
  MOZ_ASSERT(service);
  service->SetDefaultVolumeControlChannelInternal(aChannel, aHidden, mChildID);
  return true;
}

bool
ContentParent::RecvAudioChannelServiceStatus(
                                           const bool& aTelephonyChannel,
                                           const bool& aContentOrNormalChannel,
                                           const bool& aAnyChannel)
{
  RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
  MOZ_ASSERT(service);

  service->ChildStatusReceived(mChildID, aTelephonyChannel,
                               aContentOrNormalChannel, aAnyChannel);
  return true;
}

// We want ContentParent to show up in CC logs for debugging purposes, but we
// don't actually cycle collect it.
NS_IMPL_CYCLE_COLLECTION_0(ContentParent)

NS_IMPL_CYCLE_COLLECTING_ADDREF(ContentParent)
NS_IMPL_CYCLE_COLLECTING_RELEASE(ContentParent)

NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ContentParent)
  NS_INTERFACE_MAP_ENTRY(nsIContentParent)
  NS_INTERFACE_MAP_ENTRY(nsIObserver)
  NS_INTERFACE_MAP_ENTRY(nsIDOMGeoPositionCallback)
  NS_INTERFACE_MAP_ENTRY(nsIDOMGeoPositionErrorCallback)
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
NS_INTERFACE_MAP_END

NS_IMETHODIMP
ContentParent::Observe(nsISupports* aSubject,
                       const char* aTopic,
                       const char16_t* aData)
{
  if (mSubprocess && (!strcmp(aTopic, "profile-before-change") ||
                      !strcmp(aTopic, "xpcom-shutdown"))) {
    // Okay to call ShutDownProcess multiple times.
    ShutDownProcess(SEND_SHUTDOWN_MESSAGE);

    // Wait for shutdown to complete, so that we receive any shutdown
    // data (e.g. telemetry) from the child before we quit.
    // This loop terminate prematurely based on mForceKillTimer.
    while (mIPCOpen && !mCalledKillHard) {
      NS_ProcessNextEvent(nullptr, true);
    }
    NS_ASSERTION(!mSubprocess, "Close should have nulled mSubprocess");
  }

#ifdef MOZ_ENABLE_PROFILER_SPS
  // Need to do this before the mIsAlive check to avoid missing profiles.
  if (!strcmp(aTopic, "profiler-subprocess-gather")) {
    if (mGatherer) {
      mGatherer->WillGatherOOPProfile();
      if (mIsAlive && mSubprocess) {
        Unused << SendGatherProfile();
      }
    }
  }
  else if (!strcmp(aTopic, "profiler-subprocess")) {
    nsCOMPtr<nsIProfileSaveEvent> pse = do_QueryInterface(aSubject);
    if (pse) {
      if (!mProfile.IsEmpty()) {
        pse->AddSubProfile(mProfile.get());
        mProfile.Truncate();
      }
    }
  }
#endif

  if (!mIsAlive || !mSubprocess)
    return NS_OK;

  // listening for memory pressure event
  if (!strcmp(aTopic, "memory-pressure") &&
      !StringEndsWith(nsDependentString(aData),
                      NS_LITERAL_STRING("-no-forward"))) {
      Unused << SendFlushMemory(nsDependentString(aData));
  }
  // listening for remotePrefs...
  else if (!strcmp(aTopic, "nsPref:changed")) {
    // We know prefs are ASCII here.
    NS_LossyConvertUTF16toASCII strData(aData);

    PrefSetting pref(strData, null_t(), null_t());
    Preferences::GetPreference(&pref);
    if (!SendPreferenceUpdate(pref)) {
      return NS_ERROR_NOT_AVAILABLE;
    }
  }
  else if (!strcmp(aTopic, NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC)) {
    NS_ConvertUTF16toUTF8 dataStr(aData);
    const char *offline = dataStr.get();
    if (!SendSetOffline(!strcmp(offline, "true") ? true : false)) {
      return NS_ERROR_NOT_AVAILABLE;
    }
  }
  else if (!strcmp(aTopic, NS_IPC_IOSERVICE_SET_CONNECTIVITY_TOPIC)) {
    if (!SendSetConnectivity(NS_LITERAL_STRING("true").Equals(aData))) {
      return NS_ERROR_NOT_AVAILABLE;
    }
  } else if (!strcmp(aTopic, NS_IPC_CAPTIVE_PORTAL_SET_STATE)) {
    nsCOMPtr<nsICaptivePortalService> cps = do_QueryInterface(aSubject);
    MOZ_ASSERT(cps, "Should QI to a captive portal service");
    if (!cps) {
      return NS_ERROR_FAILURE;
    }
    int32_t state;
    cps->GetState(&state);
    if (!SendSetCaptivePortalState(state)) {
      return NS_ERROR_NOT_AVAILABLE;
    }
  }
  // listening for alert notifications
  else if (!strcmp(aTopic, "alertfinished") ||
       !strcmp(aTopic, "alertclickcallback") ||
       !strcmp(aTopic, "alertshow") ||
       !strcmp(aTopic, "alertdisablecallback") ||
       !strcmp(aTopic, "alertsettingscallback")) {
    if (!SendNotifyAlertsObserver(nsDependentCString(aTopic),
                                  nsDependentString(aData)))
      return NS_ERROR_NOT_AVAILABLE;
  }
  else if (!strcmp(aTopic, "child-gc-request")){
    Unused << SendGarbageCollect();
  }
  else if (!strcmp(aTopic, "child-cc-request")){
    Unused << SendCycleCollect();
  }
  else if (!strcmp(aTopic, "child-mmu-request")){
    Unused << SendMinimizeMemoryUsage();
  }
  else if (!strcmp(aTopic, "last-pb-context-exited")) {
    Unused << SendLastPrivateDocShellDestroyed();
  }
#ifdef MOZ_WIDGET_GONK
  else if(!strcmp(aTopic, NS_VOLUME_STATE_CHANGED)) {
    nsCOMPtr<nsIVolume> vol = do_QueryInterface(aSubject);
    if (!vol) {
      return NS_ERROR_NOT_AVAILABLE;
    }

    nsString volName;
    nsString mountPoint;
    int32_t  state;
    int32_t  mountGeneration;
    bool   isMediaPresent;
    bool   isSharing;
    bool   isFormatting;
    bool   isFake;
    bool   isUnmounting;
    bool   isRemovable;
    bool   isHotSwappable;

    vol->GetName(volName);
    vol->GetMountPoint(mountPoint);
    vol->GetState(&state);
    vol->GetMountGeneration(&mountGeneration);
    vol->GetIsMediaPresent(&isMediaPresent);
    vol->GetIsSharing(&isSharing);
    vol->GetIsFormatting(&isFormatting);
    vol->GetIsFake(&isFake);
    vol->GetIsUnmounting(&isUnmounting);
    vol->GetIsRemovable(&isRemovable);
    vol->GetIsHotSwappable(&isHotSwappable);

    Unused << SendFileSystemUpdate(volName, mountPoint, state,
                                   mountGeneration, isMediaPresent,
                                   isSharing, isFormatting, isFake,
                                   isUnmounting, isRemovable, isHotSwappable);
  } else if (!strcmp(aTopic, "phone-state-changed")) {
    nsString state(aData);
    Unused << SendNotifyPhoneStateChange(state);
  }
  else if(!strcmp(aTopic, NS_VOLUME_REMOVED)) {
    nsString volName(aData);
    Unused << SendVolumeRemoved(volName);
  }
#endif
#ifdef ACCESSIBILITY
  else if (aData && !strcmp(aTopic, "a11y-init-or-shutdown")) {
    if (*aData == '1') {
      // Make sure accessibility is running in content process when
      // accessibility gets initiated in chrome process.
#if !defined(XP_WIN)
      Unused << SendActivateA11y(0);
#else
      // On Windows we currently only enable a11y in the content process
      // for testing purposes.
      if (Preferences::GetBool(kForceEnableE10sPref, false)) {
        Unused << SendActivateA11y(a11y::AccessibleWrap::GetContentProcessIdFor(ChildID()));
      }
#endif
    } else {
      // If possible, shut down accessibility in content process when
      // accessibility gets shutdown in chrome process.
      Unused << SendShutdownA11y();
    }
  }
#endif
#ifdef MOZ_ENABLE_PROFILER_SPS
  else if (!strcmp(aTopic, "profiler-started")) {
    nsCOMPtr<nsIProfilerStartParams> params(do_QueryInterface(aSubject));
    StartProfiler(params);
  }
  else if (!strcmp(aTopic, "profiler-stopped")) {
    mGatherer = nullptr;
    Unused << SendStopProfiler();
  }
  else if (!strcmp(aTopic, "profiler-paused")) {
    Unused << SendPauseProfiler(true);
  }
  else if (!strcmp(aTopic, "profiler-resumed")) {
    Unused << SendPauseProfiler(false);
  }
#endif
  else if (!strcmp(aTopic, "cacheservice:empty-cache")) {
    Unused << SendNotifyEmptyHTTPCache();
  }
  return NS_OK;
}

PGMPServiceParent*
ContentParent::AllocPGMPServiceParent(mozilla::ipc::Transport* aTransport,
                                      base::ProcessId aOtherProcess)
{
  return GMPServiceParent::Create(aTransport, aOtherProcess);
}

PBackgroundParent*
ContentParent::AllocPBackgroundParent(Transport* aTransport,
                                      ProcessId aOtherProcess)
{
  return BackgroundParent::Alloc(this, aTransport, aOtherProcess);
}

PProcessHangMonitorParent*
ContentParent::AllocPProcessHangMonitorParent(Transport* aTransport,
                                              ProcessId aOtherProcess)
{
  mHangMonitorActor = CreateHangMonitorParent(this, aTransport, aOtherProcess);
  return mHangMonitorActor;
}

bool
ContentParent::RecvGetProcessAttributes(ContentParentId* aCpId,
                                        bool* aIsForApp, bool* aIsForBrowser)
{
  *aCpId = mChildID;
  *aIsForApp = IsForApp();
  *aIsForBrowser = mIsForBrowser;

  return true;
}

bool
ContentParent::RecvGetXPCOMProcessAttributes(bool* aIsOffline,
                                             bool* aIsConnected,
                                             int32_t* aCaptivePortalState,
                                             bool* aIsLangRTL,
                                             bool* aHaveBidiKeyboards,
                                             InfallibleTArray<nsString>* dictionaries,
                                             ClipboardCapabilities* clipboardCaps,
                                             DomainPolicyClone* domainPolicy,
                                             StructuredCloneData* aInitialData,
                                             OptionalURIParams* aUserContentCSSURL)
{
  nsCOMPtr<nsIIOService> io(do_GetIOService());
  MOZ_ASSERT(io, "No IO service?");
  DebugOnly<nsresult> rv = io->GetOffline(aIsOffline);
  MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed getting offline?");

  rv = io->GetConnectivity(aIsConnected);
  MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed getting connectivity?");

  *aCaptivePortalState = nsICaptivePortalService::UNKNOWN;
  nsCOMPtr<nsICaptivePortalService> cps = do_GetService(NS_CAPTIVEPORTAL_CONTRACTID);
  if (cps) {
    cps->GetState(aCaptivePortalState);
  }

  nsIBidiKeyboard* bidi = nsContentUtils::GetBidiKeyboard();

  *aIsLangRTL = false;
  *aHaveBidiKeyboards = false;
  if (bidi) {
    bidi->IsLangRTL(aIsLangRTL);
    bidi->GetHaveBidiKeyboards(aHaveBidiKeyboards);
  }

  nsCOMPtr<nsISpellChecker> spellChecker(do_GetService(NS_SPELLCHECKER_CONTRACTID));
  MOZ_ASSERT(spellChecker, "No spell checker?");

  spellChecker->GetDictionaryList(dictionaries);

  nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1"));
  MOZ_ASSERT(clipboard, "No clipboard?");

  rv = clipboard->SupportsSelectionClipboard(&clipboardCaps->supportsSelectionClipboard());
  MOZ_ASSERT(NS_SUCCEEDED(rv));

  rv = clipboard->SupportsFindClipboard(&clipboardCaps->supportsFindClipboard());
  MOZ_ASSERT(NS_SUCCEEDED(rv));

  // Let's copy the domain policy from the parent to the child (if it's active).
  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
  NS_ENSURE_TRUE(ssm, false);
  ssm->CloneDomainPolicy(domainPolicy);

  if (nsFrameMessageManager* mm = nsFrameMessageManager::sParentProcessManager) {
    AutoJSAPI jsapi;
    if (NS_WARN_IF(!jsapi.Init(xpc::PrivilegedJunkScope()))) {
      return false;
    }
    JS::RootedValue init(jsapi.cx());
    nsresult result = mm->GetInitialProcessData(jsapi.cx(), &init);
    if (NS_FAILED(result)) {
      return false;
    }

    ErrorResult rv;
    aInitialData->Write(jsapi.cx(), init, rv);
    if (NS_WARN_IF(rv.Failed())) {
      rv.SuppressException();
      return false;
    }
  }

  // Content processes have no permission to access profile directory, so we
  // send the file URL instead.
  StyleSheet* ucs = nsLayoutStylesheetCache::For(StyleBackendType::Gecko)->UserContentSheet();
  if (ucs) {
    SerializeURI(ucs->GetSheetURI(), *aUserContentCSSURL);
  } else {
    SerializeURI(nullptr, *aUserContentCSSURL);
  }

  return true;
}

mozilla::jsipc::PJavaScriptParent *
ContentParent::AllocPJavaScriptParent()
{
  MOZ_ASSERT(ManagedPJavaScriptParent().IsEmpty());
  return nsIContentParent::AllocPJavaScriptParent();
}

bool
ContentParent::DeallocPJavaScriptParent(PJavaScriptParent *parent)
{
  return nsIContentParent::DeallocPJavaScriptParent(parent);
}

PBrowserParent*
ContentParent::AllocPBrowserParent(const TabId& aTabId,
                                   const IPCTabContext& aContext,
                                   const uint32_t& aChromeFlags,
                                   const ContentParentId& aCpId,
                                   const bool& aIsForApp,
                                   const bool& aIsForBrowser)
{
  return nsIContentParent::AllocPBrowserParent(aTabId,
                                               aContext,
                                               aChromeFlags,
                                               aCpId,
                                               aIsForApp,
                                               aIsForBrowser);
}

bool
ContentParent::DeallocPBrowserParent(PBrowserParent* frame)
{
  return nsIContentParent::DeallocPBrowserParent(frame);
}

PBlobParent*
ContentParent::AllocPBlobParent(const BlobConstructorParams& aParams)
{
  return nsIContentParent::AllocPBlobParent(aParams);
}

bool
ContentParent::DeallocPBlobParent(PBlobParent* aActor)
{
  return nsIContentParent::DeallocPBlobParent(aActor);
}

bool
ContentParent::RecvPBlobConstructor(PBlobParent* aActor,
                                    const BlobConstructorParams& aParams)
{
  const ParentBlobConstructorParams& params = aParams.get_ParentBlobConstructorParams();
  if (params.blobParams().type() == AnyBlobConstructorParams::TKnownBlobConstructorParams) {
    return aActor->SendCreatedFromKnownBlob();
  }

  return true;
}

mozilla::PRemoteSpellcheckEngineParent *
ContentParent::AllocPRemoteSpellcheckEngineParent()
{
  mozilla::RemoteSpellcheckEngineParent *parent = new mozilla::RemoteSpellcheckEngineParent();
  return parent;
}

bool
ContentParent::DeallocPRemoteSpellcheckEngineParent(PRemoteSpellcheckEngineParent *parent)
{
  delete parent;
  return true;
}

/* static */ void
ContentParent::ForceKillTimerCallback(nsITimer* aTimer, void* aClosure)
{
  // We don't want to time out the content process during XPCShell tests. This
  // is the easiest way to ensure that.
  if (PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR")) {
    return;
  }

  auto self = static_cast<ContentParent*>(aClosure);
  self->KillHard("ShutDownKill");
}

// WARNING: aReason appears in telemetry, so any new value passed in requires
// data review.
void
ContentParent::KillHard(const char* aReason)
{
  PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);

  // On Windows, calling KillHard multiple times causes problems - the
  // process handle becomes invalid on the first call, causing a second call
  // to crash our process - more details in bug 890840.
  if (mCalledKillHard) {
    return;
  }
  mCalledKillHard = true;
  mForceKillTimer = nullptr;

  ProcessHandle otherProcessHandle;
  if (!base::OpenProcessHandle(OtherPid(), &otherProcessHandle)) {
    NS_ERROR("Failed to open child process when attempting kill.");
    return;
  }

  if (!KillProcess(otherProcessHandle, base::PROCESS_END_KILLED_BY_USER,
           false)) {
    NS_WARNING("failed to kill subprocess!");
  }

  if (mSubprocess) {
    mSubprocess->SetAlreadyDead();
  }

  // EnsureProcessTerminated has responsibilty for closing otherProcessHandle.
  XRE_GetIOMessageLoop()->PostTask(
    NewRunnableFunction(&ProcessWatcher::EnsureProcessTerminated,
                        otherProcessHandle, /*force=*/true));
}

bool
ContentParent::IsPreallocated() const
{
  return mAppManifestURL == MAGIC_PREALLOCATED_APP_MANIFEST_URL;
}

void
ContentParent::FriendlyName(nsAString& aName, bool aAnonymize)
{
  aName.Truncate();
  if (IsPreallocated()) {
    aName.AssignLiteral("(Preallocated)");
  } else if (mIsForBrowser) {
    aName.AssignLiteral("Browser");
  } else if (aAnonymize) {
    aName.AssignLiteral("<anonymized-name>");
  } else if (!mAppName.IsEmpty()) {
    aName = mAppName;
  } else if (!mAppManifestURL.IsEmpty()) {
    aName.AssignLiteral("Unknown app: ");
    aName.Append(mAppManifestURL);
  } else {
    aName.AssignLiteral("???");
  }
}

PCrashReporterParent*
ContentParent::AllocPCrashReporterParent(const NativeThreadId& tid,
                                         const uint32_t& processType)
{
  return nullptr;
}

bool
ContentParent::RecvPCrashReporterConstructor(PCrashReporterParent* actor,
                                             const NativeThreadId& tid,
                                             const uint32_t& processType)
{
  static_cast<CrashReporterParent*>(actor)->SetChildData(tid, processType);
  return true;
}

bool
ContentParent::DeallocPCrashReporterParent(PCrashReporterParent* crashreporter)
{
  delete crashreporter;
  return true;
}

hal_sandbox::PHalParent*
ContentParent::AllocPHalParent()
{
  return hal_sandbox::CreateHalParent();
}

bool
ContentParent::DeallocPHalParent(hal_sandbox::PHalParent* aHal)
{
  delete aHal;
  return true;
}

devtools::PHeapSnapshotTempFileHelperParent*
ContentParent::AllocPHeapSnapshotTempFileHelperParent()
{
  return devtools::HeapSnapshotTempFileHelperParent::Create();
}

bool
ContentParent::DeallocPHeapSnapshotTempFileHelperParent(
  devtools::PHeapSnapshotTempFileHelperParent* aHeapSnapshotHelper)
{
  delete aHeapSnapshotHelper;
  return true;
}

PMemoryReportRequestParent*
ContentParent::AllocPMemoryReportRequestParent(const uint32_t& aGeneration,
                                               const bool &aAnonymize,
                                               const bool &aMinimizeMemoryUsage,
                                               const MaybeFileDesc &aDMDFile)
{
  MemoryReportRequestParent* parent =
    new MemoryReportRequestParent(aGeneration);
  return parent;
}

bool
ContentParent::DeallocPMemoryReportRequestParent(PMemoryReportRequestParent* actor)
{
  delete actor;
  return true;
}

PCycleCollectWithLogsParent*
ContentParent::AllocPCycleCollectWithLogsParent(const bool& aDumpAllTraces,
                                                const FileDescriptor& aGCLog,
                                                const FileDescriptor& aCCLog)
{
  MOZ_CRASH("Don't call this; use ContentParent::CycleCollectWithLogs");
}

bool
ContentParent::DeallocPCycleCollectWithLogsParent(PCycleCollectWithLogsParent* aActor)
{
  delete aActor;
  return true;
}

bool
ContentParent::CycleCollectWithLogs(bool aDumpAllTraces,
                                    nsICycleCollectorLogSink* aSink,
                                    nsIDumpGCAndCCLogsCallback* aCallback)
{
  return CycleCollectWithLogsParent::AllocAndSendConstructor(this,
                                                             aDumpAllTraces,
                                                             aSink,
                                                             aCallback);
}

PTestShellParent*
ContentParent::AllocPTestShellParent()
{
  return new TestShellParent();
}

bool
ContentParent::DeallocPTestShellParent(PTestShellParent* shell)
{
  delete shell;
  return true;
}

PNeckoParent*
ContentParent::AllocPNeckoParent()
{
  return new NeckoParent();
}

bool
ContentParent::DeallocPNeckoParent(PNeckoParent* necko)
{
  delete necko;
  return true;
}

PPrintingParent*
ContentParent::AllocPPrintingParent()
{
#ifdef NS_PRINTING
  MOZ_RELEASE_ASSERT(!mPrintingParent,
                     "Only one PrintingParent should be created per process.");

  // Create the printing singleton for this process.
  mPrintingParent = new PrintingParent();

  // Take another reference for IPDL code.
  mPrintingParent.get()->AddRef();

  return mPrintingParent.get();
#else
  MOZ_ASSERT_UNREACHABLE("Should never be created if no printing.");
  return nullptr;
#endif
}

bool
ContentParent::DeallocPPrintingParent(PPrintingParent* printing)
{
#ifdef NS_PRINTING
  MOZ_RELEASE_ASSERT(mPrintingParent == printing,
    "Only one PrintingParent should have been created per process.");

  // Release reference taken for IPDL code.
  static_cast<PrintingParent*>(printing)->Release();

  mPrintingParent = nullptr;
#else
  MOZ_ASSERT_UNREACHABLE("Should never have been created if no printing.");
#endif
  return true;
}

#ifdef NS_PRINTING
already_AddRefed<embedding::PrintingParent>
ContentParent::GetPrintingParent()
{
  MOZ_ASSERT(mPrintingParent);

  RefPtr<embedding::PrintingParent> printingParent = mPrintingParent;
  return printingParent.forget();
}
#endif

PSendStreamParent*
ContentParent::AllocPSendStreamParent()
{
  return nsIContentParent::AllocPSendStreamParent();
}

bool
ContentParent::DeallocPSendStreamParent(PSendStreamParent* aActor)
{
  return nsIContentParent::DeallocPSendStreamParent(aActor);
}

PScreenManagerParent*
ContentParent::AllocPScreenManagerParent(uint32_t* aNumberOfScreens,
                                         float* aSystemDefaultScale,
                                         bool* aSuccess)
{
  return new ScreenManagerParent(aNumberOfScreens, aSystemDefaultScale, aSuccess);
}

bool
ContentParent::DeallocPScreenManagerParent(PScreenManagerParent* aActor)
{
  delete aActor;
  return true;
}

PPSMContentDownloaderParent*
ContentParent::AllocPPSMContentDownloaderParent(const uint32_t& aCertType)
{
  RefPtr<PSMContentDownloaderParent> downloader =
    new PSMContentDownloaderParent(aCertType);
  return downloader.forget().take();
}

bool
ContentParent::DeallocPPSMContentDownloaderParent(PPSMContentDownloaderParent* aListener)
{
  auto* listener = static_cast<PSMContentDownloaderParent*>(aListener);
  RefPtr<PSMContentDownloaderParent> downloader = dont_AddRef(listener);
  return true;
}

PExternalHelperAppParent*
ContentParent::AllocPExternalHelperAppParent(const OptionalURIParams& uri,
                                             const nsCString& aMimeContentType,
                                             const nsCString& aContentDisposition,
                                             const uint32_t& aContentDispositionHint,
                                             const nsString& aContentDispositionFilename,
                                             const bool& aForceSave,
                                             const int64_t& aContentLength,
                                             const bool& aWasFileChannel,
                                             const OptionalURIParams& aReferrer,
                                             PBrowserParent* aBrowser)
{
  ExternalHelperAppParent *parent =
    new ExternalHelperAppParent(uri, aContentLength, aWasFileChannel);
  parent->AddRef();
  parent->Init(this,
               aMimeContentType,
               aContentDisposition,
               aContentDispositionHint,
               aContentDispositionFilename,
               aForceSave,
               aReferrer,
               aBrowser);
  return parent;
}

bool
ContentParent::DeallocPExternalHelperAppParent(PExternalHelperAppParent* aService)
{
  ExternalHelperAppParent *parent = static_cast<ExternalHelperAppParent *>(aService);
  parent->Release();
  return true;
}

PHandlerServiceParent*
ContentParent::AllocPHandlerServiceParent()
{
  HandlerServiceParent* actor = new HandlerServiceParent();
  actor->AddRef();
  return actor;
}

bool
ContentParent::DeallocPHandlerServiceParent(PHandlerServiceParent* aHandlerServiceParent)
{
  static_cast<HandlerServiceParent*>(aHandlerServiceParent)->Release();
  return true;
}

media::PMediaParent*
ContentParent::AllocPMediaParent()
{
  return media::AllocPMediaParent();
}

bool
ContentParent::DeallocPMediaParent(media::PMediaParent *aActor)
{
  return media::DeallocPMediaParent(aActor);
}

PStorageParent*
ContentParent::AllocPStorageParent()
{
  return new DOMStorageDBParent();
}

bool
ContentParent::DeallocPStorageParent(PStorageParent* aActor)
{
  DOMStorageDBParent* child = static_cast<DOMStorageDBParent*>(aActor);
  child->ReleaseIPDLReference();
  return true;
}

PPresentationParent*
ContentParent::AllocPPresentationParent()
{
  RefPtr<PresentationParent> actor = new PresentationParent();
  return actor.forget().take();
}

bool
ContentParent::DeallocPPresentationParent(PPresentationParent* aActor)
{
  RefPtr<PresentationParent> actor =
  dont_AddRef(static_cast<PresentationParent*>(aActor));
  return true;
}

bool
ContentParent::RecvPPresentationConstructor(PPresentationParent* aActor)
{
  return static_cast<PresentationParent*>(aActor)->Init(mChildID);
}

PFlyWebPublishedServerParent*
ContentParent::AllocPFlyWebPublishedServerParent(const nsString& name,
                                                 const FlyWebPublishOptions& params)
{
  RefPtr<FlyWebPublishedServerParent> actor =
    new FlyWebPublishedServerParent(name, params);
  return actor.forget().take();
}

bool
ContentParent::DeallocPFlyWebPublishedServerParent(PFlyWebPublishedServerParent* aActor)
{
  RefPtr<FlyWebPublishedServerParent> actor =
    dont_AddRef(static_cast<FlyWebPublishedServerParent*>(aActor));
  return true;
}

PSpeechSynthesisParent*
ContentParent::AllocPSpeechSynthesisParent()
{
#ifdef MOZ_WEBSPEECH
  return new mozilla::dom::SpeechSynthesisParent();
#else
  return nullptr;
#endif
}

bool
ContentParent::DeallocPSpeechSynthesisParent(PSpeechSynthesisParent* aActor)
{
#ifdef MOZ_WEBSPEECH
  delete aActor;
  return true;
#else
  return false;
#endif
}

bool
ContentParent::RecvPSpeechSynthesisConstructor(PSpeechSynthesisParent* aActor)
{
#ifdef MOZ_WEBSPEECH
  return true;
#else
  return false;
#endif
}

bool
ContentParent::RecvSpeakerManagerGetSpeakerStatus(bool* aValue)
{
#ifdef MOZ_WIDGET_GONK
  *aValue = false;
  RefPtr<SpeakerManagerService> service =
  SpeakerManagerService::GetOrCreateSpeakerManagerService();
  MOZ_ASSERT(service);

  *aValue = service->GetSpeakerStatus();
  return true;
#endif
  return false;
}

bool
ContentParent::RecvSpeakerManagerForceSpeaker(const bool& aEnable)
{
#ifdef MOZ_WIDGET_GONK
  RefPtr<SpeakerManagerService> service =
  SpeakerManagerService::GetOrCreateSpeakerManagerService();
  MOZ_ASSERT(service);
  service->ForceSpeaker(aEnable, mChildID);

  return true;
#endif
  return false;
}

bool
ContentParent::RecvStartVisitedQuery(const URIParams& aURI)
{
  nsCOMPtr<nsIURI> newURI = DeserializeURI(aURI);
  if (!newURI) {
  return false;
  }
  nsCOMPtr<IHistory> history = services::GetHistoryService();
  if (history) {
  history->RegisterVisitedCallback(newURI, nullptr);
  }
  return true;
}


bool
ContentParent::RecvVisitURI(const URIParams& uri,
                            const OptionalURIParams& referrer,
                            const uint32_t& flags)
{
  nsCOMPtr<nsIURI> ourURI = DeserializeURI(uri);
  if (!ourURI) {
    return false;
  }
  nsCOMPtr<nsIURI> ourReferrer = DeserializeURI(referrer);
  nsCOMPtr<IHistory> history = services::GetHistoryService();
  if (history) {
    history->VisitURI(ourURI, ourReferrer, flags);
  }
  return true;
}


bool
ContentParent::RecvSetURITitle(const URIParams& uri,
                               const nsString& title)
{
  nsCOMPtr<nsIURI> ourURI = DeserializeURI(uri);
  if (!ourURI) {
    return false;
  }
  nsCOMPtr<IHistory> history = services::GetHistoryService();
  if (history) {
    history->SetURITitle(ourURI, title);
  }
  return true;
}

bool
ContentParent::RecvNSSU2FTokenIsCompatibleVersion(const nsString& aVersion,
                                                  bool* aIsCompatible)
{
  MOZ_ASSERT(aIsCompatible);

  nsCOMPtr<nsINSSU2FToken> nssToken(do_GetService(NS_NSSU2FTOKEN_CONTRACTID));
  if (NS_WARN_IF(!nssToken)) {
    return false;
  }

  nsresult rv = nssToken->IsCompatibleVersion(aVersion, aIsCompatible);
  return NS_SUCCEEDED(rv);
}

bool
ContentParent::RecvNSSU2FTokenIsRegistered(nsTArray<uint8_t>&& aKeyHandle,
                                           bool* aIsValidKeyHandle)
{
  MOZ_ASSERT(aIsValidKeyHandle);

  nsCOMPtr<nsINSSU2FToken> nssToken(do_GetService(NS_NSSU2FTOKEN_CONTRACTID));
  if (NS_WARN_IF(!nssToken)) {
    return false;
  }

  nsresult rv = nssToken->IsRegistered(aKeyHandle.Elements(), aKeyHandle.Length(),
                                       aIsValidKeyHandle);
  return  NS_SUCCEEDED(rv);
}

bool
ContentParent::RecvNSSU2FTokenRegister(nsTArray<uint8_t>&& aApplication,
                                       nsTArray<uint8_t>&& aChallenge,
                                       nsTArray<uint8_t>* aRegistration)
{
  MOZ_ASSERT(aRegistration);

  nsCOMPtr<nsINSSU2FToken> nssToken(do_GetService(NS_NSSU2FTOKEN_CONTRACTID));
  if (NS_WARN_IF(!nssToken)) {
    return false;
  }
  uint8_t* buffer;
  uint32_t bufferlen;
  nsresult rv = nssToken->Register(aApplication.Elements(), aApplication.Length(),
                                   aChallenge.Elements(), aChallenge.Length(),
                                   &buffer, &bufferlen);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return false;
  }

  MOZ_ASSERT(buffer);
  aRegistration->ReplaceElementsAt(0, aRegistration->Length(), buffer, bufferlen);
  free(buffer);
  return NS_SUCCEEDED(rv);
}

bool
ContentParent::RecvNSSU2FTokenSign(nsTArray<uint8_t>&& aApplication,
                                   nsTArray<uint8_t>&& aChallenge,
                                   nsTArray<uint8_t>&& aKeyHandle,
                                   nsTArray<uint8_t>* aSignature)
{
  MOZ_ASSERT(aSignature);

  nsCOMPtr<nsINSSU2FToken> nssToken(do_GetService(NS_NSSU2FTOKEN_CONTRACTID));
  if (NS_WARN_IF(!nssToken)) {
    return false;
  }
  uint8_t* buffer;
  uint32_t bufferlen;
  nsresult rv = nssToken->Sign(aApplication.Elements(), aApplication.Length(),
                               aChallenge.Elements(), aChallenge.Length(),
                               aKeyHandle.Elements(), aKeyHandle.Length(),
                               &buffer, &bufferlen);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return false;
  }

  MOZ_ASSERT(buffer);
  aSignature->ReplaceElementsAt(0, aSignature->Length(), buffer, bufferlen);
  free(buffer);
  return NS_SUCCEEDED(rv);
}

bool
ContentParent::RecvGetLookAndFeelCache(nsTArray<LookAndFeelInt>* aLookAndFeelIntCache)
{
  *aLookAndFeelIntCache = LookAndFeel::GetIntCache();
  return true;
}

bool
ContentParent::RecvIsSecureURI(const uint32_t& type,
                               const URIParams& uri,
                               const uint32_t& flags,
                               bool* isSecureURI)
{
  nsCOMPtr<nsISiteSecurityService> sss(do_GetService(NS_SSSERVICE_CONTRACTID));
  if (!sss) {
    return false;
  }
  nsCOMPtr<nsIURI> ourURI = DeserializeURI(uri);
  if (!ourURI) {
    return false;
  }
  nsresult rv = sss->IsSecureURI(type, ourURI, flags, nullptr, isSecureURI);
  return NS_SUCCEEDED(rv);
}

bool
ContentParent::RecvAccumulateMixedContentHSTS(const URIParams& aURI, const bool& aActive, const bool& aHSTSPriming)
{
  nsCOMPtr<nsIURI> ourURI = DeserializeURI(aURI);
  if (!ourURI) {
    return false;
  }
  nsMixedContentBlocker::AccumulateMixedContentHSTS(ourURI, aActive, aHSTSPriming);
  return true;
}

bool
ContentParent::RecvLoadURIExternal(const URIParams& uri,
                                   PBrowserParent* windowContext)
{
  nsCOMPtr<nsIExternalProtocolService> extProtService(do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID));
  if (!extProtService) {
    return true;
  }
  nsCOMPtr<nsIURI> ourURI = DeserializeURI(uri);
  if (!ourURI) {
    return false;
  }

  RefPtr<RemoteWindowContext> context =
    new RemoteWindowContext(static_cast<TabParent*>(windowContext));
  extProtService->LoadURI(ourURI, context);
  return true;
}

bool
ContentParent::RecvExtProtocolChannelConnectParent(const uint32_t& registrarId)
{
  nsresult rv;

  // First get the real channel created before redirect on the parent.
  nsCOMPtr<nsIChannel> channel;
  rv = NS_LinkRedirectChannels(registrarId, nullptr, getter_AddRefs(channel));
  NS_ENSURE_SUCCESS(rv, true);

  nsCOMPtr<nsIParentChannel> parent = do_QueryInterface(channel, &rv);
  NS_ENSURE_SUCCESS(rv, true);

  // The channel itself is its own (faked) parent, link it.
  rv = NS_LinkRedirectChannels(registrarId, parent, getter_AddRefs(channel));
  NS_ENSURE_SUCCESS(rv, true);

  // Signal the parent channel that it's a redirect-to parent.  This will
  // make AsyncOpen on it do nothing (what we want).
  // Yes, this is a bit of a hack, but I don't think it's necessary to invent
  // a new interface just to set this flag on the channel.
  parent->SetParentListener(nullptr);

  return true;
}

bool
ContentParent::HasNotificationPermission(const IPC::Principal& aPrincipal)
{
  return true;
}

bool
ContentParent::RecvShowAlert(const AlertNotificationType& aAlert)
{
  nsCOMPtr<nsIAlertNotification> alert(dont_AddRef(aAlert));
  if (NS_WARN_IF(!alert)) {
    return true;
  }

  nsCOMPtr<nsIPrincipal> principal;
  nsresult rv = alert->GetPrincipal(getter_AddRefs(principal));
  if (NS_WARN_IF(NS_FAILED(rv)) ||
      !HasNotificationPermission(IPC::Principal(principal))) {

      return true;
  }

  nsCOMPtr<nsIAlertsService> sysAlerts(do_GetService(NS_ALERTSERVICE_CONTRACTID));
  if (sysAlerts) {
      sysAlerts->ShowAlert(alert, this);
  }
  return true;
}

bool
ContentParent::RecvCloseAlert(const nsString& aName,
                              const IPC::Principal& aPrincipal)
{
  if (!HasNotificationPermission(aPrincipal)) {
    return true;
  }

  nsCOMPtr<nsIAlertsService> sysAlerts(do_GetService(NS_ALERTSERVICE_CONTRACTID));
  if (sysAlerts) {
    sysAlerts->CloseAlert(aName, aPrincipal);
  }

  return true;
}

bool
ContentParent::RecvDisableNotifications(const IPC::Principal& aPrincipal)
{
  if (HasNotificationPermission(aPrincipal)) {
    Unused << Notification::RemovePermission(aPrincipal);
  }
  return true;
}

bool
ContentParent::RecvOpenNotificationSettings(const IPC::Principal& aPrincipal)
{
  if (HasNotificationPermission(aPrincipal)) {
    Unused << Notification::OpenSettings(aPrincipal);
  }
  return true;
}

bool
ContentParent::RecvSyncMessage(const nsString& aMsg,
                               const ClonedMessageData& aData,
                               InfallibleTArray<CpowEntry>&& aCpows,
                               const IPC::Principal& aPrincipal,
                               nsTArray<StructuredCloneData>* aRetvals)
{
  return nsIContentParent::RecvSyncMessage(aMsg, aData, Move(aCpows),
                                           aPrincipal, aRetvals);
}

bool
ContentParent::RecvRpcMessage(const nsString& aMsg,
                              const ClonedMessageData& aData,
                              InfallibleTArray<CpowEntry>&& aCpows,
                              const IPC::Principal& aPrincipal,
                              nsTArray<StructuredCloneData>* aRetvals)
{
  return nsIContentParent::RecvRpcMessage(aMsg, aData, Move(aCpows), aPrincipal,
                                          aRetvals);
}

bool
ContentParent::RecvAsyncMessage(const nsString& aMsg,
                                InfallibleTArray<CpowEntry>&& aCpows,
                                const IPC::Principal& aPrincipal,
                                const ClonedMessageData& aData)
{
  return nsIContentParent::RecvAsyncMessage(aMsg, Move(aCpows), aPrincipal,
                                            aData);
}

static int32_t
AddGeolocationListener(nsIDOMGeoPositionCallback* watcher,
                       nsIDOMGeoPositionErrorCallback* errorCallBack,
                       bool highAccuracy)
{
  nsCOMPtr<nsIDOMGeoGeolocation> geo = do_GetService("@mozilla.org/geolocation;1");
  if (!geo) {
    return -1;
  }

  UniquePtr<PositionOptions> options = MakeUnique<PositionOptions>();
  options->mTimeout = 0;
  options->mMaximumAge = 0;
  options->mEnableHighAccuracy = highAccuracy;
  int32_t retval = 1;
  geo->WatchPosition(watcher, errorCallBack, Move(options), &retval);
  return retval;
}

bool
ContentParent::RecvAddGeolocationListener(const IPC::Principal& aPrincipal,
                                          const bool& aHighAccuracy)
{
  // To ensure no geolocation updates are skipped, we always force the
  // creation of a new listener.
  RecvRemoveGeolocationListener();
  mGeolocationWatchID = AddGeolocationListener(this, this, aHighAccuracy);
  return true;
}

bool
ContentParent::RecvRemoveGeolocationListener()
{
  if (mGeolocationWatchID != -1) {
    nsCOMPtr<nsIDOMGeoGeolocation> geo = do_GetService("@mozilla.org/geolocation;1");
    if (!geo) {
      return true;
    }
    geo->ClearWatch(mGeolocationWatchID);
    mGeolocationWatchID = -1;
  }
  return true;
}

bool
ContentParent::RecvSetGeolocationHigherAccuracy(const bool& aEnable)
{
  // This should never be called without a listener already present,
  // so this check allows us to forgo securing privileges.
  if (mGeolocationWatchID != -1) {
    RecvRemoveGeolocationListener();
    mGeolocationWatchID = AddGeolocationListener(this, this, aEnable);
  }
  return true;
}

NS_IMETHODIMP
ContentParent::HandleEvent(nsIDOMGeoPosition* postion)
{
  Unused << SendGeolocationUpdate(GeoPosition(postion));
  return NS_OK;
}

NS_IMETHODIMP
ContentParent::HandleEvent(nsIDOMGeoPositionError* postionError)
{
  int16_t errorCode;
  nsresult rv;
  rv = postionError->GetCode(&errorCode);
  NS_ENSURE_SUCCESS(rv,rv);
  Unused << SendGeolocationError(errorCode);
  return NS_OK;
}

nsConsoleService *
ContentParent::GetConsoleService()
{
  if (mConsoleService) {
    return mConsoleService.get();
  }

  // XXXkhuey everything about this is terrible.
  // Get the ConsoleService by CID rather than ContractID, so that we
  // can cast the returned pointer to an nsConsoleService (rather than
  // just an nsIConsoleService). This allows us to call the non-idl function
  // nsConsoleService::LogMessageWithMode.
  NS_DEFINE_CID(consoleServiceCID, NS_CONSOLESERVICE_CID);
  nsCOMPtr<nsIConsoleService> consoleService(do_GetService(consoleServiceCID));
  mConsoleService = static_cast<nsConsoleService*>(consoleService.get());
  return mConsoleService.get();
}

bool
ContentParent::RecvConsoleMessage(const nsString& aMessage)
{
  RefPtr<nsConsoleService> consoleService = GetConsoleService();
  if (!consoleService) {
    return true;
  }

  RefPtr<nsConsoleMessage> msg(new nsConsoleMessage(aMessage.get()));
  consoleService->LogMessageWithMode(msg, nsConsoleService::SuppressLog);
  return true;
}

bool
ContentParent::RecvScriptError(const nsString& aMessage,
                               const nsString& aSourceName,
                               const nsString& aSourceLine,
                               const uint32_t& aLineNumber,
                               const uint32_t& aColNumber,
                               const uint32_t& aFlags,
                               const nsCString& aCategory)
{
  RefPtr<nsConsoleService> consoleService = GetConsoleService();
  if (!consoleService) {
    return true;
  }

  nsCOMPtr<nsIScriptError> msg(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
  nsresult rv = msg->Init(aMessage, aSourceName, aSourceLine,
                          aLineNumber, aColNumber, aFlags, aCategory.get());
  if (NS_FAILED(rv))
    return true;

  consoleService->LogMessageWithMode(msg, nsConsoleService::SuppressLog);
  return true;
}

bool
ContentParent::RecvPrivateDocShellsExist(const bool& aExist)
{
  if (!sPrivateContent)
    sPrivateContent = new nsTArray<ContentParent*>();
  if (aExist) {
    sPrivateContent->AppendElement(this);
  } else {
    sPrivateContent->RemoveElement(this);

    // Only fire the notification if we have private and non-private
    // windows: if privatebrowsing.autostart is true, all windows are
    // private.
    if (!sPrivateContent->Length() &&
      !Preferences::GetBool("browser.privatebrowsing.autostart")) {
      nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
      obs->NotifyObservers(nullptr, "last-pb-context-exited", nullptr);
      delete sPrivateContent;
      sPrivateContent = nullptr;
    }
  }
  return true;
}

bool
ContentParent::DoLoadMessageManagerScript(const nsAString& aURL,
                                          bool aRunInGlobalScope)
{
  MOZ_ASSERT(!aRunInGlobalScope);
  return SendLoadProcessScript(nsString(aURL));
}

nsresult
ContentParent::DoSendAsyncMessage(JSContext* aCx,
                                  const nsAString& aMessage,
                                  StructuredCloneData& aHelper,
                                  JS::Handle<JSObject *> aCpows,
                                  nsIPrincipal* aPrincipal)
{
  ClonedMessageData data;
  if (!BuildClonedMessageDataForParent(this, aHelper, data)) {
    return NS_ERROR_DOM_DATA_CLONE_ERR;
  }
  InfallibleTArray<CpowEntry> cpows;
  jsipc::CPOWManager* mgr = GetCPOWManager();
  if (aCpows && (!mgr || !mgr->Wrap(aCx, aCpows, &cpows))) {
    return NS_ERROR_UNEXPECTED;
  }
  if (!SendAsyncMessage(nsString(aMessage), cpows, Principal(aPrincipal), data)) {
    return NS_ERROR_UNEXPECTED;
  }
  return NS_OK;
}

bool
ContentParent::CheckPermission(const nsAString& aPermission)
{
  return AssertAppProcessPermission(this, NS_ConvertUTF16toUTF8(aPermission).get());
}

bool
ContentParent::CheckManifestURL(const nsAString& aManifestURL)
{
  return AssertAppProcessManifestURL(this, NS_ConvertUTF16toUTF8(aManifestURL).get());
}

bool
ContentParent::CheckAppHasPermission(const nsAString& aPermission)
{
  return AssertAppHasPermission(this, NS_ConvertUTF16toUTF8(aPermission).get());
}

bool
ContentParent::CheckAppHasStatus(unsigned short aStatus)
{
  return AssertAppHasStatus(this, aStatus);
}

bool
ContentParent::KillChild()
{
  KillHard("KillChild");
  return true;
}

PBlobParent*
ContentParent::SendPBlobConstructor(PBlobParent* aActor,
                                    const BlobConstructorParams& aParams)
{
  return PContentParent::SendPBlobConstructor(aActor, aParams);
}

PBrowserParent*
ContentParent::SendPBrowserConstructor(PBrowserParent* aActor,
                                       const TabId& aTabId,
                                       const IPCTabContext& aContext,
                                       const uint32_t& aChromeFlags,
                                       const ContentParentId& aCpId,
                                       const bool& aIsForApp,
                                       const bool& aIsForBrowser)
{
  return PContentParent::SendPBrowserConstructor(aActor,
                                                 aTabId,
                                                 aContext,
                                                 aChromeFlags,
                                                 aCpId,
                                                 aIsForApp,
                                                 aIsForBrowser);
}

bool
ContentParent::RecvCreateFakeVolume(const nsString& fsName,
                                    const nsString& mountPoint)
{
#ifdef MOZ_WIDGET_GONK
  nsresult rv;
  nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID, &rv);
  if (vs) {
    vs->CreateFakeVolume(fsName, mountPoint);
  }
  return true;
#else
  NS_WARNING("ContentParent::RecvCreateFakeVolume shouldn't be called when MOZ_WIDGET_GONK is not defined");
  return false;
#endif
}

bool
ContentParent::RecvSetFakeVolumeState(const nsString& fsName, const int32_t& fsState)
{
#ifdef MOZ_WIDGET_GONK
  nsresult rv;
  nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID, &rv);
  if (vs) {
    vs->SetFakeVolumeState(fsName, fsState);
  }
  return true;
#else
  NS_WARNING("ContentParent::RecvSetFakeVolumeState shouldn't be called when MOZ_WIDGET_GONK is not defined");
  return false;
#endif
}

bool
ContentParent::RecvRemoveFakeVolume(const nsString& fsName)
{
#ifdef MOZ_WIDGET_GONK
  nsresult rv;
  nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID, &rv);
  if (vs) {
    vs->RemoveFakeVolume(fsName);
  }
  return true;
#else
  NS_WARNING("ContentParent::RecvRemoveFakeVolume shouldn't be called when MOZ_WIDGET_GONK is not defined");
  return false;
#endif
}

bool
ContentParent::RecvKeywordToURI(const nsCString& aKeyword,
                                nsString* aProviderName,
                                OptionalInputStreamParams* aPostData,
                                OptionalURIParams* aURI)
{
  *aPostData = void_t();
  *aURI = void_t();

  nsCOMPtr<nsIURIFixup> fixup = do_GetService(NS_URIFIXUP_CONTRACTID);
  if (!fixup) {
    return true;
  }

  nsCOMPtr<nsIInputStream> postData;
  nsCOMPtr<nsIURIFixupInfo> info;

  if (NS_FAILED(fixup->KeywordToURI(aKeyword, getter_AddRefs(postData),
                                    getter_AddRefs(info)))) {
    return true;
  }
  info->GetKeywordProviderName(*aProviderName);

  nsTArray<mozilla::ipc::FileDescriptor> fds;
  SerializeInputStream(postData, *aPostData, fds);
  MOZ_ASSERT(fds.IsEmpty());

  nsCOMPtr<nsIURI> uri;
  info->GetPreferredURI(getter_AddRefs(uri));
  SerializeURI(uri, *aURI);
  return true;
}

bool
ContentParent::RecvNotifyKeywordSearchLoading(const nsString &aProvider,
                                              const nsString &aKeyword)
{
#ifdef MOZ_TOOLKIT_SEARCH
  nsCOMPtr<nsIBrowserSearchService> searchSvc = do_GetService("@mozilla.org/browser/search-service;1");
  if (searchSvc) {
    nsCOMPtr<nsISearchEngine> searchEngine;
    searchSvc->GetEngineByName(aProvider, getter_AddRefs(searchEngine));
    if (searchEngine) {
      nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
      if (obsSvc) {
        // Note that "keyword-search" refers to a search via the url
        // bar, not a bookmarks keyword search.
        obsSvc->NotifyObservers(searchEngine, "keyword-search", aKeyword.get());
      }
    }
  }
#endif
  return true;
}

bool
ContentParent::RecvCopyFavicon(const URIParams& aOldURI,
                               const URIParams& aNewURI,
                               const IPC::Principal& aLoadingPrincipal,
                               const bool& aInPrivateBrowsing)
{
  nsCOMPtr<nsIURI> oldURI = DeserializeURI(aOldURI);
  if (!oldURI) {
    return true;
  }
  nsCOMPtr<nsIURI> newURI = DeserializeURI(aNewURI);
  if (!newURI) {
    return true;
  }

  nsDocShell::CopyFavicon(oldURI, newURI, aLoadingPrincipal, aInPrivateBrowsing);
  return true;
}

bool
ContentParent::ShouldContinueFromReplyTimeout()
{
  RefPtr<ProcessHangMonitor> monitor = ProcessHangMonitor::Get();
  return !monitor || !monitor->ShouldTimeOutCPOWs();
}

bool
ContentParent::RecvRecordingDeviceEvents(const nsString& aRecordingStatus,
                                         const nsString& aPageURL,
                                         const bool& aIsAudio,
                                         const bool& aIsVideo)
{
  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
  if (obs) {
    // recording-device-ipc-events needs to gather more information from content process
    RefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();
    props->SetPropertyAsUint64(NS_LITERAL_STRING("childID"), ChildID());
    props->SetPropertyAsBool(NS_LITERAL_STRING("isApp"), IsForApp());
    props->SetPropertyAsBool(NS_LITERAL_STRING("isAudio"), aIsAudio);
    props->SetPropertyAsBool(NS_LITERAL_STRING("isVideo"), aIsVideo);

    nsString requestURL = IsForApp() ? AppManifestURL() : aPageURL;
    props->SetPropertyAsAString(NS_LITERAL_STRING("requestURL"), requestURL);

    obs->NotifyObservers((nsIPropertyBag2*) props,
                         "recording-device-ipc-events",
                         aRecordingStatus.get());
  } else {
    NS_WARNING("Could not get the Observer service for ContentParent::RecvRecordingDeviceEvents.");
  }
  return true;
}

bool
ContentParent::RecvGetGraphicsFeatureStatus(const int32_t& aFeature,
                                            int32_t* aStatus,
                                            nsCString* aFailureId,
                                            bool* aSuccess)
{
  nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
  if (!gfxInfo) {
    *aSuccess = false;
    return true;
  }

  *aSuccess = NS_SUCCEEDED(gfxInfo->GetFeatureStatus(aFeature, *aFailureId, aStatus));
  return true;
}

bool
ContentParent::RecvAddIdleObserver(const uint64_t& aObserver,
                                   const uint32_t& aIdleTimeInS)
{
  nsresult rv;
  nsCOMPtr<nsIIdleService> idleService =
    do_GetService("@mozilla.org/widget/idleservice;1", &rv);
  NS_ENSURE_SUCCESS(rv, false);

  RefPtr<ParentIdleListener> listener =
    new ParentIdleListener(this, aObserver, aIdleTimeInS);
  rv = idleService->AddIdleObserver(listener, aIdleTimeInS);
  NS_ENSURE_SUCCESS(rv, false);
  mIdleListeners.AppendElement(listener);
  return true;
}

bool
ContentParent::RecvRemoveIdleObserver(const uint64_t& aObserver,
                                      const uint32_t& aIdleTimeInS)
{
  RefPtr<ParentIdleListener> listener;
  for (int32_t i = mIdleListeners.Length() - 1; i >= 0; --i) {
    listener = static_cast<ParentIdleListener*>(mIdleListeners[i].get());
    if (listener->mObserver == aObserver &&
      listener->mTime == aIdleTimeInS) {
      nsresult rv;
      nsCOMPtr<nsIIdleService> idleService =
        do_GetService("@mozilla.org/widget/idleservice;1", &rv);
      NS_ENSURE_SUCCESS(rv, false);
      idleService->RemoveIdleObserver(listener, aIdleTimeInS);
      mIdleListeners.RemoveElementAt(i);
      break;
    }
  }
  return true;
}

bool
ContentParent::RecvBackUpXResources(const FileDescriptor& aXSocketFd)
{
#ifndef MOZ_X11
  NS_RUNTIMEABORT("This message only makes sense on X11 platforms");
#else
  MOZ_ASSERT(0 > mChildXSocketFdDup.get(),
             "Already backed up X resources??");
  if (aXSocketFd.IsValid()) {
    auto rawFD = aXSocketFd.ClonePlatformHandle();
    mChildXSocketFdDup.reset(rawFD.release());
  }
#endif
  return true;
}

bool
ContentParent::RecvOpenAnonymousTemporaryFile(FileDescOrError *aFD)
{
  PRFileDesc *prfd;
  nsresult rv = NS_OpenAnonymousTemporaryFile(&prfd);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    // Returning false will kill the child process; instead
    // propagate the error and let the child handle it.
    *aFD = rv;
    return true;
  }
  *aFD = FileDescriptor(FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(prfd)));
  // The FileDescriptor object owns a duplicate of the file handle; we
  // must close the original (and clean up the NSPR descriptor).
  PR_Close(prfd);
  return true;
}

static NS_DEFINE_CID(kFormProcessorCID, NS_FORMPROCESSOR_CID);

bool
ContentParent::RecvKeygenProcessValue(const nsString& oldValue,
                                      const nsString& challenge,
                                      const nsString& keytype,
                                      const nsString& keyparams,
                                      nsString* newValue)
{
  nsCOMPtr<nsIFormProcessor> formProcessor =
    do_GetService(kFormProcessorCID);
  if (!formProcessor) {
    newValue->Truncate();
    return true;
  }

  formProcessor->ProcessValueIPC(oldValue, challenge, keytype, keyparams,
                                 *newValue);
  return true;
}

bool
ContentParent::RecvKeygenProvideContent(nsString* aAttribute,
                                        nsTArray<nsString>* aContent)
{
  nsCOMPtr<nsIFormProcessor> formProcessor =
    do_GetService(kFormProcessorCID);
  if (!formProcessor) {
    return true;
  }

  formProcessor->ProvideContent(NS_LITERAL_STRING("SELECT"), *aContent,
                                *aAttribute);
  return true;
}

PFileDescriptorSetParent*
ContentParent::AllocPFileDescriptorSetParent(const FileDescriptor& aFD)
{
  return nsIContentParent::AllocPFileDescriptorSetParent(aFD);
}

bool
ContentParent::DeallocPFileDescriptorSetParent(PFileDescriptorSetParent* aActor)
{
  return nsIContentParent::DeallocPFileDescriptorSetParent(aActor);
}

bool
ContentParent::IgnoreIPCPrincipal()
{
  static bool sDidAddVarCache = false;
  static bool sIgnoreIPCPrincipal = false;
  if (!sDidAddVarCache) {
    sDidAddVarCache = true;
    Preferences::AddBoolVarCache(&sIgnoreIPCPrincipal,
                                 "dom.testing.ignore_ipc_principal", false);
  }
  return sIgnoreIPCPrincipal;
}

void
ContentParent::NotifyUpdatedDictionaries()
{
  nsCOMPtr<nsISpellChecker> spellChecker(do_GetService(NS_SPELLCHECKER_CONTRACTID));
  MOZ_ASSERT(spellChecker, "No spell checker?");

  InfallibleTArray<nsString> dictionaries;
  spellChecker->GetDictionaryList(&dictionaries);

  for (auto* cp : AllProcesses(eLive)) {
    Unused << cp->SendUpdateDictionaryList(dictionaries);
  }
}

/*static*/ TabId
ContentParent::AllocateTabId(const TabId& aOpenerTabId,
                             const IPCTabContext& aContext,
                             const ContentParentId& aCpId)
{
  TabId tabId;
  if (XRE_IsParentProcess()) {
    ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
    tabId = cpm->AllocateTabId(aOpenerTabId, aContext, aCpId);
  }
  else {
    ContentChild::GetSingleton()->SendAllocateTabId(aOpenerTabId,
                                                      aContext,
                                                      aCpId,
                                                      &tabId);
  }
  return tabId;
}

/*static*/ void
ContentParent::DeallocateTabId(const TabId& aTabId,
                               const ContentParentId& aCpId,
                               bool aMarkedDestroying)
{
  if (XRE_IsParentProcess()) {
    ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
    ContentParent* cp = cpm->GetContentProcessById(aCpId);

    cp->NotifyTabDestroyed(aTabId, aMarkedDestroying);

    ContentProcessManager::GetSingleton()->DeallocateTabId(aCpId, aTabId);
  } else {
    ContentChild::GetSingleton()->SendDeallocateTabId(aTabId, aCpId,
                                                      aMarkedDestroying);
  }
}

bool
ContentParent::RecvAllocateTabId(const TabId& aOpenerTabId,
                                 const IPCTabContext& aContext,
                                 const ContentParentId& aCpId,
                                 TabId* aTabId)
{
  *aTabId = AllocateTabId(aOpenerTabId, aContext, aCpId);
  if (!(*aTabId)) {
    return false;
  }
  return true;
}

bool
ContentParent::RecvDeallocateTabId(const TabId& aTabId,
                                   const ContentParentId& aCpId,
                                   const bool& aMarkedDestroying)
{
  DeallocateTabId(aTabId, aCpId, aMarkedDestroying);
  return true;
}

bool
ContentParent::RecvNotifyTabDestroying(const TabId& aTabId,
                                       const ContentParentId& aCpId)
{
  NotifyTabDestroying(aTabId, aCpId);
  return true;
}

nsTArray<TabContext>
ContentParent::GetManagedTabContext()
{
  return Move(ContentProcessManager::GetSingleton()->
          GetTabContextByContentProcess(this->ChildID()));
}

mozilla::docshell::POfflineCacheUpdateParent*
ContentParent::AllocPOfflineCacheUpdateParent(const URIParams& aManifestURI,
                                              const URIParams& aDocumentURI,
                                              const PrincipalInfo& aLoadingPrincipalInfo,
                                              const bool& aStickDocument)
{
  RefPtr<mozilla::docshell::OfflineCacheUpdateParent> update =
        new mozilla::docshell::OfflineCacheUpdateParent();
  // Use this reference as the IPDL reference.
  return update.forget().take();
}

bool
ContentParent::RecvPOfflineCacheUpdateConstructor(POfflineCacheUpdateParent* aActor,
                                                  const URIParams& aManifestURI,
                                                  const URIParams& aDocumentURI,
                                                  const PrincipalInfo& aLoadingPrincipal,
                                                  const bool& aStickDocument)
{
  MOZ_ASSERT(aActor);

  RefPtr<mozilla::docshell::OfflineCacheUpdateParent> update =
    static_cast<mozilla::docshell::OfflineCacheUpdateParent*>(aActor);

  nsresult rv = update->Schedule(aManifestURI, aDocumentURI, aLoadingPrincipal, aStickDocument);
  if (NS_FAILED(rv) && IsAlive()) {
    // Inform the child of failure.
    Unused << update->SendFinish(false, false);
  }

  return true;
}

bool
ContentParent::DeallocPOfflineCacheUpdateParent(POfflineCacheUpdateParent* aActor)
{
  // Reclaim the IPDL reference.
  RefPtr<mozilla::docshell::OfflineCacheUpdateParent> update =
    dont_AddRef(static_cast<mozilla::docshell::OfflineCacheUpdateParent*>(aActor));
  return true;
}

PWebrtcGlobalParent *
ContentParent::AllocPWebrtcGlobalParent()
{
#ifdef MOZ_WEBRTC
  return WebrtcGlobalParent::Alloc();
#else
  return nullptr;
#endif
}

bool
ContentParent::DeallocPWebrtcGlobalParent(PWebrtcGlobalParent *aActor)
{
#ifdef MOZ_WEBRTC
  WebrtcGlobalParent::Dealloc(static_cast<WebrtcGlobalParent*>(aActor));
  return true;
#else
  return false;
#endif
}

bool
ContentParent::RecvSetOfflinePermission(const Principal& aPrincipal)
{
  nsIPrincipal* principal = aPrincipal;
  nsContentUtils::MaybeAllowOfflineAppByDefault(principal);
  return true;
}

void
ContentParent::MaybeInvokeDragSession(TabParent* aParent)
{
  nsCOMPtr<nsIDragService> dragService =
    do_GetService("@mozilla.org/widget/dragservice;1");
  if (dragService && dragService->MaybeAddChildProcess(this)) {
    // We need to send transferable data to child process.
    nsCOMPtr<nsIDragSession> session;
    dragService->GetCurrentSession(getter_AddRefs(session));
    if (session) {
      nsTArray<IPCDataTransfer> dataTransfers;
      nsCOMPtr<nsIDOMDataTransfer> domTransfer;
      session->GetDataTransfer(getter_AddRefs(domTransfer));
      nsCOMPtr<DataTransfer> transfer = do_QueryInterface(domTransfer);
      if (!transfer) {
        // Pass eDrop to get DataTransfer with external
        // drag formats cached.
        transfer = new DataTransfer(nullptr, eDrop, true, -1);
        session->SetDataTransfer(transfer);
      }
      // Note, even though this fills the DataTransfer object with
      // external data, the data is usually transfered over IPC lazily when
      // needed.
      transfer->FillAllExternalData();
      nsCOMPtr<nsILoadContext> lc = aParent ?
                                     aParent->GetLoadContext() : nullptr;
      nsCOMPtr<nsIArray> transferables =
        transfer->GetTransferables(lc);
      nsContentUtils::TransferablesToIPCTransferables(transferables,
                                                      dataTransfers,
                                                      false,
                                                      nullptr,
                                                      this);
      uint32_t action;
      session->GetDragAction(&action);
      mozilla::Unused << SendInvokeDragSession(dataTransfers, action);
    }
  }
}

bool
ContentParent::RecvUpdateDropEffect(const uint32_t& aDragAction,
                                    const uint32_t& aDropEffect)
{
  nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
  if (dragSession) {
    dragSession->SetDragAction(aDragAction);
    nsCOMPtr<nsIDOMDataTransfer> dt;
    dragSession->GetDataTransfer(getter_AddRefs(dt));
    if (dt) {
      dt->SetDropEffectInt(aDropEffect);
    }
    dragSession->UpdateDragEffect();
  }
  return true;
}

PContentPermissionRequestParent*
ContentParent::AllocPContentPermissionRequestParent(const InfallibleTArray<PermissionRequest>& aRequests,
                                                    const IPC::Principal& aPrincipal,
                                                    const TabId& aTabId)
{
  ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
  RefPtr<TabParent> tp =
    cpm->GetTopLevelTabParentByProcessAndTabId(this->ChildID(), aTabId);
  if (!tp) {
    return nullptr;
  }

  return nsContentPermissionUtils::CreateContentPermissionRequestParent(aRequests,
                                                                        tp->GetOwnerElement(),
                                                                        aPrincipal,
                                                                        aTabId);
}

bool
ContentParent::DeallocPContentPermissionRequestParent(PContentPermissionRequestParent* actor)
{
  nsContentPermissionUtils::NotifyRemoveContentPermissionRequestParent(actor);
  delete actor;
  return true;
}

PWebBrowserPersistDocumentParent*
ContentParent::AllocPWebBrowserPersistDocumentParent(PBrowserParent* aBrowser,
                                                     const uint64_t& aOuterWindowID)
{
  return new WebBrowserPersistDocumentParent();
}

bool
ContentParent::DeallocPWebBrowserPersistDocumentParent(PWebBrowserPersistDocumentParent* aActor)
{
  delete aActor;
  return true;
}

bool
ContentParent::RecvCreateWindow(PBrowserParent* aThisTab,
                                PBrowserParent* aNewTab,
                                PRenderFrameParent* aRenderFrame,
                                const uint32_t& aChromeFlags,
                                const bool& aCalledFromJS,
                                const bool& aPositionSpecified,
                                const bool& aSizeSpecified,
                                const nsCString& aFeatures,
                                const nsCString& aBaseURI,
                                const DocShellOriginAttributes& aOpenerOriginAttributes,
                                const float& aFullZoom,
                                nsresult* aResult,
                                bool* aWindowIsNew,
                                InfallibleTArray<FrameScriptInfo>* aFrameScripts,
                                nsCString* aURLToLoad,
                                TextureFactoryIdentifier* aTextureFactoryIdentifier,
                                uint64_t* aLayersId)
{
  // We always expect to open a new window here. If we don't, it's an error.
  *aWindowIsNew = true;
  *aResult = NS_OK;

  // The content process should never be in charge of computing whether or
  // not a window should be private or remote - the parent will do that.
  const uint32_t badFlags =
        nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW
      | nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW
      | nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME
      | nsIWebBrowserChrome::CHROME_REMOTE_WINDOW;
  if (!!(aChromeFlags & badFlags)) {
      return false;
  }

  TabParent* thisTabParent = nullptr;
  if (aThisTab) {
    thisTabParent = TabParent::GetFrom(aThisTab);
  }

  if (NS_WARN_IF(thisTabParent && thisTabParent->IsMozBrowserOrApp())) {
    return false;
  }

  TabParent* newTab = TabParent::GetFrom(aNewTab);
  MOZ_ASSERT(newTab);

  auto destroyNewTabOnError = MakeScopeExit([&] {
    if (!*aWindowIsNew || NS_FAILED(*aResult)) {
      if (newTab) {
        newTab->Destroy();
      }
    }
  });

  // Content has requested that we open this new content window, so
  // we must have an opener.
  newTab->SetHasContentOpener(true);

  nsCOMPtr<nsIContent> frame;
  if (thisTabParent) {
    frame = do_QueryInterface(thisTabParent->GetOwnerElement());
  }

  nsCOMPtr<nsPIDOMWindowOuter> parent;
  if (frame) {
    parent = frame->OwnerDoc()->GetWindow();

    // If our chrome window is in the process of closing, don't try to open a
    // new tab in it.
    if (parent && parent->Closed()) {
      parent = nullptr;
    }
  }

  nsCOMPtr<nsIBrowserDOMWindow> browserDOMWin;
  if (thisTabParent) {
    browserDOMWin = thisTabParent->GetBrowserDOMWindow();
  }

  // If we haven't found a chrome window to open in, just use the most recently
  // opened one.
  if (!parent) {
    parent = nsContentUtils::GetMostRecentNonPBWindow();
    if (NS_WARN_IF(!parent)) {
      *aResult = NS_ERROR_FAILURE;
      return true;
    }

    nsCOMPtr<nsIDOMChromeWindow> rootChromeWin = do_QueryInterface(parent);
    if (rootChromeWin) {
      rootChromeWin->GetBrowserDOMWindow(getter_AddRefs(browserDOMWin));
    }
  }

  int32_t openLocation =
    nsWindowWatcher::GetWindowOpenLocation(parent, aChromeFlags, aCalledFromJS,
                                           aPositionSpecified, aSizeSpecified);

  MOZ_ASSERT(openLocation == nsIBrowserDOMWindow::OPEN_NEWTAB ||
             openLocation == nsIBrowserDOMWindow::OPEN_NEWWINDOW);

  // Opening new tabs is the easy case...
  if (openLocation == nsIBrowserDOMWindow::OPEN_NEWTAB) {
    if (NS_WARN_IF(!browserDOMWin)) {
      *aResult = NS_ERROR_ABORT;
      return true;
    }

    bool isPrivate = false;
    if (thisTabParent) {
      nsCOMPtr<nsILoadContext> loadContext = thisTabParent->GetLoadContext();
      loadContext->GetUsePrivateBrowsing(&isPrivate);
    }

    nsCOMPtr<nsIOpenURIInFrameParams> params =
      new nsOpenURIInFrameParams(aOpenerOriginAttributes);
    params->SetReferrer(NS_ConvertUTF8toUTF16(aBaseURI));
    params->SetIsPrivate(isPrivate);

    TabParent::AutoUseNewTab aunt(newTab, aWindowIsNew, aURLToLoad);

    nsCOMPtr<nsIFrameLoaderOwner> frameLoaderOwner;
    browserDOMWin->OpenURIInFrame(nullptr, params,
                                  openLocation,
                                  nsIBrowserDOMWindow::OPEN_NEW,
                                  getter_AddRefs(frameLoaderOwner));
    if (!frameLoaderOwner) {
      *aWindowIsNew = false;
    }

    newTab->SwapFrameScriptsFrom(*aFrameScripts);

    RenderFrameParent* rfp = static_cast<RenderFrameParent*>(aRenderFrame);
    if (!newTab->SetRenderFrame(rfp) ||
        !newTab->GetRenderFrameInfo(aTextureFactoryIdentifier, aLayersId)) {
      *aResult = NS_ERROR_FAILURE;
    }

    return true;
  }

  TabParent::AutoUseNewTab aunt(newTab, aWindowIsNew, aURLToLoad);

  nsCOMPtr<nsPIWindowWatcher> pwwatch =
    do_GetService(NS_WINDOWWATCHER_CONTRACTID, aResult);

  if (NS_WARN_IF(NS_FAILED(*aResult))) {
    return true;
  }

  nsCOMPtr<nsITabParent> newRemoteTab;
  if (!thisTabParent) {
    // Because we weren't passed an opener tab, the content process has asked us
    // to open a new window that is unrelated to a pre-existing tab.
    *aResult = pwwatch->OpenWindowWithoutParent(getter_AddRefs(newRemoteTab));
  } else {
    *aResult = pwwatch->OpenWindowWithTabParent(thisTabParent, aFeatures, aCalledFromJS,
                                                aFullZoom, getter_AddRefs(newRemoteTab));
  }

  if (NS_WARN_IF(NS_FAILED(*aResult))) {
    return true;
  }

  MOZ_ASSERT(TabParent::GetFrom(newRemoteTab) == newTab);

  newTab->SwapFrameScriptsFrom(*aFrameScripts);

  RenderFrameParent* rfp = static_cast<RenderFrameParent*>(aRenderFrame);
  if (!newTab->SetRenderFrame(rfp) ||
      !newTab->GetRenderFrameInfo(aTextureFactoryIdentifier, aLayersId)) {
    *aResult = NS_ERROR_FAILURE;
  }

  return true;
}

bool
ContentParent::RecvProfile(const nsCString& aProfile)
{
#ifdef MOZ_ENABLE_PROFILER_SPS
  if (NS_WARN_IF(!mGatherer)) {
    return true;
  }
  mProfile = aProfile;
  mGatherer->GatheredOOPProfile();
#endif
  return true;
}

bool
ContentParent::RecvGetGraphicsDeviceInitData(ContentDeviceData* aOut)
{
  gfxPlatform::GetPlatform()->BuildContentDeviceData(aOut);
  return true;
}

bool
ContentParent::RecvGraphicsError(const nsCString& aError)
{
  gfx::LogForwarder* lf = gfx::Factory::GetLogForwarder();
  if (lf) {
    std::stringstream message;
    message << "CP+" << aError.get();
    lf->UpdateStringsVector(message.str());
  }
  return true;
}

bool
ContentParent::RecvBeginDriverCrashGuard(const uint32_t& aGuardType, bool* aOutCrashed)
{
  // Only one driver crash guard should be active at a time, per-process.
  MOZ_ASSERT(!mDriverCrashGuard);

  UniquePtr<gfx::DriverCrashGuard> guard;
  switch (gfx::CrashGuardType(aGuardType)) {
  case gfx::CrashGuardType::D3D11Layers:
    guard = MakeUnique<gfx::D3D11LayersCrashGuard>(this);
    break;
  case gfx::CrashGuardType::D3D9Video:
    guard = MakeUnique<gfx::D3D9VideoCrashGuard>(this);
    break;
  case gfx::CrashGuardType::GLContext:
    guard = MakeUnique<gfx::GLContextCrashGuard>(this);
    break;
  case gfx::CrashGuardType::D3D11Video:
    guard = MakeUnique<gfx::D3D11VideoCrashGuard>(this);
    break;
  default:
    MOZ_ASSERT_UNREACHABLE("unknown crash guard type");
    return false;
  }

  if (guard->Crashed()) {
    *aOutCrashed = true;
    return true;
  }

  *aOutCrashed = false;
  mDriverCrashGuard = Move(guard);
  return true;
}

bool
ContentParent::RecvEndDriverCrashGuard(const uint32_t& aGuardType)
{
  mDriverCrashGuard = nullptr;
  return true;
}

bool
ContentParent::RecvGetAndroidSystemInfo(AndroidSystemInfo* aInfo)
{
#ifdef MOZ_WIDGET_ANDROID
  nsSystemInfo::GetAndroidSystemInfo(aInfo);
  return true;
#else
  MOZ_CRASH("wrong platform!");
  return false;
#endif
}

bool
ContentParent::RecvNotifyBenchmarkResult(const nsString& aCodecName,
                                         const uint32_t& aDecodeFPS)

{
  if (aCodecName.EqualsLiteral("VP9")) {
    Preferences::SetUint(VP9Benchmark::sBenchmarkFpsPref, aDecodeFPS);
    Preferences::SetUint(VP9Benchmark::sBenchmarkFpsVersionCheck,
                         VP9Benchmark::sBenchmarkVersionID);
  }
  return true;
}

void
ContentParent::StartProfiler(nsIProfilerStartParams* aParams)
{
#ifdef MOZ_ENABLE_PROFILER_SPS
  if (NS_WARN_IF(!aParams)) {
    return;
  }

  ProfilerInitParams ipcParams;

  ipcParams.enabled() = true;
  aParams->GetEntries(&ipcParams.entries());
  aParams->GetInterval(&ipcParams.interval());
  ipcParams.features() = aParams->GetFeatures();
  ipcParams.threadFilters() = aParams->GetThreadFilterNames();

  Unused << SendStartProfiler(ipcParams);

  nsCOMPtr<nsIProfiler> profiler(do_GetService("@mozilla.org/tools/profiler;1"));
  if (NS_WARN_IF(!profiler)) {
    return;
  }
  nsCOMPtr<nsISupports> gatherer;
  profiler->GetProfileGatherer(getter_AddRefs(gatherer));
  mGatherer = static_cast<ProfileGatherer*>(gatherer.get());
#endif
}

bool
ContentParent::RecvNotifyPushObservers(const nsCString& aScope,
                                       const IPC::Principal& aPrincipal,
                                       const nsString& aMessageId)
{
  PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId, Nothing());
  Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObservers()));
  return true;
}

bool
ContentParent::RecvNotifyPushObserversWithData(const nsCString& aScope,
                                               const IPC::Principal& aPrincipal,
                                               const nsString& aMessageId,
                                               InfallibleTArray<uint8_t>&& aData)
{
  PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId, Some(aData));
  Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObservers()));
  return true;
}

bool
ContentParent::RecvNotifyPushSubscriptionChangeObservers(const nsCString& aScope,
                                                         const IPC::Principal& aPrincipal)
{
  PushSubscriptionChangeDispatcher dispatcher(aScope, aPrincipal);
  Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObservers()));
  return true;
}

bool
ContentParent::RecvNotifyPushSubscriptionModifiedObservers(const nsCString& aScope,
                                                           const IPC::Principal& aPrincipal)
{
  PushSubscriptionModifiedDispatcher dispatcher(aScope, aPrincipal);
  Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObservers()));
  return true;
}

bool
ContentParent::RecvNotifyLowMemory()
{
  return true;
}

/* static */ void
ContentParent::BroadcastBlobURLRegistration(const nsACString& aURI,
                                            BlobImpl* aBlobImpl,
                                            nsIPrincipal* aPrincipal,
                                            ContentParent* aIgnoreThisCP)
{
  nsCString uri(aURI);
  IPC::Principal principal(aPrincipal);

  for (auto* cp : AllProcesses(eLive)) {
    if (cp != aIgnoreThisCP) {
      PBlobParent* blobParent = cp->GetOrCreateActorForBlobImpl(aBlobImpl);
      if (blobParent) {
        Unused << cp->SendBlobURLRegistration(uri, blobParent, principal);
      }
    }
  }
}

/* static */ void
ContentParent::BroadcastBlobURLUnregistration(const nsACString& aURI,
                                              ContentParent* aIgnoreThisCP)
{
  nsCString uri(aURI);

  for (auto* cp : AllProcesses(eLive)) {
    if (cp != aIgnoreThisCP) {
      Unused << cp->SendBlobURLUnregistration(uri);
    }
  }
}

bool
ContentParent::RecvStoreAndBroadcastBlobURLRegistration(const nsCString& aURI,
                                                        PBlobParent* aBlobParent,
                                                        const Principal& aPrincipal)
{
  RefPtr<BlobImpl> blobImpl =
    static_cast<BlobParent*>(aBlobParent)->GetBlobImpl();
  if (NS_WARN_IF(!blobImpl)) {
    return false;
  }

  if (NS_SUCCEEDED(nsHostObjectProtocolHandler::AddDataEntry(aURI, aPrincipal,
                                                             blobImpl))) {
    BroadcastBlobURLRegistration(aURI, blobImpl, aPrincipal, this);

    // We want to store this blobURL, so we can unregister it if the child
    // crashes.
    mBlobURLs.AppendElement(aURI);
  }

  BroadcastBlobURLRegistration(aURI, blobImpl, aPrincipal, this);
  return true;
}

bool
ContentParent::RecvUnstoreAndBroadcastBlobURLUnregistration(const nsCString& aURI)
{
  nsHostObjectProtocolHandler::RemoveDataEntry(aURI,
                                               false /* Don't broadcast */);
  BroadcastBlobURLUnregistration(aURI, this);
  mBlobURLs.RemoveElement(aURI);

  return true;
}

bool
ContentParent::RecvGetA11yContentId(uint32_t* aContentId)
{
#if defined(XP_WIN32) && defined(ACCESSIBILITY)
  *aContentId = a11y::AccessibleWrap::GetContentProcessIdFor(ChildID());
  MOZ_ASSERT(*aContentId);
  return true;
#else
  return false;
#endif
}

} // namespace dom
} // namespace mozilla

NS_IMPL_ISUPPORTS(ParentIdleListener, nsIObserver)

NS_IMETHODIMP
ParentIdleListener::Observe(nsISupports*, const char* aTopic, const char16_t* aData)
{
  mozilla::Unused << mParent->SendNotifyIdleObserver(mObserver,
                                                     nsDependentCString(aTopic),
                                                     nsDependentString(aData));
  return NS_OK;
}

bool
ContentParent::HandleWindowsMessages(const Message& aMsg) const
{
  MOZ_ASSERT(aMsg.is_sync());

  // a11y messages can be triggered by windows messages, which means if we
  // allow handling windows messages while we wait for the response to a sync
  // a11y message we can reenter the ipc message sending code.
  if (a11y::PDocAccessible::PDocAccessibleStart < aMsg.type() &&
      a11y::PDocAccessible::PDocAccessibleEnd > aMsg.type()) {
    return false;
  }

  return true;
}

bool
ContentParent::RecvGetFilesRequest(const nsID& aUUID,
                                   const nsString& aDirectoryPath,
                                   const bool& aRecursiveFlag)
{
  MOZ_ASSERT(!mGetFilesPendingRequests.GetWeak(aUUID));

  ErrorResult rv;
  RefPtr<GetFilesHelper> helper =
    GetFilesHelperParent::Create(aUUID, aDirectoryPath, aRecursiveFlag, this,
                                 rv);

  if (NS_WARN_IF(rv.Failed())) {
    return SendGetFilesResponse(aUUID,
                                GetFilesResponseFailure(rv.StealNSResult()));
  }

  mGetFilesPendingRequests.Put(aUUID, helper);
  return true;
}

bool
ContentParent::RecvDeleteGetFilesRequest(const nsID& aUUID)
{
  GetFilesHelper* helper = mGetFilesPendingRequests.GetWeak(aUUID);
  if (helper) {
    mGetFilesPendingRequests.Remove(aUUID);
  }

  return true;
}

void
ContentParent::SendGetFilesResponseAndForget(const nsID& aUUID,
                                             const GetFilesResponseResult& aResult)
{
  GetFilesHelper* helper = mGetFilesPendingRequests.GetWeak(aUUID);
  if (helper) {
    mGetFilesPendingRequests.Remove(aUUID);
    Unused << SendGetFilesResponse(aUUID, aResult);
  }
}

void
ContentParent::ForceTabPaint(TabParent* aTabParent, uint64_t aLayerObserverEpoch)
{
  if (!mHangMonitorActor) {
    return;
  }
  ProcessHangMonitor::ForcePaint(mHangMonitorActor, aTabParent, aLayerObserverEpoch);
}

bool
ContentParent::RecvAccumulateChildHistogram(
                InfallibleTArray<Accumulation>&& aAccumulations)
{
  Telemetry::AccumulateChild(GeckoProcessType_Content, aAccumulations);
  return true;
}

bool
ContentParent::RecvAccumulateChildKeyedHistogram(
                InfallibleTArray<KeyedAccumulation>&& aAccumulations)
{
  Telemetry::AccumulateChildKeyed(GeckoProcessType_Content, aAccumulations);
  return true;
}