/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "ContentParent.h"

#include <map>
#include <utility>

#include "BrowserParent.h"
#include "ContentProcessManager.h"
#include "GMPServiceParent.h"
#include "GeckoProfiler.h"
#include "Geolocation.h"
#include "GfxInfoBase.h"
#include "HandlerServiceParent.h"
#include "IHistory.h"
#include "MMPrinter.h"
#include "PreallocatedProcessManager.h"
#include "ProcessPriorityManager.h"
#include "ProfilerParent.h"
#include "SandboxHal.h"
#include "SourceSurfaceRawData.h"
#include "base/basictypes.h"
#include "chrome/common/process_watcher.h"
#include "gfxPlatform.h"
#include "gfxPlatformFontList.h"
#include "mozilla/AntiTrackingUtils.h"
#include "mozilla/AppShutdown.h"
#include "mozilla/AutoRestore.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/Casting.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/ClipboardContentAnalysisParent.h"
#include "mozilla/ClipboardReadRequestParent.h"
#include "mozilla/ClipboardWriteRequestParent.h"
#include "mozilla/CmdLineAndEnvUtils.h"
#include "mozilla/Components.h"
#include "mozilla/ContentBlockingUserInteraction.h"
#include "mozilla/DebugOnly.h"
#include "mozilla/FOGIPC.h"
#include "mozilla/GeckoArgs.h"
#include "mozilla/GeckoTrace.h"
#include "mozilla/GlobalStyleSheetCache.h"
#include "mozilla/HangDetails.h"
#include "mozilla/LookAndFeel.h"
#include "mozilla/Maybe.h"
#include "mozilla/NullPrincipal.h"
#include "mozilla/PageloadEvent.h"
#include "mozilla/Preferences.h"
#include "mozilla/PresShell.h"
#include "mozilla/ProcessHangMonitor.h"
#include "mozilla/ProcessHangMonitorIPC.h"
#include "mozilla/ProfilerLabels.h"
#include "mozilla/ProfilerMarkers.h"
#include "mozilla/RDDProcessManager.h"
#include "mozilla/RecursiveMutex.h"
#include "mozilla/RemoteLazyInputStreamParent.h"
#include "mozilla/Result.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/ScriptPreloader.h"
#include "mozilla/Services.h"
#include "mozilla/Sprintf.h"
#include "mozilla/StaticPrefs_browser.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/StaticPrefs_fission.h"
#include "mozilla/StaticPrefs_media.h"
#include "mozilla/StaticPrefs_network.h"
#include "mozilla/StaticPrefs_telemetry.h"
#include "mozilla/StaticPrefs_threads.h"
#include "mozilla/StaticPrefs_widget.h"
#include "mozilla/StorageAccessAPIHelper.h"
#include "mozilla/StyleSheet.h"
#include "mozilla/StyleSheetInlines.h"
#include "mozilla/TaskController.h"
#include "mozilla/Telemetry.h"
#include "mozilla/TelemetryComms.h"
#include "mozilla/TelemetryIPC.h"
#include "mozilla/UniquePtrExtensions.h"
#include "mozilla/WebBrowserPersistDocumentParent.h"
#include "mozilla/XREAppData.h"
#include "mozilla/devtools/HeapSnapshotTempFileHelperParent.h"
#include "mozilla/dom/BlobURLProtocolHandler.h"
#include "mozilla/dom/BrowserHost.h"
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/BrowsingContextGroup.h"
#include "mozilla/dom/CancelContentJSOptionsBinding.h"
#include "mozilla/dom/CanonicalBrowsingContext.h"
#include "mozilla/dom/ClientManager.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/DataTransfer.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/ExternalHelperAppParent.h"
#include "mozilla/dom/File.h"
#include "mozilla/dom/FileSystemSecurity.h"
#include "mozilla/dom/GeolocationBinding.h"
#include "mozilla/dom/GeolocationPositionError.h"
#include "mozilla/dom/GeolocationSystem.h"
#include "mozilla/dom/GetFilesHelper.h"
#include "mozilla/dom/IPCBlobUtils.h"
#include "mozilla/dom/JSActorService.h"
#include "mozilla/dom/JSProcessActorBinding.h"
#include "mozilla/dom/LocalStorageCommon.h"
#include "mozilla/dom/MediaController.h"
#include "mozilla/dom/MediaStatusManager.h"
#include "mozilla/dom/MemoryReportRequest.h"
#include "mozilla/dom/PContentPermissionRequestParent.h"
#include "mozilla/dom/PCycleCollectWithLogsParent.h"
#include "mozilla/dom/ParentProcessChannelHandle.h"
#include "mozilla/dom/ParentProcessMessageManager.h"
#include "mozilla/dom/PermissionObserver.h"
#include "mozilla/dom/PermissionStatusBinding.h"
#include "mozilla/dom/Permissions.h"
#include "mozilla/dom/ProcessMessageManager.h"
#include "mozilla/dom/PushNotifier.h"
#include "mozilla/dom/RemoteWorkerDebuggerManagerParent.h"
#include "mozilla/dom/RemoteWorkerServiceParent.h"
#include "mozilla/dom/ServiceWorkerManager.h"
#include "mozilla/dom/ServiceWorkerRegistrar.h"
#include "mozilla/dom/ServiceWorkerUtils.h"
#include "mozilla/dom/SessionHistoryEntry.h"
#include "mozilla/dom/SessionStorageManager.h"
#include "mozilla/dom/SharedScriptCache.h"
#include "mozilla/dom/StorageIPC.h"
#include "mozilla/dom/URLClassifierParent.h"
#include "mozilla/dom/UserActivation.h"
#include "mozilla/dom/WindowGlobalParent.h"
#include "mozilla/dom/ipc/SharedMap.h"
#include "mozilla/dom/ipc/StructuredCloneData.h"
#include "mozilla/dom/notification/NotificationUtils.h"
#include "mozilla/dom/nsMixedContentBlocker.h"
#include "mozilla/dom/power/PowerManagerService.h"
#include "mozilla/dom/quota/QuotaManagerService.h"
#include "mozilla/extensions/ExtensionsParent.h"
#include "mozilla/extensions/StreamFilterParent.h"
#include "mozilla/gfx/GPUProcessManager.h"
#include "mozilla/gfx/gfxVars.h"
#ifdef MOZ_WMF
#  include "mozilla/glean/DomMediaPlatformsWmfMetrics.h"
#endif
#include "mozilla/glean/DomMetrics.h"
#include "mozilla/glean/FOGTransportParent.h"
#include "mozilla/glean/GleanPings.h"
#include "mozilla/glean/IpcMetrics.h"
#include "mozilla/glean/PFOGTransport.h"
#include "mozilla/hal_sandbox/PHalParent.h"
#include "mozilla/intl/L10nRegistry.h"
#include "mozilla/intl/LocaleService.h"
#include "mozilla/intl/OSPreferences.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/BackgroundParent.h"
#include "mozilla/ipc/ByteBuf.h"
#include "mozilla/ipc/CrashReporterHost.h"
#include "mozilla/ipc/Endpoint.h"
#include "mozilla/ipc/FileDescriptorUtils.h"
#include "mozilla/ipc/IPCStreamUtils.h"
#include "mozilla/ipc/ProcessUtils.h"
#include "mozilla/ipc/SharedMemoryHandle.h"
#include "mozilla/ipc/TestShellParent.h"
#include "mozilla/ipc/URIUtils.h"
#include "mozilla/layers/CompositorThread.h"
#include "mozilla/layers/ImageBridgeParent.h"
#include "mozilla/layers/LayerTreeOwnerTracker.h"
#include "mozilla/layers/PAPZParent.h"
#include "mozilla/loader/ScriptCacheActors.h"
#include "mozilla/media/MediaParent.h"
#include "mozilla/mozSpellChecker.h"
#include "mozilla/net/CookieKey.h"
#include "mozilla/net/CookieServiceParent.h"
#include "mozilla/net/NeckoMessageUtils.h"
#include "mozilla/net/NeckoParent.h"
#include "mozilla/net/PCookieServiceParent.h"
#include "mozilla/net/TRRService.h"
#include "mozilla/net/UrlClassifierFeatureFactory.h"
#include "mozilla/widget/RemoteLookAndFeel.h"
#include "mozilla/widget/ScreenManager.h"
#include "mozilla/widget/TextRecognition.h"
#include "nsAnonymousTemporaryFile.h"
#include "nsAppRunner.h"
#include "nsCExternalHandlerService.h"
#include "nsCOMPtr.h"
#include "nsCRT.h"
#include "nsChromeRegistryChrome.h"
#include "nsComponentManagerUtils.h"
#include "nsConsoleMessage.h"
#include "nsConsoleService.h"
#include "nsContentPermissionHelper.h"
#include "nsContentUtils.h"
#include "nsDNSService2.h"
#include "nsDebugImpl.h"
#include "nsDirectoryServiceDefs.h"
#include "nsDocShell.h"
#include "nsEmbedCID.h"
#include "nsFocusManager.h"
#include "nsFrameLoader.h"
#include "nsFrameMessageManager.h"
#include "nsGlobalWindowOuter.h"
#include "nsHashPropertyBag.h"
#include "nsHyphenationManager.h"
#include "nsIAppShell.h"
#include "nsIAppWindow.h"
#include "nsIAsyncInputStream.h"
#include "nsIBidiKeyboard.h"
#include "nsIBrowserDOMWindow.h"
#include "nsICaptivePortalService.h"
#include "nsIClipboard.h"
#include "nsIContentAnalysis.h"
#include "nsIContentSecurityPolicy.h"
#include "nsICookie.h"
#include "nsICookieNotification.h"
#include "nsICrashService.h"
#include "nsICycleCollectorListener.h"
#include "nsIDocShell.h"
#include "nsIDocShellTreeOwner.h"
#include "nsIDragService.h"
#include "nsIExternalProtocolService.h"
#include "nsIGfxInfo.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsILocalStorageManager.h"
#include "nsIMemoryInfoDumper.h"
#include "nsIMemoryReporter.h"
#include "nsINetworkLinkService.h"
#include "nsIObserverService.h"
#include "nsIParentChannel.h"
#include "nsIPrivateAttributionService.h"
#include "nsIScriptError.h"
#include "nsIScriptSecurityManager.h"
#include "nsIServiceWorkerManager.h"
#include "nsISiteSecurityService.h"
#include "nsIStringBundle.h"
#include "nsITimer.h"
#include "nsIURL.h"
#include "nsIUserIdleService.h"
#include "nsIWebBrowserChrome.h"
#include "nsIXULRuntime.h"
#include "nsPIDNSService.h"
#if defined(MOZ_WIDGET_GTK) || defined(XP_WIN)
#  include "nsIconChannel.h"
#endif
#include "XPCSelfHostedShmem.h"
#include "nsFrameLoaderOwner.h"
#include "nsMemoryInfoDumper.h"
#include "nsMemoryReporterManager.h"
#include "nsOpenURIInFrameParams.h"
#include "nsOpenWindowInfo.h"
#include "nsPIWindowWatcher.h"
#include "nsQueryObject.h"
#include "nsReadableUtils.h"
#include "nsSHistory.h"
#include "nsScriptError.h"
#include "nsServiceManagerUtils.h"
#include "nsStreamUtils.h"
#include "nsStyleSheetService.h"
#include "nsThread.h"
#include "nsThreadUtils.h"
#include "nsURLHelper.h"
#include "nsWidgetsCID.h"
#include "nsWindowWatcher.h"
#include "prenv.h"
#include "prio.h"
#include "private/pprio.h"
#include "xpcpublic.h"

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

#if defined(XP_MACOSX)
#  include "mozilla/AvailableMemoryWatcher.h"
#  include "nsMacUtilsImpl.h"
#endif

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

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

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

#include "mozilla/PermissionManager.h"

#ifdef MOZ_WIDGET_ANDROID
#  include "AndroidBridge.h"
#  include "mozilla/java/GeckoProcessManagerWrappers.h"
#  include "mozilla/java/GeckoProcessTypeWrappers.h"
#endif

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

#  include "mozilla/WidgetUtilsGtk.h"
#endif

#include "Crypto.h"
#include "mozilla/RemoteSpellCheckEngineParent.h"

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

#if defined(MOZ_SANDBOX)
#  include "mozilla/SandboxSettings.h"
#  if defined(XP_LINUX)
#    include "mozilla/SandboxBroker.h"
#    include "mozilla/SandboxBrokerPolicyFactory.h"
#    include "mozilla/SandboxInfo.h"
#  endif
#  if defined(XP_MACOSX)
#    include "mozilla/Sandbox.h"
#  endif
#endif

#ifdef XP_WIN
#  include "mozilla/WinDllServices.h"
#endif

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

#ifdef FUZZING_SNAPSHOT
#  include "mozilla/fuzzing/IPCFuzzController.h"
#endif

#ifdef ENABLE_WEBDRIVER
#  include "nsIMarionette.h"
#  include "nsIRemoteAgent.h"
#endif

#include "mozilla/RemoteDecodeUtils.h"
#include "nsIToolkitProfile.h"
#include "nsIToolkitProfileService.h"

#ifdef MOZ_WMF_CDM
#  include "mozilla/EMEUtils.h"
#  include "nsIWindowsMediaFoundationCDMOriginsListService.h"

namespace mozilla {
class OriginsListLoadCallback final : public nsIOriginsListLoadCallback {
 public:
  explicit OriginsListLoadCallback(ContentParent* aContentParent)
      : mContentParent(aContentParent) {
    MOZ_ASSERT(mContentParent);
  }

  NS_DECL_ISUPPORTS

  // nsIOriginsListLoadCallback
  NS_IMETHODIMP OnOriginsListLoaded(nsIArray* aEntries) {
    if (NS_WARN_IF(!mContentParent)) {
      return NS_ERROR_FAILURE;
    }

    uint32_t length = 0;
    nsresult rv = aEntries->GetLength(&length);
    if (NS_FAILED(rv)) {
      return rv;
    }

    nsTArray<dom::IPCOriginStatusEntry> ipcEntries;
    for (uint32_t i = 0; i < length; ++i) {
      nsCOMPtr<nsIOriginStatusEntry> entry;
      aEntries->QueryElementAt(i, NS_GET_IID(nsIOriginStatusEntry),
                               getter_AddRefs(entry));
      if (!entry) {
        NS_WARNING("OriginsListLoadCallback, skip bad entry?");
        continue;
      }
      nsAutoCString origin;
      int32_t status = 0;
      entry->GetOrigin(origin);
      entry->GetStatus(&status);
      dom::IPCOriginStatusEntry ipcEntry(origin, status);
      ipcEntries.AppendElement(ipcEntry);
    }
    (void)mContentParent->SendUpdateMFCDMOriginEntries(ipcEntries);
    return NS_OK;
  }

 private:
  ~OriginsListLoadCallback() = default;

  RefPtr<ContentParent> mContentParent;
};
NS_IMPL_ISUPPORTS(OriginsListLoadCallback, nsIOriginsListLoadCallback)
}  // namespace mozilla
#endif

static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);

using base::KillProcess;

using namespace CrashReporter;
using namespace mozilla::dom::power;
using namespace mozilla::media;
using namespace mozilla::embedding;
using namespace mozilla::gfx;
using namespace mozilla::gmp;
using namespace mozilla::hal;
using namespace mozilla::ipc;
using namespace mozilla::intl;
using namespace mozilla::layers;
using namespace mozilla::layout;
using namespace mozilla::net;
using namespace mozilla::psm;
using namespace mozilla::widget;
using namespace mozilla::Telemetry;
using mozilla::loader::PScriptCacheParent;
using mozilla::Telemetry::ProcessID;

extern mozilla::LazyLogModule gFocusLog;

#define LOGFOCUS(args) MOZ_LOG(gFocusLog, mozilla::LogLevel::Debug, args)

extern mozilla::LazyLogModule sPDMLog;
#define LOGPDM(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))

namespace mozilla {
namespace CubebUtils {
extern FileDescriptor CreateAudioIPCConnection(uint32_t aRemotePid);
}

namespace dom {

LazyLogModule gProcessLog("Process");

MOZ_RUNINIT static std::map<RemoteMediaIn, media::MediaCodecsSupported>
    sCodecsSupported;

/* static */
uint32_t ContentParent::sMaxContentProcesses = 0;

/* static */
LogModule* ContentParent::GetLog() { return gProcessLog; }

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

// IPC receiver for remote GC/CC logging.
class CycleCollectWithLogsParent final : public PCycleCollectWithLogsParent {
 public:
  MOZ_COUNTED_DTOR(CycleCollectWithLogsParent)

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

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

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

 private:
  virtual mozilla::ipc::IPCResult RecvCloseGCLog() override {
    (void)mSink->CloseGCLog();
    return IPC_OK();
  }

  virtual mozilla::ipc::IPCResult RecvCloseCCLog() override {
    (void)mSink->CloseCCLog();
    return IPC_OK();
  }

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

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

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

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

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

 public:
  NS_DECL_ISUPPORTS
  NS_DECL_NSIMEMORYREPORTER
};

NS_IMPL_ISUPPORTS(ContentParentsMemoryReporter, nsIMemoryReporter)

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

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

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

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

    const char* channelStr = "no channel";
    uint32_t numQueuedMessages = 0;
    if (channel) {
      if (channel->IsClosed()) {
        channelStr = "closed channel";
      } else {
        channelStr = "open channel";
      }
      numQueuedMessages =
          0;  // XXX was channel->Unsound_NumQueuedMessages(); Bug 1754876
    }

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

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

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

  return NS_OK;
}

// A hashtable (by type) of processes/ContentParents.  This includes
// processes that are in the Preallocator cache (which would be type
// 'prealloc'), and recycled processes ('web' and in the future
// eTLD+1-locked) processes).
nsClassHashtable<nsCStringHashKey, nsTArray<ContentParent*>>*
    ContentParent::sBrowserContentParents;

namespace {

uint64_t ComputeLoadedOriginHash(nsIPrincipal* aPrincipal) {
  uint32_t originNoSuffix =
      BasePrincipal::Cast(aPrincipal)->GetOriginNoSuffixHash();
  uint32_t originSuffix =
      BasePrincipal::Cast(aPrincipal)->GetOriginSuffixHash();

  return ((uint64_t)originNoSuffix) << 32 | originSuffix;
}

ProcessID GetTelemetryProcessID(const nsACString& remoteType) {
  // OOP WebExtensions run in a content process.
  // For Telemetry though we want to break out collected data from the
  // WebExtensions process into a separate bucket, to make sure we can analyze
  // it separately and avoid skewing normal content process metrics.
  return remoteType == EXTENSION_REMOTE_TYPE ? ProcessID::Extension
                                             : ProcessID::Content;
}

}  // anonymous namespace

StaticAutoPtr<LinkedList<ContentParent>> ContentParent::sContentParents;
#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
StaticAutoPtr<SandboxBrokerPolicyFactory>
    ContentParent::sSandboxBrokerPolicyFactory;
#endif
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
StaticAutoPtr<std::vector<std::string>> ContentParent::sMacSandboxParams;
#endif

// Set to true when the first content process gets created.
static bool sCreatedFirstContentProcess = false;

#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
// True when we're running the process selection code, and do not expect to
// enter code paths where processes may die.
static bool sInProcessSelector = false;
#endif

static const char* sObserverTopics[] = {
    NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC,
    NS_IPC_IOSERVICE_SET_CONNECTIVITY_TOPIC,
    NS_IPC_CAPTIVE_PORTAL_SET_STATE,
    "application-background",
    "application-foreground",
    "memory-pressure",
    "memory-pressure-stop",
    "child-gc-request",
    "child-cc-request",
    "child-mmu-request",
    "child-ghost-request",
    "last-pb-context-exited",
    "file-watcher-update",
#ifdef ACCESSIBILITY
    "a11y-init-or-shutdown",
#endif
    "cacheservice:empty-cache",
    "intl:app-locales-changed",
    "intl:requested-locales-changed",
    "cookie-changed",
    "private-cookie-changed",
    NS_NETWORK_LINK_TYPE_TOPIC,
    NS_NETWORK_TRR_MODE_CHANGED_TOPIC,
    "network:socket-process-crashed",
    DEFAULT_TIMEZONE_CHANGED_OBSERVER_TOPIC,
};

void ContentParent_NotifyUpdatedDictionaries() {
  ContentParent::NotifyUpdatedDictionaries();
}

// PreallocateProcess is called by the PreallocatedProcessManager.
// ContentParent then takes this process back within GetNewOrUsedBrowserProcess.
/*static*/ UniqueContentParentKeepAlive ContentParent::MakePreallocProcess() {
  RefPtr<ContentParent> process = new ContentParent(PREALLOC_REMOTE_TYPE);
  if (NS_WARN_IF(!process->BeginSubprocessLaunch(PROCESS_PRIORITY_PREALLOC))) {
    process->LaunchSubprocessReject();
    return nullptr;
  }
  return process->AddKeepAlive(/* aBrowserId */ 0);
}

/*static*/
void ContentParent::StartUp() {
  // FIXME Bug 1023701 - Stop using ContentParent static methods in
  // child process
  if (!XRE_IsParentProcess()) {
    return;
  }

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

  // Note: This reporter measures all ContentParents.
  RegisterStrongMemoryReporter(MakeAndAddRef<ContentParentsMemoryReporter>());

  BackgroundChild::Startup();
  ClientManager::Startup();

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

#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
  sSandboxBrokerPolicyFactory = new SandboxBrokerPolicyFactory();
#endif

#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
  sMacSandboxParams = new std::vector<std::string>();
#endif
}

/*static*/
void ContentParent::ShutDown() {
  // For the most, we rely on normal process shutdown and
  // ClearOnShutdown() to clean up our state.

#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
  sSandboxBrokerPolicyFactory = nullptr;
#endif

#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
  sMacSandboxParams = nullptr;
#endif
}

/*static*/
uint32_t ContentParent::GetPoolSize(const nsACString& aContentProcessType) {
  if (!sBrowserContentParents) {
    return 0;
  }

  nsTArray<ContentParent*>* parents =
      sBrowserContentParents->Get(aContentProcessType);

  return parents ? parents->Length() : 0;
}

/*static*/ nsTArray<ContentParent*>& ContentParent::GetOrCreatePool(
    const nsACString& aContentProcessType) {
  if (!sBrowserContentParents) {
    sBrowserContentParents =
        new nsClassHashtable<nsCStringHashKey, nsTArray<ContentParent*>>;
  }

  return *sBrowserContentParents->GetOrInsertNew(aContentProcessType);
}

nsDependentCSubstring RemoteTypePrefix(const nsACString& aContentProcessType) {
  // The suffix after a `=` in a remoteType is dynamic, and used to control the
  // process pool to use.
  int32_t equalIdx = aContentProcessType.FindChar(L'=');
  if (equalIdx == kNotFound) {
    equalIdx = aContentProcessType.Length();
  }
  return StringHead(aContentProcessType, equalIdx);
}

static bool IsRemoteTypeJitDisabled(const nsACString& aContentProcessType) {
  if (!StringEndsWith(aContentProcessType, DISABLE_JIT_REMOTE_TYPE_SUFFIX)) {
    return false;
  }

  auto remoteTypePrefix = RemoteTypePrefix(aContentProcessType);
  if (remoteTypePrefix != FISSION_WEB_REMOTE_TYPE &&
      remoteTypePrefix != SERVICEWORKER_REMOTE_TYPE &&
      remoteTypePrefix != WITH_COOP_COEP_REMOTE_TYPE) {
    return false;
  }

  auto suffixStart =
      aContentProcessType.Length() - DISABLE_JIT_REMOTE_TYPE_SUFFIX.Length();
  if (suffixStart > 0) {
    char priorChar = aContentProcessType[suffixStart - 1];
    if (priorChar != '&' && priorChar != '^') {
      return false;
    }
  } else {
    return false;
  }

  return true;
}

bool IsWebRemoteType(const nsACString& aContentProcessType) {
  // Note: matches webIsolated, web, and webCOOP+COEP types.
  return StringBeginsWith(aContentProcessType, DEFAULT_REMOTE_TYPE);
}

bool IsWebCoopCoepRemoteType(const nsACString& aContentProcessType) {
  return StringBeginsWith(aContentProcessType,
                          WITH_COOP_COEP_REMOTE_TYPE_PREFIX);
}

bool IsExtensionRemoteType(const nsACString& aContentProcessType) {
  return aContentProcessType == EXTENSION_REMOTE_TYPE;
}

/*static*/
uint32_t ContentParent::GetMaxProcessCount(
    const nsACString& aContentProcessType) {
  // Max process count is based only on the prefix.
  const nsDependentCSubstring processTypePrefix =
      RemoteTypePrefix(aContentProcessType);

  // Check for the default remote type of "web", as it uses different prefs.
  if (processTypePrefix == DEFAULT_REMOTE_TYPE) {
    return GetMaxWebProcessCount();
  }

  // Read the pref controling this remote type. `dom.ipc.processCount` is not
  // used as a fallback, as it is intended to control the number of "web"
  // content processes, checked in `mozilla::GetMaxWebProcessCount()`.
  nsAutoCString processCountPref("dom.ipc.processCount.");
  processCountPref.Append(processTypePrefix);

  int32_t maxContentParents = Preferences::GetInt(processCountPref.get(), 1);
  if (maxContentParents < 1) {
    maxContentParents = 1;
  }

  return static_cast<uint32_t>(maxContentParents);
}

/*static*/
bool ContentParent::IsMaxProcessCountReached(
    const nsACString& aContentProcessType) {
  return GetPoolSize(aContentProcessType) >=
         GetMaxProcessCount(aContentProcessType);
}

// Really more ReleaseUnneededProcesses()
/*static*/
void ContentParent::ReleaseCachedProcesses() {
  MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
          ("ReleaseCachedProcesses:"));
  if (!sBrowserContentParents) {
    return;
  }

#ifdef DEBUG
  for (const auto& cps : *sBrowserContentParents) {
    MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
            ("%s: %zu processes", PromiseFlatCString(cps.GetKey()).get(),
             cps.GetData()->Length()));
  }
#endif

  // First let's collect all processes and keep a grip.
  AutoTArray<RefPtr<ContentParent>, 32> fixArray;
  for (const auto& contentParents : sBrowserContentParents->Values()) {
    for (auto* cp : *contentParents) {
      fixArray.AppendElement(cp);
    }
  }

  for (const auto& cp : fixArray) {
    cp->MaybeBeginShutDown(/* aImmediate */ true,
                           /* aIgnoreKeepAlivePref */ true);
    if (cp->IsDead()) {
      // Make sure that this process is no longer accessible from JS by its
      // message manager.
      cp->ShutDownMessageManager();
    }
  }
}

/*static*/
already_AddRefed<ContentParent> ContentParent::MinTabSelect(
    const nsTArray<ContentParent*>& aContentParents, int32_t aMaxContentParents,
    uint64_t aBrowserId) {
  uint32_t maxSelectable =
      std::min(static_cast<uint32_t>(aContentParents.Length()),
               static_cast<uint32_t>(aMaxContentParents));
  uint32_t min = INT_MAX;
  RefPtr<ContentParent> candidate;

  for (uint32_t i = 0; i < maxSelectable; i++) {
    ContentParent* p = aContentParents[i];
    MOZ_DIAGNOSTIC_ASSERT(!p->IsDead());
    if (p->IsShuttingDown()) {
      continue;
    }

    // Check how many other tabs are already hosted by this process.  Ignore
    // keepalives without a BrowserId as well as keepalives corresponding to
    // `aBrowserId` when doing this calculation.
    ThreadsafeContentParentHandle* handle = p->ThreadsafeHandle();
    RecursiveMutexAutoLock lock(handle->mMutex);
    uint32_t keepAliveCount = handle->mKeepAlivesPerBrowserId.Count();
    if (handle->mKeepAlivesPerBrowserId.Contains(0)) {
      --keepAliveCount;
    }
    if (aBrowserId != 0 &&
        handle->mKeepAlivesPerBrowserId.Contains(aBrowserId)) {
      --keepAliveCount;
    }

    if (keepAliveCount < min) {
      candidate = p;
      min = keepAliveCount;
    }
  }

  // If all current processes have at least one tab and we have not yet reached
  // the maximum, use a new process.
  if (min > 0 &&
      aContentParents.Length() < static_cast<uint32_t>(aMaxContentParents)) {
    return nullptr;
  }

  // Otherwise we return candidate.
  return candidate.forget();
}

/* static */
already_AddRefed<nsIPrincipal>
ContentParent::CreateRemoteTypeIsolationPrincipal(
    const nsACString& aRemoteType) {
  if ((RemoteTypePrefix(aRemoteType) != FISSION_WEB_REMOTE_TYPE) &&
      !StringBeginsWith(aRemoteType, WITH_COOP_COEP_REMOTE_TYPE_PREFIX)) {
    return nullptr;
  }

  int32_t offset = aRemoteType.FindChar('=') + 1;
  MOZ_ASSERT(offset > 1, "can not extract origin from that remote type");
  nsAutoCString origin(
      Substring(aRemoteType, offset, aRemoteType.Length() - offset));

  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
  nsCOMPtr<nsIPrincipal> principal;
  ssm->CreateContentPrincipalFromOrigin(origin, getter_AddRefs(principal));
  return principal.forget();
}

/*static*/
UniqueContentParentKeepAlive ContentParent::GetUsedBrowserProcess(
    const nsACString& aRemoteType, nsTArray<ContentParent*>& aContentParents,
    uint32_t aMaxContentParents, bool aPreferUsed, ProcessPriority aPriority,
    uint64_t aBrowserId) {
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
  AutoRestore ar(sInProcessSelector);
  sInProcessSelector = true;
#endif

  uint32_t numberOfParents = aContentParents.Length();
  if (aPreferUsed && numberOfParents) {
    // If we prefer re-using existing content processes, we don't want to create
    // a new process, and instead re-use an existing one, so pretend the process
    // limit is at the current number of processes.
    aMaxContentParents = numberOfParents;
  }

  // Use MinTabSelect to choose a content process unless content process re-use
  // has been disabled.
  RefPtr<ContentParent> selected;
  if (!StaticPrefs::dom_ipc_disableContentProcessReuse() &&
      (selected =
           MinTabSelect(aContentParents, aMaxContentParents, aBrowserId))) {
    if (profiler_thread_is_being_profiled_for_markers()) {
      nsPrintfCString marker("Reused process %u",
                             (unsigned int)selected->ChildID());
      PROFILER_MARKER_TEXT("Process", DOM, {}, marker);
    }
    MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
            ("GetUsedProcess: Reused process id=%p childID=%" PRIu64 " for %s",
             selected.get(), (uint64_t)selected->ChildID(),
             PromiseFlatCString(aRemoteType).get()));
    selected->AssertAlive();
    return selected->AddKeepAlive(aBrowserId);
  }

  // Try to take a preallocated process except for certain remote types.
  // Note: this process may not have finished launching yet
  UniqueContentParentKeepAlive preallocated;
  if (aRemoteType != FILE_REMOTE_TYPE &&
      aRemoteType != PRIVILEGEDABOUT_REMOTE_TYPE &&
      aRemoteType != EXTENSION_REMOTE_TYPE &&  // Bug 1638119
      !IsRemoteTypeJitDisabled(aRemoteType) &&
      (preallocated = PreallocatedProcessManager::Take(aRemoteType))) {
    MOZ_DIAGNOSTIC_ASSERT(preallocated->GetRemoteType() ==
                          PREALLOC_REMOTE_TYPE);
    preallocated->AssertAlive();

    if (profiler_thread_is_being_profiled_for_markers()) {
      nsPrintfCString marker(
          "Assigned preallocated process %u%s",
          (unsigned int)preallocated->ChildID(),
          preallocated->IsLaunching() ? " (still launching)" : "");
      PROFILER_MARKER_TEXT("Process", DOM, {}, marker);
    }
    MOZ_LOG(
        ContentParent::GetLog(), LogLevel::Debug,
        ("Adopted preallocated process id=%p childID=%" PRIu64 " for type %s%s",
         preallocated.get(), (uint64_t)preallocated->ChildID(),
         PromiseFlatCString(aRemoteType).get(),
         preallocated->IsLaunching() ? " (still launching)" : ""));

    // This ensures that the preallocator won't shut down the process once
    // it finishes starting
    preallocated->mRemoteType.Assign(aRemoteType);
    {
      RecursiveMutexAutoLock lock(preallocated->mThreadsafeHandle->mMutex);
      preallocated->mThreadsafeHandle->mRemoteType = preallocated->mRemoteType;
    }
    preallocated->mRemoteTypeIsolationPrincipal =
        CreateRemoteTypeIsolationPrincipal(aRemoteType);
    preallocated->AddToPool(aContentParents);

    // rare, but will happen
    if (!preallocated->IsLaunching()) {
      // Specialize this process for the appropriate remote type, and activate
      // it.

      (void)preallocated->SendRemoteType(preallocated->mRemoteType,
                                         preallocated->mProfile);

      preallocated->StartRemoteWorkerService();

      nsCOMPtr<nsIObserverService> obs =
          mozilla::services::GetObserverService();
      if (obs) {
        nsAutoString cpId;
        cpId.AppendInt(static_cast<uint64_t>(preallocated->ChildID()));
        obs->NotifyObservers(static_cast<nsIObserver*>(preallocated.get()),
                             "process-type-set", cpId.get());
        preallocated->AssertAlive();
      }
    }
    // NOTE: Make sure to return a keepalive for the requested aBrowserId. The
    // keepalive used by the preallocated process manager will be released upon
    // returning.
    return preallocated->AddKeepAlive(aBrowserId);
  }

  return nullptr;
}

/*static*/
UniqueContentParentKeepAlive ContentParent::GetNewOrUsedLaunchingBrowserProcess(
    const nsACString& aRemoteType, BrowsingContextGroup* aGroup,
    ProcessPriority aPriority, bool aPreferUsed, uint64_t aBrowserId) {
  MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
          ("GetNewOrUsedProcess for type %s",
           PromiseFlatCString(aRemoteType).get()));

  if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
    return nullptr;
  }

  // If we have an existing host process attached to this BrowsingContextGroup,
  // always return it, as we can never have multiple host processes within a
  // single BrowsingContextGroup.
  UniqueContentParentKeepAlive contentParent;
  if (aGroup) {
    if (RefPtr<ContentParent> candidate = aGroup->GetHostProcess(aRemoteType)) {
      MOZ_DIAGNOSTIC_ASSERT(!candidate->IsShuttingDown());
      MOZ_LOG(
          ContentParent::GetLog(), LogLevel::Debug,
          ("GetNewOrUsedProcess: Existing host process id=%p childID=%" PRIu64
           " (launching %d)",
           candidate.get(), (uint64_t)candidate->ChildID(),
           candidate->IsLaunching()));
      contentParent = candidate->TryAddKeepAlive(aBrowserId);
    }
  }

  nsTArray<ContentParent*>& contentParents = GetOrCreatePool(aRemoteType);

  if (!contentParent) {
    // No host process. Let's try to re-use an existing process.
    uint32_t maxContentParents = GetMaxProcessCount(aRemoteType);

    contentParent =
        GetUsedBrowserProcess(aRemoteType, contentParents, maxContentParents,
                              aPreferUsed, aPriority, aBrowserId);
    MOZ_DIAGNOSTIC_ASSERT_IF(contentParent, !contentParent->IsShuttingDown());
  }

  if (!contentParent) {
    // No reusable process. Let's create and launch one.
    // The life cycle will be set to `LifecycleState::LAUNCHING`.
    MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
            ("Launching new process immediately for type %s",
             PromiseFlatCString(aRemoteType).get()));

    RefPtr<ContentParent> newCp = new ContentParent(aRemoteType);
    if (NS_WARN_IF(!newCp->BeginSubprocessLaunch(aPriority))) {
      // Launch aborted because of shutdown. Bailout.
      newCp->LaunchSubprocessReject();
      return nullptr;
    }
    contentParent = newCp->AddKeepAlive(aBrowserId);

    // Until the new process is ready let's not allow to start up any
    // preallocated processes. The blocker will be removed once we receive
    // the first idle message.
    contentParent->mIsAPreallocBlocker = true;
    PreallocatedProcessManager::AddBlocker(aRemoteType, contentParent.get());

    // Store this process for future reuse.
    contentParent->AddToPool(contentParents);

    MOZ_LOG(
        ContentParent::GetLog(), LogLevel::Debug,
        ("GetNewOrUsedProcess: new immediate process id=%p childID=%" PRIu64,
         contentParent.get(), (uint64_t)contentParent->ChildID()));
  }
  // else we have an existing or preallocated process (which may be
  // still launching)

  contentParent->AssertAlive();
  if (aGroup) {
    aGroup->EnsureHostProcess(contentParent.get());
  }
  return contentParent;
}

/*static*/
RefPtr<ContentParent::LaunchPromise>
ContentParent::GetNewOrUsedBrowserProcessAsync(const nsACString& aRemoteType,
                                               BrowsingContextGroup* aGroup,
                                               ProcessPriority aPriority,
                                               bool aPreferUsed,
                                               uint64_t aBrowserId) {
  // Obtain a `ContentParent` launched asynchronously.
  UniqueContentParentKeepAlive contentParent =
      GetNewOrUsedLaunchingBrowserProcess(aRemoteType, aGroup, aPriority,
                                          aPreferUsed, aBrowserId);
  if (!contentParent) {
    // In case of launch error, stop here.
    return LaunchPromise::CreateAndReject(NS_ERROR_ILLEGAL_DURING_SHUTDOWN,
                                          __func__);
  }
  return contentParent->WaitForLaunchAsync(aPriority, aBrowserId);
}

/*static*/
UniqueContentParentKeepAlive ContentParent::GetNewOrUsedBrowserProcess(
    const nsACString& aRemoteType, BrowsingContextGroup* aGroup,
    ProcessPriority aPriority, bool aPreferUsed, uint64_t aBrowserId) {
  UniqueContentParentKeepAlive contentParent =
      GetNewOrUsedLaunchingBrowserProcess(aRemoteType, aGroup, aPriority,
                                          aPreferUsed, aBrowserId);
  if (!contentParent || !contentParent->WaitForLaunchSync(aPriority)) {
    // In case of launch error, stop here.
    return nullptr;
  }
  return contentParent;
}

RefPtr<ContentParent::LaunchPromise> ContentParent::WaitForLaunchAsync(
    ProcessPriority aPriority, uint64_t aBrowserId) {
  MOZ_DIAGNOSTIC_ASSERT(!IsDead());
  UniqueContentParentKeepAlive self = AddKeepAlive(aBrowserId);

  if (!IsLaunching()) {
    MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
            ("WaitForLaunchAsync: launched"));
    return LaunchPromise::CreateAndResolve(std::move(self), __func__);
  }

  // We've started an async content process launch.
  glean::dom_contentprocess::launch_is_sync
      .EnumGet(glean::dom_contentprocess::LaunchIsSyncLabel::eFalse)
      .Add();

  // We have located a process that hasn't finished initializing, then attempt
  // to finish initializing. Both `LaunchSubprocessResolve` and
  // `LaunchSubprocessReject` are safe to call multiple times if we race with
  // other `WaitForLaunchAsync` callbacks.
  return mSubprocess->WhenProcessHandleReady()->Then(
      GetCurrentSerialEventTarget(), __func__,
      [self = std::move(self), aPriority](
          const ProcessHandlePromise::ResolveOrRejectValue& aValue) mutable {
        if (aValue.IsResolve() &&
            self->LaunchSubprocessResolve(/* aIsSync = */ false, aPriority)) {
          MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
                  ("WaitForLaunchAsync: async, now launched, process id=%p, "
                   "childID=%" PRIu64,
                   self.get(), (uint64_t)self->ChildID()));
          return LaunchPromise::CreateAndResolve(std::move(self), __func__);
        }

        MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
                ("WaitForLaunchAsync: async, rejected"));
        self->LaunchSubprocessReject();
        return LaunchPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
      });
}

bool ContentParent::WaitForLaunchSync(ProcessPriority aPriority) {
  MOZ_DIAGNOSTIC_ASSERT(!IsDead());
  if (!IsLaunching()) {
    return true;
  }

  // We've started a sync content process launch.
  glean::dom_contentprocess::launch_is_sync
      .EnumGet(glean::dom_contentprocess::LaunchIsSyncLabel::eTrue)
      .Add();

  // We're a process which hasn't finished initializing. We may be racing
  // against whoever launched it (and whoever else is already racing). Since
  // we're sync, we win the race and finish the initialization.
  bool launchSuccess = mSubprocess->WaitForProcessHandle();
  if (launchSuccess &&
      LaunchSubprocessResolve(/* aIsSync = */ true, aPriority)) {
    return true;
  }
  // In case of failure.
  LaunchSubprocessReject();
  return false;
}

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

  return docShell;
}

mozilla::ipc::IPCResult ContentParent::RecvCreateClipboardContentAnalysis(
    Endpoint<PClipboardContentAnalysisParent>&& aParentEndpoint) {
  if (mClipboardContentAnalysisCreated) {
    return IPC_FAIL(this, "ClipboardContentAnalysisParent already created");
  }
  mClipboardContentAnalysisCreated = true;

  if (!mClipboardContentAnalysisThread) {
    nsresult rv = NS_NewNamedThread(
        "BkgrndClipboard", getter_AddRefs(mClipboardContentAnalysisThread));
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return IPC_FAIL(this, "NS_NewNamedThread failed");
    }
  }

  // Bind the new endpoint to the backgroundClipboardContentAnalysis thread.
  mClipboardContentAnalysisThread->Dispatch(
      NS_NewRunnableFunction(
          "Create ClipboardContentAnalysisParent",
          [threadsafeHandle = RefPtr{ThreadsafeHandle()},
           parentEndpoint = std::move(aParentEndpoint)]() mutable {
            // Use a threadsafe handle here, so that it can
            // be used to validate that the WindowContext comes from the
            // correct content process.
            RefPtr<ClipboardContentAnalysisParent> actor =
                new ClipboardContentAnalysisParent(std::move(threadsafeHandle));
            parentEndpoint.Bind(actor);
          }),
      NS_DISPATCH_NORMAL);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvCreateGMPService() {
  Endpoint<PGMPServiceParent> parent;
  Endpoint<PGMPServiceChild> child;

  if (mGMPCreated) {
    return IPC_FAIL(this, "GMP Service already created");
  }

  nsresult rv;
  rv = PGMPService::CreateEndpoints(EndpointProcInfo::Current(),
                                    OtherEndpointProcInfo(), &parent, &child);
  if (NS_FAILED(rv)) {
    return IPC_FAIL(this, "CreateEndpoints failed");
  }

  if (!GMPServiceParent::Create(std::move(parent))) {
    return IPC_FAIL(this, "GMPServiceParent::Create failed");
  }

  if (!SendInitGMPService(std::move(child))) {
    return IPC_FAIL(this, "SendInitGMPService failed");
  }

  mGMPCreated = true;

  return IPC_OK();
}

IPCResult ContentParent::RecvAttributionEvent(
    const nsACString& aHost, PrivateAttributionImpressionType aType,
    uint32_t aIndex, const nsAString& aAd, const nsACString& aTargetHost) {
  nsCOMPtr<nsIPrivateAttributionService> pa =
      components::PrivateAttribution::Service();
  if (NS_WARN_IF(!pa)) {
    return IPC_OK();
  }
  pa->OnAttributionEvent(aHost, GetEnumString(aType), aIndex, aAd, aTargetHost);
  return IPC_OK();
}

IPCResult ContentParent::RecvAttributionConversion(
    const nsACString& aHost, const nsAString& aTask, uint32_t aHistogramSize,
    const Maybe<uint32_t>& aLookbackDays,
    const Maybe<PrivateAttributionImpressionType>& aImpressionType,
    const nsTArray<nsString>& aAds, const nsTArray<nsCString>& aSourceHosts) {
  nsCOMPtr<nsIPrivateAttributionService> pa =
      components::PrivateAttribution::Service();
  if (NS_WARN_IF(!pa)) {
    return IPC_OK();
  }
  pa->OnAttributionConversion(
      aHost, aTask, aHistogramSize, aLookbackDays.valueOr(0),
      aImpressionType ? GetEnumString(*aImpressionType) : EmptyCString(), aAds,
      aSourceHosts);
  return IPC_OK();
}

static mozilla::glean::security::FissionPrincipalsExtra
GetAndLogPrincipalValidationFailure(nsIPrincipal* aPrincipal,
                                    const char* aMethod) {
  mozilla::glean::security::FissionPrincipalsExtra extra = {};
  extra.value = Some(aMethod);

  if (!aPrincipal) {
    extra.principaltype = Some("NullPtr"_ns);
  } else if (aPrincipal->IsSystemPrincipal()) {
    extra.principaltype = Some("SystemPrincipal"_ns);
  } else if (aPrincipal->GetIsExpandedPrincipal()) {
    extra.principaltype = Some("ExpandedPrincipal"_ns);
  } else if (aPrincipal->GetIsContentPrincipal()) {
    extra.principaltype = Some("ContentPrincipal"_ns);

    // NOTE: Pull the scheme from the origin, rather than from our URI.
    // This can be relevant in cases where the URI is unwrapped to determine the
    // origin (e.g. view-source).
    nsAutoCString scheme;
    nsAutoCString origin;
    MOZ_ALWAYS_SUCCEEDS(aPrincipal->GetOriginNoSuffix(origin));
    MOZ_ALWAYS_SUCCEEDS(net_ExtractURLScheme(origin, scheme));
    extra.scheme = Some(scheme);
  } else {
    extra.principaltype = Some("Unknown"_ns);
  }

  // NOTE: This log can contain PII (the full origin) as it's local-only.
  if (MOZ_LOG_TEST(ContentParent::GetLog(), LogLevel::Error)) {
    nsAutoCString origin;
    if (aPrincipal) {
      MOZ_ALWAYS_SUCCEEDS(aPrincipal->GetOrigin(origin));
    }
    MOZ_LOG(ContentParent::GetLog(), LogLevel::Error,
            ("  Receiving unexpected Principal (%s) within %s", origin.get(),
             aMethod));
  }

  return extra;
}

/*static*/
void ContentParent::LogAndAssertFailedPrincipalValidationInfo(
    nsIPrincipal* aPrincipal, const char* aMethod) {
  auto gleanExtra = GetAndLogPrincipalValidationFailure(aPrincipal, aMethod);
  // Do not send telemetry when chrome-debugging is enabled
  bool isChromeDebuggingEnabled =
      Preferences::GetBool("devtools.chrome.enabled", false);
  if (!isChromeDebuggingEnabled) {
    glean::security::fission_principals.Record(mozilla::Some(gleanExtra));
  }

#ifdef DEBUG
  // Not only log but also ensure we do not receive an unexpected
  // principal when running in debug mode.
  MOZ_ASSERT(false, "Receiving unexpected Principal");
#endif
}

/*static*/
mozilla::ipc::IPCResult ContentParent::PrincipalValidationIpcFail(
    nsIPrincipal* aPrincipal, mozilla::ipc::IProtocol* aActor,
    const char* aMethod) {
  // Always get the telemetry extra, as we'll use it for building the crash
  // report as well.
  auto gleanExtra = GetAndLogPrincipalValidationFailure(aPrincipal, aMethod);

  // Do not send telemetry when chrome-debugging is enabled
  bool isChromeDebuggingEnabled =
      Preferences::GetBool("devtools.chrome.enabled", false);
  if (!isChromeDebuggingEnabled) {
    glean::security::fission_principals.Record(mozilla::Some(gleanExtra));
  }

  return IPC_FAIL_UNSAFE_PRINTF(
      aActor, "Invalid principal (%s%s%s) in %s%s",
      gleanExtra.principaltype->get(), gleanExtra.scheme ? " " : "",
      gleanExtra.scheme ? gleanExtra.scheme->get() : "", aMethod,
      isChromeDebuggingEnabled ? " (chrome debug on)" : "");
}

bool ContentParent::ValidatePrincipal(
    nsIPrincipal* aPrincipal,
    const EnumSet<ValidatePrincipalOptions>& aOptions) {
  return ValidatePrincipalCouldPotentiallyBeLoadedBy(aPrincipal, mRemoteType,
                                                     aOptions);
}

/*static*/
already_AddRefed<RemoteBrowser> ContentParent::CreateBrowser(
    const TabContext& aContext, Element* aFrameElement,
    const nsACString& aRemoteType, BrowsingContext* aBrowsingContext,
    ContentParent* aOpenerContentParent) {
  AUTO_PROFILER_LABEL("ContentParent::CreateBrowser", OTHER);

  MOZ_DIAGNOSTIC_ASSERT(
      !aBrowsingContext->Canonical()->GetBrowserParent(),
      "BrowsingContext must not have BrowserParent, or have previous "
      "BrowserParent cleared");

  // Don't bother creating new content browsers after entering shutdown. This
  // could lead to starting a new content process, which may significantly delay
  // shutdown, and the content is unlikely to be displayed.
  if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
    NS_WARNING("Ignoring remote browser creation request during shutdown");
    return nullptr;
  }

  nsAutoCString remoteType(aRemoteType);
  if (remoteType.IsEmpty()) {
    remoteType = SharedWebRemoteType(aBrowsingContext->OriginAttributesRef());
  }

  TabId tabId(nsContentUtils::GenerateTabId());

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

  // Hold a KeepAlive on our ContentParent throughout this function. Once the
  // `BrowserParent` has been created, it can be cleared, as that BrowserParent
  // will establish its own KeepAlive.
  UniqueContentParentKeepAlive constructorSender;
  MOZ_RELEASE_ASSERT(XRE_IsParentProcess(),
                     "Cannot allocate BrowserParent in content process");
  if (aOpenerContentParent && !aOpenerContentParent->IsShuttingDown()) {
    constructorSender =
        aOpenerContentParent->AddKeepAlive(aBrowsingContext->BrowserId());
  } else {
    constructorSender = GetNewOrUsedBrowserProcess(
        remoteType, aBrowsingContext->Group(), PROCESS_PRIORITY_FOREGROUND,
        /* aPreferUsed */ false,
        /* aBrowserId */ aBrowsingContext->BrowserId());
    if (!constructorSender) {
      return nullptr;
    }
  }

  aBrowsingContext->SetEmbedderElement(aFrameElement);

  // Ensure that the process which we're using to launch is set as the host
  // process for this BrowsingContextGroup.
  aBrowsingContext->Group()->EnsureHostProcess(constructorSender.get());

  nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
  docShell->GetTreeOwner(getter_AddRefs(treeOwner));
  if (!treeOwner) {
    return nullptr;
  }

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

  nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
  if (loadContext && loadContext->UsePrivateBrowsing()) {
    chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW;
  }
  if (loadContext && loadContext->UseRemoteTabs()) {
    chromeFlags |= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW;
  }
  if (loadContext && loadContext->UseRemoteSubframes()) {
    chromeFlags |= nsIWebBrowserChrome::CHROME_FISSION_WINDOW;
  }

  if (tabId == 0) {
    return nullptr;
  }

  aBrowsingContext->Canonical()->SetOwnerProcessId(
      constructorSender->ChildID());

  RefPtr<BrowserParent> browserParent =
      new BrowserParent(constructorSender.get(), tabId, aContext,
                        aBrowsingContext->Canonical(), chromeFlags);

  ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
  if (NS_WARN_IF(!cpm)) {
    return nullptr;
  }
  cpm->RegisterRemoteFrame(browserParent);

  // Open a remote endpoint for our PBrowser actor.
  ManagedEndpoint<PBrowserChild> childEp =
      constructorSender->OpenPBrowserEndpoint(browserParent);
  if (NS_WARN_IF(!childEp.IsValid())) {
    return nullptr;
  }

  nsCOMPtr<nsIPrincipal> initialPrincipal =
      NullPrincipal::Create(aBrowsingContext->OriginAttributesRef());
  WindowGlobalInit windowInit = WindowGlobalActor::AboutBlankInitializer(
      aBrowsingContext, initialPrincipal);

  RefPtr<WindowGlobalParent> windowParent =
      WindowGlobalParent::CreateDisconnected(windowInit,
                                             constructorSender.get());
  if (NS_WARN_IF(!windowParent)) {
    return nullptr;
  }

  // Open a remote endpoint for the initial PWindowGlobal actor.
  ManagedEndpoint<PWindowGlobalChild> windowEp =
      browserParent->OpenPWindowGlobalEndpoint(windowParent);
  if (NS_WARN_IF(!windowEp.IsValid())) {
    return nullptr;
  }

  windowParent->Init();

  // Tell the content process to set up its PBrowserChild.
  bool ok = constructorSender->SendConstructBrowser(
      std::move(childEp), std::move(windowEp), tabId,
      aContext.AsIPCTabContext(), windowInit, chromeFlags,
      constructorSender->ChildID(), constructorSender->IsForBrowser(),
      /* aIsTopLevel */ true);
  if (NS_WARN_IF(!ok)) {
    return nullptr;
  }

  // Ensure that we're marked as the current BrowserParent on our
  // CanonicalBrowsingContext.
  aBrowsingContext->Canonical()->SetCurrentBrowserParent(browserParent);

  RefPtr<BrowserHost> browserHost = new BrowserHost(browserParent);
  browserParent->SetOwnerElement(aFrameElement);
  return browserHost.forget();
}

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

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

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

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

void ContentParent::BroadcastStringBundle(
    const StringBundleDescriptor& aBundle) {
  for (auto* cp : AllProcesses(eLive)) {
    AutoTArray<StringBundleDescriptor, 1> array;
    array.AppendElement(StringBundleDescriptor(aBundle.bundleURL(),
                                               aBundle.mapHandle().Clone()));
    (void)cp->SendRegisterStringBundles(std::move(array));
  }
}

void ContentParent::BroadcastShmBlockAdded(uint32_t aGeneration,
                                           uint32_t aIndex) {
  auto* pfl = gfxPlatformFontList::PlatformFontList();
  for (auto* cp : AllProcesses(eLive)) {
    ReadOnlySharedMemoryHandle handle =
        pfl->ShareShmBlockToProcess(aIndex, cp->Pid());
    if (!handle.IsValid()) {
      // If something went wrong here, we just skip it; the child will need to
      // request the block as needed, at some performance cost.
      continue;
    }
    (void)cp->SendFontListShmBlockAdded(aGeneration, aIndex, std::move(handle));
  }
}

void ContentParent::BroadcastThemeUpdate(widget::ThemeChangeKind aKind) {
  const FullLookAndFeel& lnf = *RemoteLookAndFeel::ExtractData();
  for (auto* cp : AllProcesses(eLive)) {
    (void)cp->SendThemeChanged(lnf, aKind);
  }
}

/*static */
void ContentParent::BroadcastMediaCodecsSupportedUpdate(
    RemoteMediaIn aLocation, const media::MediaCodecsSupported& aSupported) {
  // Update processes and print the support info from the given location.
  sCodecsSupported[aLocation] = aSupported;
  for (auto* cp : AllProcesses(eAll)) {
    (void)cp->SendUpdateMediaCodecsSupported(aLocation, aSupported);
  }
  nsCString supportString;
  media::MCSInfo::GetMediaCodecsSupportedString(supportString, aSupported);
  LOGPDM("Broadcast support from '%s', support=%s",
         RemoteMediaInToStr(aLocation), supportString.get());

  // Merge incoming support with existing support list from other locations
  media::MCSInfo::AddSupport(aSupported);

#ifdef MOZ_WMF
  if (aSupported.contains(media::MediaCodecsSupport::AV1LackOfExtension)) {
    glean::media::wmf_codec_no_extension.Get("av1"_ns).Set(true);
  }
  if (aSupported.contains(media::MediaCodecsSupport::HEVCLackOfExtension)) {
    glean::media::wmf_codec_no_extension.Get("hevc"_ns).Set(true);
  }
#endif

  auto fullSupport = media::MCSInfo::GetSupport();

  // Generate + save FULL support string for display in about:support
  supportString.Truncate();
  media::MCSInfo::GetMediaCodecsSupportedString(supportString, fullSupport);

  if (nsCOMPtr<nsIGfxInfo> gfxInfo = components::GfxInfo::Service()) {
    gfxInfo->SetCodecSupportInfo(supportString);
  }
}

const nsACString& ContentParent::GetRemoteType() const { return mRemoteType; }

static StaticRefPtr<nsIAsyncShutdownClient> sXPCOMShutdownClient;
static StaticRefPtr<nsIAsyncShutdownClient> sProfileBeforeChangeClient;
static StaticRefPtr<nsIAsyncShutdownClient> sAppShutdownConfirmedClient;

void ContentParent::Init() {
  MOZ_ASSERT(sXPCOMShutdownClient);

  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
  if (obs) {
    size_t length = std::size(sObserverTopics);
    for (size_t i = 0; i < length; ++i) {
      obs->AddObserver(this, sObserverTopics[i], false);
    }
  }

  if (obs) {
    nsAutoString cpId;
    cpId.AppendInt(static_cast<uint64_t>(this->ChildID()));
    obs->NotifyObservers(static_cast<nsIObserver*>(this), "ipc:content-created",
                         cpId.get());
  }

#ifdef ACCESSIBILITY
  // If accessibility is running in chrome process then start it in content
  // process. We skip this when the only consumer is ePdfOutput. In that mode,
  // the service exists purely to build the accessibility tree for a document
  // being printed in some specific process and unrelated content processes
  // shouldn't bring up accessibility.
  if (GetAccService() && !nsAccessibilityService::IsOnlyForPdfOutput()) {
    (void)SendActivateA11y(nsAccessibilityService::GetActiveCacheDomains());
  }
#endif  // #ifdef ACCESSIBILITY

  (void)SendInitProfiler(ProfilerParent::CreateForProcess(OtherPid()));

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

  // Flush any pref updates that happened during launch and weren't
  // included in the blobs set up in BeginSubprocessLaunch.
  for (const Pref& pref : mQueuedPrefs) {
    (void)NS_WARN_IF(!SendPreferenceUpdate(pref));
  }
  mQueuedPrefs.Clear();

  (void)SendInitNextGenLocalStorageEnabled(NextGenLocalStorageEnabled());

  // sending only the remote settings schemes to the content process
  nsCOMPtr<nsIIOService> io(do_GetIOService());
  MOZ_ASSERT(io, "No IO service for SimpleURI scheme broadcast to content");
  nsTArray<nsCString> remoteSchemes;
  MOZ_ALWAYS_SUCCEEDS(io->GetSimpleURIUnknownRemoteSchemes(remoteSchemes));
  (void)SendSimpleURIUnknownRemoteSchemes(std::move(remoteSchemes));
}

void ContentParent::AsyncSendShutDownMessage() {
  MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
          ("AsyncSendShutDownMessage id=%p, childID=%" PRIu64, this,
           (uint64_t)this->ChildID()));
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(IsDead());
  if (mShutdownPending || !CanSend()) {
    // Don't bother dispatching a runnable if shutdown is already pending or the
    // channel has already been disconnected, as it won't do anything.
    // We could be very late in shutdown and unable to dispatch.
    return;
  }

  // In the case of normal shutdown, send a shutdown message to child to
  // allow it to perform shutdown tasks.
  GetCurrentSerialEventTarget()->Dispatch(NewRunnableMethod<ShutDownMethod>(
      "dom::ContentParent::ShutDownProcess", this,
      &ContentParent::ShutDownProcess, SEND_SHUTDOWN_MESSAGE));
}

void MaybeLogBlockShutdownDiagnostics(ContentParent* aSelf, const char* aMsg,
                                      const char* aFile, int32_t aLine) {
#if defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
  if (aSelf->IsBlockingShutdown()) {
    MOZ_LOG(
        ContentParent::GetLog(), LogLevel::Info,
        ("ContentParent: id=%p childID=%" PRIu64 " pid=%d - %s at %s(%d)",
         aSelf, (uint64_t)aSelf->ChildID(), aSelf->Pid(), aMsg, aFile, aLine));
  }
#else
  (void)aSelf;
  (void)aMsg;
  (void)aFile;
  (void)aLine;
#endif
}

bool ContentParent::ShutDownProcess(ShutDownMethod aMethod) {
  bool result = false;
  MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
          ("ShutDownProcess: id=%p childID=%" PRIu64, this,
           (uint64_t)this->ChildID()));
  // NB: must MarkAsDead() here so that this isn't accidentally
  // returned from Get*() while in the midst of shutdown.
  MarkAsDead();

  // Shutting down by sending a shutdown message works differently than the
  // other methods. We first call Shutdown() in the child. After the child is
  // ready, it calls FinishShutdown() on us. Then we close the channel.
  if (aMethod == SEND_SHUTDOWN_MESSAGE) {
    if (!mShutdownPending) {
      if (CanSend()) {
        // Stop sending input events with input priority when shutting down.
        SetInputPriorityEventEnabled(false);
        // If we did not earlier, let's signal the shutdown to JS now.
        SignalImpendingShutdownToContentJS();

        // Adjust the QoS priorities for shutdown, if they exist.
        if (StaticPrefs::threads_use_low_power_enabled() &&
            StaticPrefs::
                threads_lower_mainthread_priority_in_background_enabled()) {
          SetMainThreadQoSPriority(nsIThread::QOS_PRIORITY_NORMAL);
        }

        // Send the shutdown message with normal priority.
        if (SendShutdown()) {
          MaybeLogBlockShutdownDiagnostics(
              this, "ShutDownProcess: Sent shutdown message.", __FILE__,
              __LINE__);
          mShutdownPending = true;
          // We start the kill timer only after we asked our process to
          // shutdown.
          StartForceKillTimer();
          result = true;
        } else {
          MaybeLogBlockShutdownDiagnostics(
              this, "ShutDownProcess: !!! Send shutdown message failed! !!!",
              __FILE__, __LINE__);
        }
      } else {
        MaybeLogBlockShutdownDiagnostics(
            this, "ShutDownProcess: !!! !CanSend !!!", __FILE__, __LINE__);
      }
    } else {
      MaybeLogBlockShutdownDiagnostics(
          this, "ShutDownProcess: Shutdown already pending.", __FILE__,
          __LINE__);

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

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

  if (QuotaManagerService* qms = QuotaManagerService::GetOrCreate()) {
    qms->AbortOperationsForProcess(mChildID);
  }

  if (aMethod == CLOSE_CHANNEL) {
    if (!mCalledClose) {
      MaybeLogBlockShutdownDiagnostics(
          this, "ShutDownProcess: Closing channel.", __FILE__, __LINE__);
      // Close() can only be called once: It kicks off the destruction sequence.
      mCalledClose = true;
      Close();
    }
    result = true;
  }

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

mozilla::ipc::IPCResult ContentParent::RecvNotifyShutdownSuccess() {
  if (!mShutdownPending) {
    return IPC_FAIL(this, "RecvNotifyShutdownSuccess without mShutdownPending");
  }

  mIsNotifiedShutdownSuccess = true;

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvFinishShutdown() {
  if (!mShutdownPending) {
    return IPC_FAIL(this, "RecvFinishShutdown without mShutdownPending");
  }

  // At this point, we already called ShutDownProcess once with
  // SEND_SHUTDOWN_MESSAGE. To actually close the channel, we call
  // ShutDownProcess again with CLOSE_CHANNEL.
  if (mCalledClose) {
    MaybeLogBlockShutdownDiagnostics(
        this, "RecvFinishShutdown: Channel already closed.", __FILE__,
        __LINE__);
  }

  ShutDownProcess(CLOSE_CHANNEL);
  return IPC_OK();
}

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

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

void ContentParent::AddToPool(nsTArray<ContentParent*>& aPool) {
  MOZ_DIAGNOSTIC_ASSERT(!mIsInPool);
  AssertAlive();
  MOZ_DIAGNOSTIC_ASSERT(!mCalledKillHard);
  aPool.AppendElement(this);
  mIsInPool = true;
}

void ContentParent::RemoveFromPool(nsTArray<ContentParent*>& aPool) {
  MOZ_DIAGNOSTIC_ASSERT(mIsInPool);
  aPool.RemoveElement(this);
  mIsInPool = false;
}

void ContentParent::AssertNotInPool() {
  MOZ_RELEASE_ASSERT(!mIsInPool);

  MOZ_RELEASE_ASSERT(!sBrowserContentParents ||
                     !sBrowserContentParents->Contains(mRemoteType) ||
                     !sBrowserContentParents->Get(mRemoteType)->Contains(this));

  for (const auto& group : mGroups) {
    MOZ_RELEASE_ASSERT(group->GetHostProcess(mRemoteType) != this,
                       "still a host process for one of our groups?");
  }
}

void ContentParent::AssertAlive() {
  MOZ_DIAGNOSTIC_ASSERT(!mIsSignaledImpendingShutdown);
  MOZ_DIAGNOSTIC_ASSERT(!IsDead());
}

void ContentParent::RemoveFromList() {
  if (!mIsInPool) {
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
    AssertNotInPool();
#endif
    return;
  }

  // Ensure that this BrowsingContextGroup is no longer used to host new
  // documents from any associated BrowsingContextGroups. It may become a host
  // again in the future, if it is restored to the pool.
  for (const auto& group : mGroups) {
    group->RemoveHostProcess(this);
  }

  if (sBrowserContentParents) {
    if (auto entry = sBrowserContentParents->Lookup(mRemoteType)) {
      const auto& contentParents = entry.Data();
      RemoveFromPool(*contentParents);
      if (contentParents->IsEmpty()) {
        entry.Remove();
      }
    }
    if (sBrowserContentParents->IsEmpty()) {
      delete sBrowserContentParents;
      sBrowserContentParents = nullptr;
    }
  }
}

void ContentParent::MarkAsDead() {
  MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
          ("Marking ContentProcess id=%p childID=%" PRIu64 " as dead", this,
           (uint64_t)this->ChildID()));
  MOZ_DIAGNOSTIC_ASSERT(!sInProcessSelector);
  RemoveFromList();

  // Flag shutdown has started for us to our threadsafe handle.
  {
    // Depending on how we get here, the lock might or might not be set.
    RecursiveMutexAutoLock lock(mThreadsafeHandle->mMutex);

    mThreadsafeHandle->mShutdownStarted = true;
  }

  // Prevent this process from being re-used.
  PreallocatedProcessManager::Erase(this);

#if defined(MOZ_WIDGET_ANDROID) && !defined(MOZ_PROFILE_GENERATE)
  if (IsAlive()) {
    // We're intentionally killing the content process at this point to ensure
    // that we never have a "dead" content process sitting around and occupying
    // an Android Service.
    //
    // The exception is in MOZ_PROFILE_GENERATE builds where we must allow the
    // process to shutdown cleanly so that profile data can be dumped. This is
    // okay as we will not reach our process limit during the profile run.
    nsCOMPtr<nsIEventTarget> launcherThread(GetIPCLauncher());
    MOZ_ASSERT(launcherThread);

    auto procType = java::GeckoProcessType::CONTENT();
    auto selector =
        java::GeckoProcessManager::Selector::New(procType, OtherPid());

    launcherThread->Dispatch(NS_NewRunnableFunction(
        "ContentParent::MarkAsDead",
        [selector =
             java::GeckoProcessManager::Selector::GlobalRef(selector)]() {
          java::GeckoProcessManager::ShutdownProcess(selector);
        }));
  }
#endif

  mLifecycleState = LifecycleState::DEAD;
}

void ContentParent::ProcessingError(Result aCode, const char* aReason) {
  if (MsgDropped == aCode) {
    return;
  }
  // Other errors are big deals.
#ifndef FUZZING
  KillHard(aReason);
#endif
  if (CanSend()) {
    GetIPCChannel()->InduceConnectionError();
  }
}

void ContentParent::ActorDestroy(ActorDestroyReason why) {
#ifdef FUZZING_SNAPSHOT
  MOZ_FUZZING_IPC_DROP_PEER("ContentParent::ActorDestroy");
#endif

  if (mSendShutdownTimer) {
    mSendShutdownTimer->Cancel();
    mSendShutdownTimer = nullptr;
  }
  if (mForceKillTimer) {
    mForceKillTimer->Cancel();
    mForceKillTimer = nullptr;
  }

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

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

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

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

  // Make sure we always clean up.
  ShutDownProcess(CLOSE_CHANNEL);

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

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

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

  RecvRemoveGeolocationListener();

#ifdef MOZ_WMF_CDM
  if (mOriginsListCallback) {
    nsCOMPtr<nsIWindowsMediaFoundationCDMOriginsListService> rsService =
        do_GetService("@mozilla.org/media/wmfcdm-origins-list;1");
    if (rsService) {
      rsService->RemoveCallback(mOriginsListCallback);
    }
    mOriginsListCallback = nullptr;
  }
#endif

  // Destroy our JSProcessActors, and reject any pending queries.
  JSActorDidDestroy();

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

    props->SetPropertyAsUint64(u"childID"_ns, mChildID);

    if (AbnormalShutdown == why) {
      glean::subprocess::abnormal_abort.Get("content"_ns).Add(1);

      props->SetPropertyAsBool(u"abnormal"_ns, true);

      nsAutoString dumpID;
      nsAutoCString processType;
      // There's a window in which child processes can crash
      // after IPC is established, but before a crash reporter
      // is created.
      if (mCrashReporter) {
        // if mCreatedPairedMinidumps is true, we've already generated
        // parent/child dumps for desktop crashes.
        if (!mCreatedPairedMinidumps) {
#if defined(XP_MACOSX)
          RefPtr<nsAvailableMemoryWatcherBase> memWatcher;
          memWatcher = nsAvailableMemoryWatcherBase::GetSingleton();
          memWatcher->AddChildAnnotations(mCrashReporter);
#endif

          mCrashReporter->GenerateCrashReport();
        }

        if (mCrashReporter->HasMinidump()) {
          dumpID = mCrashReporter->MinidumpID();
        }
        processType = mCrashReporter->ProcessType();
      } else {
        HandleOrphanedMinidump(&dumpID);
        processType = XRE_GeckoProcessTypeToString(GeckoProcessType_Content);
      }

      if (!dumpID.IsEmpty()) {
        props->SetPropertyAsAString(u"dumpID"_ns, dumpID);
      }
      if (!processType.IsEmpty()) {
        props->SetPropertyAsACString(u"processType"_ns, processType);
      }
    }
    nsAutoString cpId;
    cpId.AppendInt(static_cast<uint64_t>(this->ChildID()));
    obs->NotifyObservers((nsIPropertyBag2*)props, "ipc:content-shutdown",
                         cpId.get());
  }

  // Remove any and all idle listeners.
  if (mIdleListeners.Length() > 0) {
    nsCOMPtr<nsIUserIdleService> idleService =
        do_GetService("@mozilla.org/widget/useridleservice;1");
    if (idleService) {
      RefPtr<ParentIdleListener> listener;
      for (const auto& lentry : mIdleListeners) {
        listener = static_cast<ParentIdleListener*>(lentry.get());
        idleService->RemoveIdleObserver(listener, listener->mTime);
      }
    }
    mIdleListeners.Clear();
  }

  MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
          ("destroying Subprocess in ActorDestroy: ContentParent id=%p "
           "mSubprocess id=%p handle %" PRIuPTR,
           this, mSubprocess,
           mSubprocess ? (uintptr_t)mSubprocess->GetChildProcessHandle() : -1));
  // FIXME (bug 1520997): does this really need an additional dispatch?
  if (GetCurrentSerialEventTarget()) {
    GetCurrentSerialEventTarget()->Dispatch(NS_NewRunnableFunction(
        "DelayedDeleteSubprocessRunnable", [subprocess = mSubprocess] {
          MOZ_LOG(
              ContentParent::GetLog(), LogLevel::Debug,
              ("destroyed Subprocess in ActorDestroy: Subprocess id=%p handle "
               "%" PRIuPTR,
               subprocess,
               subprocess ? (uintptr_t)subprocess->GetChildProcessHandle()
                          : -1));
          subprocess->Destroy();
        }));
  }
  mSubprocess = nullptr;

  ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
  if (cpm) {
    cpm->RemoveContentProcess(this->ChildID());
  }

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

  // Unregister all the BlobURLs registered by the ContentChild.
  BlobURLProtocolHandler::RemoveDataEntriesPerContentParent(ChildID());

#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
  AssertNotInPool();
#endif

  // As this process is going away, ensure that every BrowsingContext hosted by
  // it has been detached, and every BrowsingContextGroup has been fully
  // unsubscribed.
  BrowsingContext::DiscardFromContentParent(this);

  const nsTHashSet<RefPtr<BrowsingContextGroup>> groups = std::move(mGroups);
  for (const auto& group : groups) {
    group->Unsubscribe(this);
  }
  MOZ_DIAGNOSTIC_ASSERT(mGroups.IsEmpty());

  mPendingLoadStates.Clear();
  mPendingParentProcessChannelHandles.Clear();
}

UniqueContentParentKeepAlive ContentParent::TryAddKeepAlive(
    uint64_t aBrowserId) {
  UniqueContentParentKeepAlive keepAlive =
      UniqueContentParentKeepAliveFromThreadsafe(
          mThreadsafeHandle->TryAddKeepAlive(aBrowserId));
  // If we successfully added a KeepAlive, we can cancel any pending
  // MaybeBeginShutDown call (as it will no longer begin process shutdown due to
  // outstanding KeepAlives).
  // This is just an optimization and the MaybeBeginShutDown call will be a
  // no-op if it is called with the KeepAlive held.
  if (keepAlive && mMaybeBeginShutdownRunner) {
    mMaybeBeginShutdownRunner->Cancel();
    mMaybeBeginShutdownRunner = nullptr;
  }
  return keepAlive;
}

UniqueContentParentKeepAlive ContentParent::AddKeepAlive(uint64_t aBrowserId) {
  UniqueContentParentKeepAlive keepAlive = TryAddKeepAlive(aBrowserId);
  MOZ_DIAGNOSTIC_ASSERT(keepAlive, "ContentParent is already dead");
  return keepAlive;
}

void ContentParent::RemoveKeepAlive(uint64_t aBrowserId) {
  AssertIsOnMainThread();

  {
    RecursiveMutexAutoLock lock(mThreadsafeHandle->mMutex);
    auto entry = mThreadsafeHandle->mKeepAlivesPerBrowserId.Lookup(aBrowserId);
    MOZ_RELEASE_ASSERT(entry, "No KeepAlive for this BrowserId");
    if (!--entry.Data()) {
      entry.Remove();
    }
  }

  MaybeBeginShutDown();
}

void ContentParent::MaybeBeginShutDown(bool aImmediate,
                                       bool aIgnoreKeepAlivePref) {
  AssertIsOnMainThread();
  MOZ_ASSERT(!aIgnoreKeepAlivePref || aImmediate,
             "aIgnoreKeepAlivePref requires aImmediate");

  // Don't bother waiting, even if `aImmediate` is not true, if the process
  // can no longer be re-used (e.g. because it is dead, or we're in shutdown).
  bool immediate =
      aImmediate || IsDead() ||
      AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed) ||
      StaticPrefs::dom_ipc_processReuse_unusedGraceMs() == 0 ||
      IsRemoteTypeJitDisabled(mRemoteType);

  // Clean up any scheduled idle task unless we schedule a new one.
  auto cancelIdleTask = MakeScopeExit([&] {
    if (mMaybeBeginShutdownRunner) {
      mMaybeBeginShutdownRunner->Cancel();
      mMaybeBeginShutdownRunner = nullptr;
    }
  });

  {
    RecursiveMutexAutoLock lock(mThreadsafeHandle->mMutex);
    // If we still have keepalives or are still launching, we're not shutting
    // down. Return.
    if (IsLaunching() ||
        !mThreadsafeHandle->mKeepAlivesPerBrowserId.IsEmpty()) {
      return;
    }

    // If we're not in main process shutdown, we might want to keep some content
    // processes alive for performance reasons (e.g. test runs and privileged
    // content process for some about: pages). We don't want to alter behavior
    // if the pref is not set, so default to 0.
    if (!aIgnoreKeepAlivePref && mIsInPool && !mRemoteType.Contains('=') &&
        !AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
      auto* contentParents = sBrowserContentParents->Get(mRemoteType);
      MOZ_RELEASE_ASSERT(
          contentParents,
          "mIsInPool, yet no entry for mRemoteType in sBrowserContentParents?");

      nsAutoCString keepAlivePref("dom.ipc.keepProcessesAlive.");
      keepAlivePref.Append(mRemoteType);

      int32_t processesToKeepAlive = 0;
      if (NS_SUCCEEDED(Preferences::GetInt(keepAlivePref.get(),
                                           &processesToKeepAlive)) &&
          contentParents->Length() <=
              static_cast<size_t>(processesToKeepAlive)) {
        // We're keeping this process alive even though there are no keepalives
        // for it due to the keepalive pref.
        return;
      }
    }

    if (immediate) {
      // We're not keeping this process alive, begin shutdown.
      mThreadsafeHandle->mShutdownStarted = true;
    }
  }

  // If we're not beginning shutdown immediately, make sure an idle task runner
  // is scheduled to call us back. This delay is intended to avoid unnecessary
  // process churn when a process becomes momentarily unused (which can happen
  // frequently when running tests).
  if (!immediate) {
    // We want an idle task to call us back, don't cancel it.
    cancelIdleTask.release();

    MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
            ("MaybeBeginShutDown(%d) would begin shutdown, %s", OtherChildID(),
             mMaybeBeginShutdownRunner ? "already delayed" : "delaying"));

    if (!mMaybeBeginShutdownRunner) {
      TimeDuration startDelay = TimeDuration::FromMilliseconds(
          StaticPrefs::dom_ipc_processReuse_unusedGraceMs());
      TimeDuration maxDelay = startDelay + TimeDuration::FromSeconds(1);
      mMaybeBeginShutdownRunner = IdleTaskRunner::Create(
          [self = RefPtr{this}](TimeStamp) -> bool {
            MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
                    ("MaybeBeginShutDown(%d) resuming after delay",
                     self->OtherChildID()));
            self->MaybeBeginShutDown(/* aImmediate */ true);
            return true;
          },
          "ContentParent::IdleMaybeBeginShutdown"_ns, startDelay, maxDelay,
          /* aMinimumUsefulBudget */ TimeDuration::FromMilliseconds(3),
          /* aRepeating */ false, [] { return false; });
    }
    return;
  }

  MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
          ("MaybeBeginShutDown(%d) shutdown starting (%u bps)", OtherChildID(),
           ManagedPBrowserParent().Count()));

  MarkAsDead();
  SignalImpendingShutdownToContentJS();

  if (ManagedPBrowserParent().Count() > 0) {
    // We still have PBrowser instances which have not been shut down.
    // Wait for them to be destroyed before we follow-through and shut down this
    // process, but start a shutdown timer to kill them if this takes too long.
    StartSendShutdownTimer();
  } else {
    // All tabs are dead, we can fully begin shutting down.
    AsyncSendShutDownMessage();
  }
}

void ContentParent::StartSendShutdownTimer() {
  if (mSendShutdownTimer || !CanSend()) {
    return;
  }

  uint32_t timeoutSecs = StaticPrefs::dom_ipc_tabs_shutdownTimeoutSecs();
  if (timeoutSecs > 0) {
    NS_NewTimerWithFuncCallback(
        getter_AddRefs(mSendShutdownTimer),
        ContentParent::SendShutdownTimerCallback, this, timeoutSecs * 1000,
        nsITimer::TYPE_ONE_SHOT,
        "dom::ContentParent::StartSendShutdownTimer"_ns);
    MOZ_ASSERT(mSendShutdownTimer);
  }
}

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

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

already_AddRefed<TestShellParent> ContentParent::CreateTestShell() {
  RefPtr<TestShellParent> actor = new TestShellParent();
  if (!SendPTestShellConstructor(actor)) {
    return nullptr;
  }
  return actor.forget();
}

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

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

#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
// Append the sandbox command line parameters that are not static. i.e.,
// parameters that can be different for different child processes.
void ContentParent::AppendDynamicSandboxParams(
    std::vector<std::string>& aArgs) {
  // For file content processes
  if (GetRemoteType() == FILE_REMOTE_TYPE) {
    MacSandboxInfo::AppendFileAccessParam(aArgs, true);
  }
}

// Generate the static sandbox command line parameters and store
// them in the provided params vector to be used each time a new
// content process is launched.
static void CacheSandboxParams(std::vector<std::string>& aCachedParams) {
  // This must only be called once and we should
  // be starting with an empty list of parameters.
  MOZ_ASSERT(aCachedParams.empty());

  MacSandboxInfo info;
  info.type = MacSandboxType_Content;
  info.level = GetEffectiveContentSandboxLevel();

  // Sandbox logging
  if (Preferences::GetBool("security.sandbox.logging.enabled") ||
      PR_GetEnv("MOZ_SANDBOX_LOGGING")) {
    info.shouldLog = true;
  }

  // Audio access
  if (!StaticPrefs::media_cubeb_sandbox()) {
    info.hasAudio = true;
  }

  // Window server access. If the disconnect-windowserver pref is not
  // "true" or out-of-process WebGL is not enabled, allow window server
  // access in the sandbox policy.
  if (!Preferences::GetBool(
          "security.sandbox.content.mac.disconnect-windowserver")) {
    info.hasWindowServer = true;
  }

  // .app path (normalized)
  nsAutoCString appPath;
  if (!nsMacUtilsImpl::GetAppPath(appPath)) {
    MOZ_CRASH("Failed to get app dir paths");
  }
  info.appPath = appPath.get();

  // TESTING_READ_PATH1
  nsAutoCString testingReadPath1;
  Preferences::GetCString("security.sandbox.content.mac.testing_read_path1",
                          testingReadPath1);
  if (!testingReadPath1.IsEmpty()) {
    info.testingReadPath1 = testingReadPath1.get();
  }

  // TESTING_READ_PATH2
  nsAutoCString testingReadPath2;
  Preferences::GetCString("security.sandbox.content.mac.testing_read_path2",
                          testingReadPath2);
  if (!testingReadPath2.IsEmpty()) {
    info.testingReadPath2 = testingReadPath2.get();
  }

  // TESTING_READ_PATH3, TESTING_READ_PATH4. In non-packaged builds,
  // these are used to whitelist the repo dir and object dir respectively.
  nsresult rv;
  if (!mozilla::IsPackagedBuild()) {
    // Repo dir
    nsCOMPtr<nsIFile> repoDir;
    rv = nsMacUtilsImpl::GetRepoDir(getter_AddRefs(repoDir));
    if (NS_FAILED(rv)) {
      MOZ_CRASH("Failed to get path to repo dir");
    }
    nsCString repoDirPath;
    (void)repoDir->GetNativePath(repoDirPath);
    info.testingReadPath3 = repoDirPath.get();

    // Object dir
    nsCOMPtr<nsIFile> objDir;
    rv = nsMacUtilsImpl::GetObjDir(getter_AddRefs(objDir));
    if (NS_FAILED(rv)) {
      MOZ_CRASH("Failed to get path to build object dir");
    }
    nsCString objDirPath;
    (void)objDir->GetNativePath(objDirPath);
    info.testingReadPath4 = objDirPath.get();
  }

  // DEBUG_WRITE_DIR
#  ifdef DEBUG
  // For bloat/leak logging or when a content process dies intentionally
  // (|NoteIntentionalCrash|) for tests, it wants to log that it did this.
  // Allow writing to this location.
  nsAutoCString bloatLogDirPath;
  if (NS_SUCCEEDED(nsMacUtilsImpl::GetBloatLogDir(bloatLogDirPath))) {
    info.debugWriteDir = bloatLogDirPath.get();
  }
#  endif  // DEBUG

  info.AppendAsParams(aCachedParams);
}

// Append sandboxing command line parameters.
void ContentParent::AppendSandboxParams(std::vector<std::string>& aArgs) {
  MOZ_ASSERT(sMacSandboxParams != nullptr);

  // An empty sMacSandboxParams indicates this is the
  // first invocation and we don't have cached params yet.
  if (sMacSandboxParams->empty()) {
    CacheSandboxParams(*sMacSandboxParams);
    MOZ_ASSERT(!sMacSandboxParams->empty());
  }

  // Append cached arguments.
  aArgs.insert(aArgs.end(), sMacSandboxParams->begin(),
               sMacSandboxParams->end());

  // Append remaining arguments.
  AppendDynamicSandboxParams(aArgs);
}
#endif  // XP_MACOSX && MOZ_SANDBOX

bool ContentParent::BeginSubprocessLaunch(ProcessPriority aPriority) {
  AUTO_PROFILER_LABEL("ContentParent::LaunchSubprocess", OTHER);

  // Ensure we will not rush through our shutdown phases while launching.
  // LaunchSubprocessReject will remove them in case of failure,
  // otherwise ActorDestroy will take care.
  AddShutdownBlockers();

  if (!ContentProcessManager::GetSingleton()) {
    MOZ_ASSERT(false, "Unable to acquire ContentProcessManager singleton!");
    return false;
  }

  geckoargs::ChildProcessArgs extraArgs;
  geckoargs::sIsForBrowser.Put(IsForBrowser(), extraArgs);
  geckoargs::sNotForBrowser.Put(!IsForBrowser(), extraArgs);

  // Prefs information is passed via anonymous shared memory to avoid bloating
  // the command line.

  // Instantiate the pref serializer. It will be cleaned up in
  // `LaunchSubprocessReject`/`LaunchSubprocessResolve`.
  mPrefSerializer = MakeUnique<mozilla::ipc::SharedPreferenceSerializer>();
  if (!mPrefSerializer->SerializeToSharedMemory(GeckoProcessType_Content,
                                                GetRemoteType())) {
    NS_WARNING("SharedPreferenceSerializer::SerializeToSharedMemory failed");
    MarkAsDead();
    return false;
  }
  mPrefSerializer->AddSharedPrefCmdLineArgs(*mSubprocess, extraArgs);

  // The JS engine does some computation during the initialization which can be
  // shared across processes. We add command line arguments to pass a file
  // handle and its content length, to minimize the startup time of content
  // processes.
  if (xpc::SelfHostedShmem::SelfHostedUseSharedMemory()) {
    ::mozilla::ipc::ExportSharedJSInit(*mSubprocess, extraArgs);
  }

  // Register ContentParent as an observer for changes to any pref
  // whose prefix matches the empty string, i.e. all of them.  The
  // observation starts here in order to capture pref updates that
  // happen during async launch.
  Preferences::AddStrongObserver(this, "");

  geckoargs::sSafeMode.Put(gSafeMode, extraArgs);
  geckoargs::sDisableJit.Put(IsRemoteTypeJitDisabled(mRemoteType), extraArgs);

#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
  if (IsContentSandboxEnabled()) {
    AppendSandboxParams(extraArgs.mArgs);
    mSubprocess->DisableOSActivityMode();
  }
#endif

  nsCString parentBuildID(mozilla::PlatformBuildID());
  geckoargs::sParentBuildID.Put(parentBuildID.get(), extraArgs);

#ifdef MOZ_WIDGET_GTK
  // This is X11-only pending a solution for WebGL in Wayland mode.
  if (StaticPrefs::dom_ipc_avoid_gtk() && widget::GdkIsX11Display()) {
    mSubprocess->SetEnv("MOZ_HEADLESS", "1");
  }
#endif

  mLaunchYieldTS = TimeStamp::Now();
  return mSubprocess->AsyncLaunch(std::move(extraArgs));
}

void ContentParent::LaunchSubprocessReject() {
  NS_WARNING("failed to launch child in the parent");
  MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
          ("failed to launch child in the parent"));
  // Now that communication with the child is complete, we can cleanup
  // the preference serializer.
  mPrefSerializer = nullptr;
  if (mIsAPreallocBlocker) {
    PreallocatedProcessManager::RemoveBlocker(mRemoteType, this);
    mIsAPreallocBlocker = false;
  }
  MarkAsDead();
  RemoveShutdownBlockers();
}

bool ContentParent::LaunchSubprocessResolve(bool aIsSync,
                                            ProcessPriority aPriority) {
  AUTO_PROFILER_LABEL("ContentParent::LaunchSubprocess::resolve", OTHER);

  if (mLaunchResolved) {
    // We've already been called, return.
    MOZ_ASSERT(sCreatedFirstContentProcess);
    MOZ_ASSERT(!mPrefSerializer);
    MOZ_ASSERT(mLifecycleState != LifecycleState::LAUNCHING);
    return mLaunchResolvedOk;
  }
  mLaunchResolved = true;

  // Now that communication with the child is complete, we can cleanup
  // the preference serializer.
  mPrefSerializer = nullptr;

  const auto launchResumeTS = TimeStamp::Now();
  if (profiler_thread_is_being_profiled_for_markers()) {
    nsPrintfCString marker("Process start%s for %u",
                           mIsAPreallocBlocker ? " (immediate)" : "",
                           (unsigned int)ChildID());
    PROFILER_MARKER_TEXT(
        mIsAPreallocBlocker ? ProfilerString8View("Process Immediate Launch")
                            : ProfilerString8View("Process Launch"),
        DOM, MarkerTiming::Interval(mLaunchTS, launchResumeTS), marker);
  }

  if (!sCreatedFirstContentProcess) {
    nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
    obs->NotifyObservers(nullptr, "ipc:first-content-process-created", nullptr);
    sCreatedFirstContentProcess = true;
  }

  mSubprocess->TakeInitialEndpoint().Bind(this);

  ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
  if (!cpm) {
    NS_WARNING("immediately shutting-down caused by our shutdown");
    ShutDownProcess(SEND_SHUTDOWN_MESSAGE);
    return false;
  }
  cpm->AddContentProcess(this);

#ifdef MOZ_CODE_COVERAGE
  (void)SendShareCodeCoverageMutex(
      CodeCoverageHandler::Get()->GetMutexHandle());
#endif

  // We must be in the LAUNCHING state still. If we've somehow already been
  // marked as DEAD, fail the process launch, and immediately begin tearing down
  // the content process.
  if (IsDead()) {
    NS_WARNING("immediately shutting-down already-dead process");
    ShutDownProcess(SEND_SHUTDOWN_MESSAGE);
    return false;
  }
  MOZ_ASSERT(mLifecycleState == LifecycleState::LAUNCHING);
  mLifecycleState = LifecycleState::ALIVE;

  if (!InitInternal(aPriority)) {
    NS_WARNING("failed to initialize child in the parent");
    // We've already called Open() by this point, so we need to close the
    // channel to avoid leaking the process.
    ShutDownProcess(SEND_SHUTDOWN_MESSAGE);
    return false;
  }

  mHangMonitorActor = ProcessHangMonitor::AddProcess(this);

  // Set a reply timeout for CPOWs.
  SetReplyTimeoutMs(StaticPrefs::dom_ipc_cpow_timeout());

  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
  if (obs) {
    nsAutoString cpId;
    cpId.AppendInt(static_cast<uint64_t>(this->ChildID()));
    obs->NotifyObservers(static_cast<nsIObserver*>(this),
                         "ipc:content-initializing", cpId.get());
  }

  Init();

  mLifecycleState = LifecycleState::INITIALIZED;

  if (aIsSync) {
    glean::dom_contentprocess::sync_launch.AccumulateRawDuration(
        TimeStamp::Now() - mLaunchTS);
  } else {
    glean::dom_contentprocess::launch_total.AccumulateRawDuration(
        TimeStamp::Now() - mLaunchTS);

    glean::dom_contentprocess::launch_mainthread.AccumulateRawDuration(
        (mLaunchYieldTS - mLaunchTS) + (TimeStamp::Now() - launchResumeTS));
  }

  mLaunchResolvedOk = true;
  return true;
}

static bool IsFileContent(const nsACString& aRemoteType) {
  return aRemoteType == FILE_REMOTE_TYPE;
}

ContentParent::ContentParent(const nsACString& aRemoteType)
    : mSubprocess(new GeckoChildProcessHost(GeckoProcessType_Content,
                                            IsFileContent(aRemoteType))),
      mLaunchTS(TimeStamp::Now()),
      mLaunchYieldTS(mLaunchTS),
      mIsAPreallocBlocker(false),
      mRemoteType(aRemoteType),
      mChildID(mSubprocess->GetChildID()),
      mGeolocationWatchID(-1),
      mThreadsafeHandle(
          new ThreadsafeContentParentHandle(this, mChildID, mRemoteType)),
      mLifecycleState(LifecycleState::LAUNCHING),
      mIsForBrowser(!mRemoteType.IsEmpty()),
      mCalledClose(false),
      mCalledKillHard(false),
      mCreatedPairedMinidumps(false),
      mShutdownPending(false),
      mLaunchResolved(false),
      mLaunchResolvedOk(false),
      mIsUntrusted(false),
      mIsRemoteInputEventQueueEnabled(false),
      mIsInputPriorityEventEnabled(false),
      mIsInPool(false),
      mGMPCreated(false),
      mClipboardContentAnalysisCreated(false),
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
      mBlockShutdownCalled(false),
#endif
      mHangMonitorActor(nullptr) {
  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");

  mRemoteTypeIsolationPrincipal =
      CreateRemoteTypeIsolationPrincipal(aRemoteType);

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

  mMessageManager = nsFrameMessageManager::NewProcessMessageManager(true);

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

  MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
          ("CreateSubprocess: ContentParent id=%p mSubprocess id=%p childID=%d",
           this, mSubprocess, mSubprocess->GetChildID()));
}

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

  AssertIsOnMainThread();

  // Clear the weak reference from the threadsafe handle back to this actor.
  mThreadsafeHandle->mWeakActor = nullptr;

  if (mIsAPreallocBlocker) {
    MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
            ("Removing blocker on ContentProcess id=%p childID=%" PRIu64
             " destruction",
             this, (uint64_t)this->ChildID()));
    PreallocatedProcessManager::RemoveBlocker(mRemoteType, this);
    mIsAPreallocBlocker = false;
  }

  // We should be removed from all these lists in ActorDestroy.
  AssertNotInPool();

  // Normally mSubprocess is destroyed in ActorDestroy, but that won't
  // happen if the process wasn't launched or if it failed to launch.
  if (mSubprocess) {
    MOZ_LOG(
        ContentParent::GetLog(), LogLevel::Verbose,
        ("DestroySubprocess: ContentParent id=%p childID=%" PRIu64
         " mSubprocess id=%p handle "
         "%" PRIuPTR,
         this, (uint64_t)this->ChildID(), mSubprocess,
         mSubprocess ? (uintptr_t)mSubprocess->GetChildProcessHandle() : -1));
    mSubprocess->Destroy();
  }
}

static nsIDNSService::ResolverMode TrrModeFromPref() {
  auto mode =
      static_cast<nsIDNSService::ResolverMode>(StaticPrefs::network_trr_mode());
  if (mode > nsIDNSService::MODE_TRROFF) {
    mode = nsIDNSService::MODE_TRROFF;
  }
  return mode;
}

bool ContentParent::InitInternal(ProcessPriority aInitialPriority) {
  // We can't access the locale service after shutdown has started. Since we
  // can't init the process without it, and since we're going to be canceling
  // whatever load attempt that initiated this process creation anyway, just
  // bail out now if shutdown has already started.
  if (PastShutdownPhase(ShutdownPhase::XPCOMShutdown)) {
    return false;
  }

  XPCOMInitData xpcomInit;

  MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
          ("ContentParent::InitInternal: id=%p, childID=%" PRIu64, (void*)this,
           (uint64_t)this->ChildID()));
  nsCOMPtr<nsIIOService> io(do_GetIOService());
  MOZ_ASSERT(io, "No IO service?");
  DebugOnly<nsresult> rv = io->GetOffline(&xpcomInit.isOffline());
  MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed getting offline?");

  rv = io->GetConnectivity(&xpcomInit.isConnected());
  MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed getting connectivity?");

  xpcomInit.captivePortalState() = nsICaptivePortalService::UNKNOWN;
  nsCOMPtr<nsICaptivePortalService> cps =
      do_GetService(NS_CAPTIVEPORTAL_CONTRACTID);
  if (cps) {
    cps->GetState(&xpcomInit.captivePortalState());
  }

  if (StaticPrefs::fission_processProfileName()) {
    nsCOMPtr<nsIToolkitProfileService> profileSvc =
        do_GetService(NS_PROFILESERVICE_CONTRACTID);
    if (profileSvc) {
      nsCOMPtr<nsIToolkitProfile> currentProfile;
      nsresult rv =
          profileSvc->GetCurrentProfile(getter_AddRefs(currentProfile));
      if (NS_SUCCEEDED(rv) && currentProfile) {
        currentProfile->GetName(mProfile);
      }
    }
  }

  nsIBidiKeyboard* bidi = nsContentUtils::GetBidiKeyboard();

  xpcomInit.isLangRTL() = false;
  xpcomInit.haveBidiKeyboards() = false;
  if (bidi) {
    bidi->IsLangRTL(&xpcomInit.isLangRTL());
    bidi->GetHaveBidiKeyboards(&xpcomInit.haveBidiKeyboards());
  }

  RefPtr<mozSpellChecker> spellChecker(mozSpellChecker::Create());
  MOZ_ASSERT(spellChecker, "No spell checker?");

  spellChecker->GetDictionaryList(&xpcomInit.dictionaries());

  OSPreferences::GetInstance()->GetSystemLocales(xpcomInit.sysLocales());

  LocaleService::GetInstance()->GetAppLocalesAsBCP47(xpcomInit.appLocales());
  LocaleService::GetInstance()->GetRequestedLocales(
      xpcomInit.requestedLocales());

  L10nRegistry::GetParentProcessFileSourceDescriptors(
      xpcomInit.l10nFileSources());

  nsCOMPtr<nsIClipboard> clipboard(
      do_GetService("@mozilla.org/widget/clipboard;1"));
  MOZ_ASSERT(clipboard, "No clipboard?");
  MOZ_ASSERT(
      clipboard->IsClipboardTypeSupported(nsIClipboard::kGlobalClipboard),
      "We should always support the global clipboard.");

  xpcomInit.clipboardCaps().supportsSelectionClipboard() =
      clipboard->IsClipboardTypeSupported(nsIClipboard::kSelectionClipboard);

  xpcomInit.clipboardCaps().supportsFindClipboard() =
      clipboard->IsClipboardTypeSupported(nsIClipboard::kFindClipboard);

  xpcomInit.clipboardCaps().supportsSelectionCache() =
      clipboard->IsClipboardTypeSupported(nsIClipboard::kSelectionCache);

  // Let's copy the domain policy from the parent to the child (if it's active).
  auto initialData = MakeNotNull<RefPtr<StructuredCloneData>>(
      JS::StructuredCloneScope::DifferentProcess,
      StructuredCloneHolder::TransferringNotSupported);
  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
  if (ssm) {
    ssm->CloneDomainPolicy(&xpcomInit.domainPolicy());

    if (ParentProcessMessageManager* mm =
            nsFrameMessageManager::sParentProcessManager) {
      AutoJSAPI jsapi;
      if (NS_WARN_IF(!jsapi.Init(xpc::PrivilegedJunkScope()))) {
        MOZ_CRASH();
      }
      JS::Rooted<JS::Value> init(jsapi.cx());
      // We'll crash on failure, so use a IgnoredErrorResult (which also
      // auto-suppresses exceptions).
      IgnoredErrorResult rv;
      mm->GetInitialProcessData(jsapi.cx(), &init, rv);
      if (NS_WARN_IF(rv.Failed())) {
        MOZ_CRASH();
      }

      initialData->Write(jsapi.cx(), init, rv);
      if (NS_WARN_IF(rv.Failed())) {
        MOZ_CRASH();
      }
    }
  }
  // This is only implemented (returns a non-empty list) by MacOSX and Linux
  // at present.
  SystemFontList fontList;
  gfxPlatform::GetPlatform()->ReadSystemFontList(&fontList);

  const FullLookAndFeel& lnf = *RemoteLookAndFeel::ExtractData();

  // If the shared fontlist is in use, collect its shmem block handles to pass
  // to the child.
  nsTArray<ReadOnlySharedMemoryHandle> sharedFontListBlocks;
  gfxPlatformFontList::PlatformFontList()->ShareFontListToProcess(
      &sharedFontListBlocks, OtherPid());

  // Content processes have no permission to access profile directory, so we
  // send the file URL instead.
  auto* sheetCache = GlobalStyleSheetCache::Singleton();
  if (StyleSheet* ucs = sheetCache->GetUserContentSheet()) {
    xpcomInit.userContentSheetURL() = ucs->GetOriginalURI();
  } else {
    xpcomInit.userContentSheetURL() = nullptr;
  }

  // 1. Ensure the GPU process is ready, as we know we are not yet in shutdown.
  GPUProcessManager* gpm = GPUProcessManager::Get();
  nsresult gpuReadyRv = gpm->EnsureGPUReady();
  // 2. Build ContentDeviceData first, as it may affect some gfxVars.
  gfxPlatform::GetPlatform()->BuildContentDeviceData(
      &xpcomInit.contentDeviceData());
  // 3. Gather non-default gfxVars.
  xpcomInit.gfxNonDefaultVarUpdates() = gfxVars::FetchNonDefaultVars();
  // 4. Start listening for gfxVars updates, to notify content process later on.
  gfxVars::AddReceiver(this);

  nsCOMPtr<nsIGfxInfo> gfxInfo = components::GfxInfo::Service();
  if (gfxInfo) {
    GfxInfoBase* gfxInfoRaw = static_cast<GfxInfoBase*>(gfxInfo.get());
    xpcomInit.gfxFeatureStatus() = gfxInfoRaw->GetAllFeatures();
  }

  // Send the dynamic scalar definitions to the new process.
  TelemetryIPC::GetDynamicScalarDefinitions(xpcomInit.dynamicScalarDefs());

  for (auto const& [location, supported] : sCodecsSupported) {
    (void)SendUpdateMediaCodecsSupported(location, supported);
  }

  // Must send screen info before send initialData
  ScreenManager& screenManager = ScreenManager::GetSingleton();
  screenManager.CopyScreensToRemote(this);

  // Send the UA sheet shared memory buffer and the address it is mapped at.
  Maybe<ReadOnlySharedMemoryHandle> sharedUASheetHandle;
  uintptr_t sharedUASheetAddress = sheetCache->GetSharedMemoryAddress();

  if (ReadOnlySharedMemoryHandle handle = sheetCache->CloneHandle()) {
    sharedUASheetHandle.emplace(std::move(handle));
  } else {
    sharedUASheetAddress = 0;
  }

  bool isReadyForBackgroundProcessing = false;
#if defined(XP_WIN)
  RefPtr<DllServices> dllSvc(DllServices::Get());
  isReadyForBackgroundProcessing = dllSvc->IsReadyForBackgroundProcessing();
#endif

  xpcomInit.perfStatsMask() = PerfStats::GetCollectionMask();

  nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
  dns->GetTrrDomain(xpcomInit.trrDomain());

  nsIDNSService::ResolverMode mode;
  dns->GetCurrentTrrMode(&mode);
  xpcomInit.trrMode() = mode;
  xpcomInit.trrModeFromPref() = TrrModeFromPref();

  (void)SendSetXPCOMProcessAttributes(
      xpcomInit, initialData, lnf, fontList, std::move(sharedUASheetHandle),
      sharedUASheetAddress, std::move(sharedFontListBlocks),
      isReadyForBackgroundProcessing);

  ipc::WritableSharedMap* sharedData =
      nsFrameMessageManager::sParentProcessManager->SharedData();
  sharedData->Flush();
  sharedData->SendTo(this);

  nsCOMPtr<nsIChromeRegistry> registrySvc = nsChromeRegistry::GetService();
  nsChromeRegistryChrome* chromeRegistry =
      static_cast<nsChromeRegistryChrome*>(registrySvc.get());
  chromeRegistry->SendRegisteredChrome(this);

  nsCOMPtr<nsIStringBundleService> stringBundleService =
      components::StringBundle::Service();
  stringBundleService->SendContentBundles(this);

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

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

  MOZ_LOG(
      ContentParent::GetLog(), LogLevel::Verbose,
      ("FOG transport enabled: %s",
       StaticPrefs::telemetry_fog_enable_fog_transport() ? "true" : "false"));
  if (StaticPrefs::telemetry_fog_enable_fog_transport()) {
    // Set up FOGTransport
    Endpoint<PFOGTransportParent> parentEndpoint;
    Endpoint<PFOGTransportChild> childEndpoint;
    MOZ_ALWAYS_SUCCEEDS(
        glean::PFOGTransport::CreateEndpoints(&parentEndpoint, &childEndpoint));
    (void)SendCreateFOGTransport(std::move(childEndpoint));

    nsCOMPtr<nsISerialEventTarget> queue =
        glean::FOGTransportParent::GetQueue();
    mFOGTransportParentActor = MakeRefPtr<glean::FOGTransportParent>();
    queue->Dispatch(
        NS_NewRunnableFunction("Bind FOGTransportParent",
                               [parentEndpoint = std::move(parentEndpoint),
                                actor = mFOGTransportParentActor]() mutable {
                                 parentEndpoint.Bind(actor);
                               }),
        NS_DISPATCH_NORMAL);
  }

  // Send the child its remote type. On Mac, this needs to be sent prior
  // to the message we send to enable the Sandbox (SendStartProcessSandbox)
  // because different remote types require different sandbox privileges.

  (void)SendRemoteType(mRemoteType, mProfile);

  if (mRemoteType != PREALLOC_REMOTE_TYPE) {
    StartRemoteWorkerService();
  }

  ScriptPreloader::InitContentChild(*this);

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

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

  // NB: internally, this will send an IPC message to the child
  // process to get it to create the CompositorBridgeChild.  This
  // message goes through the regular IPC queue for this
  // channel, so delivery will happen-before any other messages
  // we send.  The CompositorBridgeChild must be created before any
  // PBrowsers are created, because they rely on the Compositor
  // already being around.  (Creation is async, so can't happen
  // on demand.)
  Endpoint<PCompositorManagerChild> compositor;
  Endpoint<PImageBridgeChild> imageBridge;
  Endpoint<PVRManagerChild> vrBridge;
  Endpoint<PRemoteMediaManagerChild> videoManager;
  AutoTArray<uint32_t, 4> namespaces;

  if (NS_SUCCEEDED(gpuReadyRv) &&
      gpm->CreateContentBridges(OtherEndpointProcInfo(), &compositor,
                                &imageBridge, &vrBridge, &videoManager,
                                mChildID, &namespaces)) {
    (void)SendInitRendering(std::move(compositor), std::move(imageBridge),
                            std::move(vrBridge), std::move(videoManager),
                            namespaces);
  } else {
    // This can fail if we've already started shutting down the compositor
    // thread. See Bug 1562763 comment 8. On Android this can fail we are are
    // put in the background, making it unlikely the content process will remain
    // alive for much longer, but if it does, we will do the necessary
    // operations for InitRendering in ReinitRendering when the GPU process is
    // available.
    if (gpuReadyRv == NS_ERROR_ILLEGAL_DURING_SHUTDOWN) {
      return false;
    }
  }

  gpm->AddListener(this);

  if (StaticPrefs::media_rdd_process_enabled()) {
    // Ensure the RDD process has been started.
    RDDProcessManager* rdd = RDDProcessManager::Get();
    rdd->LaunchRDDProcess();
  }

  nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance();
  if (sheetService) {
    // This looks like a lot of work, but in a normal browser session we just
    // send two loads.
    //
    // The URIs of the Gecko and Servo sheets should be the same, so it
    // shouldn't matter which we look at.

    for (StyleSheet* sheet : *sheetService->AgentStyleSheets()) {
      (void)SendLoadAndRegisterSheet(sheet->GetOriginalURI(),
                                     nsIStyleSheetService::AGENT_SHEET);
    }

    for (StyleSheet* sheet : *sheetService->UserStyleSheets()) {
      (void)SendLoadAndRegisterSheet(sheet->GetOriginalURI(),
                                     nsIStyleSheetService::USER_SHEET);
    }

    for (StyleSheet* sheet : *sheetService->AuthorStyleSheets()) {
      (void)SendLoadAndRegisterSheet(sheet->GetOriginalURI(),
                                     nsIStyleSheetService::AUTHOR_SHEET);
    }
  }

#ifdef MOZ_WMF_CDM
  if (!mOriginsListCallback && IsMediaFoundationCDMPlaybackEnabled()) {
    mOriginsListCallback = new OriginsListLoadCallback(this);
    nsCOMPtr<nsIWindowsMediaFoundationCDMOriginsListService> rsService =
        do_GetService("@mozilla.org/media/wmfcdm-origins-list;1");
    if (rsService) {
      rsService->SetCallback(mOriginsListCallback);
    }
  }
#endif

#ifdef MOZ_SANDBOX
  bool shouldSandbox = true;
  Maybe<FileDescriptor> brokerFd;
  // XXX: Checking the pref here makes it possible to enable/disable sandboxing
  // during an active session. Currently the pref is only used for testing
  // purpose. If the decision is made to permanently rely on the pref, this
  // should be changed so that it is required to restart firefox for the change
  // of value to take effect. Always send SetProcessSandbox message on macOS.
#  if !defined(XP_MACOSX)
  shouldSandbox = IsContentSandboxEnabled();
#  endif

#  ifdef XP_LINUX
  if (shouldSandbox) {
    MOZ_ASSERT(!mSandboxBroker);
    bool isFileProcess = mRemoteType == FILE_REMOTE_TYPE;
    UniquePtr<SandboxBroker::Policy> policy =
        sSandboxBrokerPolicyFactory->GetContentPolicy(Pid(), isFileProcess);
    if (policy) {
      brokerFd = Some(FileDescriptor());
      mSandboxBroker =
          SandboxBroker::Create(std::move(policy), Pid(), brokerFd.ref());
      if (!mSandboxBroker) {
        KillHard("SandboxBroker::Create failed");
        return false;
      }
      MOZ_ASSERT(brokerFd.ref().IsValid());
    }
  }
#  endif
  if (shouldSandbox && !SendSetProcessSandbox(brokerFd)) {
    KillHard("SandboxInitFailed");
  }
#endif

  // Ensure that the default set of permissions are avaliable in the content
  // process before we try to load any URIs in it.
  //
  // NOTE: All default permissions has to be transmitted to the child process
  // before the blob urls in the for loop below (See Bug 1738713 comment 12).
  EnsurePermissionsByKey(""_ns, ""_ns);

  {
    nsTArray<BlobURLRegistrationData> registrations;
    BlobURLProtocolHandler::ForEachBlobURL([&](BlobImpl* aBlobImpl,
                                               nsIPrincipal* aPrincipal,
                                               const nsCString& aPartitionKey,
                                               const nsACString& aURI,
                                               bool aRevoked) {
      // We send all moz-extension Blob URL's to all content processes
      // because content scripts mean that a moz-extension can live in any
      // process. Same thing for system principal Blob URLs. Content Blob
      // URL's are sent for content principals on-demand by
      // AboutToLoadDocumentForChild and RemoteWorkerManager.
      if (!BlobURLProtocolHandler::IsBlobURLBroadcastPrincipal(aPrincipal)) {
        return true;
      }

      registrations.AppendElement(
          BlobURLRegistrationData(nsCString(aURI), WrapNotNull(aPrincipal),
                                  nsCString(aPartitionKey), aRevoked));

      nsresult rv = TransmitPermissionsForPrincipal(aPrincipal);
      (void)NS_WARN_IF(NS_FAILED(rv));
      return true;
    });

    if (!registrations.IsEmpty()) {
      (void)SendInitBlobURLs(registrations);
    }
  }

  // Send down { Parent, Window }ActorOptions at startup to content process.
  RefPtr<JSActorService> actorSvc = JSActorService::GetSingleton();
  if (actorSvc) {
    nsTArray<JSProcessActorInfo> contentInfos;
    actorSvc->GetJSProcessActorInfos(contentInfos);

    nsTArray<JSWindowActorInfo> windowInfos;
    actorSvc->GetJSWindowActorInfos(windowInfos);

    (void)SendInitJSActorInfos(contentInfos, windowInfos);
  }

  // Begin subscribing to any BrowsingContextGroups which were hosted by this
  // process before it finished launching.
  for (const auto& group : mGroups) {
    group->Subscribe(this);
  }

  MaybeEnableRemoteInputEventQueue();

  return true;
}

bool ContentParent::IsAlive() const {
  return mLifecycleState == LifecycleState::ALIVE ||
         mLifecycleState == LifecycleState::INITIALIZED;
}

bool ContentParent::IsInitialized() const {
  return mLifecycleState == LifecycleState::INITIALIZED;
}

int32_t ContentParent::Pid() const {
  if (!mSubprocess) {
    return -1;
  }
  auto pid = mSubprocess->GetChildProcessId();
  if (pid == 0) {
    return -1;
  }
  return ReleaseAssertedCast<int32_t>(pid);
}

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

  Endpoint<PCompositorManagerChild> compositor;
  Endpoint<PImageBridgeChild> imageBridge;
  Endpoint<PVRManagerChild> vrBridge;
  Endpoint<PRemoteMediaManagerChild> videoManager;
  AutoTArray<uint32_t, 4> namespaces;

  if (!gpm->CreateContentBridges(OtherEndpointProcInfo(), &compositor,
                                 &imageBridge, &vrBridge, &videoManager,
                                 mChildID, &namespaces)) {
    return;
  }

  (void)SendReinitRendering(std::move(compositor), std::move(imageBridge),
                            std::move(vrBridge), std::move(videoManager),
                            namespaces);
}

void ContentParent::OnCompositorDeviceReset() {
  (void)SendReinitRenderingForDeviceReset();
}

void ContentParent::MaybeEnableRemoteInputEventQueue() {
  MOZ_ASSERT(!mIsRemoteInputEventQueueEnabled);
  mIsRemoteInputEventQueueEnabled = true;
  (void)SendSetInputEventQueueEnabled();
  SetInputPriorityEventEnabled(true);
}

void ContentParent::SetInputPriorityEventEnabled(bool aEnabled) {
  if (!mIsRemoteInputEventQueueEnabled ||
      mIsInputPriorityEventEnabled == aEnabled) {
    return;
  }
  mIsInputPriorityEventEnabled = aEnabled;
  // Send IPC messages to flush the pending events in the input event queue and
  // the normal event queue. See PContent.ipdl for more details.
  (void)SendSuspendInputEventQueue();
  (void)SendFlushInputEventQueue();
  (void)SendResumeInputEventQueue();
}

void ContentParent::OnVarChanged(const nsTArray<GfxVarUpdate>& aVar) {
  if (!CanSend()) {
    return;
  }
  (void)SendVarUpdate(aVar);
}

mozilla::ipc::IPCResult ContentParent::RecvSetClipboard(
    const IPCTransferable& aTransferable,
    const nsIClipboard::ClipboardType& aWhichClipboard,
    const MaybeDiscarded<WindowContext>& aRequestingWindowContext) {
  // aRequestingPrincipal is allowed to be nullptr here.

  if (!ValidatePrincipal(aTransferable.dataPrincipal(),
                         {ValidatePrincipalOptions::AllowNullPtr,
                          ValidatePrincipalOptions::AllowExpanded,
                          ValidatePrincipalOptions::AllowSystem})) {
    return PrincipalValidationIpcFail(aTransferable.dataPrincipal(), this,
                                      __func__);
  }

  nsresult rv;
  nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
  NS_ENSURE_SUCCESS(rv, IPC_OK());

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

  rv = nsContentUtils::IPCTransferableToTransferable(
      aTransferable, true /* aAddDataFlavor */, trans,
      true /* aFilterUnknownFlavors */);
  NS_ENSURE_SUCCESS(rv, IPC_OK());

  // OK if this is null
  RefPtr<WindowGlobalParent> window;
  if (!aRequestingWindowContext.IsDiscarded()) {
    window = aRequestingWindowContext.get_canonical();
  }
  clipboard->SetData(trans, nullptr, aWhichClipboard, window);
  return IPC_OK();
}

/* static */ Result<nsCOMPtr<nsITransferable>, nsresult>
ContentParent::CreateClipboardTransferable(const nsTArray<nsCString>& aTypes) {
  nsresult rv;
  nsCOMPtr<nsITransferable> trans =
      do_CreateInstance("@mozilla.org/widget/transferable;1", &rv);
  if (NS_FAILED(rv)) {
    return Err(rv);
  }

  MOZ_TRY(trans->Init(nullptr));
  // The private flag is only used to prevent the data from being cached to the
  // disk. The flag is not exported to the IPCDataTransfer object.
  // The flag is set because we are not sure whether the clipboard data is used
  // in a private browsing context. The transferable is only used in this scope,
  // so the cache would not reduce memory consumption anyway.
  trans->SetIsPrivateData(true);
  // Fill out flavors for transferable
  for (uint32_t t = 0; t < aTypes.Length(); t++) {
    MOZ_TRY(trans->AddDataFlavor(aTypes[t].get()));
  }

  return std::move(trans);
}

template <typename GetClipboardDataFunction>
nsresult ContentParent::GetClipboardDataInternal(
    nsTArray<nsCString>&& aTypes,
    const nsIClipboard::ClipboardType& aWhichClipboard,
    const MaybeDiscarded<WindowContext>& aRequestingWindowContext,
    IPCTransferableDataOrError* aResult, GetClipboardDataFunction&& aFunction) {
  // We expect content processes to always pass a non-null window so Content
  // Analysis can analyze it. (if Content Analysis is active)
  // There may be some cases when a window is closing, etc., in
  // which case returning no clipboard content should not be a problem.
  if (aRequestingWindowContext.IsDiscarded()) {
    *aResult = NS_ERROR_NOT_AVAILABLE;
    return NS_OK;
  }

  if (aRequestingWindowContext.IsNull()) {
    return NS_ERROR_INVALID_ARG;
  }

  RefPtr<WindowGlobalParent> window = aRequestingWindowContext.get_canonical();
  if (window && window->GetContentParent() != this) {
    return NS_ERROR_ILLEGAL_VALUE;
  }

  nsresult rv;
  nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
  if (NS_FAILED(rv)) {
    *aResult = rv;
    return NS_OK;
  }

  auto result = CreateClipboardTransferable(aTypes);
  if (result.isErr()) {
    *aResult = result.unwrapErr();
    return NS_OK;
  }

  nsCOMPtr<nsITransferable> transferable = result.unwrap();

  rv = aFunction(clipboard, transferable, aWhichClipboard, window);
  if (NS_FAILED(rv)) {
    *aResult = rv;
    return NS_OK;
  }

  IPCTransferableData transferableData;
  nsContentUtils::TransferableToIPCTransferableData(
      transferable, &transferableData, true, this);

  *aResult = std::move(transferableData);
  return NS_OK;
}

mozilla::ipc::IPCResult ContentParent::RecvGetClipboard(
    nsTArray<nsCString>&& aTypes,
    const nsIClipboard::ClipboardType& aWhichClipboard,
    const MaybeDiscarded<WindowContext>& aRequestingWindowContext,
    IPCTransferableDataOrError* aTransferableDataOrError) {
  nsresult rv = GetClipboardDataInternal(
      std::move(aTypes), aWhichClipboard, aRequestingWindowContext,
      aTransferableDataOrError,
      [](nsIClipboard* clipboard, nsITransferable* trans, ClipboardType type,
         WindowGlobalParent* window) {
        return clipboard->GetData(trans, type, window);
      });

  if (rv == NS_ERROR_INVALID_ARG) {
    return IPC_FAIL(this, "passed null window to RecvGetClipboard()");
  }

  if (rv == NS_ERROR_ILLEGAL_VALUE) {
    return IPC_FAIL(
        this, "attempt to paste into WindowContext loaded in another process");
  }

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvGetClipboardDataIfSmallerThan(
    nsTArray<nsCString>&& aTypes, uint64_t aThreshold,
    const nsIClipboard::ClipboardType& aWhichClipboard,
    const MaybeDiscarded<WindowContext>& aRequestingWindowContext,
    GetClipboardDataIfSmallerThanResolver&& aResolver) {
  IPCTransferableDataOrError result;

  nsresult rv = GetClipboardDataInternal(
      std::move(aTypes), aWhichClipboard, aRequestingWindowContext, &result,
      [aThreshold](nsIClipboard* clipboard, nsITransferable* trans,
                   ClipboardType type, WindowGlobalParent* window) {
        return clipboard->GetDataIfSmallerThanNative(trans, aThreshold, type,
                                                     window);
      });

  if (rv == NS_ERROR_INVALID_ARG) {
    return IPC_FAIL(
        this, "passed null window to RecvGetClipboardDataIfSmallerThan()");
  }

  if (rv == NS_ERROR_ILLEGAL_VALUE) {
    return IPC_FAIL(
        this, "attempt to paste into WindowContext loaded in another process");
  }

  aResolver(std::move(result));
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvEmptyClipboard(
    const nsIClipboard::ClipboardType& aWhichClipboard) {
  nsresult rv;
  nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
  NS_ENSURE_SUCCESS(rv, IPC_OK());

  clipboard->EmptyClipboard(aWhichClipboard);

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvClipboardHasType(
    nsTArray<nsCString>&& aTypes,
    const nsIClipboard::ClipboardType& aWhichClipboard, bool* aHasType) {
  nsresult rv;
  nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
  NS_ENSURE_SUCCESS(rv, IPC_OK());

  clipboard->HasDataMatchingFlavors(aTypes, aWhichClipboard, aHasType);

  return IPC_OK();
}

namespace {
static Result<ClipboardReadRequest, nsresult> CreateClipboardReadRequest(
    ContentParent& aContentParent,
    nsIClipboardDataSnapshot& aClipboardDataSnapshot) {
  nsTArray<nsCString> flavors;
  nsresult rv = aClipboardDataSnapshot.GetFlavorList(flavors);
  if (NS_FAILED(rv)) {
    return Err(rv);
  }

  auto requestParent = MakeNotNull<RefPtr<ClipboardReadRequestParent>>(
      &aContentParent, &aClipboardDataSnapshot);

  // Open a remote endpoint for our PClipboardReadRequest actor.
  ManagedEndpoint<PClipboardReadRequestChild> childEndpoint =
      aContentParent.OpenPClipboardReadRequestEndpoint(requestParent);
  if (NS_WARN_IF(!childEndpoint.IsValid())) {
    return Err(NS_ERROR_FAILURE);
  }

  return ClipboardReadRequest(std::move(childEndpoint), std::move(flavors));
}

class ClipboardGetCallback final : public nsIClipboardGetDataSnapshotCallback {
 public:
  ClipboardGetCallback(
      ContentParent* aContentParent,
      ContentParent::GetClipboardDataSnapshotResolver&& aResolver)
      : mContentParent(aContentParent), mResolver(std::move(aResolver)) {}

  // This object will never be held by a cycle-collected object, so it doesn't
  // need to be cycle-collected despite holding alive cycle-collected objects.
  NS_DECL_ISUPPORTS

  // nsIClipboardGetDataSnapshotCallback
  NS_IMETHOD OnSuccess(
      nsIClipboardDataSnapshot* aClipboardDataSnapshot) override {
    MOZ_ASSERT(mContentParent);
    MOZ_ASSERT(aClipboardDataSnapshot);

    auto result =
        CreateClipboardReadRequest(*mContentParent, *aClipboardDataSnapshot);
    if (result.isErr()) {
      return OnError(result.unwrapErr());
    }

    mResolver(result.unwrap());
    return NS_OK;
  }

  NS_IMETHOD OnError(nsresult aResult) override {
    mResolver(aResult);
    return NS_OK;
  }

 protected:
  ~ClipboardGetCallback() = default;

  RefPtr<ContentParent> mContentParent;
  ContentParent::GetClipboardDataSnapshotResolver mResolver;
};

NS_IMPL_ISUPPORTS(ClipboardGetCallback, nsIClipboardGetDataSnapshotCallback)

}  // namespace

mozilla::ipc::IPCResult ContentParent::RecvGetClipboardDataSnapshot(
    nsTArray<nsCString>&& aTypes,
    const nsIClipboard::ClipboardType& aWhichClipboard,
    const MaybeDiscarded<WindowContext>& aRequestingWindowContext,
    mozilla::NotNull<nsIPrincipal*> aRequestingPrincipal,
    GetClipboardDataSnapshotResolver&& aResolver) {
  if (!ValidatePrincipal(aRequestingPrincipal,
                         {ValidatePrincipalOptions::AllowSystem,
                          ValidatePrincipalOptions::AllowExpanded})) {
    return PrincipalValidationIpcFail(aRequestingPrincipal, this, __func__);
  }

  // If the requesting context has been discarded, cancel the paste.
  if (aRequestingWindowContext.IsDiscarded()) {
    aResolver(NS_ERROR_NOT_AVAILABLE);
    return IPC_OK();
  }

  RefPtr<WindowGlobalParent> requestingWindow =
      aRequestingWindowContext.get_canonical();
  if (requestingWindow && requestingWindow->GetContentParent() != this) {
    return IPC_FAIL(
        this, "attempt to paste into WindowContext loaded in another process");
  }

  nsresult rv;
  // Retrieve clipboard
  nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
  if (NS_FAILED(rv)) {
    aResolver(rv);
    return IPC_OK();
  }

  auto callback = MakeRefPtr<ClipboardGetCallback>(this, std::move(aResolver));
  rv = clipboard->GetDataSnapshot(aTypes, aWhichClipboard, requestingWindow,
                                  aRequestingPrincipal, callback);
  if (NS_FAILED(rv)) {
    callback->OnError(rv);
    return IPC_OK();
  }

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvGetClipboardDataSnapshotSync(
    nsTArray<nsCString>&& aTypes,
    const nsIClipboard::ClipboardType& aWhichClipboard,
    const MaybeDiscarded<WindowContext>& aRequestingWindowContext,
    ClipboardReadRequestOrError* aRequestOrError) {
  // If the requesting context has been discarded, cancel the paste.
  if (aRequestingWindowContext.IsDiscarded()) {
    *aRequestOrError = NS_ERROR_FAILURE;
    return IPC_OK();
  }

  RefPtr<WindowGlobalParent> requestingWindow =
      aRequestingWindowContext.get_canonical();
  if (requestingWindow && requestingWindow->GetContentParent() != this) {
    return IPC_FAIL(
        this, "attempt to paste into WindowContext loaded in another process");
  }

  nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID));
  if (!clipboard) {
    *aRequestOrError = NS_ERROR_FAILURE;
    return IPC_OK();
  }

  nsCOMPtr<nsIClipboardDataSnapshot> clipboardDataSnapshot;
  nsresult rv =
      clipboard->GetDataSnapshotSync(aTypes, aWhichClipboard, requestingWindow,
                                     getter_AddRefs(clipboardDataSnapshot));
  if (NS_FAILED(rv)) {
    *aRequestOrError = rv;
    return IPC_OK();
  }

  auto result = CreateClipboardReadRequest(*this, *clipboardDataSnapshot);
  if (result.isErr()) {
    *aRequestOrError = result.unwrapErr();
    return IPC_OK();
  }

  *aRequestOrError = result.unwrap();
  return IPC_OK();
}

already_AddRefed<PClipboardWriteRequestParent>
ContentParent::AllocPClipboardWriteRequestParent(
    const nsIClipboard::ClipboardType& aClipboardType,
    const MaybeDiscarded<WindowContext>& aSettingWindowContext) {
  WindowContext* settingWindowContext = nullptr;
  if (!aSettingWindowContext.IsDiscarded()) {
    settingWindowContext = aSettingWindowContext.get();
  }
  RefPtr<ClipboardWriteRequestParent> request =
      MakeAndAddRef<ClipboardWriteRequestParent>(this);
  request->Init(aClipboardType, settingWindowContext);
  return request.forget();
}

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

  // Explicitly zero-initialize the output to ensure we do not send
  // uninitialized heap memory over IPC to the child process to avoid heap data
  // leakage.
  bits->InsertElementsAt(0, aIconSize * aIconSize * 4, 0);

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

mozilla::ipc::IPCResult ContentParent::RecvFirstIdle() {
  // When the ContentChild goes idle, it sends us a FirstIdle message
  // which we use as a good time to signal the PreallocatedProcessManager
  // that it can start allocating processes from now on.
  if (mIsAPreallocBlocker) {
    MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
            ("RecvFirstIdle id=%p childID=%" PRIu64 ": Removing Blocker for %s",
             this, (uint64_t)this->ChildID(), mRemoteType.get()));
    PreallocatedProcessManager::RemoveBlocker(mRemoteType, this);
    mIsAPreallocBlocker = false;
  }
  return IPC_OK();
}

already_AddRefed<nsDocShellLoadState> ContentParent::TakePendingLoadStateForId(
    uint64_t aLoadIdentifier) {
  return mPendingLoadStates.Extract(aLoadIdentifier).valueOr(nullptr).forget();
}

void ContentParent::StorePendingLoadState(nsDocShellLoadState* aLoadState) {
  MOZ_DIAGNOSTIC_ASSERT(
      !mPendingLoadStates.Contains(aLoadState->GetLoadIdentifier()),
      "The same nsDocShellLoadState was sent to the same content process "
      "twice? This will mess with cross-process tracking of loads");
  mPendingLoadStates.InsertOrUpdate(aLoadState->GetLoadIdentifier(),
                                    aLoadState);
}

mozilla::ipc::IPCResult ContentParent::RecvCleanupPendingLoadState(
    uint64_t aLoadIdentifier) {
  mPendingLoadStates.Remove(aLoadIdentifier);
  return IPC_OK();
}

nsID ContentParent::AddParentProcessChannelHandle(
    ParentProcessChannelHandle* aHandle) {
  nsID uuid = nsID::GenerateUUID();
  mPendingParentProcessChannelHandles.InsertOrUpdate(uuid, aHandle);
  return uuid;
}

already_AddRefed<ParentProcessChannelHandle>
ContentParent::ReadParentProcessChannelHandle(const nsID& aUuid) {
  RefPtr<ParentProcessChannelHandle> handle =
      mPendingParentProcessChannelHandles.Get(aUuid);
  return handle.forget();
}

mozilla::ipc::IPCResult ContentParent::RecvDropParentProcessChannelHandle(
    const nsID& aUuid) {
  mPendingParentProcessChannelHandles.Remove(aUuid);
  return IPC_OK();
}

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

NS_IMPL_CYCLE_COLLECTING_ADDREF(ContentParent)
NS_IMPL_CYCLE_COLLECTING_RELEASE(ContentParent)

NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ContentParent)
  NS_INTERFACE_MAP_ENTRY_CONCRETE(ContentParent)
  NS_INTERFACE_MAP_ENTRY(nsIDOMProcessParent)
  NS_INTERFACE_MAP_ENTRY(nsIObserver)
  NS_INTERFACE_MAP_ENTRY(nsIDOMGeoPositionCallback)
  NS_INTERFACE_MAP_ENTRY(nsIDOMGeoPositionErrorCallback)
  NS_INTERFACE_MAP_ENTRY(nsIAsyncShutdownBlocker)
  NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMProcessParent)
NS_INTERFACE_MAP_END

class RequestContentJSInterruptRunnable final : public Runnable {
 public:
  explicit RequestContentJSInterruptRunnable(PProcessHangMonitorParent* aActor)
      : Runnable("dom::RequestContentJSInterruptRunnable"),
        mHangMonitorActor(aActor) {}

  NS_IMETHOD Run() override {
    MOZ_ASSERT(mHangMonitorActor);
    (void)mHangMonitorActor->SendRequestContentJSInterrupt();

    return NS_OK;
  }

 private:
  // The end-of-life of ContentParent::mHangMonitorActor is bound to
  // ContentParent::ActorDestroy and then HangMonitorParent::Shutdown
  // dispatches a shutdown runnable to this queue and waits for it to be
  // executed. So the runnable needs not to care about keeping it alive,
  // as it is surely dispatched earlier than the
  // HangMonitorParent::ShutdownOnThread.
  RefPtr<PProcessHangMonitorParent> mHangMonitorActor;
};

void ContentParent::SignalImpendingShutdownToContentJS() {
  if (!mIsSignaledImpendingShutdown &&
      !AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdown)) {
    MaybeLogBlockShutdownDiagnostics(
        this, "BlockShutdown: NotifyImpendingShutdown.", __FILE__, __LINE__);
    NotifyImpendingShutdown();
    mIsSignaledImpendingShutdown = true;
    if (mHangMonitorActor &&
        StaticPrefs::dom_abort_script_on_child_shutdown()) {
      MaybeLogBlockShutdownDiagnostics(
          this, "BlockShutdown: RequestContentJSInterrupt.", __FILE__,
          __LINE__);
      RefPtr<RequestContentJSInterruptRunnable> r =
          new RequestContentJSInterruptRunnable(mHangMonitorActor);
      ProcessHangMonitor::Get()->Dispatch(r.forget());
    }
  }
}

// Async shutdown blocker
NS_IMETHODIMP
ContentParent::BlockShutdown(nsIAsyncShutdownClient* aClient) {
  if (!AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdown)) {
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
    mBlockShutdownCalled = true;
#endif
    // This will make our process unusable for normal content, so we need to
    // ensure we won't get re-used by GetUsedBrowserProcess as we have not yet
    // done MarkAsDead.
    PreallocatedProcessManager::Erase(this);
    {
      RecursiveMutexAutoLock lock(mThreadsafeHandle->mMutex);
      mThreadsafeHandle->mShutdownStarted = true;
    }
    // Our real shutdown has not yet started. Just notify the impending
    // shutdown and eventually cancel content JS.
    SignalImpendingShutdownToContentJS();

    if (sAppShutdownConfirmedClient) {
      (void)sAppShutdownConfirmedClient->RemoveBlocker(this);
    }
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
    mBlockShutdownCalled = false;
#endif
    return NS_OK;
  }

#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
  // We register two final shutdown blockers and both would call us, but if
  // things go well we will unregister both as (delayed) reaction to the first
  // call we get and thus never receive a second call. Thus we believe that we
  // will get called only once except for quit-application-granted, which is
  // handled above.
  MOZ_ASSERT(!mBlockShutdownCalled);
  mBlockShutdownCalled = true;
#endif

  if (CanSend()) {
    MaybeLogBlockShutdownDiagnostics(this, "BlockShutdown: CanSend.", __FILE__,
                                     __LINE__);

    // Make sure that our process will get scheduled.
    ProcessPriorityManager::SetProcessPriority(this,
                                               PROCESS_PRIORITY_FOREGROUND);
    // The normal shutdown sequence is to send a shutdown message
    // to the child and then just wait for ActorDestroy which will
    // cleanup everything and remove our blockers.
    if (!ShutDownProcess(SEND_SHUTDOWN_MESSAGE)) {
      KillHard("Failed to send Shutdown message. Destroying the process...");
      return NS_OK;
    }
  } else if (IsLaunching()) {
    MaybeLogBlockShutdownDiagnostics(
        this, "BlockShutdown: !CanSend && IsLaunching.", __FILE__, __LINE__);

    // If we get here while we are launching, we must wait for the child to
    // be able to react on our commands. Mark this process as dead. This
    // will make bail out LaunchSubprocessResolve and kick off the normal
    // shutdown sequence.
    MarkAsDead();
  } else {
    MOZ_ASSERT(IsDead());
    if (!IsDead()) {
      MaybeLogBlockShutdownDiagnostics(
          this, "BlockShutdown: !!! !CanSend && !IsLaunching && !IsDead !!!",
          __FILE__, __LINE__);
    } else {
      MaybeLogBlockShutdownDiagnostics(
          this, "BlockShutdown: !CanSend && !IsLaunching && IsDead.", __FILE__,
          __LINE__);
    }
    // Nothing left we can do. We must assume that we race with an ongoing
    // process shutdown, such that we can expect our shutdown blockers to be
    // removed normally.
  }

  return NS_OK;
}

NS_IMETHODIMP
ContentParent::GetName(nsAString& aName) {
  aName.AssignLiteral("ContentParent:");
  aName.AppendPrintf(" id=%" PRIu64, (uint64_t)ChildID());
  return NS_OK;
}

NS_IMETHODIMP
ContentParent::GetState(nsIPropertyBag** aResult) {
  auto props = MakeRefPtr<nsHashPropertyBag>();
  props->SetPropertyAsACString(u"remoteTypePrefix"_ns,
                               RemoteTypePrefix(mRemoteType));
  *aResult = props.forget().downcast<nsIWritablePropertyBag>().take();
  return NS_OK;
}

static void InitShutdownClients() {
  if (!sXPCOMShutdownClient) {
    nsresult rv;
    nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdownService();
    if (!svc) {
      return;
    }

    nsCOMPtr<nsIAsyncShutdownClient> client;
    // TODO: It seems as if getPhase from AsyncShutdown.sys.mjs does not check
    // if we are beyond our phase already. See bug 1762840.
    if (!AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMWillShutdown)) {
      rv = svc->GetXpcomWillShutdown(getter_AddRefs(client));
      if (NS_SUCCEEDED(rv)) {
        sXPCOMShutdownClient = client.forget();
        ClearOnShutdown(&sXPCOMShutdownClient);
      }
    }
    if (!AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdown)) {
      rv = svc->GetProfileBeforeChange(getter_AddRefs(client));
      if (NS_SUCCEEDED(rv)) {
        sProfileBeforeChangeClient = client.forget();
        ClearOnShutdown(&sProfileBeforeChangeClient);
      }
    }
    if (!AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
      rv = svc->GetAppShutdownConfirmed(getter_AddRefs(client));
      if (NS_SUCCEEDED(rv)) {
        sAppShutdownConfirmedClient = client.forget();
        ClearOnShutdown(&sAppShutdownConfirmedClient);
      }
    }
  }
}

void ContentParent::AddShutdownBlockers() {
  InitShutdownClients();
  MOZ_ASSERT(sXPCOMShutdownClient);
  MOZ_ASSERT(sProfileBeforeChangeClient);

  if (sXPCOMShutdownClient) {
    sXPCOMShutdownClient->AddBlocker(
        this, NS_LITERAL_STRING_FROM_CSTRING(__FILE__), __LINE__, u""_ns);
  }
  if (sProfileBeforeChangeClient) {
    sProfileBeforeChangeClient->AddBlocker(
        this, NS_LITERAL_STRING_FROM_CSTRING(__FILE__), __LINE__, u""_ns);
  }
  if (sAppShutdownConfirmedClient) {
    sAppShutdownConfirmedClient->AddBlocker(
        this, NS_LITERAL_STRING_FROM_CSTRING(__FILE__), __LINE__, u""_ns);
  }
}

void ContentParent::RemoveShutdownBlockers() {
  MOZ_ASSERT(sXPCOMShutdownClient);
  MOZ_ASSERT(sProfileBeforeChangeClient);

  MaybeLogBlockShutdownDiagnostics(this, "RemoveShutdownBlockers", __FILE__,
                                   __LINE__);
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
  mBlockShutdownCalled = false;
#endif

  if (sXPCOMShutdownClient) {
    (void)sXPCOMShutdownClient->RemoveBlocker(this);
  }
  if (sProfileBeforeChangeClient) {
    (void)sProfileBeforeChangeClient->RemoveBlocker(this);
  }
  if (sAppShutdownConfirmedClient) {
    (void)sAppShutdownConfirmedClient->RemoveBlocker(this);
  }
}

NS_IMETHODIMP
ContentParent::Observe(nsISupports* aSubject, const char* aTopic,
                       const char16_t* aData) {
  if (IsDead() || !mSubprocess) {
    return NS_OK;
  }

  if (!strcmp(aTopic, "nsPref:changed")) {
    // We know prefs are ASCII here.
    NS_LossyConvertUTF16toASCII strData(aData);

    Pref pref(strData, /* isLocked */ false,
              /* isSanitized */ false, Nothing(), Nothing());

    Preferences::GetPreference(&pref, GeckoProcessType_Content,
                               GetRemoteType());

    // This check is a bit of a hack.  We want to avoid sending excessive
    // preference updates to subprocesses for performance reasons, but we
    // currently don't have a great mechanism for doing so. (See Bug 1819714)
    // We're going to hijack the sanitization mechanism to accomplish our goal
    // but it imposes the following complications:
    // 1) It doesn't avoid sending anything to other (non-web-content)
    //    subprocesses so we're not getting any perf gains there
    // 2) It confuses the subprocesses w.r.t. sanitization.  The point of
    //    sending a preference update of a sanitized preference is so that
    //    content process knows when it's asked to resolve a sanitized
    //    preference, and it can send telemetry and/or crash.  With this
    //    change, a sanitized pref that is created during the browser session
    //    will not be sent to the content process, and therefore the content
    //    process won't know it should telemetry/crash on access - it'll just
    //    silently fail to resolve it.  After browser restart, the sanitized
    //    pref will be populated into the content process via the shared pref
    //    map and _then_ if it is accessed, the content process will crash.
    //    We're seeing good telemetry/crash rates right now, so we're okay with
    //    this limitation.
    if (pref.isSanitized()) {
      return NS_OK;
    }

    if (IsInitialized()) {
      MOZ_ASSERT(mQueuedPrefs.IsEmpty());
      if (!SendPreferenceUpdate(pref)) {
        return NS_ERROR_NOT_AVAILABLE;
      }
    } else {
      MOZ_ASSERT(!IsDead());
      mQueuedPrefs.AppendElement(pref);
    }

    return NS_OK;
  }

  if (!IsAlive()) {
    return NS_OK;
  }

  // listening for memory pressure event
  if (!strcmp(aTopic, "memory-pressure")) {
    (void)SendMemoryPressure(nsDependentString(aData));
  } else if (!strcmp(aTopic, "memory-pressure-stop")) {
    (void)SendMemoryPressureStop();
  } else if (!strcmp(aTopic, "application-background")) {
    (void)SendApplicationBackground();
  } else if (!strcmp(aTopic, "application-foreground")) {
    (void)SendApplicationForeground();
  } else if (!strcmp(aTopic, NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC)) {
    NS_ConvertUTF16toUTF8 dataStr(aData);
    const char* offline = dataStr.get();
    if (!SendSetOffline(!strcmp(offline, "true"))) {
      return NS_ERROR_NOT_AVAILABLE;
    }
  } else if (!strcmp(aTopic, NS_IPC_IOSERVICE_SET_CONNECTIVITY_TOPIC)) {
    if (!SendSetConnectivity(u"true"_ns.Equals(aData))) {
      return NS_ERROR_NOT_AVAILABLE;
    }
  } else if (!strcmp(aTopic, NS_IPC_CAPTIVE_PORTAL_SET_STATE)) {
    nsCOMPtr<nsICaptivePortalService> cps = do_QueryInterface(aSubject);
    MOZ_ASSERT(cps, "Should QI to a captive portal service");
    if (!cps) {
      return NS_ERROR_FAILURE;
    }
    int32_t state;
    cps->GetState(&state);
    if (!SendSetCaptivePortalState(state)) {
      return NS_ERROR_NOT_AVAILABLE;
    }
  } else if (!strcmp(aTopic, "child-gc-request")) {
    (void)SendGarbageCollect();
  } else if (!strcmp(aTopic, "child-cc-request")) {
    (void)SendCycleCollect();
  } else if (!strcmp(aTopic, "child-mmu-request")) {
    (void)SendMinimizeMemoryUsage();
  } else if (!strcmp(aTopic, "child-ghost-request")) {
    (void)SendUnlinkGhosts();
  } else if (!strcmp(aTopic, "last-pb-context-exited")) {
    (void)SendLastPrivateDocShellDestroyed();
  }
#ifdef ACCESSIBILITY
  else if (aData && !strcmp(aTopic, "a11y-init-or-shutdown")) {
    if (*aData == '1') {
      // Make sure accessibility is running in content process when
      // accessibility gets fully initiated in chrome process.
      MOZ_ASSERT(!nsAccessibilityService::IsOnlyForPdfOutput());
      (void)SendActivateA11y(nsAccessibilityService::GetActiveCacheDomains());
    } else if (*aData == '0') {
      // If possible, shut down accessibility in content process when
      // accessibility gets shutdown in chrome process.
      (void)SendShutdownA11y();
    }
  }
#endif
  else if (!strcmp(aTopic, "cacheservice:empty-cache")) {
    (void)SendNotifyEmptyHTTPCache();
  } else if (!strcmp(aTopic, "intl:app-locales-changed")) {
    nsTArray<nsCString> appLocales;
    LocaleService::GetInstance()->GetAppLocalesAsBCP47(appLocales);
    (void)SendUpdateAppLocales(appLocales);
  } else if (!strcmp(aTopic, "intl:requested-locales-changed")) {
    nsTArray<nsCString> requestedLocales;
    LocaleService::GetInstance()->GetRequestedLocales(requestedLocales);
    (void)SendUpdateRequestedLocales(requestedLocales);
  } else if (!strcmp(aTopic, "cookie-changed") ||
             !strcmp(aTopic, "private-cookie-changed")) {
    MOZ_ASSERT(aSubject, "cookie changed notification must have subject.");
    nsCOMPtr<nsICookieNotification> notification = do_QueryInterface(aSubject);
    MOZ_ASSERT(notification,
               "cookie changed notification must have nsICookieNotification.");
    if (!notification) {
      return NS_OK;
    }
    nsICookieNotification::Action action = notification->GetAction();

    PNeckoParent* neckoParent = LoneManagedOrNullAsserts(ManagedPNeckoParent());
    if (!neckoParent) {
      return NS_OK;
    }
    PCookieServiceParent* csParent =
        LoneManagedOrNullAsserts(neckoParent->ManagedPCookieServiceParent());
    if (!csParent) {
      return NS_OK;
    }
    auto* cs = static_cast<CookieServiceParent*>(csParent);
    MOZ_ASSERT(mCookieInContentListCache.IsEmpty());

    if (action == nsICookieNotification::COOKIES_BATCH_DELETED) {
      nsCOMPtr<nsIArray> cookieList;
      DebugOnly<nsresult> rv =
          notification->GetBatchDeletedCookies(getter_AddRefs(cookieList));
      NS_ASSERTION(NS_SUCCEEDED(rv) && cookieList, "couldn't get cookie list");
      cs->RemoveBatchDeletedCookies(cookieList);
      return NS_OK;
    }

    if (action == nsICookieNotification::ALL_COOKIES_CLEARED) {
      cs->RemoveAll();
      return NS_OK;
    }

    // Do not push these cookie updates to the same process they originated
    // from.
    if (cs->ProcessingCookie()) {
      return NS_OK;
    }

    nsCOMPtr<nsICookie> xpcCookie;
    nsresult rv = notification->GetCookie(getter_AddRefs(xpcCookie));
    NS_ASSERTION(NS_SUCCEEDED(rv) && xpcCookie, "couldn't get cookie");

    // only broadcast the cookie change to content processes that need it
    const Cookie& cookie = xpcCookie->AsCookie();

    // do not send cookie if content process does not have similar cookie
    if (!cs->ContentProcessHasCookie(cookie)) {
      return NS_OK;
    }

    UniqueFreePtr<nsID> operationID;
    rv = notification->GetOperationID(TempPtrToSetter(&operationID));
    if (NS_WARN_IF(NS_FAILED(rv))) {
      return NS_OK;
    }

    if (action == nsICookieNotification::COOKIE_DELETED) {
      cs->RemoveCookie(cookie, operationID.get());
    } else if (action == nsICookieNotification::COOKIE_ADDED ||
               action == nsICookieNotification::COOKIE_CHANGED) {
      cs->AddCookie(cookie, operationID.get());
    }

  } else if (!strcmp(aTopic, NS_NETWORK_LINK_TYPE_TOPIC)) {
    UpdateNetworkLinkType();
  } else if (!strcmp(aTopic, "network:socket-process-crashed")) {
    (void)SendSocketProcessCrashed();
  } else if (!strcmp(aTopic, DEFAULT_TIMEZONE_CHANGED_OBSERVER_TOPIC)) {
    (void)SendSystemTimezoneChanged();
  } else if (!strcmp(aTopic, NS_NETWORK_TRR_MODE_CHANGED_TOPIC)) {
    nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
    nsIDNSService::ResolverMode mode;
    dns->GetCurrentTrrMode(&mode);
    (void)SendSetTRRMode(mode, TrrModeFromPref());
  }

  return NS_OK;
}

uint32_t ContentParent::UpdateNetworkLinkType() {
  uint32_t linkType = nsINetworkLinkService::LINK_TYPE_UNKNOWN;
  nsresult rv;
  nsCOMPtr<nsINetworkLinkService> nls =
      do_GetService(NS_NETWORK_LINK_SERVICE_CONTRACTID, &rv);
  if (NS_SUCCEEDED(rv)) {
    rv = nls->GetLinkType(&linkType);
    if (NS_SUCCEEDED(rv)) {
      (void)SendNetworkLinkTypeChange(linkType);
    }
  }

  return linkType;
}

NS_IMETHODIMP
ContentParent::GetInterface(const nsIID& aIID, void** aResult) {
  NS_ENSURE_ARG_POINTER(aResult);

  if (aIID.Equals(NS_GET_IID(nsIMessageSender))) {
    nsCOMPtr<nsIMessageSender> mm = GetMessageManager();
    mm.forget(aResult);
    return NS_OK;
  }

  return NS_NOINTERFACE;
}

mozilla::ipc::IPCResult ContentParent::RecvInitBackground(
    Endpoint<PBackgroundStarterParent>&& aEndpoint) {
  if (!BackgroundParent::AllocStarter(this, std::move(aEndpoint))) {
    NS_WARNING("BackgroundParent::Alloc failed");
  }

  return IPC_OK();
}

bool ContentParent::CanOpenBrowser(const IPCTabContext& aContext) {
  // (PopupIPCTabContext lets the child process prove that it has access to
  // the app it's trying to open.)
  // On e10s we also allow UnsafeTabContext to allow service workers to open
  // windows. This is enforced in MaybeInvalidTabContext.
  if (aContext.type() != IPCTabContext::TPopupIPCTabContext) {
    MOZ_CRASH_UNLESS_FUZZING(
        "Unexpected IPCTabContext type.  Aborting AllocPBrowserParent.");
    return false;
  }

  if (aContext.type() == IPCTabContext::TPopupIPCTabContext) {
    const PopupIPCTabContext& popupContext = aContext.get_PopupIPCTabContext();

    auto* opener = BrowserParent::GetFrom(popupContext.opener().AsParent());
    if (!opener) {
      MOZ_CRASH_UNLESS_FUZZING(
          "Got null opener from child; aborting AllocPBrowserParent.");
      return false;
    }
  }

  MaybeInvalidTabContext tc(aContext);
  if (!tc.IsValid()) {
    NS_ERROR(nsPrintfCString("Child passed us an invalid TabContext.  (%s)  "
                             "Aborting AllocPBrowserParent.",
                             tc.GetInvalidReason())
                 .get());
    return false;
  }

  return true;
}

static bool CloneIsLegal(ContentParent* aCp, CanonicalBrowsingContext& aSource,
                         CanonicalBrowsingContext& aTarget) {
  // Source and target must be in the same BCG
  if (NS_WARN_IF(aSource.Group() != aTarget.Group())) {
    return false;
  }
  // The source and target must be in different toplevel <browser>s
  if (NS_WARN_IF(aSource.Top() == aTarget.Top())) {
    return false;
  }

  // Neither source nor target must be toplevel.
  if (NS_WARN_IF(aSource.IsTop()) || NS_WARN_IF(aTarget.IsTop())) {
    return false;
  }

  // Both should be embedded by the same process.
  auto* sourceEmbedder = aSource.GetParentWindowContext();
  if (NS_WARN_IF(!sourceEmbedder) ||
      NS_WARN_IF(sourceEmbedder->GetContentParent() != aCp)) {
    return false;
  }

  auto* targetEmbedder = aTarget.GetParentWindowContext();
  if (NS_WARN_IF(!targetEmbedder) ||
      NS_WARN_IF(targetEmbedder->GetContentParent() != aCp)) {
    return false;
  }

  // All seems sane.
  return true;
}

mozilla::ipc::IPCResult ContentParent::RecvCloneDocumentTreeInto(
    const MaybeDiscarded<BrowsingContext>& aSource,
    const MaybeDiscarded<BrowsingContext>& aTarget, PrintData&& aPrintData) {
  if (aSource.IsNullOrDiscarded() || aTarget.IsNullOrDiscarded()) {
    return IPC_OK();
  }

  if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
    // All existing processes have potentially been slated for removal already,
    // such that any subsequent call to GetNewOrUsedLaunchingBrowserProcess
    // (normally supposed to find an existing process here) will try to create
    // a new process (but fail) that nobody would ever really use. Let's avoid
    // this together with the expensive CloneDocumentTreeInto operation.
    return IPC_OK();
  }

  RefPtr source = aSource.get_canonical();
  RefPtr target = aTarget.get_canonical();

  if (!CloneIsLegal(this, *source, *target)) {
    return IPC_FAIL(this, "Illegal subframe clone");
  }

  if (aPrintData.remotePrintJob()) {
    return IPC_FAIL(this, "Shouldn't pass print jobs around this IPC call");
  }

  ContentParent* cp = source->GetContentParent();
  if (NS_WARN_IF(!cp)) {
    return IPC_OK();
  }

  if (NS_WARN_IF(cp->GetRemoteType() == GetRemoteType())) {
    // Wanted to switch to a target browsing context that's already local again.
    // See bug 1676996 for how this can happen.
    //
    // Dropping the switch on the floor seems fine for this case, though we
    // could also try to clone the local document.
    //
    // If the remote type matches & it's in the same group (which was confirmed
    // by CloneIsLegal), it must be the exact same process.
    MOZ_DIAGNOSTIC_ASSERT(cp == this);
    return IPC_OK();
  }

  target->CloneDocumentTreeInto(source, cp->GetRemoteType(),
                                std::move(aPrintData));
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvUpdateRemotePrintSettings(
    const MaybeDiscarded<BrowsingContext>& aTarget, PrintData&& aPrintData) {
  if (aTarget.IsNullOrDiscarded()) {
    return IPC_OK();
  }

  auto* target = aTarget.get_canonical();
  auto* bp = target->GetBrowserParent();
  if (NS_WARN_IF(!bp)) {
    return IPC_OK();
  }

  (void)bp->SendUpdateRemotePrintSettings(aPrintData);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvConstructPopupBrowser(
    ManagedEndpoint<PBrowserParent>&& aBrowserEp,
    ManagedEndpoint<PWindowGlobalParent>&& aWindowEp, const TabId& aTabId,
    const IPCTabContext& aContext, const WindowGlobalInit& aInitialWindowInit,
    const uint32_t& aChromeFlags) {
  MOZ_ASSERT(XRE_IsParentProcess());

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

  if (!CanOpenBrowser(aContext)) {
    return IPC_FAIL(this, "CanOpenBrowser Failed");
  }

  RefPtr<CanonicalBrowsingContext> browsingContext =
      CanonicalBrowsingContext::Get(
          aInitialWindowInit.context().mBrowsingContextId);
  if (!browsingContext || browsingContext->IsDiscarded()) {
    return IPC_FAIL(this, "Null or discarded initial BrowsingContext");
  }
  if (!browsingContext->Group()->IsKnownForChildID(OtherChildID())) {
    return IPC_FAIL(this, "Unknown BrowsingContextGroup for this process");
  }
  if (!aInitialWindowInit.principal()) {
    return IPC_FAIL(this, "Cannot create without valid initial principal");
  }

  if (!ValidatePrincipal(aInitialWindowInit.principal())) {
    return PrincipalValidationIpcFail(aInitialWindowInit.principal(), this,
                                      __func__);
  }

  if (browsingContext->GetBrowserParent()) {
    return IPC_FAIL(this, "BrowsingContext already has a BrowserParent");
  }

  uint32_t chromeFlags = aChromeFlags;
  TabId openerTabId(0);
  ContentParentId openerCpId(0);
  if (aContext.type() == IPCTabContext::TPopupIPCTabContext) {
    // CanOpenBrowser has ensured that the IPCTabContext is of
    // type PopupIPCTabContext, and that the opener BrowserParent is
    // reachable.
    const PopupIPCTabContext& popupContext = aContext.get_PopupIPCTabContext();
    auto* opener = BrowserParent::GetFrom(popupContext.opener().AsParent());
    openerTabId = opener->GetTabId();
    openerCpId = opener->Manager()->ChildID();

    // We must ensure that the private browsing and remoteness flags
    // match those of the opener.
    nsCOMPtr<nsILoadContext> loadContext = opener->GetLoadContext();
    if (!loadContext) {
      return IPC_FAIL(this, "Missing Opener LoadContext");
    }
    if (loadContext->UsePrivateBrowsing()) {
      chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW;
    }
    if (loadContext->UseRemoteSubframes()) {
      chromeFlags |= nsIWebBrowserChrome::CHROME_FISSION_WINDOW;
    }
  }

  // And because we're allocating a remote browser, of course the
  // window is remote.
  chromeFlags |= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW;

  if (NS_WARN_IF(!browsingContext->IsOwnedByProcess(ChildID()))) {
    return IPC_FAIL(this, "BrowsingContext Owned by Incorrect Process!");
  }

  MaybeInvalidTabContext tc(aContext);
  MOZ_ASSERT(tc.IsValid());

  RefPtr<WindowGlobalParent> initialWindow =
      WindowGlobalParent::CreateDisconnected(aInitialWindowInit, this);
  if (!initialWindow) {
    return IPC_FAIL(this, "Failed to create WindowGlobalParent");
  }

  auto parent = MakeRefPtr<BrowserParent>(this, aTabId, tc.GetTabContext(),
                                          browsingContext, chromeFlags);

  // The creation of PBrowser was triggered from content process through
  // window.open().
  // We need to register remote frame with the child generated tab id.
  ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
  if (!cpm || !cpm->RegisterRemoteFrame(parent)) {
    return IPC_FAIL(this, "RegisterRemoteFrame Failed");
  }

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

  if (NS_WARN_IF(!parent->BindPWindowGlobalEndpoint(std::move(aWindowEp),
                                                    initialWindow))) {
    return IPC_FAIL(this, "BindPWindowGlobalEndpoint failed");
  }

  browsingContext->SetCurrentBrowserParent(parent);

  initialWindow->Init();

  // When enabling input event prioritization, input events may preempt other
  // normal priority IPC messages. To prevent the input events preempt
  // PBrowserConstructor, we use an IPC 'RemoteIsReadyToHandleInputEvents' to
  // notify parent that BrowserChild is created. In this case, PBrowser is
  // initiated from content so that we can set BrowserParent as ready to handle
  // input
  parent->SetReadyToHandleInputEvents();
  return IPC_OK();
}

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

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

/* static */
void ContentParent::SendShutdownTimerCallback(nsITimer* aTimer,
                                              void* aClosure) {
  auto* self = static_cast<ContentParent*>(aClosure);
  self->AsyncSendShutDownMessage();
}

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

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

void ContentParent::GeneratePairedMinidump(const char* aReason) {
  // We're about to kill the child process associated with this content.
  // Something has gone wrong to get us here, so we generate a minidump
  // of the parent and child for submission to the crash server unless we're
  // already shutting down.
  if (mCrashReporter &&
      !AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed) &&
      StaticPrefs::dom_ipc_tabs_createKillHardCrashReports_AtStartup()) {
    // GeneratePairedMinidump creates two minidumps for us - the main
    // one is for the content process we're about to kill, and the other
    // one is for the main browser process. That second one is the extra
    // minidump tagging along, so we have to tell the crash reporter that
    // it exists and is being appended.
    nsAutoCString additionalDumps("browser");
    mCrashReporter->AddAnnotationNSCString(
        CrashReporter::Annotation::additional_minidumps, additionalDumps);
    nsDependentCString reason(aReason);
    mCrashReporter->AddAnnotationNSCString(
        CrashReporter::Annotation::ipc_channel_error, reason);

    // Generate the report and insert into the queue for submittal.
    if (mCrashReporter->GenerateMinidumpAndPair(mSubprocess, "browser"_ns)) {
      mCrashReporter->FinalizeCrashReport();
      mCreatedPairedMinidumps = true;
    }
  }
}

void ContentParent::HandleOrphanedMinidump(nsString* aDumpId) {
  if (CrashReporter::FinalizeOrphanedMinidump(
          OtherChildID(), GeckoProcessType_Content, aDumpId)) {
    CrashReporterHost::RecordCrash(GeckoProcessType_Content,
                                   nsICrashService::CRASH_TYPE_CRASH, *aDumpId);
  } else {
    NS_WARNING(nsPrintfCString("content process childID = %d pid = %" PRIPID
                               " crashed without leaving a minidump behind",
                               OtherChildID(), OtherPid())
                   .get());
  }
}

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

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

  RemoveShutdownBlockers();
  nsCString reason = nsDependentCString(aReason);

  // If we find mIsNotifiedShutdownSuccess there is no reason to blame this
  // content process, most probably our parent process is just slow in
  // processing its own main thread queue.
  if (!mIsNotifiedShutdownSuccess) {
    GeneratePairedMinidump(aReason);
  } else {
    reason = nsDependentCString("KillHard after IsNotifiedShutdownSuccess.");
  }
  glean::subprocess::kill_hard.Get(reason).Add(1);

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

  if (!KillProcess(otherProcessHandle, base::PROCESS_END_KILLED_BY_USER)) {
    if (mCrashReporter) {
      mCrashReporter->DeleteCrashReport();
    }
    NS_WARNING("failed to kill subprocess!");
  }

  if (mSubprocess) {
    MOZ_LOG(
        ContentParent::GetLog(), LogLevel::Verbose,
        ("KillHard Subprocess(%s): ContentParent id=%p mSubprocess id=%p "
         "handle "
         "%" PRIuPTR,
         aReason, this, mSubprocess,
         mSubprocess ? (uintptr_t)mSubprocess->GetChildProcessHandle() : -1));
    mSubprocess->SetAlreadyDead();
  }

  // After we've killed the remote process, also ensure we induce a connection
  // error in the IPC channel to immediately stop all IPC communication on this
  // channel.
  if (CanSend()) {
    GetIPCChannel()->InduceConnectionError();
  }

  // EnsureProcessTerminated has responsibilty for closing otherProcessHandle.
  XRE_GetAsyncIOEventTarget()->Dispatch(
      NewRunnableFunction("EnsureProcessTerminatedRunnable",
                          &ProcessWatcher::EnsureProcessTerminated,
                          otherProcessHandle, /*force=*/true));
}

void ContentParent::FriendlyName(nsAString& aName, bool aAnonymize) {
  aName.Truncate();
  if (mIsForBrowser) {
    aName.AssignLiteral("Browser");
  } else if (aAnonymize) {
    aName.AssignLiteral("<anonymized-name>");
  } else {
    aName.AssignLiteral("???");
  }
}

mozilla::ipc::IPCResult ContentParent::RecvInitCrashReporter(
    const CrashReporter::CrashReporterInitArgs& aInitArgs) {
  mCrashReporter = MakeUnique<CrashReporterHost>(GeckoProcessType_Content,
                                                 OtherChildID(), aInitArgs);
  return IPC_OK();
}

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

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

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

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

bool ContentParent::SendRequestMemoryReport(
    const uint32_t& aGeneration, const bool& aAnonymize,
    const bool& aMinimizeMemoryUsage, const Maybe<FileDescriptor>& aDMDFile) {
  // This automatically cancels the previous request.
  mMemoryReportRequest = MakeUnique<MemoryReportRequestHost>(aGeneration);
  RefPtr<ContentParent> self(this);
  PContentParent::SendRequestMemoryReport(aGeneration, aAnonymize,
                                          aMinimizeMemoryUsage, aDMDFile)
      ->Then(
          GetCurrentSerialEventTarget(), __func__,
          [self](uint32_t aGeneration2) {
            if (self->mMemoryReportRequest) {
              self->mMemoryReportRequest->Finish(aGeneration2);
              self->mMemoryReportRequest = nullptr;
            }
          },
          [self](mozilla::ipc::ResponseRejectReason) {
            self->mMemoryReportRequest = nullptr;
          });
  return true;
}

mozilla::ipc::IPCResult ContentParent::RecvAddMemoryReport(
    const MemoryReport& aReport) {
  if (mMemoryReportRequest) {
    mMemoryReportRequest->RecvReport(aReport);
  }
  return IPC_OK();
}

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

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

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

PScriptCacheParent* ContentParent::AllocPScriptCacheParent(
    const FileDescOrError& cacheFile, const bool& wantCacheData) {
  return new loader::ScriptCacheParent(wantCacheData);
}

bool ContentParent::DeallocPScriptCacheParent(PScriptCacheParent* cache) {
  delete static_cast<loader::ScriptCacheParent*>(cache);
  return true;
}

mozilla::ipc::IPCResult ContentParent::RecvUpdateScriptCacheEverHitTelemetry(
    const uint64_t& aChildId, const uint32_t& aRate) {
  mozilla::dom::SharedScriptCache::RecvUpdateEverHitTelemetry(aChildId, aRate);
  return IPC_OK();
}

already_AddRefed<PNeckoParent> ContentParent::AllocPNeckoParent() {
  RefPtr<NeckoParent> actor = new NeckoParent();
  return actor.forget();
}

mozilla::ipc::IPCResult ContentParent::RecvInitStreamFilter(
    const uint64_t& aChannelId, const nsAString& aAddonId,
    InitStreamFilterResolver&& aResolver) {
  extensions::StreamFilterParent::Create(this, aChannelId, aAddonId)
      ->Then(
          GetCurrentSerialEventTarget(), __func__,
          [aResolver](mozilla::ipc::Endpoint<PStreamFilterChild>&& aEndpoint) {
            aResolver(std::move(aEndpoint));
          },
          [aResolver](bool aDummy) {
            aResolver(mozilla::ipc::Endpoint<PStreamFilterChild>());
          });

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvAddSecurityState(
    const MaybeDiscarded<WindowContext>& aContext, uint32_t aStateFlags) {
  if (aContext.IsNullOrDiscarded()) {
    return IPC_OK();
  }

  aContext.get()->AddSecurityState(aStateFlags);
  return IPC_OK();
}

already_AddRefed<PExternalHelperAppParent>
ContentParent::AllocPExternalHelperAppParent(
    nsIURI* uri, const mozilla::net::LoadInfoArgs& aLoadInfoArgs,
    const nsACString& aMimeContentType, const nsACString& aContentDisposition,
    const uint32_t& aContentDispositionHint,
    const nsAString& aContentDispositionFilename, const bool& aForceSave,
    const int64_t& aContentLength, nsIURI* aReferrer,
    const MaybeDiscarded<BrowsingContext>& aContext) {
  RefPtr<ExternalHelperAppParent> parent = new ExternalHelperAppParent(
      uri, aContentLength, aContentDisposition, aContentDispositionHint,
      aContentDispositionFilename);
  return parent.forget();
}

mozilla::ipc::IPCResult ContentParent::RecvPExternalHelperAppConstructor(
    PExternalHelperAppParent* actor, nsIURI* uri,
    const LoadInfoArgs& loadInfoArgs, const nsACString& aMimeContentType,
    const nsACString& aContentDisposition,
    const uint32_t& aContentDispositionHint,
    const nsAString& aContentDispositionFilename, const bool& aForceSave,
    const int64_t& aContentLength, nsIURI* aReferrer,
    const MaybeDiscarded<BrowsingContext>& aContext) {
  // A content process must never be able to drive a native helper-app launch
  // of a local file it chose: that decision has to be bound to a channel the
  // process was actually allowed to open. A file:// URI can only be loaded in
  // a file content process (mirrors ValidatePrincipalCouldPotentiallyBeLoadedBy
  // and the sandboxing policy enforced in ProcessIsolation), so reject a
  // file:// URI coming from any other process.
  if (uri && uri->SchemeIs("file") &&
      StaticPrefs::browser_tabs_remote_separateFileUriProcess() &&
      GetRemoteType() != FILE_REMOTE_TYPE) {
    return IPC_FAIL(this, "Non-file process sent a file:// URI.");
  }

  BrowsingContext* context = aContext.IsDiscarded() ? nullptr : aContext.get();
  if (!static_cast<ExternalHelperAppParent*>(actor)->Init(
          loadInfoArgs, aMimeContentType, aForceSave, aReferrer, context)) {
    return IPC_FAIL(this, "Init failed.");
  }
  return IPC_OK();
}

already_AddRefed<PHandlerServiceParent>
ContentParent::AllocPHandlerServiceParent() {
  RefPtr<HandlerServiceParent> actor = new HandlerServiceParent();
  return actor.forget();
}

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

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

#ifdef MOZ_WEBSPEECH
already_AddRefed<PSpeechSynthesisParent>
ContentParent::AllocPSpeechSynthesisParent() {
  if (!StaticPrefs::media_webspeech_synth_enabled()) {
    return nullptr;
  }
  RefPtr<SpeechSynthesisParent> actor = new SpeechSynthesisParent();
  return actor.forget();
}

mozilla::ipc::IPCResult ContentParent::RecvPSpeechSynthesisConstructor(
    PSpeechSynthesisParent* aActor) {
  if (!static_cast<SpeechSynthesisParent*>(aActor)->SendInit()) {
    return IPC_FAIL(this, "SpeechSynthesisParent::SendInit failed.");
  }
  return IPC_OK();
}
#endif

mozilla::ipc::IPCResult ContentParent::RecvStartVisitedQueries(
    const nsTArray<RefPtr<nsIURI>>& aUris) {
  nsCOMPtr<IHistory> history = components::History::Service();
  if (!history) {
    return IPC_OK();
  }
  for (const auto& uri : aUris) {
    if (NS_WARN_IF(!uri)) {
      continue;
    }
    history->ScheduleVisitedQuery(uri, this);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvSetURITitle(nsIURI* uri,
                                                       const nsAString& title) {
  if (!uri) {
    return IPC_FAIL(this, "uri must not be null.");
  }
  nsCOMPtr<IHistory> history = components::History::Service();
  if (history) {
    history->SetURITitle(uri, title);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvIsSecureURI(
    nsIURI* aURI, const OriginAttributes& aOriginAttributes,
    bool* aIsSecureURI) {
  nsCOMPtr<nsISiteSecurityService> sss(do_GetService(NS_SSSERVICE_CONTRACTID));
  if (!sss) {
    return IPC_FAIL(this, "Failed to get nsISiteSecurityService.");
  }
  if (!aURI) {
    return IPC_FAIL(this, "aURI must not be null.");
  }
  nsresult rv = sss->IsSecureURI(aURI, aOriginAttributes, aIsSecureURI);
  if (NS_FAILED(rv)) {
    return IPC_FAIL(this, "IsSecureURI failed.");
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvAccumulateMixedContentHSTS(
    nsIURI* aURI, const bool& aActive,
    const OriginAttributes& aOriginAttributes) {
  if (!aURI) {
    return IPC_FAIL(this, "aURI must not be null.");
  }
  nsMixedContentBlocker::AccumulateMixedContentHSTS(aURI, aActive,
                                                    aOriginAttributes);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvLoadURIExternal(
    NotNull<nsIURI*> uri, NotNull<nsIPrincipal*> aTriggeringPrincipal,
    nsIPrincipal* aRedirectPrincipal,
    const MaybeDiscarded<BrowsingContext>& aContext,
    bool aWasExternallyTriggered, bool aHasValidUserGestureActivation,
    bool aNewWindowTarget) {
  if (aContext.IsDiscarded()) {
    return IPC_OK();
  }

  if (!ValidatePrincipal(aTriggeringPrincipal)) {
    return PrincipalValidationIpcFail(aTriggeringPrincipal, this, __func__);
  }

  if (!ValidatePrincipal(aRedirectPrincipal,
                         {ValidatePrincipalOptions::AllowNullPtr})) {
    return PrincipalValidationIpcFail(aRedirectPrincipal, this, __func__);
  }

  nsCOMPtr<nsIExternalProtocolService> extProtService(
      do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID));
  if (!extProtService) {
    return IPC_OK();
  }

  BrowsingContext* bc = aContext.get();
  extProtService->LoadURI(uri, aTriggeringPrincipal, aRedirectPrincipal, bc,
                          aWasExternallyTriggered,
                          aHasValidUserGestureActivation, aNewWindowTarget);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvExtProtocolChannelConnectParent(
    const uint64_t& registrarId) {
  nsresult rv;

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

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

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

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

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvSyncMessage(
    const nsAString& aMsg, NotNull<StructuredCloneData*> aData,
    nsTArray<NotNull<RefPtr<StructuredCloneData>>>* aRetvals) {
  AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING("ContentParent::RecvSyncMessage",
                                             OTHER, aMsg);
  MMPrinter::Print("ContentParent::RecvSyncMessage", aMsg, aData);

  RefPtr<nsFrameMessageManager> ppm = mMessageManager;
  if (ppm) {
    ppm->ReceiveMessage(ppm, nullptr, aMsg, true, aData, aRetvals);
  }
  return IPC_OK();
}

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

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

MOZ_CAN_RUN_SCRIPT
static int32_t AddGeolocationListener(
    nsIDOMGeoPositionCallback* watcher,
    nsIDOMGeoPositionErrorCallback* errorCallBack, bool highAccuracy) {
  RefPtr<Geolocation> geo = Geolocation::NonWindowSingleton();

  UniquePtr<PositionOptions> options = MakeUnique<PositionOptions>();
  options->mTimeout = 0;
  options->mMaximumAge = 0;
  options->mEnableHighAccuracy = highAccuracy;
  return geo->WatchPosition(watcher, errorCallBack, std::move(options));
}

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

mozilla::ipc::IPCResult ContentParent::RecvRemoveGeolocationListener() {
  if (mGeolocationWatchID != -1) {
    RefPtr<Geolocation> geo = Geolocation::NonWindowSingleton();
    if (geo) {
      geo->ClearWatch(mGeolocationWatchID);
    }
    mGeolocationWatchID = -1;
  }
  return IPC_OK();
}

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

NS_IMETHODIMP
ContentParent::HandleEvent(nsIDOMGeoPosition* postion) {
  (void)SendGeolocationUpdate(postion);
  return NS_OK;
}

NS_IMETHODIMP
ContentParent::HandleEvent(GeolocationPositionError* positionError) {
  (void)SendGeolocationError(positionError->Code());
  return NS_OK;
}

mozilla::ipc::IPCResult ContentParent::RecvConsoleMessage(
    const nsAString& aMessage) {
  nsresult rv;
  nsCOMPtr<nsIConsoleService> consoleService =
      do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv);
  if (NS_SUCCEEDED(rv)) {
    RefPtr<nsConsoleMessage> msg(new nsConsoleMessage(aMessage));
    msg->SetIsForwardedFromContentProcess(true);
    consoleService->LogMessageWithMode(msg, nsIConsoleService::SuppressLog);
  }
  return IPC_OK();
}

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

  RefPtr<WindowGlobalParent> parent =
      WindowGlobalParent::GetByInnerWindowId(loadInfoArgs.innerWindowID());
  if (!parent || !parent->GetContentParent()) {
    return IPC_OK();
  }

  MOZ_ASSERT(parent->GetContentParent() != this,
             "No need to bounce around if in the same process");

  (void)parent->GetContentParent()->SendReportFrameTimingData(
      loadInfoArgs, entryName, initiatorType, aData);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvScriptError(
    const nsAString& aMessage, const nsACString& aSourceName,
    const uint32_t& aLineNumber, const uint32_t& aColNumber,
    const uint32_t& aFlags, const nsACString& aCategory,
    const bool& aIsFromPrivateWindow, const uint64_t& aInnerWindowId,
    const bool& aIsFromChromeContext) {
  return RecvScriptErrorInternal(aMessage, aSourceName, aLineNumber, aColNumber,
                                 aFlags, aCategory, aIsFromPrivateWindow,
                                 aIsFromChromeContext);
}

mozilla::ipc::IPCResult ContentParent::RecvScriptErrorWithStack(
    const nsAString& aMessage, const nsACString& aSourceName,
    const uint32_t& aLineNumber, const uint32_t& aColNumber,
    const uint32_t& aFlags, const nsACString& aCategory,
    const bool& aIsFromPrivateWindow, const bool& aIsFromChromeContext,
    NotNull<StructuredCloneData*> aStack) {
  return RecvScriptErrorInternal(aMessage, aSourceName, aLineNumber, aColNumber,
                                 aFlags, aCategory, aIsFromPrivateWindow,
                                 aIsFromChromeContext, aStack);
}

mozilla::ipc::IPCResult ContentParent::RecvScriptErrorInternal(
    const nsAString& aMessage, const nsACString& aSourceName,
    const uint32_t& aLineNumber, const uint32_t& aColNumber,
    const uint32_t& aFlags, const nsACString& aCategory,
    const bool& aIsFromPrivateWindow, const bool& aIsFromChromeContext,
    StructuredCloneData* aStack) {
  nsresult rv;
  nsCOMPtr<nsIConsoleService> consoleService =
      do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv);
  if (NS_FAILED(rv)) {
    return IPC_OK();
  }

  nsCOMPtr<nsIScriptError> msg;

  if (aStack) {
    AutoJSAPI jsapi;
    if (NS_WARN_IF(!jsapi.Init(xpc::PrivilegedJunkScope()))) {
      MOZ_CRASH();
    }
    JSContext* cx = jsapi.cx();

    JS::Rooted<JS::Value> stack(cx);
    IgnoredErrorResult rv;
    aStack->Read(cx, &stack, rv);
    if (rv.Failed() || !stack.isObject()) {
      return IPC_OK();
    }

    JS::Rooted<JSObject*> stackObj(cx, &stack.toObject());
    if (!JS::IsUnwrappedSavedFrame(stackObj)) {
      return IPC_FAIL(this, "Unexpected object");
    }

    JS::Rooted<JSObject*> stackGlobal(cx, JS::GetNonCCWObjectGlobal(stackObj));
    msg = new nsScriptErrorWithStack(JS::NothingHandleValue, stackObj,
                                     stackGlobal);
  } else {
    msg = new nsScriptError();
  }

  rv = msg->Init(aMessage, aSourceName, aLineNumber, aColNumber, aFlags,
                 aCategory, aIsFromPrivateWindow, aIsFromChromeContext);
  if (NS_FAILED(rv)) return IPC_OK();

  msg->SetIsForwardedFromContentProcess(true);

  consoleService->LogMessageWithMode(msg, nsIConsoleService::SuppressLog);
  return IPC_OK();
}

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

nsresult ContentParent::DoSendAsyncMessage(
    const nsAString& aMessage, NotNull<StructuredCloneData*> aData) {
  if (!SendAsyncMessage(aMessage, aData)) {
    return NS_ERROR_UNEXPECTED;
  }
  return NS_OK;
}

mozilla::ipc::IPCResult ContentParent::RecvCopyFavicon(
    nsIURI* aOldURI, nsIURI* aNewURI, const bool& aInPrivateBrowsing) {
  if (!aOldURI) {
    return IPC_FAIL(this, "aOldURI should not be null");
  }
  if (!aNewURI) {
    return IPC_FAIL(this, "aNewURI should not be null");
  }

  nsDocShell::CopyFavicon(aOldURI, aNewURI, aInPrivateBrowsing);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvFindImageText(
    IPCImage&& aImage, nsTArray<nsCString>&& aLanguages,
    FindImageTextResolver&& aResolver) {
  if (!TextRecognition::IsSupported() ||
      !Preferences::GetBool("dom.text-recognition.enabled")) {
    return IPC_FAIL(this, "Text recognition not available.");
  }

  RefPtr<DataSourceSurface> surf = nsContentUtils::IPCImageToSurface(aImage);
  if (!surf) {
    aResolver(TextRecognitionResultOrError("Failed to read image"_ns));
    return IPC_OK();
  }
  TextRecognition::FindText(*surf, aLanguages)
      ->Then(
          GetCurrentSerialEventTarget(), __func__,
          [resolver = std::move(aResolver)](
              TextRecognition::NativePromise::ResolveOrRejectValue&& aValue) {
            if (aValue.IsResolve()) {
              resolver(TextRecognitionResultOrError(aValue.ResolveValue()));
            } else {
              resolver(TextRecognitionResultOrError(aValue.RejectValue()));
            }
          });
  return IPC_OK();
}

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

mozilla::ipc::IPCResult ContentParent::RecvAddIdleObserver(
    const uint64_t& aObserverId, const uint32_t& aIdleTimeInS) {
  nsresult rv;
  nsCOMPtr<nsIUserIdleService> idleService =
      do_GetService("@mozilla.org/widget/useridleservice;1", &rv);
  NS_ENSURE_SUCCESS(rv, IPC_FAIL(this, "Failed to get UserIdleService."));

  RefPtr<ParentIdleListener> listener =
      new ParentIdleListener(this, aObserverId, aIdleTimeInS);
  rv = idleService->AddIdleObserver(listener, aIdleTimeInS);
  NS_ENSURE_SUCCESS(rv, IPC_FAIL(this, "AddIdleObserver failed."));
  mIdleListeners.AppendElement(listener);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvRemoveIdleObserver(
    const uint64_t& aObserverId, const uint32_t& aIdleTimeInS) {
  RefPtr<ParentIdleListener> listener;
  for (int32_t i = mIdleListeners.Length() - 1; i >= 0; --i) {
    listener = static_cast<ParentIdleListener*>(mIdleListeners[i].get());
    if (listener->mObserver == aObserverId && listener->mTime == aIdleTimeInS) {
      nsresult rv;
      nsCOMPtr<nsIUserIdleService> idleService =
          do_GetService("@mozilla.org/widget/useridleservice;1", &rv);
      NS_ENSURE_SUCCESS(rv, IPC_FAIL(this, "Failed to get UserIdleService."));
      idleService->RemoveIdleObserver(listener, aIdleTimeInS);
      mIdleListeners.RemoveElementAt(i);
      break;
    }
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvBackUpXResources(
    const FileDescriptor& aXSocketFd) {
#ifndef MOZ_X11
  MOZ_CRASH("This message only makes sense on X11 platforms");
#else
  MOZ_ASSERT(!mChildXSocketFdDup, "Already backed up X resources??");
  if (aXSocketFd.IsValid()) {
    mChildXSocketFdDup = aXSocketFd.ClonePlatformHandle();
  }
#endif
  return IPC_OK();
}

class AnonymousTemporaryFileRequestor final : public Runnable {
 public:
  AnonymousTemporaryFileRequestor(ContentParent* aCP, const uint64_t& aID)
      : Runnable("dom::AnonymousTemporaryFileRequestor"),
        mCP(aCP),
        mID(aID),
        mRv(NS_OK),
        mPRFD(nullptr) {}

  NS_IMETHOD Run() override {
    if (NS_IsMainThread()) {
      FileDescOrError result;
      if (NS_WARN_IF(NS_FAILED(mRv))) {
        // Returning false will kill the child process; instead
        // propagate the error and let the child handle it.
        result = mRv;
      } else {
        result = FileDescriptor(FileDescriptor::PlatformHandleType(
            PR_FileDesc2NativeHandle(mPRFD)));
        // The FileDescriptor object owns a duplicate of the file handle; we
        // must close the original (and clean up the NSPR descriptor).
        PR_Close(mPRFD);
      }
      (void)mCP->SendProvideAnonymousTemporaryFile(mID, result);
      // It's important to release this reference while wr're on the main
      // thread!
      mCP = nullptr;
    } else {
      mRv = NS_OpenAnonymousTemporaryFile(&mPRFD);
      NS_DispatchToMainThread(this);
    }
    return NS_OK;
  }

 private:
  RefPtr<ContentParent> mCP;
  uint64_t mID;
  nsresult mRv;
  PRFileDesc* mPRFD;
};

mozilla::ipc::IPCResult ContentParent::RecvRequestAnonymousTemporaryFile(
    const uint64_t& aID) {
  // Make sure to send a callback to the child if we bail out early.
  nsresult rv = NS_OK;
  RefPtr<ContentParent> self(this);
  auto autoNotifyChildOnError = MakeScopeExit([&, self]() {
    if (NS_FAILED(rv)) {
      FileDescOrError result(rv);
      (void)self->SendProvideAnonymousTemporaryFile(aID, result);
    }
  });

  // We use a helper runnable to open the anonymous temporary file on the IO
  // thread.  The same runnable will call us back on the main thread when the
  // file has been opened.
  nsCOMPtr<nsIEventTarget> target =
      do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
  if (!target) {
    return IPC_OK();
  }

  rv = target->Dispatch(new AnonymousTemporaryFileRequestor(this, aID),
                        NS_DISPATCH_NORMAL);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return IPC_OK();
  }

  rv = NS_OK;
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvCreateAudioIPCConnection(
    CreateAudioIPCConnectionResolver&& aResolver) {
  FileDescriptor fd = CubebUtils::CreateAudioIPCConnection(OtherPid());
  FileDescOrError result;
  if (fd.IsValid()) {
    result = fd;
  } else {
    result = NS_ERROR_FAILURE;
  }
  aResolver(result);
  return IPC_OK();
}

already_AddRefed<extensions::PExtensionsParent>
ContentParent::AllocPExtensionsParent() {
  return MakeAndAddRef<extensions::ExtensionsParent>();
}

void ContentParent::NotifyUpdatedDictionaries() {
  RefPtr<mozSpellChecker> spellChecker(mozSpellChecker::Create());
  MOZ_ASSERT(spellChecker, "No spell checker?");

  nsTArray<nsCString> dictionaries;
  spellChecker->GetDictionaryList(&dictionaries);

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

void ContentParent::NotifyUpdatedFonts(bool aFullRebuild) {
  if (gfxPlatformFontList::PlatformFontList()->SharedFontList()) {
    for (auto* cp : AllProcesses(eLive)) {
      (void)cp->SendRebuildFontList(aFullRebuild);
    }
    return;
  }

  SystemFontList fontList;
  gfxPlatform::GetPlatform()->ReadSystemFontList(&fontList);

  for (auto* cp : AllProcesses(eLive)) {
    (void)cp->SendUpdateFontList(fontList);
  }
}

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

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

PContentPermissionRequestParent*
ContentParent::AllocPContentPermissionRequestParent(
    const nsTArray<PermissionRequest>& aRequests, nsIPrincipal* aPrincipal,
    nsIPrincipal* aTopLevelPrincipal, const bool& aIsHandlingUserInput,
    const bool& aMaybeUnsafePermissionDelegate, const TabId& aTabId,
    const bool& aIgnoreAllowSitePermission) {
  RefPtr<BrowserParent> tp;
  ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
  if (cpm) {
    tp =
        cpm->GetTopLevelBrowserParentByProcessAndTabId(this->ChildID(), aTabId);
  }
  if (!tp) {
    return nullptr;
  }

  nsIPrincipal* topPrincipal = aTopLevelPrincipal;
  if (!topPrincipal) {
    nsCOMPtr<nsIPrincipal> principal = tp->GetContentPrincipal();
    topPrincipal = principal;
  }
  return nsContentPermissionUtils::CreateContentPermissionRequestParent(
      tp->GetOwnerElement(), aPrincipal, topPrincipal, aIsHandlingUserInput,
      aMaybeUnsafePermissionDelegate, aTabId, aIgnoreAllowSitePermission);
}

mozilla::ipc::IPCResult ContentParent::RecvPContentPermissionRequestConstructor(
    PContentPermissionRequestParent* aActor,
    nsTArray<PermissionRequest>&& aRequests, nsIPrincipal* aPrincipal,
    nsIPrincipal* aTopLevelPrincipal, const bool& aIsHandlingUserInput,
    const bool& aMaybeUnsafePermissionDelegate, const TabId& tabId,
    const bool& aIgnoreAllowSitePermission) {
  nsContentPermissionUtils::InitContentPermissionRequestParent(
      aActor, std::move(aRequests));
  return IPC_OK();
}

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

already_AddRefed<PWebBrowserPersistDocumentParent>
ContentParent::AllocPWebBrowserPersistDocumentParent(
    PBrowserParent* aBrowser, const MaybeDiscarded<BrowsingContext>& aContext) {
  return MakeAndAddRef<WebBrowserPersistDocumentParent>();
}

mozilla::ipc::IPCResult ContentParent::CommonCreateWindow(
    PBrowserParent* aThisTab, BrowsingContext& aParent, bool aSetOpener,
    const uint32_t& aChromeFlags, const bool& aCalledFromJS,
    const bool& aForPrinting, const bool& aForWindowDotPrint,
    const bool& aIsTopLevelCreatedByWebContent, nsIURI* aURIToLoad,
    const nsACString& aFeatures, const UserActivation::Modifiers& aModifiers,
    BrowserParent* aNextRemoteBrowser, nsresult& aResult,
    nsCOMPtr<nsIRemoteTab>& aNewRemoteTab, bool* aWindowIsNew,
    int32_t& aOpenLocation, nsIPrincipal* aTriggeringPrincipal,
    nsIReferrerInfo* aReferrerInfo, nsIPolicyContainer* aPolicyContainer,
    const OriginAttributes& aOriginAttributes, bool aUserActivation,
    bool aTextDirectiveUserActivation) {
  // The content process should never be in charge of computing whether or
  // not a window should be private - the parent will do that.
  const uint32_t badFlags = nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW |
                            nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW |
                            nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME;
  if (!!(aChromeFlags & badFlags)) {
    return IPC_FAIL(this, "Forbidden aChromeFlags passed");
  }

  RefPtr<nsOpenWindowInfo> openInfo = new nsOpenWindowInfo();
  openInfo->mForceNoOpener = !aSetOpener;
  openInfo->mParent = &aParent;
  openInfo->mIsRemote = true;
  openInfo->mIsForPrinting = aForPrinting;
  openInfo->mIsForWindowDotPrint = aForWindowDotPrint;
  openInfo->mNextRemoteBrowser = aNextRemoteBrowser;
  openInfo->mIsTopLevelCreatedByWebContent = aIsTopLevelCreatedByWebContent;
  openInfo->mHasValidUserGestureActivation = aUserActivation;
  openInfo->mTextDirectiveUserActivation = aTextDirectiveUserActivation;
  openInfo->mPrincipalToInheritForAboutBlank = aTriggeringPrincipal;
  MOZ_ASSERT(
      aOriginAttributes == openInfo->GetOriginAttributes(),
      "Open window info gets the expected origin attributes from principal");

  MOZ_ASSERT_IF(aForWindowDotPrint, aForPrinting);

  RefPtr<BrowserParent> topParent = BrowserParent::GetFrom(aThisTab);
  while (topParent && topParent->GetBrowserBridgeParent()) {
    topParent = topParent->GetBrowserBridgeParent()->Manager();
  }
  RefPtr<BrowserHost> thisBrowserHost =
      topParent ? topParent->GetBrowserHost() : nullptr;
  MOZ_ASSERT_IF(topParent, thisBrowserHost);
  RefPtr<BrowsingContext> topBC =
      topParent ? topParent->GetBrowsingContext() : nullptr;
  MOZ_ASSERT_IF(topParent, topBC);

  // The content process should have set its remote and fission flags correctly.
  if (topBC) {
    if ((!!(aChromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW) !=
         topBC->UseRemoteTabs()) ||
        (!!(aChromeFlags & nsIWebBrowserChrome::CHROME_FISSION_WINDOW) !=
         topBC->UseRemoteSubframes())) {
      return IPC_FAIL(this, "Unexpected aChromeFlags passed");
    }

    if (!aOriginAttributes.EqualsIgnoringFPD(topBC->OriginAttributesRef())) {
      return IPC_FAIL(this, "Passed-in OriginAttributes does not match opener");
    }
  }

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

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

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

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

  // If we haven't found a chrome window to open in, just use the most recently
  // opened non PBM window.
  if (!outerWin) {
    // The parent was a private window but it's no longer available.
    if (aOriginAttributes.mPrivateBrowsingId !=
        nsIScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID) {
      aResult = NS_ERROR_FAILURE;
      return IPC_OK();
    }

    outerWin = nsContentUtils::GetMostRecentNonPBWindow();
    if (NS_WARN_IF(!outerWin)) {
      aResult = NS_ERROR_FAILURE;
      return IPC_OK();
    }

    if (nsGlobalWindowOuter::Cast(outerWin)->IsChromeWindow()) {
      browserDOMWin =
          nsGlobalWindowOuter::Cast(outerWin)->GetBrowserDOMWindow();
    }
  }

  aOpenLocation = nsWindowWatcher::GetWindowOpenLocation(
      outerWin, aChromeFlags, aModifiers, aCalledFromJS, aForPrinting);

  MOZ_ASSERT(aOpenLocation == nsIBrowserDOMWindow::OPEN_NEWTAB ||
             aOpenLocation == nsIBrowserDOMWindow::OPEN_NEWTAB_BACKGROUND ||
             aOpenLocation == nsIBrowserDOMWindow::OPEN_NEWTAB_FOREGROUND ||
             aOpenLocation == nsIBrowserDOMWindow::OPEN_NEWWINDOW ||
             aOpenLocation == nsIBrowserDOMWindow::OPEN_PRINT_BROWSER);

  if (NS_WARN_IF(!browserDOMWin)) {
#ifdef MOZ_GECKOVIEW
    // This might be print preview, but GeckoView doesn't support yet.
    aResult = NS_ERROR_FAILURE;
    return IPC_OK();
#else
    // Opening in the same window or headless requires an nsIBrowserDOMWindow.
    aOpenLocation = nsIBrowserDOMWindow::OPEN_NEWWINDOW;
#endif
  }

  if (aOpenLocation == nsIBrowserDOMWindow::OPEN_NEWTAB ||
      aOpenLocation == nsIBrowserDOMWindow::OPEN_NEWTAB_BACKGROUND ||
      aOpenLocation == nsIBrowserDOMWindow::OPEN_NEWTAB_FOREGROUND ||
      aOpenLocation == nsIBrowserDOMWindow::OPEN_PRINT_BROWSER) {
    RefPtr<Element> openerElement = do_QueryObject(frame);

    nsCOMPtr<nsIOpenURIInFrameParams> params =
        new nsOpenURIInFrameParams(openInfo, openerElement);
    params->SetReferrerInfo(aReferrerInfo);
    MOZ_ASSERT(aTriggeringPrincipal, "need a valid triggeringPrincipal");
    params->SetTriggeringPrincipal(aTriggeringPrincipal);
    params->SetPolicyContainer(aPolicyContainer);

    RefPtr<Element> el;

    aResult = browserDOMWin->CreateContentWindowInFrame(
        aURIToLoad, params, aOpenLocation, nsIBrowserDOMWindow::OPEN_NEW,
        VoidString(), getter_AddRefs(el));
    RefPtr<nsFrameLoaderOwner> frameLoaderOwner = do_QueryObject(el);
    if (NS_SUCCEEDED(aResult) && frameLoaderOwner) {
      RefPtr<nsFrameLoader> frameLoader = frameLoaderOwner->GetFrameLoader();
      if (frameLoader) {
        aNewRemoteTab = frameLoader->GetRemoteTab();
        // At this point, it's possible the inserted frameloader hasn't gone
        // through layout yet. To ensure that the dimensions that we send down
        // when telling the frameloader to display will be correct (instead of
        // falling back to a 10x10 default), we force layout if necessary to get
        // the most up-to-date dimensions. See bug 1358712 for details.
        frameLoader->ForceLayoutIfNecessary();
      }
    } else if (NS_SUCCEEDED(aResult) && !frameLoaderOwner) {
      // Fall through to the normal window opening code path when there is no
      // window which we can open a new tab in.
      aOpenLocation = nsIBrowserDOMWindow::OPEN_NEWWINDOW;
    } else {
      *aWindowIsNew = false;
    }

    // If we didn't retarget our window open into a new window, we should return
    // now.
    if (aOpenLocation != nsIBrowserDOMWindow::OPEN_NEWWINDOW) {
      return IPC_OK();
    }
  }

  nsCOMPtr<nsPIWindowWatcher> pwwatch =
      do_GetService(NS_WINDOWWATCHER_CONTRACTID, &aResult);
  if (NS_WARN_IF(NS_FAILED(aResult))) {
    return IPC_OK();
  }

  WindowFeatures features;
  features.Tokenize(aFeatures);

  aResult = pwwatch->OpenWindowWithRemoteTab(
      thisBrowserHost, features, aModifiers, aCalledFromJS, aParent.FullZoom(),
      openInfo, getter_AddRefs(aNewRemoteTab));
  if (NS_WARN_IF(NS_FAILED(aResult))) {
    return IPC_OK();
  }

  MOZ_ASSERT(aNewRemoteTab);
  RefPtr<BrowserHost> newBrowserHost = BrowserHost::GetFrom(aNewRemoteTab);

  // At this point, it's possible the inserted frameloader hasn't gone through
  // layout yet. To ensure that the dimensions that we send down when telling
  // the frameloader to display will be correct (instead of falling back to a
  // 10x10 default), we force layout if necessary to get the most up-to-date
  // dimensions. See bug 1358712 for details.
  nsCOMPtr<Element> frameElement = newBrowserHost->GetOwnerElement();
  MOZ_ASSERT(frameElement);
  if (nsWindowWatcher::HaveSpecifiedSize(features)) {
    // We want to flush the layout anyway because of the resize to the specified
    // size. (Bug 1793605).
    RefPtr<Document> chromeDoc = frameElement->OwnerDoc();
    MOZ_ASSERT(chromeDoc);
    chromeDoc->FlushPendingNotifications(FlushType::Layout);
  } else {
    RefPtr<nsFrameLoaderOwner> frameLoaderOwner = do_QueryObject(frameElement);
    MOZ_ASSERT(frameLoaderOwner);
    RefPtr<nsFrameLoader> frameLoader = frameLoaderOwner->GetFrameLoader();
    MOZ_ASSERT(frameLoader);
    frameLoader->ForceLayoutIfNecessary();
  }

  MOZ_ASSERT(newBrowserHost->GetBrowsingContext()->OriginAttributesRef() ==
             aOriginAttributes);

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvCreateWindow(
    PBrowserParent* aThisTab, const MaybeDiscarded<BrowsingContext>& aParent,
    PBrowserParent* aNewTab, const uint32_t& aChromeFlags,
    const bool& aCalledFromJS, const bool& aForPrinting,
    const bool& aForWindowDotPrint, const bool& aIsTopLevelCreatedByWebContent,
    nsIURI* aURIToLoad, const nsACString& aFeatures,
    const UserActivation::Modifiers& aModifiers,
    nsIPrincipal* aTriggeringPrincipal, nsIPolicyContainer* aPolicyContainer,
    nsIReferrerInfo* aReferrerInfo, const OriginAttributes& aOriginAttributes,
    bool aUserActivation, bool aTextDirectiveUserActivation,
    CreateWindowResolver&& aResolve) {
  if (!aTriggeringPrincipal) {
    return IPC_FAIL(this, "No principal");
  }

  if (!ValidatePrincipal(aTriggeringPrincipal)) {
    return PrincipalValidationIpcFail(aTriggeringPrincipal, this, __func__);
  }

  nsresult rv = NS_OK;
  CreatedWindowInfo cwi;

  // We always expect to open a new window here. If we don't, it's an error.
  cwi.windowOpened() = true;
  cwi.maxTouchPoints() = 0;

  // Make sure to resolve the resolver when this function exits, even if we
  // failed to generate a valid response.
  auto resolveOnExit = MakeScopeExit([&] {
    // Copy over the nsresult, and then resolve.
    cwi.rv() = rv;
    aResolve(cwi);
  });

  RefPtr<BrowserParent> thisTab = BrowserParent::GetFrom(aThisTab);
  RefPtr<BrowserParent> newTab = BrowserParent::GetFrom(aNewTab);
  MOZ_ASSERT(newTab);

  auto destroyNewTabOnError = MakeScopeExit([&] {
    // We always expect to open a new window here. If we don't, it's an error.
    if (!cwi.windowOpened() || NS_FAILED(rv)) {
      if (newTab) {
        newTab->Destroy();
      }
    }
  });

  // Don't continue to try to create a new window if we've been fully discarded.
  RefPtr<BrowsingContext> parent = aParent.GetMaybeDiscarded();
  if (NS_WARN_IF(!parent)) {
    rv = NS_ERROR_FAILURE;
    return IPC_OK();
  }

  // Validate that our new BrowsingContext looks as we would expect it.
  RefPtr<BrowsingContext> newBC = newTab->GetBrowsingContext();
  if (!newBC) {
    return IPC_FAIL(this, "Missing BrowsingContext for new tab");
  }

  uint64_t newBCOpenerId = newBC->GetOpenerId();
  if (newBCOpenerId != 0 && parent->Id() != newBCOpenerId) {
    return IPC_FAIL(this, "Invalid opener BrowsingContext for new tab");
  }
  if (newBC->GetParent() != nullptr) {
    return IPC_FAIL(this,
                    "Unexpected non-toplevel BrowsingContext for new tab");
  }
  if (!!(aChromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW) !=
          newBC->UseRemoteTabs() ||
      !!(aChromeFlags & nsIWebBrowserChrome::CHROME_FISSION_WINDOW) !=
          newBC->UseRemoteSubframes()) {
    return IPC_FAIL(this, "Unexpected aChromeFlags passed");
  }
  if (!aOriginAttributes.EqualsIgnoringFPD(newBC->OriginAttributesRef())) {
    return IPC_FAIL(this, "Opened tab has mismatched OriginAttributes");
  }

  if (thisTab && BrowserParent::GetFrom(thisTab)->GetBrowsingContext()) {
    BrowsingContext* thisTabBC = thisTab->GetBrowsingContext();
    if (thisTabBC->UseRemoteTabs() != newBC->UseRemoteTabs() ||
        thisTabBC->UseRemoteSubframes() != newBC->UseRemoteSubframes() ||
        thisTabBC->UsePrivateBrowsing() != newBC->UsePrivateBrowsing()) {
      return IPC_FAIL(this, "New BrowsingContext has mismatched LoadContext");
    }
  }
  BrowserParent::AutoUseNewTab aunt(newTab);

  nsCOMPtr<nsIRemoteTab> newRemoteTab;
  int32_t openLocation = nsIBrowserDOMWindow::OPEN_NEWWINDOW;
  mozilla::ipc::IPCResult ipcResult = CommonCreateWindow(
      aThisTab, *parent, newBCOpenerId != 0, aChromeFlags, aCalledFromJS,
      aForPrinting, aForWindowDotPrint, aIsTopLevelCreatedByWebContent,
      aURIToLoad, aFeatures, aModifiers, newTab, rv, newRemoteTab,
      &cwi.windowOpened(), openLocation, aTriggeringPrincipal, aReferrerInfo,
      aPolicyContainer, aOriginAttributes, aUserActivation,
      aTextDirectiveUserActivation);
  if (!ipcResult) {
    return ipcResult;
  }

  if (NS_WARN_IF(NS_FAILED(rv)) || !newRemoteTab) {
    return IPC_OK();
  }

  MOZ_ASSERT(BrowserHost::GetFrom(newRemoteTab.get()) ==
             newTab->GetBrowserHost());

  // This used to happen in the child - there may now be a better place to
  // do this work.
  MOZ_ALWAYS_SUCCEEDS(newBC->SetHasSiblings(
      openLocation == nsIBrowserDOMWindow::OPEN_NEWTAB ||
      openLocation == nsIBrowserDOMWindow::OPEN_NEWTAB_BACKGROUND ||
      openLocation == nsIBrowserDOMWindow::OPEN_NEWTAB_FOREGROUND));

  newTab->SwapFrameScriptsFrom(cwi.frameScripts());
  newTab->MaybeShowFrame();

  nsCOMPtr<nsIWidget> widget = newTab->GetWidget();
  if (widget) {
    cwi.dimensions() = newTab->GetDimensionInfo();
  }

  cwi.maxTouchPoints() = newTab->GetMaxTouchPoints();

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvShutdownProfile(
    mozilla::ProfileAndAdditionalInformation&&
        aProfileAndAdditionalInformation) {
  profiler_received_exit_profile(std::move(aProfileAndAdditionalInformation));
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvShutdownPerfStats(
    const nsACString& aPerfStats) {
  PerfStats::StorePerfStats(
      this, std::string(aPerfStats.Data(), aPerfStats.Length()));
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvGetFontListShmBlock(
    const uint32_t& aGeneration, const uint32_t& aIndex,
    ReadOnlySharedMemoryHandle* aOut) {
  auto* fontList = gfxPlatformFontList::PlatformFontList();
  MOZ_RELEASE_ASSERT(fontList, "gfxPlatformFontList not initialized?");
  fontList->ShareFontListShmBlockToProcess(aGeneration, aIndex, Pid(), aOut);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvInitializeFamily(
    const uint32_t& aGeneration, const uint32_t& aFamilyIndex,
    const bool& aLoadCmaps) {
  auto* fontList = gfxPlatformFontList::PlatformFontList();
  MOZ_RELEASE_ASSERT(fontList, "gfxPlatformFontList not initialized?");
  fontList->InitializeFamily(aGeneration, aFamilyIndex, aLoadCmaps);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvSetCharacterMap(
    const uint32_t& aGeneration, const uint32_t& aFamilyIndex,
    const bool& aAlias, const uint32_t& aFaceIndex,
    const gfxSparseBitSet& aMap) {
  auto* fontList = gfxPlatformFontList::PlatformFontList();
  MOZ_RELEASE_ASSERT(fontList, "gfxPlatformFontList not initialized?");
  fontList->SetCharacterMap(aGeneration, aFamilyIndex, aAlias, aFaceIndex,
                            aMap);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvInitOtherFamilyNames(
    const uint32_t& aGeneration, const bool& aDefer, bool* aLoaded) {
  auto* fontList = gfxPlatformFontList::PlatformFontList();
  MOZ_RELEASE_ASSERT(fontList, "gfxPlatformFontList not initialized?");
  *aLoaded = fontList->InitOtherFamilyNames(aGeneration, aDefer);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvSetupFamilyCharMap(
    const uint32_t& aGeneration, const uint32_t& aIndex, const bool& aAlias) {
  auto* fontList = gfxPlatformFontList::PlatformFontList();
  MOZ_RELEASE_ASSERT(fontList, "gfxPlatformFontList not initialized?");
  fontList->SetupFamilyCharMap(aGeneration, aIndex, aAlias);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvStartCmapLoading(
    const uint32_t& aGeneration, const uint32_t& aStartIndex) {
  auto* fontList = gfxPlatformFontList::PlatformFontList();
  MOZ_RELEASE_ASSERT(fontList, "gfxPlatformFontList not initialized?");
  fontList->StartCmapLoading(aGeneration, aStartIndex);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvGetHyphDict(
    nsIURI* aURI, ReadOnlySharedMemoryHandle* aOutHandle) {
  if (!aURI) {
    return IPC_FAIL(this, "aURI must not be null.");
  }
  nsHyphenationManager::Instance()->ShareHyphDictToProcess(aURI, Pid(),
                                                           aOutHandle);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvGraphicsError(
    const nsACString& aError) {
  if (gfx::LogForwarder* lf = gfx::Factory::GetLogForwarder()) {
    std::stringstream message;
    message << "CP+" << aError;
    lf->UpdateStringsVector(message.str());
  }
  return IPC_OK();
}

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

  UniquePtr<gfx::DriverCrashGuard> guard;
  switch (aGuardType) {
    case gfx::CrashGuardType::D3D11Layers:
      guard = MakeUnique<gfx::D3D11LayersCrashGuard>(this);
      break;
    case gfx::CrashGuardType::GLContext:
      guard = MakeUnique<gfx::GLContextCrashGuard>(this);
      break;
    case gfx::CrashGuardType::WMFVPXVideo:
      guard = MakeUnique<gfx::WMFVPXVideoCrashGuard>(this);
      break;
    default:
      return IPC_FAIL(this, "unknown crash guard type");
  }

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

  *aOutCrashed = false;
  mDriverCrashGuard = std::move(guard);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvEndDriverCrashGuard(
    const gfx::CrashGuardType& aGuardType) {
  mDriverCrashGuard = nullptr;
  return IPC_OK();
}

/* static */
void ContentParent::BroadcastBlobURLRegistration(const nsACString& aURI,
                                                 BlobImpl* aBlobImpl,
                                                 nsIPrincipal* aPrincipal,
                                                 const nsCString& aPartitionKey,
                                                 ContentParent* aIgnoreThisCP) {
  uint64_t originHash = ComputeLoadedOriginHash(aPrincipal);

  bool toBeSent =
      BlobURLProtocolHandler::IsBlobURLBroadcastPrincipal(aPrincipal);

  nsCString uri(aURI);

  for (auto* cp : AllProcesses(eLive)) {
    if (cp != aIgnoreThisCP) {
      if (!toBeSent && !cp->mLoadedOriginHashes.Contains(originHash)) {
        continue;
      }

      nsresult rv = cp->TransmitPermissionsForPrincipal(aPrincipal);
      if (NS_WARN_IF(NS_FAILED(rv))) {
        break;
      }

      (void)cp->SendBlobURLRegistration(uri, aPrincipal, aPartitionKey);
    }
  }
}

/* static */
void ContentParent::BroadcastBlobURLUnregistration(
    const nsTArray<BroadcastBlobURLUnregistrationRequest>& aRequests,
    ContentParent* aIgnoreThisCP) {
  struct DataRequest {
    const BroadcastBlobURLUnregistrationRequest& mRequest;
    uint64_t mOriginHash;
    bool mToBeSent;
  };

  nsTArray<DataRequest> dataRequests(aRequests.Length());
  for (const BroadcastBlobURLUnregistrationRequest& request : aRequests) {
    uint64_t originHash = ComputeLoadedOriginHash(request.principal());

    bool toBeSent = BlobURLProtocolHandler::IsBlobURLBroadcastPrincipal(
        request.principal());

    dataRequests.AppendElement(DataRequest{request, originHash, toBeSent});
  }

  for (auto* cp : AllProcesses(eLive)) {
    if (cp == aIgnoreThisCP) {
      continue;
    }

    nsTArray<nsCString> urls;
    for (const DataRequest& data : dataRequests) {
      if (data.mToBeSent ||
          cp->mLoadedOriginHashes.Contains(data.mOriginHash)) {
        urls.AppendElement(data.mRequest.url());
      }
    }

    if (!urls.IsEmpty()) {
      (void)cp->SendBlobURLUnregistration(urls);
    }
  }
}

mozilla::ipc::IPCResult ContentParent::RecvStoreAndBroadcastBlobURLRegistration(
    const nsACString& aURI, const IPCBlob& aBlob, nsIPrincipal* aPrincipal,
    const nsCString& aPartitionKey) {
  if (!aPrincipal) {
    return IPC_FAIL(this, "No principal");
  }

  if (!ValidatePrincipal(aPrincipal, {ValidatePrincipalOptions::AllowSystem})) {
    return PrincipalValidationIpcFail(aPrincipal, this, __func__);
  }

  if (!BlobURLProtocolHandler::IsBlobURLValid(aPrincipal, aURI)) {
    return IPC_FAIL(this, "Blob URL format is invalid.");
  }

  RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(aBlob);
  if (NS_WARN_IF(!blobImpl)) {
    return IPC_FAIL(this, "Blob deserialization failed.");
  }

  BlobURLProtocolHandler::AddDataEntryParent(aURI, aPrincipal, aPartitionKey,
                                             blobImpl, ChildID());
  BroadcastBlobURLRegistration(aURI, blobImpl, aPrincipal, aPartitionKey, this);

  return IPC_OK();
}

mozilla::ipc::IPCResult
ContentParent::RecvUnstoreAndBroadcastBlobURLUnregistration(
    const nsTArray<BroadcastBlobURLUnregistrationRequest>& aRequests) {
  nsTArray<nsCString> uris;

  for (const BroadcastBlobURLUnregistrationRequest& request : aRequests) {
    if (!ValidatePrincipal(request.principal(),
                           {ValidatePrincipalOptions::AllowSystem})) {
      return PrincipalValidationIpcFail(request.principal(), this, __func__);
    }

    uris.AppendElement(request.url());
  }

  BroadcastBlobURLUnregistration(aRequests, this);
  BlobURLProtocolHandler::RemoveDataEntries(uris, false /* Don't broadcast */);

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvGetFilesRequest(
    const nsID& aID, nsTArray<nsString>&& aDirectoryPaths,
    const bool& aRecursiveFlag) {
  MOZ_ASSERT(!mGetFilesPendingRequests.GetWeak(aID));

  if (!mozilla::Preferences::GetBool("dom.filesystem.pathcheck.disabled",
                                     false)) {
    RefPtr<FileSystemSecurity> fss = FileSystemSecurity::Get();
    if (!fss) {
      return IPC_FAIL(this, "Failed to get FileSystemSecurity.");
    }

    for (const auto& directoryPath : aDirectoryPaths) {
      if (!fss->ContentProcessHasAccessTo(ChildID(), directoryPath)) {
        return IPC_FAIL(this, "ContentProcessHasAccessTo failed.");
      }
    }
  }

  ErrorResult rv;
  RefPtr<GetFilesHelper> helper = GetFilesHelperParent::Create(
      aID, std::move(aDirectoryPaths), aRecursiveFlag, this, rv);

  if (NS_WARN_IF(rv.Failed())) {
    if (!SendGetFilesResponse(aID,
                              GetFilesResponseFailure(rv.StealNSResult()))) {
      return IPC_FAIL(this, "SendGetFilesResponse failed.");
    }
    return IPC_OK();
  }

  mGetFilesPendingRequests.InsertOrUpdate(aID, std::move(helper));
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvDeleteGetFilesRequest(
    const nsID& aID) {
  mGetFilesPendingRequests.Remove(aID);
  return IPC_OK();
}

void ContentParent::SendGetFilesResponseAndForget(
    const nsID& aID, const GetFilesResponseResult& aResult) {
  if (mGetFilesPendingRequests.Remove(aID)) {
    (void)SendGetFilesResponse(aID, aResult);
  }
}

void ContentParent::PaintTabWhileInterruptingJS(BrowserParent* aBrowserParent) {
  if (!mHangMonitorActor) {
    return;
  }
  ProcessHangMonitor::PaintWhileInterruptingJS(mHangMonitorActor,
                                               aBrowserParent);
}

void ContentParent::UnloadLayersWhileInterruptingJS(
    BrowserParent* aBrowserParent) {
  if (!mHangMonitorActor) {
    return;
  }
  ProcessHangMonitor::UnloadLayersWhileInterruptingJS(mHangMonitorActor,
                                                      aBrowserParent);
}

void ContentParent::CancelContentJSExecutionIfRunning(
    BrowserParent* aBrowserParent, nsIRemoteTab::NavigationType aNavigationType,
    const CancelContentJSOptions& aCancelContentJSOptions) {
  if (!mHangMonitorActor) {
    return;
  }

  ProcessHangMonitor::CancelContentJSExecutionIfRunning(
      mHangMonitorActor, aBrowserParent, aNavigationType,
      aCancelContentJSOptions);
}

void ContentParent::SetMainThreadQoSPriority(
    nsIThread::QoSPriority aQoSPriority) {
  if (!mHangMonitorActor) {
    return;
  }

  ProcessHangMonitor::SetMainThreadQoSPriority(mHangMonitorActor, aQoSPriority);
}

nsresult ContentParent::AboutToLoadDocumentForChild(nsIChannel* aChannel) {
  MOZ_ASSERT(aChannel);

  // Get the principal for the channel result, so that we can get the permission
  // key for the document which will be created from this response.
  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
  if (NS_WARN_IF(!ssm)) {
    return NS_ERROR_FAILURE;
  }

  nsCOMPtr<nsIPrincipal> principal;
  nsCOMPtr<nsIPrincipal> partitionedPrincipal;
  nsresult rv =
      ssm->GetChannelResultPrincipals(aChannel, getter_AddRefs(principal),
                                      getter_AddRefs(partitionedPrincipal));
  NS_ENSURE_SUCCESS(rv, rv);

  TransmitBlobURLsForPrincipal(principal);

  // Tranmit permissions for both regular and partitioned principal so that the
  // content process can get permissions for the partitioned principal. For
  // example, the desk-notification permission for a partitioned service worker.
  rv = TransmitPermissionsForPrincipal(principal);
  NS_ENSURE_SUCCESS(rv, rv);

  rv = TransmitPermissionsForPrincipal(partitionedPrincipal);
  NS_ENSURE_SUCCESS(rv, rv);

  // Forward browser-scoped (temporary) permissions for this tab to the content
  // process. These are stored per-browserId and must be re-transmitted on
  // cross-origin navigations that switch content processes. (Bug 2021626)
  nsCOMPtr<nsILoadInfo> loadInfo;
  aChannel->GetLoadInfo(getter_AddRefs(loadInfo));
  if (loadInfo) {
    RefPtr<dom::BrowsingContext> bc;
    loadInfo->GetTargetBrowsingContext(getter_AddRefs(bc));
    if (bc) {
      uint64_t browserId = bc->Top()->BrowserId();
      if (browserId) {
        RefPtr<PermissionManager> permManager =
            PermissionManager::GetInstance();
        if (permManager) {
          permManager->TransmitBrowserPermissionsForPrincipal(this, principal,
                                                              browserId);
          permManager->TransmitBrowserPermissionsForPrincipal(
              this, partitionedPrincipal, browserId);
        }
      }
    }
  }

  if (!NextGenLocalStorageEnabled()) {
    return NS_OK;
  }

  if (principal->GetIsContentPrincipal()) {
    nsCOMPtr<nsILocalStorageManager> lsm =
        do_GetService("@mozilla.org/dom/localStorage-manager;1");
    if (NS_WARN_IF(!lsm)) {
      return NS_ERROR_FAILURE;
    }

    nsCOMPtr<nsIPrincipal> storagePrincipal;
    rv = ssm->GetChannelResultStoragePrincipal(
        aChannel, getter_AddRefs(storagePrincipal));
    NS_ENSURE_SUCCESS(rv, rv);

    RefPtr<Promise> dummy;
    rv = lsm->Preload(storagePrincipal, nullptr, getter_AddRefs(dummy));
    if (NS_FAILED(rv) && rv != NS_ERROR_NOT_AVAILABLE) {
      NS_WARNING("Failed to preload local storage!");
    }
  }

  return NS_OK;
}

nsresult ContentParent::TransmitPermissionsForPrincipal(
    nsIPrincipal* aPrincipal) {
  // Create the key, and send it down to the content process.
  nsTArray<std::pair<nsCString, nsCString>> pairs =
      PermissionManager::GetAllKeysForPrincipal(aPrincipal);
  MOZ_ASSERT(pairs.Length() >= 1);
  for (auto& pair : pairs) {
    EnsurePermissionsByKey(pair.first, pair.second);
  }

  // We need to add the Site to the secondary keys of interest here.
  // This allows site-scoped permission updates to propogate when the
  // port is non-standard.
  nsAutoCString siteKey;
  nsresult rv =
      PermissionManager::GetKeyForPrincipal(aPrincipal, false, true, siteKey);
  if (NS_SUCCEEDED(rv) && !siteKey.IsEmpty()) {
    mActiveSecondaryPermissionKeys.EnsureInserted(siteKey);
  }

  return NS_OK;
}

void ContentParent::AddPrincipalToCookieInProcessCache(
    nsIPrincipal* aPrincipal) {
  MOZ_ASSERT(aPrincipal);
  mCookieInContentListCache.AppendElement(aPrincipal);
}

void ContentParent::TakeCookieInProcessCache(
    nsTArray<nsCOMPtr<nsIPrincipal>>& aList) {
  aList.SwapElements(mCookieInContentListCache);
}

void ContentParent::TransmitBlobURLsForPrincipal(nsIPrincipal* aPrincipal) {
  // If we're already broadcasting BlobURLs with this principal, we don't need
  // to send them here.
  if (BlobURLProtocolHandler::IsBlobURLBroadcastPrincipal(aPrincipal)) {
    return;
  }

  // We shouldn't have any Blob URLs with expanded principals, so transmit URLs
  // for each principal in the AllowList instead.
  if (nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aPrincipal)) {
    for (const auto& prin : ep->AllowList()) {
      TransmitBlobURLsForPrincipal(prin);
    }
    return;
  }

  uint64_t originHash = ComputeLoadedOriginHash(aPrincipal);

  if (!mLoadedOriginHashes.Contains(originHash)) {
    mLoadedOriginHashes.AppendElement(originHash);

    nsTArray<BlobURLRegistrationData> registrations;
    BlobURLProtocolHandler::ForEachBlobURL(
        [&](BlobImpl* aBlobImpl, nsIPrincipal* aBlobPrincipal,
            const nsCString& aPartitionKey, const nsACString& aURI,
            bool aRevoked) {
          // This check uses `ComputeLoadedOriginHash` to compare, rather than
          // doing the more accurate `Equals` check, as it needs to match the
          // behaviour of the logic to broadcast new registrations.
          if (originHash != ComputeLoadedOriginHash(aBlobPrincipal)) {
            return true;
          }

          registrations.AppendElement(BlobURLRegistrationData(
              nsCString(aURI), WrapNotNull(aBlobPrincipal),
              nsCString(aPartitionKey), aRevoked));

          nsresult rv = TransmitPermissionsForPrincipal(aBlobPrincipal);
          (void)NS_WARN_IF(NS_FAILED(rv));
          return true;
        });

    if (!registrations.IsEmpty()) {
      (void)SendInitBlobURLs(registrations);
    }
  }
}

void ContentParent::TransmitBlobDataIfBlobURL(nsIURI* aURI,
                                              const OriginAttributes& aAttrs) {
  MOZ_ASSERT(aURI);

  nsCOMPtr<nsIPrincipal> principal;
  if (BlobURLProtocolHandler::GetBlobURLPrincipal(aURI, aAttrs,
                                                  getter_AddRefs(principal))) {
    TransmitBlobURLsForPrincipal(principal);
  }
}

void ContentParent::EnsurePermissionsByKey(const nsACString& aKey,
                                           const nsACString& aOrigin) {
  // NOTE: Make sure to initialize the permission manager before updating the
  // mActivePermissionKeys list. If the permission manager is being initialized
  // by this call to GetPermissionManager, and we've added the key to
  // mActivePermissionKeys, then the permission manager will send down a
  // SendAddPermission before receiving the SendSetPermissionsWithKey message.
  RefPtr<PermissionManager> permManager = PermissionManager::GetInstance();
  if (!permManager) {
    return;
  }

  if (!mActivePermissionKeys.EnsureInserted(aKey)) {
    return;
  }

  nsTArray<IPC::Permission> perms;
  if (permManager->GetPermissionsFromOriginOrKey(aOrigin, aKey, perms)) {
    (void)SendSetPermissionsWithKey(aKey, perms);
  }
}

bool ContentParent::NeedsPermissionsUpdate(
    const nsACString& aPermissionKey) const {
  return mActivePermissionKeys.Contains(aPermissionKey);
}

bool ContentParent::NeedsSecondaryKeyPermissionsUpdate(
    const nsACString& aPermissionKey) const {
  return mActiveSecondaryPermissionKeys.Contains(aPermissionKey);
}

mozilla::ipc::IPCResult ContentParent::RecvAccumulateChildHistograms(
    nsTArray<HistogramAccumulation>&& aAccumulations) {
  TelemetryIPC::AccumulateChildHistograms(GetTelemetryProcessID(mRemoteType),
                                          aAccumulations);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvAccumulateChildKeyedHistograms(
    nsTArray<KeyedHistogramAccumulation>&& aAccumulations) {
  TelemetryIPC::AccumulateChildKeyedHistograms(
      GetTelemetryProcessID(mRemoteType), aAccumulations);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvUpdateChildScalars(
    nsTArray<ScalarAction>&& aScalarActions) {
  TelemetryIPC::UpdateChildScalars(GetTelemetryProcessID(mRemoteType),
                                   aScalarActions);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvUpdateChildKeyedScalars(
    nsTArray<KeyedScalarAction>&& aScalarActions) {
  TelemetryIPC::UpdateChildKeyedScalars(GetTelemetryProcessID(mRemoteType),
                                        aScalarActions);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvRecordChildEvents(
    nsTArray<mozilla::Telemetry::ChildEventData>&& aEvents) {
  TelemetryIPC::RecordChildEvents(GetTelemetryProcessID(mRemoteType), aEvents);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvRecordDiscardedData(
    const mozilla::Telemetry::DiscardedData& aDiscardedData) {
  TelemetryIPC::RecordDiscardedData(GetTelemetryProcessID(mRemoteType),
                                    aDiscardedData);
  return IPC_OK();
}

static bool WebdriverRunning() {
#ifdef ENABLE_WEBDRIVER
  nsCOMPtr<nsIMarionette> marionette = do_GetService(NS_MARIONETTE_CONTRACTID);
  if (marionette) {
    bool marionetteRunning = false;
    marionette->GetRunning(&marionetteRunning);
    if (marionetteRunning) {
      return true;
    }
  }

  nsCOMPtr<nsIRemoteAgent> agent = do_GetService(NS_REMOTEAGENT_CONTRACTID);
  if (agent) {
    bool remoteAgentRunning = false;
    agent->GetRunning(&remoteAgentRunning);
    if (remoteAgentRunning) {
      return true;
    }
  }
#endif

  return false;
}

#ifdef ANDROID
void ContentParent::RecordAndroidAppLinkTelemetry(
    mozilla::performance::pageload_event::PageloadEventData* aPageloadData,
    const TimeStamp& aNavStartTime, CanonicalBrowsingContext* cbc) {
  MOZ_ASSERT(aPageloadData);
  MOZ_ASSERT(cbc);
  uint32_t appLinkLaunchType = cbc->GetAndroidAppLinkLaunchType();
  if (appLinkLaunchType == 0 /* Unknown applink type */) {
    return;
  }
  // Clear so only the first page load in this context is attributed as an app
  // link launch; subsequent navigations should not inherit it.
  cbc->SetAndroidAppLinkLaunchType(0);

  aPageloadData->set_androidAppLinkLaunchType(appLinkLaunchType);

  //  ProcessStart to navigationStart is only meaningful for cold applink
  //  launches
  if (appLinkLaunchType != 1 /* Cold applink */) {
    return;
  }

  const TimeStamp parentProcessCreationTime = TimeStamp::ProcessCreation();
  if (parentProcessCreationTime.IsNull() || aNavStartTime.IsNull()) {
    return;
  }

  const TimeDuration delta = aNavStartTime - parentProcessCreationTime;
  if (delta.IsZero()) {
    return;
  }

  aPageloadData->set_androidAppLinkToNavigationStart(
      static_cast<uint32_t>(delta.ToMilliseconds()));

  if (profiler_thread_is_being_profiled_for_markers()) {
    PROFILER_MARKER_FMT("Applink Startup", NETWORK,
                        MarkerOptions(MarkerTiming::Interval(
                            parentProcessCreationTime, aNavStartTime)),
                        "ContentParent::RecordAndroidAppLinkTelemetry - Parent "
                        "Process Start To "
                        "Navigation Start for appLinkLaunchType {}",
                        appLinkLaunchType);
  }
}
#endif

mozilla::ipc::IPCResult ContentParent::RecvRecordPageLoadEvent(
    mozilla::performance::pageload_event::PageloadEventData&&
        aPageloadEventData,
    const TimeStamp& aNavigationStartTime,
    const MaybeDiscarded<BrowsingContext>& aBrowsingContext) {
  // Check whether a webdriver is running.
  aPageloadEventData.set_usingWebdriver(WebdriverRunning());

#if defined(ANDROID)
  // Get network link type iff android.
  aPageloadEventData.set_networkType(UpdateNetworkLinkType());
#endif

#if defined(XP_WIN)
  // The "hasSSD" property is only set on Windows during the first
  // call to nsISystemInfo.diskInfo - which is done before
  // browser-startup-idle-tasks-finished
  // Other platforms do not compute hasSSD, so there's no reason to
  // query this on other platforms.
  nsresult rv;
  nsCOMPtr<nsIPropertyBag2> infoService =
      mozilla::components::SystemInfo::Service();
  bool hasSSD;
  rv = infoService->GetPropertyAsBool(u"hasSSD"_ns, &hasSSD);
  if (NS_SUCCEEDED(rv)) {
    aPageloadEventData.set_hasSsd(hasSSD);
  }
#endif

#ifdef ANDROID
  if (!aBrowsingContext.IsNullOrDiscarded()) {
    RefPtr<CanonicalBrowsingContext> cbc = aBrowsingContext.get_canonical();
    if (cbc->IsOwnedByProcess(ChildID())) {
      RecordAndroidAppLinkTelemetry(&aPageloadEventData, aNavigationStartTime,
                                    cbc);
    }
  }

  // Set the process isolation category based on the remote type for Android.
  const nsDependentCSubstring remoteTypePrefix =
      RemoteTypePrefix(GetRemoteType());

  using namespace mozilla::performance::pageload_event;
  AndroidIsolationCategory isolationCategory;
  if (remoteTypePrefix == WEB_REMOTE_TYPE) {
    isolationCategory = AndroidIsolationCategory::SHARED_WEB;
  } else if (remoteTypePrefix == FISSION_WEB_REMOTE_TYPE) {
    isolationCategory = AndroidIsolationCategory::SITE_ISOLATED;
  } else if (remoteTypePrefix == WITH_COOP_COEP_REMOTE_TYPE) {
    isolationCategory = AndroidIsolationCategory::COOP_ISOLATED;
  } else {
    isolationCategory = AndroidIsolationCategory::OTHER;
  }
  aPageloadEventData.set_androidIsolationCategory(isolationCategory);
#endif

  // If the domain information exists, then we need to send it using a special
  // page load event ping that is sent via ohttp and stripped of any information
  // that can be used to fingerprint the client.  Otherwise, use the regular
  // pageload event ping.
  if (aPageloadEventData.HasDomain()) {
    aPageloadEventData.SendAsPageLoadDomainEvent();
  } else {
    aPageloadEventData.SendAsPageLoadEvent();
  }
  return IPC_OK();
}

//////////////////////////////////////////////////////////////////
// PURLClassifierParent

PURLClassifierParent* ContentParent::AllocPURLClassifierParent(
    nsIPrincipal* aPrincipal, bool* aSuccess) {
  MOZ_ASSERT(NS_IsMainThread());

  *aSuccess = true;
  RefPtr<URLClassifierParent> actor = new URLClassifierParent();
  return actor.forget().take();
}

mozilla::ipc::IPCResult ContentParent::RecvPURLClassifierConstructor(
    PURLClassifierParent* aActor, nsIPrincipal* aPrincipal, bool* aSuccess) {
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(aActor);
  *aSuccess = false;

  auto* actor = static_cast<URLClassifierParent*>(aActor);
  nsCOMPtr<nsIPrincipal> principal(aPrincipal);
  if (!principal) {
    actor->ClassificationFailed();
    return IPC_OK();
  }
  if (!ValidatePrincipal(aPrincipal)) {
    return PrincipalValidationIpcFail(aPrincipal, this, __func__);
  }
  return actor->StartClassify(principal, aSuccess);
}

bool ContentParent::DeallocPURLClassifierParent(PURLClassifierParent* aActor) {
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(aActor);

  RefPtr<URLClassifierParent> actor =
      dont_AddRef(static_cast<URLClassifierParent*>(aActor));
  return true;
}

//////////////////////////////////////////////////////////////////
// PURLClassifierLocalParent

PURLClassifierLocalParent* ContentParent::AllocPURLClassifierLocalParent(
    nsIURI* aURI, const nsTArray<IPCURLClassifierFeature>& aFeatures) {
  MOZ_ASSERT(NS_IsMainThread());

  RefPtr<URLClassifierLocalParent> actor = new URLClassifierLocalParent();
  return actor.forget().take();
}

mozilla::ipc::IPCResult ContentParent::RecvPURLClassifierLocalConstructor(
    PURLClassifierLocalParent* aActor, nsIURI* aURI,
    nsTArray<IPCURLClassifierFeature>&& aFeatures) {
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(aActor);

  nsTArray<IPCURLClassifierFeature> features = std::move(aFeatures);

  if (!aURI) {
    return IPC_FAIL(this, "aURI should not be null");
  }

  auto* actor = static_cast<URLClassifierLocalParent*>(aActor);
  return actor->StartClassify(aURI, features);
}

bool ContentParent::DeallocPURLClassifierLocalParent(
    PURLClassifierLocalParent* aActor) {
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(aActor);

  RefPtr<URLClassifierLocalParent> actor =
      dont_AddRef(static_cast<URLClassifierLocalParent*>(aActor));
  return true;
}

//////////////////////////////////////////////////////////////////
// PURLClassifierLocalByNameParent

PURLClassifierLocalByNameParent*
ContentParent::AllocPURLClassifierLocalByNameParent(
    nsIURI* aURI, const nsTArray<nsCString>& aFeatures,
    const nsIUrlClassifierFeature::listType& aListType) {
  MOZ_ASSERT(NS_IsMainThread());

  RefPtr<URLClassifierLocalByNameParent> actor =
      new URLClassifierLocalByNameParent();
  return actor.forget().take();
}

mozilla::ipc::IPCResult ContentParent::RecvPURLClassifierLocalByNameConstructor(
    PURLClassifierLocalByNameParent* aActor, nsIURI* aURI,
    nsTArray<nsCString>&& aFeatureNames,
    const nsIUrlClassifierFeature::listType& aListType) {
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(aActor);
  if (!aURI) {
    return IPC_FAIL(this, "aURI should not be null");
  }

  nsTArray<IPCURLClassifierFeature> ipcFeatures;
  for (nsCString& featureName : aFeatureNames) {
    RefPtr<nsIUrlClassifierFeature> feature =
        UrlClassifierFeatureFactory::GetFeatureByName(featureName);
    if (!feature) {
      continue;
    }
    nsAutoCString name;
    nsresult rv = feature->GetName(name);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      continue;
    }

    nsTArray<nsCString> tables;
    rv = feature->GetTables(aListType, tables);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      continue;
    }

    ipcFeatures.AppendElement(IPCURLClassifierFeature(name, tables));
  }

  auto* actor = static_cast<URLClassifierLocalByNameParent*>(aActor);
  return actor->StartClassify(aURI, ipcFeatures, aListType);
}

bool ContentParent::DeallocPURLClassifierLocalByNameParent(
    PURLClassifierLocalByNameParent* aActor) {
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(aActor);

  RefPtr<URLClassifierLocalByNameParent> actor =
      dont_AddRef(static_cast<URLClassifierLocalByNameParent*>(aActor));
  return true;
}

PSessionStorageObserverParent*
ContentParent::AllocPSessionStorageObserverParent() {
  MOZ_ASSERT(NS_IsMainThread());

  return mozilla::dom::AllocPSessionStorageObserverParent();
}

mozilla::ipc::IPCResult ContentParent::RecvPSessionStorageObserverConstructor(
    PSessionStorageObserverParent* aActor) {
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(aActor);

  if (!mozilla::dom::RecvPSessionStorageObserverConstructor(aActor)) {
    return IPC_FAIL(this, "RecvPSessionStorageObserverConstructor failed.");
  }
  return IPC_OK();
}

bool ContentParent::DeallocPSessionStorageObserverParent(
    PSessionStorageObserverParent* aActor) {
  MOZ_ASSERT(NS_IsMainThread());
  MOZ_ASSERT(aActor);

  return mozilla::dom::DeallocPSessionStorageObserverParent(aActor);
}

mozilla::ipc::IPCResult ContentParent::RecvBHRThreadHang(
    const HangDetails& aHangDetails) {
  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
  if (obs) {
    // Copy the HangDetails recieved over the network into a nsIHangDetails, and
    // then fire our own observer notification.
    // XXX: We should be able to avoid this potentially expensive copy here by
    // moving our deserialized argument.
    nsCOMPtr<nsIHangDetails> hangDetails =
        new nsHangDetails(HangDetails(aHangDetails), PersistedToDisk::No);
    obs->NotifyObservers(hangDetails, "bhr-thread-hang", nullptr);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult
ContentParent::RecvAutomaticStorageAccessPermissionCanBeGranted(
    nsIPrincipal* aPrincipal,
    AutomaticStorageAccessPermissionCanBeGrantedResolver&& aResolver) {
  if (!aPrincipal) {
    return IPC_FAIL(this, "No principal");
  }

  if (!ValidatePrincipal(aPrincipal)) {
    return PrincipalValidationIpcFail(aPrincipal, this, __func__);
  }
  aResolver(Document::AutomaticStorageAccessPermissionCanBeGranted(aPrincipal));
  return IPC_OK();
}

mozilla::ipc::IPCResult
ContentParent::RecvStorageAccessPermissionGrantedForOrigin(
    uint64_t aTopLevelWindowId,
    const MaybeDiscarded<BrowsingContext>& aParentContext,
    nsIPrincipal* aTrackingPrincipal, const nsACString& aTrackingOrigin,
    const StorageAccessPromptChoices& aAllowMode,
    const Maybe<ContentBlockingNotifier::StorageAccessPermissionGrantedReason>&
        aReason,
    const bool& aFrameOnly,
    StorageAccessPermissionGrantedForOriginResolver&& aResolver) {
  if (aParentContext.IsNullOrDiscarded()) {
    return IPC_OK();
  }

  if (!aTrackingPrincipal) {
    aResolver(false);
    return IPC_OK();
  }

  // We only report here if we cannot report the console directly in the content
  // process. In that case, the `aReason` would be given a value. Otherwise, it
  // will be nothing.
  if (aReason) {
    ContentBlockingNotifier::ReportUnblockingToConsole(
        aParentContext.get_canonical(), NS_ConvertUTF8toUTF16(aTrackingOrigin),
        aReason.value());
  }

  StorageAccessAPIHelper::SaveAccessForOriginOnParentProcess(
      aTopLevelWindowId, aParentContext.get_canonical(), aTrackingPrincipal,
      aAllowMode, aFrameOnly)
      ->Then(GetCurrentSerialEventTarget(), __func__,
             [aResolver = std::move(aResolver)](
                 StorageAccessAPIHelper::ParentAccessGrantPromise::
                     ResolveOrRejectValue&& aValue) {
               bool success =
                   aValue.IsResolve() && NS_SUCCEEDED(aValue.ResolveValue());
               aResolver(success);
             });
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvCompleteAllowAccessFor(
    const MaybeDiscarded<BrowsingContext>& aParentContext,
    uint64_t aTopLevelWindowId, nsIPrincipal* aTrackingPrincipal,
    const nsACString& aTrackingOrigin, uint32_t aCookieBehavior,
    const ContentBlockingNotifier::StorageAccessPermissionGrantedReason&
        aReason,
    CompleteAllowAccessForResolver&& aResolver) {
  if (aParentContext.IsNullOrDiscarded()) {
    return IPC_OK();
  }

  StorageAccessAPIHelper::CompleteAllowAccessForOnParentProcess(
      aParentContext.get_canonical(), aTopLevelWindowId, aTrackingPrincipal,
      aTrackingOrigin, aCookieBehavior, aReason, nullptr)
      ->Then(GetCurrentSerialEventTarget(), __func__,
             [aResolver = std::move(aResolver)](
                 StorageAccessAPIHelper::StorageAccessPermissionGrantPromise::
                     ResolveOrRejectValue&& aValue) {
               Maybe<StorageAccessPromptChoices> choice;
               if (aValue.IsResolve()) {
                 choice.emplace(aValue.ResolveValue());
               }
               aResolver(choice);
             });
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvStoreUserInteractionAsPermission(
    nsIPrincipal* aPrincipal) {
  if (!aPrincipal) {
    return IPC_FAIL(this, "No principal");
  }

  if (!ValidatePrincipal(aPrincipal)) {
    return PrincipalValidationIpcFail(aPrincipal, this, __func__);
  }
  ContentBlockingUserInteraction::Observe(aPrincipal);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvTestCookiePermissionDecided(
    const MaybeDiscarded<BrowsingContext>& aContext, nsIPrincipal* aPrincipal,
    const TestCookiePermissionDecidedResolver&& aResolver) {
  if (aContext.IsNullOrDiscarded()) {
    return IPC_OK();
  }

  if (!aPrincipal) {
    return IPC_FAIL(this, "No principal");
  }

  RefPtr<WindowGlobalParent> wgp =
      aContext.get_canonical()->GetCurrentWindowGlobal();

  if (!wgp) {
    return IPC_FAIL(this, "No current window global");
  }

  nsCOMPtr<nsICookieJarSettings> cjs = wgp->CookieJarSettings();

  Maybe<bool> result =
      StorageAccessAPIHelper::CheckCookiesPermittedDecidesStorageAccessAPI(
          cjs, aPrincipal);
  aResolver(result);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvNotifyMediaPlaybackChanged(
    const MaybeDiscarded<BrowsingContext>& aContext,
    MediaPlaybackState aState) {
  if (aContext.IsNullOrDiscarded()) {
    return IPC_OK();
  }
  if (RefPtr<IMediaInfoUpdater> updater =
          aContext.get_canonical()->GetMediaController()) {
    updater->NotifyMediaPlaybackChanged(aContext.ContextId(), aState);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvNotifyMediaAudibleChanged(
    const MaybeDiscarded<BrowsingContext>& aContext, MediaAudibleState aState,
    ControlType aType, AudioSessionType aSessionType) {
  if (aContext.IsNullOrDiscarded()) {
    return IPC_OK();
  }
  if (RefPtr<IMediaInfoUpdater> updater =
          aContext.get_canonical()->GetMediaController()) {
    updater->NotifyMediaAudibleChanged(aContext.ContextId(), aState, aType,
                                       aSessionType);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvNotifyPictureInPictureModeChanged(
    const MaybeDiscarded<BrowsingContext>& aContext, bool aEnabled) {
  if (aContext.IsNullOrDiscarded()) {
    return IPC_OK();
  }
  if (RefPtr<MediaController> controller =
          aContext.get_canonical()->GetMediaController()) {
    controller->SetIsInPictureInPictureMode(aContext.ContextId(), aEnabled);
  }
  return IPC_OK();
}

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

  CanonicalBrowsingContext* context = aContext.get_canonical();

  context->Group()->EachOtherParent(this, [&](ContentParent* aParent) {
    (void)aParent->SendAbortOrientationPendingPromises(context);
  });

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvNotifyMediaSessionUpdated(
    const MaybeDiscarded<BrowsingContext>& aContext, bool aIsCreated) {
  if (aContext.IsNullOrDiscarded()) {
    return IPC_OK();
  }

  RefPtr<IMediaInfoUpdater> updater =
      aContext.get_canonical()->GetMediaController();
  if (!updater) {
    return IPC_OK();
  }
  if (aIsCreated) {
    updater->NotifySessionCreated(aContext->Id());
  } else {
    updater->NotifySessionDestroyed(aContext->Id());
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvNotifyUpdateMediaMetadata(
    const MaybeDiscarded<BrowsingContext>& aContext,
    const Maybe<MediaMetadataBase>& aMetadata) {
  if (aContext.IsNullOrDiscarded()) {
    return IPC_OK();
  }
  if (RefPtr<IMediaInfoUpdater> updater =
          aContext.get_canonical()->GetMediaController()) {
    updater->UpdateMetadata(aContext.ContextId(), aMetadata);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult
ContentParent::RecvNotifyMediaSessionPlaybackStateChanged(
    const MaybeDiscarded<BrowsingContext>& aContext,
    MediaSessionPlaybackState aPlaybackState) {
  if (aContext.IsNullOrDiscarded()) {
    return IPC_OK();
  }
  if (RefPtr<IMediaInfoUpdater> updater =
          aContext.get_canonical()->GetMediaController()) {
    updater->SetDeclaredPlaybackState(aContext.ContextId(), aPlaybackState);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult
ContentParent::RecvNotifyMediaSessionSupportedActionChanged(
    const MaybeDiscarded<BrowsingContext>& aContext, MediaSessionAction aAction,
    bool aEnabled) {
  if (aContext.IsNullOrDiscarded()) {
    return IPC_OK();
  }
  RefPtr<IMediaInfoUpdater> updater =
      aContext.get_canonical()->GetMediaController();
  if (!updater) {
    return IPC_OK();
  }
  if (aEnabled) {
    updater->EnableAction(aContext.ContextId(), aAction);
  } else {
    updater->DisableAction(aContext.ContextId(), aAction);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvNotifyMediaFullScreenState(
    const MaybeDiscarded<BrowsingContext>& aContext, bool aIsInFullScreen) {
  if (aContext.IsNullOrDiscarded()) {
    return IPC_OK();
  }
  if (RefPtr<IMediaInfoUpdater> updater =
          aContext.get_canonical()->GetMediaController()) {
    updater->NotifyMediaFullScreenState(aContext.ContextId(), aIsInFullScreen);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvNotifyPositionStateChanged(
    const MaybeDiscarded<BrowsingContext>& aContext,
    const Maybe<PositionState>& aState) {
  if (aContext.IsNullOrDiscarded()) {
    return IPC_OK();
  }
  if (RefPtr<IMediaInfoUpdater> updater =
          aContext.get_canonical()->GetMediaController()) {
    updater->UpdatePositionState(aContext.ContextId(), aState);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvNotifyGuessedPositionStateChanged(
    const MaybeDiscarded<BrowsingContext>& aContext, const nsID& aMediaId,
    const Maybe<PositionState>& aState) {
  if (aContext.IsNullOrDiscarded()) {
    return IPC_OK();
  }
  if (RefPtr<IMediaInfoUpdater> updater =
          aContext.get_canonical()->GetMediaController()) {
    updater->UpdateGuessedPositionState(aContext.ContextId(), aMediaId, aState);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvAddOrRemovePageAwakeRequest(
    const MaybeDiscarded<BrowsingContext>& aContext,
    const bool& aShouldAddCount) {
  if (aContext.IsNullOrDiscarded()) {
    return IPC_OK();
  }
  if (aShouldAddCount) {
    aContext.get_canonical()->AddPageAwakeRequest();
  } else {
    aContext.get_canonical()->RemovePageAwakeRequest();
  }
  return IPC_OK();
}

#if defined(XP_WIN)
mozilla::ipc::IPCResult ContentParent::RecvGetModulesTrust(
    ModulePaths&& aModPaths, bool aRunAtNormalPriority,
    GetModulesTrustResolver&& aResolver) {
  RefPtr<DllServices> dllSvc(DllServices::Get());
  dllSvc->GetModulesTrust(std::move(aModPaths), aRunAtNormalPriority)
      ->Then(
          GetMainThreadSerialEventTarget(), __func__,
          [aResolver](ModulesMapResult&& aResult) {
            aResolver(Some(ModulesMapResult(std::move(aResult))));
          },
          [aResolver](nsresult aRv) { aResolver(Nothing()); });
  return IPC_OK();
}
#endif  // defined(XP_WIN)

mozilla::ipc::IPCResult ContentParent::RecvCreateBrowsingContext(
    uint64_t aGroupId, BrowsingContext::IPCInitializer&& aInit) {
  RefPtr<WindowGlobalParent> parent;
  if (aInit.mParentId != 0) {
    parent = WindowGlobalParent::GetByInnerWindowId(aInit.mParentId);
    if (!parent) {
      return IPC_FAIL(this, "Parent doesn't exist in parent process");
    }
  }

  if (parent && parent->GetContentParent() != this) {
    // We're trying attach a child BrowsingContext to a parent
    // WindowContext in another process. This is illegal since the
    // only thing that could create that child BrowsingContext is the parent
    // window's process.
    return IPC_FAIL(this,
                    "Must create BrowsingContext from the parent's process");
  }

  RefPtr<BrowsingContext> opener;
  if (aInit.GetOpenerId() != 0) {
    opener = BrowsingContext::Get(aInit.GetOpenerId());
    if (!opener) {
      return IPC_FAIL(this, "Opener doesn't exist in parent process");
    }
  }

  RefPtr<BrowsingContext> child = BrowsingContext::Get(aInit.mId);
  if (child) {
    // This is highly suspicious. BrowsingContexts should only be created once,
    // so finding one indicates that someone is doing something they shouldn't.
    return IPC_FAIL(this, "A BrowsingContext with this ID already exists");
  }

  // Ensure that the passed-in BrowsingContextGroup is valid.
  RefPtr<BrowsingContextGroup> group =
      BrowsingContextGroup::GetOrCreate(aGroupId);
  if (parent) {
    if (parent->Group()->Id() != aGroupId) {
      return IPC_FAIL(this, "Parent has different group ID");
    }
    if (parent->IsDiscarded()) {
      return IPC_FAIL(this, "Parent is discarded");
    }
    if (parent->Group() != group) {
      return IPC_FAIL(this, "Parent has different group object");
    }
  }
  if (opener && opener->Group() != group) {
    if (opener->Group()->Id() != aGroupId) {
      return IPC_FAIL(this, "Opener has different group ID");
    }
    return IPC_FAIL(this, "Opener has different group object");
  }
  if (!parent && !opener && !group->Toplevels().IsEmpty()) {
    return IPC_FAIL(this, "Unrelated context from child in stale group");
  }

  return BrowsingContext::CreateFromIPC(std::move(aInit), group, this);
}

bool ContentParent::CheckBrowsingContextEmbedder(CanonicalBrowsingContext* aBC,
                                                 const char* aOperation) const {
  if (!aBC->IsEmbeddedInProcess(ChildID())) {
    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Warning,
            ("ParentIPC: Trying to %s out of process context 0x%08" PRIx64,
             aOperation, aBC->Id()));
    return false;
  }
  return true;
}

mozilla::ipc::IPCResult ContentParent::RecvDiscardBrowsingContext(
    const MaybeDiscarded<BrowsingContext>& aContext, bool aDoDiscard,
    DiscardBrowsingContextResolver&& aResolve) {
  if (CanonicalBrowsingContext* context =
          CanonicalBrowsingContext::Cast(aContext.GetMaybeDiscarded())) {
    if (aDoDiscard && !context->IsDiscarded()) {
      if (!CheckBrowsingContextEmbedder(context, "discard")) {
        return IPC_FAIL(this, "Illegal Discard attempt");
      }

      context->Detach(/* aFromIPC */ true);
    }
    context->AddFinalDiscardListener(aResolve);
    return IPC_OK();
  }

  // Resolve the promise, as we've received and handled the message. This will
  // allow the content process to fully-discard references to this BC.
  aResolve(true);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvWindowClose(
    const MaybeDiscarded<BrowsingContext>& aContext, bool aTrustedCaller) {
  if (aContext.IsNullOrDiscarded()) {
    MOZ_LOG(
        BrowsingContext::GetLog(), LogLevel::Debug,
        ("ParentIPC: Trying to send a message to dead or detached context"));
    return IPC_OK();
  }
  CanonicalBrowsingContext* context = aContext.get_canonical();
  if (ContentParent* cp = context->GetContentParent()) {
    (void)cp->SendWindowClose(context, aTrustedCaller);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvWindowFocus(
    const MaybeDiscarded<BrowsingContext>& aContext, CallerType aCallerType,
    uint64_t aActionId) {
  if (aContext.IsNullOrDiscarded()) {
    MOZ_LOG(
        BrowsingContext::GetLog(), LogLevel::Debug,
        ("ParentIPC: Trying to send a message to dead or detached context"));
    return IPC_OK();
  }
  LOGFOCUS(("ContentParent::RecvWindowFocus actionid: %" PRIu64, aActionId));
  CanonicalBrowsingContext* context = aContext.get_canonical();

  if (ContentParent* cp = context->GetContentParent()) {
    (void)cp->SendWindowFocus(context, aCallerType, aActionId);
  }
  return IPC_OK();
}

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

  if (ContentParent* cp = context->GetContentParent()) {
    (void)cp->SendWindowBlur(context, aCallerType);
  }
  return IPC_OK();
}

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

  CanonicalBrowsingContext* context = aContext.get_canonical();

  if (ContentParent* cp = context->GetContentParent()) {
    (void)cp->SendRaiseWindow(context, aCallerType, aActionId);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvAdjustWindowFocus(
    const MaybeDiscarded<BrowsingContext>& aContext, bool aIsVisible,
    uint64_t aActionId, bool aShouldClearFocus,
    const MaybeDiscarded<BrowsingContext>& aAncestorBrowsingContextToFocus) {
  if (aContext.IsNullOrDiscarded()) {
    MOZ_LOG(
        BrowsingContext::GetLog(), LogLevel::Debug,
        ("ParentIPC: Trying to send a message to dead or detached context"));
    return IPC_OK();
  }
  LOGFOCUS(
      ("ContentParent::RecvAdjustWindowFocus isVisible %d actionid: %" PRIu64,
       aIsVisible, aActionId));

  nsTHashMap<nsPtrHashKey<ContentParent>, bool> processes(2);
  processes.InsertOrUpdate(this, true);

  ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
  if (cpm) {
    CanonicalBrowsingContext* context = aContext.get_canonical();
    while (context) {
      BrowsingContext* parent = context->GetParent();
      if (!parent) {
        break;
      }

      CanonicalBrowsingContext* canonicalParent = parent->Canonical();
      ContentParent* cp = cpm->GetContentProcessById(
          ContentParentId(canonicalParent->OwnerProcessId()));
      if (cp && !processes.Get(cp)) {
        (void)cp->SendAdjustWindowFocus(context, aIsVisible, aActionId,
                                        aShouldClearFocus,
                                        aAncestorBrowsingContextToFocus);
        processes.InsertOrUpdate(cp, true);
      }
      context = canonicalParent;
    }
  }
  return IPC_OK();
}

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

  if (ContentParent* cp = context->GetContentParent()) {
    (void)cp->SendClearFocus(context);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvSetFocusedBrowsingContext(
    const MaybeDiscarded<BrowsingContext>& aContext, uint64_t aActionId) {
  if (aContext.IsNullOrDiscarded()) {
    MOZ_LOG(
        BrowsingContext::GetLog(), LogLevel::Debug,
        ("ParentIPC: Trying to send a message to dead or detached context"));
    return IPC_OK();
  }
  LOGFOCUS(("ContentParent::RecvSetFocusedBrowsingContext actionid: %" PRIu64,
            aActionId));
  CanonicalBrowsingContext* context = aContext.get_canonical();

  nsFocusManager* fm = nsFocusManager::GetFocusManager();
  if (!fm) {
    return IPC_OK();
  }

  if (!fm->SetFocusedBrowsingContextInChrome(context, aActionId)) {
    LOGFOCUS((
        "Ignoring out-of-sequence attempt [%p] to set focused browsing context "
        "in parent.",
        context));
    (void)SendReviseFocusedBrowsingContext(
        aActionId, fm->GetFocusedBrowsingContextInChrome(),
        fm->GetActionIdForFocusedBrowsingContextInChrome());
    return IPC_OK();
  }

  BrowserParent::UpdateFocusFromBrowsingContext();

  context->Group()->EachOtherParent(this, [&](ContentParent* aParent) {
    (void)aParent->SendSetFocusedBrowsingContext(context, aActionId);
  });

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvSetActiveBrowsingContext(
    const MaybeDiscarded<BrowsingContext>& aContext, uint64_t aActionId) {
  if (aContext.IsNullOrDiscarded()) {
    MOZ_LOG(
        BrowsingContext::GetLog(), LogLevel::Debug,
        ("ParentIPC: Trying to send a message to dead or detached context"));
    return IPC_OK();
  }
  LOGFOCUS(("ContentParent::RecvSetActiveBrowsingContext actionid: %" PRIu64,
            aActionId));
  CanonicalBrowsingContext* context = aContext.get_canonical();

  nsFocusManager* fm = nsFocusManager::GetFocusManager();
  if (!fm) {
    return IPC_OK();
  }

  if (!fm->SetActiveBrowsingContextInChrome(context, aActionId)) {
    LOGFOCUS(
        ("Ignoring out-of-sequence attempt [%p] to set active browsing context "
         "in parent.",
         context));
    (void)SendReviseActiveBrowsingContext(
        aActionId, fm->GetActiveBrowsingContextInChrome(),
        fm->GetActionIdForActiveBrowsingContextInChrome());
    return IPC_OK();
  }

  context->Group()->EachOtherParent(this, [&](ContentParent* aParent) {
    (void)aParent->SendSetActiveBrowsingContext(context, aActionId);
  });

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvUnsetActiveBrowsingContext(
    const MaybeDiscarded<BrowsingContext>& aContext, uint64_t aActionId) {
  if (aContext.IsNullOrDiscarded()) {
    MOZ_LOG(
        BrowsingContext::GetLog(), LogLevel::Debug,
        ("ParentIPC: Trying to send a message to dead or detached context"));
    return IPC_OK();
  }
  LOGFOCUS(("ContentParent::RecvUnsetActiveBrowsingContext actionid: %" PRIu64,
            aActionId));
  CanonicalBrowsingContext* context = aContext.get_canonical();

  nsFocusManager* fm = nsFocusManager::GetFocusManager();
  if (!fm) {
    return IPC_OK();
  }

  if (!fm->SetActiveBrowsingContextInChrome(nullptr, aActionId)) {
    LOGFOCUS(
        ("Ignoring out-of-sequence attempt to unset active browsing context in "
         "parent [%p].",
         context));
    (void)SendReviseActiveBrowsingContext(
        aActionId, fm->GetActiveBrowsingContextInChrome(),
        fm->GetActionIdForActiveBrowsingContextInChrome());
    return IPC_OK();
  }

  context->Group()->EachOtherParent(this, [&](ContentParent* aParent) {
    (void)aParent->SendUnsetActiveBrowsingContext(context, aActionId);
  });

  return IPC_OK();
}

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

  if (ContentParent* cp = context->GetContentParent()) {
    (void)cp->SendSetFocusedElement(context, aNeedsFocus);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvFinalizeFocusOuter(
    const MaybeDiscarded<BrowsingContext>& aContext, bool aCanFocus,
    CallerType aCallerType) {
  if (aContext.IsNullOrDiscarded()) {
    MOZ_LOG(
        BrowsingContext::GetLog(), LogLevel::Debug,
        ("ParentIPC: Trying to send a message to dead or detached context"));
    return IPC_OK();
  }
  LOGFOCUS(("ContentParent::RecvFinalizeFocusOuter"));
  CanonicalBrowsingContext* context = aContext.get_canonical();
  ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
  if (cpm) {
    ContentParent* cp = cpm->GetContentProcessById(
        ContentParentId(context->EmbedderProcessId()));
    if (cp) {
      (void)cp->SendFinalizeFocusOuter(context, aCanFocus, aCallerType);
    }
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvInsertNewFocusActionId(
    uint64_t aActionId) {
  LOGFOCUS(("ContentParent::RecvInsertNewFocusActionId actionid: %" PRIu64,
            aActionId));
  nsFocusManager* fm = nsFocusManager::GetFocusManager();
  if (fm) {
    fm->InsertNewFocusActionId(aActionId);
  }
  return IPC_OK();
}

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

  LOGFOCUS(
      ("ContentParent::RecvBlurToParent isLeavingDocument %d adjustWidget %d "
       "browsingContextToClearHandled %d ancestorBrowsingContextToFocusHandled "
       "%d actionid: %" PRIu64,
       aIsLeavingDocument, aAdjustWidget, aBrowsingContextToClearHandled,
       aAncestorBrowsingContextToFocusHandled, aActionId));

  CanonicalBrowsingContext* focusedBrowsingContext =
      aFocusedBrowsingContext.get_canonical();

  // If aBrowsingContextToClear and aAncestorBrowsingContextToFocusHandled
  // didn't get handled in the process that sent this IPC message and they
  // aren't in the same process as aFocusedBrowsingContext, we need to split
  // off their handling here and use SendSetFocusedElement to send them
  // elsewhere than the blurring itself.

  bool ancestorDifferent =
      (!aAncestorBrowsingContextToFocusHandled &&
       !aAncestorBrowsingContextToFocus.IsNullOrDiscarded() &&
       (focusedBrowsingContext->OwnerProcessId() !=
        aAncestorBrowsingContextToFocus.get_canonical()->OwnerProcessId()));
  if (!aBrowsingContextToClearHandled &&
      !aBrowsingContextToClear.IsNullOrDiscarded() &&
      (focusedBrowsingContext->OwnerProcessId() !=
       aBrowsingContextToClear.get_canonical()->OwnerProcessId())) {
    MOZ_RELEASE_ASSERT(!ancestorDifferent,
                       "This combination is not supposed to happen.");
    if (ContentParent* cp =
            aBrowsingContextToClear.get_canonical()->GetContentParent()) {
      (void)cp->SendSetFocusedElement(aBrowsingContextToClear, false);
    }
  } else if (ancestorDifferent) {
    if (ContentParent* cp = aAncestorBrowsingContextToFocus.get_canonical()
                                ->GetContentParent()) {
      (void)cp->SendSetFocusedElement(aAncestorBrowsingContextToFocus, true);
    }
  }

  if (ContentParent* cp = focusedBrowsingContext->GetContentParent()) {
    (void)cp->SendBlurToChild(aFocusedBrowsingContext, aBrowsingContextToClear,
                              aAncestorBrowsingContextToFocus,
                              aIsLeavingDocument, aAdjustWidget, aActionId);
  }

  return IPC_OK();
}

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

  if (ContentParent* cp = context->GetContentParent()) {
    (void)cp->SendMaybeExitFullscreen(context);
  }

  return IPC_OK();
}

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

  if (aData.source().IsDiscarded()) {
    MOZ_LOG(
        BrowsingContext::GetLog(), LogLevel::Debug,
        ("ParentIPC: Trying to send a message from dead or detached context"));
    return IPC_OK();
  }

  RefPtr<ContentParent> cp = context->GetContentParent();
  if (!cp) {
    MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
            ("ParentIPC: Trying to send PostMessage to dead content process"));
    return IPC_OK();
  }

  (void)cp->SendWindowPostMessage(context, aMessage, aData);
  return IPC_OK();
}

void ContentParent::AddBrowsingContextGroup(BrowsingContextGroup* aGroup) {
  MOZ_DIAGNOSTIC_ASSERT(aGroup);
  // Ensure that the group has been inserted, and if we're not launching
  // anymore, also begin subscribing. Launching processes will be subscribed if
  // they finish launching in `LaunchSubprocessResolve`.
  if (mGroups.EnsureInserted(aGroup) && !IsLaunching()) {
    aGroup->Subscribe(this);
  }
}

void ContentParent::RemoveBrowsingContextGroup(BrowsingContextGroup* aGroup) {
  MOZ_DIAGNOSTIC_ASSERT(aGroup);
  // Remove the group from our list. This is called from the
  // BrowsingContextGroup when unsubscribing, so we don't need to do it here.
  if (mGroups.EnsureRemoved(aGroup) && CanSend()) {
    // If we're removing the entry for the first time, tell the content process
    // to clean up the group.
    (void)SendDestroyBrowsingContextGroup(aGroup->Id());
  }
}

mozilla::ipc::IPCResult ContentParent::RecvCommitBrowsingContextTransaction(
    const MaybeDiscarded<BrowsingContext>& aContext,
    BrowsingContext::BaseTransaction&& aTransaction, uint64_t aEpoch) {
  // Record the new BrowsingContextFieldEpoch associated with this transaction.
  // This should be done unconditionally, so that we're always in-sync.
  //
  // The order the parent process receives transactions is considered the
  // "canonical" ordering, so we don't need to worry about doing any
  // epoch-related validation.
  MOZ_ASSERT(aEpoch == mBrowsingContextFieldEpoch + 1,
             "Child process skipped an epoch?");
  mBrowsingContextFieldEpoch = aEpoch;

  return aTransaction.CommitFromIPC(aContext, this);
}

mozilla::ipc::IPCResult ContentParent::RecvBlobURLDataRequest(
    const nsACString& aBlobURL, nsIPrincipal* aTriggeringPrincipal,
    nsIPrincipal* aLoadingPrincipal, const OriginAttributes& aOriginAttributes,
    uint64_t aInnerWindowId, const nsCString& aPartitionKey,
    BlobURLDataRequestResolver&& aResolver) {
  RefPtr<BlobImpl> blobImpl;

  // Since revoked blobs are also retrieved, it is possible that the blob no
  // longer exists (due to the 5 second timeout) when execution reaches here
  if (!BlobURLProtocolHandler::GetDataEntry(
          aBlobURL, getter_AddRefs(blobImpl), aLoadingPrincipal,
          aTriggeringPrincipal, aOriginAttributes, aInnerWindowId,
          aPartitionKey, true /* AlsoIfRevoked */)) {
    aResolver(NS_ERROR_DOM_BAD_URI);
    return IPC_OK();
  }

  IPCBlob ipcBlob;
  nsresult rv = IPCBlobUtils::Serialize(blobImpl, ipcBlob);

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

  aResolver(ipcBlob);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvReportServiceWorkerShutdownProgress(
    uint32_t aShutdownStateId, ServiceWorkerShutdownState::Progress aProgress) {
  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
  MOZ_RELEASE_ASSERT(swm, "ServiceWorkers should shutdown before SWM.");

  swm->ReportServiceWorkerShutdownProgress(aShutdownStateId, aProgress);

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvNotifyOnHistoryReload(
    const MaybeDiscarded<BrowsingContext>& aContext, const bool& aForceReload,
    NotifyOnHistoryReloadResolver&& aResolver) {
  bool canReload = false;
  Maybe<NotNull<RefPtr<nsDocShellLoadState>>> loadState;
  Maybe<bool> reloadActiveEntry;
  if (!aContext.IsNullOrDiscarded()) {
    aContext.get_canonical()->NotifyOnHistoryReload(
        aForceReload, canReload, loadState, reloadActiveEntry);
  }
  aResolver(
      std::tuple<const bool&,
                 const Maybe<NotNull<RefPtr<nsDocShellLoadState>>>&,
                 const Maybe<bool>&>(canReload, loadState, reloadActiveEntry));
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvHistoryCommit(
    const MaybeDiscarded<BrowsingContext>& aContext, const uint64_t& aLoadID,
    const nsID& aChangeID, const uint32_t& aLoadType,
    const bool& aCloneEntryChildren, const bool& aChannelExpired,
    const uint32_t& aCacheKey) {
  if (!aContext.IsDiscarded()) {
    CanonicalBrowsingContext* canonical = aContext.get_canonical();
    if (!canonical) {
      return IPC_FAIL(
          this, "Could not get canonical. aContext.get_canonical() fails.");
    }
    canonical->SessionHistoryCommit(aLoadID, aChangeID, aLoadType,
                                    aCloneEntryChildren, aChannelExpired,
                                    aCacheKey);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvHistoryGo(
    const MaybeDiscarded<BrowsingContext>& aContext, int32_t aOffset,
    uint64_t aHistoryEpoch, bool aRequireUserInteraction, bool aUserActivation,
    bool aCheckForCancelation, HistoryGoResolver&& aResolveRequestedIndex) {
  if (!aContext.IsNullOrDiscarded()) {
    RefPtr<CanonicalBrowsingContext> canonical = aContext.get_canonical();
    aResolveRequestedIndex(canonical->HistoryGo(
        aOffset, aHistoryEpoch, aRequireUserInteraction, aUserActivation,
        aCheckForCancelation, Some(ChildID())));
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvNavigationTraverse(
    const MaybeDiscarded<BrowsingContext>& aContext, const nsID& aKey,
    uint64_t aHistoryEpoch, bool aUserActivation, bool aCheckForCancelation,
    NavigationTraverseResolver&& aResolver) {
  if (!aContext.IsNullOrDiscarded()) {
    RefPtr<CanonicalBrowsingContext> canonical = aContext.get_canonical();
    canonical->NavigationTraverse(aKey, aHistoryEpoch, aUserActivation,
                                  aCheckForCancelation, Some(ChildID()),
                                  aResolver);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvSynchronizeLayoutHistoryState(
    const MaybeDiscarded<BrowsingContext>& aContext,
    nsILayoutHistoryState* aState) {
  if (aContext.IsNull()) {
    return IPC_OK();
  }

  BrowsingContext* bc = aContext.GetMaybeDiscarded();
  if (!bc) {
    return IPC_OK();
  }
  SessionHistoryEntry* entry = bc->Canonical()->GetActiveSessionHistoryEntry();
  if (entry) {
    entry->SetLayoutHistoryState(aState);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvSessionHistoryEntryTitle(
    const MaybeDiscarded<BrowsingContext>& aContext, const nsAString& aTitle) {
  if (aContext.IsNullOrDiscarded()) {
    return IPC_OK();
  }

  SessionHistoryEntry* entry =
      aContext.get_canonical()->GetActiveSessionHistoryEntry();
  if (entry) {
    entry->SetTitle(aTitle);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult
ContentParent::RecvSessionHistoryEntryScrollRestorationIsManual(
    const MaybeDiscarded<BrowsingContext>& aContext, const bool& aIsManual) {
  if (aContext.IsNullOrDiscarded()) {
    return IPC_OK();
  }

  SessionHistoryEntry* entry =
      aContext.get_canonical()->GetActiveSessionHistoryEntry();
  if (entry) {
    entry->SetScrollRestorationIsManual(aIsManual);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvSessionHistoryEntryScrollPosition(
    const MaybeDiscarded<BrowsingContext>& aContext, const int32_t& aX,
    const int32_t& aY) {
  if (aContext.IsNullOrDiscarded()) {
    return IPC_OK();
  }

  SessionHistoryEntry* entry =
      aContext.get_canonical()->GetActiveSessionHistoryEntry();
  if (entry) {
    entry->SetScrollPosition(aX, aY);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult
ContentParent::RecvSessionHistoryEntryStoreWindowNameInContiguousEntries(
    const MaybeDiscarded<BrowsingContext>& aContext, const nsAString& aName) {
  if (aContext.IsNullOrDiscarded()) {
    return IPC_OK();
  }

  // Per https://html.spec.whatwg.org/#history-traversal 4.2.1, we need to set
  // the name to all contiguous entries. This has to be called before
  // CanonicalBrowsingContext::SessionHistoryCommit(), so the active entry is
  // still the old entry that we want to set.

  SessionHistoryEntry* entry =
      aContext.get_canonical()->GetActiveSessionHistoryEntry();

  if (entry) {
    nsSHistory::WalkContiguousEntries(
        entry, [&](SessionHistoryEntry* aEntry) { aEntry->SetName(aName); });
  }

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvSessionHistoryEntryCacheKey(
    const MaybeDiscarded<BrowsingContext>& aContext,
    const uint32_t& aCacheKey) {
  if (aContext.IsNullOrDiscarded()) {
    return IPC_OK();
  }

  SessionHistoryEntry* entry =
      aContext.get_canonical()->GetActiveSessionHistoryEntry();
  if (entry) {
    entry->SetCacheKey(aCacheKey);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvSessionHistoryEntryWireframe(
    const MaybeDiscarded<BrowsingContext>& aContext,
    const Wireframe& aWireframe) {
  if (aContext.IsNull()) {
    return IPC_OK();
  }

  BrowsingContext* bc = aContext.GetMaybeDiscarded();
  if (!bc) {
    return IPC_OK();
  }

  SessionHistoryEntry* entry = bc->Canonical()->GetActiveSessionHistoryEntry();
  if (entry) {
    entry->SetWireframe(Some(aWireframe));
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult
ContentParent::RecvGetLoadingSessionHistoryInfoFromParent(
    const MaybeDiscarded<BrowsingContext>& aContext,
    GetLoadingSessionHistoryInfoFromParentResolver&& aResolver) {
  if (aContext.IsNullOrDiscarded()) {
    return IPC_OK();
  }

  Maybe<LoadingSessionHistoryInfo> info;
  aContext.get_canonical()->GetLoadingSessionHistoryInfoFromParent(info);
  aResolver(info);

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvSynchronizeNavigationAPIState(
    const MaybeDiscarded<BrowsingContext>& aContext,
    NotNull<nsStructuredCloneContainer*> aState) {
  if (aContext.IsNullOrDiscarded()) {
    return IPC_OK();
  }

  aContext.get_canonical()->SynchronizeNavigationAPIState(aState);
  return IPC_OK();
}

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

  nsCOMPtr<nsFrameLoaderOwner> owner =
      do_QueryInterface(aContext.get_canonical()->GetEmbedderElement());
  if (!owner) {
    return IPC_OK();
  }

  RefPtr<nsFrameLoader> frameLoader = owner->GetFrameLoader();
  if (!frameLoader || !frameLoader->GetMaybePendingBrowsingContext()) {
    return IPC_OK();
  }

  nsCOMPtr<nsISHistory> shistory = frameLoader->GetMaybePendingBrowsingContext()
                                       ->Canonical()
                                       ->GetSessionHistory();
  if (!shistory) {
    return IPC_OK();
  }

  uint32_t count = shistory->GetCount();
  for (uint32_t i = 0; i < count; ++i) {
    nsCOMPtr<nsISHEntry> entry;
    shistory->GetEntryAtIndex(i, getter_AddRefs(entry));
    RefPtr she = entry->GetAsSessionHistoryEntry();
    if (RefPtr<nsFrameLoader> frameLoader = she->GetFrameLoader()) {
      if (frameLoader->GetMaybePendingBrowsingContext() == aContext.get()) {
        she->SetFrameLoader(nullptr);
        frameLoader->Destroy();
        break;
      }
    }
  }

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvSetActiveSessionHistoryEntry(
    const MaybeDiscarded<BrowsingContext>& aContext,
    const Maybe<nsPoint>& aPreviousScrollPos, SessionHistoryInfo&& aInfo,
    uint32_t aLoadType, uint32_t aUpdatedCacheKey, const nsID& aChangeID) {
  if (!aContext.IsNullOrDiscarded()) {
    aContext.get_canonical()->SetActiveSessionHistoryEntry(
        aPreviousScrollPos, &aInfo, aLoadType, aUpdatedCacheKey, aChangeID);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvReplaceActiveSessionHistoryEntry(
    const MaybeDiscarded<BrowsingContext>& aContext,
    SessionHistoryInfo&& aInfo) {
  if (!aContext.IsNullOrDiscarded()) {
    aContext.get_canonical()->ReplaceActiveSessionHistoryEntry(&aInfo);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult
ContentParent::RecvRemoveDynEntriesFromActiveSessionHistoryEntry(
    const MaybeDiscarded<BrowsingContext>& aContext) {
  if (!aContext.IsNullOrDiscarded()) {
    aContext.get_canonical()->RemoveDynEntriesFromActiveSessionHistoryEntry();
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvRemoveFromSessionHistory(
    const MaybeDiscarded<BrowsingContext>& aContext, const nsID& aChangeID) {
  if (!aContext.IsNullOrDiscarded()) {
    aContext.get_canonical()->RemoveFromSessionHistory(aChangeID);
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvHistoryReload(
    const MaybeDiscarded<BrowsingContext>& aContext,
    const uint32_t aReloadFlags) {
  if (!aContext.IsNullOrDiscarded()) {
    nsCOMPtr<nsISHistory> shistory =
        aContext.get_canonical()->GetSessionHistory();
    if (shistory) {
      shistory->Reload(aReloadFlags);
    }
  }
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvConsumeHistoryActivation(
    const MaybeDiscarded<BrowsingContext>& aTop) {
  if (aTop.IsNullOrDiscarded()) {
    return IPC_OK();
  }
  aTop->Group()->EachOtherParent(this, [aTop](ContentParent* aParent) {
    (void)aParent->SendConsumeHistoryActivation(aTop);
  });
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvCommitWindowContextTransaction(
    const MaybeDiscarded<WindowContext>& aContext,
    WindowContext::BaseTransaction&& aTransaction, uint64_t aEpoch) {
  // Record the new BrowsingContextFieldEpoch associated with this transaction.
  // This should be done unconditionally, so that we're always in-sync.
  //
  // The order the parent process receives transactions is considered the
  // "canonical" ordering, so we don't need to worry about doing any
  // epoch-related validation.
  MOZ_ASSERT(aEpoch == mBrowsingContextFieldEpoch + 1,
             "Child process skipped an epoch?");
  mBrowsingContextFieldEpoch = aEpoch;

  return aTransaction.CommitFromIPC(aContext, this);
}

NS_IMETHODIMP ContentParent::GetChildID(uint64_t* aOut) {
  *aOut = this->ChildID();
  return NS_OK;
}

NS_IMETHODIMP ContentParent::GetOsPid(int32_t* aOut) {
  *aOut = Pid();
  return NS_OK;
}

NS_IMETHODIMP ContentParent::GetRemoteType(nsACString& aRemoteType) {
  aRemoteType = GetRemoteType();
  return NS_OK;
}

void ContentParent::StartRemoteWorkerService() {
  MOZ_ASSERT(!mRemoteWorkerServiceActor);
  MOZ_ASSERT(mRemoteType != PREALLOC_REMOTE_TYPE);

  Endpoint<PRemoteWorkerServiceChild> childEp;
  mRemoteWorkerServiceActor =
      RemoteWorkerServiceParent::CreateForProcess(this, &childEp);

  Endpoint<PRemoteWorkerDebuggerManagerChild> remoteDebuggerChildEp;
  mRemoteWorkerDebuggerManagerActor =
      RemoteWorkerDebuggerManagerParent::CreateForProcess(
          &remoteDebuggerChildEp);
  if (mRemoteWorkerServiceActor) {
    (void)SendInitRemoteWorkerService(std::move(childEp),
                                      std::move(remoteDebuggerChildEp));
  }
}

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

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

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

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

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

IPCResult ContentParent::RecvFOGData(ByteBuf&& buf) {
  glean::FOGData(std::move(buf));
  return IPC_OK();
}

RefPtr<FlushFOGDataPromise> ContentParent::DoFlushFOGData() {
  MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
          ("FOGTransportActor exists: %s",
           !!mFOGTransportParentActor ? "true" : "false"));
  if (mFOGTransportParentActor) {
    nsCOMPtr<nsISerialEventTarget> queue =
        glean::FOGTransportParent::GetQueue();
    MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
            ("Calling FOGtransportParentActor->SendFlushFOGData()"));
    return InvokeAsync(queue, __func__,
                       [transport = RefPtr{mFOGTransportParentActor}] {
                         return transport->SendFlushFOGData();
                       });
  }
  MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
          ("Calling ContentParent->SendFlushFOGData()"));
  return SendFlushFOGData();
}

mozilla::ipc::IPCResult ContentParent::RecvGeckoTraceExport(ByteBuf&& aBuf) {
  recv_gecko_trace_export(aBuf.mData, aBuf.mLen);
  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvSetContainerFeaturePolicy(
    const MaybeDiscardedBrowsingContext& aContainerContext,
    MaybeFeaturePolicyInfo&& aContainerFeaturePolicyInfo) {
  if (aContainerContext.IsNullOrDiscarded()) {
    return IPC_OK();
  }

  auto* context = aContainerContext.get_canonical();
  context->SetContainerFeaturePolicy(std::move(aContainerFeaturePolicyInfo));

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvUpdateAncestorOriginsList(
    const MaybeDiscardedBrowsingContext& aContext) {
  if (!aContext.IsNullOrDiscarded()) {
    auto* canonical = aContext.get_canonical();
    if (WindowGlobalParent* windowGlobal =
            canonical->GetCurrentWindowGlobal()) {
      canonical->CreateRedactedAncestorOriginsList(
          windowGlobal->DocumentPrincipal(),
          canonical->GetEmbedderFrameReferrerPolicy());
    }
  }

  return IPC_OK();
}

mozilla::ipc::IPCResult ContentParent::RecvSetReferrerPolicyForEmbedderFrame(
    const MaybeDiscardedBrowsingContext& aContext,
    const ReferrerPolicy& aPolicy) {
  if (!aContext.IsNullOrDiscarded()) {
    auto* canonical = aContext.get_canonical();
    canonical->SetEmbedderFrameReferrerPolicy(aPolicy);
  }

  return IPC_OK();
}

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

ContentParent* ContentParent::AsContentParent() { return this; }

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

IPCResult ContentParent::RecvGetSystemIcon(nsIURI* aURI,
                                           GetSystemIconResolver&& aResolver) {
  using ResolverArgs = std::tuple<const nsresult&, mozilla::Maybe<ByteBuf>&&>;

  if (!aURI) {
    Maybe<ByteBuf> bytebuf = Nothing();
    aResolver(ResolverArgs(NS_ERROR_NULL_POINTER, std::move(bytebuf)));
    return IPC_OK();
  }

#if defined(MOZ_WIDGET_GTK)
  Maybe<ByteBuf> bytebuf = Some(ByteBuf{});
  nsresult rv = nsIconChannel::GetIcon(aURI, bytebuf.ptr());
  if (NS_WARN_IF(NS_FAILED(rv))) {
    bytebuf = Nothing();
  }
  aResolver(ResolverArgs(rv, std::move(bytebuf)));
  return IPC_OK();
#elif defined(XP_WIN)
  nsIconChannel::GetIconAsync(aURI)->Then(
      GetCurrentSerialEventTarget(), __func__,
      [aResolver](ByteBuf&& aByteBuf) {
        Maybe<ByteBuf> bytebuf = Some(std::move(aByteBuf));
        aResolver(ResolverArgs(NS_OK, std::move(bytebuf)));
      },
      [aResolver](nsresult aErr) {
        Maybe<ByteBuf> bytebuf = Nothing();
        aResolver(ResolverArgs(aErr, std::move(bytebuf)));
      });
  return IPC_OK();
#else
  MOZ_CRASH(
      "This message is currently implemented only on GTK and Windows "
      "platforms");
#endif
}

IPCResult ContentParent::RecvGetSystemGeolocationPermissionBehavior(
    GetSystemGeolocationPermissionBehaviorResolver&& aResolver) {
  PermissionObserver::EnsureMonitoringInParent(PermissionName::Geolocation);
  aResolver(Geolocation::GetLocationOSPermission());
  return IPC_OK();
}

/* static */
void ContentParent::BroadcastSystemPermissionChanged(PermissionName aName,
                                                     PermissionState aState) {
  for (auto* cp : AllProcesses(eLive)) {
    (void)cp->SendSystemPermissionChanged(aName, aState);
  }
}

IPCResult ContentParent::RecvRequestGeolocationPermissionFromUser(
    const MaybeDiscardedBrowsingContext& aBrowsingContext,
    RequestGeolocationPermissionFromUserResolver&& aResolver) {
  if (MOZ_UNLIKELY(aBrowsingContext.IsNullOrDiscarded())) {
    aResolver(GeolocationPermissionStatus::Error);
    return IPC_OK();
  }
  RefPtr<BrowsingContext> browsingContext = aBrowsingContext.get();

  Geolocation::ReallowWithSystemPermissionOrCancel(browsingContext,
                                                   std::move(aResolver));
  return IPC_OK();
}

#ifdef FUZZING_SNAPSHOT
IPCResult ContentParent::RecvSignalFuzzingReady() {
  // No action needed here, we already observe this message directly
  // on the channel and act accordingly.
  return IPC_OK();
}
#endif

#ifdef FUZZING
IPCResult ContentParent::RecvKillGPUProcess() {
  gfx::GPUProcessManager* gpm = gfx::GPUProcessManager::Get();
  if (gpm) {
    gpm->KillProcess();
  }
  return IPC_OK();
}
#endif

nsCString ThreadsafeContentParentHandle::GetRemoteType() {
  RecursiveMutexAutoLock lock(mMutex);
  return mRemoteType;
}

UniqueThreadsafeContentParentKeepAlive
ThreadsafeContentParentHandle::TryAddKeepAlive(uint64_t aBrowserId) {
  RecursiveMutexAutoLock lock(mMutex);
  // If shutdown has already started, we can't keep this ContentParent alive
  // anymore.
  if (mShutdownStarted) {
    return nullptr;
  }

  // Otherwise, ensure there is an entry for this BrowserId, and increment it.
  ++mKeepAlivesPerBrowserId.LookupOrInsert(aBrowserId, 0);
  return UniqueThreadsafeContentParentKeepAlive{do_AddRef(this).take(),
                                                {.mBrowserId = aBrowserId}};
}

}  // namespace dom
}  // namespace mozilla

NS_IMPL_ISUPPORTS(ParentIdleListener, nsIObserver)

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

#undef LOGPDM
