/* 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_ANDROID
#  include "AndroidDecoderModule.h"
#endif

#include "BrowserChild.h"
#include "ContentChild.h"
#include "GMPServiceChild.h"
#include "GeckoProfiler.h"
#include "Geolocation.h"
#include "HandlerServiceChild.h"
#include "ScrollingMetrics.h"
#include "imgLoader.h"
#include "mozilla/AppShutdown.h"
#include "mozilla/Attributes.h"
#include "mozilla/BackgroundHangMonitor.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/ClipboardContentAnalysisChild.h"
#include "mozilla/ClipboardReadRequestChild.h"
#include "mozilla/Components.h"
#include "mozilla/FOGIPC.h"
#include "mozilla/HangDetails.h"
#include "mozilla/LoadInfo.h"
#include "mozilla/Logging.h"
#include "mozilla/LookAndFeel.h"
#include "mozilla/MemoryTelemetry.h"
#include "mozilla/NullPrincipal.h"
#include "mozilla/PerfStats.h"
#include "mozilla/Preferences.h"
#include "mozilla/ProcessHangMonitorIPC.h"
#include "mozilla/RemoteLazyInputStreamChild.h"
#include "mozilla/RemoteMediaManagerChild.h"
#include "mozilla/SchedulerGroup.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/SharedStyleSheetCache.h"
#include "mozilla/SimpleEnumerator.h"
#include "mozilla/SpinEventLoopUntil.h"
#include "mozilla/StaticMutex.h"
#include "mozilla/StaticPrefs_browser.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/StaticPrefs_fission.h"
#include "mozilla/StaticPrefs_javascript.h"
#include "mozilla/StaticPrefs_media.h"
#include "mozilla/StaticPrefs_threads.h"
#include "mozilla/StorageAccessAPIHelper.h"
#include "mozilla/TelemetryIPC.h"
#include "mozilla/WebBrowserPersistDocumentChild.h"
#include "mozilla/devtools/HeapSnapshotTempFileHelperChild.h"
#include "mozilla/dom/AutoSuppressEventHandlingAndSuspend.h"
#include "mozilla/dom/BlobImpl.h"
#include "mozilla/dom/BrowserBridgeHost.h"
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/BrowsingContextGroup.h"
#include "mozilla/dom/ChildProcessChannelListener.h"
#include "mozilla/dom/ChildProcessMessageManager.h"
#include "mozilla/dom/ClientManager.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/ContentPlaybackController.h"
#include "mozilla/dom/ContentProcessManager.h"
#include "mozilla/dom/ContentProcessMessageManager.h"
#include "mozilla/dom/DataTransfer.h"
#include "mozilla/dom/DocGroup.h"
#include "mozilla/dom/ExternalHelperAppChild.h"
#include "mozilla/dom/GetFilesHelper.h"
#include "mozilla/dom/IPCBlobUtils.h"
#include "mozilla/dom/InProcessChild.h"
#include "mozilla/dom/JSActorService.h"
#include "mozilla/dom/JSProcessActorBinding.h"
#include "mozilla/dom/JSProcessActorChild.h"
#include "mozilla/dom/LSObject.h"
#include "mozilla/dom/MemoryReportRequest.h"
#include "mozilla/dom/Navigation.h"
#include "mozilla/dom/NavigationHistoryEntry.h"
#include "mozilla/dom/PSessionStorageObserverChild.h"
#include "mozilla/dom/PolicyContainer.h"
#include "mozilla/dom/PostMessageEvent.h"
#include "mozilla/dom/PushNotifier.h"
#include "mozilla/dom/RemoteWorkerDebuggerManagerChild.h"
#include "mozilla/dom/RemoteWorkerService.h"
#include "mozilla/dom/ScreenOrientation.h"
#include "mozilla/dom/ServiceWorkerManager.h"
#include "mozilla/dom/SessionStorageManager.h"
#include "mozilla/dom/SharedScriptCache.h"
#include "mozilla/dom/URLClassifierChild.h"
#include "mozilla/dom/UserActivation.h"
#include "mozilla/dom/WindowGlobalChild.h"
#include "mozilla/dom/WorkerDebugger.h"
#include "mozilla/dom/WorkerDebuggerManager.h"
#include "mozilla/dom/ipc/SharedMap.h"
#include "mozilla/extensions/ExtensionsChild.h"
#include "mozilla/extensions/StreamFilterParent.h"
#include "mozilla/gfx/2D.h"
#include "mozilla/gfx/Logging.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/glean/FOGTransportChild.h"
#include "mozilla/hal_sandbox/PHalChild.h"
#include "mozilla/image/FetchDecodedImage.h"
#include "mozilla/image/RemoteImageProtocolHandler.h"
#include "mozilla/intl/L10nRegistry.h"
#include "mozilla/intl/LocaleService.h"
#include "mozilla/intl/OSPreferences.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/Endpoint.h"
#include "mozilla/ipc/FileDescriptorUtils.h"
#include "mozilla/ipc/GeckoChildProcessHost.h"
#include "mozilla/ipc/ProcessChild.h"
#include "mozilla/ipc/TestShellChild.h"
#include "mozilla/layers/APZChild.h"
#include "mozilla/layers/CompositorManagerChild.h"
#include "mozilla/layers/ContentProcessController.h"
#include "mozilla/layers/ImageBridgeChild.h"
#include "nsNSSComponent.h"
#include "nsXPLookAndFeel.h"
#ifdef NS_PRINTING
#  include "mozilla/layout/RemotePrintJobChild.h"
#endif
#include "ChildProfilerController.h"
#include "mozilla/loader/ScriptCacheActors.h"
#include "mozilla/media/MediaChild.h"
#include "mozilla/net/CaptivePortalService.h"
#include "mozilla/net/ChildDNSService.h"
#include "mozilla/net/CookieServiceChild.h"
#include "mozilla/net/DocumentChannelChild.h"
#include "mozilla/net/HttpChannelChild.h"
#include "mozilla/widget/RemoteLookAndFeel.h"
#include "mozilla/widget/ScreenManager.h"
#include "mozilla/widget/WidgetMessageUtils.h"
#include "mozmemory.h"
#include "nsBaseDragService.h"
#include "nsDocShellLoadTypes.h"
#include "nsFocusManager.h"
#include "nsHttpHandler.h"
#include "nsIConsoleService.h"
#include "nsIInputStreamChannel.h"
#include "nsILayoutHistoryState.h"
#include "nsILoadGroup.h"
#include "nsIOpenWindowInfo.h"
#include "nsISimpleEnumerator.h"
#include "nsIStringBundle.h"
#include "nsIURIMutator.h"
#include "nsOpenWindowInfo.h"
#include "nsQueryObject.h"
#include "nsRefreshDriver.h"
#include "nsSandboxFlags.h"

#if defined(MOZ_SANDBOX)
#  if defined(XP_WIN)
#    include "mozilla/sandboxTarget.h"
#  elif defined(XP_LINUX)
#    include "CubebUtils.h"
#    include "mozilla/Sandbox.h"
#    include "mozilla/SandboxInfo.h"
#    include "mozilla/SandboxProfilerObserver.h"
#  elif defined(XP_MACOSX)
#    include <CoreGraphics/CGError.h>

#    include "mozilla/Sandbox.h"
#    include "mozilla/SandboxSettings.h"
#  elif defined(__OpenBSD__)
#    include <err.h>
#    include <sys/stat.h>
#    include <unistd.h>

#    include <fstream>

#    include "BinaryPath.h"
#    include "SpecialSystemDirectory.h"
#    include "mozilla/SandboxSettings.h"
#    include "mozilla/ipc/UtilityProcessSandboxing.h"
#    include "nsILineInputStream.h"
#  endif
#  if defined(MOZ_DEBUG) && defined(ENABLE_TESTS)
#    include "mozilla/SandboxTestingChild.h"
#  endif
#endif

#include "IHistory.h"
#include "ReferrerInfo.h"
#include "SandboxHal.h"
#include "base/message_loop.h"
#include "base/process_util.h"
#include "base/task.h"
#include "mozInlineSpellChecker.h"
#include "mozilla/GlobalStyleSheetCache.h"
#include "mozilla/dom/BlobURLProtocolHandler.h"
#include "mozilla/dom/PCycleCollectWithLogsChild.h"
#include "mozilla/dom/PerformanceStorage.h"
#include "nsAnonymousTemporaryFile.h"
#include "nsCategoryManagerUtils.h"
#include "nsChromeRegistryContent.h"
#include "nsClipboardProxy.h"
#include "nsContentPermissionHelper.h"
#include "nsDebugImpl.h"
#include "nsDirectoryService.h"
#include "nsDirectoryServiceDefs.h"
#include "nsDirectoryServiceUtils.h"
#include "nsDocShell.h"
#include "nsDocShellLoadState.h"
#include "nsFrameMessageManager.h"
#include "nsHashPropertyBag.h"
#include "nsIConsoleListener.h"
#include "nsICycleCollectorListener.h"
#include "nsIDocShellTreeOwner.h"
#include "nsIDocumentViewer.h"
#include "nsIDragService.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsIMemoryInfoDumper.h"
#include "nsIMemoryReporter.h"
#include "nsIOService.h"
#include "nsIObserverService.h"
#include "nsIScriptError.h"
#include "nsIScriptSecurityManager.h"
#include "nsJSEnvironment.h"
#include "nsJSUtils.h"
#include "nsMemoryInfoDumper.h"
#include "nsNetUtil.h"
#include "nsServiceManagerUtils.h"
#include "nsStyleSheetService.h"
#include "nsThreadManager.h"
#include "nsWindowMemoryReporter.h"
#include "nsXULAppAPI.h"

#ifdef MOZ_WEBRTC
#  include "jsapi/WebrtcGlobalChild.h"
#endif

#include "PermissionMessageUtils.h"
#include "mozilla/Permission.h"
#include "mozilla/PermissionManager.h"
#include "mozilla/dom/PermissionObserver.h"
#include "mozilla/dom/PermissionStatusBinding.h"

#if defined(MOZ_WIDGET_ANDROID)
#  include <sched.h>

#  include "APKOpen.h"
#endif

#ifdef XP_WIN
#  include <process.h>
#  define getpid _getpid
#  include "mozilla/WinDllServices.h"
#endif

#if defined(XP_MACOSX)
#  include <sys/qos.h>

#  include "nsMacUtilsImpl.h"
#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
#  include "mozilla/a11y/DocAccessible.h"
#  include "mozilla/a11y/DocManager.h"
#  include "mozilla/a11y/OuterDocAccessible.h"
#endif

#include "mozilla/dom/File.h"
#include "mozilla/dom/MediaControllerBinding.h"

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

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

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

#  include "mozilla/WidgetUtilsGtk.h"
#  include "nsAppRunner.h"
#endif

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

#ifdef MOZ_WMF_CDM
#  include "mozilla/dom/MediaKeySystemAccess.h"
#endif

extern mozilla::LazyLogModule gSHIPBFCacheLog;

using namespace mozilla;
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::widget;
using mozilla::loader::PScriptCacheChild;

namespace geckoprofiler::markers {
struct ProcessPriorityChange {
  static constexpr Span<const char> MarkerTypeName() {
    return MakeStringSpan("ProcessPriorityChange");
  }
  static void StreamJSONMarkerData(baseprofiler::SpliceableJSONWriter& aWriter,
                                   const ProfilerString8View& aPreviousPriority,
                                   const ProfilerString8View& aNewPriority) {
    aWriter.StringProperty("Before", aPreviousPriority);
    aWriter.StringProperty("After", aNewPriority);
  }
  static MarkerSchema MarkerTypeDisplay() {
    using MS = MarkerSchema;
    MS schema{MS::Location::MarkerChart, MS::Location::MarkerTable};
    schema.AddKeyFormat("Before", MS::Format::String);
    schema.AddKeyFormat("After", MS::Format::String);
    schema.AddStaticLabelValue("Note",
                               "This is a notification of the priority change "
                               "that was done by the parent process");
    schema.SetAllLabels(
        "priority: {marker.data.Before} -> {marker.data.After}");
    return schema;
  }
};

struct ProcessPriority {
  static constexpr Span<const char> MarkerTypeName() {
    return MakeStringSpan("ProcessPriority");
  }
  static void StreamJSONMarkerData(baseprofiler::SpliceableJSONWriter& aWriter,
                                   const ProfilerString8View& aPriority,
                                   const ProfilingState& aProfilingState) {
    aWriter.StringProperty("Priority", aPriority);
    aWriter.StringProperty("Marker cause",
                           ProfilerString8View::WrapNullTerminatedString(
                               ProfilingStateToString(aProfilingState)));
  }
  static MarkerSchema MarkerTypeDisplay() {
    using MS = MarkerSchema;
    MS schema{MS::Location::MarkerChart, MS::Location::MarkerTable};
    schema.AddKeyFormat("Priority", MS::Format::String);
    schema.AddKeyFormat("Marker cause", MS::Format::String);
    schema.SetAllLabels("priority: {marker.data.Priority}");
    return schema;
  }
};
}  // namespace geckoprofiler::markers

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;
      (void)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() = default;
};

NS_IMPL_ISUPPORTS(CycleCollectWithLogsChild::Sink, nsICycleCollectorLogSink);

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.
template <typename CharT>
static void TruncateString(nsTSubstring<CharT>& 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;
    nsAutoCString sourceName;
    nsCString category;
    uint32_t lineNum, colNum, flags;
    bool fromPrivateWindow, fromChromeContext;

    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->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);
    rv = scriptError->GetIsFromChromeContext(&fromChromeContext);
    NS_ENSURE_SUCCESS(rv, rv);

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

      JS::Rooted<JS::Value> 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::Rooted<JS::Value> stackGlobal(cx);
        rv = scriptError->GetStackGlobal(&stackGlobal);
        NS_ENSURE_SUCCESS(rv, rv);

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

        auto data = MakeNotNull<RefPtr<StructuredCloneData>>(
            JS::StructuredCloneScope::DifferentProcess,
            StructuredCloneHolder::TransferringNotSupported);
        ErrorResult err;
        data->Write(cx, stack, err);
        err.WouldReportJSException();
        if (err.Failed()) {
          return err.StealNSResult();
        }

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

    mChild->SendScriptError(msg, sourceName, lineNum, colNum, flags, category,
                            fromPrivateWindow, 0, fromChromeContext);
    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(u"PendingInput"_ns, pending);
    }
  }

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

class ContentChild::ShutdownCanary final {};

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

ContentChild::ContentChild()
    : mIsForBrowser(false), mIsAlive(true), mShuttingDown(false) {
  // This process is a content process, so it's clearly running in
  // multiprocess mode!
  nsDebugImpl::SetMultiprocessMode("Child");

  // Our static analysis doesn't allow capturing ref-counted pointers in
  // lambdas, so we need to hide it in a uintptr_t. This is safe because this
  // lambda will be destroyed in ~ContentChild().
  uintptr_t self = reinterpret_cast<uintptr_t>(this);
  profiler_add_state_change_callback(
      AllProfilingStates(),
      [self](ProfilingState aProfilingState) {
        const ContentChild* selfPtr =
            reinterpret_cast<const ContentChild*>(self);
        PROFILER_MARKER("Process Priority", OTHER,
                        mozilla::MarkerThreadId::MainThread(), ProcessPriority,
                        ProfilerString8View::WrapNullTerminatedString(
                            ProcessPriorityToString(selfPtr->mProcessPriority)),
                        aProfilingState);
      },
      self);

  // 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::XPCOMShutdown);
  }
}

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

ContentChild::~ContentChild() {
  profiler_remove_state_change_callback(reinterpret_cast<uintptr_t>(this));

#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(nsIDOMProcessChild)
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMProcessChild)
NS_INTERFACE_MAP_END

mozilla::ipc::IPCResult ContentChild::RecvSetXPCOMProcessAttributes(
    XPCOMInitData&& aXPCOMInit, NotNull<StructuredCloneData*> aInitialData,
    FullLookAndFeel&& aLookAndFeelData, dom::SystemFontList&& aFontList,
    Maybe<mozilla::ipc::ReadOnlySharedMemoryHandle>&& aSharedUASheetHandle,
    const uintptr_t& aSharedUASheetAddress,
    nsTArray<mozilla::ipc::ReadOnlySharedMemoryHandle>&& aSharedFontListBlocks,
    const bool& aIsReadyForBackgroundProcessing) {
  if (!sShutdownCanary) {
    return IPC_OK();
  }

  mLookAndFeelData = std::move(aLookAndFeelData);
  mFontList = std::move(aFontList);
  mSharedFontListBlocks = std::move(aSharedFontListBlocks);

  gfx::gfxVars::SetValuesForInitialize(aXPCOMInit.gfxNonDefaultVarUpdates());
  PerfStats::SetCollectionMask(aXPCOMInit.perfStatsMask());
  LookAndFeel::EnsureInit();
  InitSharedUASheets(std::move(aSharedUASheetHandle), aSharedUASheetAddress);
  InitXPCOM(aXPCOMInit, aInitialData, aIsReadyForBackgroundProcessing);
  InitGraphicsDeviceData(aXPCOMInit.contentDeviceData());
  RefPtr<net::ChildDNSService> dnsServiceChild =
      dont_AddRef(net::ChildDNSService::GetSingleton());
  if (dnsServiceChild) {
    dnsServiceChild->SetTRRDomain(aXPCOMInit.trrDomain());
    dnsServiceChild->SetTRRModeInChild(aXPCOMInit.trrMode(),
                                       aXPCOMInit.trrModeFromPref());
  }
  return IPC_OK();
}

void ContentChild::Init(mozilla::ipc::UntypedEndpoint&& aEndpoint,
                        const char* aParentBuildID, 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");
    if (!display_name) {
      bool waylandEnabled = false;
#  ifdef MOZ_WAYLAND
      waylandEnabled = IsWaylandEnabled();
#  endif
      if (!waylandEnabled) {
        display_name = PR_GetEnv("DISPLAY");
      }
    }
    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

  MOZ_ASSERT(!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))) {
    MOZ_CRASH("Failed to initialize the thread manager in ContentChild::Init");
  }

  if (!aEndpoint.Bind(this)) {
    MOZ_CRASH("Bind failed in ContentChild::Init");
  }
  sSingleton = this;

  // If communications with the parent have broken down, take the process
  // down so it's not hanging around.
  GetIPCChannel()->SetAbortOnError(true);

  // 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();
  }

#if defined(__OpenBSD__) && defined(MOZ_SANDBOX)
  StartOpenBSDSandbox(GeckoProcessType_Content);
#endif

#ifdef MOZ_X11
#  ifdef MOZ_WIDGET_GTK
  if (GdkIsX11Display() && !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
#endif

  CrashReporterClient::InitSingleton(this);

  mIsForBrowser = aIsForBrowser;

  SetProcessName("Web Content"_ns);

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

#if defined(MOZ_MEMORY) && defined(DEBUG) && !defined(MOZ_UBSAN)
  jemalloc_stats_t stats;
  jemalloc_stats(&stats);
  MOZ_ASSERT(!stats.opt_randomize_small,
             "Content process should not randomize small allocations");
#endif
}

void ContentChild::AddProfileToProcessName(const nsACString& aProfile) {
  nsCOMPtr<nsIPrincipal> isolationPrincipal =
      ContentParent::CreateRemoteTypeIsolationPrincipal(mRemoteType);
  if (isolationPrincipal) {
    if (isolationPrincipal->OriginAttributesRef().IsPrivateBrowsing()) {
      return;
    }
  }

  mProcessName = aProfile + ":"_ns + mProcessName;  //<profile_name>:example.com
}

void ContentChild::SetProcessName(const nsACString& aName,
                                  const nsACString* aSite,
                                  const nsACString* aCurrentProfile) {
  char* name;
  if ((name = PR_GetEnv("MOZ_DEBUG_APP_PROCESS")) && aName.EqualsASCII(name)) {
#ifdef XP_UNIX
    printf_stderr("\n\nCHILDCHILDCHILDCHILD\n  [%s] debug me @%d\n\n", name,
                  getpid());
    sleep(30);
#elif defined(XP_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
  }

  if (aSite) {
    profiler_set_process_name(aName, aSite);
  } else {
    profiler_set_process_name(aName);
  }

  mProcessName = aName;

  // Requires pref flip
  if (aSite && StaticPrefs::fission_processSiteNames()) {
    nsCOMPtr<nsIPrincipal> isolationPrincipal =
        ContentParent::CreateRemoteTypeIsolationPrincipal(mRemoteType);
    if (isolationPrincipal) {
      // DEFAULT_PRIVATE_BROWSING_ID is the value when it's not private
      MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
              ("private = %d, pref = %d",
               isolationPrincipal->OriginAttributesRef().IsPrivateBrowsing(),
               StaticPrefs::fission_processPrivateWindowSiteNames()));
      if (!isolationPrincipal->OriginAttributesRef().IsPrivateBrowsing()
#ifdef NIGHTLY_BUILD
          // Nightly can show site names for private windows, with a second pref
          || StaticPrefs::fission_processPrivateWindowSiteNames()
#endif
      ) {
#if !defined(XP_MACOSX)
        // Mac doesn't have the 15-character limit Linux does
        // Sets profiler process name
        if (isolationPrincipal->SchemeIs("https")) {
          nsAutoCString schemeless;
          isolationPrincipal->GetHostPort(schemeless);
          nsAutoCString originSuffix;
          isolationPrincipal->GetOriginSuffix(originSuffix);
          schemeless.Append(originSuffix);
          mProcessName = schemeless;
        } else
#endif
        {
          mProcessName = *aSite;
        }
      }
    }
  }

  if (StaticPrefs::fission_processProfileName() && aCurrentProfile &&
      !aCurrentProfile->IsEmpty()) {
    AddProfileToProcessName(*aCurrentProfile);
  }

  // else private window, don't change process name, or the pref isn't set
  // mProcessName is always flat (mProcessName == aName)

  mozilla::ipc::SetThisProcessName(mProcessName.get());

  MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
          ("Changed name of process %d to %s", getpid(),
           PromiseFlatCString(mProcessName).get()));
}

static nsresult GetCreateWindowParams(nsIOpenWindowInfo* aOpenWindowInfo,
                                      nsDocShellLoadState* aLoadState,
                                      bool aForceNoReferrer,
                                      nsIReferrerInfo** aReferrerInfo,
                                      nsIPrincipal** aTriggeringPrincipal,
                                      nsIPolicyContainer** aPolicyContainer) {
  if (!aTriggeringPrincipal || !aPolicyContainer) {
    NS_ERROR("aTriggeringPrincipal || aPolicyContainer is null");
    return NS_ERROR_FAILURE;
  }

  if (!aReferrerInfo) {
    NS_ERROR("aReferrerInfo is null");
    return NS_ERROR_FAILURE;
  }

  nsCOMPtr<nsIReferrerInfo> referrerInfo;
  if (aForceNoReferrer) {
    referrerInfo = new ReferrerInfo(nullptr, ReferrerPolicy::_empty, false);
  }
  if (aLoadState && !referrerInfo) {
    referrerInfo = aLoadState->GetReferrerInfo();
  }

  RefPtr<BrowsingContext> parent = aOpenWindowInfo->GetParent();
  nsCOMPtr<nsPIDOMWindowOuter> opener =
      parent ? parent->GetDOMWindow() : nullptr;
  if (!opener) {
    nsCOMPtr<nsIPrincipal> nullPrincipal =
        NullPrincipal::Create(aOpenWindowInfo->GetOriginAttributes());
    if (!referrerInfo) {
      referrerInfo = new ReferrerInfo(nullptr, ReferrerPolicy::_empty);
    }

    referrerInfo.swap(*aReferrerInfo);
    NS_ADDREF(*aTriggeringPrincipal = nullPrincipal);
    return NS_OK;
  }

  nsCOMPtr<Document> doc = opener->GetDoc();
  NS_ADDREF(*aTriggeringPrincipal = doc->NodePrincipal());

  nsCOMPtr<nsIPolicyContainer> policyContainer = doc->GetPolicyContainer();
  if (policyContainer) {
    policyContainer.forget(aPolicyContainer);
  }

  nsCOMPtr<nsIURI> baseURI = doc->GetDocBaseURI();
  if (!baseURI) {
    NS_ERROR("Document didn't return a base URI");
    return NS_ERROR_FAILURE;
  }

  if (!referrerInfo) {
    referrerInfo = new ReferrerInfo(*doc);
  }

  referrerInfo.swap(*aReferrerInfo);
  return NS_OK;
}

nsresult ContentChild::ProvideWindowCommon(
    NotNull<BrowserChild*> aTabOpener, nsIOpenWindowInfo* aOpenWindowInfo,
    uint32_t aChromeFlags, bool aCalledFromJS, nsIURI* aURI,
    const nsAString& aName, const nsACString& aFeatures,
    const UserActivation::Modifiers& aModifiers, bool aForceNoOpener,
    bool aForceNoReferrer, bool aIsPopupRequested,
    nsDocShellLoadState* aLoadState, bool* aWindowIsNew,
    BrowsingContext** aReturn) {
  MOZ_ASSERT(aOpenWindowInfo, "Must have openwindowinfo");

  *aReturn = nullptr;

  nsAutoCString features(aFeatures);

  nsresult rv;

  RefPtr<BrowsingContext> parent = aOpenWindowInfo->GetParent();
  MOZ_DIAGNOSTIC_ASSERT(parent, "We must have a parent BC");

  // Block the attempt to open a new window if the opening BrowsingContext is
  // not marked to use remote tabs. This ensures that the newly opened window is
  // correctly remote.
  if (NS_WARN_IF(!parent->UseRemoteTabs())) {
    return NS_ERROR_ABORT;
  }

  RefPtr<BrowsingContext> openerBC;
  if (!aForceNoOpener) {
    openerBC = parent;
  }

  RefPtr<BrowsingContext> browsingContext = BrowsingContext::CreateDetached(
      nullptr, openerBC, nullptr, aName, BrowsingContext::Type::Content,
      BrowsingContext::CreateDetachedOptions{
          .isPopupRequested = aIsPopupRequested,
          .topLevelCreatedByWebContent = true,
          .isForPrinting = aOpenWindowInfo->GetIsForPrinting(),
      });
  MOZ_ALWAYS_SUCCEEDS(browsingContext->SetRemoteTabs(true));
  MOZ_ALWAYS_SUCCEEDS(browsingContext->SetRemoteSubframes(
      aChromeFlags & nsIWebBrowserChrome::CHROME_FISSION_WINDOW));
  MOZ_ALWAYS_SUCCEEDS(browsingContext->SetOriginAttributes(
      aOpenWindowInfo->GetOriginAttributes()));

  browsingContext->InitPendingInitialization(true);
  auto unsetPending = MakeScopeExit([browsingContext]() {
    (void)browsingContext->SetPendingInitialization(false);
  });

  browsingContext->EnsureAttached();

  // Since bug 543435, aOpenWindowInfo has the right principal to load for the
  // initial document. But creating windows is complicated and our code expects
  // to setup the initial window global and browser pairs before the document.
  // But without a document and window, we cannot use the
  // WindowGlobalActor::WindowInitializer to correctly initialize a window
  // global. So we must use the AboutBlankInitializer to create a dummy window
  // global with corresponding document and window. Then we immediately set the
  // correct principal which causes the creation of a new document, window and
  // window global. These use the dummy window to correctly initialize the
  // window global via a WindowInitializer.
  nsCOMPtr<nsIPrincipal> initialPrincipal =
      NullPrincipal::Create(browsingContext->OriginAttributesRef());
  WindowGlobalInit windowInit = WindowGlobalActor::AboutBlankInitializer(
      browsingContext, initialPrincipal);
  nsCOMPtr<nsIOpenWindowInfo> openWindowInfoInitialPrincipal;
  // Init code expects window global and open window info to have matching
  // principals.
  aOpenWindowInfo->CloneWithPrincipals(
      initialPrincipal, initialPrincipal,
      getter_AddRefs(openWindowInfoInitialPrincipal));

  RefPtr<WindowGlobalChild> windowChild =
      WindowGlobalChild::CreateDisconnected(windowInit);
  if (NS_WARN_IF(!windowChild)) {
    return NS_ERROR_ABORT;
  }

  TabId tabId(nsContentUtils::GenerateTabId());

  auto newChild = MakeNotNull<RefPtr<BrowserChild>>(
      this, tabId, *aTabOpener, browsingContext, aChromeFlags,
      /* aIsTopLevel */ true);

  if (IsShuttingDown()) {
    return NS_ERROR_ABORT;
  }

  // Open a remote endpoint for our PBrowser actor.
  ManagedEndpoint<PBrowserParent> parentEp = OpenPBrowserEndpoint(newChild);
  if (NS_WARN_IF(!parentEp.IsValid())) {
    return NS_ERROR_ABORT;
  }

  // Open a remote endpoint for our PWindowGlobal actor.
  ManagedEndpoint<PWindowGlobalParent> windowParentEp =
      newChild->OpenPWindowGlobalEndpoint(windowChild);
  if (NS_WARN_IF(!windowParentEp.IsValid())) {
    return NS_ERROR_ABORT;
  }

  // Tell the parent process to set up its PBrowserParent.
  PopupIPCTabContext ipcContext(aTabOpener, 0);
  if (NS_WARN_IF(!SendConstructPopupBrowser(
          std::move(parentEp), std::move(windowParentEp), tabId, ipcContext,
          windowInit, aChromeFlags))) {
    return NS_ERROR_ABORT;
  }

  windowChild->Init();
  auto guardNullWindowGlobal = MakeScopeExit([&] {
    if (!windowChild->GetWindowGlobal()) {
      windowChild->Destroy();
    }
  });

  // Now that |newChild| has had its IPC link established, call |Init| to set it
  // up.
  // XXX: This MOZ_KnownLive is only necessary because the static analysis can't
  // tell that NotNull<RefPtr<BrowserChild>> is a strong pointer.
  RefPtr<nsPIDOMWindowOuter> parentWindow =
      parent ? parent->GetDOMWindow() : nullptr;
  if (NS_FAILED(MOZ_KnownLive(newChild)->Init(
          parentWindow, windowChild, openWindowInfoInitialPrincipal))) {
    return NS_ERROR_ABORT;
  }

  // Now change the principal to what it should be according to aOpenWindowInfo.
  // This creates a new document and the timing is quite fragile.
  nsCOMPtr<nsPIDOMWindowOuter> outerWindow = browsingContext->GetDOMWindow();
  NS_ENSURE_TRUE(outerWindow, NS_ERROR_ABORT);
  NS_ENSURE_TRUE(outerWindow->GetExtantDoc(), NS_ERROR_ABORT);
  nsCOMPtr<nsIPrincipal> principalToInherit =
      aOpenWindowInfo->PrincipalToInheritForAboutBlank();
  outerWindow->SetInitialPrincipal(principalToInherit);

  // 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 = [&](CreatedWindowInfo&& info) {
    MOZ_RELEASE_ASSERT(NS_IsMainThread());
    rv = info.rv();
    *aWindowIsNew = info.windowOpened();
    nsTArray<FrameScriptInfo> frameScripts(std::move(info.frameScripts()));
    uint32_t maxTouchPoints = info.maxTouchPoints();
    DimensionInfo dimensionInfo = std::move(info.dimensions());

    // 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 BrowserChild 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;
    }

    ParentShowInfo showInfo(
        u""_ns, /* fakeShowInfo = */ true,
        /* isTransparent = */ false, newChild->WebWidget()->GetDPI(),
        newChild->WebWidget()->RoundsWidgetCoordinatesTo(),
        newChild->WebWidget()->GetDefaultScale().scale,
        newChild->WebWidget()->GetDesktopToDeviceScale().scale);

    newChild->SetMaxTouchPoints(maxTouchPoints);

    if (aForceNoOpener || !parent) {
      MOZ_DIAGNOSTIC_ASSERT(!browsingContext->HadOriginalOpener());
      MOZ_DIAGNOSTIC_ASSERT(browsingContext->GetTopLevelCreatedByWebContent());
      MOZ_DIAGNOSTIC_ASSERT(browsingContext->GetOpenerId() == 0);
    } else {
      MOZ_DIAGNOSTIC_ASSERT(browsingContext->HadOriginalOpener());
      MOZ_DIAGNOSTIC_ASSERT(browsingContext->GetTopLevelCreatedByWebContent());
      MOZ_DIAGNOSTIC_ASSERT(browsingContext->GetOpenerId() == parent->Id());
    }

    // 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 (xpc::IsInAutomation()) {
      if (nsCOMPtr<nsPIDOMWindowOuter> outer =
              do_GetInterface(newChild->WebNavigation())) {
        nsCOMPtr<nsIObserverService> obs(services::GetObserverService());
        obs->NotifyObservers(
            outer, "dangerous:test-only:new-browser-child-ready", nullptr);
      }
    }

    browsingContext.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.
  nsCOMPtr<nsIPrincipal> triggeringPrincipal;
  nsCOMPtr<nsIPolicyContainer> policyContainer;
  nsCOMPtr<nsIReferrerInfo> referrerInfo;
  rv = GetCreateWindowParams(aOpenWindowInfo, aLoadState, aForceNoReferrer,
                             getter_AddRefs(referrerInfo),
                             getter_AddRefs(triggeringPrincipal),
                             getter_AddRefs(policyContainer));
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return rv;
  }

  SendCreateWindow(
      aTabOpener, parent, newChild, aChromeFlags, aCalledFromJS,
      aOpenWindowInfo->GetIsForPrinting(),
      aOpenWindowInfo->GetIsForWindowDotPrint(),
      aOpenWindowInfo->GetIsTopLevelCreatedByWebContent(), aURI, features,
      aModifiers, triggeringPrincipal, policyContainer, referrerInfo,
      aOpenWindowInfo->GetOriginAttributes(),
      aLoadState ? aLoadState->HasValidUserGestureActivation() : false,
      aLoadState ? aLoadState->GetTextDirectiveUserActivation() : false,
      std::move(resolve), std::move(reject));

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

  // We have to wait for a response from SendCreateWindow or 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.

  {
    // Suppress event handling for all contexts in our BrowsingContextGroup so
    // that event handlers cannot target our new window while it's still being
    // opened. Note that pending events that were suppressed while our blocker
    // was active will be dispatched asynchronously from a runnable dispatched
    // to the main event loop after this function returns, not immediately when
    // we leave this scope.
    AutoSuppressEventHandlingAndSuspend seh(browsingContext->Group());

    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("ContentChild::ProvideWindowCommon"_ns,
                       [&]() { return ready; });
    MOZ_RELEASE_ASSERT(ready,
                       "We are on the main thread, so we should not exit this "
                       "loop without ready being true.");
  }

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

  // It's possible for our new BrowsingContext to become discarded during the
  // nested event loop, in which case we shouldn't return it, since our callers
  // will generally not be prepared to deal with that.
  if (*aReturn && (*aReturn)->IsDiscarded()) {
    NS_RELEASE(*aReturn);
    return NS_ERROR_ABORT;
  }

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

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

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

// NOTE: This is atomic only for reading. Modifying this flag is only possible
// using MaybeBecomeUntrusted on the main thread.
static std::atomic<bool> sContentChildIsUntrusted;

/* static */
bool ContentChild::IsUntrusted() {
  return sContentChildIsUntrusted.load(std::memory_order_relaxed);
}

/* static */
void ContentChild::MaybeBecomeUntrusted() {
  MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
  if (!XRE_IsContentProcess() || IsUntrusted()) {
    return;
  }

  ContentChild* cc = ContentChild::GetSingleton();
  MOZ_DIAGNOSTIC_ASSERT(cc->GetRemoteType() != PREALLOC_REMOTE_TYPE,
                        "Prealloc process cannot become untrusted");

  // Never mark the privilegedabout process as untrusted.
  if (cc->GetRemoteType() == PRIVILEGEDABOUT_REMOTE_TYPE) {
    return;
  }

  if (nsCOMPtr<nsIObserverService> obs =
          mozilla::services::GetObserverService()) {
    obs->NotifyObservers(nullptr, kBecameUntrustedTopic.get(), nullptr);
  }

  // Flip the flag & notify last, as the process should be considered trusted
  // while observing kBecomeUntrustedTopic.
  sContentChildIsUntrusted.store(true, std::memory_order_relaxed);
  cc->SendBecomeUntrusted();
}

void ContentChild::GetProcessName(nsACString& aName) const {
  aName = 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::InitSharedUASheets(
    Maybe<mozilla::ipc::ReadOnlySharedMemoryHandle>&& aHandle,
    uintptr_t aAddress) {
  MOZ_ASSERT_IF(!aHandle, !aAddress);

  if (!aAddress) {
    return;
  }

  // Map the shared memory storing the user agent style sheets.  Do this as
  // early as possible to maximize the chance of being able to map at the
  // address we want.
  GlobalStyleSheetCache::SetSharedMemory(std::move(*aHandle), aAddress);
}

void ContentChild::InitXPCOM(
    XPCOMInitData& aXPCOMInit,
    NotNull<mozilla::dom::ipc::StructuredCloneData*> aInitialData,
    bool aIsReadyForBackgroundProcessing) {
#if defined(XP_WIN)
  // DLL services untrusted modules processing depends on
  // BackgroundChild::Startup having been called
  RefPtr<DllServices> dllSvc(DllServices::Get());
  dllSvc->StartUntrustedModulesProcessor(aIsReadyForBackgroundProcessing);
#endif  // defined(XP_WIN)

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

  ClientManager::Startup();

  // RemoteWorkerService will be initialized in RecvRemoteType, to avoid to
  // register it to the RemoteWorkerManager while it is still a prealloc
  // remoteType and defer it to the point the child process is assigned a.
  // actual remoteType.

  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 = std::move(aXPCOMInit.dictionaries());
  MOZ_ASSERT(aXPCOMInit.dictionaries().IsEmpty(),
             "nsTArray is left in an empty but valid state");

  RecvSetOffline(aXPCOMInit.isOffline());
  RecvSetConnectivity(aXPCOMInit.isConnected());

  OSPreferences::GetInstance()->AssignSysLocales(aXPCOMInit.sysLocales());

  LocaleService::GetInstance()->AssignAppLocales(aXPCOMInit.appLocales());
  LocaleService::GetInstance()->AssignRequestedLocales(
      aXPCOMInit.requestedLocales());

  L10nRegistry::RegisterFileSourcesFromParentProcess(
      aXPCOMInit.l10nFileSources());

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

  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();
    }
    IgnoredErrorResult rv;
    JS::Rooted<JS::Value> data(jsapi.cx());
    aInitialData->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 = std::move(aXPCOMInit.userContentSheetURL());
  MOZ_ASSERT(!aXPCOMInit.userContentSheetURL(),
             "RefPtr is left in a null but valid state");
  GlobalStyleSheetCache::SetUserContentCSSURL(ucsURL);

  GfxInfoBase::SetFeatureStatus(std::move(aXPCOMInit.gfxFeatureStatus()));
  MOZ_ASSERT(aXPCOMInit.gfxFeatureStatus().IsEmpty(),
             "nsTArray is left in an empty but valid state");

  // Initialize the RemoteMediaManager thread and its associated PBackground
  // channel.
  RemoteMediaManagerChild::Init();

  Preferences::RegisterCallbackAndCall(&OnFissionBlocklistPrefChange,
                                       kFissionEnforceBlockList);
  Preferences::RegisterCallbackAndCall(&OnFissionBlocklistPrefChange,
                                       kFissionOmitBlockListValues);

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

mozilla::ipc::IPCResult ContentChild::RecvRequestMemoryReport(
    const uint32_t& aGeneration, const bool& aAnonymize,
    const bool& aMinimizeMemoryUsage,
    const Maybe<mozilla::ipc::FileDescriptor>& aDMDFile,
    const RequestMemoryReportResolver& aResolver) {
  nsCString process;
  if (aAnonymize || mRemoteType.IsEmpty()) {
    GetProcessName(process);
  } else {
    process = mRemoteType;
  }
  AppendProcessId(process);
  MOZ_ASSERT(!process.IsEmpty());

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

mozilla::ipc::IPCResult ContentChild::RecvDecodeImage(
    NotNull<nsIURI*> aURI, const ImageIntSize& aSize,
    const ColorScheme& aColorScheme, DecodeImageResolver&& aResolver) {
  // We're about to decode a potentially untrusted image.
  MaybeBecomeUntrusted();

  auto size = aSize.ToUnknownSize();
  // TODO(Bug 1999930): Investigate using  a content-principal for
  // moz-remote-image: requests
  image::FetchDecodedImage(aURI, size, nsContentUtils::GetSystemPrincipal())
      ->Then(
          GetCurrentSerialEventTarget(), __func__,
          [size, aColorScheme,
           aResolver](already_AddRefed<imgIContainer> aImage) {
            using Result = std::tuple<nsresult, mozilla::Maybe<IPCImage>>;

            nsCOMPtr<imgIContainer> image(std::move(aImage));

            RefPtr<gfx::SourceSurface> surface =
                image::RemoteImageProtocolHandler::GetImageSurface(
                    image, size, aColorScheme);
            if (!surface) {
              aResolver(Result(NS_ERROR_FAILURE, Nothing()));
              return;
            }

            if (RefPtr<gfx::DataSourceSurface> dataSurface =
                    surface->GetDataSurface()) {
              if (Maybe<IPCImage> image =
                      nsContentUtils::SurfaceToIPCImage(*dataSurface)) {
                aResolver(Result(NS_OK, std::move(image)));
                return;
              }
            }

            aResolver(Result(NS_ERROR_FAILURE, Nothing()));
            return;
          },
          [aResolver](nsresult aStatus) {
            aResolver(std::tuple<nsresult, mozilla::Maybe<IPCImage>>(
                aStatus, Nothing()));
          });

  return IPC_OK();
}

#if defined(XP_WIN)
mozilla::ipc::IPCResult ContentChild::RecvGetUntrustedModulesData(
    GetUntrustedModulesDataResolver&& aResolver) {
  RefPtr<DllServices> dllSvc(DllServices::Get());
  dllSvc->GetUntrustedModulesData()->Then(
      GetMainThreadSerialEventTarget(), __func__,
      [aResolver](Maybe<UntrustedModulesData>&& aData) {
        aResolver(std::move(aData));
      },
      [aResolver](nsresult aReason) { aResolver(Nothing()); });
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvUnblockUntrustedModulesThread() {
  if (nsCOMPtr<nsIObserverService> obs =
          mozilla::services::GetObserverService()) {
    obs->NotifyObservers(nullptr, "unblock-untrusted-modules-thread", nullptr);
  }
  return IPC_OK();
}
#endif  // defined(XP_WIN)

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::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) {
  mProfilerController = ChildProfilerController::Create(std::move(aEndpoint));
  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(
    GeckoChildID aOtherChildID) {
  if (aOtherChildID == XRE_GetChildID() || aOtherChildID == OtherChildID()) {
    // 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();
}

#if defined(XP_MACOSX)
extern "C" {
void CGSShutdownServerConnections();
};
#endif

mozilla::ipc::IPCResult ContentChild::RecvInitRendering(
    Endpoint<PCompositorManagerChild>&& aCompositor,
    Endpoint<PImageBridgeChild>&& aImageBridge,
    Endpoint<PVRManagerChild>&& aVRBridge,
    Endpoint<PRemoteMediaManagerChild>&& aVideoManager,
    nsTArray<uint32_t>&& namespaces) {
  MOZ_ASSERT(namespaces.Length() == 4);
  const uint32_t compositorManagerNamespace = namespaces[0];
  const uint32_t compositorBridgeNamespace = namespaces[1];
  const uint32_t imageBridgeNamespace = namespaces[2];
  const uint32_t vrManagerNamespace = namespaces[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),
                                    compositorManagerNamespace)) {
    return GetResultForRenderingInitFailure(aCompositor.OtherChildID());
  }
  if (!CompositorManagerChild::CreateContentCompositorBridge(
          compositorBridgeNamespace)) {
    return GetResultForRenderingInitFailure(aCompositor.OtherChildID());
  }
  if (!ImageBridgeChild::InitForContent(std::move(aImageBridge),
                                        imageBridgeNamespace)) {
    return GetResultForRenderingInitFailure(aImageBridge.OtherChildID());
  }
  if (!gfx::VRManagerChild::InitForContent(std::move(aVRBridge),
                                           vrManagerNamespace)) {
    return GetResultForRenderingInitFailure(aVRBridge.OtherChildID());
  }
  RemoteMediaManagerChild::InitForGPUProcess(std::move(aVideoManager));

#if defined(XP_MACOSX) && !defined(MOZ_SANDBOX)
  // 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. When the
  // build is configured with sandbox support, this is called during sandbox
  // setup.
  CGSShutdownServerConnections();
#endif

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvReinitRendering(
    Endpoint<PCompositorManagerChild>&& aCompositor,
    Endpoint<PImageBridgeChild>&& aImageBridge,
    Endpoint<PVRManagerChild>&& aVRBridge,
    Endpoint<PRemoteMediaManagerChild>&& aVideoManager,
    nsTArray<uint32_t>&& namespaces) {
  MOZ_ASSERT(namespaces.Length() == 4);
  const uint32_t compositorManagerNamespace = namespaces[0];
  const uint32_t compositorBridgeNamespace = namespaces[1];
  const uint32_t imageBridgeNamespace = namespaces[2];
  const uint32_t vrManagerNamespace = namespaces[3];

  nsTArray<RefPtr<BrowserChild>> tabs = BrowserChild::GetAll();

  // Re-establish singleton bridges to the compositor.
  if (!CompositorManagerChild::Init(std::move(aCompositor),
                                    compositorManagerNamespace)) {
    return GetResultForRenderingInitFailure(aCompositor.OtherChildID());
  }
  if (!CompositorManagerChild::CreateContentCompositorBridge(
          compositorBridgeNamespace)) {
    return GetResultForRenderingInitFailure(aCompositor.OtherChildID());
  }
  if (!ImageBridgeChild::ReinitForContent(std::move(aImageBridge),
                                          imageBridgeNamespace)) {
    return GetResultForRenderingInitFailure(aImageBridge.OtherChildID());
  }
  if (!gfx::VRManagerChild::InitForContent(std::move(aVRBridge),
                                           vrManagerNamespace)) {
    return GetResultForRenderingInitFailure(aVRBridge.OtherChildID());
  }
  gfxPlatform::GetPlatform()->CompositorUpdated();

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

  // Notify any observers that the compositor has been reinitialized,
  // eg the ZoomConstraintsClients for documents in this process.
  // This must occur after the ReinitRendering call above so that the
  // APZCTreeManagers have been connected.
  nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
  if (observerService) {
    observerService->NotifyObservers(nullptr, "compositor-reinitialized",
                                     nullptr);
  }

  RemoteMediaManagerChild::InitForGPUProcess(std::move(aVideoManager));
  return IPC_OK();
}

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

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

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

static void DisconnectWindowServer(bool aIsSandboxEnabled) {
  // 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.
  // This is required with or without the sandbox enabled. Until the
  // window server is blocked as the policy level, this should be called
  // just before CGSSetDenyWindowServerConnections() so there are no
  // windowserver connections active when CGSSetDenyWindowServerConnections()
  // is called.
  CGSShutdownServerConnections();

  // Actual security benefits are only achieved when we additionally deny
  // future connections using the sandbox policy. WebGL must be remoted if
  // the windowserver connections are blocked. WebGL remoting is disabled
  // for some tests.
  if (aIsSandboxEnabled &&
      Preferences::GetBool(
          "security.sandbox.content.mac.disconnect-windowserver")) {
    CGError result = CGSSetDenyWindowServerConnections(true);
    MOZ_DIAGNOSTIC_ASSERT(result == kCGErrorSuccess);
#  if !MOZ_DIAGNOSTIC_ASSERT_ENABLED
    (void)result;
#  endif
  }
}
#endif

mozilla::ipc::IPCResult ContentChild::RecvSetProcessSandbox(
    const Maybe<mozilla::ipc::FileDescriptor>& aBroker) {
  // We may want to move the sandbox initialization somewhere else
  // at some point; see bug 880808.
#if defined(MOZ_SANDBOX)

  bool sandboxEnabled = true;
#  if defined(XP_LINUX)
  // On Linux, we have to support systems that can't use any sandboxing.
  sandboxEnabled = SandboxInfo::Get().CanSandboxContent();

  if (sandboxEnabled && !StaticPrefs::media_cubeb_sandbox()) {
    // Pre-start audio before sandboxing; see bug 1443612.
    (void)CubebUtils::GetCubeb();
  }

  if (sandboxEnabled) {
    RegisterProfilerObserversForSandboxProfiler();
    sandboxEnabled = SetContentProcessSandbox(
        ContentProcessSandboxParams::ForThisProcess(aBroker));
  }
#  elif defined(XP_WIN)
  mozilla::SandboxTarget::Instance()->LowerContentSandbox();
#  elif defined(XP_MACOSX)
  sandboxEnabled = (GetEffectiveContentSandboxLevel() >= 1);
  DisconnectWindowServer(sandboxEnabled);
#  elif defined(XP_IOS)
  LockdownExtensionKitProcess(ExtensionKitSandboxRevision::Revision1);
#  endif

  CrashReporter::RecordAnnotationBool(
      CrashReporter::Annotation::ContentSandboxEnabled, sandboxEnabled);
#  if defined(XP_LINUX) && !defined(ANDROID)
  CrashReporter::RecordAnnotationU32(
      CrashReporter::Annotation::ContentSandboxCapabilities,
      SandboxInfo::Get().AsInteger());
#  endif /* XP_LINUX && !ANDROID */
#endif   /* MOZ_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;

  ContentChild::GetSingleton()->SendFirstIdle();
}

mozilla::ipc::IPCResult ContentChild::RecvConstructBrowser(
    ManagedEndpoint<PBrowserChild>&& aBrowserEp,
    ManagedEndpoint<PWindowGlobalChild>&& aWindowEp, const TabId& aTabId,
    const IPCTabContext& aContext, const WindowGlobalInit& aWindowInit,
    const uint32_t& aChromeFlags, const ContentParentId& aCpID,
    const bool& aIsForBrowser, const bool& aIsTopLevel) {
  MOZ_DIAGNOSTIC_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_DispatchToCurrentThreadQueue(firstIdleTask.forget(),
                                                  EventQueuePriority::Idle))) {
      gFirstIdleTask = nullptr;
      hasRunOnce = false;
    }
  }

  if (!aBrowserEp.IsValidForManager(this)) {
    return IPC_FAIL(this, "Invalid PBrowserChild endpoint");
  }
  if (!aWindowEp.IsValidForManager(aBrowserEp)) {
    return IPC_FAIL(this, "Invalid PWindowGlobalChild endpoint");
  }

  RefPtr<BrowsingContext> browsingContext =
      BrowsingContext::Get(aWindowInit.context().mBrowsingContextId);
  if (!browsingContext || browsingContext->IsDiscarded()) {
    nsPrintfCString reason("%s initial %s BrowsingContext",
                           browsingContext ? "discarded" : "missing",
                           aIsTopLevel ? "top" : "frame");
    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Warning, ("%s", reason.get()));
    if (!aIsTopLevel) {
      // Recover if the BrowsingContext is missing for a new subframe. The
      // `ManagedEndpoint` instances will be automatically destroyed.
      NS_WARNING(reason.get());
      return IPC_OK();
    }

    // (these are the only possible values of `reason` at this point)
    return browsingContext
               ? IPC_FAIL(this, "discarded initial top BrowsingContext")
               : IPC_FAIL(this, "missing initial top BrowsingContext");
  }
  if (!browsingContext->AncestorsAreCurrent()) {
    MOZ_ASSERT(!aIsTopLevel, "Top level discard was already checked");
    MOZ_LOG(
        BrowsingContext::GetLog(), LogLevel::Warning,
        ("Discarded or inactive ancestor of initial frame BrowsingContext"));
    return IPC_OK();
  }

  if (xpc::IsInAutomation() &&
      StaticPrefs::
          browser_tabs_remote_testOnly_failPBrowserCreation_enabled()) {
    nsAutoCString idString;
    if (NS_SUCCEEDED(Preferences::GetCString(
            "browser.tabs.remote.testOnly.failPBrowserCreation.browsingContext",
            idString))) {
      nsresult rv = NS_OK;
      uint64_t bcid = idString.ToInteger64(&rv);
      if (NS_SUCCEEDED(rv) && bcid == browsingContext->Id()) {
        NS_WARNING("Injecting artificial PBrowser creation failure");
        return IPC_OK();
      }
    }
  }

  if (!aWindowInit.isInitialDocument() ||
      !NS_IsAboutBlank(aWindowInit.documentURI())) {
    return IPC_FAIL(this,
                    "Logic in CreateDocumentViewerForActor currently requires "
                    "actors to be initial about:blank documents");
  }

  // We'll happily accept any kind of IPCTabContext here; we don't need to
  // check that it's of a certain type for security purposes, because we
  // believe whatever the parent process tells us.
  MaybeInvalidTabContext tc(aContext);
  if (!tc.IsValid()) {
    NS_ERROR(nsPrintfCString("Received an invalid TabContext from "
                             "the parent process. (%s)  Crashing...",
                             tc.GetInvalidReason())
                 .get());
    MOZ_CRASH("Invalid TabContext received from the parent process.");
  }

  RefPtr<WindowGlobalChild> windowChild =
      WindowGlobalChild::CreateDisconnected(aWindowInit);
  if (!windowChild) {
    return IPC_FAIL(this, "Failed to create initial WindowGlobalChild");
  }

  RefPtr<BrowserChild> browserChild =
      BrowserChild::Create(this, aTabId, tc.GetTabContext(), browsingContext,
                           aChromeFlags, aIsTopLevel);

  // Bind the created BrowserChild to IPC to actually link the actor.
  if (NS_WARN_IF(!BindPBrowserEndpoint(std::move(aBrowserEp), browserChild))) {
    return IPC_FAIL(this, "BindPBrowserEndpoint failed");
  }

  if (NS_WARN_IF(!browserChild->BindPWindowGlobalEndpoint(std::move(aWindowEp),
                                                          windowChild))) {
    return IPC_FAIL(this, "BindPWindowGlobalEndpoint failed");
  }
  windowChild->Init();
  auto guardNullWindowGlobal = MakeScopeExit([&] {
    if (!windowChild->GetWindowGlobal()) {
      windowChild->Destroy();
    }
  });

  // Ensure that a BrowsingContext is set for our BrowserChild before
  // running `Init`.
  MOZ_RELEASE_ASSERT(browserChild->mBrowsingContext->Id() ==
                     aWindowInit.context().mBrowsingContextId);

  RefPtr<nsOpenWindowInfo> openWindowInfo = new nsOpenWindowInfo();
  openWindowInfo->mPrincipalToInheritForAboutBlank = aWindowInit.principal();
  // XXX We should consider moving this to CreateAboutBlankDocumentViewer (bug
  // 2004943)
  openWindowInfo->mPolicyContainerToInheritForAboutBlank =
      new PolicyContainer();

  nsresult rv = browserChild->Init(
      /* aOpener */ nullptr, windowChild, openWindowInfo);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    if (rv == NS_ERROR_OUT_OF_MEMORY) {
      NS_ABORT_OOM(0);
    }
    IPC_FAIL_UNSAFE_PRINTF(browserChild, "BrowserChild::Init failed (rv=%s)",
                           mozilla::GetStaticErrorName(rv));
  }

  nsCOMPtr<nsIObserverService> os = services::GetObserverService();
  if (os) {
    os->NotifyObservers(static_cast<nsIBrowserChild*>(browserChild),
                        "tab-child-created", nullptr);
  }
  // Notify parent that we are ready to handle input events.
  browserChild->SendRemoteIsReadyToHandleInputEvents();
  return IPC_OK();
}

void ContentChild::GetAvailableDictionaries(
    nsTArray<nsCString>& aDictionaries) {
  aDictionaries = mAvailableDictionaries.Clone();
}

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;
}

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;
}

already_AddRefed<PTestShellChild> ContentChild::AllocPTestShellChild() {
  return MakeAndAddRef<TestShellChild>();
}

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

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

  return 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);

  // Some scripts listen for "app-startup" to start. However, in the content
  // process, this category runs before the ScriptPreloader is initialized so
  // these scripts wouldn't be added to the cache. Instead, if a script needs to
  // run on start up in the content process, it should listen for this category.
  NS_CreateServicesFromCategory("content-process-ready-for-script", nullptr,
                                "content-process-ready-for-script", nullptr);

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvNetworkLinkTypeChange(
    const uint32_t& aType) {
  mNetworkLinkType = aType;
  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
  if (obs) {
    obs->NotifyObservers(nullptr, "contentchild:network-link-type-changed",
                         nullptr);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvSocketProcessCrashed() {
  nsIOService::IncreaseSocketProcessCrashCount();
  return IPC_OK();
}

PRemotePrintJobChild* ContentChild::AllocPRemotePrintJobChild() {
#ifdef NS_PRINTING
  return new RemotePrintJobChild();
#else
  return nullptr;
#endif
}

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

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

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

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

mozilla::ipc::IPCResult ContentChild::RecvRegisterChrome(
    nsTArray<ChromePackage>&& packages,
    nsTArray<SubstitutionMapping>&& resources,
    nsTArray<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::RecvClearStyleSheetCache(
    const Maybe<bool>& aChrome, const Maybe<RefPtr<nsIPrincipal>>& aPrincipal,
    const Maybe<nsCString>& aSchemelessSite,
    const Maybe<OriginAttributesPattern>& aPattern,
    const Maybe<nsCString>& aURL) {
  SharedStyleSheetCache::Clear(aChrome, aPrincipal, aSchemelessSite, aPattern,
                               aURL);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvClearScriptCache(
    const Maybe<bool>& aChrome, const Maybe<RefPtr<nsIPrincipal>>& aPrincipal,
    const Maybe<nsCString>& aSchemelessSite,
    const Maybe<OriginAttributesPattern>& aPattern,
    const Maybe<nsCString>& aURL) {
  SharedScriptCache::Clear(aChrome, aPrincipal, aSchemelessSite, aPattern,
                           aURL);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvInvalidateScriptCache() {
  SharedScriptCache::Invalidate();
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvClearImageCache(
    const Maybe<bool>& aPrivateLoader, const Maybe<bool>& aChrome,
    const Maybe<RefPtr<nsIPrincipal>>& aPrincipal,
    const Maybe<nsCString>& aSchemelessSite,
    const Maybe<OriginAttributesPattern>& aPattern,
    const Maybe<nsCString>& aURL) {
  imgLoader::ClearCache(aPrincipal, aChrome, aPrincipal, aSchemelessSite,
                        aPattern, aURL);
  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();
}

mozilla::ipc::IPCResult ContentChild::RecvSetTRRMode(
    const nsIDNSService::ResolverMode& mode,
    const nsIDNSService::ResolverMode& modeFromPref) {
  RefPtr<net::ChildDNSService> dnsServiceChild =
      dont_AddRef(net::ChildDNSService::GetSingleton());
  if (dnsServiceChild) {
    dnsServiceChild->SetTRRModeInChild(mode, modeFromPref);
  }
  return IPC_OK();
}

void ContentChild::ActorDestroy(ActorDestroyReason why) {
#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
  DestroySandboxProfiler();
#endif

  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
  // Destroy our JSProcessActors, and reject any pending queries.
  JSActorDidDestroy();

#  if defined(XP_WIN)
  RefPtr<DllServices> dllSvc(DllServices::Get());
  dllSvc->DisableFull();
#  endif  // defined(XP_WIN)

  if (gFirstIdleTask) {
    gFirstIdleTask->Cancel();
    gFirstIdleTask = nullptr;
  }

  BlobURLProtocolHandler::RemoveDataEntries();

  mSharedData = nullptr;

  mIdleObservers.Clear();

  if (mConsoleListener) {
    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 MsgValueError:
      break;

    default:
      MOZ_CRASH("not reached");
  }

  CrashReporter::RecordAnnotationCString(
      CrashReporter::Annotation::ipc_channel_error, aReason);

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

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

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

mozilla::ipc::IPCResult ContentChild::RecvUpdatePerfStatsCollectionMask(
    const uint64_t& aMask) {
  PerfStats::SetCollectionMask(static_cast<PerfStats::MetricMask>(aMask));
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvCollectPerfStatsJSON(
    CollectPerfStatsJSONResolver&& aResolver) {
  auto s = PerfStats::CollectLocalPerfStatsJSON();
  aResolver(nsCString(s.c_str(), s.length()));
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvCollectScrollingMetrics(
    CollectScrollingMetricsResolver&& aResolver) {
  auto metrics = ScrollingMetrics::CollectLocalScrollingMetrics();
  using ResolverArgs = std::tuple<const uint32_t&, const uint32_t&>;
  aResolver(ResolverArgs(std::get<0>(metrics), std::get<1>(metrics)));
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvNotifyVisited(
    nsTArray<VisitedQueryResult>&& aURIs) {
  nsCOMPtr<IHistory> history = components::History::Service();
  if (!history) {
    return IPC_OK();
  }
  for (const VisitedQueryResult& result : aURIs) {
    nsCOMPtr<nsIURI> newURI = result.uri();
    if (!newURI) {
      return IPC_FAIL_NO_REASON(this);
    }
    auto status = result.visited() ? IHistory::VisitedStatus::Visited
                                   : IHistory::VisitedStatus::Unvisited;
    history->NotifyVisited(newURI, status);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvThemeChanged(
    FullLookAndFeel&& aLookAndFeelData, widget::ThemeChangeKind aKind) {
  LookAndFeel::SetData(std::move(aLookAndFeelData));
  LookAndFeel::NotifyChangedAllWindows(aKind);
  return IPC_OK();
}

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

mozilla::ipc::IPCResult ContentChild::RecvAsyncMessage(
    const nsString& aMsg, NotNull<StructuredCloneData*> aData) {
  AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING("ContentChild::RecvAsyncMessage",
                                             OTHER, aMsg);
  MMPrinter::Print("ContentChild::RecvAsyncMessage", aMsg, aData);

  RefPtr<nsFrameMessageManager> cpm =
      nsFrameMessageManager::GetChildProcessManager();
  if (cpm) {
    cpm->ReceiveMessage(cpm, nullptr, aMsg, false, aData, nullptr);
  }
  return IPC_OK();
}

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

  for (auto& descriptor : aDescriptors) {
    stringBundleService->RegisterContentBundle(
        descriptor.bundleURL(), std::move(descriptor.mapHandle()));
  }

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvSimpleURIUnknownRemoteSchemes(
    nsTArray<nsCString>&& aRemoteSchemes) {
  RefPtr<nsIOService> io = nsIOService::GetInstance();
  io->SetSimpleURIUnknownRemoteSchemes(aRemoteSchemes);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvUpdateL10nFileSources(
    nsTArray<mozilla::dom::L10nFileSourceDescriptor>&& aDescriptors) {
  L10nRegistry::RegisterFileSourcesFromParentProcess(aDescriptors);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvUpdateSharedData(
    mozilla::ipc::ReadOnlySharedMemoryHandle&& aMapHandle,
    nsTArray<IPCBlob>&& aBlobs, nsTArray<nsCString>&& aChangedKeys) {
  nsTArray<NotNull<RefPtr<BlobImpl>>> blobImpls(aBlobs.Length());
  for (auto& ipcBlob : aBlobs) {
    RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(ipcBlob);
    if (!blobImpl) {
      return IPC_FAIL(this, "IPCBlobUtils::Deserialize failed");
    }
    blobImpls.AppendElement(WrapNotNull(blobImpl));
  }

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

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvForceGlobalReflow(
    const gfxPlatform::GlobalReflowFlags& aFlags) {
  gfxPlatform::ForceGlobalReflow(aFlags);

  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(
    nsTArray<nsCString>&& aDictionaries) {
  mAvailableDictionaries = std::move(aDictionaries);
  mozInlineSpellChecker::UpdateCanEnableInlineSpellChecking();
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvUpdateFontList(
    dom::SystemFontList&& aFontList) {
  mFontList = std::move(aFontList);
  if (gfxPlatform::Initialized()) {
    gfxPlatform::GetPlatform()->UpdateFontList(true);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvRebuildFontList(
    const bool& aFullRebuild) {
  if (gfxPlatform::Initialized()) {
    gfxPlatform::GetPlatform()->UpdateFontList(aFullRebuild);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvFontListShmBlockAdded(
    const uint32_t& aGeneration, const uint32_t& aIndex,
    mozilla::ipc::ReadOnlySharedMemoryHandle&& aHandle) {
  if (gfxPlatform::Initialized()) {
    gfxPlatformFontList::PlatformFontList()->ShmBlockAdded(aGeneration, aIndex,
                                                           std::move(aHandle));
  }
  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::RecvSystemTimezoneChanged() {
  nsJSUtils::ResetTimeZone();
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvAddPermission(
    const IPC::Permission& permission) {
  RefPtr<PermissionManager> permissionManager =
      PermissionManager::GetInstance();
  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::CreateContentPrincipal(uri, attrs);

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

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

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvSetBrowserPermission(
    const nsCString& aOrigin, const nsCString& aType, const uint32_t& aAction,
    const uint64_t& aBrowserId, const bool& aIsRemoval) {
  RefPtr<PermissionManager> permissionManager =
      PermissionManager::GetInstance();
  MOZ_ASSERT(permissionManager);

  nsAutoCString originNoSuffix;
  OriginAttributes attrs;
  bool success = attrs.PopulateFromOrigin(aOrigin, 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::CreateContentPrincipal(uri, attrs);

  permissionManager->SetBrowserPermissionFromIPC(principal, aType, aAction,
                                                 aBrowserId, aIsRemoval);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvClearBrowserPermissions(
    const uint64_t& aBrowserId, const uint32_t& aActionFilter) {
  RefPtr<PermissionManager> permissionManager =
      PermissionManager::GetInstance();
  MOZ_ASSERT(permissionManager);

  permissionManager->ClearBrowserPermissionsFromIPC(aBrowserId, aActionFilter);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvRemoveAllPermissions() {
  RefPtr<PermissionManager> permissionManager =
      PermissionManager::GetInstance();
  MOZ_ASSERT(permissionManager,
             "We have no permissionManager in the Content process !");

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

void ContentChild::NotifyMemoryPressure(const char* aTopic,
                                        const char16_t* aReason) {
  nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
  if (!mShuttingDown && os) {
    os->NotifyObservers(nullptr, aTopic, aReason);
  }
}

mozilla::ipc::IPCResult ContentChild::RecvMemoryPressure(
    const nsString& reason) {
  NotifyMemoryPressure("memory-pressure", reason.get());
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvMemoryPressureStop() {
  NotifyMemoryPressure("memory-pressure-stop", nullptr);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvActivateA11y(uint64_t aCacheDomains) {
#ifdef ACCESSIBILITY
  // Start accessibility in content process if it's running in chrome
  // process.
  GetOrCreateAccService(nsAccessibilityService::eMainProcess, aCacheDomains);
#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::RecvSetCacheDomains(
    uint64_t aCacheDomains) {
#ifdef ACCESSIBILITY
  nsAccessibilityService* accService = GetAccService();
  if (!accService) {
    return IPC_FAIL(this, "Accessibility service should exist");
  }
  accService->SetCacheDomains(aCacheDomains);
#endif
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvApplicationForeground() {
  // Rebroadcast the "application-foreground"
  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
  if (obs) {
    obs->NotifyObservers(nullptr, "application-foreground", nullptr);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvApplicationBackground() {
  // Rebroadcast the "application-background"
  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
  if (obs) {
    obs->NotifyObservers(nullptr, "application-background", nullptr);
  }
  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(CCReason::IPC_MESSAGE);
  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, const nsCString& updateURL) {
  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);
  mAppInfo.updateURL.Assign(updateURL);

  return IPC_OK();
}

static StaticMutex sCurrentRemoteTypeMutex;
static StaticAutoPtr<nsCString> sCurrentRemoteType
    MOZ_GUARDED_BY(sCurrentRemoteTypeMutex);

nsCString CurrentRemoteType() {
  if (XRE_IsContentProcess()) {
    StaticMutexAutoLock lock(sCurrentRemoteTypeMutex);
    if (sCurrentRemoteType) {
      return *sCurrentRemoteType;
    }
    return PREALLOC_REMOTE_TYPE;
  }

  return NOT_REMOTE_TYPE;
}

mozilla::ipc::IPCResult ContentChild::RecvRemoteType(
    const nsCString& aRemoteType, const nsCString& aProfile) {
  if (aRemoteType == mRemoteType) {
    // Allocation of preallocated processes that are still launching can
    // cause this
    return IPC_OK();
  }

  if (!mRemoteType.IsVoid()) {
    // Preallocated processes are type PREALLOC_REMOTE_TYPE; they may not
    // become a File: process, or Privileged About Content Process
    MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
            ("Changing remoteType of process %d from %s to %s", getpid(),
             mRemoteType.get(), aRemoteType.get()));
    // prealloc->anything (but file) or web->web allowed, and no-change
    MOZ_RELEASE_ASSERT(mRemoteType == PREALLOC_REMOTE_TYPE &&
                       aRemoteType != FILE_REMOTE_TYPE &&
                       aRemoteType != PRIVILEGEDABOUT_REMOTE_TYPE);
  } else {
    // Initial setting of remote type.  Either to 'prealloc' or the actual
    // final type (if we didn't use a preallocated process)
    MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
            ("Setting remoteType of process %d to %s", getpid(),
             aRemoteType.get()));

    if (aRemoteType == PREALLOC_REMOTE_TYPE) {
      PreallocInit();
    }
  }

  auto remoteTypePrefix = RemoteTypePrefix(aRemoteType);

  // Must do before SetProcessName
  mRemoteType.Assign(aRemoteType);

  {
    StaticMutexAutoLock lock(sCurrentRemoteTypeMutex);
    if (!sCurrentRemoteType) {
      sCurrentRemoteType = new nsCString();
      RunOnShutdown([] {
        StaticMutexAutoLock lock(sCurrentRemoteTypeMutex);
        sCurrentRemoteType = nullptr;
      });
    }
    sCurrentRemoteType->Assign(mRemoteType);
  }

  // Update the process name so about:memory's process names are more obvious.
  if (aRemoteType == FILE_REMOTE_TYPE) {
    SetProcessName("file:// Content"_ns, nullptr, &aProfile);
  } else if (aRemoteType == EXTENSION_REMOTE_TYPE) {
    SetProcessName("WebExtensions"_ns, nullptr, &aProfile);
  } else if (aRemoteType == PRIVILEGEDABOUT_REMOTE_TYPE) {
    SetProcessName("Privileged Content"_ns, nullptr, &aProfile);
  } else if (aRemoteType == PRIVILEGEDMOZILLA_REMOTE_TYPE) {
    SetProcessName("Privileged Mozilla"_ns, nullptr, &aProfile);
  } else if (aRemoteType == INFERENCE_REMOTE_TYPE) {
    SetProcessName("Inference"_ns, nullptr, &aProfile);
  } else if (remoteTypePrefix == WITH_COOP_COEP_REMOTE_TYPE) {
    // The profiler can sanitize out the eTLD+1
    nsDependentCSubstring etld =
        Substring(aRemoteType, WITH_COOP_COEP_REMOTE_TYPE.Length() + 1);
#ifdef NIGHTLY_BUILD
    SetProcessName("WebCOOP+COEP Content"_ns, &etld, &aProfile);
#else
    SetProcessName("Isolated Web Content"_ns, &etld,
                   &aProfile);  // to avoid confusing people
#endif
  } else if (remoteTypePrefix == FISSION_WEB_REMOTE_TYPE) {
    // The profiler can sanitize out the eTLD+1
    nsDependentCSubstring etld =
        Substring(aRemoteType, FISSION_WEB_REMOTE_TYPE.Length() + 1);
    SetProcessName("Isolated Web Content"_ns, &etld, &aProfile);
  } else if (remoteTypePrefix == SERVICEWORKER_REMOTE_TYPE) {
    // The profiler can sanitize out the eTLD+1
    nsDependentCSubstring etld =
        Substring(aRemoteType, SERVICEWORKER_REMOTE_TYPE.Length() + 1);
    SetProcessName("Isolated Service Worker"_ns, &etld, &aProfile);
  } else {
    // else "prealloc" or "web" type -> "Web Content"
    SetProcessName("Web Content"_ns, nullptr, &aProfile);
  }

  // Turn off Spectre mitigations in isolated web content processes.
  if (StaticPrefs::javascript_options_spectre_disable_for_isolated_content() &&
      StaticPrefs::browser_opaqueResponseBlocking() &&
      (remoteTypePrefix == FISSION_WEB_REMOTE_TYPE ||
       remoteTypePrefix == SERVICEWORKER_REMOTE_TYPE ||
       remoteTypePrefix == WITH_COOP_COEP_REMOTE_TYPE ||
       aRemoteType == PRIVILEGEDABOUT_REMOTE_TYPE ||
       aRemoteType == PRIVILEGEDMOZILLA_REMOTE_TYPE)) {
    JS::DisableSpectreMitigationsAfterInit();
  }

  // Use the prefix to avoid URIs from Fission isolated processes.
  CrashReporter::RecordAnnotationNSCString(
      CrashReporter::Annotation::RemoteType, remoteTypePrefix);

  return IPC_OK();
}

// A method to initialize anything we need during the preallocation phase
void ContentChild::PreallocInit() {
  EnsureNSSInitializedChromeOrContent();

  // SetAcceptLanguages() needs to read localized strings (file access),
  // which is slow, so do this in prealloc
  nsHttpHandler::PresetAcceptLanguages();
}

// Call RemoteTypePrefix() on the result to remove URIs if you want to use this
// for telemetry.
const nsACString& ContentChild::GetRemoteType() const { return mRemoteType; }

mozilla::ipc::IPCResult ContentChild::RecvInitRemoteWorkerService(
    Endpoint<PRemoteWorkerServiceChild>&& aEndpoint,
    Endpoint<PRemoteWorkerDebuggerManagerChild>&& aDebuggerChiledEp) {
  RemoteWorkerService::InitializeChild(std::move(aEndpoint),
                                       std::move(aDebuggerChiledEp));
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvInitBlobURLs(
    nsTArray<BlobURLRegistrationData>&& aRegistrations) {
  for (uint32_t i = 0; i < aRegistrations.Length(); ++i) {
    BlobURLRegistrationData& registration = aRegistrations[i];
    BlobURLProtocolHandler::AddDataEntryChild(registration.url(),
                                              registration.principal(),
                                              registration.partitionKey());
    // 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::RemoveDataEntries(nsTArray{registration.url()},
                                                false);
    }
  }

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvInitJSActorInfos(
    nsTArray<JSProcessActorInfo>&& aContentInfos,
    nsTArray<JSWindowActorInfo>&& aWindowInfos) {
  RefPtr<JSActorService> actSvc = JSActorService::GetSingleton();
  actSvc->LoadJSActorInfos(aContentInfos, aWindowInfos);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvUnregisterJSWindowActor(
    const nsCString& aName) {
  RefPtr<JSActorService> actSvc = JSActorService::GetSingleton();
  actSvc->UnregisterWindowActor(aName);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvUnregisterJSProcessActor(
    const nsCString& aName) {
  RefPtr<JSActorService> actSvc = JSActorService::GetSingleton();
  actSvc->UnregisterProcessActor(aName);
  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(u"priority"_ns, static_cast<int32_t>(aPriority));

  PROFILER_MARKER("Process Priority", OTHER,
                  mozilla::MarkerThreadId::MainThread(), ProcessPriorityChange,
                  ProfilerString8View::WrapNullTerminatedString(
                      ProcessPriorityToString(mProcessPriority)),
                  ProfilerString8View::WrapNullTerminatedString(
                      ProcessPriorityToString(aPriority)));

  // Record FOG data before the priority change.
  // Ignore the change if it's the first time we set the process priority.
  if (mProcessPriority != hal::PROCESS_PRIORITY_UNKNOWN) {
    glean::RecordPowerMetrics();
  }

  ConfigureThreadPerformanceHints(aPriority);

  mProcessPriority = aPriority;

  os->NotifyObservers(static_cast<nsIPropertyBag2*>(props),
                      "ipc:process-priority-changed", nullptr);
  if (StaticPrefs::
          dom_memory_foreground_content_processes_have_larger_page_cache()) {
#ifdef MOZ_MEMORY
    if (mProcessPriority >= hal::PROCESS_PRIORITY_FOREGROUND) {
      // Note: keep this in sync with the JS shell (js/src/shell/js.cpp).
      moz_set_max_dirty_page_modifier(4);
    }
#endif
    if (mProcessPriority == hal::PROCESS_PRIORITY_BACKGROUND) {
#ifdef MOZ_MEMORY
      moz_set_max_dirty_page_modifier(-2);

      if (StaticPrefs::dom_memory_memory_pressure_on_background() == 1) {
        jemalloc_free_dirty_pages();
      }
#endif
      if (StaticPrefs::dom_memory_memory_pressure_on_background() == 2) {
        nsCOMPtr<nsIObserverService> obsServ = services::GetObserverService();
        obsServ->NotifyObservers(nullptr, "memory-pressure", u"heap-minimize");
      } else if (StaticPrefs::dom_memory_memory_pressure_on_background() == 3) {
        nsCOMPtr<nsIObserverService> obsServ = services::GetObserverService();
        obsServ->NotifyObservers(nullptr, "memory-pressure", u"low-memory");
      }
#ifdef MOZ_MEMORY
    } else {
      moz_set_max_dirty_page_modifier(0);
#endif
    }
  }

  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());

  (void)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.Insert(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.Remove(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(
    nsIURI* aURI, const uint32_t& aType) {
  if (!aURI) {
    return IPC_OK();
  }

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

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvUnregisterSheet(
    nsIURI* aURI, const uint32_t& aType) {
  if (!aURI) {
    return IPC_OK();
  }

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

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvDomainSetChanged(
    const uint32_t& aSetType, const uint32_t& aChangeType, nsIURI* 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);

  switch (aChangeType) {
    case ADD_DOMAIN:
      NS_ENSURE_TRUE(aDomain, IPC_FAIL_NO_REASON(this));
      set->Add(aDomain);
      break;
    case REMOVE_DOMAIN:
      NS_ENSURE_TRUE(aDomain, IPC_FAIL_NO_REASON(this));
      set->Remove(aDomain);
      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"_ns);
    MOZ_ASSERT(mForceKillTimer);
  }
}

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

mozilla::ipc::IPCResult ContentChild::RecvShutdown() {
  ProcessChild::AppendToIPCShutdownStateAnnotation("RecvShutdown entry"_ns);

  // Signal the ongoing shutdown to AppShutdown, this
  // will make abort nested SpinEventLoopUntilOrQuit loops
  AppShutdown::AdvanceShutdownPhaseWithoutNotify(
      ShutdownPhase::AppShutdownConfirmed);

  nsCOMPtr<nsIObserverService> os = services::GetObserverService();
  if (os) {
    ProcessChild::AppendToIPCShutdownStateAnnotation(
        "content-child-will-shutdown started"_ns);

    os->NotifyObservers(ToSupports(this), "content-child-will-shutdown",
                        nullptr);
  }

  ShutdownInternal();
  return IPC_OK();
}

void ContentChild::ShutdownInternal() {
  ProcessChild::AppendToIPCShutdownStateAnnotation("ShutdownInternal entry"_ns);

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

  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.
    GetCurrentSerialEventTarget()->DelayedDispatch(
        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) {
    ProcessChild::AppendToIPCShutdownStateAnnotation(
        "content-child-shutdown started"_ns);
    os->NotifyObservers(ToSupports(this), "content-child-shutdown", nullptr);
  }

  GetIPCChannel()->SetAbortOnError(false);

  if (mProfilerController) {
    const bool isProfiling = profiler_is_active();
    CrashReporter::RecordAnnotationCString(
        CrashReporter::Annotation::ProfilerChildShutdownPhase,
        isProfiling ? "Profiling - GrabShutdownProfileAndShutdown"
                    : "Not profiling - GrabShutdownProfileAndShutdown");
    ProfileAndAdditionalInformation shutdownProfileAndAdditionalInformation =
        mProfilerController->GrabShutdownProfileAndShutdown();
    CrashReporter::RecordAnnotationCString(
        CrashReporter::Annotation::ProfilerChildShutdownPhase,
        isProfiling ? "Profiling - Destroying ChildProfilerController"
                    : "Not profiling - Destroying ChildProfilerController");
    mProfilerController = nullptr;
    CrashReporter::RecordAnnotationCString(
        CrashReporter::Annotation::ProfilerChildShutdownPhase,
        isProfiling ? "Profiling - SendShutdownProfile (sending)"
                    : "Not profiling - SendShutdownProfile (sending)");
    if (const size_t len = shutdownProfileAndAdditionalInformation.SizeOf();
        len >= size_t(IPC::Channel::kMaximumMessageSize)) {
      shutdownProfileAndAdditionalInformation.mProfile = nsPrintfCString(
          "*Profile from pid %u bigger (%zu) than IPC max (%zu)",
          unsigned(profiler_current_process_id().ToNumber()), len,
          size_t(IPC::Channel::kMaximumMessageSize));
      shutdownProfileAndAdditionalInformation.mAdditionalInformation.reset();
    }
    // Send the shutdown profile to the parent process through our own
    // message channel, which we know will survive for long enough.
    bool sent =
        SendShutdownProfile(std::move(shutdownProfileAndAdditionalInformation));
    CrashReporter::RecordAnnotationCString(
        CrashReporter::Annotation::ProfilerChildShutdownPhase,
        sent ? (isProfiling ? "Profiling - SendShutdownProfile (sent)"
                            : "Not profiling - SendShutdownProfile (sent)")
             : (isProfiling ? "Profiling - SendShutdownProfile (failed)"
                            : "Not profiling - SendShutdownProfile (failed)"));
  }

  if (PerfStats::GetCollectionMask() != 0) {
    auto s = PerfStats::CollectLocalPerfStatsJSON();
    SendShutdownPerfStats(nsCString(s.c_str(), s.length()));
  }

  // Start a timer that will ensure we quickly exit after a reasonable period
  // of time. Prevents shutdown hangs after our connection to the parent
  // closes or when the parent is too busy to ever kill us.
  ProcessChild::AppendToIPCShutdownStateAnnotation("StartForceKillTimer"_ns);
  StartForceKillTimer();

  ProcessChild::AppendToIPCShutdownStateAnnotation(
      "SendFinishShutdown (sending)"_ns);

  // Notify the parent that we are done with shutdown. This is sent with high
  // priority and will just flag we are done.
  (void)SendNotifyShutdownSuccess();

  // Now tell the parent to actually destroy our channel which will make end
  // our process. This is expected to be the last event the parent will
  // ever process for this ContentChild.
  bool sent = SendFinishShutdown();

  ProcessChild::AppendToIPCShutdownStateAnnotation(
      sent ? "SendFinishShutdown (sent)"_ns : "SendFinishShutdown (failed)"_ns);
}

mozilla::ipc::IPCResult ContentChild::RecvUpdateWindow(
    const uintptr_t& aChildId) {
  MOZ_ASSERT(
      false,
      "ContentChild::RecvUpdateWindow calls unexpected on this platform.");
  return IPC_FAIL_NO_REASON(this);
}

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

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

already_AddRefed<PWebBrowserPersistDocumentChild>
ContentChild::AllocPWebBrowserPersistDocumentChild(
    PBrowserChild* aBrowser, const MaybeDiscarded<BrowsingContext>& aContext) {
  return MakeAndAddRef<WebBrowserPersistDocumentChild>();
}

mozilla::ipc::IPCResult ContentChild::RecvPWebBrowserPersistDocumentConstructor(
    PWebBrowserPersistDocumentChild* aActor, PBrowserChild* aBrowser,
    const MaybeDiscarded<BrowsingContext>& aContext) {
  if (NS_WARN_IF(!aBrowser)) {
    return IPC_FAIL_NO_REASON(this);
  }

  if (aContext.IsNullOrDiscarded()) {
    aActor->SendInitFailure(NS_ERROR_NO_CONTENT);
    return IPC_OK();
  }

  nsCOMPtr<Document> foundDoc = aContext.get()->GetDocument();

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

mozilla::ipc::IPCResult ContentChild::RecvBlobURLRegistration(
    const nsCString& aURI, nsIPrincipal* aPrincipal,
    const nsCString& aPartitionKey) {
  BlobURLProtocolHandler::AddDataEntryChild(aURI, aPrincipal, aPartitionKey);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvBlobURLUnregistration(
    const nsTArray<nsCString>& aURIs) {
  BlobURLProtocolHandler::RemoveDataEntries(
      aURIs,
      /* aBroadcastToOtherProcesses = */ false);
  return IPC_OK();
}

void ContentChild::CreateGetFilesRequest(nsTArray<nsString>&& aDirectoryPaths,
                                         bool aRecursiveFlag, nsID& aUUID,
                                         GetFilesHelperChild* aChild) {
  MOZ_ASSERT(aChild);
  MOZ_ASSERT(!mGetFilesPendingRequests.Contains(aUUID));

  (void)SendGetFilesRequest(aUUID, aDirectoryPaths, aRecursiveFlag);
  mGetFilesPendingRequests.InsertOrUpdate(aUUID, RefPtr{aChild});
}

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

  (void)SendDeleteGetFilesRequest(aUUID);
  mGetFilesPendingRequests.Remove(aUUID);
}

mozilla::ipc::IPCResult ContentChild::RecvGetFilesResponse(
    const nsID& aUUID, const GetFilesResponseResult& aResult) {
  RefPtr<GetFilesHelperChild> child;

  // This object can already been deleted in case DeleteGetFilesRequest has
  // been called when the response was sending by the parent.
  if (!mGetFilesPendingRequests.Remove(aUUID, getter_AddRefs(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);
  }
  return IPC_OK();
}

/* static */
void ContentChild::FatalErrorIfNotUsingGPUProcess(const char* const aErrorMsg,
                                                  GeckoChildID aChildID) {
  // 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 (aChildID == XRE_GetChildID() || aChildID == 0) {
    mozilla::ipc::FatalError(aErrorMsg, false);
  } else {
    nsAutoCString formattedMessage("IPDL error: \"");
    formattedMessage.AppendASCII(aErrorMsg);
    formattedMessage.AppendLiteral(R"(".)");
    NS_WARNING(formattedMessage.get());
  }
}

PURLClassifierChild* ContentChild::AllocPURLClassifierChild(
    nsIPrincipal* aPrincipal, bool* aSuccess) {
  *aSuccess = true;
  return new URLClassifierChild();
}

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

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

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

PURLClassifierLocalByNameChild*
ContentChild::AllocPURLClassifierLocalByNameChild(
    nsIURI* aUri, Span<const nsCString> aFeatures,
    const nsIUrlClassifierFeature::listType& aListType) {
  return new URLClassifierLocalByNameChild();
}

bool ContentChild::DeallocPURLClassifierLocalByNameChild(
    PURLClassifierLocalByNameChild* aActor) {
  MOZ_ASSERT(aActor);
  delete aActor;
  return true;
}

PSessionStorageObserverChild*
ContentChild::AllocPSessionStorageObserverChild() {
  MOZ_CRASH(
      "PSessionStorageObserverChild actors should be manually constructed!");
}

bool ContentChild::DeallocPSessionStorageObserverChild(
    PSessionStorageObserverChild* aActor) {
  MOZ_ASSERT(aActor);

  delete aActor;
  return true;
}

mozilla::ipc::IPCResult ContentChild::RecvProvideAnonymousTemporaryFile(
    const uint64_t& aID, const FileDescOrError& aFDOrError) {
  mozilla::UniquePtr<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.GetOrInsertNew(newID, aCallback);
  return NS_OK;
}

mozilla::ipc::IPCResult ContentChild::RecvSetPermissionsWithKey(
    const nsCString& aPermissionKey, nsTArray<IPC::Permission>&& aPerms) {
  RefPtr<PermissionManager> permManager = PermissionManager::GetInstance();
  if (permManager) {
    permManager->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();
}

mozilla::ipc::IPCResult ContentChild::RecvShareCodeCoverageMutex(
    CrossProcessMutexHandle aHandle) {
#ifdef MOZ_CODE_COVERAGE
  CodeCoverageHandler::Init(std::move(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::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::RecvCrossProcessRedirect(
    RedirectToRealChannelArgs&& aArgs,
    nsTArray<Endpoint<extensions::PStreamFilterParent>>&& aEndpoints,
    CrossProcessRedirectResolver&& aResolve) {
  nsCOMPtr<nsILoadInfo> loadInfo;
  nsresult rv = mozilla::ipc::LoadInfoArgsToLoadInfo(
      aArgs.loadInfo(), NOT_REMOTE_TYPE, getter_AddRefs(loadInfo));
  if (NS_FAILED(rv)) {
    MOZ_DIAGNOSTIC_CRASH("LoadInfoArgsToLoadInfo failed");
    return IPC_OK();
  }

  nsCOMPtr<nsIChannel> newChannel;
  MOZ_ASSERT((aArgs.loadStateInternalLoadFlags() &
              nsDocShell::InternalLoad::INTERNAL_LOAD_FLAGS_IS_SRCDOC) ||
             aArgs.srcdocData().IsVoid());
  rv = nsDocShell::CreateRealChannelForDocument(
      getter_AddRefs(newChannel), aArgs.uri(), loadInfo, nullptr,
      aArgs.newLoadFlags(), aArgs.srcdocData(), aArgs.baseUri());

  if (RefPtr<HttpBaseChannel> httpChannel = do_QueryObject(newChannel)) {
    httpChannel->SetEarlyHints(std::move(aArgs.earlyHints()));
    httpChannel->SetEarlyHintLinkType(aArgs.earlyHintLinkType());
  }

  // This is used to report any errors back to the parent by calling
  // CrossProcessRedirectFinished.
  RefPtr<HttpChannelChild> httpChild = do_QueryObject(newChannel);
  auto resolve = [=](const nsresult& aRv) {
    nsresult rv = aRv;
    if (httpChild) {
      rv = httpChild->CrossProcessRedirectFinished(rv);
    }
    aResolve(rv);
  };
  auto scopeExit = MakeScopeExit([&]() { resolve(rv); });

  if (NS_FAILED(rv)) {
    return IPC_OK();
  }

  if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(newChannel)) {
    rv = httpChannel->SetChannelId(aArgs.channelId());
  }
  if (NS_FAILED(rv)) {
    return IPC_OK();
  }

  rv = newChannel->SetOriginalURI(aArgs.originalURI());
  if (NS_FAILED(rv)) {
    return IPC_OK();
  }

  if (nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
          do_QueryInterface(newChannel)) {
    rv = httpChannelInternal->SetRedirectMode(aArgs.redirectMode());
  }
  if (NS_FAILED(rv)) {
    return IPC_OK();
  }

  if (aArgs.init()) {
    HttpBaseChannel::ReplacementChannelConfig config(std::move(*aArgs.init()));
    HttpBaseChannel::ConfigureReplacementChannel(
        newChannel, config,
        HttpBaseChannel::ReplacementReason::DocumentChannel);
  }

  if (aArgs.contentDisposition()) {
    newChannel->SetContentDisposition(*aArgs.contentDisposition());
  }

  if (aArgs.contentDispositionFilename()) {
    newChannel->SetContentDispositionFilename(
        *aArgs.contentDispositionFilename());
  }

  if (nsCOMPtr<nsIChildChannel> childChannel = do_QueryInterface(newChannel)) {
    // Connect to the parent if this is a remote channel. If it's entirely
    // handled locally, then we'll call AsyncOpen from the docshell when
    // we complete the setup
    rv = childChannel->ConnectParent(
        aArgs.registrarId());  // creates parent channel
    if (NS_FAILED(rv)) {
      return IPC_OK();
    }
  }

  // We need to copy the property bag before signaling that the channel
  // is ready so that the nsDocShell can retrieve the history data when called.
  if (nsCOMPtr<nsIWritablePropertyBag> bag = do_QueryInterface(newChannel)) {
    nsHashPropertyBag::CopyFrom(bag, aArgs.properties());
  }

  // Track the provided parent-process channel handle on our channel.
  if (aArgs.channelHandle()) {
    rv = newChannel->SetParentProcessChannelHandle(aArgs.channelHandle());
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return IPC_OK();
    }
  }

  RefPtr<nsDocShellLoadState> loadState;
  rv = nsDocShellLoadState::CreateFromPendingChannel(
      newChannel, aArgs.loadIdentifier(), aArgs.registrarId(),
      getter_AddRefs(loadState));
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return IPC_OK();
  }
  loadState->SetLoadFlags(aArgs.loadStateExternalLoadFlags());
  loadState->SetInternalLoadFlags(aArgs.loadStateInternalLoadFlags());
  if (IsValidLoadType(aArgs.loadStateLoadType())) {
    loadState->SetLoadType(aArgs.loadStateLoadType());
  }

  if (aArgs.loadingSessionHistoryInfo().isSome()) {
    loadState->SetLoadingSessionHistoryInfo(
        aArgs.loadingSessionHistoryInfo().ref());
  }
  if (aArgs.originalUriString().isSome()) {
    loadState->SetOriginalURIString(aArgs.originalUriString().ref());
  }

  RefPtr<ChildProcessChannelListener> processListener =
      ChildProcessChannelListener::GetSingleton();
  // The listener will call completeRedirectSetup or asyncOpen on the channel.
  processListener->OnChannelReady(loadState, aArgs.loadIdentifier(),
                                  std::move(aEndpoints), aArgs.timing(),
                                  std::move(resolve));
  scopeExit.release();

  // scopeExit will call CrossProcessRedirectFinished(rv) here
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvStartDelayedAutoplayMediaComponents(
    const MaybeDiscarded<BrowsingContext>& aContext) {
  if (NS_WARN_IF(aContext.IsNullOrDiscarded())) {
    return IPC_OK();
  }

  aContext.get()->StartDelayedAutoplayMediaComponents();
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvUpdateMediaControlAction(
    const MaybeDiscarded<BrowsingContext>& aContext,
    const MediaControlAction& aAction) {
  if (NS_WARN_IF(aContext.IsNullOrDiscarded())) {
    return IPC_OK();
  }

  ContentMediaControlKeyHandler::HandleMediaControlAction(aContext.get(),
                                                          aAction);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvUpdateMediaSessionInterrupt(
    const MaybeDiscarded<BrowsingContext>& aContext,
    const AudioFocusInterruptAction& aAction) {
  if (NS_WARN_IF(aContext.IsNullOrDiscarded())) {
    return IPC_OK();
  }

  ContentMediaControlKeyHandler::HandleAudioFocusInterrupt(aContext.get(),
                                                           aAction);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvOnAllowAccessFor(
    const MaybeDiscarded<BrowsingContext>& aContext,
    const nsCString& aTrackingOrigin, uint32_t aCookieBehavior,
    const ContentBlockingNotifier::StorageAccessPermissionGrantedReason&
        aReason) {
  MOZ_ASSERT(!aContext.IsNull(), "Browsing context cannot be null");

  StorageAccessAPIHelper::OnAllowAccessFor(
      aContext.GetMaybeDiscarded(), aTrackingOrigin, aCookieBehavior, aReason);

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvOnContentBlockingDecision(
    const MaybeDiscarded<BrowsingContext>& aContext,
    const ContentBlockingNotifier::BlockingDecision& aDecision,
    uint32_t aRejectedReason) {
  MOZ_ASSERT(!aContext.IsNull(), "Browsing context cannot be null");

  nsCOMPtr<nsPIDOMWindowOuter> outer = aContext.get()->GetDOMWindow();
  if (!outer) {
    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
            ("ChildIPC: Trying to send a message to a context without a outer "
             "window"));
    return IPC_OK();
  }

  nsCOMPtr<nsPIDOMWindowInner> inner = outer->GetCurrentInnerWindow();
  if (!inner) {
    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
            ("ChildIPC: Trying to send a message to a context without a inner "
             "window"));
    return IPC_OK();
  }

  ContentBlockingNotifier::OnDecision(inner, aDecision, aRejectedReason);
  return IPC_OK();
}

#ifdef NIGHTLY_BUILD
void ContentChild::OnChannelReceivedMessage(const Message& aMsg) {
  if (nsContentUtils::IsMessageInputEvent(aMsg)) {
    mPendingInputEvents++;
  }
}

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

  return PContentChild::OnMessageReceived(aMsg);
}

PContentChild::Result ContentChild::OnMessageReceived(
    const Message& aMsg, UniquePtr<Message>& aReply) {
  return PContentChild::OnMessageReceived(aMsg, aReply);
}
#endif

mozilla::ipc::IPCResult ContentChild::RecvCreateBrowsingContext(
    uint64_t aGroupId, BrowsingContext::IPCInitializer&& aInit) {
  // We can't already have a BrowsingContext with this ID.
  if (RefPtr<BrowsingContext> existing = BrowsingContext::Get(aInit.mId)) {
    return IPC_FAIL(this, "Browsing context already exists");
  }

  RefPtr<WindowContext> parent = WindowContext::GetById(aInit.mParentId);
  if (!parent && aInit.mParentId != 0) {
    // Handle this case by ignoring the request, as parent must be in the
    // process of being discarded.
    // In the future it would be nice to avoid sending this message to the child
    // at all.
    NS_WARNING("Attempt to attach BrowsingContext to discarded parent");
    return IPC_OK();
  }

  RefPtr<BrowsingContextGroup> group =
      BrowsingContextGroup::GetOrCreate(aGroupId);
  return BrowsingContext::CreateFromIPC(std::move(aInit), group, nullptr);
}

mozilla::ipc::IPCResult ContentChild::RecvDiscardBrowsingContext(
    const MaybeDiscarded<BrowsingContext>& aContext, bool aDoDiscard,
    DiscardBrowsingContextResolver&& aResolve) {
  if (BrowsingContext* context = aContext.GetMaybeDiscarded()) {
    if (aDoDiscard && !context->IsDiscarded()) {
      context->Detach(/* aFromIPC */ true);
    }
    context->AddDiscardListener(aResolve);
    return IPC_OK();
  }

  // Immediately resolve the promise, as we've received the message. This will
  // allow the parent process to discard references to this BC.
  aResolve(true);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvRegisterBrowsingContextGroup(
    uint64_t aGroupId, nsTArray<SyncedContextInitializer>&& aInits,
    nsTArray<OriginAgentClusterInitializer>&& aUseOriginAgentCluster) {
  RefPtr<BrowsingContextGroup> group =
      BrowsingContextGroup::GetOrCreate(aGroupId);

  for (auto& entry : aUseOriginAgentCluster) {
    group->SetUseOriginAgentClusterFromIPC(entry.principal(),
                                           entry.useOriginAgentCluster());
  }

  // Each of the initializers in aInits is sorted in pre-order, so our parent
  // should always be available before the element itself.
  for (auto& initUnion : aInits) {
    switch (initUnion.type()) {
      case SyncedContextInitializer::TBrowsingContextInitializer: {
        auto& init = initUnion.get_BrowsingContextInitializer();
#ifdef DEBUG
        RefPtr<BrowsingContext> existing = BrowsingContext::Get(init.mId);
        MOZ_ASSERT(!existing, "BrowsingContext must not exist yet!");

        RefPtr<WindowContext> parent = init.GetParent();
        MOZ_ASSERT_IF(parent, parent->Group() == group);
#endif

        BrowsingContext::CreateFromIPC(std::move(init), group, nullptr);
        break;
      }
      case SyncedContextInitializer::TWindowContextInitializer: {
        auto& init = initUnion.get_WindowContextInitializer();
#ifdef DEBUG
        RefPtr<WindowContext> existing =
            WindowContext::GetById(init.mInnerWindowId);
        MOZ_ASSERT(!existing, "WindowContext must not exist yet!");
        RefPtr<BrowsingContext> parent =
            BrowsingContext::Get(init.mBrowsingContextId);
        MOZ_ASSERT(parent && parent->Group() == group);
#endif

        WindowContext::CreateFromIPC(std::move(init));
        break;
      };
      default:
        MOZ_ASSERT_UNREACHABLE();
    }
  }

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvDestroyBrowsingContextGroup(
    uint64_t aGroupId) {
  if (RefPtr<BrowsingContextGroup> group =
          BrowsingContextGroup::GetExisting(aGroupId)) {
    group->ChildDestroy();
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvSetUseOriginAgentCluster(
    uint64_t aGroupId, nsIPrincipal* aPrincipal, bool aUseOriginAgentCluster) {
  RefPtr<BrowsingContextGroup> group =
      BrowsingContextGroup::GetOrCreate(aGroupId);
  group->SetUseOriginAgentClusterFromIPC(aPrincipal, aUseOriginAgentCluster);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvWindowClose(
    const MaybeDiscarded<BrowsingContext>& aContext, bool aTrustedCaller) {
  if (aContext.IsNullOrDiscarded()) {
    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
            ("ChildIPC: Trying to send a message to dead or detached context"));
    return IPC_OK();
  }

  nsCOMPtr<nsPIDOMWindowOuter> window = aContext.get()->GetDOMWindow();
  if (!window) {
    MOZ_LOG(
        BrowsingContext::GetLog(), LogLevel::Debug,
        ("ChildIPC: Trying to send a message to a context without a window"));
    return IPC_OK();
  }

  // Call `GetDocument()` to force the document and its inner window to be
  // created, as it would be forced to be created if this call was being
  // performed in-process.
  if (NS_WARN_IF(!aContext.get()->GetDocument())) {
    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
            ("ChildIPC: Trying to send a message to a context but document "
             "creation failed"));
    return IPC_OK();
  }

  nsGlobalWindowOuter::Cast(window)->CloseOuter(aTrustedCaller);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvWindowFocus(
    const MaybeDiscarded<BrowsingContext>& aContext, CallerType aCallerType,
    uint64_t aActionId) {
  if (aContext.IsNullOrDiscarded()) {
    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
            ("ChildIPC: Trying to send a message to dead or detached context"));
    return IPC_OK();
  }

  nsCOMPtr<nsPIDOMWindowOuter> window = aContext.get()->GetDOMWindow();
  if (!window) {
    MOZ_LOG(
        BrowsingContext::GetLog(), LogLevel::Debug,
        ("ChildIPC: Trying to send a message to a context without a window"));
    return IPC_OK();
  }

  // Call `GetDocument()` to force the document and its inner window to be
  // created, as it would be forced to be created if this call was being
  // performed in-process.
  if (NS_WARN_IF(!aContext.get()->GetDocument())) {
    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
            ("ChildIPC: Trying to send a message to a context but document "
             "creation failed"));
    return IPC_OK();
  }

  nsGlobalWindowOuter::Cast(window)->FocusOuter(
      aCallerType, /* aFromOtherProcess */ true, aActionId);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvWindowBlur(
    const MaybeDiscarded<BrowsingContext>& aContext, CallerType aCallerType) {
  if (aContext.IsNullOrDiscarded()) {
    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
            ("ChildIPC: Trying to send a message to dead or detached context"));
    return IPC_OK();
  }

  nsCOMPtr<nsPIDOMWindowOuter> window = aContext.get()->GetDOMWindow();
  if (!window) {
    MOZ_LOG(
        BrowsingContext::GetLog(), LogLevel::Debug,
        ("ChildIPC: Trying to send a message to a context without a window"));
    return IPC_OK();
  }

  // Call `GetDocument()` to force the document and its inner window to be
  // created, as it would be forced to be created if this call was being
  // performed in-process.
  if (NS_WARN_IF(!aContext.get()->GetDocument())) {
    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
            ("ChildIPC: Trying to send a message to a context but document "
             "creation failed"));
    return IPC_OK();
  }

  nsGlobalWindowOuter::Cast(window)->BlurOuter(aCallerType);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvRaiseWindow(
    const MaybeDiscarded<BrowsingContext>& aContext, CallerType aCallerType,
    uint64_t aActionId) {
  if (aContext.IsNullOrDiscarded()) {
    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
            ("ChildIPC: Trying to send a message to dead or detached context"));
    return IPC_OK();
  }

  nsCOMPtr<nsPIDOMWindowOuter> window = aContext.get()->GetDOMWindow();
  if (!window) {
    MOZ_LOG(
        BrowsingContext::GetLog(), LogLevel::Debug,
        ("ChildIPC: Trying to send a message to a context without a window"));
    return IPC_OK();
  }

  if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
    fm->RaiseWindow(window, aCallerType, aActionId);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvAdjustWindowFocus(
    const MaybeDiscarded<BrowsingContext>& aContext, bool aIsVisible,
    uint64_t aActionId, bool aShouldClearAncestorFocus,
    const MaybeDiscarded<BrowsingContext>& aAncestorBrowsingContextToFocus) {
  if (aContext.IsNullOrDiscarded()) {
    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
            ("ChildIPC: Trying to send a message to dead or detached context"));
    return IPC_OK();
  }

  if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
    RefPtr<BrowsingContext> bc = aContext.get();
    RefPtr<BrowsingContext> ancestor =
        aAncestorBrowsingContextToFocus.IsNullOrDiscarded()
            ? nullptr
            : aAncestorBrowsingContextToFocus.get();
    fm->AdjustInProcessWindowFocus(bc, false, aIsVisible, aActionId,
                                   aShouldClearAncestorFocus, ancestor);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvClearFocus(
    const MaybeDiscarded<BrowsingContext>& aContext) {
  if (aContext.IsNullOrDiscarded()) {
    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
            ("ChildIPC: Trying to send a message to dead or detached context"));
    return IPC_OK();
  }

  nsCOMPtr<nsPIDOMWindowOuter> window = aContext.get()->GetDOMWindow();
  if (!window) {
    MOZ_LOG(
        BrowsingContext::GetLog(), LogLevel::Debug,
        ("ChildIPC: Trying to send a message to a context without a window"));
    return IPC_OK();
  }

  if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
    fm->ClearFocus(window);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvSetFocusedBrowsingContext(
    const MaybeDiscarded<BrowsingContext>& aContext, uint64_t aActionId) {
  if (aContext.IsNullOrDiscarded()) {
    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
            ("ChildIPC: Trying to send a message to dead or detached context"));
    return IPC_OK();
  }

  nsFocusManager* fm = nsFocusManager::GetFocusManager();
  if (fm) {
    fm->SetFocusedBrowsingContextFromOtherProcess(aContext.get(), aActionId);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvSetActiveBrowsingContext(
    const MaybeDiscarded<BrowsingContext>& aContext, uint64_t aActionId) {
  if (aContext.IsNullOrDiscarded()) {
    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
            ("ChildIPC: Trying to send a message to dead or detached context"));
    return IPC_OK();
  }

  nsFocusManager* fm = nsFocusManager::GetFocusManager();
  if (fm) {
    fm->SetActiveBrowsingContextFromOtherProcess(aContext.get(), aActionId);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvAbortOrientationPendingPromises(
    const MaybeDiscarded<BrowsingContext>& aContext) {
  if (aContext.IsNullOrDiscarded()) {
    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
            ("ChildIPC: Trying to send a message to dead or detached context"));
    return IPC_OK();
  }

  dom::ScreenOrientation::AbortInProcessOrientationPromises(aContext.get());
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvUnsetActiveBrowsingContext(
    const MaybeDiscarded<BrowsingContext>& aContext, uint64_t aActionId) {
  if (aContext.IsNullOrDiscarded()) {
    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
            ("ChildIPC: Trying to send a message to dead or detached context"));
    return IPC_OK();
  }

  nsFocusManager* fm = nsFocusManager::GetFocusManager();
  if (fm) {
    fm->UnsetActiveBrowsingContextFromOtherProcess(aContext.get(), aActionId);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvSetFocusedElement(
    const MaybeDiscarded<BrowsingContext>& aContext, bool aNeedsFocus) {
  if (aContext.IsNullOrDiscarded()) {
    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
            ("ChildIPC: Trying to send a message to dead or detached context"));
    return IPC_OK();
  }

  nsCOMPtr<nsPIDOMWindowOuter> window = aContext.get()->GetDOMWindow();
  if (!window) {
    MOZ_LOG(
        BrowsingContext::GetLog(), LogLevel::Debug,
        ("ChildIPC: Trying to send a message to a context without a window"));
    return IPC_OK();
  }

  window->SetFocusedElement(nullptr, 0, aNeedsFocus);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvFinalizeFocusOuter(
    const MaybeDiscarded<BrowsingContext>& aContext, bool aCanFocus,
    CallerType aCallerType) {
  if (aContext.IsNullOrDiscarded()) {
    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
            ("ChildIPC: Trying to send a message to dead or detached context"));
    return IPC_OK();
  }

  if (Element* frame = aContext.get()->GetEmbedderElement()) {
    nsContentUtils::RequestFrameFocus(*frame, aCanFocus, aCallerType);
  }

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvBlurToChild(
    const MaybeDiscarded<BrowsingContext>& aFocusedBrowsingContext,
    const MaybeDiscarded<BrowsingContext>& aBrowsingContextToClear,
    const MaybeDiscarded<BrowsingContext>& aAncestorBrowsingContextToFocus,
    bool aIsLeavingDocument, bool aAdjustWidget, uint64_t aActionId) {
  if (aFocusedBrowsingContext.IsNullOrDiscarded()) {
    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
            ("ChildIPC: Trying to send a message to dead or detached context"));
    return IPC_OK();
  }

  RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager();
  if (MOZ_UNLIKELY(!fm)) {
    return IPC_OK();
  }

  RefPtr<BrowsingContext> toClear = aBrowsingContextToClear.IsDiscarded()
                                        ? nullptr
                                        : aBrowsingContextToClear.get();
  RefPtr<BrowsingContext> toFocus =
      aAncestorBrowsingContextToFocus.IsDiscarded()
          ? nullptr
          : aAncestorBrowsingContextToFocus.get();

  RefPtr<BrowsingContext> focusedBrowsingContext =
      aFocusedBrowsingContext.get();

  fm->BlurFromOtherProcess(focusedBrowsingContext, toClear, toFocus,
                           aIsLeavingDocument, aAdjustWidget, aActionId);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvSetupFocusedAndActive(
    const MaybeDiscarded<BrowsingContext>& aFocusedBrowsingContext,
    uint64_t aActionIdForFocused,
    const MaybeDiscarded<BrowsingContext>& aActiveBrowsingContext,
    uint64_t aActionIdForActive) {
  nsFocusManager* fm = nsFocusManager::GetFocusManager();
  if (fm) {
    if (!aActiveBrowsingContext.IsNullOrDiscarded()) {
      fm->SetActiveBrowsingContextFromOtherProcess(aActiveBrowsingContext.get(),
                                                   aActionIdForActive);
    }
    if (!aFocusedBrowsingContext.IsNullOrDiscarded()) {
      fm->SetFocusedBrowsingContextFromOtherProcess(
          aFocusedBrowsingContext.get(), aActionIdForFocused);
    }
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvReviseActiveBrowsingContext(
    uint64_t aOldActionId,
    const MaybeDiscarded<BrowsingContext>& aActiveBrowsingContext,
    uint64_t aNewActionId) {
  nsFocusManager* fm = nsFocusManager::GetFocusManager();
  if (fm && !aActiveBrowsingContext.IsNullOrDiscarded()) {
    fm->ReviseActiveBrowsingContext(aOldActionId, aActiveBrowsingContext.get(),
                                    aNewActionId);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvReviseFocusedBrowsingContext(
    uint64_t aOldActionId,
    const MaybeDiscarded<BrowsingContext>& aFocusedBrowsingContext,
    uint64_t aNewActionId) {
  nsFocusManager* fm = nsFocusManager::GetFocusManager();
  if (fm && !aFocusedBrowsingContext.IsNullOrDiscarded()) {
    fm->ReviseFocusedBrowsingContext(
        aOldActionId, aFocusedBrowsingContext.get(), aNewActionId);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvMaybeExitFullscreen(
    const MaybeDiscarded<BrowsingContext>& aContext) {
  if (aContext.IsNullOrDiscarded()) {
    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
            ("ChildIPC: Trying to send a message to dead or detached context"));
    return IPC_OK();
  }

  nsIDocShell* shell = aContext.get()->GetDocShell();
  if (!shell) {
    return IPC_OK();
  }

  Document* doc = shell->GetDocument();
  if (doc && doc->GetFullscreenElement()) {
    Document::AsyncExitFullscreen(doc);
  }

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvWindowPostMessage(
    const MaybeDiscarded<BrowsingContext>& aContext,
    StructuredCloneData* aMessage, const PostMessageData& aData) {
  if (aContext.IsNullOrDiscarded()) {
    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
            ("ChildIPC: Trying to send a message to dead or detached context"));
    return IPC_OK();
  }

  RefPtr<nsGlobalWindowOuter> window =
      nsGlobalWindowOuter::Cast(aContext.get()->GetDOMWindow());
  if (!window) {
    MOZ_LOG(
        BrowsingContext::GetLog(), LogLevel::Debug,
        ("ChildIPC: Trying to send a message to a context without a window"));
    return IPC_OK();
  }

  nsCOMPtr<nsIPrincipal> providedPrincipal;
  if (!window->GetPrincipalForPostMessage(
          aData.targetOrigin(), aData.targetOriginURI(),
          aData.callerPrincipal(), *aData.subjectPrincipal(),
          getter_AddRefs(providedPrincipal))) {
    return IPC_OK();
  }

  // Call `GetDocument()` to force the document and its inner window to be
  // created, as it would be forced to be created if this call was being
  // performed in-process.
  if (NS_WARN_IF(!aContext.get()->GetDocument())) {
    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
            ("ChildIPC: Trying to send a message to a context but document "
             "creation failed"));
    return IPC_OK();
  }

  // It's OK if `sourceBc` has already been discarded, so long as we can
  // continue to wrap it.
  RefPtr<BrowsingContext> sourceBc = aData.source().GetMaybeDiscarded();

  // Create and asynchronously dispatch a runnable which will handle actual DOM
  // event creation and dispatch.
  RefPtr<PostMessageEvent> event =
      new PostMessageEvent(sourceBc, aData.origin(), window, providedPrincipal,
                           aData.innerWindowId(), aData.callerURI(),
                           aData.scriptLocation(), aData.isFromPrivateWindow());
  if (aMessage) {
    event->SetMessageData(aMessage);
  }

  event->DispatchToTargetThread(IgnoredErrorResult());
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvCommitBrowsingContextTransaction(
    const MaybeDiscarded<BrowsingContext>& aContext,
    BrowsingContext::BaseTransaction&& aTransaction, uint64_t aEpoch) {
  return aTransaction.CommitFromIPC(aContext, aEpoch, this);
}

mozilla::ipc::IPCResult ContentChild::RecvCommitWindowContextTransaction(
    const MaybeDiscarded<WindowContext>& aContext,
    WindowContext::BaseTransaction&& aTransaction, uint64_t aEpoch) {
  return aTransaction.CommitFromIPC(aContext, aEpoch, this);
}

mozilla::ipc::IPCResult ContentChild::RecvCreateWindowContext(
    WindowContext::IPCInitializer&& aInit) {
  RefPtr<BrowsingContext> bc = BrowsingContext::Get(aInit.mBrowsingContextId);
  if (!bc) {
    // Handle this case by ignoring the request, as bc must be in the process of
    // being discarded.
    // In the future it would be nice to avoid sending this message to the child
    // at all.
    NS_WARNING("Attempt to attach WindowContext to discarded parent");
    return IPC_OK();
  }

  WindowContext::CreateFromIPC(std::move(aInit));
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvDiscardWindowContext(
    uint64_t aContextId, DiscardWindowContextResolver&& aResolve) {
  // Resolve immediately to acknowledge call
  aResolve(true);

  RefPtr<WindowContext> window = WindowContext::GetById(aContextId);
  if (NS_WARN_IF(!window) || NS_WARN_IF(window->IsDiscarded())) {
    return IPC_OK();
  }

  window->Discard();
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvScriptError(
    const nsString& aMessage, const nsCString& aSourceName,
    const uint32_t& aLineNumber, const uint32_t& aColNumber,
    const uint32_t& aFlags, const nsCString& aCategory,
    const bool& aFromPrivateWindow, const uint64_t& aInnerWindowId,
    const bool& aFromChromeContext) {
  nsresult rv = NS_OK;
  nsCOMPtr<nsIConsoleService> consoleService =
      do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv);
  NS_ENSURE_SUCCESS(rv, IPC_FAIL(this, "Failed to get console service"));

  nsCOMPtr<nsIScriptError> scriptError(
      do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
  NS_ENSURE_TRUE(scriptError,
                 IPC_FAIL(this, "Failed to construct nsIScriptError"));

  scriptError->InitWithWindowID(aMessage, aSourceName, aLineNumber, aColNumber,
                                aFlags, aCategory, aInnerWindowId,
                                aFromChromeContext);
  rv = consoleService->LogMessage(scriptError);
  NS_ENSURE_SUCCESS(rv, IPC_FAIL(this, "Failed to log script error"));

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvReportFrameTimingData(
    const LoadInfoArgs& loadInfoArgs, const nsString& entryName,
    const nsString& initiatorType, UniquePtr<PerformanceTimingData>&& aData) {
  if (!aData) {
    return IPC_FAIL(this, "aData should not be null");
  }

  nsCOMPtr<nsILoadInfo> loadInfo;
  nsresult rv = mozilla::ipc::LoadInfoArgsToLoadInfo(
      loadInfoArgs, NOT_REMOTE_TYPE, getter_AddRefs(loadInfo));
  if (NS_FAILED(rv)) {
    MOZ_DIAGNOSTIC_CRASH("LoadInfoArgsToLoadInfo failed");
    return IPC_OK();
  }

  // It is important to call LoadInfo::GetPerformanceStorage instead of simply
  // getting the performance object via the innerWindowID in order to perform
  // necessary cross origin checks.
  if (PerformanceStorage* storage = loadInfo->GetPerformanceStorage()) {
    storage->AddEntry(entryName, initiatorType, std::move(aData));
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvLoadURI(
    const MaybeDiscarded<BrowsingContext>& aContext,
    nsDocShellLoadState* aLoadState, bool aSetNavigating) {
  if (aContext.IsNullOrDiscarded()) {
    return IPC_OK();
  }
  RefPtr<BrowsingContext> context = aContext.get();
  if (!context->IsInProcess()) {
    // The DocShell has been torn down or the BrowsingContext has changed
    // process in the middle of the load request. There's not much we can do at
    // this point, so just give up.
    return IPC_OK();
  }

  context->LoadURI(aLoadState, aSetNavigating);

  nsCOMPtr<nsPIDOMWindowOuter> window = context->GetDOMWindow();
  BrowserChild* bc = BrowserChild::GetFrom(window);
  if (bc) {
    bc->NotifyNavigationFinished();
  }

#ifdef MOZ_CRASHREPORTER
  if (CrashReporter::GetEnabled()) {
    nsCOMPtr<nsIURI> annotationURI;

    nsresult rv = NS_MutateURI(aLoadState->URI())
                      .SetUserPass(""_ns)
                      .Finalize(annotationURI);

    if (NS_FAILED(rv)) {
      // Ignore failures on about: URIs.
      annotationURI = aLoadState->URI();
    }

    CrashReporter::RecordAnnotationNSCString(CrashReporter::Annotation::URL,
                                             annotationURI->GetSpecOrDefault());
  }
#endif

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvInternalLoad(
    nsDocShellLoadState* aLoadState) {
  if (!aLoadState->Target().IsEmpty() ||
      aLoadState->TargetBrowsingContext().IsNull()) {
    return IPC_FAIL(this, "must already be retargeted");
  }
  if (aLoadState->TargetBrowsingContext().IsDiscarded()) {
    return IPC_OK();
  }
  RefPtr<BrowsingContext> context = aLoadState->TargetBrowsingContext().get();

  context->InternalLoad(aLoadState);

#ifdef MOZ_CRASHREPORTER
  if (CrashReporter::GetEnabled()) {
    nsCOMPtr<nsIURI> annotationURI;

    nsresult rv = NS_MutateURI(aLoadState->URI())
                      .SetUserPass(""_ns)
                      .Finalize(annotationURI);

    if (NS_FAILED(rv)) {
      // Ignore failures on about: URIs.
      annotationURI = aLoadState->URI();
    }

    CrashReporter::RecordAnnotationNSCString(CrashReporter::Annotation::URL,
                                             annotationURI->GetSpecOrDefault());
  }
#endif

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvDisplayLoadError(
    const MaybeDiscarded<BrowsingContext>& aContext, const nsAString& aURI) {
  if (aContext.IsNullOrDiscarded()) {
    return IPC_OK();
  }
  RefPtr<BrowsingContext> context = aContext.get();

  context->DisplayLoadError(aURI);

  nsCOMPtr<nsPIDOMWindowOuter> window = context->GetDOMWindow();
  BrowserChild* bc = BrowserChild::GetFrom(window);
  if (bc) {
    bc->NotifyNavigationFinished();
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvHistoryCommitIndexAndLength(
    const MaybeDiscarded<BrowsingContext>& aContext, const uint32_t& aIndex,
    const uint32_t& aLength, const nsID& aChangeID,
    nsTArray<NavigationEntriesTruncation>&& aTruncations) {
  if (!aContext.IsNullOrDiscarded()) {
    ChildSHistory* shistory = aContext.get()->GetChildSessionHistory();
    if (shistory) {
      shistory->SetIndexAndLength(aIndex, aLength, aChangeID);
    }
  }

  for (const auto& truncation : aTruncations) {
    if (truncation.context().IsNullOrDiscarded()) {
      continue;
    }
    RefPtr<nsDocShell> docShell =
        nsDocShell::Cast(truncation.context().get()->GetDocShell());
    if (!docShell) {
      continue;
    }
    RefPtr<nsPIDOMWindowInner> window = docShell->GetActiveWindow();
    if (!window) {
      continue;
    }
    if (RefPtr<Navigation> navigation = window->Navigation()) {
      navigation->TruncateForwardEntries(truncation.newLength());
    }
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvConsumeHistoryActivation(
    const MaybeDiscarded<BrowsingContext>& aTop) {
  if (!aTop.IsNullOrDiscarded()) {
    aTop->ConsumeHistoryActivation();
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvGetLayoutHistoryState(
    const MaybeDiscarded<BrowsingContext>& aContext,
    GetLayoutHistoryStateResolver&& aResolver) {
  nsCOMPtr<nsILayoutHistoryState> state;
  nsIDocShell* docShell;
  mozilla::Maybe<mozilla::dom::Wireframe> wireframe;
  if (!aContext.IsNullOrDiscarded() &&
      (docShell = aContext.get()->GetDocShell())) {
    docShell->PersistLayoutHistoryState();
    docShell->GetLayoutHistoryState(getter_AddRefs(state));
    wireframe = static_cast<nsDocShell*>(docShell)->GetWireframe();
  }
  aResolver(
      std::tuple<nsILayoutHistoryState*, const mozilla::Maybe<Wireframe>&>(
          state, wireframe));

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvDispatchLocationChangeEvent(
    const MaybeDiscarded<BrowsingContext>& aContext) {
  if (!aContext.IsNullOrDiscarded() && aContext.get()->GetDocShell()) {
    aContext.get()->GetDocShell()->DispatchLocationChangeEvent();
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvDispatchBeforeUnloadToSubtree(
    const MaybeDiscarded<BrowsingContext>& aStartingAt,
    const mozilla::Maybe<mozilla::NotNull<RefPtr<nsDocShellLoadState>>>&
        aLoadState,
    DispatchBeforeUnloadToSubtreeResolver&& aResolver) {
  if (aStartingAt.IsNullOrDiscarded()) {
    aResolver(nsIDocumentViewer::eContinue);
  } else {
    DispatchBeforeUnloadToSubtree(aStartingAt.get(), aLoadState, aResolver);
  }
  return IPC_OK();
}

/* static */ void ContentChild::DispatchBeforeUnloadToSubtree(
    BrowsingContext* aStartingAt,
    const mozilla::Maybe<mozilla::NotNull<RefPtr<nsDocShellLoadState>>>&
        aLoadState,
    const DispatchBeforeUnloadToSubtreeResolver& aResolver) {
  bool resolved = false;

  aStartingAt->PreOrderWalk(
      [&](const RefPtr<dom::BrowsingContext>& aBC)
          MOZ_CAN_RUN_SCRIPT_BOUNDARY_LAMBDA {
            if (RefPtr docShell = nsDocShell::Cast(aBC->GetDocShell())) {
              nsCOMPtr<nsIDocumentViewer> viewer;
              docShell->GetDocViewer(getter_AddRefs(viewer));
              nsIDocumentViewer::PermitUnloadResult finalStatus =
                  nsIDocumentViewer::eContinue;
              if (viewer) {
                finalStatus = viewer->DispatchBeforeUnload();
              }

              if (finalStatus == nsIDocumentViewer::eContinue && aBC->IsTop() &&
                  aLoadState) {
                // https://html.spec.whatwg.org/#preventing-navigation:fire-a-traverse-navigate-event.
                // If this is the top-level navigable and we've passed `aInfo`,
                // we should perform #fire-a-traverse-navigate-event.
                // #checking-if-unloading-is-canceled will block the navigation
                // if the "navigate" event handler for the top level window's
                // navigation object returns false.
                // This should send the correct user involvment. See bug
                // 1903552.
                RefPtr<nsDocShellLoadState> loadState = *aLoadState;
                finalStatus = docShell->MaybeFireTraversableTraverseHistory(
                    loadState, Nothing());
              }

              if (!resolved && finalStatus != nsIDocumentViewer::eContinue) {
                // Send our response as soon as we find any blocker, so that we
                // can show the permit unload prompt as soon as possible,
                // without giving subsequent handlers a chance to delay it.
                aResolver(finalStatus);
                resolved = true;
              }
            }
          });

  if (!resolved) {
    aResolver(nsIDocumentViewer::eContinue);
  }
}

mozilla::ipc::IPCResult ContentChild::RecvDispatchNavigateToTraversable(
    const MaybeDiscarded<BrowsingContext>& aTraversable,
    const mozilla::NotNull<RefPtr<nsDocShellLoadState>>& aLoadState,
    DispatchNavigateToTraversableResolver&& aResolver) {
  if (aTraversable.IsNullOrDiscarded() || !aTraversable->GetDocShell()) {
    aResolver(nsIDocumentViewer::eContinue);
  } else {
    RefPtr docShell = nsDocShell::Cast(aTraversable->GetDocShell());
    aResolver(docShell->MaybeFireTraversableTraverseHistory(
        MOZ_KnownLive(aLoadState.get()), Nothing()));
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvInitNextGenLocalStorageEnabled(
    const bool& aEnabled) {
  mozilla::dom::RecvInitNextGenLocalStorageEnabled(aEnabled);

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvGoBack(
    const MaybeDiscarded<BrowsingContext>& aContext,
    const Maybe<int32_t>& aCancelContentJSEpoch, bool aRequireUserInteraction,
    bool aUserActivation) {
  if (aContext.IsNullOrDiscarded()) {
    return IPC_OK();
  }
  BrowsingContext* bc = aContext.get();

  if (RefPtr<nsDocShell> docShell = nsDocShell::Cast(bc->GetDocShell())) {
    if (aCancelContentJSEpoch) {
      docShell->SetCancelContentJSEpoch(*aCancelContentJSEpoch);
    }
    docShell->GoBack(aRequireUserInteraction, aUserActivation);

    if (BrowserChild* browserChild = BrowserChild::GetFrom(docShell)) {
      browserChild->NotifyNavigationFinished();
    }
  }

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvGoForward(
    const MaybeDiscarded<BrowsingContext>& aContext,
    const Maybe<int32_t>& aCancelContentJSEpoch, bool aRequireUserInteraction,
    bool aUserActivation) {
  if (aContext.IsNullOrDiscarded()) {
    return IPC_OK();
  }
  BrowsingContext* bc = aContext.get();

  if (RefPtr<nsDocShell> docShell = nsDocShell::Cast(bc->GetDocShell())) {
    if (aCancelContentJSEpoch) {
      docShell->SetCancelContentJSEpoch(*aCancelContentJSEpoch);
    }
    docShell->GoForward(aRequireUserInteraction, aUserActivation);

    if (BrowserChild* browserChild = BrowserChild::GetFrom(docShell)) {
      browserChild->NotifyNavigationFinished();
    }
  }

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvGoToIndex(
    const MaybeDiscarded<BrowsingContext>& aContext, const int32_t& aIndex,
    const Maybe<int32_t>& aCancelContentJSEpoch, bool aUserActivation) {
  if (aContext.IsNullOrDiscarded()) {
    return IPC_OK();
  }
  BrowsingContext* bc = aContext.get();

  if (RefPtr<nsDocShell> docShell = nsDocShell::Cast(bc->GetDocShell())) {
    if (aCancelContentJSEpoch) {
      docShell->SetCancelContentJSEpoch(*aCancelContentJSEpoch);
    }
    docShell->GotoIndex(aIndex, aUserActivation);

    if (BrowserChild* browserChild = BrowserChild::GetFrom(docShell)) {
      browserChild->NotifyNavigationFinished();
    }
  }

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvReload(
    const MaybeDiscarded<BrowsingContext>& aContext,
    const uint32_t aReloadFlags) {
  if (aContext.IsNullOrDiscarded()) {
    return IPC_OK();
  }
  BrowsingContext* bc = aContext.get();

  if (RefPtr<nsDocShell> docShell = nsDocShell::Cast(bc->GetDocShell())) {
    docShell->Reload(aReloadFlags);
  }

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvStopLoad(
    const MaybeDiscarded<BrowsingContext>& aContext,
    const uint32_t aStopFlags) {
  if (aContext.IsNullOrDiscarded()) {
    return IPC_OK();
  }
  BrowsingContext* bc = aContext.get();

  if (auto* docShell = nsDocShell::Cast(bc->GetDocShell())) {
    docShell->Stop(aStopFlags);
  }

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentChild::RecvDeactivateDocuments(
    const MaybeDiscarded<BrowsingContext>& aContext) {
  if (aContext.IsNullOrDiscarded()) {
    return IPC_OK();
  }
  BrowsingContext* browsingContext = aContext.get();
  MOZ_DIAGNOSTIC_ASSERT(browsingContext->IsTopContent());

  browsingContext->DeactivateDocuments();

  return IPC_OK();
}

// https://html.spec.whatwg.org/#update-document-for-history-step-application
// Perform steps 7 and 9 for the BFCache case.
mozilla::ipc::IPCResult ContentChild::RecvReactivateDocuments(
    const MaybeDiscarded<BrowsingContext>& aContext,
    const Maybe<SessionHistoryInfo>& aReactivatedEntry,
    const nsTArray<SessionHistoryInfo>& aNewSHEs,
    const Maybe<PreviousSessionHistoryInfo>& aPreviousEntryForActivation) {
  if (aContext.IsNullOrDiscarded()) {
    return IPC_OK();
  }
  RefPtr browsingContext = aContext.get();
  MOZ_DIAGNOSTIC_ASSERT(browsingContext->IsTopContent());

  browsingContext->ReactivateDocuments(aReactivatedEntry, aNewSHEs,
                                       aPreviousEntryForActivation);

  return IPC_OK();
}

#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS)
mozilla::ipc::IPCResult ContentChild::RecvInitSandboxTesting(
    Endpoint<PSandboxTestingChild>&& aEndpoint) {
  if (!SandboxTestingChild::Initialize(std::move(aEndpoint))) {
    return IPC_FAIL(
        this, "InitSandboxTesting failed to initialise the child process.");
  }
  return IPC_OK();
}
#endif

NS_IMETHODIMP ContentChild::GetChildID(uint64_t* aOut) {
  *aOut = XRE_GetChildID();
  return NS_OK;
}

NS_IMETHODIMP ContentChild::GetActor(const nsACString& aName, JSContext* aCx,
                                     JSProcessActorChild** retval) {
  ErrorResult error;
  RefPtr<JSProcessActorChild> actor =
      JSActorManager::GetActor(aCx, aName, error)
          .downcast<JSProcessActorChild>();
  if (error.MaybeSetPendingException(aCx)) {
    return NS_ERROR_FAILURE;
  }
  actor.forget(retval);
  return NS_OK;
}

NS_IMETHODIMP ContentChild::GetExistingActor(const nsACString& aName,
                                             JSProcessActorChild** retval) {
  RefPtr<JSProcessActorChild> actor =
      JSActorManager::GetExistingActor(aName).downcast<JSProcessActorChild>();
  actor.forget(retval);
  return NS_OK;
}

already_AddRefed<JSActor> ContentChild::InitJSActor(
    JS::Handle<JSObject*> aMaybeActor, const nsACString& aName,
    ErrorResult& aRv) {
  RefPtr<JSProcessActorChild> actor;
  if (aMaybeActor.get()) {
    aRv = UNWRAP_OBJECT(JSProcessActorChild, aMaybeActor.get(), actor);
    if (aRv.Failed()) {
      return nullptr;
    }
  } else {
    actor = new JSProcessActorChild();
  }

  MOZ_RELEASE_ASSERT(!actor->Manager(),
                     "mManager was already initialized once!");
  actor->Init(aName, this);
  return actor.forget();
}

IPCResult ContentChild::RecvRawMessage(const JSActorMessageMeta& aMeta,
                                       JSIPCValue&& aData,
                                       ipc::StructuredCloneData* aStack) {
  ReceiveRawMessage(aMeta, std::move(aData), aStack);
  return IPC_OK();
}

NS_IMETHODIMP ContentChild::GetCanSend(bool* aCanSend) {
  *aCanSend = CanSend();
  return NS_OK;
}

ContentChild* ContentChild::AsContentChild() { return this; }

JSActorManager* ContentChild::AsJSActorManager() { return this; }

IPCResult ContentChild::RecvCreateFOGTransport(
    Endpoint<PFOGTransportChild>&& aChildEndpoint) {
  if (glean::FOGTransportChild::GetSingleton()) {
    return IPC_FAIL(this, "FOGTransportChild already created");
  }

  glean::FOGTransportChild::Create(std::move(aChildEndpoint));

  return IPC_OK();
}

IPCResult ContentChild::RecvFlushFOGData(FlushFOGDataResolver&& aResolver) {
  glean::FlushFOGData(std::move(aResolver));
  return IPC_OK();
}

IPCResult ContentChild::RecvSystemPermissionChanged(PermissionName aName,
                                                    PermissionState aState) {
  PermissionObserver::NotifySystemPermissionChanged(aName, aState);
  return IPC_OK();
}

IPCResult ContentChild::RecvUpdateMediaCodecsSupported(
    RemoteMediaIn aLocation, const media::MediaCodecsSupported& aSupported) {
  RemoteMediaManagerChild::SetSupported(aLocation, aSupported);

  return IPC_OK();
}

void ContentChild::ConfigureThreadPerformanceHints(
    const hal::ProcessPriority& aPriority) {
  if (aPriority >= hal::PROCESS_PRIORITY_FOREGROUND) {
    static bool canUsePerformanceHintSession = true;
    if (!mPerformanceHintSession && canUsePerformanceHintSession) {
      nsTArray<PlatformThreadHandle> threads;
      Servo_ThreadPool_GetThreadHandles(&threads);
#ifdef XP_WIN
      threads.AppendElement(GetCurrentThread());
#else
      threads.AppendElement(pthread_self());
#endif

      mPerformanceHintSession = hal::CreatePerformanceHintSession(
          threads, GetPerformanceHintTarget(TimeDuration::FromMilliseconds(
                       nsRefreshDriver::DefaultInterval())));

      // Avoid repeatedly attempting to create a session if it is not
      // supported.
      canUsePerformanceHintSession = mPerformanceHintSession != nullptr;
    }

#ifdef MOZ_WIDGET_ANDROID
    // On Android if we are unable to use PerformanceHintManager then fall back
    // to setting the stylo threads' affinities to the performant cores. Android
    // automatically sets each thread's affinity to all cores when a process is
    // foregrounded, and to a subset of cores when the process is backgrounded.
    // We must therefore override this each time the process is foregrounded,
    // but we don't have to do anything when backgrounded.
    if (!mPerformanceHintSession) {
      if (const auto& cpuInfo = hal::GetHeterogeneousCpuInfo()) {
        // If CPUs are homogeneous there is no point setting affinity.
        if (cpuInfo->mBigCpus.Count() != cpuInfo->mTotalNumCpus) {
          BitSet<hal::HeterogeneousCpuInfo::MAX_CPUS> cpus = cpuInfo->mBigCpus;
          if (cpus.Count() < 2) {
            // From testing on a variety of devices it appears using only the
            // big cores gives best performance when there are 2 or more big
            // cores. If there are fewer than 2 big cores then additionally
            // using the medium cores performs better.
            cpus |= cpuInfo->mMediumCpus;
          }

          static_assert(
              hal::HeterogeneousCpuInfo::MAX_CPUS <= CPU_SETSIZE,
              "HeterogeneousCpuInfo::MAX_CPUS is too large for CPU_SETSIZE");

          cpu_set_t cpuset;
          CPU_ZERO(&cpuset);
          for (size_t i = 0; i < cpuInfo->mTotalNumCpus; i++) {
            if (cpus.test(i)) {
              CPU_SET(i, &cpuset);
            }
          }

          // Only set the affinity for the stylo threads, not the main thread.
          // Testing showed no difference in performance between the two
          // options, and as newly spawned threads automatically inherit their
          // parent's affinity mask there may be unintended consequences to
          // setting the main thread affinity.
          nsTArray<PlatformThreadHandle> threads;
          Servo_ThreadPool_GetThreadHandles(&threads);
          for (pthread_t thread : threads) {
            int ret = sched_setaffinity(pthread_gettid_np(thread),
                                        sizeof(cpu_set_t), &cpuset);
            // Occasionally sched_setaffinity fails, presumably due to a race
            // between us receiving the process foreground signal and whatever
            // is responsible for restricting which processes can use certain
            // cores. Trying again in a runnable seems to do the trick, but if
            // that still fails it's not the end of the world.
            if (ret < 0) {
              NS_DispatchToCurrentThread(NS_NewRunnableFunction(
                  "ContentChild::ConfigureThreadPerformanceHints",
                  [threads = std::move(threads), cpuset] {
                    for (pthread_t thread : threads) {
                      sched_setaffinity(pthread_gettid_np(thread),
                                        sizeof(cpu_set_t), &cpuset);
                    }
                  }));
              break;
            }
          }
        }
      }
    }
#endif
  } else {
    mPerformanceHintSession = nullptr;
  }
}

#ifdef MOZ_WMF_CDM
mozilla::ipc::IPCResult ContentChild::RecvUpdateMFCDMOriginEntries(
    const nsTArray<IPCOriginStatusEntry>& aEntries) {
  MediaKeySystemAccess::UpdateMFCDMOriginEntries(aEntries);
  return IPC_OK();
}
#endif

}  // namespace dom

#if defined(__OpenBSD__) && defined(MOZ_SANDBOX)

static LazyLogModule sPledgeLog("OpenBSDSandbox");

NS_IMETHODIMP
OpenBSDFindPledgeUnveilFilePath(const char* file, nsACString& result) {
  struct stat st;

  // Allow overriding files in /etc/$MOZ_APP_NAME
  result.Assign(nsPrintfCString("/etc/%s/%s", MOZ_APP_NAME, file));
  if (stat(PromiseFlatCString(result).get(), &st) == 0) {
    return NS_OK;
  }

  // Or look in the system default directory
  result.Assign(nsPrintfCString(
      "/usr/local/lib/%s/browser/defaults/preferences/%s", MOZ_APP_NAME, file));
  if (stat(PromiseFlatCString(result).get(), &st) == 0) {
    return NS_OK;
  }

  errx(1, "can't locate %s", file);
}

NS_IMETHODIMP
OpenBSDPledgePromises(const nsACString& aPath) {
  // Using NS_LOCAL_FILE_CONTRACTID/NS_LOCALFILEINPUTSTREAM_CONTRACTID requires
  // a lot of setup before they are supported and we want to pledge early on
  // before all of that, so read the file directly
  std::ifstream input(PromiseFlatCString(aPath).get());

  // Build up one line of pledge promises without comments
  nsAutoCString promises;
  bool disabled = false;
  int linenum = 0;
  for (std::string tLine; std::getline(input, tLine);) {
    nsAutoCString line(tLine.c_str());
    linenum++;

    // Cut off any comments at the end of the line, also catches lines
    // that are entirely a comment
    int32_t hash = line.FindChar('#');
    if (hash >= 0) {
      line = Substring(line, 0, hash);
    }
    line.CompressWhitespace(true, true);
    if (line.IsEmpty()) {
      continue;
    }

    if (linenum == 1 && line.EqualsLiteral("disable")) {
      disabled = true;
      break;
    }

    if (!promises.IsEmpty()) {
      promises.Append(" ");
    }
    promises.Append(line);
  }
  input.close();

  if (disabled) {
    warnx("%s: disabled", PromiseFlatCString(aPath).get());
  } else {
    MOZ_LOG(
        sPledgeLog, LogLevel::Debug,
        ("%s: pledge(%s)\n", PromiseFlatCString(aPath).get(), promises.get()));
    if (pledge(promises.get(), nullptr) != 0) {
      err(1, "%s: pledge(%s) failed", PromiseFlatCString(aPath).get(),
          promises.get());
    }
  }

  return NS_OK;
}

void ExpandUnveilPath(nsAutoCString& path) {
  // Expand $XDG_RUNTIME_DIR to the environment variable, or ~/.cache
  nsCString xdgRuntimeDir(PR_GetEnv("XDG_RUNTIME_DIR"));
  if (xdgRuntimeDir.IsEmpty()) {
    xdgRuntimeDir = "~/.cache";
  }
  path.ReplaceSubstring("$XDG_RUNTIME_DIR", xdgRuntimeDir.get());

  // Expand $XDG_CONFIG_HOME to the environment variable, or ~/.config
  nsCString xdgConfigHome(PR_GetEnv("XDG_CONFIG_HOME"));
  if (xdgConfigHome.IsEmpty()) {
    xdgConfigHome = "~/.config";
  }
  path.ReplaceSubstring("$XDG_CONFIG_HOME", xdgConfigHome.get());

  // Expand $XDG_CACHE_HOME to the environment variable, or ~/.cache
  nsCString xdgCacheHome(PR_GetEnv("XDG_CACHE_HOME"));
  if (xdgCacheHome.IsEmpty()) {
    xdgCacheHome = "~/.cache";
  }
  path.ReplaceSubstring("$XDG_CACHE_HOME", xdgCacheHome.get());

  // Expand $XDG_DATA_HOME to the environment variable, or ~/.local/share
  nsCString xdgDataHome(PR_GetEnv("XDG_DATA_HOME"));
  if (xdgDataHome.IsEmpty()) {
    xdgDataHome = "~/.local/share";
  }
  path.ReplaceSubstring("$XDG_DATA_HOME", xdgDataHome.get());

  // Expand leading ~ to the user's home directory
  nsCOMPtr<nsIFile> homeDir;
  nsresult rv =
      GetSpecialSystemDirectory(Unix_HomeDirectory, getter_AddRefs(homeDir));
  if (NS_FAILED(rv)) {
    errx(1, "failed getting home directory");
  }
  if (path.FindChar('~') == 0) {
    nsCString tHome(homeDir->NativePath());
    tHome.Append(Substring(path, 1, path.Length() - 1));
    path = tHome.get();
  }
}

void MkdirP(nsAutoCString& path) {
  // nsLocalFile::CreateAllAncestors would be nice to use

  nsAutoCString tPath("");
  for (const nsACString& dir : path.Split('/')) {
    struct stat st;

    if (dir.IsEmpty()) {
      continue;
    }

    tPath.Append("/");
    tPath.Append(dir);

    if (stat(tPath.get(), &st) == -1) {
      if (mkdir(tPath.get(), 0700) == -1) {
        err(1, "failed mkdir(%s) while MkdirP(%s)",
            PromiseFlatCString(tPath).get(), PromiseFlatCString(path).get());
      }
    }
  }
}

NS_IMETHODIMP
OpenBSDUnveilPaths(const nsACString& uPath, const nsACString& pledgePath) {
  // Using NS_LOCAL_FILE_CONTRACTID/NS_LOCALFILEINPUTSTREAM_CONTRACTID requires
  // a lot of setup before they are allowed/supported and we want to pledge and
  // unveil early on before all of that is setup
  std::ifstream input(PromiseFlatCString(uPath).get());

  bool disabled = false;
  int linenum = 0;
  for (std::string tLine; std::getline(input, tLine);) {
    nsAutoCString line(tLine.c_str());
    linenum++;

    // Cut off any comments at the end of the line, also catches lines
    // that are entirely a comment
    int32_t hash = line.FindChar('#');
    if (hash >= 0) {
      line = Substring(line, 0, hash);
    }
    line.CompressWhitespace(true, true);
    if (line.IsEmpty()) {
      continue;
    }

    if (linenum == 1 && line.EqualsLiteral("disable")) {
      disabled = true;
      break;
    }

    int32_t space = line.FindChar(' ');
    if (space <= 0) {
      errx(1, "%s: line %d: invalid format", PromiseFlatCString(uPath).get(),
           linenum);
    }

    nsAutoCString uPath(Substring(line, 0, space));
    ExpandUnveilPath(uPath);

    nsAutoCString perms(Substring(line, space + 1, line.Length() - space - 1));

    MOZ_LOG(sPledgeLog, LogLevel::Debug,
            ("%s: unveil(%s, %s)\n", PromiseFlatCString(uPath).get(),
             uPath.get(), perms.get()));
    if (unveil(uPath.get(), perms.get()) == -1 && errno != ENOENT) {
      err(1, "%s: unveil(%s, %s) failed", PromiseFlatCString(uPath).get(),
          uPath.get(), perms.get());
    }
  }
  input.close();

  if (disabled) {
    warnx("%s: disabled", PromiseFlatCString(uPath).get());
  } else {
    struct stat st;

    // Only unveil the pledgePath file if it's not already unveiled, otherwise
    // some containing directory will lose visibility.
    if (stat(PromiseFlatCString(pledgePath).get(), &st) == -1) {
      if (errno == ENOENT) {
        if (unveil(PromiseFlatCString(pledgePath).get(), "r") == -1) {
          err(1, "unveil(%s, r) failed", PromiseFlatCString(pledgePath).get());
        }
      } else {
        err(1, "stat(%s)", PromiseFlatCString(pledgePath).get());
      }
    }
  }

  return NS_OK;
}

bool StartOpenBSDSandbox(GeckoProcessType type, ipc::SandboxingKind kind) {
  nsAutoCString pledgeFile;
  nsAutoCString unveilFile;
  char binaryPath[MAXPATHLEN];

  switch (type) {
    case GeckoProcessType_Default: {
      OpenBSDFindPledgeUnveilFilePath("pledge.main", pledgeFile);
      OpenBSDFindPledgeUnveilFilePath("unveil.main", unveilFile);

      // Ensure dconf dir exists before we veil the filesystem
      nsAutoCString dConf("$XDG_RUNTIME_DIR/dconf");
      ExpandUnveilPath(dConf);
      MkdirP(dConf);
      break;
    }

    case GeckoProcessType_Content:
      OpenBSDFindPledgeUnveilFilePath("pledge.content", pledgeFile);
      OpenBSDFindPledgeUnveilFilePath("unveil.content", unveilFile);
      break;

    case GeckoProcessType_GPU:
      OpenBSDFindPledgeUnveilFilePath("pledge.gpu", pledgeFile);
      OpenBSDFindPledgeUnveilFilePath("unveil.gpu", unveilFile);
      break;

    case GeckoProcessType_Socket:
      OpenBSDFindPledgeUnveilFilePath("pledge.socket", pledgeFile);
      OpenBSDFindPledgeUnveilFilePath("unveil.socket", unveilFile);
      break;

    case GeckoProcessType_RDD:
      OpenBSDFindPledgeUnveilFilePath("pledge.rdd", pledgeFile);
      OpenBSDFindPledgeUnveilFilePath("unveil.rdd", unveilFile);
      break;

    case GeckoProcessType_Utility: {
      MOZ_RELEASE_ASSERT(kind <= SandboxingKind::COUNT,
                         "Should define a sandbox");
      switch (kind) {
        case ipc::SandboxingKind::GENERIC_UTILITY:
        default:
          OpenBSDFindPledgeUnveilFilePath("pledge.utility", pledgeFile);
          OpenBSDFindPledgeUnveilFilePath("unveil.utility", unveilFile);
          break;
      }
    } break;

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

  nsresult rv = mozilla::BinaryPath::Get(binaryPath);
  if (NS_FAILED(rv)) {
    errx(1, "failed to cache binary path ?");
  }

  if (NS_WARN_IF(NS_FAILED(OpenBSDUnveilPaths(unveilFile, pledgeFile)))) {
    errx(1, "failed reading/parsing %s", unveilFile.get());
  }

  if (NS_WARN_IF(NS_FAILED(OpenBSDPledgePromises(pledgeFile)))) {
    errx(1, "failed reading/parsing %s", pledgeFile.get());
  }

  // Don't overwrite an existing session dbus address, but ensure it is set
  if (!PR_GetEnv("DBUS_SESSION_BUS_ADDRESS")) {
    PR_SetEnv("DBUS_SESSION_BUS_ADDRESS=");
  }

  return true;
}
#endif

}  // namespace mozilla

/* static */
nsIDOMProcessChild* nsIDOMProcessChild::GetSingleton() {
  if (XRE_IsContentProcess()) {
    return mozilla::dom::ContentChild::GetSingleton();
  }
  return mozilla::dom::InProcessChild::Singleton();
}
