summaryrefslogtreecommitdiffstats
path: root/dom/ipc/ContentParent.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/ipc/ContentParent.cpp')
-rw-r--r--dom/ipc/ContentParent.cpp8227
1 files changed, 8227 insertions, 0 deletions
diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp
new file mode 100644
index 0000000000..a6e95d04bf
--- /dev/null
+++ b/dom/ipc/ContentParent.cpp
@@ -0,0 +1,8227 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifdef MOZ_WIDGET_ANDROID
+# include "AndroidDecoderModule.h"
+#endif
+
+#include "mozilla/AppShutdown.h"
+#include "mozilla/DebugOnly.h"
+
+#include "base/basictypes.h"
+#include "base/shared_memory.h"
+
+#include "ContentParent.h"
+#include "mozilla/ipc/ProcessUtils.h"
+#include "mozilla/CmdLineAndEnvUtils.h"
+#include "BrowserParent.h"
+
+#include "chrome/common/process_watcher.h"
+#include "mozilla/Result.h"
+#include "mozilla/XREAppData.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIBrowserDOMWindow.h"
+
+#include "GMPServiceParent.h"
+#include "HandlerServiceParent.h"
+#include "IHistory.h"
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+# include "mozilla/a11y/AccessibleWrap.h"
+# include "mozilla/a11y/Compatibility.h"
+#endif
+#include <map>
+#include <utility>
+
+#include "ContentProcessManager.h"
+#include "GeckoProfiler.h"
+#include "Geolocation.h"
+#include "GfxInfoBase.h"
+#include "MMPrinter.h"
+#include "PreallocatedProcessManager.h"
+#include "ProcessPriorityManager.h"
+#include "ProfilerParent.h"
+#include "SandboxHal.h"
+#include "SourceSurfaceRawData.h"
+#include "mozilla/ipc/URIUtils.h"
+#include "gfxPlatform.h"
+#include "gfxPlatformFontList.h"
+#include "nsDNSService2.h"
+#include "nsPIDNSService.h"
+#include "mozilla/AntiTrackingUtils.h"
+#include "mozilla/AppShutdown.h"
+#include "mozilla/AutoRestore.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/BenchmarkStorageParent.h"
+#include "mozilla/Casting.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/ClipboardWriteRequestParent.h"
+#include "mozilla/ContentBlockingUserInteraction.h"
+#include "mozilla/FOGIPC.h"
+#include "mozilla/GlobalStyleSheetCache.h"
+#include "mozilla/GeckoArgs.h"
+#include "mozilla/HangDetails.h"
+#include "mozilla/LoginReputationIPC.h"
+#include "mozilla/LookAndFeel.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/NullPrincipal.h"
+#include "mozilla/PerformanceMetricsCollector.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/ScopeExit.h"
+#include "mozilla/ScriptPreloader.h"
+#include "mozilla/Components.h"
+#include "mozilla/Sprintf.h"
+#include "mozilla/StaticPrefs_dom.h"
+#include "mozilla/StaticPrefs_fission.h"
+#include "mozilla/StaticPrefs_media.h"
+#include "mozilla/StaticPrefs_network.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/TelemetryIPC.h"
+#include "mozilla/Unused.h"
+#include "mozilla/WebBrowserPersistDocumentParent.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/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/MemoryReportRequest.h"
+#include "mozilla/dom/MediaStatusManager.h"
+#include "mozilla/dom/Notification.h"
+#include "mozilla/dom/PContentPermissionRequestParent.h"
+#include "mozilla/dom/PCycleCollectWithLogsParent.h"
+#include "mozilla/dom/ParentProcessMessageManager.h"
+#include "mozilla/dom/Permissions.h"
+#include "mozilla/dom/ProcessMessageManager.h"
+#include "mozilla/dom/PushNotifier.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/StorageIPC.h"
+#include "mozilla/dom/URLClassifierParent.h"
+#include "mozilla/dom/WindowGlobalParent.h"
+#include "mozilla/dom/ipc/SharedMap.h"
+#include "mozilla/dom/ipc/StructuredCloneData.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"
+#include "mozilla/glean/GleanPings.h"
+#include "mozilla/hal_sandbox/PHalParent.h"
+#include "mozilla/intl/L10nRegistry.h"
+#include "mozilla/intl/LocaleService.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/TestShellParent.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/CookieServiceParent.h"
+#include "mozilla/net/NeckoMessageUtils.h"
+#include "mozilla/net/NeckoParent.h"
+#include "mozilla/net/PCookieServiceParent.h"
+#include "mozilla/net/CookieKey.h"
+#include "mozilla/net/TRRService.h"
+#include "mozilla/TelemetryComms.h"
+#include "mozilla/TelemetryEventEnums.h"
+#include "mozilla/RemoteLazyInputStreamParent.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 "nsChromeRegistryChrome.h"
+#include "nsConsoleMessage.h"
+#include "nsConsoleService.h"
+#include "nsContentPermissionHelper.h"
+#include "nsContentUtils.h"
+#include "nsCRT.h"
+#include "nsDebugImpl.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsDocShell.h"
+#include "nsEmbedCID.h"
+#include "nsFocusManager.h"
+#include "nsFrameLoader.h"
+#include "nsFrameMessageManager.h"
+#include "nsHashPropertyBag.h"
+#include "nsHyphenationManager.h"
+#include "nsIAlertsService.h"
+#include "nsIAppShell.h"
+#include "nsIAppWindow.h"
+#include "nsIAsyncInputStream.h"
+#include "nsIBidiKeyboard.h"
+#include "nsICaptivePortalService.h"
+#include "nsICertOverrideService.h"
+#include "nsIClipboard.h"
+#include "nsIContentProcess.h"
+#include "nsIContentSecurityPolicy.h"
+#include "nsICookie.h"
+#include "nsICrashService.h"
+#include "nsICycleCollectorListener.h"
+#include "nsIDOMChromeWindow.h"
+#include "nsIDocShell.h"
+#include "nsIDocShellTreeOwner.h"
+#include "nsIDragService.h"
+#include "nsIExternalProtocolService.h"
+#include "nsIGfxInfo.h"
+#include "nsIUserIdleService.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsILocalStorageManager.h"
+#include "nsIMemoryInfoDumper.h"
+#include "nsIMemoryReporter.h"
+#include "nsIMozBrowserFrame.h"
+#include "nsINetworkLinkService.h"
+#include "nsIObserverService.h"
+#include "nsIParentChannel.h"
+#include "nsIScriptError.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsIServiceWorkerManager.h"
+#include "nsISiteSecurityService.h"
+#include "nsISound.h"
+#include "nsIStringBundle.h"
+#include "nsITimer.h"
+#include "nsIURL.h"
+#include "nsIWebBrowserChrome.h"
+#include "nsIX509Cert.h"
+#include "nsIXULRuntime.h"
+#if defined(MOZ_WIDGET_GTK) || defined(XP_WIN)
+# include "nsIconChannel.h"
+#endif
+#include "nsMemoryInfoDumper.h"
+#include "nsMemoryReporterManager.h"
+#include "nsOpenURIInFrameParams.h"
+#include "nsPIWindowWatcher.h"
+#include "nsPluginHost.h"
+#include "nsPluginTags.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 "nsWidgetsCID.h"
+#include "nsWindowWatcher.h"
+#include "prio.h"
+#include "private/pprio.h"
+#include "xpcpublic.h"
+#include "nsOpenWindowInfo.h"
+#include "nsFrameLoaderOwner.h"
+
+#ifdef MOZ_WEBRTC
+# include "jsapi/WebrtcGlobalParent.h"
+#endif
+
+#if defined(XP_MACOSX)
+# include "nsMacUtilsImpl.h"
+# include "mozilla/AvailableMemoryWatcher.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 "mozilla/RemoteSpellCheckEngineParent.h"
+
+#include "Crypto.h"
+
+#ifdef MOZ_WEBSPEECH
+# include "mozilla/dom/SpeechSynthesisParent.h"
+#endif
+
+#if defined(MOZ_SANDBOX)
+# include "mozilla/SandboxSettings.h"
+# if defined(XP_LINUX)
+# include "mozilla/SandboxInfo.h"
+# include "mozilla/SandboxBroker.h"
+# include "mozilla/SandboxBrokerPolicyFactory.h"
+# endif
+# if defined(XP_MACOSX)
+# include "mozilla/Sandbox.h"
+# endif
+#endif
+
+#ifdef XP_WIN
+# include "mozilla/widget/AudioSession.h"
+# include "mozilla/WinDllServices.h"
+#endif
+
+#ifdef MOZ_CODE_COVERAGE
+# include "mozilla/CodeCoverageHandler.h"
+#endif
+
+#ifdef FUZZING_SNAPSHOT
+# include "mozilla/fuzzing/IPCFuzzController.h"
+#endif
+
+// For VP9Benchmark::sBenchmarkFpsPref
+#include "Benchmark.h"
+
+#include "nsIToolkitProfileService.h"
+#include "nsIToolkitProfile.h"
+
+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)
+
+namespace mozilla {
+namespace CubebUtils {
+extern FileDescriptor CreateAudioIPCConnection();
+}
+
+namespace dom {
+
+LazyLogModule gProcessLog("Process");
+
+static std::map<RemoteDecodeIn, media::MediaCodecsSupported> sCodecsSupported;
+
+/* static */
+uint32_t ContentParent::sMaxContentProcesses = 0;
+
+/* static */
+LogModule* ContentParent::GetLog() { return gProcessLog; }
+
+/* static */
+uint32_t ContentParent::sPageLoadEventCounter = 0;
+
+#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 {
+ Unused << mSink->CloseGCLog();
+ return IPC_OK();
+ }
+
+ virtual mozilla::ipc::IPCResult RecvCloseCCLog() override {
+ Unused << 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));
+ Unused << 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;
+}
+
+class ScriptableCPInfo final : public nsIContentProcessInfo {
+ public:
+ explicit ScriptableCPInfo(ContentParent* aParent) : mContentParent(aParent) {
+ MOZ_ASSERT(mContentParent);
+ }
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSICONTENTPROCESSINFO
+
+ void ProcessDied() { mContentParent = nullptr; }
+
+ private:
+ ~ScriptableCPInfo() { MOZ_ASSERT(!mContentParent, "must call ProcessDied"); }
+
+ ContentParent* mContentParent;
+};
+
+NS_IMPL_ISUPPORTS(ScriptableCPInfo, nsIContentProcessInfo)
+
+NS_IMETHODIMP
+ScriptableCPInfo::GetIsAlive(bool* aIsAlive) {
+ *aIsAlive = mContentParent != nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ScriptableCPInfo::GetProcessId(int32_t* aPID) {
+ if (!mContentParent) {
+ *aPID = -1;
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ *aPID = mContentParent->Pid();
+ if (*aPID == -1) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ScriptableCPInfo::GetTabCount(int32_t* aTabCount) {
+ if (!mContentParent) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+ *aTabCount = cpm->GetBrowserParentCountByProcessId(mContentParent->ChildID());
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ScriptableCPInfo::GetMessageManager(nsISupports** aMessenger) {
+ *aMessenger = nullptr;
+ if (!mContentParent) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ RefPtr<ProcessMessageManager> manager = mContentParent->GetMessageManager();
+ manager.forget(aMessenger);
+ return NS_OK;
+}
+
+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<nsTHashMap<nsUint32HashKey, ContentParent*>>
+ ContentParent::sJSPluginContentParents;
+StaticAutoPtr<LinkedList<ContentParent>> ContentParent::sContentParents;
+StaticRefPtr<ContentParent> ContentParent::sRecycledE10SProcess;
+#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
+
+// The first content child has ID 1, so the chrome process can have ID 0.
+static uint64_t gContentChildID = 1;
+
+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",
+ "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,
+};
+
+// PreallocateProcess is called by the PreallocatedProcessManager.
+// ContentParent then takes this process back within GetNewOrUsedBrowserProcess.
+/*static*/ already_AddRefed<ContentParent>
+ContentParent::MakePreallocProcess() {
+ RefPtr<ContentParent> process = new ContentParent(PREALLOC_REMOTE_TYPE);
+ return process.forget();
+}
+
+/*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(new 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);
+}
+
+const 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);
+}
+
+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 IsPrivilegedMozillaRemoteType(const nsACString& aContentProcessType) {
+ return aContentProcessType == PRIVILEGEDMOZILLA_REMOTE_TYPE;
+}
+
+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
+ // We process the toRelease array outside of the iteration to avoid modifying
+ // the list (via RemoveFromList()) while we're iterating it.
+ nsTArray<ContentParent*> toRelease;
+ for (const auto& contentParents : sBrowserContentParents->Values()) {
+ // Shutting down these processes will change the array so let's use another
+ // array for the removal.
+ for (auto* cp : *contentParents) {
+ if (cp->ManagedPBrowserParent().Count() == 0 &&
+ !cp->HasActiveWorkerOrJSPlugin() &&
+ cp->mRemoteType == DEFAULT_REMOTE_TYPE) {
+ toRelease.AppendElement(cp);
+ } else {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ (" Skipping %p (%s), count %d, HasActiveWorkerOrJSPlugin %d",
+ cp, cp->mRemoteType.get(), cp->ManagedPBrowserParent().Count(),
+ cp->HasActiveWorkerOrJSPlugin()));
+ }
+ }
+ }
+
+ for (auto* cp : toRelease) {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ (" Shutdown %p (%s)", cp, cp->mRemoteType.get()));
+ PreallocatedProcessManager::Erase(cp);
+ // Start a soft shutdown.
+ cp->ShutDownProcess(SEND_SHUTDOWN_MESSAGE);
+ // Make sure we don't select this process for new tabs.
+ cp->MarkAsDead();
+ // 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) {
+ uint32_t maxSelectable =
+ std::min(static_cast<uint32_t>(aContentParents.Length()),
+ static_cast<uint32_t>(aMaxContentParents));
+ uint32_t min = INT_MAX;
+ RefPtr<ContentParent> candidate;
+ ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+
+ for (uint32_t i = 0; i < maxSelectable; i++) {
+ ContentParent* p = aContentParents[i];
+ MOZ_DIAGNOSTIC_ASSERT(!p->IsDead());
+
+ // Ignore processes that were slated for removal but not yet removed from
+ // the pool (see also GetUsedBrowserProcess and BlockShutdown).
+ if (!p->IsShuttingDown()) {
+ uint32_t tabCount = cpm->GetBrowserParentCountByProcessId(p->ChildID());
+ if (tabCount < min) {
+ candidate = p;
+ min = tabCount;
+ }
+ }
+ }
+
+ // 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*/
+already_AddRefed<ContentParent> ContentParent::GetUsedBrowserProcess(
+ const nsACString& aRemoteType, nsTArray<ContentParent*>& aContentParents,
+ uint32_t aMaxContentParents, bool aPreferUsed, ProcessPriority aPriority) {
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ AutoRestore ar(sInProcessSelector);
+ sInProcessSelector = true;
+#endif
+
+ uint32_t numberOfParents = aContentParents.Length();
+ nsTArray<RefPtr<nsIContentProcessInfo>> infos(numberOfParents);
+ for (auto* cp : aContentParents) {
+ infos.AppendElement(cp->mScriptableHelper);
+ }
+
+ 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;
+ }
+
+ nsCOMPtr<nsIContentProcessProvider> cpp =
+ do_GetService("@mozilla.org/ipc/processselector;1");
+ int32_t index;
+ if (cpp && NS_SUCCEEDED(cpp->ProvideProcess(aRemoteType, infos,
+ aMaxContentParents, &index))) {
+ // If the provider returned an existing ContentParent, use that one.
+ if (0 <= index && static_cast<uint32_t>(index) <= aMaxContentParents) {
+ RefPtr<ContentParent> retval = aContentParents[index];
+ // Ignore processes that were slated for removal but not yet removed from
+ // the pool.
+ if (!retval->IsShuttingDown()) {
+ if (profiler_thread_is_being_profiled_for_markers()) {
+ nsPrintfCString marker("Reused process %u",
+ (unsigned int)retval->ChildID());
+ PROFILER_MARKER_TEXT("Process", DOM, {}, marker);
+ }
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("GetUsedProcess: Reused process %p (%u) for %s", retval.get(),
+ (unsigned int)retval->ChildID(),
+ PromiseFlatCString(aRemoteType).get()));
+ retval->AssertAlive();
+ retval->StopRecyclingE10SOnly(true);
+ return retval.forget();
+ }
+ }
+ } else {
+ // If there was a problem with the JS chooser, fall back to a random
+ // selection.
+ NS_WARNING("nsIContentProcessProvider failed to return a process");
+ RefPtr<ContentParent> random;
+ if ((random = MinTabSelect(aContentParents, aMaxContentParents))) {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("GetUsedProcess: Reused random process %p (%d) for %s",
+ random.get(), (unsigned int)random->ChildID(),
+ PromiseFlatCString(aRemoteType).get()));
+ random->AssertAlive();
+ random->StopRecyclingE10SOnly(true);
+ return random.forget();
+ }
+ }
+
+ // If we are loading into the "web" remote type, are choosing to launch a new
+ // tab, and have a recycled E10S process, we should launch into that process.
+ if (aRemoteType == DEFAULT_REMOTE_TYPE && sRecycledE10SProcess) {
+ RefPtr<ContentParent> recycled = sRecycledE10SProcess;
+ MOZ_DIAGNOSTIC_ASSERT(recycled->GetRemoteType() == DEFAULT_REMOTE_TYPE);
+ recycled->AssertAlive();
+ recycled->StopRecyclingE10SOnly(true);
+ if (profiler_thread_is_being_profiled_for_markers()) {
+ nsPrintfCString marker("Recycled process %u (%p)",
+ (unsigned int)recycled->ChildID(), recycled.get());
+ PROFILER_MARKER_TEXT("Process", DOM, {}, marker);
+ }
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("Recycled process %p", recycled.get()));
+
+ return recycled.forget();
+ }
+
+ // Try to take a preallocated process except for certain remote types.
+ // Note: this process may not have finished launching yet
+ RefPtr<ContentParent> preallocated;
+ if (aRemoteType != FILE_REMOTE_TYPE &&
+ aRemoteType != PRIVILEGEDABOUT_REMOTE_TYPE &&
+ aRemoteType != EXTENSION_REMOTE_TYPE && // Bug 1638119
+ (preallocated = PreallocatedProcessManager::Take(aRemoteType))) {
+ MOZ_DIAGNOSTIC_ASSERT(preallocated->GetRemoteType() ==
+ PREALLOC_REMOTE_TYPE);
+ MOZ_DIAGNOSTIC_ASSERT(sRecycledE10SProcess != preallocated);
+ 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 %p for type %s%s",
+ preallocated.get(), 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);
+ {
+ MutexAutoLock lock(preallocated->mThreadsafeHandle->mMutex);
+ preallocated->mThreadsafeHandle->mRemoteType = preallocated->mRemoteType;
+ }
+ preallocated->mRemoteTypeIsolationPrincipal =
+ CreateRemoteTypeIsolationPrincipal(aRemoteType);
+ preallocated->mActivateTS = TimeStamp::Now();
+ preallocated->AddToPool(aContentParents);
+
+ // rare, but will happen
+ if (!preallocated->IsLaunching()) {
+ // Specialize this process for the appropriate remote type, and activate
+ // it.
+
+ Unused << preallocated->SendRemoteType(preallocated->mRemoteType,
+ preallocated->mProfile);
+
+ nsCOMPtr<nsIObserverService> obs =
+ mozilla::services::GetObserverService();
+ if (obs) {
+ nsAutoString cpId;
+ cpId.AppendInt(static_cast<uint64_t>(preallocated->ChildID()));
+ obs->NotifyObservers(static_cast<nsIObserver*>(preallocated),
+ "process-type-set", cpId.get());
+ preallocated->AssertAlive();
+ }
+ }
+ return preallocated.forget();
+ }
+
+ return nullptr;
+}
+
+/*static*/
+already_AddRefed<ContentParent>
+ContentParent::GetNewOrUsedLaunchingBrowserProcess(
+ const nsACString& aRemoteType, BrowsingContextGroup* aGroup,
+ ProcessPriority aPriority, bool aPreferUsed) {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("GetNewOrUsedProcess for type %s",
+ PromiseFlatCString(aRemoteType).get()));
+
+ // Fallback check (we really want our callers to avoid this).
+ if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
+ MOZ_DIAGNOSTIC_ASSERT(
+ false, "Late attempt to GetNewOrUsedLaunchingBrowserProcess!");
+ 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.
+ RefPtr<ContentParent> contentParent;
+ if (aGroup) {
+ contentParent = aGroup->GetHostProcess(aRemoteType);
+ Unused << NS_WARN_IF(contentParent && contentParent->IsShuttingDown());
+ if (contentParent && !contentParent->IsShuttingDown()) {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("GetNewOrUsedProcess: Existing host process %p (launching %d)",
+ contentParent.get(), contentParent->IsLaunching()));
+ contentParent->AssertAlive();
+ contentParent->StopRecyclingE10SOnly(true);
+ return contentParent.forget();
+ }
+ }
+
+ nsTArray<ContentParent*>& contentParents = GetOrCreatePool(aRemoteType);
+ uint32_t maxContentParents = GetMaxProcessCount(aRemoteType);
+
+ // Let's try and reuse an existing process.
+ contentParent = GetUsedBrowserProcess(
+ aRemoteType, contentParents, maxContentParents, aPreferUsed, aPriority);
+
+ 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()));
+
+ contentParent = new ContentParent(aRemoteType);
+ if (NS_WARN_IF(!contentParent->BeginSubprocessLaunch(aPriority))) {
+ // Launch aborted because of shutdown. Bailout.
+ contentParent->LaunchSubprocessReject();
+ return nullptr;
+ }
+ // 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);
+
+ // Store this process for future reuse.
+ contentParent->AddToPool(contentParents);
+
+ MOZ_LOG(
+ ContentParent::GetLog(), LogLevel::Debug,
+ ("GetNewOrUsedProcess: new immediate process %p", contentParent.get()));
+ }
+ // else we have an existing or preallocated process (which may be
+ // still launching)
+
+ contentParent->AssertAlive();
+ contentParent->StopRecyclingE10SOnly(true);
+ if (aGroup) {
+ aGroup->EnsureHostProcess(contentParent);
+ }
+ return contentParent.forget();
+}
+
+/*static*/
+RefPtr<ContentParent::LaunchPromise>
+ContentParent::GetNewOrUsedBrowserProcessAsync(const nsACString& aRemoteType,
+ BrowsingContextGroup* aGroup,
+ ProcessPriority aPriority,
+ bool aPreferUsed) {
+ // Obtain a `ContentParent` launched asynchronously.
+ RefPtr<ContentParent> contentParent = GetNewOrUsedLaunchingBrowserProcess(
+ aRemoteType, aGroup, aPriority, aPreferUsed);
+ if (!contentParent) {
+ // In case of launch error, stop here.
+ return LaunchPromise::CreateAndReject(NS_ERROR_ILLEGAL_DURING_SHUTDOWN,
+ __func__);
+ }
+ return contentParent->WaitForLaunchAsync(aPriority);
+}
+
+/*static*/
+already_AddRefed<ContentParent> ContentParent::GetNewOrUsedBrowserProcess(
+ const nsACString& aRemoteType, BrowsingContextGroup* aGroup,
+ ProcessPriority aPriority, bool aPreferUsed) {
+ RefPtr<ContentParent> contentParent = GetNewOrUsedLaunchingBrowserProcess(
+ aRemoteType, aGroup, aPriority, aPreferUsed);
+ if (!contentParent || !contentParent->WaitForLaunchSync(aPriority)) {
+ // In case of launch error, stop here.
+ return nullptr;
+ }
+ return contentParent.forget();
+}
+
+RefPtr<ContentParent::LaunchPromise> ContentParent::WaitForLaunchAsync(
+ ProcessPriority aPriority) {
+ MOZ_DIAGNOSTIC_ASSERT(!IsDead());
+ if (!IsLaunching()) {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("WaitForLaunchAsync: launched"));
+ return LaunchPromise::CreateAndResolve(this, __func__);
+ }
+
+ // We've started an async content process launch.
+ Telemetry::Accumulate(Telemetry::CONTENT_PROCESS_LAUNCH_IS_SYNC, 0);
+
+ // 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 = RefPtr{this}, aPriority]() {
+ if (self->LaunchSubprocessResolve(/* aIsSync = */ false, aPriority)) {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("WaitForLaunchAsync: async, now launched"));
+ self->mActivateTS = TimeStamp::Now();
+ return LaunchPromise::CreateAndResolve(self, __func__);
+ }
+
+ self->LaunchSubprocessReject();
+ return LaunchPromise::CreateAndReject(NS_ERROR_INVALID_ARG, __func__);
+ },
+ [self = RefPtr{this}]() {
+ 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.
+ Telemetry::Accumulate(Telemetry::CONTENT_PROCESS_LAUNCH_IS_SYNC, 1);
+
+ // 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)) {
+ mActivateTS = TimeStamp::Now();
+ return true;
+ }
+ // In case of failure.
+ LaunchSubprocessReject();
+ return false;
+}
+
+/*static*/
+already_AddRefed<ContentParent> ContentParent::GetNewOrUsedJSPluginProcess(
+ uint32_t aPluginID, const hal::ProcessPriority& aPriority) {
+ RefPtr<ContentParent> p;
+ if (sJSPluginContentParents) {
+ p = sJSPluginContentParents->Get(aPluginID);
+ } else {
+ sJSPluginContentParents = new nsTHashMap<nsUint32HashKey, ContentParent*>();
+ }
+
+ if (p) {
+ return p.forget();
+ }
+
+ p = new ContentParent(aPluginID);
+
+ if (!p->LaunchSubprocessSync(aPriority)) {
+ return nullptr;
+ }
+
+ sJSPluginContentParents->InsertOrUpdate(aPluginID, p);
+
+ return p.forget();
+}
+
+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::RecvCreateGMPService() {
+ Endpoint<PGMPServiceParent> parent;
+ Endpoint<PGMPServiceChild> child;
+
+ if (mGMPCreated) {
+ return IPC_FAIL(this, "GMP Service already created");
+ }
+
+ nsresult rv;
+ rv = PGMPService::CreateEndpoints(base::GetCurrentProcId(), OtherPid(),
+ &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();
+}
+
+Atomic<bool, mozilla::Relaxed> sContentParentTelemetryEventEnabled(false);
+
+/*static*/
+void ContentParent::LogAndAssertFailedPrincipalValidationInfo(
+ nsIPrincipal* aPrincipal, const char* aMethod) {
+ // nsContentSecurityManager may also enable this same event, but that's okay
+ if (!sContentParentTelemetryEventEnabled.exchange(true)) {
+ sContentParentTelemetryEventEnabled = true;
+ Telemetry::SetEventRecordingEnabled("security"_ns, true);
+ }
+
+ // Send Telemetry
+ nsAutoCString principalScheme, principalType, spec;
+ CopyableTArray<EventExtraEntry> extra(2);
+
+ if (!aPrincipal) {
+ principalType.AssignLiteral("NullPtr");
+ } else if (aPrincipal->IsSystemPrincipal()) {
+ principalType.AssignLiteral("SystemPrincipal");
+ } else if (aPrincipal->GetIsExpandedPrincipal()) {
+ principalType.AssignLiteral("ExpandedPrincipal");
+ } else if (aPrincipal->GetIsContentPrincipal()) {
+ principalType.AssignLiteral("ContentPrincipal");
+ aPrincipal->GetSpec(spec);
+ aPrincipal->GetScheme(principalScheme);
+
+ extra.AppendElement(EventExtraEntry{"scheme"_ns, principalScheme});
+ } else {
+ principalType.AssignLiteral("Unknown");
+ }
+
+ extra.AppendElement(EventExtraEntry{"principalType"_ns, principalType});
+
+ // Do not send telemetry when chrome-debugging is enabled
+ bool isChromeDebuggingEnabled =
+ Preferences::GetBool("devtools.chrome.enabled", false);
+ if (!isChromeDebuggingEnabled) {
+ Telemetry::EventID eventType =
+ Telemetry::EventID::Security_Fissionprincipals_Contentparent;
+ Telemetry::RecordEvent(eventType, mozilla::Some(aMethod),
+ mozilla::Some(extra));
+ }
+
+ // And log it
+ MOZ_LOG(
+ ContentParent::GetLog(), LogLevel::Error,
+ (" Receiving unexpected Principal (%s) within %s",
+ aPrincipal && aPrincipal->GetIsContentPrincipal() ? spec.get()
+ : principalType.get(),
+ aMethod));
+
+#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
+}
+
+bool ContentParent::ValidatePrincipal(
+ nsIPrincipal* aPrincipal,
+ const EnumSet<ValidatePrincipalOptions>& aOptions) {
+ // If the pref says we should not validate, then there is nothing to do
+ if (!StaticPrefs::dom_security_enforceIPCBasedPrincipalVetting()) {
+ return true;
+ }
+
+ // If there is no principal, then there is nothing to validate!
+ if (!aPrincipal) {
+ return aOptions.contains(ValidatePrincipalOptions::AllowNullPtr);
+ }
+
+ // We currently do not track relationships between specific null principals
+ // and content processes, so we can not validate much here - just allow all
+ // null principals we see because they are generally safe anyway!
+ if (aPrincipal->GetIsNullPrincipal()) {
+ return true;
+ }
+
+ // Only allow the system principal if the passed in options flags
+ // request permitting the system principal.
+ if (aPrincipal->IsSystemPrincipal()) {
+ return aOptions.contains(ValidatePrincipalOptions::AllowSystem);
+ }
+
+ // XXXckerschb: we should eliminate the resource carve-out here and always
+ // validate the Principal, see Bug 1686200: Investigate Principal for pdf.js
+ if (aPrincipal->SchemeIs("resource")) {
+ return true;
+ }
+
+ // Validate each inner principal individually, allowing us to catch expanded
+ // principals containing the system principal, etc.
+ if (aPrincipal->GetIsExpandedPrincipal()) {
+ if (!aOptions.contains(ValidatePrincipalOptions::AllowExpanded)) {
+ return false;
+ }
+ // FIXME: There are more constraints on expanded principals in-practice,
+ // such as the structure of extension expanded principals. This may need
+ // to be investigated more in the future.
+ nsCOMPtr<nsIExpandedPrincipal> expandedPrincipal =
+ do_QueryInterface(aPrincipal);
+ const auto& allowList = expandedPrincipal->AllowList();
+ for (const auto& innerPrincipal : allowList) {
+ if (!ValidatePrincipal(innerPrincipal, aOptions)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // A URI with a file:// scheme can never load in a non-file content process
+ // due to sandboxing.
+ if (aPrincipal->SchemeIs("file")) {
+ // If we don't support a separate 'file' process, then we can return here.
+ if (!StaticPrefs::browser_tabs_remote_separateFileUriProcess()) {
+ return true;
+ }
+ return mRemoteType == FILE_REMOTE_TYPE;
+ }
+
+ if (aPrincipal->SchemeIs("about")) {
+ uint32_t flags = 0;
+ if (NS_FAILED(aPrincipal->GetAboutModuleFlags(&flags))) {
+ return false;
+ }
+
+ // Block principals for about: URIs which can't load in this process.
+ if (!(flags & (nsIAboutModule::URI_CAN_LOAD_IN_CHILD |
+ nsIAboutModule::URI_MUST_LOAD_IN_CHILD))) {
+ return false;
+ }
+ if (flags & nsIAboutModule::URI_MUST_LOAD_IN_EXTENSION_PROCESS) {
+ return mRemoteType == EXTENSION_REMOTE_TYPE;
+ }
+ return true;
+ }
+
+ if (!mRemoteTypeIsolationPrincipal ||
+ RemoteTypePrefix(mRemoteType) != FISSION_WEB_REMOTE_TYPE) {
+ return true;
+ }
+
+ // Web content can contain extension content frames, so a content process may
+ // send us an extension's principal.
+ auto* addonPolicy = BasePrincipal::Cast(aPrincipal)->AddonPolicy();
+ if (addonPolicy) {
+ return true;
+ }
+
+ // Ensure that the expected site-origin matches the one specified by our
+ // mRemoteTypeIsolationPrincipal.
+ nsAutoCString siteOriginNoSuffix;
+ if (NS_FAILED(aPrincipal->GetSiteOriginNoSuffix(siteOriginNoSuffix))) {
+ return false;
+ }
+ nsAutoCString remoteTypeSiteOriginNoSuffix;
+ if (NS_FAILED(mRemoteTypeIsolationPrincipal->GetSiteOriginNoSuffix(
+ remoteTypeSiteOriginNoSuffix))) {
+ return false;
+ }
+
+ return remoteTypeSiteOriginNoSuffix.Equals(siteOriginNoSuffix);
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvRemovePermission(
+ nsIPrincipal* aPrincipal, const nsACString& aPermissionType,
+ nsresult* aRv) {
+ if (!aPrincipal) {
+ return IPC_FAIL(this, "No principal");
+ }
+
+ if (!ValidatePrincipal(aPrincipal)) {
+ LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __func__);
+ }
+ *aRv = Permissions::RemovePermission(aPrincipal, aPermissionType);
+ return IPC_OK();
+}
+
+/*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");
+
+ nsAutoCString remoteType(aRemoteType);
+ if (remoteType.IsEmpty()) {
+ remoteType = DEFAULT_REMOTE_TYPE;
+ }
+
+ TabId tabId(nsContentUtils::GenerateTabId());
+
+ nsIDocShell* docShell = GetOpenerDocShellHelper(aFrameElement);
+ TabId openerTabId;
+ if (docShell) {
+ openerTabId = BrowserParent::GetTabIdFrom(docShell);
+ }
+
+ RefPtr<ContentParent> constructorSender;
+ MOZ_RELEASE_ASSERT(XRE_IsParentProcess(),
+ "Cannot allocate BrowserParent in content process");
+ if (aOpenerContentParent && !aOpenerContentParent->IsShuttingDown()) {
+ constructorSender = aOpenerContentParent;
+ } else {
+ if (aContext.IsJSPlugin()) {
+ constructorSender = GetNewOrUsedJSPluginProcess(
+ aContext.JSPluginId(), PROCESS_PRIORITY_FOREGROUND);
+ } else {
+ constructorSender = GetNewOrUsedBrowserProcess(
+ remoteType, aBrowsingContext->Group(), PROCESS_PRIORITY_FOREGROUND);
+ }
+ 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);
+
+ 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, tabId, aContext,
+ aBrowsingContext->Canonical(), chromeFlags);
+
+ // Open a remote endpoint for our PBrowser actor.
+ ManagedEndpoint<PBrowserChild> childEp =
+ constructorSender->OpenPBrowserEndpoint(browserParent);
+ if (NS_WARN_IF(!childEp.IsValid())) {
+ return nullptr;
+ }
+
+ ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+ if (NS_WARN_IF(!cpm)) {
+ return nullptr;
+ }
+ cpm->RegisterRemoteFrame(browserParent);
+
+ nsCOMPtr<nsIPrincipal> initialPrincipal =
+ NullPrincipal::Create(aBrowsingContext->OriginAttributesRef());
+ WindowGlobalInit windowInit = WindowGlobalActor::AboutBlankInitializer(
+ aBrowsingContext, initialPrincipal);
+
+ RefPtr<WindowGlobalParent> windowParent =
+ WindowGlobalParent::CreateDisconnected(windowInit);
+ 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;
+ }
+
+ // 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);
+
+ windowParent->Init();
+
+ 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) {
+ AutoTArray<StringBundleDescriptor, 1> array;
+ array.AppendElement(aBundle);
+
+ for (auto* cp : AllProcesses(eLive)) {
+ Unused << cp->SendRegisterStringBundles(array);
+ }
+}
+
+void ContentParent::BroadcastFontListChanged() {
+ for (auto* cp : AllProcesses(eLive)) {
+ Unused << cp->SendFontListChanged();
+ }
+}
+
+void ContentParent::BroadcastShmBlockAdded(uint32_t aGeneration,
+ uint32_t aIndex) {
+ auto* pfl = gfxPlatformFontList::PlatformFontList();
+ for (auto* cp : AllProcesses(eLive)) {
+ base::SharedMemoryHandle handle =
+ pfl->ShareShmBlockToProcess(aIndex, cp->Pid());
+ if (handle == base::SharedMemory::NULLHandle()) {
+ // If something went wrong here, we just skip it; the child will need to
+ // request the block as needed, at some performance cost.
+ continue;
+ }
+ Unused << cp->SendFontListShmBlockAdded(aGeneration, aIndex,
+ std::move(handle));
+ }
+}
+
+void ContentParent::BroadcastThemeUpdate(widget::ThemeChangeKind aKind) {
+ const FullLookAndFeel& lnf = *RemoteLookAndFeel::ExtractData();
+ for (auto* cp : AllProcesses(eLive)) {
+ Unused << cp->SendThemeChanged(lnf, aKind);
+ }
+}
+
+/*static */
+void ContentParent::BroadcastMediaCodecsSupportedUpdate(
+ RemoteDecodeIn aLocation, const media::MediaCodecsSupported& aSupported) {
+ // Merge incoming codec support with existing support list
+ media::MCSInfo::AddSupport(aSupported);
+ auto support = media::MCSInfo::GetSupport();
+
+ // Update processes
+ sCodecsSupported[aLocation] = support;
+ for (auto* cp : AllProcesses(eAll)) {
+ Unused << cp->SendUpdateMediaCodecsSupported(aLocation, support);
+ }
+
+ // Generate + save support string for display in about:support
+ nsCString supportString;
+ media::MCSInfo::GetMediaCodecsSupportedString(supportString, support);
+ gfx::gfxVars::SetCodecSupportInfo(supportString);
+}
+
+const nsACString& ContentParent::GetRemoteType() const { return mRemoteType; }
+
+static StaticRefPtr<nsIAsyncShutdownClient> sXPCOMShutdownClient;
+static StaticRefPtr<nsIAsyncShutdownClient> sProfileBeforeChangeClient;
+static StaticRefPtr<nsIAsyncShutdownClient> sQuitApplicationGrantedClient;
+
+void ContentParent::Init() {
+ MOZ_ASSERT(sXPCOMShutdownClient);
+
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ size_t length = ArrayLength(sObserverTopics);
+ for (size_t i = 0; i < length; ++i) {
+ obs->AddObserver(this, sObserverTopics[i], false);
+ }
+ }
+
+ if (obs) {
+ nsAutoString cpId;
+ cpId.AppendInt(static_cast<uint64_t>(this->ChildID()));
+ obs->NotifyObservers(static_cast<nsIObserver*>(this), "ipc:content-created",
+ cpId.get());
+ }
+
+#ifdef ACCESSIBILITY
+ // If accessibility is running in chrome process then start it in content
+ // process.
+ if (GetAccService()) {
+# if defined(XP_WIN)
+ // Don't init content a11y if we detect an incompat version of JAWS in use.
+ if (!mozilla::a11y::Compatibility::IsOldJAWS()) {
+ Unused << SendActivateA11y();
+ }
+# else
+ Unused << SendActivateA11y();
+# endif
+ }
+#endif // #ifdef ACCESSIBILITY
+
+ Unused << 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) {
+ Unused << NS_WARN_IF(!SendPreferenceUpdate(pref));
+ }
+ mQueuedPrefs.Clear();
+
+ Unused << SendInitNextGenLocalStorageEnabled(NextGenLocalStorageEnabled());
+}
+
+// Note that for E10S we can get a false here that will be overruled by
+// TryToRecycleE10SOnly as late as MaybeBeginShutdown. We cannot really
+// foresee its result here.
+bool ContentParent::CheckTabDestroyWillKeepAlive(
+ uint32_t aExpectedBrowserCount) {
+ return ManagedPBrowserParent().Count() != aExpectedBrowserCount ||
+ ShouldKeepProcessAlive();
+}
+
+void ContentParent::NotifyTabWillDestroy() {
+ if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)
+#if !defined(MOZ_WIDGET_ANDROID)
+ /* on Android we keep processes alive more agressively, see
+ NotifyTabDestroying where we omit MaybeBeginShutdown */
+ || (/* we cannot trust CheckTabDestroyWillKeepAlive in E10S mode */
+ mozilla::FissionAutostart() &&
+ !CheckTabDestroyWillKeepAlive(mNumDestroyingTabs + 1))
+#endif
+ ) {
+ // Once we notify the impending shutdown, the content process will stop
+ // to process content JS on interrupt (among other things), so we need to
+ // be sure that the process will not be re-used after this point.
+ // The inverse is harmless, that is if we decide later to shut it down
+ // but did not notify here, it will be just notified later (but in rare
+ // cases too late to avoid a hang).
+ NotifyImpendingShutdown();
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ mNotifiedImpendingShutdownOnTabWillDestroy = true;
+#endif
+ }
+}
+
+void ContentParent::MaybeBeginShutDown(uint32_t aExpectedBrowserCount,
+ bool aSendShutDown) {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
+ ("MaybeBeginShutdown %p, %u vs %u", this,
+ ManagedPBrowserParent().Count(), aExpectedBrowserCount));
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // Both CheckTabDestroyWillKeepAlive and TryToRecycleE10SOnly will return
+ // false if IsInOrBeyond(AppShutdownConfirmed), so if the parent shuts
+ // down we will always shutdown the child.
+ if (CheckTabDestroyWillKeepAlive(aExpectedBrowserCount) ||
+ TryToRecycleE10SOnly()) {
+ return;
+ }
+
+ MOZ_LOG(
+ ContentParent::GetLog(), LogLevel::Debug,
+ ("Beginning ContentParent Shutdown %p (%s)", this, mRemoteType.get()));
+
+ // We're dying now, prevent anything from re-using this process.
+ MarkAsDead();
+ SignalImpendingShutdownToContentJS();
+ StartForceKillTimer();
+
+ if (aSendShutDown) {
+ MaybeAsyncSendShutDownMessage();
+ }
+}
+
+void ContentParent::MaybeAsyncSendShutDownMessage() {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
+ ("MaybeAsyncSendShutDownMessage %p", this));
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(sRecycledE10SProcess != this);
+
+#ifdef DEBUG
+ // Calling this below while the lock is acquired will deadlock.
+ bool shouldKeepProcessAlive = ShouldKeepProcessAlive();
+#endif
+
+ {
+ MutexAutoLock lock(mThreadsafeHandle->mMutex);
+ MOZ_ASSERT_IF(!mThreadsafeHandle->mRemoteWorkerActorCount,
+ !shouldKeepProcessAlive);
+
+ if (mThreadsafeHandle->mRemoteWorkerActorCount) {
+ return;
+ }
+
+ MOZ_ASSERT(!mThreadsafeHandle->mShutdownStarted);
+ mThreadsafeHandle->mShutdownStarted = true;
+ }
+
+ // 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 pid=%d - %s at %s(%d)", aSelf, aSelf->Pid(),
+ aMsg, aFile, aLine));
+ }
+#else
+ Unused << aSelf;
+ Unused << aMsg;
+ Unused << aFile;
+ Unused << aLine;
+#endif
+}
+
+bool ContentParent::ShutDownProcess(ShutDownMethod aMethod) {
+ bool result = false;
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("ShutDownProcess: %p", this));
+ // 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);
+ // Send a high priority announcement first. If this fails, SendShutdown
+ // will also fail.
+ Unused << SendShutdownConfirmedHP();
+ // Send the definite message with normal priority.
+ if (SendShutdown()) {
+ MaybeLogBlockShutdownDiagnostics(
+ this, "ShutDownProcess: Sent shutdown message.", __FILE__,
+ __LINE__);
+ mShutdownPending = true;
+ // Start the force-kill timer if we haven't already.
+ // This can happen if we shutdown a process while launching or
+ // because it is removed from the cached processes pool.
+ if (!mForceKillTimer) {
+ SignalImpendingShutdownToContentJS();
+ 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 || aMethod == CLOSE_CHANNEL_WITH_ERROR) {
+ if (!mCalledClose) {
+ MaybeLogBlockShutdownDiagnostics(
+ this, "ShutDownProcess: Closing channel.", __FILE__, __LINE__);
+ // Close()/CloseWithError() can only be called once: They kick off the
+ // destruction sequence.
+ mCalledClose = true;
+ if (aMethod == CLOSE_CHANNEL_WITH_ERROR) {
+ CloseWithError();
+ } else {
+ 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->ReceiveMessage(mMessageManager, nullptr,
+ CHILD_PROCESS_SHUTDOWN_MESSAGE, false,
+ nullptr, nullptr, IgnoreErrors());
+
+ 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(sRecycledE10SProcess != this);
+ if (IsForJSPlugin()) {
+ MOZ_RELEASE_ASSERT(!sJSPluginContentParents ||
+ !sJSPluginContentParents->Get(mJSPluginID));
+ } else {
+ 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(!mNotifiedImpendingShutdownOnTabWillDestroy);
+ MOZ_DIAGNOSTIC_ASSERT(!mIsSignaledImpendingShutdown);
+ MOZ_DIAGNOSTIC_ASSERT(!IsDead());
+}
+
+void ContentParent::RemoveFromList() {
+ if (IsForJSPlugin()) {
+ if (sJSPluginContentParents) {
+ sJSPluginContentParents->Remove(mJSPluginID);
+ if (!sJSPluginContentParents->Count()) {
+ sJSPluginContentParents = nullptr;
+ }
+ }
+ return;
+ }
+
+ 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);
+ }
+
+ StopRecyclingE10SOnly(/* aForeground */ false);
+
+ 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 %p as dead", this));
+ MOZ_DIAGNOSTIC_ASSERT(!sInProcessSelector);
+ RemoveFromList();
+
+ // Prevent this process from being re-used.
+ PreallocatedProcessManager::Erase(this);
+ StopRecyclingE10SOnly(false);
+
+#ifdef MOZ_WIDGET_ANDROID
+ 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.
+ 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
+
+ if (mScriptableHelper) {
+ static_cast<ScriptableCPInfo*>(mScriptableHelper.get())->ProcessDied();
+ mScriptableHelper = nullptr;
+ }
+
+ mLifecycleState = LifecycleState::DEAD;
+}
+
+void ContentParent::OnChannelError() {
+ RefPtr<ContentParent> kungFuDeathGrip(this);
+ PContentParent::OnChannelError();
+}
+
+void ContentParent::ProcessingError(Result aCode, const char* aReason) {
+ if (MsgDropped == aCode) {
+ return;
+ }
+ // Other errors are big deals.
+#ifndef FUZZING
+ KillHard(aReason);
+#endif
+ ShutDownProcess(CLOSE_CHANNEL_WITH_ERROR);
+}
+
+void ContentParent::ActorDestroy(ActorDestroyReason why) {
+ if (mForceKillTimer) {
+ mForceKillTimer->Cancel();
+ mForceKillTimer = nullptr;
+ }
+
+ // Signal shutdown completion regardless of error state, so we can
+ // finish waiting in the xpcom-shutdown/profile-before-change observer.
+ 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(why == NormalShutdown ? CLOSE_CHANNEL
+ : CLOSE_CHANNEL_WITH_ERROR);
+
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ size_t length = ArrayLength(sObserverTopics);
+ for (size_t i = 0; i < length; ++i) {
+ obs->RemoveObserver(static_cast<nsIObserver*>(this), sObserverTopics[i]);
+ }
+ }
+
+ // remove the global remote preferences observers
+ Preferences::RemoveObserver(this, "");
+ gfxVars::RemoveReceiver(this);
+
+ if (GPUProcessManager* gpu = GPUProcessManager::Get()) {
+ // Note: the manager could have shutdown already.
+ gpu->RemoveListener(this);
+ }
+
+ RecvRemoveGeolocationListener();
+
+ // 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) {
+ Telemetry::Accumulate(Telemetry::SUBPROCESS_ABNORMAL_ABORT, "content"_ns,
+ 1);
+
+ props->SetPropertyAsBool(u"abnormal"_ns, true);
+
+ nsAutoString dumpID;
+ // 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(OtherPid());
+ }
+
+ if (mCrashReporter->HasMinidump()) {
+ dumpID = mCrashReporter->MinidumpID();
+ }
+ } else {
+ HandleOrphanedMinidump(&dumpID);
+ }
+
+ if (!dumpID.IsEmpty()) {
+ props->SetPropertyAsAString(u"dumpID"_ns, dumpID);
+ }
+ }
+ 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 %p "
+ "mSubprocess %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 %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.
+ for (uint32_t i = 0; i < mBlobURLs.Length(); ++i) {
+ BlobURLProtocolHandler::RemoveDataEntry(mBlobURLs[i]);
+ }
+
+ mBlobURLs.Clear();
+
+#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();
+}
+
+bool ContentParent::TryToRecycleE10SOnly() {
+ // Only try to recycle "web" content processes, as other remote types are
+ // generally more unique, and cannot be effectively re-used. This is disabled
+ // with Fission, as "web" content processes are no longer frequently used.
+ //
+ // Disabling the process pre-allocator will also disable process recycling,
+ // allowing for more consistent process counts under testing.
+ if (mRemoteType != DEFAULT_REMOTE_TYPE || mozilla::FissionAutostart() ||
+ !PreallocatedProcessManager::Enabled()) {
+ return false;
+ }
+
+ // This life time check should be replaced by a memory health check (memory
+ // usage + fragmentation).
+
+ // Note that this is specifically to help with edge cases that rapidly
+ // create-and-destroy processes
+ const double kMaxLifeSpan = 5;
+ MOZ_LOG(
+ ContentParent::GetLog(), LogLevel::Debug,
+ ("TryToRecycle ContentProcess %p (%u) with lifespan %f seconds", this,
+ (unsigned int)ChildID(), (TimeStamp::Now() - mActivateTS).ToSeconds()));
+
+ if (mCalledKillHard || !IsAlive() ||
+ (TimeStamp::Now() - mActivateTS).ToSeconds() > kMaxLifeSpan) {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("TryToRecycle did not recycle %p", this));
+
+ // It's possible that the process was already cached, and we're being called
+ // from a different path, and we're now past kMaxLifeSpan (or some other).
+ // Ensure that if we're going to kill this process we don't recycle it.
+ StopRecyclingE10SOnly(/* aForeground */ false);
+ return false;
+ }
+
+ if (!sRecycledE10SProcess) {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("TryToRecycle began recycling %p", this));
+ sRecycledE10SProcess = this;
+
+ ProcessPriorityManager::SetProcessPriority(this,
+ PROCESS_PRIORITY_BACKGROUND);
+ return true;
+ }
+
+ if (sRecycledE10SProcess == this) {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("TryToRecycle continue recycling %p", this));
+ return true;
+ }
+
+ // Some other process is already being recycled, just shut this one down.
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("TryToRecycle did not recycle %p (already recycling %p)", this,
+ sRecycledE10SProcess.get()));
+ return false;
+}
+
+void ContentParent::StopRecyclingE10SOnly(bool aForeground) {
+ if (sRecycledE10SProcess != this) {
+ return;
+ }
+
+ sRecycledE10SProcess = nullptr;
+ if (aForeground) {
+ ProcessPriorityManager::SetProcessPriority(this,
+ PROCESS_PRIORITY_FOREGROUND);
+ }
+}
+
+bool ContentParent::HasActiveWorkerOrJSPlugin() {
+ if (IsForJSPlugin()) {
+ return true;
+ }
+
+ // If we have active workers, we need to stay alive.
+ {
+ MutexAutoLock lock(mThreadsafeHandle->mMutex);
+ if (mThreadsafeHandle->mRemoteWorkerActorCount) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ContentParent::ShouldKeepProcessAlive() {
+ if (HasActiveWorkerOrJSPlugin()) {
+ return true;
+ }
+
+ if (mNumKeepaliveCalls > 0) {
+ return true;
+ }
+
+ if (IsLaunching()) {
+ return true;
+ }
+
+ // If we have already been marked as dead, don't prevent shutdown.
+ if (IsDead()) {
+ return false;
+ }
+
+ // If everything is going down, there is no need to keep us alive, neither.
+ if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
+ return false;
+ }
+
+ if (!sBrowserContentParents) {
+ return false;
+ }
+
+ auto contentParents = sBrowserContentParents->Get(mRemoteType);
+ if (!contentParents) {
+ return false;
+ }
+
+ // 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.
+ int32_t processesToKeepAlive = 0;
+
+ nsAutoCString keepAlivePref("dom.ipc.keepProcessesAlive.");
+
+ if (StringBeginsWith(mRemoteType, FISSION_WEB_REMOTE_TYPE) &&
+ xpc::IsInAutomation()) {
+ keepAlivePref.Append(FISSION_WEB_REMOTE_TYPE);
+ keepAlivePref.AppendLiteral(".perOrigin");
+ } else {
+ keepAlivePref.Append(mRemoteType);
+ }
+ if (NS_FAILED(
+ Preferences::GetInt(keepAlivePref.get(), &processesToKeepAlive))) {
+ return false;
+ }
+
+ int32_t numberOfAliveProcesses = contentParents->Length();
+
+ return numberOfAliveProcesses <= processesToKeepAlive;
+}
+
+void ContentParent::NotifyTabDestroying() {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("NotifyTabDestroying %p:", this));
+ // There can be more than one PBrowser for a given app process
+ // because of popup windows. PBrowsers can also destroy
+ // concurrently. When all the PBrowsers are destroying, kick off
+ // another task to ensure the child process *really* shuts down,
+ // even if the PBrowsers themselves never finish destroying.
+ ++mNumDestroyingTabs;
+
+ /**
+ * We intentionally skip this code on Android:
+ * 1. Android has a fixed upper bound on the number of content processes, so
+ * we prefer to re-use them whenever possible (as opposed to letting an
+ * old process wind down while we launch a new one).
+ * 2. GeckoView always hard-kills content processes (and if it does not,
+ * Android itself will), so we don't concern ourselves with the ForceKill
+ * timer either.
+ */
+#if !defined(MOZ_WIDGET_ANDROID)
+ MaybeBeginShutDown(/* aExpectedBrowserCount */ mNumDestroyingTabs,
+ /* aSendShutDown */ false);
+#endif // !defined(MOZ_WIDGET_ANDROID)
+}
+
+void ContentParent::AddKeepAlive() {
+ AssertAlive();
+ // Something wants to keep this content process alive.
+ ++mNumKeepaliveCalls;
+}
+
+void ContentParent::RemoveKeepAlive() {
+ MOZ_DIAGNOSTIC_ASSERT(mNumKeepaliveCalls > 0);
+ --mNumKeepaliveCalls;
+
+ MaybeBeginShutDown();
+}
+
+void ContentParent::StartForceKillTimer() {
+ if (mForceKillTimer || !CanSend()) {
+ return;
+ }
+
+ int32_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");
+ MOZ_ASSERT(mForceKillTimer);
+ }
+}
+
+void ContentParent::NotifyTabDestroyed(const TabId& aTabId,
+ bool aNotifiedDestroying) {
+ if (aNotifiedDestroying) {
+ --mNumDestroyingTabs;
+ }
+
+ nsTArray<PContentPermissionRequestParent*> parentArray =
+ nsContentPermissionUtils::GetContentPermissionRequestParentById(aTabId);
+
+ // Need to close undeleted ContentPermissionRequestParents before tab is
+ // closed.
+ for (auto& permissionRequestParent : parentArray) {
+ Unused << PContentPermissionRequestParent::Send__delete__(
+ permissionRequestParent);
+ }
+
+ // There can be more than one PBrowser for a given app process
+ // because of popup windows. When the last one closes, shut
+ // us down.
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
+ ("NotifyTabDestroyed %p", this));
+
+ MaybeBeginShutDown(/* aExpectedBrowserCount */ 1);
+}
+
+TestShellParent* ContentParent::CreateTestShell() {
+ return static_cast<TestShellParent*>(SendPTestShellConstructor());
+}
+
+bool ContentParent::DestroyTestShell(TestShellParent* aTestShell) {
+ return PTestShellParent::Send__delete__(aTestShell);
+}
+
+TestShellParent* ContentParent::GetTestShellSingleton() {
+ PTestShellParent* p = LoneManagedOrNullAsserts(ManagedPTestShellParent());
+ return static_cast<TestShellParent*>(p);
+}
+
+#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") ||
+ !Preferences::GetBool("webgl.out-of-process")) {
+ 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;
+ Unused << 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;
+ Unused << 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;
+ }
+
+ std::vector<std::string> extraArgs;
+ geckoargs::sChildID.Put(mChildID, 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.
+ ::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, "");
+
+ if (gSafeMode) {
+ geckoargs::sSafeMode.Put(extraArgs);
+ }
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+ if (IsContentSandboxEnabled()) {
+ AppendSandboxParams(extraArgs);
+ 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() &&
+ StaticPrefs::widget_non_native_theme_enabled() &&
+ 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
+ Unused << 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) {
+ Telemetry::AccumulateTimeDelta(Telemetry::CONTENT_PROCESS_SYNC_LAUNCH_MS,
+ mLaunchTS);
+ } else {
+ Telemetry::AccumulateTimeDelta(Telemetry::CONTENT_PROCESS_LAUNCH_TOTAL_MS,
+ mLaunchTS);
+
+ Telemetry::Accumulate(
+ Telemetry::CONTENT_PROCESS_LAUNCH_MAINTHREAD_MS,
+ static_cast<uint32_t>(
+ ((mLaunchYieldTS - mLaunchTS) + (TimeStamp::Now() - launchResumeTS))
+ .ToMilliseconds()));
+ }
+
+ mLaunchResolvedOk = true;
+ return true;
+}
+
+bool ContentParent::LaunchSubprocessSync(
+ hal::ProcessPriority aInitialPriority) {
+ // We've started a sync content process launch.
+ Telemetry::Accumulate(Telemetry::CONTENT_PROCESS_LAUNCH_IS_SYNC, 1);
+
+ if (BeginSubprocessLaunch(aInitialPriority)) {
+ const bool ok = mSubprocess->WaitForProcessHandle();
+ if (ok && LaunchSubprocessResolve(/* aIsSync = */ true, aInitialPriority)) {
+ return true;
+ }
+ }
+ LaunchSubprocessReject();
+ return false;
+}
+
+RefPtr<ContentParent::LaunchPromise> ContentParent::LaunchSubprocessAsync(
+ hal::ProcessPriority aInitialPriority) {
+ // We've started an async content process launch.
+ Telemetry::Accumulate(Telemetry::CONTENT_PROCESS_LAUNCH_IS_SYNC, 0);
+
+ if (!BeginSubprocessLaunch(aInitialPriority)) {
+ // Launch aborted because of shutdown. Bailout.
+ LaunchSubprocessReject();
+ return LaunchPromise::CreateAndReject(NS_ERROR_ILLEGAL_DURING_SHUTDOWN,
+ __func__);
+ }
+
+ // Otherwise, wait until the process is ready.
+ RefPtr<ProcessHandlePromise> ready = mSubprocess->WhenProcessHandleReady();
+ RefPtr<ContentParent> self = this;
+ mLaunchYieldTS = TimeStamp::Now();
+
+ return ready->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [self, aInitialPriority](
+ const ProcessHandlePromise::ResolveOrRejectValue& aValue) {
+ if (aValue.IsResolve() &&
+ self->LaunchSubprocessResolve(/* aIsSync = */ false,
+ aInitialPriority)) {
+ return LaunchPromise::CreateAndResolve(self, __func__);
+ }
+ self->LaunchSubprocessReject();
+ return LaunchPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
+ });
+}
+
+ContentParent::ContentParent(const nsACString& aRemoteType, int32_t aJSPluginID)
+ : mSubprocess(nullptr),
+ mLaunchTS(TimeStamp::Now()),
+ mLaunchYieldTS(mLaunchTS),
+ mActivateTS(mLaunchTS),
+ mIsAPreallocBlocker(false),
+ mRemoteType(aRemoteType),
+ mChildID(gContentChildID++),
+ mGeolocationWatchID(-1),
+ mJSPluginID(aJSPluginID),
+ mThreadsafeHandle(
+ new ThreadsafeContentParentHandle(this, mChildID, mRemoteType)),
+ mNumDestroyingTabs(0),
+ mNumKeepaliveCalls(0),
+ mLifecycleState(LifecycleState::LAUNCHING),
+ mIsForBrowser(!mRemoteType.IsEmpty()),
+ mCalledClose(false),
+ mCalledKillHard(false),
+ mCreatedPairedMinidumps(false),
+ mShutdownPending(false),
+ mLaunchResolved(false),
+ mLaunchResolvedOk(false),
+ mIsRemoteInputEventQueueEnabled(false),
+ mIsInputPriorityEventEnabled(false),
+ mIsInPool(false),
+ mGMPCreated(false),
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ mBlockShutdownCalled(false),
+#endif
+ mHangMonitorActor(nullptr) {
+ MOZ_DIAGNOSTIC_ASSERT(!IsForJSPlugin(),
+ "XXX(nika): How are we creating a JSPlugin?");
+
+ 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
+
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+ bool isFile = mRemoteType == FILE_REMOTE_TYPE;
+ mSubprocess = new GeckoChildProcessHost(GeckoProcessType_Content, isFile);
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
+ ("CreateSubprocess: ContentParent %p mSubprocess %p handle %" PRIuPTR,
+ this, mSubprocess,
+ mSubprocess ? (uintptr_t)mSubprocess->GetChildProcessHandle() : -1));
+
+ // This is safe to do in the constructor, as it doesn't take a strong
+ // reference.
+ mScriptableHelper = new ScriptableCPInfo(this);
+}
+
+ContentParent::~ContentParent() {
+ 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 destruction"));
+ 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 %p mSubprocess %p handle %" PRIuPTR,
+ this, mSubprocess,
+ mSubprocess ? (uintptr_t)mSubprocess->GetChildProcessHandle() : -1));
+ mSubprocess->Destroy();
+ }
+
+ // Make sure to clear the connection from `mScriptableHelper` if it hasn't
+ // been cleared yet.
+ if (mScriptableHelper) {
+ static_cast<ScriptableCPInfo*>(mScriptableHelper.get())->ProcessDied();
+ mScriptableHelper = nullptr;
+ }
+}
+
+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: %p", (void*)this));
+ 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());
+
+ 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).
+ StructuredCloneData initialData;
+ 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<SharedMemoryHandle> 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->GetSheetURI();
+ } else {
+ xpcomInit.userContentSheetURL() = nullptr;
+ }
+
+ // 1. Build ContentDeviceData first, as it may affect some gfxVars.
+ gfxPlatform::GetPlatform()->BuildContentDeviceData(
+ &xpcomInit.contentDeviceData());
+ // 2. Gather non-default gfxVars.
+ xpcomInit.gfxNonDefaultVarUpdates() = gfxVars::FetchNonDefaultVars();
+ // 3. 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) {
+ Unused << SendUpdateMediaCodecsSupported(location, supported);
+ }
+
+#ifdef MOZ_WIDGET_ANDROID
+ if (!(StaticPrefs::media_utility_process_enabled() &&
+ StaticPrefs::media_utility_android_media_codec_enabled())) {
+ Unused << SendDecoderSupportedMimeTypes(
+ AndroidDecoderModule::GetSupportedMimeTypes());
+ }
+#endif
+
+ // 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<SharedMemoryHandle> sharedUASheetHandle;
+ uintptr_t sharedUASheetAddress = sheetCache->GetSharedMemoryAddress();
+
+ if (SharedMemoryHandle 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() =
+ static_cast<nsIDNSService::ResolverMode>(StaticPrefs::network_trr_mode());
+
+ Unused << 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.
+ Unused << SendAppInfo(version, buildID, name, UAName, ID, vendor, sourceURL,
+ updateURL);
+ }
+
+ // 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.
+
+ Unused << SendRemoteType(mRemoteType, mProfile);
+
+ 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.)
+ GPUProcessManager* gpm = GPUProcessManager::Get();
+
+ Endpoint<PCompositorManagerChild> compositor;
+ Endpoint<PImageBridgeChild> imageBridge;
+ Endpoint<PVRManagerChild> vrBridge;
+ Endpoint<PRemoteDecoderManagerChild> videoManager;
+ AutoTArray<uint32_t, 3> namespaces;
+
+ if (!gpm->CreateContentBridges(OtherPid(), &compositor, &imageBridge,
+ &vrBridge, &videoManager, mChildID,
+ &namespaces)) {
+ // This can fail if we've already started shutting down the compositor
+ // thread. See Bug 1562763 comment 8.
+ MOZ_ASSERT(AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdown));
+ return false;
+ }
+
+ Unused << SendInitRendering(std::move(compositor), std::move(imageBridge),
+ std::move(vrBridge), std::move(videoManager),
+ namespaces);
+
+ gpm->AddListener(this);
+
+ 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()) {
+ Unused << SendLoadAndRegisterSheet(sheet->GetSheetURI(),
+ nsIStyleSheetService::AGENT_SHEET);
+ }
+
+ for (StyleSheet* sheet : *sheetService->UserStyleSheets()) {
+ Unused << SendLoadAndRegisterSheet(sheet->GetSheetURI(),
+ nsIStyleSheetService::USER_SHEET);
+ }
+
+ for (StyleSheet* sheet : *sheetService->AuthorStyleSheets()) {
+ Unused << SendLoadAndRegisterSheet(sheet->GetSheetURI(),
+ nsIStyleSheetService::AUTHOR_SHEET);
+ }
+ }
+
+#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 Maybe<nsID>& aAgentClusterId, 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
+ // AboutToLoadHttpFtpDocumentForChild and RemoteWorkerManager.
+ if (!BlobURLProtocolHandler::IsBlobURLBroadcastPrincipal(
+ aPrincipal)) {
+ return true;
+ }
+
+ IPCBlob ipcBlob;
+ nsresult rv = IPCBlobUtils::Serialize(aBlobImpl, ipcBlob);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return false;
+ }
+
+ registrations.AppendElement(BlobURLRegistrationData(
+ nsCString(aURI), ipcBlob, aPrincipal, aAgentClusterId, aRevoked));
+
+ rv = TransmitPermissionsForPrincipal(aPrincipal);
+ Unused << NS_WARN_IF(NS_FAILED(rv));
+ return true;
+ });
+
+ if (!registrations.IsEmpty()) {
+ Unused << 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);
+
+ Unused << 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);
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvGetGfxVars(
+ nsTArray<GfxVarUpdate>* aVars) {
+ // Ensure gfxVars is initialized (for xpcshell tests).
+ gfxVars::Initialize();
+
+ *aVars = gfxVars::FetchNonDefaultVars();
+
+ // Now that content has initialized gfxVars, we can start listening for
+ // updates.
+ gfxVars::AddReceiver(this);
+ return IPC_OK();
+}
+
+void ContentParent::OnCompositorUnexpectedShutdown() {
+ GPUProcessManager* gpm = GPUProcessManager::Get();
+
+ Endpoint<PCompositorManagerChild> compositor;
+ Endpoint<PImageBridgeChild> imageBridge;
+ Endpoint<PVRManagerChild> vrBridge;
+ Endpoint<PRemoteDecoderManagerChild> videoManager;
+ AutoTArray<uint32_t, 3> namespaces;
+
+ if (!gpm->CreateContentBridges(OtherPid(), &compositor, &imageBridge,
+ &vrBridge, &videoManager, mChildID,
+ &namespaces)) {
+ MOZ_ASSERT(AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdown));
+ return;
+ }
+
+ Unused << SendReinitRendering(std::move(compositor), std::move(imageBridge),
+ std::move(vrBridge), std::move(videoManager),
+ namespaces);
+}
+
+void ContentParent::OnCompositorDeviceReset() {
+ Unused << SendReinitRenderingForDeviceReset();
+}
+
+void ContentParent::MaybeEnableRemoteInputEventQueue() {
+ MOZ_ASSERT(!mIsRemoteInputEventQueueEnabled);
+ if (!IsInputEventQueueSupported()) {
+ return;
+ }
+ mIsRemoteInputEventQueueEnabled = true;
+ Unused << SendSetInputEventQueueEnabled();
+ SetInputPriorityEventEnabled(true);
+}
+
+void ContentParent::SetInputPriorityEventEnabled(bool aEnabled) {
+ if (!IsInputEventQueueSupported() || !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.
+ Unused << SendSuspendInputEventQueue();
+ Unused << SendFlushInputEventQueue();
+ Unused << SendResumeInputEventQueue();
+}
+
+/*static*/
+bool ContentParent::IsInputEventQueueSupported() {
+ static bool sSupported = false;
+ static bool sInitialized = false;
+ if (!sInitialized) {
+ MOZ_ASSERT(Preferences::IsServiceAvailable());
+ sSupported = Preferences::GetBool("input_event_queue.supported", false);
+ sInitialized = true;
+ }
+ return sSupported;
+}
+
+void ContentParent::OnVarChanged(const GfxVarUpdate& aVar) {
+ if (!CanSend()) {
+ return;
+ }
+ Unused << SendVarUpdate(aVar);
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvSetClipboard(
+ const IPCTransferable& aTransferable, const int32_t& aWhichClipboard) {
+ // aRequestingPrincipal is allowed to be nullptr here.
+
+ if (!ValidatePrincipal(aTransferable.requestingPrincipal(),
+ {ValidatePrincipalOptions::AllowNullPtr})) {
+ LogAndAssertFailedPrincipalValidationInfo(
+ aTransferable.requestingPrincipal(), __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());
+
+ clipboard->SetData(trans, nullptr, aWhichClipboard);
+ return IPC_OK();
+}
+
+namespace {
+
+static Result<nsCOMPtr<nsITransferable>, nsresult> CreateTransferable(
+ 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);
+}
+
+} // anonymous namespace
+
+mozilla::ipc::IPCResult ContentParent::RecvGetClipboard(
+ nsTArray<nsCString>&& aTypes, const int32_t& aWhichClipboard,
+ IPCTransferableData* aTransferableData) {
+ nsresult rv;
+ // Retrieve clipboard
+ nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
+ if (NS_FAILED(rv)) {
+ return IPC_OK();
+ }
+
+ // Create transferable
+ auto result = CreateTransferable(aTypes);
+ if (result.isErr()) {
+ return IPC_OK();
+ }
+
+ // Get data from clipboard
+ nsCOMPtr<nsITransferable> trans = result.unwrap();
+ clipboard->GetData(trans, aWhichClipboard);
+
+ nsContentUtils::TransferableToIPCTransferableData(
+ trans, aTransferableData, true /* aInSyncMessage */, this);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvEmptyClipboard(
+ const int32_t& 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 int32_t& 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();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvClipboardHasTypesAsync(
+ nsTArray<nsCString>&& aTypes, const int32_t& aWhichClipboard,
+ ClipboardHasTypesAsyncResolver&& aResolver) {
+ nsresult rv;
+ nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
+ if (NS_FAILED(rv)) {
+ return IPC_FAIL(this, "RecvGetClipboardTypes failed.");
+ }
+
+ clipboard->AsyncHasDataMatchingFlavors(aTypes, aWhichClipboard)
+ ->Then(
+ GetMainThreadSerialEventTarget(), __func__,
+ /* resolve */
+ [aResolver](nsTArray<nsCString> types) { aResolver(types); },
+ /* reject */
+ [aResolver](nsresult rv) { aResolver(nsTArray<nsCString>{}); });
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvGetExternalClipboardFormats(
+ const int32_t& aWhichClipboard, const bool& aPlainTextOnly,
+ nsTArray<nsCString>* aTypes) {
+ MOZ_ASSERT(aTypes);
+ DataTransfer::GetExternalClipboardFormats(aWhichClipboard, aPlainTextOnly,
+ aTypes);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvGetClipboardAsync(
+ nsTArray<nsCString>&& aTypes, const int32_t& aWhichClipboard,
+ GetClipboardAsyncResolver&& aResolver) {
+ nsresult rv;
+ // Retrieve clipboard
+ nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
+ if (NS_FAILED(rv)) {
+ aResolver(rv);
+ return IPC_OK();
+ }
+
+ // Create transferable
+ auto result = CreateTransferable(aTypes);
+ if (result.isErr()) {
+ aResolver(result.unwrapErr());
+ return IPC_OK();
+ }
+
+ // Get data from clipboard
+ nsCOMPtr<nsITransferable> trans = result.unwrap();
+ clipboard->AsyncGetData(trans, nsIClipboard::kGlobalClipboard)
+ ->Then(
+ GetMainThreadSerialEventTarget(), __func__,
+ [trans, aResolver,
+ self = RefPtr{this}](GenericPromise::ResolveOrRejectValue&& aValue) {
+ IPCTransferableData ipcTransferableData;
+ nsContentUtils::TransferableToIPCTransferableData(
+ trans, &ipcTransferableData, false /* aInSyncMessage */, self);
+ aResolver(std::move(ipcTransferableData));
+ });
+ return IPC_OK();
+}
+
+already_AddRefed<PClipboardWriteRequestParent>
+ContentParent::AllocPClipboardWriteRequestParent(
+ const int32_t& aClipboardType) {
+ RefPtr<ClipboardWriteRequestParent> request =
+ MakeAndAddRef<ClipboardWriteRequestParent>(this);
+ request->Init(aClipboardType);
+ return request.forget();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvPlaySound(nsIURI* aURI) {
+ // If the check here fails, it can only mean that this message was spoofed.
+ if (!aURI || !aURI->SchemeIs("chrome")) {
+ // PlaySound only accepts a valid chrome URI.
+ return IPC_FAIL(this, "Invalid aURI passed.");
+ }
+ nsCOMPtr<nsIURL> soundURL(do_QueryInterface(aURI));
+ if (!soundURL) {
+ return IPC_OK();
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsISound> sound(do_GetService(NS_SOUND_CID, &rv));
+ NS_ENSURE_SUCCESS(rv, IPC_OK());
+
+ sound->Play(soundURL);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvBeep() {
+ nsresult rv;
+ nsCOMPtr<nsISound> sound(do_GetService(NS_SOUND_CID, &rv));
+ NS_ENSURE_SUCCESS(rv, IPC_OK());
+
+ sound->Beep();
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvPlayEventSound(
+ const uint32_t& aEventId) {
+ nsresult rv;
+ nsCOMPtr<nsISound> sound(do_GetService(NS_SOUND_CID, &rv));
+ NS_ENSURE_SUCCESS(rv, IPC_OK());
+
+ sound->PlayEventSound(aEventId);
+
+ return IPC_OK();
+}
+
+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();
+ }
+
+ bits->AppendElements(aIconSize * aIconSize * 4);
+
+ 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 %p: Removing Blocker for %s", this, 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();
+}
+
+// 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);
+ Unused << 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
+ // Our real shutdown has not yet started. Just notify the impending
+ // shutdown and eventually cancel content JS.
+ SignalImpendingShutdownToContentJS();
+ // 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);
+ StopRecyclingE10SOnly(false);
+
+ if (sQuitApplicationGrantedClient) {
+ Unused << sQuitApplicationGrantedClient->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=%p", this);
+ 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);
+ }
+ }
+ // TODO: ShutdownPhase::AppShutdownConfirmed is not mapping to
+ // QuitApplicationGranted, see bug 1762840 comment 4.
+ if (!AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
+ rv = svc->GetQuitApplicationGranted(getter_AddRefs(client));
+ if (NS_SUCCEEDED(rv)) {
+ sQuitApplicationGrantedClient = client.forget();
+ ClearOnShutdown(&sQuitApplicationGrantedClient);
+ }
+ }
+ }
+}
+
+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 (sQuitApplicationGrantedClient) {
+ sQuitApplicationGrantedClient->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) {
+ Unused << sXPCOMShutdownClient->RemoveBlocker(this);
+ }
+ if (sProfileBeforeChangeClient) {
+ Unused << sProfileBeforeChangeClient->RemoveBlocker(this);
+ }
+ if (sQuitApplicationGrantedClient) {
+ Unused << sQuitApplicationGrantedClient->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")) {
+ Unused << SendFlushMemory(nsDependentString(aData));
+ } else if (!strcmp(aTopic, "application-background")) {
+ Unused << SendApplicationBackground();
+ } else if (!strcmp(aTopic, "application-foreground")) {
+ Unused << 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;
+ }
+ }
+ // listening for alert notifications
+ else if (!strcmp(aTopic, "alertfinished") ||
+ !strcmp(aTopic, "alertclickcallback") ||
+ !strcmp(aTopic, "alertshow") ||
+ !strcmp(aTopic, "alertdisablecallback") ||
+ !strcmp(aTopic, "alertsettingscallback")) {
+ if (!SendNotifyAlertsObserver(nsDependentCString(aTopic),
+ nsDependentString(aData)))
+ return NS_ERROR_NOT_AVAILABLE;
+ } else if (!strcmp(aTopic, "child-gc-request")) {
+ Unused << SendGarbageCollect();
+ } else if (!strcmp(aTopic, "child-cc-request")) {
+ Unused << SendCycleCollect();
+ } else if (!strcmp(aTopic, "child-mmu-request")) {
+ Unused << SendMinimizeMemoryUsage();
+ } else if (!strcmp(aTopic, "child-ghost-request")) {
+ Unused << SendUnlinkGhosts();
+ } else if (!strcmp(aTopic, "last-pb-context-exited")) {
+ Unused << 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 initiated in chrome process.
+# if defined(XP_WIN)
+ // Don't init content a11y if we detect an incompat version of JAWS in
+ // use.
+ if (!mozilla::a11y::Compatibility::IsOldJAWS()) {
+ Unused << SendActivateA11y();
+ }
+# else
+ Unused << SendActivateA11y();
+# endif
+ } else {
+ // If possible, shut down accessibility in content process when
+ // accessibility gets shutdown in chrome process.
+ Unused << SendShutdownA11y();
+ }
+ }
+#endif
+ else if (!strcmp(aTopic, "cacheservice:empty-cache")) {
+ Unused << SendNotifyEmptyHTTPCache();
+ } else if (!strcmp(aTopic, "intl:app-locales-changed")) {
+ nsTArray<nsCString> appLocales;
+ LocaleService::GetInstance()->GetAppLocalesAsBCP47(appLocales);
+ Unused << SendUpdateAppLocales(appLocales);
+ } else if (!strcmp(aTopic, "intl:requested-locales-changed")) {
+ nsTArray<nsCString> requestedLocales;
+ LocaleService::GetInstance()->GetRequestedLocales(requestedLocales);
+ Unused << SendUpdateRequestedLocales(requestedLocales);
+ } else if (!strcmp(aTopic, "cookie-changed") ||
+ !strcmp(aTopic, "private-cookie-changed")) {
+ if (!aData) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ 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);
+ if (!nsCRT::strcmp(aData, u"batch-deleted")) {
+ nsCOMPtr<nsIArray> cookieList = do_QueryInterface(aSubject);
+ NS_ASSERTION(cookieList, "couldn't get cookie list");
+ cs->RemoveBatchDeletedCookies(cookieList);
+ return NS_OK;
+ }
+
+ if (!nsCRT::strcmp(aData, u"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 = do_QueryInterface(aSubject);
+ NS_ASSERTION(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;
+ }
+
+ if (!nsCRT::strcmp(aData, u"deleted")) {
+ cs->RemoveCookie(cookie);
+ } else if ((!nsCRT::strcmp(aData, u"added")) ||
+ (!nsCRT::strcmp(aData, u"changed"))) {
+ cs->AddCookie(cookie);
+ }
+ } else if (!strcmp(aTopic, NS_NETWORK_LINK_TYPE_TOPIC)) {
+ UpdateNetworkLinkType();
+ } else if (!strcmp(aTopic, "network:socket-process-crashed")) {
+ Unused << SendSocketProcessCrashed();
+ } else if (!strcmp(aTopic, DEFAULT_TIMEZONE_CHANGED_OBSERVER_TOPIC)) {
+ Unused << 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);
+ Unused << SendSetTRRMode(mode, static_cast<nsIDNSService::ResolverMode>(
+ StaticPrefs::network_trr_mode()));
+ }
+
+ return NS_OK;
+}
+
+void ContentParent::UpdateNetworkLinkType() {
+ nsresult rv;
+ nsCOMPtr<nsINetworkLinkService> nls =
+ do_GetService(NS_NETWORK_LINK_SERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ uint32_t linkType = nsINetworkLinkService::LINK_TYPE_UNKNOWN;
+ rv = nls->GetLinkType(&linkType);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ Unused << SendNetworkLinkTypeChange(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();
+ }
+
+ auto* source = aSource.get_canonical();
+ auto* target = aTarget.get_canonical();
+
+ if (!CloneIsLegal(this, *source, *target)) {
+ return IPC_FAIL(this, "Illegal subframe clone");
+ }
+
+ 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();
+ }
+
+ Unused << bp->SendUpdateRemotePrintSettings(std::move(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 (!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 (!aInitialWindowInit.principal()) {
+ return IPC_FAIL(this, "Cannot create without valid initial principal");
+ }
+
+ if (!ValidatePrincipal(aInitialWindowInit.principal())) {
+ LogAndAssertFailedPrincipalValidationInfo(aInitialWindowInit.principal(),
+ __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);
+ if (!initialWindow) {
+ return IPC_FAIL(this, "Failed to create WindowGlobalParent");
+ }
+
+ auto parent = MakeRefPtr<BrowserParent>(this, aTabId, tc.GetTabContext(),
+ browsingContext, chromeFlags);
+
+ // 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");
+ }
+
+ // XXX: Why are we checking these requirements? It seems we should register
+ // the created frame unconditionally?
+ if (openerTabId > 0) {
+ // The creation of PBrowser was triggered from content process through
+ // window.open().
+ // We need to register remote frame with the child generated tab id.
+ auto* cpm = ContentProcessManager::GetSingleton();
+ if (!cpm || !cpm->RegisterRemoteFrame(parent)) {
+ return IPC_FAIL(this, "RegisterRemoteFrame 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::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->AddAnnotation(
+ CrashReporter::Annotation::additional_minidumps, additionalDumps);
+ nsDependentCString reason(aReason);
+ mCrashReporter->AddAnnotation(CrashReporter::Annotation::ipc_channel_error,
+ reason);
+
+ // Generate the report and insert into the queue for submittal.
+ if (mCrashReporter->GenerateMinidumpAndPair(this, "browser"_ns)) {
+ mCrashReporter->FinalizeCrashReport();
+ mCreatedPairedMinidumps = true;
+ }
+ }
+}
+
+void ContentParent::HandleOrphanedMinidump(nsString* aDumpId) {
+ if (CrashReporter::FinalizeOrphanedMinidump(
+ OtherPid(), GeckoProcessType_Content, aDumpId)) {
+ CrashReporterHost::RecordCrash(GeckoProcessType_Content,
+ nsICrashService::CRASH_TYPE_CRASH, *aDumpId);
+ } else {
+ NS_WARNING(nsPrintfCString("content process pid = %" PRIPID
+ " crashed without leaving a minidump behind",
+ 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;
+ 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.");
+ }
+ Telemetry::Accumulate(Telemetry::SUBPROCESS_KILL_HARD, reason, 1);
+
+ ProcessHandle otherProcessHandle;
+ if (!base::OpenProcessHandle(OtherPid(), &otherProcessHandle)) {
+ NS_ERROR("Failed to open child process when attempting kill.");
+ ShutDownProcess(CLOSE_CHANNEL_WITH_ERROR);
+ 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 %p mSubprocess %p handle "
+ "%" PRIuPTR,
+ aReason, this, mSubprocess,
+ mSubprocess ? (uintptr_t)mSubprocess->GetChildProcessHandle() : -1));
+ mSubprocess->SetAlreadyDead();
+ }
+
+ // After we've killed the remote process, also ensure we close the IPC channel
+ // with an error to immediately stop all IPC communication on this channel.
+ ShutDownProcess(CLOSE_CHANNEL_WITH_ERROR);
+
+ // EnsureProcessTerminated has responsibilty for closing otherProcessHandle.
+ XRE_GetIOMessageLoop()->PostTask(
+ 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 NativeThreadId& aThreadId) {
+ mCrashReporter =
+ MakeUnique<CrashReporterHost>(GeckoProcessType_Content, aThreadId);
+
+ 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);
+ // If we run the callback in response to a reply, then by definition |this|
+ // is still alive, so the ref pointer is redundant, but it seems easier
+ // to hold a strong reference than to worry about that.
+ RefPtr<ContentParent> self(this);
+ PContentParent::SendRequestMemoryReport(
+ aGeneration, aAnonymize, aMinimizeMemoryUsage, aDMDFile,
+ [&, self](const uint32_t& aGeneration2) {
+ if (self->mMemoryReportRequest) {
+ self->mMemoryReportRequest->Finish(aGeneration2);
+ self->mMemoryReportRequest = nullptr;
+ }
+ },
+ [&, self](mozilla::ipc::ResponseRejectReason) {
+ self->mMemoryReportRequest = nullptr;
+ });
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvAddMemoryReport(
+ const MemoryReport& aReport) {
+ if (mMemoryReportRequest) {
+ mMemoryReportRequest->RecvReport(aReport);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvAddPerformanceMetrics(
+ const nsID& aID, nsTArray<PerformanceInfo>&& aMetrics) {
+ nsresult rv = PerformanceMetricsCollector::DataReceived(aID, aMetrics);
+ Unused << NS_WARN_IF(NS_FAILED(rv));
+ 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);
+}
+
+PTestShellParent* ContentParent::AllocPTestShellParent() {
+ return new TestShellParent();
+}
+
+bool ContentParent::DeallocPTestShellParent(PTestShellParent* shell) {
+ delete shell;
+ return true;
+}
+
+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;
+}
+
+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 Maybe<mozilla::net::LoadInfoArgs>& aLoadInfoArgs,
+ const nsACString& aMimeContentType, const nsACString& aContentDisposition,
+ const uint32_t& aContentDispositionHint,
+ const nsAString& aContentDispositionFilename, const bool& aForceSave,
+ const int64_t& aContentLength, const bool& aWasFileChannel,
+ nsIURI* aReferrer, const MaybeDiscarded<BrowsingContext>& aContext,
+ const bool& aShouldCloseWindow) {
+ RefPtr<ExternalHelperAppParent> parent = new ExternalHelperAppParent(
+ uri, aContentLength, aWasFileChannel, aContentDisposition,
+ aContentDispositionHint, aContentDispositionFilename);
+ return parent.forget();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvPExternalHelperAppConstructor(
+ PExternalHelperAppParent* actor, nsIURI* uri,
+ const Maybe<LoadInfoArgs>& loadInfoArgs, const nsACString& aMimeContentType,
+ const nsACString& aContentDisposition,
+ const uint32_t& aContentDispositionHint,
+ const nsAString& aContentDispositionFilename, const bool& aForceSave,
+ const int64_t& aContentLength, const bool& aWasFileChannel,
+ nsIURI* aReferrer, const MaybeDiscarded<BrowsingContext>& aContext,
+ const bool& aShouldCloseWindow) {
+ BrowsingContext* context = aContext.IsDiscarded() ? nullptr : aContext.get();
+ if (!static_cast<ExternalHelperAppParent*>(actor)->Init(
+ loadInfoArgs, aMimeContentType, aForceSave, aReferrer, context,
+ aShouldCloseWindow)) {
+ 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);
+}
+
+PBenchmarkStorageParent* ContentParent::AllocPBenchmarkStorageParent() {
+ return new BenchmarkStorageParent;
+}
+
+bool ContentParent::DeallocPBenchmarkStorageParent(
+ PBenchmarkStorageParent* aActor) {
+ delete aActor;
+ return true;
+}
+
+#ifdef MOZ_WEBSPEECH
+PSpeechSynthesisParent* ContentParent::AllocPSpeechSynthesisParent() {
+ if (!StaticPrefs::media_webspeech_synth_enabled()) {
+ return nullptr;
+ }
+ return new mozilla::dom::SpeechSynthesisParent();
+}
+
+bool ContentParent::DeallocPSpeechSynthesisParent(
+ PSpeechSynthesisParent* aActor) {
+ delete aActor;
+ return true;
+}
+
+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(
+ nsIURI* uri, nsIPrincipal* aTriggeringPrincipal,
+ nsIPrincipal* aRedirectPrincipal,
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ bool aWasExternallyTriggered, bool aHasValidUserGestureActivation) {
+ if (aContext.IsDiscarded()) {
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsIExternalProtocolService> extProtService(
+ do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID));
+ if (!extProtService) {
+ return IPC_OK();
+ }
+
+ if (!uri) {
+ return IPC_FAIL(this, "uri must not be null.");
+ }
+
+ BrowsingContext* bc = aContext.get();
+ extProtService->LoadURI(uri, aTriggeringPrincipal, aRedirectPrincipal, bc,
+ aWasExternallyTriggered,
+ aHasValidUserGestureActivation);
+ 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, 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, 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::RecvShowAlert(
+ nsIAlertNotification* aAlert) {
+ if (!aAlert) {
+ return IPC_FAIL(this, "aAlert must not be null.");
+ }
+ nsCOMPtr<nsIAlertsService> sysAlerts(components::Alerts::Service());
+ if (sysAlerts) {
+ sysAlerts->ShowAlert(aAlert, this);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvCloseAlert(const nsAString& aName,
+ bool aContextClosed) {
+ nsCOMPtr<nsIAlertsService> sysAlerts(components::Alerts::Service());
+ if (sysAlerts) {
+ sysAlerts->CloseAlert(aName, aContextClosed);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvDisableNotifications(
+ nsIPrincipal* aPrincipal) {
+ if (!aPrincipal) {
+ return IPC_FAIL(this, "No principal");
+ }
+
+ if (!ValidatePrincipal(aPrincipal)) {
+ LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __func__);
+ }
+ Unused << Notification::RemovePermission(aPrincipal);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvOpenNotificationSettings(
+ nsIPrincipal* aPrincipal) {
+ if (!aPrincipal) {
+ return IPC_FAIL(this, "No principal");
+ }
+
+ if (!ValidatePrincipal(aPrincipal)) {
+ LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __func__);
+ }
+ Unused << Notification::OpenSettings(aPrincipal);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvNotificationEvent(
+ const nsAString& aType, const NotificationEventData& aData) {
+ nsCOMPtr<nsIServiceWorkerManager> swm =
+ mozilla::components::ServiceWorkerManager::Service();
+ if (NS_WARN_IF(!swm)) {
+ // Probably shouldn't happen, but no need to crash the child process.
+ return IPC_OK();
+ }
+
+ if (aType.EqualsLiteral("click")) {
+ nsresult rv = swm->SendNotificationClickEvent(
+ aData.originSuffix(), aData.scope(), aData.ID(), aData.title(),
+ aData.dir(), aData.lang(), aData.body(), aData.tag(), aData.icon(),
+ aData.data(), aData.behavior());
+ Unused << NS_WARN_IF(NS_FAILED(rv));
+ } else {
+ MOZ_ASSERT(aType.EqualsLiteral("close"));
+ nsresult rv = swm->SendNotificationCloseEvent(
+ aData.originSuffix(), aData.scope(), aData.ID(), aData.title(),
+ aData.dir(), aData.lang(), aData.body(), aData.tag(), aData.icon(),
+ aData.data(), aData.behavior());
+ Unused << NS_WARN_IF(NS_FAILED(rv));
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvSyncMessage(
+ const nsAString& aMsg, const ClonedMessageData& aData,
+ nsTArray<StructuredCloneData>* aRetvals) {
+ AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING("ContentParent::RecvSyncMessage",
+ OTHER, aMsg);
+ MMPrinter::Print("ContentParent::RecvSyncMessage", aMsg, aData);
+
+ RefPtr<nsFrameMessageManager> ppm = mMessageManager;
+ if (ppm) {
+ ipc::StructuredCloneData data;
+ ipc::UnpackClonedMessageData(aData, data);
+
+ ppm->ReceiveMessage(ppm, nullptr, aMsg, true, &data, aRetvals,
+ IgnoreErrors());
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvAsyncMessage(
+ const nsAString& aMsg, const ClonedMessageData& aData) {
+ AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING("ContentParent::RecvAsyncMessage",
+ OTHER, aMsg);
+ MMPrinter::Print("ContentParent::RecvAsyncMessage", aMsg, aData);
+
+ RefPtr<nsFrameMessageManager> ppm = mMessageManager;
+ if (ppm) {
+ ipc::StructuredCloneData data;
+ ipc::UnpackClonedMessageData(aData, data);
+
+ ppm->ReceiveMessage(ppm, nullptr, aMsg, false, &data, nullptr,
+ IgnoreErrors());
+ }
+ 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) {
+ Unused << SendGeolocationUpdate(postion);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ContentParent::HandleEvent(GeolocationPositionError* positionError) {
+ Unused << 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 mozilla::Maybe<LoadInfoArgs>& loadInfoArgs,
+ const nsAString& entryName, const nsAString& initiatorType,
+ UniquePtr<PerformanceTimingData>&& aData) {
+ if (!aData) {
+ return IPC_FAIL(this, "aData should not be null");
+ }
+
+ if (loadInfoArgs.isNothing()) {
+ return IPC_FAIL(this, "loadInfoArgs 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");
+
+ Unused << parent->GetContentParent()->SendReportFrameTimingData(
+ loadInfoArgs, entryName, initiatorType, std::move(aData));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvScriptError(
+ const nsAString& aMessage, const nsAString& aSourceName,
+ const nsAString& aSourceLine, const uint32_t& aLineNumber,
+ const uint32_t& aColNumber, const uint32_t& aFlags,
+ const nsACString& aCategory, const bool& aFromPrivateWindow,
+ const uint64_t& aInnerWindowId, const bool& aFromChromeContext) {
+ return RecvScriptErrorInternal(aMessage, aSourceName, aSourceLine,
+ aLineNumber, aColNumber, aFlags, aCategory,
+ aFromPrivateWindow, aFromChromeContext);
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvScriptErrorWithStack(
+ const nsAString& aMessage, const nsAString& aSourceName,
+ const nsAString& aSourceLine, const uint32_t& aLineNumber,
+ const uint32_t& aColNumber, const uint32_t& aFlags,
+ const nsACString& aCategory, const bool& aFromPrivateWindow,
+ const bool& aFromChromeContext, const ClonedMessageData& aFrame) {
+ return RecvScriptErrorInternal(
+ aMessage, aSourceName, aSourceLine, aLineNumber, aColNumber, aFlags,
+ aCategory, aFromPrivateWindow, aFromChromeContext, &aFrame);
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvScriptErrorInternal(
+ const nsAString& aMessage, const nsAString& aSourceName,
+ const nsAString& aSourceLine, const uint32_t& aLineNumber,
+ const uint32_t& aColNumber, const uint32_t& aFlags,
+ const nsACString& aCategory, const bool& aFromPrivateWindow,
+ const bool& aFromChromeContext, const ClonedMessageData* aStack) {
+ nsresult rv;
+ nsCOMPtr<nsIConsoleService> consoleService =
+ do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) {
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsIScriptError> msg;
+
+ if (aStack) {
+ StructuredCloneData data;
+ UnpackClonedMessageData(*aStack, data);
+
+ AutoJSAPI jsapi;
+ if (NS_WARN_IF(!jsapi.Init(xpc::PrivilegedJunkScope()))) {
+ MOZ_CRASH();
+ }
+ JSContext* cx = jsapi.cx();
+
+ JS::Rooted<JS::Value> stack(cx);
+ ErrorResult rv;
+ data.Read(cx, &stack, rv);
+ if (rv.Failed() || !stack.isObject()) {
+ rv.SuppressException();
+ return IPC_OK();
+ }
+
+ JS::Rooted<JSObject*> stackObj(cx, &stack.toObject());
+ MOZ_ASSERT(JS::IsUnwrappedSavedFrame(stackObj));
+
+ JS::Rooted<JSObject*> stackGlobal(cx, JS::GetNonCCWObjectGlobal(stackObj));
+ msg = new nsScriptErrorWithStack(JS::NothingHandleValue, stackObj,
+ stackGlobal);
+ } else {
+ msg = new nsScriptError();
+ }
+
+ rv = msg->Init(aMessage, aSourceName, aSourceLine, aLineNumber, aColNumber,
+ aFlags, aCategory, aFromPrivateWindow, aFromChromeContext);
+ 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,
+ StructuredCloneData& aHelper) {
+ ClonedMessageData data;
+ if (!BuildClonedMessageData(aHelper, data)) {
+ return NS_ERROR_DOM_DATA_CLONE_ERR;
+ }
+ if (!SendAsyncMessage(aMessage, data)) {
+ 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) {
+ RefPtr<DataSourceSurface> surf =
+ nsContentUtils::IPCImageToSurface(std::move(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::RecvRecordingDeviceEvents(
+ const nsAString& aRecordingStatus, const nsAString& aPageURL,
+ const bool& aIsAudio, const bool& aIsVideo) {
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ // recording-device-ipc-events needs to gather more information from content
+ // process
+ RefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();
+ props->SetPropertyAsUint64(u"childID"_ns, ChildID());
+ props->SetPropertyAsBool(u"isAudio"_ns, aIsAudio);
+ props->SetPropertyAsBool(u"isVideo"_ns, aIsVideo);
+ props->SetPropertyAsAString(u"requestURL"_ns, aPageURL);
+
+ obs->NotifyObservers((nsIPropertyBag2*)props, "recording-device-ipc-events",
+ PromiseFlatString(aRecordingStatus).get());
+ } else {
+ NS_WARNING(
+ "Could not get the Observer service for "
+ "ContentParent::RecvRecordingDeviceEvents.");
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvAddIdleObserver(
+ const uint64_t& aObserver, 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, aObserver, 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& aObserver, const uint32_t& aIdleTimeInS) {
+ RefPtr<ParentIdleListener> listener;
+ for (int32_t i = mIdleListeners.Length() - 1; i >= 0; --i) {
+ listener = static_cast<ParentIdleListener*>(mIdleListeners[i].get());
+ if (listener->mObserver == aObserver && listener->mTime == aIdleTimeInS) {
+ nsresult rv;
+ nsCOMPtr<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(0 > mChildXSocketFdDup.get(), "Already backed up X resources??");
+ if (aXSocketFd.IsValid()) {
+ auto rawFD = aXSocketFd.ClonePlatformHandle();
+ mChildXSocketFdDup.reset(rawFD.release());
+ }
+#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);
+ }
+ Unused << 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);
+ Unused << 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();
+ FileDescOrError result;
+ if (fd.IsValid()) {
+ result = fd;
+ } else {
+ result = NS_ERROR_FAILURE;
+ }
+ aResolver(std::move(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)) {
+ Unused << cp->SendUpdateDictionaryList(dictionaries);
+ }
+}
+
+void ContentParent::NotifyUpdatedFonts(bool aFullRebuild) {
+ if (gfxPlatformFontList::PlatformFontList()->SharedFontList()) {
+ for (auto* cp : AllProcesses(eLive)) {
+ Unused << cp->SendRebuildFontList(aFullRebuild);
+ }
+ return;
+ }
+
+ SystemFontList fontList;
+ gfxPlatform::GetPlatform()->ReadSystemFontList(&fontList);
+
+ for (auto* cp : AllProcesses(eLive)) {
+ Unused << 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
+
+void ContentParent::MaybeInvokeDragSession(BrowserParent* aParent) {
+ // dnd uses IPCBlob to transfer data to the content process and the IPC
+ // message is sent as normal priority. When sending input events with input
+ // priority, the message may be preempted by the later dnd events. To make
+ // sure the input events and the blob message are processed in time order
+ // on the content process, we temporarily send the input events with normal
+ // priority when there is an active dnd session.
+ SetInputPriorityEventEnabled(false);
+
+ nsCOMPtr<nsIDragService> dragService =
+ do_GetService("@mozilla.org/widget/dragservice;1");
+ if (dragService && dragService->MaybeAddChildProcess(this)) {
+ // We need to send transferable data to child process.
+ nsCOMPtr<nsIDragSession> session;
+ dragService->GetCurrentSession(getter_AddRefs(session));
+ if (session) {
+ nsTArray<IPCTransferableData> ipcTransferables;
+ RefPtr<DataTransfer> transfer = session->GetDataTransfer();
+ if (!transfer) {
+ // Pass eDrop to get DataTransfer with external
+ // drag formats cached.
+ transfer = new DataTransfer(nullptr, eDrop, true, -1);
+ session->SetDataTransfer(transfer);
+ }
+ // Note, even though this fills the DataTransfer object with
+ // external data, the data is usually transfered over IPC lazily when
+ // needed.
+ transfer->FillAllExternalData();
+ nsCOMPtr<nsILoadContext> lc =
+ aParent ? aParent->GetLoadContext() : nullptr;
+ nsCOMPtr<nsIArray> transferables = transfer->GetTransferables(lc);
+ nsContentUtils::TransferablesToIPCTransferableDatas(
+ transferables, ipcTransferables, false, this);
+ uint32_t action;
+ session->GetDragAction(&action);
+
+ RefPtr<WindowContext> sourceWC;
+ session->GetSourceWindowContext(getter_AddRefs(sourceWC));
+ RefPtr<WindowContext> sourceTopWC;
+ session->GetSourceTopWindowContext(getter_AddRefs(sourceTopWC));
+ mozilla::Unused << SendInvokeDragSession(
+ sourceWC, sourceTopWC, std::move(ipcTransferables), action);
+ }
+ }
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvUpdateDropEffect(
+ const uint32_t& aDragAction, const uint32_t& aDropEffect) {
+ nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
+ if (dragSession) {
+ dragSession->SetDragAction(aDragAction);
+ RefPtr<DataTransfer> dt = dragSession->GetDataTransfer();
+ if (dt) {
+ dt->SetDropEffectInt(aDropEffect);
+ }
+ dragSession->UpdateDragEffect();
+ }
+ return IPC_OK();
+}
+
+PContentPermissionRequestParent*
+ContentParent::AllocPContentPermissionRequestParent(
+ const nsTArray<PermissionRequest>& aRequests, nsIPrincipal* aPrincipal,
+ nsIPrincipal* aTopLevelPrincipal, const bool& aIsHandlingUserInput,
+ const bool& aMaybeUnsafePermissionDelegate, const TabId& aTabId) {
+ 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(
+ aRequests, tp->GetOwnerElement(), aPrincipal, topPrincipal,
+ aIsHandlingUserInput, aMaybeUnsafePermissionDelegate, aTabId);
+}
+
+bool ContentParent::DeallocPContentPermissionRequestParent(
+ PContentPermissionRequestParent* actor) {
+ nsContentPermissionUtils::NotifyRemoveContentPermissionRequestParent(actor);
+ delete actor;
+ return true;
+}
+
+PWebBrowserPersistDocumentParent*
+ContentParent::AllocPWebBrowserPersistDocumentParent(
+ PBrowserParent* aBrowser, const MaybeDiscarded<BrowsingContext>& aContext) {
+ return new WebBrowserPersistDocumentParent();
+}
+
+bool ContentParent::DeallocPWebBrowserPersistDocumentParent(
+ PWebBrowserPersistDocumentParent* aActor) {
+ delete aActor;
+ return true;
+}
+
+mozilla::ipc::IPCResult ContentParent::CommonCreateWindow(
+ PBrowserParent* aThisTab, BrowsingContext& aParent, bool aSetOpener,
+ const uint32_t& aChromeFlags, const bool& aCalledFromJS,
+ const bool& aForPrinting, const bool& aForWindowDotPrint,
+ nsIURI* aURIToLoad, const nsACString& aFeatures,
+ BrowserParent* aNextRemoteBrowser, const nsAString& aName,
+ nsresult& aResult, nsCOMPtr<nsIRemoteTab>& aNewRemoteTab,
+ bool* aWindowIsNew, int32_t& aOpenLocation,
+ nsIPrincipal* aTriggeringPrincipal, nsIReferrerInfo* aReferrerInfo,
+ bool aLoadURI, nsIContentSecurityPolicy* aCsp,
+ const OriginAttributes& aOriginAttributes) {
+ // 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->mOriginAttributes = aOriginAttributes;
+
+ 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 one.
+ if (!outerWin) {
+ outerWin = nsContentUtils::GetMostRecentNonPBWindow();
+ if (NS_WARN_IF(!outerWin)) {
+ aResult = NS_ERROR_FAILURE;
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsIDOMChromeWindow> rootChromeWin = do_QueryInterface(outerWin);
+ if (rootChromeWin) {
+ rootChromeWin->GetBrowserDOMWindow(getter_AddRefs(browserDOMWin));
+ }
+ }
+
+ aOpenLocation = nsWindowWatcher::GetWindowOpenLocation(
+ outerWin, aChromeFlags, aCalledFromJS, aForPrinting);
+
+ MOZ_ASSERT(aOpenLocation == nsIBrowserDOMWindow::OPEN_NEWTAB ||
+ aOpenLocation == nsIBrowserDOMWindow::OPEN_NEWWINDOW ||
+ aOpenLocation == nsIBrowserDOMWindow::OPEN_PRINT_BROWSER);
+
+ if (NS_WARN_IF(!browserDOMWin)) {
+ // Opening in the same window or headless requires an nsIBrowserDOMWindow.
+ aOpenLocation = nsIBrowserDOMWindow::OPEN_NEWWINDOW;
+ }
+
+ if (aOpenLocation == nsIBrowserDOMWindow::OPEN_NEWTAB ||
+ 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->SetCsp(aCsp);
+
+ RefPtr<Element> el;
+
+ if (aLoadURI) {
+ aResult = browserDOMWin->OpenURIInFrame(aURIToLoad, params, aOpenLocation,
+ nsIBrowserDOMWindow::OPEN_NEW,
+ aName, getter_AddRefs(el));
+ } else {
+ aResult = browserDOMWin->CreateContentWindowInFrame(
+ aURIToLoad, params, aOpenLocation, nsIBrowserDOMWindow::OPEN_NEW,
+ aName, 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, 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);
+ RefPtr<BrowserParent> newBrowserParent = newBrowserHost->GetActor();
+
+ // 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();
+ }
+
+ // If we were passed a name for the window which would override the default,
+ // we should send it down to the new tab.
+ if (nsContentUtils::IsOverridingWindowName(aName)) {
+ MOZ_ALWAYS_SUCCEEDS(newBrowserHost->GetBrowsingContext()->SetName(aName));
+ }
+
+ MOZ_ASSERT(newBrowserHost->GetBrowsingContext()->OriginAttributesRef() ==
+ aOriginAttributes);
+
+ if (aURIToLoad && aLoadURI) {
+ nsCOMPtr<mozIDOMWindowProxy> openerWindow;
+ if (aSetOpener && topParent) {
+ openerWindow = topParent->GetParentWindowOuter();
+ }
+ nsCOMPtr<nsIBrowserDOMWindow> newBrowserDOMWin =
+ newBrowserParent->GetBrowserDOMWindow();
+ if (NS_WARN_IF(!newBrowserDOMWin)) {
+ aResult = NS_ERROR_ABORT;
+ return IPC_OK();
+ }
+ RefPtr<BrowsingContext> bc;
+ aResult = newBrowserDOMWin->OpenURI(
+ aURIToLoad, openInfo, nsIBrowserDOMWindow::OPEN_CURRENTWINDOW,
+ nsIBrowserDOMWindow::OPEN_NEW, aTriggeringPrincipal, aCsp,
+ getter_AddRefs(bc));
+ }
+
+ 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& aForPrintPreview, nsIURI* aURIToLoad,
+ const nsACString& aFeatures, nsIPrincipal* aTriggeringPrincipal,
+ nsIContentSecurityPolicy* aCsp, nsIReferrerInfo* aReferrerInfo,
+ const OriginAttributes& aOriginAttributes,
+ CreateWindowResolver&& aResolve) {
+ if (!aTriggeringPrincipal) {
+ return IPC_FAIL(this, "No principal");
+ }
+
+ if (!ValidatePrincipal(aTriggeringPrincipal)) {
+ LogAndAssertFailedPrincipalValidationInfo(aTriggeringPrincipal, __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, aForPrintPreview, aURIToLoad, aFeatures, newTab,
+ VoidString(), rv, newRemoteTab, &cwi.windowOpened(), openLocation,
+ aTriggeringPrincipal, aReferrerInfo, /* aLoadUri = */ false, aCsp,
+ aOriginAttributes);
+ 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));
+
+ 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::RecvCreateWindowInDifferentProcess(
+ PBrowserParent* aThisTab, const MaybeDiscarded<BrowsingContext>& aParent,
+ const uint32_t& aChromeFlags, const bool& aCalledFromJS, nsIURI* aURIToLoad,
+ const nsACString& aFeatures, const nsAString& aName,
+ nsIPrincipal* aTriggeringPrincipal, nsIContentSecurityPolicy* aCsp,
+ nsIReferrerInfo* aReferrerInfo, const OriginAttributes& aOriginAttributes) {
+ MOZ_DIAGNOSTIC_ASSERT(!nsContentUtils::IsSpecialName(aName));
+
+ // 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)) {
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsIRemoteTab> newRemoteTab;
+ bool windowIsNew;
+ int32_t openLocation = nsIBrowserDOMWindow::OPEN_NEWWINDOW;
+
+ // If we have enough data, check the schemes of the loader and loadee
+ // to make sure they make sense.
+ if (aURIToLoad && aURIToLoad->SchemeIs("file") &&
+ GetRemoteType() != FILE_REMOTE_TYPE &&
+ Preferences::GetBool("browser.tabs.remote.enforceRemoteTypeRestrictions",
+ false)) {
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+# ifdef DEBUG
+ nsAutoCString uriToLoadStr;
+ nsAutoCString triggeringUriStr;
+ aURIToLoad->GetAsciiSpec(uriToLoadStr);
+ aTriggeringPrincipal->GetAsciiSpec(triggeringUriStr);
+
+ NS_WARNING(nsPrintfCString(
+ "RecvCreateWindowInDifferentProcess blocked loading file "
+ "scheme from non-file remotetype: %s tried to load %s",
+ triggeringUriStr.get(), uriToLoadStr.get())
+ .get());
+# endif
+ MOZ_CRASH(
+ "RecvCreateWindowInDifferentProcess blocked loading improper scheme");
+#endif
+ return IPC_OK();
+ }
+
+ nsresult rv;
+ mozilla::ipc::IPCResult ipcResult = CommonCreateWindow(
+ aThisTab, *parent, /* aSetOpener = */ false, aChromeFlags, aCalledFromJS,
+ /* aForPrinting = */ false,
+ /* aForPrintPreview = */ false, aURIToLoad, aFeatures,
+ /* aNextRemoteBrowser = */ nullptr, aName, rv, newRemoteTab, &windowIsNew,
+ openLocation, aTriggeringPrincipal, aReferrerInfo,
+ /* aLoadUri = */ true, aCsp, aOriginAttributes);
+ if (!ipcResult) {
+ return ipcResult;
+ }
+
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Call to CommonCreateWindow failed.");
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvShutdownProfile(
+ const nsACString& aProfile) {
+ profiler_received_exit_profile(aProfile);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvShutdownPerfStats(
+ const nsACString& aPerfStats) {
+ PerfStats::StorePerfStats(this, aPerfStats);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvGetGraphicsDeviceInitData(
+ ContentDeviceData* aOut) {
+ gfxPlatform::GetPlatform()->BuildContentDeviceData(aOut);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvGetOutputColorProfileData(
+ nsTArray<uint8_t>* aOutputColorProfileData) {
+ (*aOutputColorProfileData) =
+ gfxPlatform::GetPlatform()->GetPlatformCMSOutputProfileData();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvGetFontListShmBlock(
+ const uint32_t& aGeneration, const uint32_t& aIndex,
+ base::SharedMemoryHandle* 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 mozilla::fontlist::Pointer& aFacePtr,
+ const gfxSparseBitSet& aMap) {
+ auto* fontList = gfxPlatformFontList::PlatformFontList();
+ MOZ_RELEASE_ASSERT(fontList, "gfxPlatformFontList not initialized?");
+ fontList->SetCharacterMap(aGeneration, aFacePtr, 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 mozilla::fontlist::Pointer& aFamilyPtr) {
+ auto* fontList = gfxPlatformFontList::PlatformFontList();
+ MOZ_RELEASE_ASSERT(fontList, "gfxPlatformFontList not initialized?");
+ fontList->SetupFamilyCharMap(aGeneration, aFamilyPtr);
+ 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, base::SharedMemoryHandle* aOutHandle, uint32_t* aOutSize) {
+ if (!aURI) {
+ return IPC_FAIL(this, "aURI must not be null.");
+ }
+ nsHyphenationManager::Instance()->ShareHyphDictToProcess(
+ aURI, Pid(), aOutHandle, aOutSize);
+ 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 uint32_t& aGuardType, bool* aOutCrashed) {
+ // Only one driver crash guard should be active at a time, per-process.
+ MOZ_ASSERT(!mDriverCrashGuard);
+
+ UniquePtr<gfx::DriverCrashGuard> guard;
+ switch (gfx::CrashGuardType(aGuardType)) {
+ case gfx::CrashGuardType::D3D11Layers:
+ guard = MakeUnique<gfx::D3D11LayersCrashGuard>(this);
+ break;
+ case gfx::CrashGuardType::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 uint32_t& aGuardType) {
+ mDriverCrashGuard = nullptr;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvNotifyBenchmarkResult(
+ const nsAString& aCodecName, const uint32_t& aDecodeFPS)
+
+{
+ if (aCodecName.EqualsLiteral("VP9")) {
+ Preferences::SetUint(VP9Benchmark::sBenchmarkFpsPref, aDecodeFPS);
+ Preferences::SetUint(VP9Benchmark::sBenchmarkFpsVersionCheck,
+ VP9Benchmark::sBenchmarkVersionID);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvNotifyPushObservers(
+ const nsACString& aScope, nsIPrincipal* aPrincipal,
+ const nsAString& aMessageId) {
+ if (!aPrincipal) {
+ return IPC_FAIL(this, "No principal");
+ }
+
+ if (!ValidatePrincipal(aPrincipal)) {
+ LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __func__);
+ }
+ PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId, Nothing());
+ Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers()));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvNotifyPushObserversWithData(
+ const nsACString& aScope, nsIPrincipal* aPrincipal,
+ const nsAString& aMessageId, nsTArray<uint8_t>&& aData) {
+ if (!aPrincipal) {
+ return IPC_FAIL(this, "No principal");
+ }
+
+ if (!ValidatePrincipal(aPrincipal)) {
+ LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __func__);
+ }
+ PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId,
+ Some(std::move(aData)));
+ Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers()));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+ContentParent::RecvNotifyPushSubscriptionChangeObservers(
+ const nsACString& aScope, nsIPrincipal* aPrincipal) {
+ if (!aPrincipal) {
+ return IPC_FAIL(this, "No principal");
+ }
+
+ if (!ValidatePrincipal(aPrincipal)) {
+ LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __func__);
+ }
+ PushSubscriptionChangeDispatcher dispatcher(aScope, aPrincipal);
+ Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers()));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvPushError(const nsACString& aScope,
+ nsIPrincipal* aPrincipal,
+ const nsAString& aMessage,
+ const uint32_t& aFlags) {
+ if (!aPrincipal) {
+ return IPC_FAIL(this, "No principal");
+ }
+
+ if (!ValidatePrincipal(aPrincipal)) {
+ LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __func__);
+ }
+ PushErrorDispatcher dispatcher(aScope, aPrincipal, aMessage, aFlags);
+ Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers()));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+ContentParent::RecvNotifyPushSubscriptionModifiedObservers(
+ const nsACString& aScope, nsIPrincipal* aPrincipal) {
+ if (!aPrincipal) {
+ return IPC_FAIL(this, "No principal");
+ }
+
+ if (!ValidatePrincipal(aPrincipal)) {
+ LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __func__);
+ }
+ PushSubscriptionModifiedDispatcher dispatcher(aScope, aPrincipal);
+ Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObservers()));
+ return IPC_OK();
+}
+
+/* static */
+void ContentParent::BroadcastBlobURLRegistration(
+ const nsACString& aURI, BlobImpl* aBlobImpl, nsIPrincipal* aPrincipal,
+ const Maybe<nsID>& aAgentClusterId, 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;
+ }
+
+ IPCBlob ipcBlob;
+ rv = IPCBlobUtils::Serialize(aBlobImpl, ipcBlob);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ break;
+ }
+
+ Unused << cp->SendBlobURLRegistration(uri, ipcBlob, aPrincipal,
+ aAgentClusterId);
+ }
+ }
+}
+
+/* static */
+void ContentParent::BroadcastBlobURLUnregistration(
+ const nsACString& aURI, nsIPrincipal* aPrincipal,
+ ContentParent* aIgnoreThisCP) {
+ uint64_t originHash = ComputeLoadedOriginHash(aPrincipal);
+
+ bool toBeSent =
+ BlobURLProtocolHandler::IsBlobURLBroadcastPrincipal(aPrincipal);
+
+ nsCString uri(aURI);
+
+ for (auto* cp : AllProcesses(eLive)) {
+ if (cp != aIgnoreThisCP &&
+ (toBeSent || cp->mLoadedOriginHashes.Contains(originHash))) {
+ Unused << cp->SendBlobURLUnregistration(uri);
+ }
+ }
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvStoreAndBroadcastBlobURLRegistration(
+ const nsACString& aURI, const IPCBlob& aBlob, nsIPrincipal* aPrincipal,
+ const Maybe<nsID>& aAgentClusterId) {
+ if (!aPrincipal) {
+ return IPC_FAIL(this, "No principal");
+ }
+
+ if (!ValidatePrincipal(aPrincipal, {ValidatePrincipalOptions::AllowSystem})) {
+ LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __func__);
+ }
+ RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(aBlob);
+ if (NS_WARN_IF(!blobImpl)) {
+ return IPC_FAIL(this, "Blob deserialization failed.");
+ }
+
+ BlobURLProtocolHandler::AddDataEntry(aURI, aPrincipal, aAgentClusterId,
+ blobImpl);
+ BroadcastBlobURLRegistration(aURI, blobImpl, aPrincipal, aAgentClusterId,
+ this);
+
+ // We want to store this blobURL, so we can unregister it if the child
+ // crashes.
+ mBlobURLs.AppendElement(aURI);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+ContentParent::RecvUnstoreAndBroadcastBlobURLUnregistration(
+ const nsACString& aURI, nsIPrincipal* aPrincipal) {
+ if (!aPrincipal) {
+ return IPC_FAIL(this, "No principal");
+ }
+
+ if (!ValidatePrincipal(aPrincipal, {ValidatePrincipalOptions::AllowSystem})) {
+ LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __func__);
+ }
+ BlobURLProtocolHandler::RemoveDataEntry(aURI, false /* Don't broadcast */);
+ BroadcastBlobURLUnregistration(aURI, aPrincipal, this);
+ mBlobURLs.RemoveElement(aURI);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvGetFilesRequest(
+ const nsID& aUUID, const nsAString& aDirectoryPath,
+ const bool& aRecursiveFlag) {
+ MOZ_ASSERT(!mGetFilesPendingRequests.GetWeak(aUUID));
+
+ if (!mozilla::Preferences::GetBool("dom.filesystem.pathcheck.disabled",
+ false)) {
+ RefPtr<FileSystemSecurity> fss = FileSystemSecurity::Get();
+ if (!fss) {
+ return IPC_FAIL(this, "Failed to get FileSystemSecurity.");
+ }
+
+ if (!fss->ContentProcessHasAccessTo(ChildID(), aDirectoryPath)) {
+ return IPC_FAIL(this, "ContentProcessHasAccessTo failed.");
+ }
+ }
+
+ ErrorResult rv;
+ RefPtr<GetFilesHelper> helper = GetFilesHelperParent::Create(
+ aUUID, aDirectoryPath, aRecursiveFlag, this, rv);
+
+ if (NS_WARN_IF(rv.Failed())) {
+ if (!SendGetFilesResponse(aUUID,
+ GetFilesResponseFailure(rv.StealNSResult()))) {
+ return IPC_FAIL(this, "SendGetFilesResponse failed.");
+ }
+ return IPC_OK();
+ }
+
+ mGetFilesPendingRequests.InsertOrUpdate(aUUID, std::move(helper));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvDeleteGetFilesRequest(
+ const nsID& aUUID) {
+ mGetFilesPendingRequests.Remove(aUUID);
+ return IPC_OK();
+}
+
+void ContentParent::SendGetFilesResponseAndForget(
+ const nsID& aUUID, const GetFilesResponseResult& aResult) {
+ if (mGetFilesPendingRequests.Remove(aUUID)) {
+ Unused << SendGetFilesResponse(aUUID, aResult);
+ }
+}
+
+void ContentParent::PaintTabWhileInterruptingJS(
+ BrowserParent* aBrowserParent, const layers::LayersObserverEpoch& aEpoch) {
+ if (!mHangMonitorActor) {
+ return;
+ }
+ ProcessHangMonitor::PaintWhileInterruptingJS(mHangMonitorActor,
+ aBrowserParent, aEpoch);
+}
+
+void ContentParent::UnloadLayersWhileInterruptingJS(
+ BrowserParent* aBrowserParent, const layers::LayersObserverEpoch& aEpoch) {
+ if (!mHangMonitorActor) {
+ return;
+ }
+ ProcessHangMonitor::UnloadLayersWhileInterruptingJS(mHangMonitorActor,
+ aBrowserParent, aEpoch);
+}
+
+void ContentParent::CancelContentJSExecutionIfRunning(
+ BrowserParent* aBrowserParent, nsIRemoteTab::NavigationType aNavigationType,
+ const CancelContentJSOptions& aCancelContentJSOptions) {
+ if (!mHangMonitorActor) {
+ return;
+ }
+
+ ProcessHangMonitor::CancelContentJSExecutionIfRunning(
+ mHangMonitorActor, aBrowserParent, aNavigationType,
+ aCancelContentJSOptions);
+}
+
+void ContentParent::UpdateCookieStatus(nsIChannel* aChannel) {
+ PNeckoParent* neckoParent = LoneManagedOrNullAsserts(ManagedPNeckoParent());
+ PCookieServiceParent* csParent =
+ LoneManagedOrNullAsserts(neckoParent->ManagedPCookieServiceParent());
+ if (csParent) {
+ auto* cs = static_cast<CookieServiceParent*>(csParent);
+ cs->TrackCookieLoad(aChannel);
+ }
+}
+
+nsresult ContentParent::AboutToLoadHttpFtpDocumentForChild(
+ nsIChannel* aChannel, bool* aShouldWaitForPermissionCookieUpdate) {
+ MOZ_ASSERT(aChannel);
+
+ if (aShouldWaitForPermissionCookieUpdate) {
+ *aShouldWaitForPermissionCookieUpdate = false;
+ }
+
+ nsresult rv;
+ bool isDocument = aChannel->IsDocument();
+ if (!isDocument) {
+ // We may be looking at a nsIHttpChannel which has isMainDocumentChannel set
+ // (e.g. the internal http channel for a view-source: load.).
+ nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
+ if (httpChannel) {
+ rv = httpChannel->GetIsMainDocumentChannel(&isDocument);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+ if (!isDocument) {
+ return NS_OK;
+ }
+
+ // 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;
+ rv = ssm->GetChannelResultPrincipals(aChannel, getter_AddRefs(principal),
+ getter_AddRefs(partitionedPrincipal));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Let the caller know we're going to send main thread IPC for updating
+ // permisssions/cookies.
+ if (aShouldWaitForPermissionCookieUpdate) {
+ *aShouldWaitForPermissionCookieUpdate = true;
+ }
+
+ 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);
+
+ nsLoadFlags newLoadFlags;
+ aChannel->GetLoadFlags(&newLoadFlags);
+ if (newLoadFlags & nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE) {
+ UpdateCookieStatus(aChannel);
+ }
+
+ RefPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
+ RefPtr<BrowsingContext> browsingContext;
+ rv = loadInfo->GetTargetBrowsingContext(getter_AddRefs(browsingContext));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ 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)) {
+ 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);
+ }
+
+ return NS_OK;
+}
+
+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 Maybe<nsID>& aAgentClusterId, 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;
+ }
+
+ IPCBlob ipcBlob;
+ nsresult rv = IPCBlobUtils::Serialize(aBlobImpl, ipcBlob);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return false;
+ }
+
+ registrations.AppendElement(
+ BlobURLRegistrationData(nsCString(aURI), ipcBlob, aBlobPrincipal,
+ aAgentClusterId, aRevoked));
+
+ rv = TransmitPermissionsForPrincipal(aBlobPrincipal);
+ Unused << NS_WARN_IF(NS_FAILED(rv));
+ return true;
+ });
+
+ if (!registrations.IsEmpty()) {
+ Unused << SendInitBlobURLs(registrations);
+ }
+ }
+}
+
+void ContentParent::TransmitBlobDataIfBlobURL(nsIURI* aURI) {
+ MOZ_ASSERT(aURI);
+
+ nsCOMPtr<nsIPrincipal> principal;
+ if (BlobURLProtocolHandler::GetBlobURLPrincipal(aURI,
+ 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)) {
+ Unused << SendSetPermissionsWithKey(aKey, perms);
+ }
+}
+
+bool ContentParent::NeedsPermissionsUpdate(
+ const nsACString& aPermissionKey) const {
+ return mActivePermissionKeys.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();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvRecordPageLoadEvent(
+ const mozilla::glean::perf::PageLoadExtra& aPageLoadEventExtra) {
+ mozilla::glean::perf::page_load.Record(mozilla::Some(aPageLoadEventExtra));
+
+ // Send the PageLoadPing after every 30 page loads, or on startup.
+ if (++sPageLoadEventCounter >= 30) {
+ NS_SUCCEEDED(NS_DispatchToMainThreadQueue(
+ NS_NewRunnableFunction(
+ "PageLoadPingIdleTask",
+ [] { mozilla::glean_pings::Pageload.Submit("threshold"_ns); }),
+ EventQueuePriority::Idle));
+ sPageLoadEventCounter = 0;
+ }
+
+ 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)) {
+ LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __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;
+}
+
+PLoginReputationParent* ContentParent::AllocPLoginReputationParent(
+ nsIURI* aURI) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ RefPtr<LoginReputationParent> actor = new LoginReputationParent();
+ return actor.forget().take();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvPLoginReputationConstructor(
+ PLoginReputationParent* aActor, nsIURI* aURI) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aActor);
+
+ if (!aURI) {
+ return IPC_FAIL(this, "aURI should not be null");
+ }
+
+ auto* actor = static_cast<LoginReputationParent*>(aActor);
+ return actor->QueryReputation(aURI);
+}
+
+bool ContentParent::DeallocPLoginReputationParent(
+ PLoginReputationParent* aActor) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aActor);
+
+ RefPtr<LoginReputationParent> actor =
+ dont_AddRef(static_cast<LoginReputationParent*>(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& aDetails) {
+ 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(aDetails), PersistedToDisk::No);
+ obs->NotifyObservers(hangDetails, "bhr-thread-hang", nullptr);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvAddCertException(
+ nsIX509Cert* aCert, const nsACString& aHostName, int32_t aPort,
+ const OriginAttributes& aOriginAttributes, bool aIsTemporary,
+ AddCertExceptionResolver&& aResolver) {
+ nsCOMPtr<nsICertOverrideService> overrideService =
+ do_GetService(NS_CERTOVERRIDE_CONTRACTID);
+ if (!overrideService) {
+ aResolver(NS_ERROR_FAILURE);
+ return IPC_OK();
+ }
+ nsresult rv = overrideService->RememberValidityOverride(
+ aHostName, aPort, aOriginAttributes, aCert, aIsTemporary);
+ aResolver(rv);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+ContentParent::RecvAutomaticStorageAccessPermissionCanBeGranted(
+ nsIPrincipal* aPrincipal,
+ AutomaticStorageAccessPermissionCanBeGrantedResolver&& aResolver) {
+ if (!aPrincipal) {
+ return IPC_FAIL(this, "No principal");
+ }
+
+ if (!ValidatePrincipal(aPrincipal)) {
+ LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __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 int& aAllowMode,
+ const Maybe<ContentBlockingNotifier::StorageAccessPermissionGrantedReason>&
+ aReason,
+ StorageAccessPermissionGrantedForOriginResolver&& aResolver) {
+ if (aParentContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+
+ if (!aTrackingPrincipal) {
+ return IPC_FAIL(this, "No principal");
+ }
+
+ // 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)
+ ->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::CompleteAllowAccessFor(
+ 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(static_cast<StorageAccessPromptChoices>(
+ aValue.ResolveValue()));
+ }
+ aResolver(choice);
+ });
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvSetAllowStorageAccessRequestFlag(
+ nsIPrincipal* aEmbeddedPrincipal, nsIURI* aEmbeddingOrigin,
+ SetAllowStorageAccessRequestFlagResolver&& aResolver) {
+ MOZ_ASSERT(aEmbeddedPrincipal);
+ MOZ_ASSERT(aEmbeddingOrigin);
+
+ if (!aEmbeddedPrincipal || !aEmbeddingOrigin) {
+ aResolver(false);
+ return IPC_OK();
+ }
+
+ // Get the permission manager and build the key.
+ RefPtr<PermissionManager> permManager = PermissionManager::GetInstance();
+ if (!permManager) {
+ aResolver(false);
+ return IPC_OK();
+ }
+ nsCOMPtr<nsIURI> embeddedURI = aEmbeddedPrincipal->GetURI();
+ nsCString permissionKey;
+ bool success = AntiTrackingUtils::CreateStorageRequestPermissionKey(
+ embeddedURI, permissionKey);
+ if (!success) {
+ aResolver(false);
+ return IPC_OK();
+ }
+
+ // Set the permission to ALLOW for a prefence specified amount of seconds.
+ // Time units are inconsistent, be careful
+ int64_t when = (PR_Now() / PR_USEC_PER_MSEC) +
+ StaticPrefs::dom_storage_access_forward_declared_lifetime() *
+ PR_MSEC_PER_SEC;
+ nsCOMPtr<nsIPrincipal> principal = BasePrincipal::CreateContentPrincipal(
+ aEmbeddingOrigin, aEmbeddedPrincipal->OriginAttributesRef());
+ nsresult rv = permManager->AddFromPrincipal(
+ principal, permissionKey, nsIPermissionManager::ALLOW_ACTION,
+ nsIPermissionManager::EXPIRE_TIME, when);
+ if (NS_FAILED(rv)) {
+ aResolver(false);
+ return IPC_OK();
+ }
+
+ // Resolve with success if we set the permission.
+ aResolver(true);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvTestAllowStorageAccessRequestFlag(
+ nsIPrincipal* aEmbeddingPrincipal, nsIURI* aEmbeddedOrigin,
+ TestAllowStorageAccessRequestFlagResolver&& aResolver) {
+ MOZ_ASSERT(aEmbeddingPrincipal);
+ MOZ_ASSERT(aEmbeddedOrigin);
+
+ // Get the permission manager and build the key.
+ RefPtr<PermissionManager> permManager = PermissionManager::GetInstance();
+ if (!permManager) {
+ aResolver(false);
+ return IPC_OK();
+ }
+ nsCString requestPermissionKey;
+ bool success = AntiTrackingUtils::CreateStorageRequestPermissionKey(
+ aEmbeddedOrigin, requestPermissionKey);
+ if (!success) {
+ aResolver(false);
+ return IPC_OK();
+ }
+
+ // Get the permission and resolve false if it is not set to ALLOW.
+ uint32_t access = nsIPermissionManager::UNKNOWN_ACTION;
+ nsresult rv = permManager->TestPermissionFromPrincipal(
+ aEmbeddingPrincipal, requestPermissionKey, &access);
+ if (NS_FAILED(rv)) {
+ aResolver(false);
+ return IPC_OK();
+ }
+ if (access != nsIPermissionManager::ALLOW_ACTION) {
+ aResolver(false);
+ return IPC_OK();
+ }
+
+ // Remove the permission, failing if the permission manager fails
+ rv = permManager->RemoveFromPrincipal(aEmbeddingPrincipal,
+ requestPermissionKey);
+ if (NS_FAILED(rv)) {
+ aResolver(false);
+ return IPC_OK();
+ }
+
+ // At this point, signal to our caller that the permission was set
+ aResolver(true);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvStoreUserInteractionAsPermission(
+ nsIPrincipal* aPrincipal) {
+ if (!aPrincipal) {
+ return IPC_FAIL(this, "No principal");
+ }
+
+ if (!ValidatePrincipal(aPrincipal)) {
+ LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __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();
+ nsCOMPtr<nsICookieJarSettings> cjs = wgp->CookieJarSettings();
+
+ Maybe<bool> result =
+ StorageAccessAPIHelper::CheckCookiesPermittedDecidesStorageAccessAPI(
+ cjs, aPrincipal);
+ aResolver(result);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvTestStorageAccessPermission(
+ nsIPrincipal* aEmbeddingPrincipal, const nsCString& aEmbeddedOrigin,
+ const TestStorageAccessPermissionResolver&& aResolver) {
+ // Get the permission manager and build the key.
+ RefPtr<PermissionManager> permManager = PermissionManager::GetInstance();
+ if (!permManager) {
+ aResolver(Nothing());
+ return IPC_OK();
+ }
+ nsCString requestPermissionKey;
+ AntiTrackingUtils::CreateStoragePermissionKey(aEmbeddedOrigin,
+ requestPermissionKey);
+
+ // Test the permission
+ uint32_t access = nsIPermissionManager::UNKNOWN_ACTION;
+ nsresult rv = permManager->TestPermissionFromPrincipal(
+ aEmbeddingPrincipal, requestPermissionKey, &access);
+ if (NS_FAILED(rv)) {
+ aResolver(Nothing());
+ return IPC_OK();
+ }
+ if (access == nsIPermissionManager::ALLOW_ACTION) {
+ aResolver(Some(true));
+ } else if (access == nsIPermissionManager::DENY_ACTION) {
+ aResolver(Some(false));
+ } else {
+ aResolver(Nothing());
+ }
+
+ 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) {
+ if (aContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+ if (RefPtr<IMediaInfoUpdater> updater =
+ aContext.get_canonical()->GetMediaController()) {
+ updater->NotifyMediaAudibleChanged(aContext.ContextId(), aState);
+ }
+ 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) {
+ Unused << 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 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::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 && parent->Group() != group) {
+ if (parent->Group()->Id() != aGroupId) {
+ return IPC_FAIL(this, "Parent has different group ID");
+ } else {
+ 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");
+ } else {
+ 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();
+}
+
+void ContentParent::UnregisterRemoveWorkerActor() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ {
+ MutexAutoLock lock(mThreadsafeHandle->mMutex);
+ if (--mThreadsafeHandle->mRemoteWorkerActorCount) {
+ return;
+ }
+ }
+
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
+ ("UnregisterRemoveWorkerActor %p", this));
+ MaybeBeginShutDown();
+}
+
+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();
+
+ // FIXME Need to check that the sending process has access to the unit of
+ // related
+ // browsing contexts of bc.
+
+ if (ContentParent* cp = context->GetContentParent()) {
+ Unused << 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()) {
+ Unused << 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()) {
+ Unused << 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()) {
+ Unused << cp->SendRaiseWindow(context, aCallerType, aActionId);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvAdjustWindowFocus(
+ const MaybeDiscarded<BrowsingContext>& aContext, bool aIsVisible,
+ 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::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)) {
+ Unused << cp->SendAdjustWindowFocus(context, aIsVisible, aActionId);
+ 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()) {
+ Unused << 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));
+ Unused << SendReviseFocusedBrowsingContext(
+ aActionId, fm->GetFocusedBrowsingContextInChrome(),
+ fm->GetActionIdForFocusedBrowsingContextInChrome());
+ return IPC_OK();
+ }
+
+ BrowserParent::UpdateFocusFromBrowsingContext();
+
+ context->Group()->EachOtherParent(this, [&](ContentParent* aParent) {
+ Unused << 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));
+ Unused << SendReviseActiveBrowsingContext(
+ aActionId, fm->GetActiveBrowsingContextInChrome(),
+ fm->GetActionIdForActiveBrowsingContextInChrome());
+ return IPC_OK();
+ }
+
+ context->Group()->EachOtherParent(this, [&](ContentParent* aParent) {
+ Unused << 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));
+ Unused << SendReviseActiveBrowsingContext(
+ aActionId, fm->GetActiveBrowsingContextInChrome(),
+ fm->GetActionIdForActiveBrowsingContextInChrome());
+ return IPC_OK();
+ }
+
+ context->Group()->EachOtherParent(this, [&](ContentParent* aParent) {
+ Unused << 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()) {
+ Unused << 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) {
+ Unused << 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()) {
+ Unused << cp->SendSetFocusedElement(aBrowsingContextToClear, false);
+ }
+ } else if (ancestorDifferent) {
+ if (ContentParent* cp = aAncestorBrowsingContextToFocus.get_canonical()
+ ->GetContentParent()) {
+ Unused << cp->SendSetFocusedElement(aAncestorBrowsingContextToFocus,
+ true);
+ }
+ }
+
+ if (ContentParent* cp = focusedBrowsingContext->GetContentParent()) {
+ Unused << 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()) {
+ Unused << cp->SendMaybeExitFullscreen(context);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvWindowPostMessage(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const ClonedOrErrorMessageData& 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();
+ }
+
+ ClonedOrErrorMessageData message;
+ StructuredCloneData messageFromChild;
+ if (aMessage.type() == ClonedOrErrorMessageData::TClonedMessageData) {
+ UnpackClonedMessageData(aMessage, messageFromChild);
+
+ ClonedMessageData clonedMessageData;
+ if (BuildClonedMessageData(messageFromChild, clonedMessageData)) {
+ message = std::move(clonedMessageData);
+ } else {
+ // FIXME Logging?
+ message = ErrorMessageData();
+ }
+ } else {
+ MOZ_ASSERT(aMessage.type() == ClonedOrErrorMessageData::TErrorMessageData);
+ message = ErrorMessageData();
+ }
+
+ Unused << cp->SendWindowPostMessage(context, message, 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.
+ Unused << 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 Maybe<nsID>& aAgentClusterId,
+ 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,
+ aAgentClusterId, 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.IsDiscarded()) {
+ 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& aPersist,
+ 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, aPersist,
+ 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,
+ HistoryGoResolver&& aResolveRequestedIndex) {
+ if (!aContext.IsDiscarded()) {
+ RefPtr<CanonicalBrowsingContext> canonical = aContext.get_canonical();
+ aResolveRequestedIndex(
+ canonical->HistoryGo(aOffset, aHistoryEpoch, aRequireUserInteraction,
+ aUserActivation, Some(ChildID())));
+ }
+ 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, [&](nsISHEntry* 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::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));
+ nsCOMPtr<SessionHistoryEntry> she = do_QueryInterface(entry);
+ if (she) {
+ 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.IsDiscarded()) {
+ 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.IsDiscarded()) {
+ aContext.get_canonical()->ReplaceActiveSessionHistoryEntry(&aInfo);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+ContentParent::RecvRemoveDynEntriesFromActiveSessionHistoryEntry(
+ const MaybeDiscarded<BrowsingContext>& aContext) {
+ if (!aContext.IsDiscarded()) {
+ aContext.get_canonical()->RemoveDynEntriesFromActiveSessionHistoryEntry();
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvRemoveFromSessionHistory(
+ const MaybeDiscarded<BrowsingContext>& aContext, const nsID& aChangeID) {
+ if (!aContext.IsDiscarded()) {
+ aContext.get_canonical()->RemoveFromSessionHistory(aChangeID);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvHistoryReload(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const uint32_t aReloadFlags) {
+ if (!aContext.IsDiscarded()) {
+ nsCOMPtr<nsISHistory> shistory =
+ aContext.get_canonical()->GetSessionHistory();
+ if (shistory) {
+ shistory->Reload(aReloadFlags);
+ }
+ }
+ 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;
+}
+
+IPCResult ContentParent::RecvRawMessage(
+ const JSActorMessageMeta& aMeta, const Maybe<ClonedMessageData>& aData,
+ const Maybe<ClonedMessageData>& aStack) {
+ Maybe<StructuredCloneData> data;
+ if (aData) {
+ data.emplace();
+ data->BorrowFromClonedMessageData(*aData);
+ }
+ Maybe<StructuredCloneData> stack;
+ if (aStack) {
+ stack.emplace();
+ stack->BorrowFromClonedMessageData(*aStack);
+ }
+ ReceiveRawMessage(aMeta, std::move(data), std::move(stack));
+ 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();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvSetContainerFeaturePolicy(
+ const MaybeDiscardedBrowsingContext& aContainerContext,
+ FeaturePolicy* aContainerFeaturePolicy) {
+ if (aContainerContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+
+ auto* context = aContainerContext.get_canonical();
+ context->SetContainerFeaturePolicy(aContainerFeaturePolicy);
+
+ 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
+}
+
+#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
+
+nsCString ThreadsafeContentParentHandle::GetRemoteType() {
+ MutexAutoLock lock(mMutex);
+ return mRemoteType;
+}
+
+bool ThreadsafeContentParentHandle::MaybeRegisterRemoteWorkerActor(
+ MoveOnlyFunction<bool(uint32_t, bool)> aCallback) {
+ MutexAutoLock lock(mMutex);
+ if (aCallback(mRemoteWorkerActorCount, mShutdownStarted)) {
+ ++mRemoteWorkerActorCount;
+ return true;
+ }
+ return false;
+}
+
+} // namespace dom
+} // namespace mozilla
+
+NS_IMPL_ISUPPORTS(ParentIdleListener, nsIObserver)
+
+NS_IMETHODIMP
+ParentIdleListener::Observe(nsISupports*, const char* aTopic,
+ const char16_t* aData) {
+ mozilla::Unused << mParent->SendNotifyIdleObserver(
+ mObserver, nsDependentCString(aTopic), nsDependentString(aData));
+ return NS_OK;
+}