dom/ipc/ContentChild.cpp
author Nathan Froyd <froydnj@mozilla.com>
Wed, 02 Jan 2019 16:18:13 -0500
changeset 509416 7b71897ff6be49225613180bac536673a03bb7c0
parent 509304 d96e39ed615b40b9b1ccb661f72b29c81b34fe34
child 509418 d3d32e312a50622e2a0cdb7ac1afc1170ac60154
permissions -rw-r--r--
Bug 1517276 - thread callbacks through MemoryReportRequestClient; r=mccr8 The guts of MemoryReportRequestClient's supporting runnables contain switches on the particular type of process we're running. If you're bringing up a new process type, having to add extra cases for your process type here is a bit onerous. These runnables really shouldn't know anything about the process types that they're running on, either. The easiest thing to do is modify MemoryReportRequestClient::Start to take callbacks for what to do when a report is created and when reporting is finished. Then all process-specific knowledge can be pushed out to the clients themselves, leaving MemoryReportRequestClient and friends process-type agnostic. We could even, at some later date, move this code into xpcom/base/ to sit near nsMemoryReporterManager, where it belongs.

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

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

#include "ContentChild.h"

#include "GeckoProfiler.h"
#include "TabChild.h"
#include "HandlerServiceChild.h"

#include "mozilla/Attributes.h"
#include "mozilla/BackgroundHangMonitor.h"
#include "mozilla/LookAndFeel.h"
#include "mozilla/MemoryTelemetry.h"
#include "mozilla/NullPrincipal.h"
#include "mozilla/Preferences.h"
#include "mozilla/ProcessHangMonitorIPC.h"
#include "mozilla/RemoteDecoderManagerChild.h"
#include "mozilla/Unused.h"
#include "mozilla/StaticPrefs.h"
#include "mozilla/TelemetryIPC.h"
#include "mozilla/VideoDecoderManagerChild.h"
#include "mozilla/devtools/HeapSnapshotTempFileHelperChild.h"
#include "mozilla/docshell/OfflineCacheUpdateChild.h"
#include "mozilla/dom/ClientManager.h"
#include "mozilla/dom/ClientOpenWindowOpActors.h"
#include "mozilla/dom/ChildProcessMessageManager.h"
#include "mozilla/dom/ContentBridgeChild.h"
#include "mozilla/dom/ContentBridgeParent.h"
#include "mozilla/dom/ContentProcessMessageManager.h"
#include "mozilla/dom/DOMPrefs.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/DataTransfer.h"
#include "mozilla/dom/DocGroup.h"
#include "mozilla/dom/ExternalHelperAppChild.h"
#include "mozilla/dom/FileCreatorHelper.h"
#include "mozilla/dom/GetFilesHelper.h"
#include "mozilla/dom/IPCBlobUtils.h"
#include "mozilla/dom/LSObject.h"
#include "mozilla/dom/MemoryReportRequest.h"
#include "mozilla/dom/PLoginReputationChild.h"
#include "mozilla/dom/PushNotifier.h"
#include "mozilla/dom/RemoteWorkerService.h"
#include "mozilla/dom/ServiceWorkerManager.h"
#include "mozilla/dom/TabGroup.h"
#include "mozilla/dom/nsIContentChild.h"
#include "mozilla/dom/URLClassifierChild.h"
#include "mozilla/dom/WorkerDebugger.h"
#include "mozilla/dom/WorkerDebuggerManager.h"
#include "mozilla/dom/ipc/SharedMap.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/gfx/Logging.h"
#include "mozilla/psm/PSMContentListener.h"
#include "mozilla/hal_sandbox/PHalChild.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/FileDescriptorSetChild.h"
#include "mozilla/ipc/FileDescriptorUtils.h"
#include "mozilla/ipc/GeckoChildProcessHost.h"
#include "mozilla/ipc/ProcessChild.h"
#include "mozilla/ipc/PChildToParentStreamChild.h"
#include "mozilla/intl/LocaleService.h"
#include "mozilla/ipc/TestShellChild.h"
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
#include "mozilla/jsipc/PJavaScript.h"
#include "mozilla/layers/APZChild.h"
#include "mozilla/layers/CompositorManagerChild.h"
#include "mozilla/layers/ContentProcessController.h"
#include "mozilla/layers/ImageBridgeChild.h"
#include "mozilla/loader/ScriptCacheActors.h"
#include "mozilla/net/NeckoChild.h"
#include "mozilla/net/CookieServiceChild.h"
#include "mozilla/net/CaptivePortalService.h"
#include "mozilla/PerformanceMetricsCollector.h"
#include "mozilla/PerformanceUtils.h"
#include "mozilla/plugins/PluginInstanceParent.h"
#include "mozilla/plugins/PluginModuleParent.h"
#include "mozilla/recordreplay/ParentIPC.h"
#include "mozilla/widget/ScreenManager.h"
#include "mozilla/widget/WidgetMessageUtils.h"
#include "nsBaseDragService.h"
#include "mozilla/media/MediaChild.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/WebBrowserPersistDocumentChild.h"
#include "mozilla/HangDetails.h"
#include "imgLoader.h"
#include "GMPServiceChild.h"
#include "nsISimpleEnumerator.h"
#include "nsIStringBundle.h"
#include "nsIWorkerDebuggerManager.h"
#include "nsGeolocation.h"

#if !defined(XP_WIN)
#include "mozilla/Omnijar.h"
#endif

#ifdef MOZ_GECKO_PROFILER
#include "ChildProfilerController.h"
#endif

#if defined(MOZ_CONTENT_SANDBOX)
#include "mozilla/SandboxSettings.h"
#if defined(XP_WIN)
#include "mozilla/sandboxTarget.h"
#elif defined(XP_LINUX)
#include "mozilla/Sandbox.h"
#include "mozilla/SandboxInfo.h"
#include "CubebUtils.h"
#elif defined(XP_MACOSX)
#include "mozilla/Sandbox.h"
#elif defined(__OpenBSD__)
#include <unistd.h>
#endif
#endif

#include "mozilla/Unused.h"

#include "mozInlineSpellChecker.h"
#include "nsDocShell.h"
#include "nsDocShellLoadState.h"
#include "nsIConsoleListener.h"
#include "nsIContentViewer.h"
#include "nsICycleCollectorListener.h"
#include "nsIIdlePeriod.h"
#include "nsIDragService.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsIMemoryReporter.h"
#include "nsIMemoryInfoDumper.h"
#include "nsIMutable.h"
#include "nsIObserverService.h"
#include "nsIScriptSecurityManager.h"
#include "nsMemoryInfoDumper.h"
#include "nsServiceManagerUtils.h"
#include "nsStyleSheetService.h"
#include "nsVariant.h"
#include "nsXULAppAPI.h"
#include "nsIScriptError.h"
#include "nsIConsoleService.h"
#include "nsJSEnvironment.h"
#include "SandboxHal.h"
#include "nsDebugImpl.h"
#include "nsHashPropertyBag.h"
#include "nsLayoutStylesheetCache.h"
#include "nsThreadManager.h"
#include "nsAnonymousTemporaryFile.h"
#include "nsClipboardProxy.h"
#include "nsDirectoryService.h"
#include "nsDirectoryServiceUtils.h"
#include "nsDirectoryServiceDefs.h"
#include "nsContentPermissionHelper.h"
#include "nsPluginHost.h"
#ifdef NS_PRINTING
#include "nsPrintingProxy.h"
#endif
#include "nsWindowMemoryReporter.h"

#include "IHistory.h"
#include "nsNetUtil.h"

#include "base/message_loop.h"
#include "base/process_util.h"
#include "base/task.h"

#include "nsChromeRegistryContent.h"
#include "nsFrameMessageManager.h"

#include "nsIGeolocationProvider.h"
#include "mozilla/dom/PCycleCollectWithLogsChild.h"

#include "nsIScriptSecurityManager.h"
#include "mozilla/dom/BlobURLProtocolHandler.h"

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

#include "nsPermission.h"
#include "nsPermissionManager.h"

#include "PermissionMessageUtils.h"

#if defined(MOZ_WIDGET_ANDROID)
#include "APKOpen.h"
#endif

#ifdef XP_WIN
#include <process.h>
#define getpid _getpid
#include "mozilla/widget/AudioSession.h"
#include "mozilla/audio/AudioNotificationReceiver.h"
#endif

#if defined(XP_MACOSX)
#include "nsMacUtilsImpl.h"
#include <CoreServices/CoreServices.h>
// Info.plist key associated with the developer repo path
#define MAC_DEV_REPO_KEY "MozillaDeveloperRepoPath"
// Info.plist key associated with the developer repo object directory
#define MAC_DEV_OBJ_KEY "MozillaDeveloperObjPath"
#endif /* XP_MACOSX */

#ifdef MOZ_X11
#include "mozilla/X11Util.h"
#endif

#ifdef ACCESSIBILITY
#include "nsAccessibilityService.h"
#ifdef XP_WIN
#include "mozilla/a11y/AccessibleWrap.h"
#endif
#endif

#include "mozilla/dom/File.h"
#include "mozilla/dom/PPresentationChild.h"
#include "mozilla/dom/PresentationIPCService.h"
#include "mozilla/ipc/InputStreamUtils.h"

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

#include "ClearOnShutdown.h"
#include "ProcessUtils.h"
#include "URIUtils.h"
#include "nsContentUtils.h"
#include "nsIPrincipal.h"
#include "DomainPolicy.h"
#include "mozilla/dom/ipc/StructuredCloneData.h"
#include "mozilla/ipc/CrashReporterClient.h"
#include "mozilla/net/NeckoMessageUtils.h"
#include "mozilla/widget/PuppetBidiKeyboard.h"
#include "mozilla/RemoteSpellCheckEngineChild.h"
#include "GMPServiceChild.h"
#include "GfxInfoBase.h"
#include "gfxPlatform.h"
#include "nscore.h"  // for NS_FREE_PERMANENT_DATA
#include "VRManagerChild.h"
#include "private/pprio.h"
#include "nsString.h"
#include "MMPrinter.h"

#ifdef MOZ_WIDGET_GTK
#include "nsAppRunner.h"
#endif

#ifdef MOZ_CODE_COVERAGE
#include "mozilla/CodeCoverageHandler.h"
#endif

using namespace mozilla;
using namespace mozilla::docshell;
using namespace mozilla::dom::ipc;
using namespace mozilla::media;
using namespace mozilla::embedding;
using namespace mozilla::gmp;
using namespace mozilla::hal_sandbox;
using namespace mozilla::ipc;
using namespace mozilla::intl;
using namespace mozilla::layers;
using namespace mozilla::layout;
using namespace mozilla::net;
using namespace mozilla::jsipc;
using namespace mozilla::psm;
using namespace mozilla::widget;
using mozilla::loader::PScriptCacheChild;

namespace mozilla {

namespace dom {

// IPC sender for remote GC/CC logging.
class CycleCollectWithLogsChild final : public PCycleCollectWithLogsChild {
 public:
  NS_INLINE_DECL_REFCOUNTING(CycleCollectWithLogsChild)

  class Sink final : public nsICycleCollectorLogSink {
    NS_DECL_ISUPPORTS

    Sink(CycleCollectWithLogsChild* aActor, const FileDescriptor& aGCLog,
         const FileDescriptor& aCCLog) {
      mActor = aActor;
      mGCLog = FileDescriptorToFILE(aGCLog, "w");
      mCCLog = FileDescriptorToFILE(aCCLog, "w");
    }

    NS_IMETHOD Open(FILE** aGCLog, FILE** aCCLog) override {
      if (NS_WARN_IF(!mGCLog) || NS_WARN_IF(!mCCLog)) {
        return NS_ERROR_FAILURE;
      }
      *aGCLog = mGCLog;
      *aCCLog = mCCLog;
      return NS_OK;
    }

    NS_IMETHOD CloseGCLog() override {
      MOZ_ASSERT(mGCLog);
      fclose(mGCLog);
      mGCLog = nullptr;
      mActor->SendCloseGCLog();
      return NS_OK;
    }

    NS_IMETHOD CloseCCLog() override {
      MOZ_ASSERT(mCCLog);
      fclose(mCCLog);
      mCCLog = nullptr;
      mActor->SendCloseCCLog();
      return NS_OK;
    }

    NS_IMETHOD GetFilenameIdentifier(nsAString& aIdentifier) override {
      return UnimplementedProperty();
    }

    NS_IMETHOD SetFilenameIdentifier(const nsAString& aIdentifier) override {
      return UnimplementedProperty();
    }

    NS_IMETHOD GetProcessIdentifier(int32_t* aIdentifier) override {
      return UnimplementedProperty();
    }

    NS_IMETHOD SetProcessIdentifier(int32_t aIdentifier) override {
      return UnimplementedProperty();
    }

    NS_IMETHOD GetGcLog(nsIFile** aPath) override {
      return UnimplementedProperty();
    }

    NS_IMETHOD GetCcLog(nsIFile** aPath) override {
      return UnimplementedProperty();
    }

   private:
    ~Sink() {
      if (mGCLog) {
        fclose(mGCLog);
        mGCLog = nullptr;
      }
      if (mCCLog) {
        fclose(mCCLog);
        mCCLog = nullptr;
      }
      // The XPCOM refcount drives the IPC lifecycle;
      Unused << mActor->Send__delete__(mActor);
    }

    nsresult UnimplementedProperty() {
      MOZ_ASSERT(false,
                 "This object is a remote GC/CC logger;"
                 " this property isn't meaningful.");
      return NS_ERROR_UNEXPECTED;
    }

    RefPtr<CycleCollectWithLogsChild> mActor;
    FILE* mGCLog;
    FILE* mCCLog;
  };

 private:
  ~CycleCollectWithLogsChild() {}
};

NS_IMPL_ISUPPORTS(CycleCollectWithLogsChild::Sink, nsICycleCollectorLogSink);

class AlertObserver {
 public:
  AlertObserver(nsIObserver* aObserver, const nsString& aData)
      : mObserver(aObserver), mData(aData) {}

  ~AlertObserver() = default;

  bool ShouldRemoveFrom(nsIObserver* aObserver, const nsString& aData) const {
    return (mObserver == aObserver && mData == aData);
  }

  bool Observes(const nsString& aData) const { return mData.Equals(aData); }

  bool Notify(const nsCString& aType) const {
    mObserver->Observe(nullptr, aType.get(), mData.get());
    return true;
  }

 private:
  nsCOMPtr<nsIObserver> mObserver;
  nsString mData;
};

class ConsoleListener final : public nsIConsoleListener {
 public:
  explicit ConsoleListener(ContentChild* aChild) : mChild(aChild) {}

  NS_DECL_ISUPPORTS
  NS_DECL_NSICONSOLELISTENER

 private:
  ~ConsoleListener() = default;

  ContentChild* mChild;
  friend class ContentChild;
};

NS_IMPL_ISUPPORTS(ConsoleListener, nsIConsoleListener)

// Before we send the error to the parent process (which
// involves copying the memory), truncate any long lines.  CSS
// errors in particular share the memory for long lines with
// repeated errors, but the IPC communication we're about to do
// will break that sharing, so we better truncate now.
static void TruncateString(nsAString& aString) {
  if (aString.Length() > 1000) {
    aString.Truncate(1000);
  }
}

NS_IMETHODIMP
ConsoleListener::Observe(nsIConsoleMessage* aMessage) {
  if (!mChild) {
    return NS_OK;
  }

  nsCOMPtr<nsIScriptError> scriptError = do_QueryInterface(aMessage);
  if (scriptError) {
    nsAutoString msg, sourceName, sourceLine;
    nsCString category;
    uint32_t lineNum, colNum, flags;
    bool fromPrivateWindow;

    nsresult rv = scriptError->GetErrorMessage(msg);
    NS_ENSURE_SUCCESS(rv, rv);
    TruncateString(msg);
    rv = scriptError->GetSourceName(sourceName);
    NS_ENSURE_SUCCESS(rv, rv);
    TruncateString(sourceName);
    rv = scriptError->GetSourceLine(sourceLine);
    NS_ENSURE_SUCCESS(rv, rv);
    TruncateString(sourceLine);

    rv = scriptError->GetCategory(getter_Copies(category));
    NS_ENSURE_SUCCESS(rv, rv);
    rv = scriptError->GetLineNumber(&lineNum);
    NS_ENSURE_SUCCESS(rv, rv);
    rv = scriptError->GetColumnNumber(&colNum);
    NS_ENSURE_SUCCESS(rv, rv);
    rv = scriptError->GetFlags(&flags);
    NS_ENSURE_SUCCESS(rv, rv);
    rv = scriptError->GetIsFromPrivateWindow(&fromPrivateWindow);
    NS_ENSURE_SUCCESS(rv, rv);

    {
      AutoJSAPI jsapi;
      jsapi.Init();
      JSContext* cx = jsapi.cx();

      JS::RootedValue stack(cx);
      rv = scriptError->GetStack(&stack);
      NS_ENSURE_SUCCESS(rv, rv);

      if (stack.isObject()) {
        // Because |stack| might be a cross-compartment wrapper, we can't use it
        // with JSAutoRealm. Use the stackGlobal for that.
        JS::RootedValue stackGlobal(cx);
        rv = scriptError->GetStackGlobal(&stackGlobal);
        NS_ENSURE_SUCCESS(rv, rv);

        JSAutoRealm ar(cx, &stackGlobal.toObject());

        StructuredCloneData data;
        ErrorResult err;
        data.Write(cx, stack, err);
        if (err.Failed()) {
          return err.StealNSResult();
        }

        ClonedMessageData cloned;
        if (!data.BuildClonedMessageDataForChild(mChild, cloned)) {
          return NS_ERROR_FAILURE;
        }

        mChild->SendScriptErrorWithStack(msg, sourceName, sourceLine, lineNum,
                                         colNum, flags, category,
                                         fromPrivateWindow, cloned);
        return NS_OK;
      }
    }

    mChild->SendScriptError(msg, sourceName, sourceLine, lineNum, colNum, flags,
                            category, fromPrivateWindow);
    return NS_OK;
  }

  nsString msg;
  nsresult rv = aMessage->GetMessageMoz(msg);
  NS_ENSURE_SUCCESS(rv, rv);
  mChild->SendConsoleMessage(msg);
  return NS_OK;
}

#ifdef NIGHTLY_BUILD
/**
 * The singleton of this class is registered with the BackgroundHangMonitor as
 * an annotator, so that the hang monitor can record whether or not there were
 * pending input events when the thread hung.
 */
class PendingInputEventHangAnnotator final : public BackgroundHangAnnotator {
 public:
  virtual void AnnotateHang(BackgroundHangAnnotations& aAnnotations) override {
    int32_t pending = ContentChild::GetSingleton()->GetPendingInputEvents();
    if (pending > 0) {
      aAnnotations.AddAnnotation(NS_LITERAL_STRING("PendingInput"), pending);
    }
  }

  static PendingInputEventHangAnnotator sSingleton;
};
PendingInputEventHangAnnotator PendingInputEventHangAnnotator::sSingleton;
#endif

class ContentChild::ShutdownCanary final {};

ContentChild* ContentChild::sSingleton;
StaticAutoPtr<ContentChild::ShutdownCanary> ContentChild::sShutdownCanary;

ContentChild::ContentChild()
    : mID(uint64_t(-1))
#if defined(XP_WIN) && defined(ACCESSIBILITY)
      ,
      mMainChromeTid(0),
      mMsaaID(0)
#endif
      ,
      mIsForBrowser(false),
      mIsAlive(true),
      mShuttingDown(false) {
  // This process is a content process, so it's clearly running in
  // multiprocess mode!
  nsDebugImpl::SetMultiprocessMode("Child");

  // When ContentChild is created, the observer service does not even exist.
  // When ContentChild::RecvSetXPCOMProcessAttributes is called (the first
  // IPDL call made on this object), shutdown may have already happened. Thus
  // we create a canary here that relies upon getting cleared if shutdown
  // happens without requiring the observer service at this time.
  if (!sShutdownCanary) {
    sShutdownCanary = new ShutdownCanary();
    ClearOnShutdown(&sShutdownCanary, ShutdownPhase::Shutdown);
  }
}

#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4722) /* Silence "destructor never returns" warning \
                                 */
#endif

ContentChild::~ContentChild() {
#ifndef NS_FREE_PERMANENT_DATA
  MOZ_CRASH("Content Child shouldn't be destroyed.");
#endif
}

#ifdef _MSC_VER
#pragma warning(pop)
#endif

NS_INTERFACE_MAP_BEGIN(ContentChild)
  NS_INTERFACE_MAP_ENTRY(nsIContentChild)
  NS_INTERFACE_MAP_ENTRY(nsIWindowProvider)
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentChild)
NS_INTERFACE_MAP_END

mozilla::ipc::IPCResult ContentChild::RecvSetXPCOMProcessAttributes(
    const XPCOMInitData& aXPCOMInit, const StructuredCloneData& aInitialData,
    nsTArray<LookAndFeelInt>&& aLookAndFeelIntCache,
    nsTArray<SystemFontListEntry>&& aFontList) {
  if (!sShutdownCanary) {
    return IPC_OK();
  }

  mLookAndFeelCache = std::move(aLookAndFeelIntCache);
  mFontList = std::move(aFontList);
  gfx::gfxVars::SetValuesForInitialize(aXPCOMInit.gfxNonDefaultVarUpdates());
  InitXPCOM(aXPCOMInit, aInitialData);
  InitGraphicsDeviceData(aXPCOMInit.contentDeviceData());

  return IPC_OK();
}

bool ContentChild::Init(MessageLoop* aIOLoop, base::ProcessId aParentPid,
                        const char* aParentBuildID, IPC::Channel* aChannel,
                        uint64_t aChildID, bool aIsForBrowser) {
#ifdef MOZ_WIDGET_GTK
  // When running X11 only build we need to pass a display down
  // to gtk_init because it's not going to use the one from the environment
  // on its own when deciding which backend to use, and when starting under
  // XWayland, it may choose to start with the wayland backend
  // instead of the x11 backend.
  // The DISPLAY environment variable is normally set by the parent process.
  // The MOZ_GDK_DISPLAY environment variable is set from nsAppRunner.cpp
  // when --display is set by the command line.
  if (!gfxPlatform::IsHeadless()) {
    const char* display_name = PR_GetEnv("MOZ_GDK_DISPLAY");
#ifndef MOZ_WAYLAND
    if (!display_name) {
      display_name = PR_GetEnv("DISPLAY");
    }
#endif
    if (display_name) {
      int argc = 3;
      char option_name[] = "--display";
      char* argv[] = {
          // argv0 is unused because g_set_prgname() was called in
          // XRE_InitChildProcess().
          nullptr, option_name, const_cast<char*>(display_name), nullptr};
      char** argvp = argv;
      gtk_init(&argc, &argvp);
    } else {
      gtk_init(nullptr, nullptr);
    }
  }
#endif

#ifdef MOZ_X11
  if (!gfxPlatform::IsHeadless()) {
    // Do this after initializing GDK, or GDK will install its own handler.
    XRE_InstallX11ErrorHandler();
  }
#endif

  NS_ASSERTION(!sSingleton, "only one ContentChild per child");

  // Once we start sending IPC messages, we need the thread manager to be
  // initialized so we can deal with the responses. Do that here before we
  // try to construct the crash reporter.
  nsresult rv = nsThreadManager::get().Init();
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return false;
  }

  // Middleman processes use a special channel for forwarding messages to
  // their own children.
  if (recordreplay::IsMiddleman()) {
    SetMiddlemanIPCChannel(recordreplay::parent::ChannelToUIProcess());
  }

  if (!Open(aChannel, aParentPid, aIOLoop)) {
    return false;
  }
  sSingleton = this;

  // If communications with the parent have broken down, take the process
  // down so it's not hanging around.
  GetIPCChannel()->SetAbortOnError(true);
#if defined(XP_WIN) && defined(ACCESSIBILITY)
  GetIPCChannel()->SetChannelFlags(MessageChannel::REQUIRE_A11Y_REENTRY);
#endif

  // This must be checked before any IPDL message, which may hit sentinel
  // errors due to parent and content processes having different
  // versions.
  MessageChannel* channel = GetIPCChannel();
  if (channel && !channel->SendBuildIDsMatchMessage(aParentBuildID)) {
    // We need to quit this process if the buildID doesn't match the parent's.
    // This can occur when an update occurred in the background.
    ProcessChild::QuickExit();
  }

#ifdef MOZ_X11
  if (!gfxPlatform::IsHeadless()) {
    // Send the parent our X socket to act as a proxy reference for our X
    // resources.
    int xSocketFd = ConnectionNumber(DefaultXDisplay());
    SendBackUpXResources(FileDescriptor(xSocketFd));
  }
#endif

  CrashReporterClient::InitSingleton(this);

  mID = aChildID;
  mIsForBrowser = aIsForBrowser;

#ifdef NS_PRINTING
  // Force the creation of the nsPrintingProxy so that it's IPC counterpart,
  // PrintingParent, is always available for printing initiated from the parent.
  RefPtr<nsPrintingProxy> printingProxy = nsPrintingProxy::GetInstance();
#endif

  SetProcessName(NS_LITERAL_STRING("Web Content"));

#ifdef NIGHTLY_BUILD
  // NOTE: We have to register the annotator on the main thread, as annotators
  // only affect a single thread.
  SystemGroup::Dispatch(
      TaskCategory::Other,
      NS_NewRunnableFunction("RegisterPendingInputEventHangAnnotator", [] {
        BackgroundHangMonitor::RegisterAnnotator(
            PendingInputEventHangAnnotator::sSingleton);
      }));
#endif

  return true;
}

void ContentChild::SetProcessName(const nsAString& aName) {
  char* name;
  if ((name = PR_GetEnv("MOZ_DEBUG_APP_PROCESS")) && aName.EqualsASCII(name)) {
#ifdef OS_POSIX
    printf_stderr("\n\nCHILDCHILDCHILDCHILD\n  [%s] debug me @%d\n\n", name,
                  getpid());
    sleep(30);
#elif defined(OS_WIN)
    // Windows has a decent JIT debugging story, so NS_DebugBreak does the
    // right thing.
    NS_DebugBreak(NS_DEBUG_BREAK,
                  "Invoking NS_DebugBreak() to debug child process", nullptr,
                  __FILE__, __LINE__);
#endif
  }

  mProcessName = aName;
  mozilla::ipc::SetThisProcessName(NS_LossyConvertUTF16toASCII(aName).get());
}

NS_IMETHODIMP
ContentChild::ProvideWindow(mozIDOMWindowProxy* aParent, uint32_t aChromeFlags,
                            bool aCalledFromJS, bool aPositionSpecified,
                            bool aSizeSpecified, nsIURI* aURI,
                            const nsAString& aName, const nsACString& aFeatures,
                            bool aForceNoOpener,
                            nsDocShellLoadState* aLoadState, bool* aWindowIsNew,
                            mozIDOMWindowProxy** aReturn) {
  return ProvideWindowCommon(nullptr, aParent, false, aChromeFlags,
                             aCalledFromJS, aPositionSpecified, aSizeSpecified,
                             aURI, aName, aFeatures, aForceNoOpener, aLoadState,
                             aWindowIsNew, aReturn);
}

static nsresult GetCreateWindowParams(mozIDOMWindowProxy* aParent,
                                      nsDocShellLoadState* aLoadState,
                                      nsACString& aBaseURIString,
                                      float* aFullZoom,
                                      uint32_t* aReferrerPolicy,
                                      nsIPrincipal** aTriggeringPrincipal) {
  *aFullZoom = 1.0f;
  if (!aTriggeringPrincipal) {
    NS_ERROR("aTriggeringPrincipal is null");
    return NS_ERROR_FAILURE;
  }
  auto* opener = nsPIDOMWindowOuter::From(aParent);
  if (!opener) {
    nsCOMPtr<nsIPrincipal> nullPrincipal =
        NullPrincipal::CreateWithoutOriginAttributes();
    NS_ADDREF(*aTriggeringPrincipal = nullPrincipal);
    return NS_OK;
  }

  nsCOMPtr<nsIDocument> doc = opener->GetDoc();
  NS_ADDREF(*aTriggeringPrincipal = doc->NodePrincipal());
  nsCOMPtr<nsIURI> baseURI = doc->GetDocBaseURI();
  if (!baseURI) {
    NS_ERROR("nsIDocument didn't return a base URI");
    return NS_ERROR_FAILURE;
  }

  baseURI->GetSpec(aBaseURIString);

  if (aLoadState) {
    if (!aLoadState->SendReferrer()) {
      *aReferrerPolicy = mozilla::net::RP_No_Referrer;
    } else {
      *aReferrerPolicy = aLoadState->ReferrerPolicy();
    }
  }

  RefPtr<nsDocShell> openerDocShell =
      static_cast<nsDocShell*>(opener->GetDocShell());
  if (!openerDocShell) {
    return NS_OK;
  }

  nsCOMPtr<nsIContentViewer> cv;
  nsresult rv = openerDocShell->GetContentViewer(getter_AddRefs(cv));
  if (NS_SUCCEEDED(rv) && cv) {
    cv->GetFullZoom(aFullZoom);
  }

  return NS_OK;
}

nsresult ContentChild::ProvideWindowCommon(
    TabChild* aTabOpener, mozIDOMWindowProxy* aParent, bool aIframeMoz,
    uint32_t aChromeFlags, bool aCalledFromJS, bool aPositionSpecified,
    bool aSizeSpecified, nsIURI* aURI, const nsAString& aName,
    const nsACString& aFeatures, bool aForceNoOpener,
    nsDocShellLoadState* aLoadState, bool* aWindowIsNew,
    mozIDOMWindowProxy** aReturn) {
  *aReturn = nullptr;

  nsAutoPtr<IPCTabContext> ipcContext;
  TabId openerTabId = TabId(0);
  nsAutoCString features(aFeatures);
  nsAutoString name(aName);

  nsresult rv;

  MOZ_ASSERT(!aParent || aTabOpener,
             "If aParent is non-null, we should have an aTabOpener");

  // Cache the boolean preference for allowing noopener windows to open in a
  // separate process.
  static bool sNoopenerNewProcess = false;
  static bool sNoopenerNewProcessInited = false;
  if (!sNoopenerNewProcessInited) {
    Preferences::AddBoolVarCache(&sNoopenerNewProcess,
                                 "dom.noopener.newprocess.enabled");
    sNoopenerNewProcessInited = true;
  }

  // Check if we should load in a different process. We always want to load in a
  // different process if we have noopener set, but we also might if we can't
  // load in the current process.
  bool loadInDifferentProcess = aForceNoOpener && sNoopenerNewProcess;
  if (aTabOpener && !loadInDifferentProcess && aURI) {
    nsCOMPtr<nsIWebBrowserChrome3> browserChrome3;
    rv = aTabOpener->GetWebBrowserChrome(getter_AddRefs(browserChrome3));
    if (NS_SUCCEEDED(rv) && browserChrome3) {
      bool shouldLoad;
      rv = browserChrome3->ShouldLoadURIInThisProcess(aURI, &shouldLoad);
      loadInDifferentProcess = NS_SUCCEEDED(rv) && !shouldLoad;
    }
  }

  // If we're in a content process and we have noopener set, there's no reason
  // to load in our process, so let's load it elsewhere!
  if (loadInDifferentProcess) {
    nsAutoCString baseURIString;
    float fullZoom;
    nsCOMPtr<nsIPrincipal> triggeringPrincipal;
    uint32_t referrerPolicy = mozilla::net::RP_Unset;
    rv = GetCreateWindowParams(aParent, aLoadState, baseURIString, &fullZoom,
                               &referrerPolicy,
                               getter_AddRefs(triggeringPrincipal));
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return rv;
    }

    OptionalURIParams uriToLoad;
    SerializeURI(aURI, uriToLoad);
    Unused << SendCreateWindowInDifferentProcess(
        aTabOpener, aChromeFlags, aCalledFromJS, aPositionSpecified,
        aSizeSpecified, uriToLoad, features, baseURIString, fullZoom, name,
        Principal(triggeringPrincipal), referrerPolicy);

    // We return NS_ERROR_ABORT, so that the caller knows that we've abandoned
    // the window open as far as it is concerned.
    return NS_ERROR_ABORT;
  }

  if (aTabOpener) {
    PopupIPCTabContext context;
    openerTabId = aTabOpener->GetTabId();
    context.opener() = openerTabId;
    context.isMozBrowserElement() = aTabOpener->IsMozBrowserElement();
    ipcContext = new IPCTabContext(context);
  } else {
    // It's possible to not have a TabChild opener in the case
    // of ServiceWorker::OpenWindow.
    UnsafeIPCTabContext unsafeTabContext;
    ipcContext = new IPCTabContext(unsafeTabContext);
  }

  MOZ_ASSERT(ipcContext);
  TabId tabId(nsContentUtils::GenerateTabId());

  // We need to assign a TabGroup to the PBrowser actor before we send it to the
  // parent. Otherwise, the parent could send messages to us before we have a
  // proper TabGroup for that actor.
  RefPtr<TabGroup> tabGroup;
  if (aTabOpener && !aForceNoOpener) {
    // The new actor will use the same tab group as the opener.
    tabGroup = aTabOpener->TabGroup();
  } else {
    tabGroup = new TabGroup();
  }

  TabContext newTabContext = aTabOpener ? *aTabOpener : TabContext();
  RefPtr<TabChild> newChild =
      new TabChild(this, tabId, tabGroup, newTabContext, aChromeFlags);

  if (aTabOpener) {
    MOZ_ASSERT(ipcContext->type() == IPCTabContext::TPopupIPCTabContext);
    ipcContext->get_PopupIPCTabContext().opener() = aTabOpener;
  }

  nsCOMPtr<nsIEventTarget> target =
      tabGroup->EventTargetFor(TaskCategory::Other);
  SetEventTargetForActor(newChild, target);

  Unused << SendPBrowserConstructor(
      // We release this ref in DeallocPBrowserChild
      RefPtr<TabChild>(newChild).forget().take(), tabId, TabId(0), *ipcContext,
      aChromeFlags, GetID(), IsForBrowser());

  // Now that |newChild| has had its IPC link established, call |Init| to set it
  // up.
  if (NS_FAILED(newChild->Init(aParent))) {
    return NS_ERROR_ABORT;
  }

  nsCOMPtr<nsPIDOMWindowInner> parentTopInnerWindow;
  if (aParent) {
    nsCOMPtr<nsPIDOMWindowOuter> parentTopWindow =
        nsPIDOMWindowOuter::From(aParent)->GetTop();
    if (parentTopWindow) {
      parentTopInnerWindow = parentTopWindow->GetCurrentInnerWindow();
    }
  }

  // Set to true when we're ready to return from this function.
  bool ready = false;

  // NOTE: Capturing by reference here is safe, as this function won't return
  // until one of these callbacks is called.
  auto resolve = [&](const CreatedWindowInfo& info) {
    MOZ_RELEASE_ASSERT(NS_IsMainThread());
    rv = info.rv();
    *aWindowIsNew = info.windowOpened();
    nsTArray<FrameScriptInfo> frameScripts(info.frameScripts());
    nsCString urlToLoad = info.urlToLoad();
    uint32_t maxTouchPoints = info.maxTouchPoints();
    DimensionInfo dimensionInfo = info.dimensions();
    bool hasSiblings = info.hasSiblings();

    // Once this function exits, we should try to exit the nested event loop.
    ready = true;

    // NOTE: We have to handle this immediately in the resolve callback in order
    // to make sure that we don't process any more IPC messages before returning
    // from ProvideWindowCommon.

    // Handle the error which we got back from the parent process, if we got
    // one.
    if (NS_FAILED(rv)) {
      return;
    }

    if (!*aWindowIsNew) {
      rv = NS_ERROR_ABORT;
      return;
    }

    // If the TabChild has been torn down, we don't need to do this anymore.
    if (NS_WARN_IF(!newChild->IPCOpen() || newChild->IsDestroyed())) {
      rv = NS_ERROR_ABORT;
      return;
    }

    ShowInfo showInfo(EmptyString(), false, false, true, false, 0, 0, 0);
    auto* opener = nsPIDOMWindowOuter::From(aParent);
    nsIDocShell* openerShell;
    if (opener && (openerShell = opener->GetDocShell())) {
      nsCOMPtr<nsILoadContext> context = do_QueryInterface(openerShell);
      showInfo = ShowInfo(EmptyString(), false, context->UsePrivateBrowsing(),
                          true, false, aTabOpener->WebWidget()->GetDPI(),
                          aTabOpener->WebWidget()->RoundsWidgetCoordinatesTo(),
                          aTabOpener->WebWidget()->GetDefaultScale().scale);
    }

    newChild->SetMaxTouchPoints(maxTouchPoints);
    newChild->SetHasSiblings(hasSiblings);

    // Set the opener window for this window before we start loading the
    // document inside of it. We have to do this before loading the remote
    // scripts, because they can poke at the document and cause the nsIDocument
    // to be created before the openerwindow
    nsCOMPtr<mozIDOMWindowProxy> windowProxy =
        do_GetInterface(newChild->WebNavigation());
    if (!aForceNoOpener && windowProxy && aParent) {
      nsPIDOMWindowOuter* outer = nsPIDOMWindowOuter::From(windowProxy);
      nsPIDOMWindowOuter* parent = nsPIDOMWindowOuter::From(aParent);
      outer->SetOpenerWindow(parent, *aWindowIsNew);
    }

    // Unfortunately we don't get a window unless we've shown the frame.  That's
    // pretty bogus; see bug 763602.
    newChild->DoFakeShow(showInfo);

    newChild->RecvUpdateDimensions(dimensionInfo);

    for (size_t i = 0; i < frameScripts.Length(); i++) {
      FrameScriptInfo& info = frameScripts[i];
      if (!newChild->RecvLoadRemoteScript(info.url(),
                                          info.runInGlobalScope())) {
        MOZ_CRASH();
      }
    }

    if (!urlToLoad.IsEmpty()) {
      newChild->RecvLoadURL(urlToLoad, showInfo);
    }

    nsCOMPtr<mozIDOMWindowProxy> win =
        do_GetInterface(newChild->WebNavigation());
    win.forget(aReturn);
  };

  // NOTE: Capturing by reference here is safe, as this function won't return
  // until one of these callbacks is called.
  auto reject = [&](ResponseRejectReason) {
    MOZ_RELEASE_ASSERT(NS_IsMainThread());
    NS_WARNING("windowCreated promise rejected");
    rv = NS_ERROR_NOT_AVAILABLE;
    ready = true;
  };

  // Send down the request to open the window.
  if (aIframeMoz) {
    MOZ_ASSERT(aTabOpener);
    nsAutoCString url;
    if (aURI) {
      aURI->GetSpec(url);
    } else {
      // We can't actually send a nullptr up as the URI, since IPDL doesn't let
      // us send nullptr's for primitives. We indicate that the nsString for the
      // URI should be converted to a nullptr by voiding the string.
      url.SetIsVoid(true);
    }

    // NOTE: BrowserFrameOpenWindowPromise is the same type as
    // CreateWindowPromise, and this code depends on that fact.
    newChild->SendBrowserFrameOpenWindow(aTabOpener, NS_ConvertUTF8toUTF16(url),
                                         name, NS_ConvertUTF8toUTF16(features),
                                         std::move(resolve), std::move(reject));
  } else {
    nsAutoCString baseURIString;
    float fullZoom;
    nsCOMPtr<nsIPrincipal> triggeringPrincipal;
    uint32_t referrerPolicy = mozilla::net::RP_Unset;
    rv = GetCreateWindowParams(aParent, aLoadState, baseURIString, &fullZoom,
                               &referrerPolicy,
                               getter_AddRefs(triggeringPrincipal));
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return rv;
    }

    OptionalURIParams uriToLoad;
    if (aURI) {
      SerializeURI(aURI, uriToLoad);
    } else {
      uriToLoad = mozilla::void_t();
    }

    SendCreateWindow(aTabOpener, newChild, aChromeFlags, aCalledFromJS,
                     aPositionSpecified, aSizeSpecified, uriToLoad, features,
                     baseURIString, fullZoom, Principal(triggeringPrincipal),
                     referrerPolicy, std::move(resolve), std::move(reject));
  }

  // =======================
  // Begin Nested Event Loop
  // =======================

  // We have to wait for a response from either SendCreateWindow or
  // SendBrowserFrameOpenWindow with information we're going to need to return
  // from this function, So we spin a nested event loop until they get back to
  // us.

  // Prevent the docshell from becoming active while the nested event loop is
  // spinning.
  newChild->AddPendingDocShellBlocker();
  auto removePendingDocShellBlocker = MakeScopeExit([&] {
    if (newChild) {
      newChild->RemovePendingDocShellBlocker();
    }
  });

  // Suspend our window if we have one to make sure we don't re-enter it.
  if (parentTopInnerWindow) {
    parentTopInnerWindow->Suspend();
  }

  {
    AutoNoJSAPI nojsapi;

    // Spin the event loop until we get a response. Callers of this function
    // already have to guard against an inner event loop spinning in the
    // non-e10s case because of the need to spin one to create a new chrome
    // window.
    SpinEventLoopUntil([&]() { return ready; });
    MOZ_RELEASE_ASSERT(ready,
                       "We are on the main thread, so we should not exit this "
                       "loop without ready being true.");
  }

  if (parentTopInnerWindow) {
    parentTopInnerWindow->Resume();
  }

  // =====================
  // End Nested Event Loop
  // =====================

  // We should have the results already set by the callbacks.
  MOZ_ASSERT_IF(NS_SUCCEEDED(rv), *aReturn);
  return rv;
}

void ContentChild::GetProcessName(nsAString& aName) const {
  aName.Assign(mProcessName);
}

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

bool ContentChild::IsShuttingDown() const { return mShuttingDown; }

void ContentChild::GetProcessName(nsACString& aName) const {
  aName.Assign(NS_ConvertUTF16toUTF8(mProcessName));
}

/* static */ void ContentChild::AppendProcessId(nsACString& aName) {
  if (!aName.IsEmpty()) {
    aName.Append(' ');
  }
  unsigned pid = getpid();
  aName.Append(nsPrintfCString("(pid %u)", pid));
}

void ContentChild::InitGraphicsDeviceData(const ContentDeviceData& aData) {
  gfxPlatform::InitChild(aData);
}

void ContentChild::InitXPCOM(
    const XPCOMInitData& aXPCOMInit,
    const mozilla::dom::ipc::StructuredCloneData& aInitialData) {
  // Do this as early as possible to get the parent process to initialize the
  // background thread since we'll likely need database information very soon.
  BackgroundChild::Startup();

  PBackgroundChild* actorChild = BackgroundChild::GetOrCreateForCurrentThread();
  if (NS_WARN_IF(!actorChild)) {
    MOZ_ASSERT_UNREACHABLE("PBackground init can't fail at this point");
    return;
  }

  ClientManager::Startup();

  RemoteWorkerService::Initialize();

  nsCOMPtr<nsIConsoleService> svc(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
  if (!svc) {
    NS_WARNING("Couldn't acquire console service");
    return;
  }

  mConsoleListener = new ConsoleListener(this);
  if (NS_FAILED(svc->RegisterListener(mConsoleListener)))
    NS_WARNING("Couldn't register console listener for child process");

  mAvailableDictionaries = aXPCOMInit.dictionaries();

  RecvSetOffline(aXPCOMInit.isOffline());
  RecvSetConnectivity(aXPCOMInit.isConnected());
  LocaleService::GetInstance()->AssignAppLocales(aXPCOMInit.appLocales());
  LocaleService::GetInstance()->AssignRequestedLocales(
      aXPCOMInit.requestedLocales());

  RecvSetCaptivePortalState(aXPCOMInit.captivePortalState());
  RecvBidiKeyboardNotify(aXPCOMInit.isLangRTL(),
                         aXPCOMInit.haveBidiKeyboards());

  // Create the CPOW manager as soon as possible. Middleman processes don't use
  // CPOWs, because their recording child will also have a CPOW manager that
  // communicates with the UI process.
  if (!recordreplay::IsMiddleman()) {
    SendPJavaScriptConstructor();
  }

  if (aXPCOMInit.domainPolicy().active()) {
    nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
    MOZ_ASSERT(ssm);
    ssm->ActivateDomainPolicyInternal(getter_AddRefs(mPolicy));
    if (!mPolicy) {
      MOZ_CRASH("Failed to activate domain policy.");
    }
    mPolicy->ApplyClone(&aXPCOMInit.domainPolicy());
  }

  nsCOMPtr<nsIClipboard> clipboard(
      do_GetService("@mozilla.org/widget/clipboard;1"));
  if (nsCOMPtr<nsIClipboardProxy> clipboardProxy =
          do_QueryInterface(clipboard)) {
    clipboardProxy->SetCapabilities(aXPCOMInit.clipboardCaps());
  }

  {
    AutoJSAPI jsapi;
    if (NS_WARN_IF(!jsapi.Init(xpc::PrivilegedJunkScope()))) {
      MOZ_CRASH();
    }
    ErrorResult rv;
    JS::RootedValue data(jsapi.cx());
    mozilla::dom::ipc::StructuredCloneData id;
    id.Copy(aInitialData);
    id.Read(jsapi.cx(), &data, rv);
    if (NS_WARN_IF(rv.Failed())) {
      MOZ_CRASH();
    }
    auto* global = ContentProcessMessageManager::Get();
    global->SetInitialProcessData(data);
  }

  // The stylesheet cache is not ready yet. Store this URL for future use.
  nsCOMPtr<nsIURI> ucsURL = DeserializeURI(aXPCOMInit.userContentSheetURL());
  nsLayoutStylesheetCache::SetUserContentCSSURL(ucsURL);

  GfxInfoBase::SetFeatureStatus(aXPCOMInit.gfxFeatureStatus());

  DataStorage::SetCachedStorageEntries(aXPCOMInit.dataStorage());

  // Set the dynamic scalar definitions for this process.
  TelemetryIPC::AddDynamicScalarDefinitions(aXPCOMInit.dynamicScalarDefs());

  DOMPrefs::Initialize();
}

mozilla::ipc::IPCResult ContentChild::RecvRequestMemoryReport(
    const uint32_t& aGeneration, const bool& aAnonymize,
    const bool& aMinimizeMemoryUsage, const MaybeFileDesc& aDMDFile) {
  nsCString process;
  GetProcessName(process);
  AppendProcessId(process);

  MemoryReportRequestClient::Start(aGeneration, aAnonymize,
                                   aMinimizeMemoryUsage, aDMDFile, process,
                                   [&](const MemoryReport& aReport) {
                                     Unused << GetSingleton()->SendAddMemoryReport(aReport);
                                   },
                                   [&](const uint32_t& aGeneration) {
                                     return GetSingleton()->SendFinishMemoryReport(aGeneration);
                                   });
  return IPC_OK();
}

PCycleCollectWithLogsChild* ContentChild::AllocPCycleCollectWithLogsChild(
    const bool& aDumpAllTraces, const FileDescriptor& aGCLog,
    const FileDescriptor& aCCLog) {
  return do_AddRef(new CycleCollectWithLogsChild()).take();
}

mozilla::ipc::IPCResult ContentChild::RecvPCycleCollectWithLogsConstructor(
    PCycleCollectWithLogsChild* aActor, const bool& aDumpAllTraces,
    const FileDescriptor& aGCLog, const FileDescriptor& aCCLog) {
  // The sink's destructor is called when the last reference goes away, which
  // will cause the actor to be closed down.
  auto* actor = static_cast<CycleCollectWithLogsChild*>(aActor);
  RefPtr<CycleCollectWithLogsChild::Sink> sink =
      new CycleCollectWithLogsChild::Sink(actor, aGCLog, aCCLog);

  // Invoke the dumper, which will take a reference to the sink.
  nsCOMPtr<nsIMemoryInfoDumper> dumper =
      do_GetService("@mozilla.org/memory-info-dumper;1");
  dumper->DumpGCAndCCLogsToSink(aDumpAllTraces, sink);
  return IPC_OK();
}

bool ContentChild::DeallocPCycleCollectWithLogsChild(
    PCycleCollectWithLogsChild* aActor) {
  RefPtr<CycleCollectWithLogsChild> actor =
      dont_AddRef(static_cast<CycleCollectWithLogsChild*>(aActor));
  return true;
}

mozilla::ipc::IPCResult ContentChild::RecvInitContentBridgeChild(
    Endpoint<PContentBridgeChild>&& aEndpoint) {
  ContentBridgeChild::Create(std::move(aEndpoint));
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvInitGMPService(
    Endpoint<PGMPServiceChild>&& aGMPService) {
  if (!GMPServiceChild::Create(std::move(aGMPService))) {
    return IPC_FAIL_NO_REASON(this);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvInitProfiler(
    Endpoint<PProfilerChild>&& aEndpoint) {
#ifdef MOZ_GECKO_PROFILER
  mProfilerController = ChildProfilerController::Create(std::move(aEndpoint));
#endif
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvGMPsChanged(
    nsTArray<GMPCapabilityData>&& capabilities) {
  GeckoMediaPluginServiceChild::UpdateGMPCapabilities(std::move(capabilities));
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvInitProcessHangMonitor(
    Endpoint<PProcessHangMonitorChild>&& aHangMonitor) {
  CreateHangMonitorChild(std::move(aHangMonitor));
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::GetResultForRenderingInitFailure(
    base::ProcessId aOtherPid) {
  if (aOtherPid == base::GetCurrentProcId() || aOtherPid == OtherPid()) {
    // If we are talking to ourselves, or the UI process, then that is a fatal
    // protocol error.
    return IPC_FAIL_NO_REASON(this);
  }

  // If we are talking to the GPU process, then we should recover from this on
  // the next ContentChild::RecvReinitRendering call.
  gfxCriticalNote << "Could not initialize rendering with GPU process";
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvRequestPerformanceMetrics(
    const nsID& aID) {
  MOZ_ASSERT(mozilla::StaticPrefs::dom_performance_enable_scheduler_timing());
  RefPtr<ContentChild> self = this;
  RefPtr<AbstractThread> mainThread =
      SystemGroup::AbstractMainThreadFor(TaskCategory::Performance);
  nsTArray<RefPtr<PerformanceInfoPromise>> promises = CollectPerformanceInfo();

  PerformanceInfoPromise::All(mainThread, promises)
      ->Then(
          mainThread, __func__,
          [self, aID](const nsTArray<mozilla::dom::PerformanceInfo>& aResult) {
            self->SendAddPerformanceMetrics(aID, aResult);
          },
          []() { /* silently fails -- the parent times out
                    and proceeds when the data is not coming back */
          });

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvInitRendering(
    Endpoint<PCompositorManagerChild>&& aCompositor,
    Endpoint<PImageBridgeChild>&& aImageBridge,
    Endpoint<PVRManagerChild>&& aVRBridge,
    Endpoint<PVideoDecoderManagerChild>&& aVideoManager,
    nsTArray<uint32_t>&& namespaces) {
  MOZ_ASSERT(namespaces.Length() == 3);

  // Note that for all of the methods below, if it can fail, it should only
  // return false if the failure is an IPDL error. In such situations,
  // ContentChild can reason about whether or not to wait for
  // RecvReinitRendering (because we surmised the GPU process crashed), or if it
  // should crash itself (because we are actually talking to the UI process). If
  // there are localized failures (e.g. failed to spawn a thread), then it
  // should MOZ_RELEASE_ASSERT or MOZ_CRASH as necessary instead.
  if (!CompositorManagerChild::Init(std::move(aCompositor), namespaces[0])) {
    return GetResultForRenderingInitFailure(aCompositor.OtherPid());
  }
  if (!CompositorManagerChild::CreateContentCompositorBridge(namespaces[1])) {
    return GetResultForRenderingInitFailure(aCompositor.OtherPid());
  }
  if (!ImageBridgeChild::InitForContent(std::move(aImageBridge),
                                        namespaces[2])) {
    return GetResultForRenderingInitFailure(aImageBridge.OtherPid());
  }
  if (!gfx::VRManagerChild::InitForContent(std::move(aVRBridge))) {
    return GetResultForRenderingInitFailure(aVRBridge.OtherPid());
  }
  VideoDecoderManagerChild::InitForContent(std::move(aVideoManager));
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvReinitRendering(
    Endpoint<PCompositorManagerChild>&& aCompositor,
    Endpoint<PImageBridgeChild>&& aImageBridge,
    Endpoint<PVRManagerChild>&& aVRBridge,
    Endpoint<PVideoDecoderManagerChild>&& aVideoManager,
    nsTArray<uint32_t>&& namespaces) {
  MOZ_ASSERT(namespaces.Length() == 3);
  nsTArray<RefPtr<TabChild>> tabs = TabChild::GetAll();

  // Zap all the old layer managers we have lying around.
  for (const auto& tabChild : tabs) {
    if (tabChild->GetLayersId().IsValid()) {
      tabChild->InvalidateLayers();
    }
  }

  // Re-establish singleton bridges to the compositor.
  if (!CompositorManagerChild::Init(std::move(aCompositor), namespaces[0])) {
    return GetResultForRenderingInitFailure(aCompositor.OtherPid());
  }
  if (!CompositorManagerChild::CreateContentCompositorBridge(namespaces[1])) {
    return GetResultForRenderingInitFailure(aCompositor.OtherPid());
  }
  if (!ImageBridgeChild::ReinitForContent(std::move(aImageBridge),
                                          namespaces[2])) {
    return GetResultForRenderingInitFailure(aImageBridge.OtherPid());
  }
  if (!gfx::VRManagerChild::ReinitForContent(std::move(aVRBridge))) {
    return GetResultForRenderingInitFailure(aVRBridge.OtherPid());
  }
  gfxPlatform::GetPlatform()->CompositorUpdated();

  // Establish new PLayerTransactions.
  for (const auto& tabChild : tabs) {
    if (tabChild->GetLayersId().IsValid()) {
      tabChild->ReinitRendering();
    }
  }

  VideoDecoderManagerChild::InitForContent(std::move(aVideoManager));
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvAudioDefaultDeviceChange() {
#ifdef XP_WIN
  audio::AudioNotificationReceiver::NotifyDefaultDeviceChanged();
#endif
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvReinitRenderingForDeviceReset() {
  gfxPlatform::GetPlatform()->CompositorUpdated();

  nsTArray<RefPtr<TabChild>> tabs = TabChild::GetAll();
  for (const auto& tabChild : tabs) {
    if (tabChild->GetLayersId().IsValid()) {
      tabChild->ReinitRenderingForDeviceReset();
    }
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvInitRemoteDecoder(
    Endpoint<PRemoteDecoderManagerChild>&& aRemoteManager) {
  RemoteDecoderManagerChild::InitForContent(std::move(aRemoteManager));
  return IPC_OK();
}

#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
extern "C" {
CGError CGSSetDenyWindowServerConnections(bool);
void CGSShutdownServerConnections();
};

static bool StartMacOSContentSandbox() {
  int sandboxLevel = GetEffectiveContentSandboxLevel();
  if (sandboxLevel < 1) {
    return false;
  }

  // Close all current connections to the WindowServer. This ensures that the
  // Activity Monitor will not label the content process as "Not responding"
  // because it's not running a native event loop. See bug 1384336.
  CGSShutdownServerConnections();

  // Actual security benefits are only acheived when we additionally deny
  // future connections, however this currently breaks WebGL so it's not done
  // by default.
  if (Preferences::GetBool(
          "security.sandbox.content.mac.disconnect-windowserver")) {
    CGError result = CGSSetDenyWindowServerConnections(true);
    MOZ_DIAGNOSTIC_ASSERT(result == kCGErrorSuccess);
#if !MOZ_DIAGNOSTIC_ASSERT_ENABLED
    Unused << result;
#endif
  }

  // If the sandbox is already enabled, there's nothing more to do here.
  if (Preferences::GetBool("security.sandbox.content.mac.earlyinit") &&
      !recordreplay::IsRecordingOrReplaying()) {
    return true;
  }

  nsAutoCString appPath;
  if (!nsMacUtilsImpl::GetAppPath(appPath)) {
    MOZ_CRASH("Error resolving child process app path");
  }

  ContentChild* cc = ContentChild::GetSingleton();

  nsresult rv;
  nsCOMPtr<nsIFile> profileDir;
  cc->GetProfileDir(getter_AddRefs(profileDir));
  nsCString profileDirPath;
  if (profileDir) {
    profileDir->Normalize();
    rv = profileDir->GetNativePath(profileDirPath);
    if (NS_FAILED(rv) || profileDirPath.IsEmpty()) {
      MOZ_CRASH("Failed to get profile path");
    }
  }

  bool isFileProcess = cc->GetRemoteType().EqualsLiteral(FILE_REMOTE_TYPE);

  MacSandboxInfo info;
  info.type = MacSandboxType_Content;
  info.level = sandboxLevel;
  info.hasFilePrivileges = isFileProcess;
  info.shouldLog = Preferences::GetBool("security.sandbox.logging.enabled") ||
                   PR_GetEnv("MOZ_SANDBOX_LOGGING");
  info.appPath.assign(appPath.get());
  info.hasAudio = !Preferences::GetBool("media.cubeb.sandbox");
  info.hasWindowServer = !Preferences::GetBool(
      "security.sandbox.content.mac.disconnect-windowserver");

  // These paths are used to allowlist certain directories used by the testing
  // system. They should not be considered a public API, and are only intended
  // for use in automation.
  nsAutoCString testingReadPath1;
  Preferences::GetCString("security.sandbox.content.mac.testing_read_path1",
                          testingReadPath1);
  if (!testingReadPath1.IsEmpty()) {
    info.testingReadPath1.assign(testingReadPath1.get());
  }
  nsAutoCString testingReadPath2;
  Preferences::GetCString("security.sandbox.content.mac.testing_read_path2",
                          testingReadPath2);
  if (!testingReadPath2.IsEmpty()) {
    info.testingReadPath2.assign(testingReadPath2.get());
  }

  if (mozilla::IsDevelopmentBuild()) {
    nsCOMPtr<nsIFile> repoDir;
    rv = mozilla::GetRepoDir(getter_AddRefs(repoDir));
    if (NS_FAILED(rv)) {
      MOZ_CRASH("Failed to get path to repo dir");
    }
    nsCString repoDirPath;
    Unused << repoDir->GetNativePath(repoDirPath);
    info.testingReadPath3.assign(repoDirPath.get());

    nsCOMPtr<nsIFile> objDir;
    rv = mozilla::GetObjDir(getter_AddRefs(objDir));
    if (NS_FAILED(rv)) {
      MOZ_CRASH("Failed to get path to build object dir");
    }

    nsCString objDirPath;
    Unused << objDir->GetNativePath(objDirPath);
    info.testingReadPath4.assign(objDirPath.get());
  }

  if (profileDir) {
    info.hasSandboxedProfile = true;
    info.profileDir.assign(profileDirPath.get());
  } else {
    info.hasSandboxedProfile = false;
  }

#ifdef DEBUG
  // When a content process dies intentionally (|NoteIntentionalCrash|), for
  // tests it wants to log that it did this. Allow writing to this location
  // that the testrunner wants.
  char* bloatLog = PR_GetEnv("XPCOM_MEM_BLOAT_LOG");
  if (bloatLog != nullptr) {
    // |bloatLog| points to a specific file, but we actually write to a sibling
    // of that path.
    nsAutoCString bloatDirectoryPath =
        nsMacUtilsImpl::GetDirectoryPath(bloatLog);
    info.debugWriteDir.assign(bloatDirectoryPath.get());
  }
#endif  // DEBUG

  std::string err;
  if (!mozilla::StartMacSandbox(info, err)) {
    NS_WARNING(err.c_str());
    MOZ_CRASH("sandbox_init() failed");
  }

  return true;
}
#endif

mozilla::ipc::IPCResult ContentChild::RecvSetProcessSandbox(
    const MaybeFileDesc& aBroker) {
  // We may want to move the sandbox initialization somewhere else
  // at some point; see bug 880808.
#if defined(MOZ_CONTENT_SANDBOX)
  bool sandboxEnabled = true;
#if defined(XP_LINUX)
  // On Linux, we have to support systems that can't use any sandboxing.
  if (!SandboxInfo::Get().CanSandboxContent()) {
    sandboxEnabled = false;
  } else {
    // Pre-start audio before sandboxing; see bug 1443612.
    if (!Preferences::GetBool("media.cubeb.sandbox")) {
      Unused << CubebUtils::GetCubebContext();
    }
  }

  if (sandboxEnabled) {
    sandboxEnabled = SetContentProcessSandbox(
        ContentProcessSandboxParams::ForThisProcess(aBroker));
  }
#elif defined(XP_WIN)
  mozilla::SandboxTarget::Instance()->StartSandbox();
#elif defined(XP_MACOSX)
  sandboxEnabled = StartMacOSContentSandbox();
#elif defined(__OpenBSD__)
  sandboxEnabled = StartOpenBSDSandbox(GeckoProcessType_Content);
  /* dont overwrite an existing session dbus address, but ensure it is set */
  if (!PR_GetEnv("DBUS_SESSION_BUS_ADDRESS")) {
    static LazyLogModule sPledgeLog("SandboxPledge");
    MOZ_LOG(sPledgeLog, LogLevel::Debug,
            ("no session dbus found, faking one\n"));
    PR_SetEnv("DBUS_SESSION_BUS_ADDRESS=");
  }
#endif

  CrashReporter::AnnotateCrashReport(
      CrashReporter::Annotation::ContentSandboxEnabled, sandboxEnabled);
#if defined(XP_LINUX) && !defined(OS_ANDROID)
  CrashReporter::AnnotateCrashReport(
      CrashReporter::Annotation::ContentSandboxCapabilities,
      static_cast<int>(SandboxInfo::Get().AsInteger()));
#endif /* XP_LINUX && !OS_ANDROID */
  CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::RemoteType,
                                     NS_ConvertUTF16toUTF8(GetRemoteType()));
#endif /* MOZ_CONTENT_SANDBOX */

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvBidiKeyboardNotify(
    const bool& aIsLangRTL, const bool& aHaveBidiKeyboards) {
  // bidi is always of type PuppetBidiKeyboard* (because in the child, the only
  // possible implementation of nsIBidiKeyboard is PuppetBidiKeyboard).
  PuppetBidiKeyboard* bidi =
      static_cast<PuppetBidiKeyboard*>(nsContentUtils::GetBidiKeyboard());
  if (bidi) {
    bidi->SetBidiKeyboardInfo(aIsLangRTL, aHaveBidiKeyboards);
  }
  return IPC_OK();
}

static StaticRefPtr<CancelableRunnable> gFirstIdleTask;

static void FirstIdle(void) {
  MOZ_ASSERT(gFirstIdleTask);
  gFirstIdleTask = nullptr;

  // When recording or replaying, the middleman process will send this message
  // instead.
  if (!recordreplay::IsRecordingOrReplaying()) {
    ContentChild::GetSingleton()->SendFirstIdle();
  }
}

mozilla::jsipc::PJavaScriptChild* ContentChild::AllocPJavaScriptChild() {
  MOZ_ASSERT(ManagedPJavaScriptChild().IsEmpty());

  return nsIContentChild::AllocPJavaScriptChild();
}

bool ContentChild::DeallocPJavaScriptChild(PJavaScriptChild* aChild) {
  return nsIContentChild::DeallocPJavaScriptChild(aChild);
}

PBrowserChild* ContentChild::AllocPBrowserChild(const TabId& aTabId,
                                                const TabId& aSameTabGroupAs,
                                                const IPCTabContext& aContext,
                                                const uint32_t& aChromeFlags,
                                                const ContentParentId& aCpID,
                                                const bool& aIsForBrowser) {
  return nsIContentChild::AllocPBrowserChild(
      aTabId, aSameTabGroupAs, aContext, aChromeFlags, aCpID, aIsForBrowser);
}

bool ContentChild::SendPBrowserConstructor(
    PBrowserChild* aActor, const TabId& aTabId, const TabId& aSameTabGroupAs,
    const IPCTabContext& aContext, const uint32_t& aChromeFlags,
    const ContentParentId& aCpID, const bool& aIsForBrowser) {
  if (IsShuttingDown()) {
    return false;
  }

  return PContentChild::SendPBrowserConstructor(aActor, aTabId, aSameTabGroupAs,
                                                aContext, aChromeFlags, aCpID,
                                                aIsForBrowser);
}

mozilla::ipc::IPCResult ContentChild::RecvPBrowserConstructor(
    PBrowserChild* aActor, const TabId& aTabId, const TabId& aSameTabGroupAs,
    const IPCTabContext& aContext, const uint32_t& aChromeFlags,
    const ContentParentId& aCpID, const bool& aIsForBrowser) {
  MOZ_ASSERT(!IsShuttingDown());

  static bool hasRunOnce = false;
  if (!hasRunOnce) {
    hasRunOnce = true;
    MOZ_ASSERT(!gFirstIdleTask);
    RefPtr<CancelableRunnable> firstIdleTask =
        NewCancelableRunnableFunction("FirstIdleRunnable", FirstIdle);
    gFirstIdleTask = firstIdleTask;
    if (NS_FAILED(NS_IdleDispatchToCurrentThread(firstIdleTask.forget()))) {
      gFirstIdleTask = nullptr;
      hasRunOnce = false;
    }
  }

  return nsIContentChild::RecvPBrowserConstructor(
      aActor, aTabId, aSameTabGroupAs, aContext, aChromeFlags, aCpID,
      aIsForBrowser);
}

void ContentChild::GetAvailableDictionaries(
    InfallibleTArray<nsString>& aDictionaries) {
  aDictionaries = mAvailableDictionaries;
}

PFileDescriptorSetChild* ContentChild::SendPFileDescriptorSetConstructor(
    const FileDescriptor& aFD) {
  if (IsShuttingDown()) {
    return nullptr;
  }

  return PContentChild::SendPFileDescriptorSetConstructor(aFD);
}

PFileDescriptorSetChild* ContentChild::AllocPFileDescriptorSetChild(
    const FileDescriptor& aFD) {
  return nsIContentChild::AllocPFileDescriptorSetChild(aFD);
}

bool ContentChild::DeallocPFileDescriptorSetChild(
    PFileDescriptorSetChild* aActor) {
  return nsIContentChild::DeallocPFileDescriptorSetChild(aActor);
}

bool ContentChild::DeallocPBrowserChild(PBrowserChild* aIframe) {
  return nsIContentChild::DeallocPBrowserChild(aIframe);
}

PIPCBlobInputStreamChild* ContentChild::AllocPIPCBlobInputStreamChild(
    const nsID& aID, const uint64_t& aSize) {
  return nsIContentChild::AllocPIPCBlobInputStreamChild(aID, aSize);
}

bool ContentChild::DeallocPIPCBlobInputStreamChild(
    PIPCBlobInputStreamChild* aActor) {
  return nsIContentChild::DeallocPIPCBlobInputStreamChild(aActor);
}

mozilla::PRemoteSpellcheckEngineChild*
ContentChild::AllocPRemoteSpellcheckEngineChild() {
  MOZ_CRASH(
      "Default Constructor for PRemoteSpellcheckEngineChild should never be "
      "called");
  return nullptr;
}

bool ContentChild::DeallocPRemoteSpellcheckEngineChild(
    PRemoteSpellcheckEngineChild* child) {
  delete child;
  return true;
}

PPresentationChild* ContentChild::AllocPPresentationChild() {
  MOZ_CRASH("We should never be manually allocating PPresentationChild actors");
  return nullptr;
}

bool ContentChild::DeallocPPresentationChild(PPresentationChild* aActor) {
  delete aActor;
  return true;
}

mozilla::ipc::IPCResult ContentChild::RecvNotifyPresentationReceiverLaunched(
    PBrowserChild* aIframe, const nsString& aSessionId) {
  nsCOMPtr<nsIDocShell> docShell =
      do_GetInterface(static_cast<TabChild*>(aIframe)->WebNavigation());
  NS_WARNING_ASSERTION(docShell, "WebNavigation failed");

  nsCOMPtr<nsIPresentationService> service =
      do_GetService(PRESENTATION_SERVICE_CONTRACTID);
  NS_WARNING_ASSERTION(service, "presentation service is missing");

  Unused << NS_WARN_IF(
      NS_FAILED(static_cast<PresentationIPCService*>(service.get())
                    ->MonitorResponderLoading(aSessionId, docShell)));

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvNotifyPresentationReceiverCleanUp(
    const nsString& aSessionId) {
  nsCOMPtr<nsIPresentationService> service =
      do_GetService(PRESENTATION_SERVICE_CONTRACTID);
  NS_WARNING_ASSERTION(service, "presentation service is missing");

  Unused << NS_WARN_IF(NS_FAILED(service->UntrackSessionInfo(
      aSessionId, nsIPresentationService::ROLE_RECEIVER)));

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvNotifyEmptyHTTPCache() {
  MOZ_ASSERT(NS_IsMainThread());
  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
  obs->NotifyObservers(nullptr, "cacheservice:empty-cache", nullptr);
  return IPC_OK();
}

PHalChild* ContentChild::AllocPHalChild() { return CreateHalChild(); }

bool ContentChild::DeallocPHalChild(PHalChild* aHal) {
  delete aHal;
  return true;
}

devtools::PHeapSnapshotTempFileHelperChild*
ContentChild::AllocPHeapSnapshotTempFileHelperChild() {
  return devtools::HeapSnapshotTempFileHelperChild::Create();
}

bool ContentChild::DeallocPHeapSnapshotTempFileHelperChild(
    devtools::PHeapSnapshotTempFileHelperChild* aHeapSnapshotHelper) {
  delete aHeapSnapshotHelper;
  return true;
}

PTestShellChild* ContentChild::AllocPTestShellChild() {
  return new TestShellChild();
}

bool ContentChild::DeallocPTestShellChild(PTestShellChild* shell) {
  delete shell;
  return true;
}

jsipc::CPOWManager* ContentChild::GetCPOWManager() {
  if (PJavaScriptChild* c =
          LoneManagedOrNullAsserts(ManagedPJavaScriptChild())) {
    return CPOWManagerFor(c);
  }
  if (recordreplay::IsMiddleman()) {
    return nullptr;
  }
  return CPOWManagerFor(SendPJavaScriptConstructor());
}

mozilla::ipc::IPCResult ContentChild::RecvPTestShellConstructor(
    PTestShellChild* actor) {
  return IPC_OK();
}

void ContentChild::UpdateCookieStatus(nsIChannel* aChannel) {
  RefPtr<CookieServiceChild> csChild = CookieServiceChild::GetSingleton();
  NS_ASSERTION(csChild, "Couldn't get CookieServiceChild");

  csChild->TrackCookieLoad(aChannel);
}

PScriptCacheChild* ContentChild::AllocPScriptCacheChild(
    const FileDescOrError& cacheFile, const bool& wantCacheData) {
  return new loader::ScriptCacheChild();
}

bool ContentChild::DeallocPScriptCacheChild(PScriptCacheChild* cache) {
  delete static_cast<loader::ScriptCacheChild*>(cache);
  return true;
}

mozilla::ipc::IPCResult ContentChild::RecvPScriptCacheConstructor(
    PScriptCacheChild* actor, const FileDescOrError& cacheFile,
    const bool& wantCacheData) {
  Maybe<FileDescriptor> fd;
  if (cacheFile.type() == cacheFile.TFileDescriptor) {
    fd.emplace(cacheFile.get_FileDescriptor());
  }

  static_cast<loader::ScriptCacheChild*>(actor)->Init(fd, wantCacheData);
  return IPC_OK();
}

PNeckoChild* ContentChild::AllocPNeckoChild() { return new NeckoChild(); }

bool ContentChild::DeallocPNeckoChild(PNeckoChild* necko) {
  delete necko;
  return true;
}

PPrintingChild* ContentChild::AllocPPrintingChild() {
  // The ContentParent should never attempt to allocate the nsPrintingProxy,
  // which implements PPrintingChild. Instead, the nsPrintingProxy service is
  // requested and instantiated via XPCOM, and the constructor of
  // nsPrintingProxy sets up the IPC connection.
  MOZ_CRASH("Should never get here!");
  return nullptr;
}

bool ContentChild::DeallocPPrintingChild(PPrintingChild* printing) {
  return true;
}

PChildToParentStreamChild* ContentChild::SendPChildToParentStreamConstructor(
    PChildToParentStreamChild* aActor) {
  if (IsShuttingDown()) {
    return nullptr;
  }

  return PContentChild::SendPChildToParentStreamConstructor(aActor);
}

PChildToParentStreamChild* ContentChild::AllocPChildToParentStreamChild() {
  return nsIContentChild::AllocPChildToParentStreamChild();
}

bool ContentChild::DeallocPChildToParentStreamChild(
    PChildToParentStreamChild* aActor) {
  return nsIContentChild::DeallocPChildToParentStreamChild(aActor);
}

PParentToChildStreamChild* ContentChild::AllocPParentToChildStreamChild() {
  return nsIContentChild::AllocPParentToChildStreamChild();
}

bool ContentChild::DeallocPParentToChildStreamChild(
    PParentToChildStreamChild* aActor) {
  return nsIContentChild::DeallocPParentToChildStreamChild(aActor);
}

PPSMContentDownloaderChild* ContentChild::AllocPPSMContentDownloaderChild(
    const uint32_t& aCertType) {
  // NB: We don't need aCertType in the child actor.
  RefPtr<PSMContentDownloaderChild> child = new PSMContentDownloaderChild();
  return child.forget().take();
}

bool ContentChild::DeallocPPSMContentDownloaderChild(
    PPSMContentDownloaderChild* aListener) {
  auto* listener = static_cast<PSMContentDownloaderChild*>(aListener);
  RefPtr<PSMContentDownloaderChild> child = dont_AddRef(listener);
  return true;
}

PExternalHelperAppChild* ContentChild::AllocPExternalHelperAppChild(
    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, PBrowserChild* aBrowser) {
  auto* child = new ExternalHelperAppChild();
  child->AddRef();
  return child;
}

bool ContentChild::DeallocPExternalHelperAppChild(
    PExternalHelperAppChild* aService) {
  ExternalHelperAppChild* child =
      static_cast<ExternalHelperAppChild*>(aService);
  child->Release();
  return true;
}

PHandlerServiceChild* ContentChild::AllocPHandlerServiceChild() {
  auto* actor = new HandlerServiceChild();
  actor->AddRef();
  return actor;
}

bool ContentChild::DeallocPHandlerServiceChild(
    PHandlerServiceChild* aHandlerServiceChild) {
  static_cast<HandlerServiceChild*>(aHandlerServiceChild)->Release();
  return true;
}

media::PMediaChild* ContentChild::AllocPMediaChild() {
  return media::AllocPMediaChild();
}

bool ContentChild::DeallocPMediaChild(media::PMediaChild* aActor) {
  return media::DeallocPMediaChild(aActor);
}

PSpeechSynthesisChild* ContentChild::AllocPSpeechSynthesisChild() {
#ifdef MOZ_WEBSPEECH
  MOZ_CRASH("No one should be allocating PSpeechSynthesisChild actors");
#else
  return nullptr;
#endif
}

bool ContentChild::DeallocPSpeechSynthesisChild(PSpeechSynthesisChild* aActor) {
#ifdef MOZ_WEBSPEECH
  delete aActor;
  return true;
#else
  return false;
#endif
}

PWebrtcGlobalChild* ContentChild::AllocPWebrtcGlobalChild() {
#ifdef MOZ_WEBRTC
  auto* child = new WebrtcGlobalChild();
  return child;
#else
  return nullptr;
#endif
}

bool ContentChild::DeallocPWebrtcGlobalChild(PWebrtcGlobalChild* aActor) {
#ifdef MOZ_WEBRTC
  delete static_cast<WebrtcGlobalChild*>(aActor);
  return true;
#else
  return false;
#endif
}

mozilla::ipc::IPCResult ContentChild::RecvRegisterChrome(
    InfallibleTArray<ChromePackage>&& packages,
    InfallibleTArray<SubstitutionMapping>&& resources,
    InfallibleTArray<OverrideMapping>&& overrides, const nsCString& locale,
    const bool& reset) {
  nsCOMPtr<nsIChromeRegistry> registrySvc = nsChromeRegistry::GetService();
  nsChromeRegistryContent* chromeRegistry =
      static_cast<nsChromeRegistryContent*>(registrySvc.get());
  if (!chromeRegistry) {
    return IPC_FAIL(this, "ChromeRegistryContent is null!");
  }
  chromeRegistry->RegisterRemoteChrome(packages, resources, overrides, locale,
                                       reset);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvRegisterChromeItem(
    const ChromeRegistryItem& item) {
  nsCOMPtr<nsIChromeRegistry> registrySvc = nsChromeRegistry::GetService();
  nsChromeRegistryContent* chromeRegistry =
      static_cast<nsChromeRegistryContent*>(registrySvc.get());
  if (!chromeRegistry) {
    return IPC_FAIL(this, "ChromeRegistryContent is null!");
  }
  switch (item.type()) {
    case ChromeRegistryItem::TChromePackage:
      chromeRegistry->RegisterPackage(item.get_ChromePackage());
      break;

    case ChromeRegistryItem::TOverrideMapping:
      chromeRegistry->RegisterOverride(item.get_OverrideMapping());
      break;

    case ChromeRegistryItem::TSubstitutionMapping:
      chromeRegistry->RegisterSubstitution(item.get_SubstitutionMapping());
      break;

    default:
      MOZ_ASSERT(false, "bad chrome item");
      return IPC_FAIL_NO_REASON(this);
  }

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvClearImageCache(
    const bool& privateLoader, const bool& chrome) {
  imgLoader* loader = privateLoader ? imgLoader::PrivateBrowsingLoader()
                                    : imgLoader::NormalLoader();

  loader->ClearCache(chrome);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvSetOffline(const bool& offline) {
  nsCOMPtr<nsIIOService> io(do_GetIOService());
  NS_ASSERTION(io, "IO Service can not be null");

  io->SetOffline(offline);

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvSetConnectivity(
    const bool& connectivity) {
  nsCOMPtr<nsIIOService> io(do_GetIOService());
  nsCOMPtr<nsIIOServiceInternal> ioInternal(do_QueryInterface(io));
  NS_ASSERTION(ioInternal, "IO Service can not be null");

  ioInternal->SetConnectivity(connectivity);

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvSetCaptivePortalState(
    const int32_t& aState) {
  nsCOMPtr<nsICaptivePortalService> cps = do_GetService(NS_CAPTIVEPORTAL_CID);
  if (!cps) {
    return IPC_OK();
  }

  mozilla::net::CaptivePortalService* portal =
      static_cast<mozilla::net::CaptivePortalService*>(cps.get());
  portal->SetStateInChild(aState);

  return IPC_OK();
}

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

  if (AbnormalShutdown == why) {
    NS_WARNING("shutting down early because of crash!");
    ProcessChild::QuickExit();
  }

#ifndef NS_FREE_PERMANENT_DATA
  // In release builds, there's no point in the content process
  // going through the full XPCOM shutdown path, because it doesn't
  // keep persistent state.
  ProcessChild::QuickExit();
#else
  if (gFirstIdleTask) {
    gFirstIdleTask->Cancel();
    gFirstIdleTask = nullptr;
  }

  BlobURLProtocolHandler::RemoveDataEntries();

  mSharedData = nullptr;

  mAlertObservers.Clear();

  mIdleObservers.Clear();

  nsCOMPtr<nsIConsoleService> svc(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
  if (svc) {
    svc->UnregisterListener(mConsoleListener);
    mConsoleListener->mChild = nullptr;
  }
  mIsAlive = false;

  CrashReporterClient::DestroySingleton();

  XRE_ShutdownChildProcess();
#endif  // NS_FREE_PERMANENT_DATA
}

void ContentChild::ProcessingError(Result aCode, const char* aReason) {
  switch (aCode) {
    case MsgDropped:
      NS_WARNING("MsgDropped in ContentChild");
      return;

    case MsgNotKnown:
    case MsgNotAllowed:
    case MsgPayloadError:
    case MsgProcessingError:
    case MsgRouteError:
    case MsgValueError:
      break;

    default:
      MOZ_CRASH("not reached");
  }

  nsDependentCString reason(aReason);
  CrashReporter::AnnotateCrashReport(
      CrashReporter::Annotation::ipc_channel_error, reason);

  MOZ_CRASH("Content child abort due to IPC error");
}

nsresult ContentChild::AddRemoteAlertObserver(const nsString& aData,
                                              nsIObserver* aObserver) {
  NS_ASSERTION(aObserver, "Adding a null observer?");
  mAlertObservers.AppendElement(new AlertObserver(aObserver, aData));
  return NS_OK;
}

mozilla::ipc::IPCResult ContentChild::RecvPreferenceUpdate(const Pref& aPref) {
  Preferences::SetPreference(aPref);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvVarUpdate(const GfxVarUpdate& aVar) {
  gfx::gfxVars::ApplyUpdate(aVar);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvDataStoragePut(
    const nsString& aFilename, const DataStorageItem& aItem) {
  RefPtr<DataStorage> storage = DataStorage::GetFromRawFileName(aFilename);
  if (storage) {
    storage->Put(aItem.key(), aItem.value(), aItem.type());
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvDataStorageRemove(
    const nsString& aFilename, const nsCString& aKey,
    const DataStorageType& aType) {
  RefPtr<DataStorage> storage = DataStorage::GetFromRawFileName(aFilename);
  if (storage) {
    storage->Remove(aKey, aType);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvDataStorageClear(
    const nsString& aFilename) {
  RefPtr<DataStorage> storage = DataStorage::GetFromRawFileName(aFilename);
  if (storage) {
    storage->Clear();
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvNotifyAlertsObserver(
    const nsCString& aType, const nsString& aData) {
  for (uint32_t i = 0; i < mAlertObservers.Length();
       /*we mutate the array during the loop; ++i iff no mutation*/) {
    AlertObserver* observer = mAlertObservers[i];
    if (observer->Observes(aData) && observer->Notify(aType)) {
      // if aType == alertfinished, this alert is done.  we can
      // remove the observer.
      if (aType.Equals(nsDependentCString("alertfinished"))) {
        mAlertObservers.RemoveElementAt(i);
        continue;
      }
    }
    ++i;
  }
  return IPC_OK();
}

// NOTE: This method is being run in the SystemGroup, and thus cannot directly
// touch pages. See GetSpecificMessageEventTarget.
mozilla::ipc::IPCResult ContentChild::RecvNotifyVisited(
    nsTArray<URIParams>&& aURIs) {
  for (const URIParams& uri : aURIs) {
    nsCOMPtr<nsIURI> newURI = DeserializeURI(uri);
    if (!newURI) {
      return IPC_FAIL_NO_REASON(this);
    }
    nsCOMPtr<IHistory> history = services::GetHistoryService();
    if (history) {
      history->NotifyVisited(newURI);
    }
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvLoadProcessScript(
    const nsString& aURL) {
  auto* global = ContentProcessMessageManager::Get();
  global->LoadScript(aURL);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvAsyncMessage(
    const nsString& aMsg, InfallibleTArray<CpowEntry>&& aCpows,
    const IPC::Principal& aPrincipal, const ClonedMessageData& aData) {
  AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING("ContentChild::RecvAsyncMessage",
                                             OTHER, aMsg);
  MMPrinter::Print("ContentChild::RecvAsyncMessage", aMsg, aData);

  CrossProcessCpowHolder cpows(this, aCpows);
  RefPtr<nsFrameMessageManager> cpm =
      nsFrameMessageManager::GetChildProcessManager();
  if (cpm) {
    StructuredCloneData data;
    ipc::UnpackClonedMessageDataForChild(aData, data);
    cpm->ReceiveMessage(cpm, nullptr, aMsg, false, &data, &cpows, aPrincipal,
                        nullptr, IgnoreErrors());
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvRegisterStringBundles(
    nsTArray<mozilla::dom::StringBundleDescriptor>&& aDescriptors) {
  nsCOMPtr<nsIStringBundleService> stringBundleService =
      services::GetStringBundleService();

  for (auto& descriptor : aDescriptors) {
    stringBundleService->RegisterContentBundle(
        descriptor.bundleURL(), descriptor.mapFile(), descriptor.mapSize());
  }

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvUpdateSharedData(
    const FileDescriptor& aMapFile, const uint32_t& aMapSize,
    nsTArray<IPCBlob>&& aBlobs, nsTArray<nsCString>&& aChangedKeys) {
  nsTArray<RefPtr<BlobImpl>> blobImpls(aBlobs.Length());
  for (auto& ipcBlob : aBlobs) {
    blobImpls.AppendElement(IPCBlobUtils::Deserialize(ipcBlob));
  }

  if (mSharedData) {
    mSharedData->Update(aMapFile, aMapSize, std::move(blobImpls),
                        std::move(aChangedKeys));
  } else {
    mSharedData =
        new SharedMap(ContentProcessMessageManager::Get()->GetParentObject(),
                      aMapFile, aMapSize, std::move(blobImpls));
  }

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvGeolocationUpdate(
    nsIDOMGeoPosition* aPosition) {
  RefPtr<nsGeolocationService> gs =
      nsGeolocationService::GetGeolocationService();
  if (!gs) {
    return IPC_OK();
  }
  gs->Update(aPosition);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvGeolocationError(
    const uint16_t& errorCode) {
  RefPtr<nsGeolocationService> gs =
      nsGeolocationService::GetGeolocationService();
  if (!gs) {
    return IPC_OK();
  }
  gs->NotifyError(errorCode);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvUpdateDictionaryList(
    InfallibleTArray<nsString>&& aDictionaries) {
  mAvailableDictionaries = aDictionaries;
  mozInlineSpellChecker::UpdateCanEnableInlineSpellChecking();
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvUpdateFontList(
    InfallibleTArray<SystemFontListEntry>&& aFontList) {
  mFontList = std::move(aFontList);
  gfxPlatform::GetPlatform()->UpdateFontList();
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvUpdateAppLocales(
    nsTArray<nsCString>&& aAppLocales) {
  LocaleService::GetInstance()->AssignAppLocales(aAppLocales);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvUpdateRequestedLocales(
    nsTArray<nsCString>&& aRequestedLocales) {
  LocaleService::GetInstance()->AssignRequestedLocales(aRequestedLocales);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvClearSiteDataReloadNeeded(
    const nsString& aOrigin) {
  // Rebroadcast "clear-site-data-reload-needed".
  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
  if (obs) {
    obs->NotifyObservers(nullptr, "clear-site-data-reload-needed",
                         aOrigin.get());
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvAddPermission(
    const IPC::Permission& permission) {
  nsCOMPtr<nsIPermissionManager> permissionManagerIface =
      services::GetPermissionManager();
  nsPermissionManager* permissionManager =
      static_cast<nsPermissionManager*>(permissionManagerIface.get());
  MOZ_ASSERT(permissionManager,
             "We have no permissionManager in the Content process !");

  // note we do not need to force mUserContextId to the default here because
  // the permission manager does that internally.
  nsAutoCString originNoSuffix;
  OriginAttributes attrs;
  bool success = attrs.PopulateFromOrigin(permission.origin, originNoSuffix);
  NS_ENSURE_TRUE(success, IPC_FAIL_NO_REASON(this));

  nsCOMPtr<nsIURI> uri;
  nsresult rv = NS_NewURI(getter_AddRefs(uri), originNoSuffix);
  NS_ENSURE_SUCCESS(rv, IPC_OK());

  nsCOMPtr<nsIPrincipal> principal =
      mozilla::BasePrincipal::CreateCodebasePrincipal(uri, attrs);

  // child processes don't care about modification time.
  int64_t modificationTime = 0;

  permissionManager->AddInternal(
      principal, nsCString(permission.type), permission.capability, 0,
      permission.expireType, permission.expireTime, modificationTime,
      nsPermissionManager::eNotify, nsPermissionManager::eNoDBOperation);

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvRemoveAllPermissions() {
  nsCOMPtr<nsIPermissionManager> permissionManagerIface =
      services::GetPermissionManager();
  nsPermissionManager* permissionManager =
      static_cast<nsPermissionManager*>(permissionManagerIface.get());
  MOZ_ASSERT(permissionManager,
             "We have no permissionManager in the Content process !");

  permissionManager->RemoveAllFromIPC();
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvFlushMemory(const nsString& reason) {
  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
  if (os) {
    os->NotifyObservers(nullptr, "memory-pressure", reason.get());
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvActivateA11y(
    const uint32_t& aMainChromeTid, const uint32_t& aMsaaID) {
#ifdef ACCESSIBILITY
#ifdef XP_WIN
  MOZ_ASSERT(aMainChromeTid != 0);
  mMainChromeTid = aMainChromeTid;

  MOZ_ASSERT(aMsaaID != 0);
  mMsaaID = aMsaaID;
#endif  // XP_WIN

  // Start accessibility in content process if it's running in chrome
  // process.
  GetOrCreateAccService(nsAccessibilityService::eMainProcess);
#endif  // ACCESSIBILITY
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvShutdownA11y() {
#ifdef ACCESSIBILITY
  // Try to shutdown accessibility in content process if it's shutting down in
  // chrome process.
  MaybeShutdownAccService(nsAccessibilityService::eMainProcess);
#endif
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvGarbageCollect() {
  // Rebroadcast the "child-gc-request" so that workers will GC.
  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
  if (obs) {
    obs->NotifyObservers(nullptr, "child-gc-request", nullptr);
  }
  nsJSContext::GarbageCollectNow(JS::gcreason::DOM_IPC);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvCycleCollect() {
  // Rebroadcast the "child-cc-request" so that workers will CC.
  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
  if (obs) {
    obs->NotifyObservers(nullptr, "child-cc-request", nullptr);
  }
  nsJSContext::CycleCollectNow();
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvUnlinkGhosts() {
#ifdef DEBUG
  nsWindowMemoryReporter::UnlinkGhostWindows();
#endif
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvAppInfo(
    const nsCString& version, const nsCString& buildID, const nsCString& name,
    const nsCString& UAName, const nsCString& ID, const nsCString& vendor,
    const nsCString& sourceURL) {
  mAppInfo.version.Assign(version);
  mAppInfo.buildID.Assign(buildID);
  mAppInfo.name.Assign(name);
  mAppInfo.UAName.Assign(UAName);
  mAppInfo.ID.Assign(ID);
  mAppInfo.vendor.Assign(vendor);
  mAppInfo.sourceURL.Assign(sourceURL);

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvRemoteType(
    const nsString& aRemoteType) {
  MOZ_ASSERT(DOMStringIsNull(mRemoteType));

  mRemoteType.Assign(aRemoteType);

  // For non-default ("web") types, update the process name so about:memory's
  // process names are more obvious.
  if (aRemoteType.EqualsLiteral(FILE_REMOTE_TYPE)) {
    SetProcessName(NS_LITERAL_STRING("file:// Content"));
  } else if (aRemoteType.EqualsLiteral(EXTENSION_REMOTE_TYPE)) {
    SetProcessName(NS_LITERAL_STRING("WebExtensions"));
  } else if (aRemoteType.EqualsLiteral(PRIVILEGED_REMOTE_TYPE)) {
    SetProcessName(NS_LITERAL_STRING("Privileged Content"));
  } else if (aRemoteType.EqualsLiteral(LARGE_ALLOCATION_REMOTE_TYPE)) {
    SetProcessName(NS_LITERAL_STRING("Large Allocation Web Content"));
  }

  return IPC_OK();
}

const nsAString& ContentChild::GetRemoteType() const { return mRemoteType; }

mozilla::ipc::IPCResult ContentChild::RecvInitServiceWorkers(
    const ServiceWorkerConfiguration& aConfig) {
  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
  if (!swm) {
    // browser shutdown began
    return IPC_OK();
  }
  swm->LoadRegistrations(aConfig.serviceWorkerRegistrations());
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvInitBlobURLs(
    nsTArray<BlobURLRegistrationData>&& aRegistrations) {
  for (uint32_t i = 0; i < aRegistrations.Length(); ++i) {
    BlobURLRegistrationData& registration = aRegistrations[i];
    RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(registration.blob());
    MOZ_ASSERT(blobImpl);

    BlobURLProtocolHandler::AddDataEntry(registration.url(),
                                         registration.principal(), blobImpl);
    // If we have received an already-revoked blobURL, we have to keep it alive
    // for a while (see BlobURLProtocolHandler) in order to support pending
    // operations such as navigation, download and so on.
    if (registration.revoked()) {
      BlobURLProtocolHandler::RemoveDataEntry(registration.url(), false);
    }
  }

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvLastPrivateDocShellDestroyed() {
  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
  obs->NotifyObservers(nullptr, "last-pb-context-exited", nullptr);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvNotifyProcessPriorityChanged(
    const hal::ProcessPriority& aPriority) {
  nsCOMPtr<nsIObserverService> os = services::GetObserverService();
  NS_ENSURE_TRUE(os, IPC_OK());

  RefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();
  props->SetPropertyAsInt32(NS_LITERAL_STRING("priority"),
                            static_cast<int32_t>(aPriority));

  os->NotifyObservers(static_cast<nsIPropertyBag2*>(props),
                      "ipc:process-priority-changed", nullptr);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvMinimizeMemoryUsage() {
  nsCOMPtr<nsIMemoryReporterManager> mgr =
      do_GetService("@mozilla.org/memory-reporter-manager;1");
  NS_ENSURE_TRUE(mgr, IPC_OK());

  Unused << mgr->MinimizeMemoryUsage(/* callback = */ nullptr);
  return IPC_OK();
}

void ContentChild::AddIdleObserver(nsIObserver* aObserver,
                                   uint32_t aIdleTimeInS) {
  MOZ_ASSERT(aObserver, "null idle observer");
  // Make sure aObserver isn't released while we wait for the parent
  aObserver->AddRef();
  SendAddIdleObserver(reinterpret_cast<uint64_t>(aObserver), aIdleTimeInS);
  mIdleObservers.PutEntry(aObserver);
}

void ContentChild::RemoveIdleObserver(nsIObserver* aObserver,
                                      uint32_t aIdleTimeInS) {
  MOZ_ASSERT(aObserver, "null idle observer");
  SendRemoveIdleObserver(reinterpret_cast<uint64_t>(aObserver), aIdleTimeInS);
  aObserver->Release();
  mIdleObservers.RemoveEntry(aObserver);
}

mozilla::ipc::IPCResult ContentChild::RecvNotifyIdleObserver(
    const uint64_t& aObserver, const nsCString& aTopic,
    const nsString& aTimeStr) {
  nsIObserver* observer = reinterpret_cast<nsIObserver*>(aObserver);
  if (mIdleObservers.Contains(observer)) {
    observer->Observe(nullptr, aTopic.get(), aTimeStr.get());
  } else {
    NS_WARNING("Received notification for an idle observer that was removed.");
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvLoadAndRegisterSheet(
    const URIParams& aURI, const uint32_t& aType) {
  nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
  if (!uri) {
    return IPC_OK();
  }

  nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance();
  if (sheetService) {
    sheetService->LoadAndRegisterSheet(uri, aType);
  }

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvUnregisterSheet(
    const URIParams& aURI, const uint32_t& aType) {
  nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
  if (!uri) {
    return IPC_OK();
  }

  nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance();
  if (sheetService) {
    sheetService->UnregisterSheet(uri, aType);
  }

  return IPC_OK();
}

POfflineCacheUpdateChild* ContentChild::AllocPOfflineCacheUpdateChild(
    const URIParams& manifestURI, const URIParams& documentURI,
    const PrincipalInfo& aLoadingPrincipalInfo, const bool& stickDocument) {
  MOZ_CRASH("unused");
  return nullptr;
}

bool ContentChild::DeallocPOfflineCacheUpdateChild(
    POfflineCacheUpdateChild* actor) {
  OfflineCacheUpdateChild* offlineCacheUpdate =
      static_cast<OfflineCacheUpdateChild*>(actor);
  NS_RELEASE(offlineCacheUpdate);
  return true;
}

mozilla::ipc::IPCResult ContentChild::RecvDomainSetChanged(
    const uint32_t& aSetType, const uint32_t& aChangeType,
    const OptionalURIParams& aDomain) {
  if (aChangeType == ACTIVATE_POLICY) {
    if (mPolicy) {
      return IPC_OK();
    }
    nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
    MOZ_ASSERT(ssm);
    ssm->ActivateDomainPolicyInternal(getter_AddRefs(mPolicy));
    if (!mPolicy) {
      return IPC_FAIL_NO_REASON(this);
    }
    return IPC_OK();
  }
  if (!mPolicy) {
    MOZ_ASSERT_UNREACHABLE(
        "If the domain policy is not active yet,"
        " the first message should be ACTIVATE_POLICY");
    return IPC_FAIL_NO_REASON(this);
  }

  NS_ENSURE_TRUE(mPolicy, IPC_FAIL_NO_REASON(this));

  if (aChangeType == DEACTIVATE_POLICY) {
    mPolicy->Deactivate();
    mPolicy = nullptr;
    return IPC_OK();
  }

  nsCOMPtr<nsIDomainSet> set;
  switch (aSetType) {
    case BLOCKLIST:
      mPolicy->GetBlocklist(getter_AddRefs(set));
      break;
    case SUPER_BLOCKLIST:
      mPolicy->GetSuperBlocklist(getter_AddRefs(set));
      break;
    case ALLOWLIST:
      mPolicy->GetAllowlist(getter_AddRefs(set));
      break;
    case SUPER_ALLOWLIST:
      mPolicy->GetSuperAllowlist(getter_AddRefs(set));
      break;
    default:
      MOZ_ASSERT_UNREACHABLE("Unexpected setType");
      return IPC_FAIL_NO_REASON(this);
  }

  MOZ_ASSERT(set);

  nsCOMPtr<nsIURI> uri = DeserializeURI(aDomain);

  switch (aChangeType) {
    case ADD_DOMAIN:
      NS_ENSURE_TRUE(uri, IPC_FAIL_NO_REASON(this));
      set->Add(uri);
      break;
    case REMOVE_DOMAIN:
      NS_ENSURE_TRUE(uri, IPC_FAIL_NO_REASON(this));
      set->Remove(uri);
      break;
    case CLEAR_DOMAINS:
      set->Clear();
      break;
    default:
      MOZ_ASSERT_UNREACHABLE("Unexpected changeType");
      return IPC_FAIL_NO_REASON(this);
  }

  return IPC_OK();
}

void ContentChild::StartForceKillTimer() {
  if (mForceKillTimer) {
    return;
  }

  int32_t timeoutSecs = StaticPrefs::dom_ipc_tabs_shutdownTimeoutSecs();
  if (timeoutSecs > 0) {
    NS_NewTimerWithFuncCallback(getter_AddRefs(mForceKillTimer),
                                ContentChild::ForceKillTimerCallback, this,
                                timeoutSecs * 1000, nsITimer::TYPE_ONE_SHOT,
                                "dom::ContentChild::StartForceKillTimer");
    MOZ_ASSERT(mForceKillTimer);
  }
}

/* static */ void ContentChild::ForceKillTimerCallback(nsITimer* aTimer,
                                                       void* aClosure) {
  ProcessChild::QuickExit();
}

mozilla::ipc::IPCResult ContentChild::RecvShutdown() {
  nsCOMPtr<nsIObserverService> os = services::GetObserverService();
  if (os) {
    os->NotifyObservers(static_cast<nsIContentChild*>(this),
                        "content-child-will-shutdown", nullptr);
  }

  ShutdownInternal();
  return IPC_OK();
}

void ContentChild::ShutdownInternal() {
  // If we receive the shutdown message from within a nested event loop, we want
  // to wait for that event loop to finish. Otherwise we could prematurely
  // terminate an "unload" or "pagehide" event handler (which might be doing a
  // sync XHR, for example).
  CrashReporter::AnnotateCrashReport(
      CrashReporter::Annotation::IPCShutdownState,
      NS_LITERAL_CSTRING("RecvShutdown"));

  MOZ_ASSERT(NS_IsMainThread());
  RefPtr<nsThread> mainThread = nsThreadManager::get().GetCurrentThread();
  // Note that we only have to check the recursion count for the current
  // cooperative thread. Since the Shutdown message is not labeled with a
  // SchedulerGroup, there can be no other cooperative threads doing work while
  // we're running.
  if (mainThread && mainThread->RecursionDepth() > 1) {
    // We're in a nested event loop. Let's delay for an arbitrary period of
    // time (100ms) in the hopes that the event loop will have finished by
    // then.
    MessageLoop::current()->PostDelayedTask(
        NewRunnableMethod("dom::ContentChild::RecvShutdown", this,
                          &ContentChild::ShutdownInternal),
        100);
    return;
  }

  mShuttingDown = true;

#ifdef NIGHTLY_BUILD
  BackgroundHangMonitor::UnregisterAnnotator(
      PendingInputEventHangAnnotator::sSingleton);
#endif

  if (mPolicy) {
    mPolicy->Deactivate();
    mPolicy = nullptr;
  }

  nsCOMPtr<nsIObserverService> os = services::GetObserverService();
  if (os) {
    os->NotifyObservers(static_cast<nsIContentChild*>(this),
                        "content-child-shutdown", nullptr);
  }

#if defined(XP_WIN)
  mozilla::widget::StopAudioSession();
#endif

  GetIPCChannel()->SetAbortOnError(false);

#ifdef MOZ_GECKO_PROFILER
  if (mProfilerController) {
    nsCString shutdownProfile =
        mProfilerController->GrabShutdownProfileAndShutdown();
    mProfilerController = nullptr;
    // Send the shutdown profile to the parent process through our own
    // message channel, which we know will survive for long enough.
    Unused << SendShutdownProfile(shutdownProfile);
  }
#endif

  // Start a timer that will insure we quickly exit after a reasonable
  // period of time. Prevents shutdown hangs after our connection to the
  // parent closes.
  StartForceKillTimer();

  CrashReporter::AnnotateCrashReport(
      CrashReporter::Annotation::IPCShutdownState,
      NS_LITERAL_CSTRING("SendFinishShutdown (sending)"));
  bool sent = SendFinishShutdown();
  CrashReporter::AnnotateCrashReport(
      CrashReporter::Annotation::IPCShutdownState,
      sent ? NS_LITERAL_CSTRING("SendFinishShutdown (sent)")
           : NS_LITERAL_CSTRING("SendFinishShutdown (failed)"));
}

PBrowserOrId ContentChild::GetBrowserOrId(TabChild* aTabChild) {
  if (!aTabChild || this == aTabChild->Manager()) {
    return PBrowserOrId(aTabChild);
  }
  return PBrowserOrId(aTabChild->GetTabId());
}

mozilla::ipc::IPCResult ContentChild::RecvUpdateWindow(
    const uintptr_t& aChildId) {
#if defined(XP_WIN)
  NS_ASSERTION(aChildId,
               "Expected child hwnd value for remote plugin instance.");
  mozilla::plugins::PluginInstanceParent* parentInstance =
      mozilla::plugins::PluginInstanceParent::LookupPluginInstanceByID(
          aChildId);
  if (parentInstance) {
    // sync! update call to the plugin instance that forces the
    // plugin to paint its child window.
    if (!parentInstance->CallUpdateWindow()) {
      return IPC_FAIL_NO_REASON(this);
    }
  }
  return IPC_OK();
#else
  MOZ_ASSERT(
      false,
      "ContentChild::RecvUpdateWindow calls unexpected on this platform.");
  return IPC_FAIL_NO_REASON(this);
#endif
}

PContentPermissionRequestChild*
ContentChild::AllocPContentPermissionRequestChild(
    const InfallibleTArray<PermissionRequest>& aRequests,
    const IPC::Principal& aPrincipal, const IPC::Principal& aTopLevelPrincipal,
    const bool& aIsHandlingUserInput, const TabId& aTabId) {
  MOZ_CRASH("unused");
  return nullptr;
}

bool ContentChild::DeallocPContentPermissionRequestChild(
    PContentPermissionRequestChild* actor) {
  nsContentPermissionUtils::NotifyRemoveContentPermissionRequestChild(actor);
  auto child = static_cast<RemotePermissionRequest*>(actor);
  child->IPDLRelease();
  return true;
}

PWebBrowserPersistDocumentChild*
ContentChild::AllocPWebBrowserPersistDocumentChild(
    PBrowserChild* aBrowser, const uint64_t& aOuterWindowID) {
  return new WebBrowserPersistDocumentChild();
}

mozilla::ipc::IPCResult ContentChild::RecvPWebBrowserPersistDocumentConstructor(
    PWebBrowserPersistDocumentChild* aActor, PBrowserChild* aBrowser,
    const uint64_t& aOuterWindowID) {
  if (NS_WARN_IF(!aBrowser)) {
    return IPC_FAIL_NO_REASON(this);
  }
  nsCOMPtr<nsIDocument> rootDoc =
      static_cast<TabChild*>(aBrowser)->GetDocument();
  nsCOMPtr<nsIDocument> foundDoc;
  if (aOuterWindowID) {
    foundDoc = nsContentUtils::GetSubdocumentWithOuterWindowId(rootDoc,
                                                               aOuterWindowID);
  } else {
    foundDoc = rootDoc;
  }

  if (!foundDoc) {
    aActor->SendInitFailure(NS_ERROR_NO_CONTENT);
  } else {
    static_cast<WebBrowserPersistDocumentChild*>(aActor)->Start(foundDoc);
  }
  return IPC_OK();
}

bool ContentChild::DeallocPWebBrowserPersistDocumentChild(
    PWebBrowserPersistDocumentChild* aActor) {
  delete aActor;
  return true;
}

mozilla::ipc::IPCResult ContentChild::RecvSetAudioSessionData(
    const nsID& aId, const nsString& aDisplayName, const nsString& aIconPath) {
#if defined(XP_WIN)
  if (NS_FAILED(mozilla::widget::RecvAudioSessionData(aId, aDisplayName,
                                                      aIconPath))) {
    return IPC_OK();
  }

  // Ignore failures here; we can't really do anything about them
  mozilla::widget::StartAudioSession();
  return IPC_OK();
#else
  MOZ_CRASH("Not Reached!");
#endif
}

// This code goes here rather than nsGlobalWindow.cpp because nsGlobalWindow.cpp
// can't include ContentChild.h since it includes windows.h.

static uint64_t gNextWindowID = 0;

// We use only 53 bits for the window ID so that it can be converted to and from
// a JS value without loss of precision. The upper bits of the window ID hold
// the process ID. The lower bits identify the window.
static const uint64_t kWindowIDTotalBits = 53;
static const uint64_t kWindowIDProcessBits = 22;
static const uint64_t kWindowIDWindowBits =
    kWindowIDTotalBits - kWindowIDProcessBits;

// Try to return a window ID that is unique across processes and that will never
// be recycled.
uint64_t NextWindowID() {
  uint64_t processID = 0;
  if (XRE_IsContentProcess()) {
    ContentChild* cc = ContentChild::GetSingleton();
    processID = cc->GetID();
  }

  MOZ_RELEASE_ASSERT(processID < (uint64_t(1) << kWindowIDProcessBits));
  uint64_t processBits =
      processID & ((uint64_t(1) << kWindowIDProcessBits) - 1);

  // Make sure no actual window ends up with mWindowID == 0.
  uint64_t windowID = ++gNextWindowID;

  MOZ_RELEASE_ASSERT(windowID < (uint64_t(1) << kWindowIDWindowBits));
  uint64_t windowBits = windowID & ((uint64_t(1) << kWindowIDWindowBits) - 1);

  // Make sure that the middleman process doesn't generate WindowIDs which
  // conflict with the process it's wrapping (which shares a ContentParentID
  // with it).
  if (recordreplay::IsMiddleman()) {
    windowBits |= uint64_t(1) << (kWindowIDWindowBits - 1);
  }

  return (processBits << kWindowIDWindowBits) | windowBits;
}

mozilla::ipc::IPCResult ContentChild::RecvInvokeDragSession(
    nsTArray<IPCDataTransfer>&& aTransfers, const uint32_t& aAction) {
  nsCOMPtr<nsIDragService> dragService =
      do_GetService("@mozilla.org/widget/dragservice;1");
  if (dragService) {
    dragService->StartDragSession();
    nsCOMPtr<nsIDragSession> session;
    dragService->GetCurrentSession(getter_AddRefs(session));
    if (session) {
      session->SetDragAction(aAction);
      // Check if we are receiving any file objects. If we are we will want
      // to hide any of the other objects coming in from content.
      bool hasFiles = false;
      for (uint32_t i = 0; i < aTransfers.Length() && !hasFiles; ++i) {
        auto& items = aTransfers[i].items();
        for (uint32_t j = 0; j < items.Length() && !hasFiles; ++j) {
          if (items[j].data().type() == IPCDataTransferData::TIPCBlob) {
            hasFiles = true;
          }
        }
      }

      // Add the entries from the IPC to the new DataTransfer
      nsCOMPtr<DataTransfer> dataTransfer =
          new DataTransfer(nullptr, eDragStart, false, -1);
      for (uint32_t i = 0; i < aTransfers.Length(); ++i) {
        auto& items = aTransfers[i].items();
        for (uint32_t j = 0; j < items.Length(); ++j) {
          const IPCDataTransferItem& item = items[j];
          RefPtr<nsVariantCC> variant = new nsVariantCC();
          if (item.data().type() == IPCDataTransferData::TnsString) {
            const nsString& data = item.data().get_nsString();
            variant->SetAsAString(data);
          } else if (item.data().type() == IPCDataTransferData::TShmem) {
            Shmem data = item.data().get_Shmem();
            variant->SetAsACString(
                nsDependentCSubstring(data.get<char>(), data.Size<char>()));
            Unused << DeallocShmem(data);
          } else if (item.data().type() == IPCDataTransferData::TIPCBlob) {
            RefPtr<BlobImpl> blobImpl =
                IPCBlobUtils::Deserialize(item.data().get_IPCBlob());
            variant->SetAsISupports(blobImpl);
          } else {
            continue;
          }
          // We should hide this data from content if we have a file, and we
          // aren't a file.
          bool hidden =
              hasFiles && item.data().type() != IPCDataTransferData::TIPCBlob;
          dataTransfer->SetDataWithPrincipalFromOtherProcess(
              NS_ConvertUTF8toUTF16(item.flavor()), variant, i,
              nsContentUtils::GetSystemPrincipal(), hidden);
        }
      }
      session->SetDataTransfer(dataTransfer);
    }
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvEndDragSession(
    const bool& aDoneDrag, const bool& aUserCancelled,
    const LayoutDeviceIntPoint& aDragEndPoint, const uint32_t& aKeyModifiers) {
  nsCOMPtr<nsIDragService> dragService =
      do_GetService("@mozilla.org/widget/dragservice;1");
  if (dragService) {
    if (aUserCancelled) {
      nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
      if (dragSession) {
        dragSession->UserCancelled();
      }
    }
    static_cast<nsBaseDragService*>(dragService.get())
        ->SetDragEndPoint(aDragEndPoint);
    dragService->EndDragSession(aDoneDrag, aKeyModifiers);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvPush(const nsCString& aScope,
                                               const IPC::Principal& aPrincipal,
                                               const nsString& aMessageId) {
  PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId, Nothing());
  Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers()));
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvPushWithData(
    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.NotifyObserversAndWorkers()));
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvPushSubscriptionChange(
    const nsCString& aScope, const IPC::Principal& aPrincipal) {
  PushSubscriptionChangeDispatcher dispatcher(aScope, aPrincipal);
  Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers()));
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvPushError(
    const nsCString& aScope, const IPC::Principal& aPrincipal,
    const nsString& aMessage, const uint32_t& aFlags) {
  PushErrorDispatcher dispatcher(aScope, aPrincipal, aMessage, aFlags);
  Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers()));
  return IPC_OK();
}

mozilla::ipc::IPCResult
ContentChild::RecvNotifyPushSubscriptionModifiedObservers(
    const nsCString& aScope, const IPC::Principal& aPrincipal) {
  PushSubscriptionModifiedDispatcher dispatcher(aScope, aPrincipal);
  Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObservers()));
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvBlobURLRegistration(
    const nsCString& aURI, const IPCBlob& aBlob,
    const IPC::Principal& aPrincipal) {
  RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(aBlob);
  MOZ_ASSERT(blobImpl);

  BlobURLProtocolHandler::AddDataEntry(aURI, aPrincipal, blobImpl);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvBlobURLUnregistration(
    const nsCString& aURI) {
  BlobURLProtocolHandler::RemoveDataEntry(
      aURI,
      /* aBroadcastToOtherProcesses = */ false);
  return IPC_OK();
}

#if defined(XP_WIN) && defined(ACCESSIBILITY)
bool ContentChild::SendGetA11yContentId() {
  return PContentChild::SendGetA11yContentId(&mMsaaID);
}
#endif  // defined(XP_WIN) && defined(ACCESSIBILITY)

void ContentChild::CreateGetFilesRequest(const nsAString& aDirectoryPath,
                                         bool aRecursiveFlag, nsID& aUUID,
                                         GetFilesHelperChild* aChild) {
  MOZ_ASSERT(aChild);
  MOZ_ASSERT(!mGetFilesPendingRequests.GetWeak(aUUID));

  Unused << SendGetFilesRequest(aUUID, nsString(aDirectoryPath),
                                aRecursiveFlag);
  mGetFilesPendingRequests.Put(aUUID, aChild);
}

void ContentChild::DeleteGetFilesRequest(nsID& aUUID,
                                         GetFilesHelperChild* aChild) {
  MOZ_ASSERT(aChild);
  MOZ_ASSERT(mGetFilesPendingRequests.GetWeak(aUUID));

  Unused << SendDeleteGetFilesRequest(aUUID);
  mGetFilesPendingRequests.Remove(aUUID);
}

mozilla::ipc::IPCResult ContentChild::RecvGetFilesResponse(
    const nsID& aUUID, const GetFilesResponseResult& aResult) {
  GetFilesHelperChild* child = mGetFilesPendingRequests.GetWeak(aUUID);
  // This object can already been deleted in case DeleteGetFilesRequest has
  // been called when the response was sending by the parent.
  if (!child) {
    return IPC_OK();
  }

  if (aResult.type() == GetFilesResponseResult::TGetFilesResponseFailure) {
    child->Finished(aResult.get_GetFilesResponseFailure().errorCode());
  } else {
    MOZ_ASSERT(aResult.type() ==
               GetFilesResponseResult::TGetFilesResponseSuccess);

    const nsTArray<IPCBlob>& ipcBlobs =
        aResult.get_GetFilesResponseSuccess().blobs();

    bool succeeded = true;
    for (uint32_t i = 0; succeeded && i < ipcBlobs.Length(); ++i) {
      RefPtr<BlobImpl> impl = IPCBlobUtils::Deserialize(ipcBlobs[i]);
      succeeded = child->AppendBlobImpl(impl);
    }

    child->Finished(succeeded ? NS_OK : NS_ERROR_OUT_OF_MEMORY);
  }

  mGetFilesPendingRequests.Remove(aUUID);
  return IPC_OK();
}

/* static */ void ContentChild::FatalErrorIfNotUsingGPUProcess(
    const char* const aErrorMsg, base::ProcessId aOtherPid) {
  // If we're communicating with the same process or the UI process then we
  // want to crash normally. Otherwise we want to just warn as the other end
  // must be the GPU process and it crashing shouldn't be fatal for us.
  if (aOtherPid == base::GetCurrentProcId() ||
      (GetSingleton() && GetSingleton()->OtherPid() == aOtherPid)) {
    mozilla::ipc::FatalError(aErrorMsg, false);
  } else {
    nsAutoCString formattedMessage("IPDL error: \"");
    formattedMessage.AppendASCII(aErrorMsg);
    formattedMessage.AppendLiteral(R"(".)");
    NS_WARNING(formattedMessage.get());
  }
}

PURLClassifierChild* ContentChild::AllocPURLClassifierChild(
    const Principal& aPrincipal, const bool& aUseTrackingProtection,
    bool* aSuccess) {
  *aSuccess = true;
  return new URLClassifierChild();
}

bool ContentChild::DeallocPURLClassifierChild(PURLClassifierChild* aActor) {
  MOZ_ASSERT(aActor);
  delete aActor;
  return true;
}

PURLClassifierLocalChild* ContentChild::AllocPURLClassifierLocalChild(
    const URIParams& aUri, const nsTArray<IPCURLClassifierFeature>& aFeatures) {
  return new URLClassifierLocalChild();
}

bool ContentChild::DeallocPURLClassifierLocalChild(
    PURLClassifierLocalChild* aActor) {
  MOZ_ASSERT(aActor);
  delete aActor;
  return true;
}

PLoginReputationChild* ContentChild::AllocPLoginReputationChild(
    const URIParams& aUri) {
  return new PLoginReputationChild();
}

bool ContentChild::DeallocPLoginReputationChild(PLoginReputationChild* aActor) {
  MOZ_ASSERT(aActor);
  delete aActor;
  return true;
}

// The IPC code will call this method asking us to assign an event target to new
// actors created by the ContentParent.
already_AddRefed<nsIEventTarget> ContentChild::GetConstructedEventTarget(
    const Message& aMsg) {
  // Currently we only set targets for PBrowser.
  if (aMsg.type() != PContent::Msg_PBrowserConstructor__ID) {
    return nullptr;
  }

  return nsIContentChild::GetConstructedEventTarget(aMsg);
}

void ContentChild::FileCreationRequest(nsID& aUUID, FileCreatorHelper* aHelper,
                                       const nsAString& aFullPath,
                                       const nsAString& aType,
                                       const nsAString& aName,
                                       const Optional<int64_t>& aLastModified,
                                       bool aExistenceCheck,
                                       bool aIsFromNsIFile) {
  MOZ_ASSERT(aHelper);

  bool lastModifiedPassed = false;
  int64_t lastModified = 0;
  if (aLastModified.WasPassed()) {
    lastModifiedPassed = true;
    lastModified = aLastModified.Value();
  }

  Unused << SendFileCreationRequest(
      aUUID, nsString(aFullPath), nsString(aType), nsString(aName),
      lastModifiedPassed, lastModified, aExistenceCheck, aIsFromNsIFile);
  mFileCreationPending.Put(aUUID, aHelper);
}

mozilla::ipc::IPCResult ContentChild::RecvFileCreationResponse(
    const nsID& aUUID, const FileCreationResult& aResult) {
  FileCreatorHelper* helper = mFileCreationPending.GetWeak(aUUID);
  if (!helper) {
    return IPC_FAIL_NO_REASON(this);
  }

  if (aResult.type() == FileCreationResult::TFileCreationErrorResult) {
    helper->ResponseReceived(nullptr,
                             aResult.get_FileCreationErrorResult().errorCode());
  } else {
    MOZ_ASSERT(aResult.type() ==
               FileCreationResult::TFileCreationSuccessResult);

    RefPtr<BlobImpl> impl = IPCBlobUtils::Deserialize(
        aResult.get_FileCreationSuccessResult().blob());
    helper->ResponseReceived(impl, NS_OK);
  }

  mFileCreationPending.Remove(aUUID);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvActivate(PBrowserChild* aTab) {
  TabChild* tab = static_cast<TabChild*>(aTab);
  return tab->RecvActivate();
}

mozilla::ipc::IPCResult ContentChild::RecvDeactivate(PBrowserChild* aTab) {
  TabChild* tab = static_cast<TabChild*>(aTab);
  return tab->RecvDeactivate();
}

mozilla::ipc::IPCResult ContentChild::RecvProvideAnonymousTemporaryFile(
    const uint64_t& aID, const FileDescOrError& aFDOrError) {
  nsAutoPtr<AnonymousTemporaryFileCallback> callback;
  mPendingAnonymousTemporaryFiles.Remove(aID, &callback);
  MOZ_ASSERT(callback);

  PRFileDesc* prfile = nullptr;
  if (aFDOrError.type() == FileDescOrError::Tnsresult) {
    DebugOnly<nsresult> rv = aFDOrError.get_nsresult();
    MOZ_ASSERT(NS_FAILED(rv));
  } else {
    auto rawFD = aFDOrError.get_FileDescriptor().ClonePlatformHandle();
    prfile = PR_ImportFile(PROsfd(rawFD.release()));
  }
  (*callback)(prfile);
  return IPC_OK();
}

nsresult ContentChild::AsyncOpenAnonymousTemporaryFile(
    const AnonymousTemporaryFileCallback& aCallback) {
  MOZ_ASSERT(NS_IsMainThread());

  static uint64_t id = 0;
  auto newID = id++;
  if (!SendRequestAnonymousTemporaryFile(newID)) {
    return NS_ERROR_FAILURE;
  }

  // Remember the association with the callback.
  MOZ_ASSERT(!mPendingAnonymousTemporaryFiles.Get(newID));
  mPendingAnonymousTemporaryFiles.LookupOrAdd(newID, aCallback);
  return NS_OK;
}

mozilla::ipc::IPCResult ContentChild::RecvSetPermissionsWithKey(
    const nsCString& aPermissionKey, nsTArray<IPC::Permission>&& aPerms) {
  nsCOMPtr<nsIPermissionManager> permissionManager =
      services::GetPermissionManager();
  permissionManager->SetPermissionsWithKey(aPermissionKey, aPerms);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvRefreshScreens(
    nsTArray<ScreenDetails>&& aScreens) {
  ScreenManager& screenManager = ScreenManager::GetSingleton();
  screenManager.Refresh(std::move(aScreens));
  return IPC_OK();
}

already_AddRefed<nsIEventTarget> ContentChild::GetEventTargetFor(
    TabChild* aTabChild) {
  return IToplevelProtocol::GetActorEventTarget(aTabChild);
}

mozilla::ipc::IPCResult ContentChild::RecvSetPluginList(
    const uint32_t& aPluginEpoch, nsTArray<plugins::PluginTag>&& aPluginTags,
    nsTArray<plugins::FakePluginTag>&& aFakePluginTags) {
  RefPtr<nsPluginHost> host = nsPluginHost::GetInst();
  host->SetPluginsInContent(aPluginEpoch, aPluginTags, aFakePluginTags);
  return IPC_OK();
}

PClientOpenWindowOpChild* ContentChild::AllocPClientOpenWindowOpChild(
    const ClientOpenWindowArgs& aArgs) {
  return AllocClientOpenWindowOpChild();
}

IPCResult ContentChild::RecvPClientOpenWindowOpConstructor(
    PClientOpenWindowOpChild* aActor, const ClientOpenWindowArgs& aArgs) {
  InitClientOpenWindowOpChild(aActor, aArgs);
  return IPC_OK();
}

bool ContentChild::DeallocPClientOpenWindowOpChild(
    PClientOpenWindowOpChild* aActor) {
  return DeallocClientOpenWindowOpChild(aActor);
}

mozilla::ipc::IPCResult ContentChild::RecvShareCodeCoverageMutex(
    const CrossProcessMutexHandle& aHandle) {
#ifdef MOZ_CODE_COVERAGE
  CodeCoverageHandler::Init(aHandle);
  return IPC_OK();
#else
  MOZ_CRASH("Shouldn't receive this message in non-code coverage builds!");
#endif
}

mozilla::ipc::IPCResult ContentChild::RecvFlushCodeCoverageCounters(
    FlushCodeCoverageCountersResolver&& aResolver) {
#ifdef MOZ_CODE_COVERAGE
  CodeCoverageHandler::FlushCounters();
  aResolver(/* unused */ true);
  return IPC_OK();
#else
  MOZ_CRASH("Shouldn't receive this message in non-code coverage builds!");
#endif
}

mozilla::ipc::IPCResult ContentChild::RecvGetMemoryUniqueSetSize(
    GetMemoryUniqueSetSizeResolver&& aResolver) {
  MemoryTelemetry::Get().GetUniqueSetSize(std::move(aResolver));
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvSetInputEventQueueEnabled() {
  nsThreadManager::get().EnableMainThreadEventPrioritization();
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvFlushInputEventQueue() {
  nsThreadManager::get().FlushInputEventPrioritization();
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvSuspendInputEventQueue() {
  nsThreadManager::get().SuspendInputEventPrioritization();
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvResumeInputEventQueue() {
  nsThreadManager::get().ResumeInputEventPrioritization();
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvAddDynamicScalars(
    nsTArray<DynamicScalarDefinition>&& aDefs) {
  TelemetryIPC::AddDynamicScalarDefinitions(aDefs);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvSaveRecording(
    const FileDescriptor& aFile) {
  recordreplay::parent::SaveRecording(aFile);
  return IPC_OK();
}

already_AddRefed<nsIEventTarget> ContentChild::GetSpecificMessageEventTarget(
    const Message& aMsg) {
  switch (aMsg.type()) {
    // Javascript
    case PJavaScript::Msg_DropTemporaryStrongReferences__ID:
    case PJavaScript::Msg_DropObject__ID:

    // Navigation
    case PContent::Msg_NotifyVisited__ID:

    // Storage API
    case PContent::Msg_DataStoragePut__ID:
    case PContent::Msg_DataStorageRemove__ID:
    case PContent::Msg_DataStorageClear__ID:

    // Blob and BlobURL
    case PContent::Msg_BlobURLRegistration__ID:
    case PContent::Msg_BlobURLUnregistration__ID:
    case PContent::Msg_InitBlobURLs__ID:
    case PContent::Msg_PIPCBlobInputStreamConstructor__ID:
    case PContent::Msg_StoreAndBroadcastBlobURLRegistration__ID:

      return do_AddRef(SystemGroup::EventTargetFor(TaskCategory::Other));

    default:
      return nullptr;
  }
}

void ContentChild::OnChannelReceivedMessage(const Message& aMsg) {
  if (aMsg.is_sync() && !aMsg.is_reply()) {
    LSObject::OnSyncMessageReceived();
  }

#ifdef NIGHTLY_BUILD
  if (nsContentUtils::IsMessageInputEvent(aMsg)) {
    mPendingInputEvents++;
  }
#endif
}

#ifdef NIGHTLY_BUILD
PContentChild::Result ContentChild::OnMessageReceived(const Message& aMsg) {
  if (nsContentUtils::IsMessageInputEvent(aMsg)) {
    DebugOnly<uint32_t> prevEvts = mPendingInputEvents--;
    MOZ_ASSERT(prevEvts > 0);
  }

  return PContentChild::OnMessageReceived(aMsg);
}
#endif

PContentChild::Result ContentChild::OnMessageReceived(const Message& aMsg,
                                                      Message*& aReply) {
  Result result = PContentChild::OnMessageReceived(aMsg, aReply);

  if (aMsg.is_sync()) {
    // OnMessageReceived shouldn't be called for sync replies.
    MOZ_ASSERT(!aMsg.is_reply());

    LSObject::OnSyncMessageHandled();
  }

  return result;
}

}  // namespace dom

#if defined(__OpenBSD__) && defined(MOZ_CONTENT_SANDBOX)
#include <unistd.h>

static LazyLogModule sPledgeLog("SandboxPledge");

bool StartOpenBSDSandbox(GeckoProcessType type) {
  nsAutoCString promisesString;
  nsAutoCString processTypeString;

  switch (type) {
    case GeckoProcessType_Default:
      processTypeString = "main";
      Preferences::GetCString("security.sandbox.pledge.main", promisesString);
      break;

    case GeckoProcessType_Content:
      processTypeString = "content";
      Preferences::GetCString("security.sandbox.pledge.content",
                              promisesString);
      break;

    default:
      MOZ_ASSERT(false, "unknown process type");
      return false;
  };

  if (pledge(promisesString.get(), NULL) == -1) {
    if (errno == EINVAL) {
      MOZ_LOG(sPledgeLog, LogLevel::Error,
              ("pledge promises for %s process is a malformed string: '%s'\n",
               processTypeString.get(), promisesString.get()));
    } else if (errno == EPERM) {
      MOZ_LOG(
          sPledgeLog, LogLevel::Error,
          ("pledge promises for %s process can't elevate privileges: '%s'\n",
           processTypeString.get(), promisesString.get()));
    }
    return false;
  } else {
    MOZ_LOG(sPledgeLog, LogLevel::Debug,
            ("pledged %s process with promises: '%s'\n",
             processTypeString.get(), promisesString.get()));
  }
  return true;
}
#endif

#if !defined(XP_WIN)
bool IsDevelopmentBuild() {
  nsCOMPtr<nsIFile> path = mozilla::Omnijar::GetPath(mozilla::Omnijar::GRE);
  // If the path doesn't exist, we're a dev build.
  return path == nullptr;
}
#endif /* !XP_WIN */

#if defined(XP_MACOSX)
/*
 * Helper function to read a string value for a given key from the .app's
 * Info.plist.
 */
static nsresult GetStringValueFromBundlePlist(const nsAString& aKey,
                                              nsAutoCString& aValue) {
  CFBundleRef mainBundle = CFBundleGetMainBundle();
  if (mainBundle == nullptr) {
    return NS_ERROR_FAILURE;
  }

  // Read this app's bundle Info.plist as a dictionary
  CFDictionaryRef bundleInfoDict = CFBundleGetInfoDictionary(mainBundle);
  if (bundleInfoDict == nullptr) {
    return NS_ERROR_FAILURE;
  }

  nsAutoCString keyAutoCString = NS_ConvertUTF16toUTF8(aKey);
  CFStringRef key = CFStringCreateWithCString(
      kCFAllocatorDefault, keyAutoCString.get(), kCFStringEncodingUTF8);
  if (key == nullptr) {
    return NS_ERROR_FAILURE;
  }

  CFStringRef value = (CFStringRef)CFDictionaryGetValue(bundleInfoDict, key);
  CFRelease(key);
  if (value == nullptr) {
    return NS_ERROR_FAILURE;
  }

  CFIndex valueLength = CFStringGetLength(value);
  if (valueLength == 0) {
    return NS_ERROR_FAILURE;
  }

  const char* valueCString =
      CFStringGetCStringPtr(value, kCFStringEncodingUTF8);
  if (valueCString) {
    aValue.Assign(valueCString);
    return NS_OK;
  }

  CFIndex maxLength =
      CFStringGetMaximumSizeForEncoding(valueLength, kCFStringEncodingUTF8) + 1;
  char* valueBuffer = static_cast<char*>(moz_xmalloc(maxLength));

  if (!CFStringGetCString(value, valueBuffer, maxLength,
                          kCFStringEncodingUTF8)) {
    free(valueBuffer);
    return NS_ERROR_FAILURE;
  }

  aValue.Assign(valueBuffer);
  free(valueBuffer);
  return NS_OK;
}

/*
 * Helper function for reading a path string from the .app's Info.plist
 * and returning a directory object for that path with symlinks resolved.
 */
static nsresult GetDirFromBundlePlist(const nsAString& aKey, nsIFile** aDir) {
  nsresult rv;

  nsAutoCString dirPath;
  rv = GetStringValueFromBundlePlist(aKey, dirPath);
  NS_ENSURE_SUCCESS(rv, rv);

  nsCOMPtr<nsIFile> dir;
  rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(dirPath), false,
                       getter_AddRefs(dir));
  NS_ENSURE_SUCCESS(rv, rv);

  rv = dir->Normalize();
  NS_ENSURE_SUCCESS(rv, rv);

  bool isDirectory = false;
  rv = dir->IsDirectory(&isDirectory);
  NS_ENSURE_SUCCESS(rv, rv);
  if (!isDirectory) {
    return NS_ERROR_FILE_NOT_DIRECTORY;
  }

  dir.swap(*aDir);
  return NS_OK;
}

nsresult GetRepoDir(nsIFile** aRepoDir) {
  MOZ_ASSERT(IsDevelopmentBuild());
  return GetDirFromBundlePlist(NS_LITERAL_STRING(MAC_DEV_REPO_KEY), aRepoDir);
}

nsresult GetObjDir(nsIFile** aObjDir) {
  MOZ_ASSERT(IsDevelopmentBuild());
  return GetDirFromBundlePlist(NS_LITERAL_STRING(MAC_DEV_OBJ_KEY), aObjDir);
}
#endif /* XP_MACOSX */

}  // namespace mozilla