fune/dom/ipc/ContentParent.cpp
Cosmin Sabou 36dc5703c6 Backed out 3 changesets (bug 1859625) for causing reftest assertion failures on gfxFeature.cpp. CLOSED TREE
Backed out changeset ea804fe0a160 (bug 1859625)
Backed out changeset 7ba7a8156187 (bug 1859625)
Backed out changeset ab1801c9add6 (bug 1859625)
2023-11-21 19:38:28 +02:00

8202 lines
278 KiB
C++

/* -*- 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"
#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/ClipboardReadRequestParent.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/LookAndFeel.h"
#include "mozilla/Maybe.h"
#include "mozilla/NullPrincipal.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/RecursiveMutex.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/ThreadSafety.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 "nsGlobalWindowOuter.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 "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 "nsIStringBundle.h"
#include "nsITimer.h"
#include "nsIURL.h"
#include "nsIWebBrowserChrome.h"
#include "nsIX509Cert.h"
#include "nsIXULRuntime.h"
#include "nsICookieNotification.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 "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 "prenv.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 "mozilla/RemoteDecodeUtils.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)
extern mozilla::LazyLogModule sPDMLog;
#define LOGPDM(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_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 IsExtensionRemoteType(const nsACString& aContentProcessType) {
return aContentProcessType == EXTENSION_REMOTE_TYPE;
}
/*static*/
uint32_t ContentParent::GetMaxProcessCount(
const nsACString& aContentProcessType) {
// Max process count is based only on the prefix.
const nsDependentCSubstring processTypePrefix =
RemoteTypePrefix(aContentProcessType);
// Check for the default remote type of "web", as it uses different prefs.
if (processTypePrefix == DEFAULT_REMOTE_TYPE) {
return GetMaxWebProcessCount();
}
// Read the pref controling this remote type. `dom.ipc.processCount` is not
// used as a fallback, as it is intended to control the number of "web"
// content processes, checked in `mozilla::GetMaxWebProcessCount()`.
nsAutoCString processCountPref("dom.ipc.processCount.");
processCountPref.Append(processTypePrefix);
int32_t maxContentParents = Preferences::GetInt(processCountPref.get(), 1);
if (maxContentParents < 1) {
maxContentParents = 1;
}
return static_cast<uint32_t>(maxContentParents);
}
/*static*/
bool ContentParent::IsMaxProcessCountReached(
const nsACString& aContentProcessType) {
return GetPoolSize(aContentProcessType) >=
GetMaxProcessCount(aContentProcessType);
}
// Really more ReleaseUnneededProcesses()
/*static*/
void ContentParent::ReleaseCachedProcesses() {
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
("ReleaseCachedProcesses:"));
if (!sBrowserContentParents) {
return;
}
#ifdef DEBUG
for (const auto& cps : *sBrowserContentParents) {
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
("%s: %zu processes", PromiseFlatCString(cps.GetKey()).get(),
cps.GetData()->Length()));
}
#endif
// First let's collect all processes and keep a grip.
AutoTArray<RefPtr<ContentParent>, 32> fixArray;
for (const auto& contentParents : sBrowserContentParents->Values()) {
for (auto* cp : *contentParents) {
fixArray.AppendElement(cp);
}
}
for (const auto& cp : fixArray) {
// Ensure the process cannot be claimed between check and MarkAsDead.
RecursiveMutexAutoLock lock(cp->ThreadsafeHandleMutex());
if (cp->ManagedPBrowserParent().Count() == 0 &&
!cp->HasActiveWorkerOrJSPlugin() &&
cp->mRemoteType == DEFAULT_REMOTE_TYPE) {
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
(" Shutdown %p (%s)", cp.get(), cp->mRemoteType.get()));
PreallocatedProcessManager::Erase(cp);
// Make sure we don't select this process for new tabs or workers.
cp->MarkAsDead();
// Start a soft shutdown.
cp->ShutDownProcess(SEND_SHUTDOWN_MESSAGE);
// Make sure that this process is no longer accessible from JS by its
// message manager.
cp->ShutDownMessageManager();
} else {
MOZ_LOG(
ContentParent::GetLog(), LogLevel::Debug,
(" Skipping %p (%s), count %d, HasActiveWorkerOrJSPlugin %d",
cp.get(), cp->mRemoteType.get(), cp->ManagedPBrowserParent().Count(),
cp->HasActiveWorkerOrJSPlugin()));
}
}
}
/*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);
{
RecursiveMutexAutoLock 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);
}
/*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);
// Print the support info only from the given location for debug purpose.
supportString.Truncate();
media::MCSInfo::GetMediaCodecsSupportedString(supportString, aSupported);
supportString.ReplaceSubstring("\n"_ns, ", "_ns);
LOGPDM("Broadcast support from '%s', support=%s",
RemoteDecodeInToStr(aLocation), supportString.get());
}
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()) {
Unused << SendActivateA11y();
}
#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();
}
RecursiveMutex& ContentParent::ThreadsafeHandleMutex() {
return mThreadsafeHandle->mMutex;
}
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());
// We need to lock our mutex here to ensure the state does not change
// between the check and the MarkAsDead.
// Note that if we come through BrowserParent::Destroy our mutex is
// already locked.
// TODO: We want to get rid of the ThreadsafeHandle, see bug 1683595.
RecursiveMutexAutoLock lock(mThreadsafeHandle->mMutex);
// 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();
if (aSendShutDown) {
AsyncSendShutDownMessage();
} else {
// aSendShutDown is false only when we get called from
// NotifyTabDestroying where we expect a subsequent call from
// NotifyTabDestroyed triggered by a Browser actor destroy
// roundtrip through the content process that might never arrive.
StartSendShutdownTimer();
}
}
void ContentParent::AsyncSendShutDownMessage() {
MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
("AsyncSendShutDownMessage %p", this));
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(sRecycledE10SProcess != this);
// 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);
// If we did not earlier, let's signal the shutdown to JS now.
SignalImpendingShutdownToContentJS();
// 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;
// We start the kill timer only after we asked our process to
// shutdown.
StartForceKillTimer();
result = true;
} else {
MaybeLogBlockShutdownDiagnostics(
this, "ShutDownProcess: !!! Send shutdown message failed! !!!",
__FILE__, __LINE__);
}
} else {
MaybeLogBlockShutdownDiagnostics(
this, "ShutDownProcess: !!! !CanSend !!!", __FILE__, __LINE__);
}
} else {
MaybeLogBlockShutdownDiagnostics(
this, "ShutDownProcess: Shutdown already pending.", __FILE__,
__LINE__);
result = true;
}
// If call was not successful, the channel must have been broken
// somehow, and we will clean up the error in ActorDestroy.
return result;
}
using mozilla::dom::quota::QuotaManagerService;
if (QuotaManagerService* qms = QuotaManagerService::GetOrCreate()) {
qms->AbortOperationsForProcess(mChildID);
}
if (aMethod == CLOSE_CHANNEL) {
if (!mCalledClose) {
MaybeLogBlockShutdownDiagnostics(
this, "ShutDownProcess: Closing channel.", __FILE__, __LINE__);
// Close() can only be called once: It kicks off the destruction sequence.
mCalledClose = true;
Close();
}
result = true;
}
// A ContentParent object might not get freed until after XPCOM shutdown has
// shut down the cycle collector. But by then it's too late to release any
// CC'ed objects, so we need to null them out here, while we still can. See
// bug 899761.
ShutDownMessageManager();
return result;
}
mozilla::ipc::IPCResult ContentParent::RecvNotifyShutdownSuccess() {
if (!mShutdownPending) {
return IPC_FAIL(this, "RecvNotifyShutdownSuccess without mShutdownPending");
}
mIsNotifiedShutdownSuccess = true;
return IPC_OK();
}
mozilla::ipc::IPCResult ContentParent::RecvFinishShutdown() {
if (!mShutdownPending) {
return IPC_FAIL(this, "RecvFinishShutdown without mShutdownPending");
}
// At this point, we already called ShutDownProcess once with
// SEND_SHUTDOWN_MESSAGE. To actually close the channel, we call
// ShutDownProcess again with CLOSE_CHANNEL.
if (mCalledClose) {
MaybeLogBlockShutdownDiagnostics(
this, "RecvFinishShutdown: Channel already closed.", __FILE__,
__LINE__);
}
ShutDownProcess(CLOSE_CHANNEL);
return IPC_OK();
}
void ContentParent::ShutDownMessageManager() {
if (!mMessageManager) {
return;
}
mMessageManager->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();
// Flag shutdown has started for us to our threadsafe handle.
{
// Depending on how we get here, the lock might or might not be set.
RecursiveMutexAutoLock lock(mThreadsafeHandle->mMutex);
mThreadsafeHandle->mShutdownStarted = true;
}
// Prevent this process from being re-used.
PreallocatedProcessManager::Erase(this);
StopRecyclingE10SOnly(false);
#if defined(MOZ_WIDGET_ANDROID) && !defined(MOZ_PROFILE_GENERATE)
if (IsAlive()) {
// We're intentionally killing the content process at this point to ensure
// that we never have a "dead" content process sitting around and occupying
// an Android Service.
//
// The exception is in MOZ_PROFILE_GENERATE builds where we must allow the
// process to shutdown cleanly so that profile data can be dumped. This is
// okay as we will not reach our process limit during the profile run.
nsCOMPtr<nsIEventTarget> launcherThread(GetIPCLauncher());
MOZ_ASSERT(launcherThread);
auto procType = java::GeckoProcessType::CONTENT();
auto selector =
java::GeckoProcessManager::Selector::New(procType, OtherPid());
launcherThread->Dispatch(NS_NewRunnableFunction(
"ContentParent::MarkAsDead",
[selector =
java::GeckoProcessManager::Selector::GlobalRef(selector)]() {
java::GeckoProcessManager::ShutdownProcess(selector);
}));
}
#endif
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
if (CanSend()) {
GetIPCChannel()->InduceConnectionError();
}
}
void ContentParent::ActorDestroy(ActorDestroyReason why) {
#ifdef FUZZING_SNAPSHOT
MOZ_FUZZING_IPC_DROP_PEER("ContentParent::ActorDestroy");
#endif
if (mSendShutdownTimer) {
mSendShutdownTimer->Cancel();
mSendShutdownTimer = nullptr;
}
if (mForceKillTimer) {
mForceKillTimer->Cancel();
mForceKillTimer = nullptr;
}
// Signal shutdown completion regardless of error state, so we can
// finish waiting in the xpcom-shutdown/profile-before-change observer.
RemoveShutdownBlockers();
if (mHangMonitorActor) {
ProcessHangMonitor::RemoveProcess(mHangMonitorActor);
mHangMonitorActor = nullptr;
}
RefPtr<FileSystemSecurity> fss = FileSystemSecurity::Get();
if (fss) {
fss->Forget(ChildID());
}
if (why == NormalShutdown && !mCalledClose) {
// If we shut down normally but haven't called Close, assume somebody
// else called Close on us. In that case, we still need to call
// ShutDownProcess below to perform other necessary clean up.
mCalledClose = true;
}
// Make sure we always clean up.
ShutDownProcess(CLOSE_CHANNEL);
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
if (obs) {
size_t length = 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.
{
// Most of the times we'll get here with the mutex acquired, but still.
RecursiveMutexAutoLock 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::StartSendShutdownTimer() {
if (mSendShutdownTimer || !CanSend()) {
return;
}
uint32_t timeoutSecs = StaticPrefs::dom_ipc_tabs_shutdownTimeoutSecs();
if (timeoutSecs > 0) {
NS_NewTimerWithFuncCallback(getter_AddRefs(mSendShutdownTimer),
ContentParent::SendShutdownTimerCallback, this,
timeoutSecs * 1000, nsITimer::TYPE_ONE_SHOT,
"dom::ContentParent::StartSendShutdownTimer");
MOZ_ASSERT(mSendShutdownTimer);
}
}
void ContentParent::StartForceKillTimer() {
if (mForceKillTimer || !CanSend()) {
return;
}
uint32_t timeoutSecs = StaticPrefs::dom_ipc_tabs_shutdownTimeoutSecs();
if (timeoutSecs > 0) {
NS_NewTimerWithFuncCallback(getter_AddRefs(mForceKillTimer),
ContentParent::ForceKillTimerCallback, this,
timeoutSecs * 1000, nsITimer::TYPE_ONE_SHOT,
"dom::ContentParent::StartForceKillTimer");
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 (mSendShutdownTimer) {
mSendShutdownTimer->Cancel();
}
if (mForceKillTimer) {
mForceKillTimer->Cancel();
}
AssertIsOnMainThread();
// Clear the weak reference from the threadsafe handle back to this actor.
mThreadsafeHandle->mWeakActor = nullptr;
if (mIsAPreallocBlocker) {
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
("Removing blocker on ContentProcess 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::GetSupportedMimeTypesPrefixed());
}
#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, &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 nsCString& aPartitionKey,
const nsACString& aURI,
bool aRevoked) {
// We send all moz-extension Blob URL's to all content processes
// because content scripts mean that a moz-extension can live in any
// process. Same thing for system principal Blob URLs. Content Blob
// URL's are sent for content principals on-demand by
// 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,
nsCString(aPartitionKey), 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, &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::RecvGetExternalClipboardFormats(
const int32_t& aWhichClipboard, const bool& aPlainTextOnly,
nsTArray<nsCString>* aTypes) {
MOZ_ASSERT(aTypes);
DataTransfer::GetExternalClipboardFormats(aWhichClipboard, aPlainTextOnly,
aTypes);
return IPC_OK();
}
namespace {
class ClipboardGetCallback final : public nsIAsyncClipboardGetCallback {
public:
ClipboardGetCallback(ContentParent* aContentParent,
ContentParent::GetClipboardAsyncResolver&& aResolver)
: mContentParent(aContentParent), mResolver(std::move(aResolver)) {}
// This object will never be held by a cycle-collected object, so it doesn't
// need to be cycle-collected despite holding alive cycle-collected objects.
NS_DECL_ISUPPORTS
// nsIAsyncClipboardGetCallback
NS_IMETHOD OnSuccess(
nsIAsyncGetClipboardData* aAsyncGetClipboardData) override {
nsTArray<nsCString> flavors;
nsresult rv = aAsyncGetClipboardData->GetFlavorList(flavors);
if (NS_FAILED(rv)) {
return OnError(rv);
}
auto requestParent = MakeNotNull<RefPtr<ClipboardReadRequestParent>>(
mContentParent, aAsyncGetClipboardData);
if (!mContentParent->SendPClipboardReadRequestConstructor(
requestParent, std::move(flavors))) {
return OnError(NS_ERROR_FAILURE);
}
mResolver(PClipboardReadRequestOrError(requestParent));
return NS_OK;
}
NS_IMETHOD OnError(nsresult aResult) override {
mResolver(aResult);
return NS_OK;
}
protected:
~ClipboardGetCallback() = default;
RefPtr<ContentParent> mContentParent;
ContentParent::GetClipboardAsyncResolver mResolver;
};
NS_IMPL_ISUPPORTS(ClipboardGetCallback, nsIAsyncClipboardGetCallback)
} // namespace
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();
}
auto callback = MakeRefPtr<ClipboardGetCallback>(this, std::move(aResolver));
rv = clipboard->AsyncGetData(aTypes, aWhichClipboard, callback);
if (NS_FAILED(rv)) {
callback->OnError(rv);
return IPC_OK();
}
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::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.
Unused << SendActivateA11y();
} 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")) {
MOZ_ASSERT(aSubject, "cookie changed notification must have subject.");
nsCOMPtr<nsICookieNotification> notification = do_QueryInterface(aSubject);
MOZ_ASSERT(notification,
"cookie changed notification must have nsICookieNotification.");
nsICookieNotification::Action action = notification->GetAction();
PNeckoParent* neckoParent = LoneManagedOrNullAsserts(ManagedPNeckoParent());
if (!neckoParent) {
return NS_OK;
}
PCookieServiceParent* csParent =
LoneManagedOrNullAsserts(neckoParent->ManagedPCookieServiceParent());
if (!csParent) {
return NS_OK;
}
auto* cs = static_cast<CookieServiceParent*>(csParent);
if (action == nsICookieNotification::COOKIES_BATCH_DELETED) {
nsCOMPtr<nsIArray> cookieList;
DebugOnly<nsresult> rv =
notification->GetBatchDeletedCookies(getter_AddRefs(cookieList));
NS_ASSERTION(NS_SUCCEEDED(rv) && cookieList, "couldn't get cookie list");
cs->RemoveBatchDeletedCookies(cookieList);
return NS_OK;
}
if (action == nsICookieNotification::ALL_COOKIES_CLEARED) {
cs->RemoveAll();
return NS_OK;
}
// Do not push these cookie updates to the same process they originated
// from.
if (cs->ProcessingCookie()) {
return NS_OK;
}
nsCOMPtr<nsICookie> xpcCookie;
DebugOnly<nsresult> rv = notification->GetCookie(getter_AddRefs(xpcCookie));
NS_ASSERTION(NS_SUCCEEDED(rv) && xpcCookie, "couldn't get cookie");
// only broadcast the cookie change to content processes that need it
const Cookie& cookie = xpcCookie->AsCookie();
// do not send cookie if content process does not have similar cookie
if (!cs->ContentProcessHasCookie(cookie)) {
return NS_OK;
}
if (action == nsICookieNotification::COOKIE_DELETED) {
cs->RemoveCookie(cookie);
} else if (action == nsICookieNotification::COOKIE_ADDED ||
action == nsICookieNotification::COOKIE_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 = aSource.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::SendShutdownTimerCallback(nsITimer* aTimer,
void* aClosure) {
auto* self = static_cast<ContentParent*>(aClosure);
self->AsyncSendShutDownMessage();
}
/* static */
void ContentParent::ForceKillTimerCallback(nsITimer* aTimer, void* aClosure) {
// We don't want to time out the content process during XPCShell tests. This
// is the easiest way to ensure that.
if (PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR")) {
return;
}
auto* self = static_cast<ContentParent*>(aClosure);
self->KillHard("ShutDownKill");
}
void ContentParent::GeneratePairedMinidump(const char* aReason) {
// We're about to kill the child process associated with this content.
// Something has gone wrong to get us here, so we generate a minidump
// of the parent and child for submission to the crash server unless we're
// already shutting down.
if (mCrashReporter &&
!AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed) &&
StaticPrefs::dom_ipc_tabs_createKillHardCrashReports_AtStartup()) {
// GeneratePairedMinidump creates two minidumps for us - the main
// one is for the content process we're about to kill, and the other
// one is for the main browser process. That second one is the extra
// minidump tagging along, so we have to tell the crash reporter that
// it exists and is being appended.
nsAutoCString additionalDumps("browser");
mCrashReporter->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;
if (mSendShutdownTimer) {
mSendShutdownTimer->Cancel();
mSendShutdownTimer = nullptr;
}
if (mForceKillTimer) {
mForceKillTimer->Cancel();
mForceKillTimer = nullptr;
}
RemoveShutdownBlockers();
nsCString reason = nsDependentCString(aReason);
// If we find mIsNotifiedShutdownSuccess there is no reason to blame this
// content process, most probably our parent process is just slow in
// processing its own main thread queue.
if (!mIsNotifiedShutdownSuccess) {
GeneratePairedMinidump(aReason);
} else {
reason = nsDependentCString("KillHard after IsNotifiedShutdownSuccess.");
}
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.");
if (CanSend()) {
GetIPCChannel()->InduceConnectionError();
}
return;
}
if (!KillProcess(otherProcessHandle, base::PROCESS_END_KILLED_BY_USER)) {
if (mCrashReporter) {
mCrashReporter->DeleteCrashReport();
}
NS_WARNING("failed to kill subprocess!");
}
if (mSubprocess) {
MOZ_LOG(
ContentParent::GetLog(), LogLevel::Verbose,
("KillHard Subprocess(%s): ContentParent %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 induce a connection
// error in the IPC channel to immediately stop all IPC communication on this
// channel.
if (CanSend()) {
GetIPCChannel()->InduceConnectionError();
}
// EnsureProcessTerminated has responsibilty for closing otherProcessHandle.
XRE_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();
}
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 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 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 LoadInfoArgs& loadInfoArgs, const nsAString& entryName,
const nsAString& initiatorType, UniquePtr<PerformanceTimingData>&& aData) {
if (!aData) {
return IPC_FAIL(this, "aData should not be null");
}
RefPtr<WindowGlobalParent> parent =
WindowGlobalParent::GetByInnerWindowId(loadInfoArgs.innerWindowID());
if (!parent || !parent->GetContentParent()) {
return IPC_OK();
}
MOZ_ASSERT(parent->GetContentParent() != this,
"No need to bounce around if in the same process");
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) {
if (!TextRecognition::IsSupported() ||
!Preferences::GetBool("dom.text-recognition.enabled")) {
return IPC_FAIL(this, "Text recognition not available.");
}
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::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();
}
if (nsGlobalWindowOuter::Cast(outerWin)->IsChromeWindow()) {
browserDOMWin =
nsGlobalWindowOuter::Cast(outerWin)->GetBrowserDOMWindow();
}
}
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 nsCString& aPartitionKey,
ContentParent* aIgnoreThisCP) {
uint64_t originHash = ComputeLoadedOriginHash(aPrincipal);
bool toBeSent =
BlobURLProtocolHandler::IsBlobURLBroadcastPrincipal(aPrincipal);
nsCString uri(aURI);
for (auto* cp : AllProcesses(eLive)) {
if (cp != aIgnoreThisCP) {
if (!toBeSent && !cp->mLoadedOriginHashes.Contains(originHash)) {
continue;
}
nsresult rv = cp->TransmitPermissionsForPrincipal(aPrincipal);
if (NS_WARN_IF(NS_FAILED(rv))) {
break;
}
IPCBlob ipcBlob;
rv = IPCBlobUtils::Serialize(aBlobImpl, ipcBlob);
if (NS_WARN_IF(NS_FAILED(rv))) {
break;
}
Unused << cp->SendBlobURLRegistration(uri, ipcBlob, aPrincipal,
aPartitionKey);
}
}
}
/* 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 nsCString& aPartitionKey) {
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, aPartitionKey,
blobImpl);
BroadcastBlobURLRegistration(aURI, blobImpl, aPrincipal, aPartitionKey, 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) {
if (!mHangMonitorActor) {
return;
}
ProcessHangMonitor::PaintWhileInterruptingJS(mHangMonitorActor,
aBrowserParent);
}
void ContentParent::UnloadLayersWhileInterruptingJS(
BrowserParent* aBrowserParent) {
if (!mHangMonitorActor) {
return;
}
ProcessHangMonitor::UnloadLayersWhileInterruptingJS(mHangMonitorActor,
aBrowserParent);
}
void ContentParent::CancelContentJSExecutionIfRunning(
BrowserParent* aBrowserParent, nsIRemoteTab::NavigationType aNavigationType,
const CancelContentJSOptions& aCancelContentJSOptions) {
if (!mHangMonitorActor) {
return;
}
ProcessHangMonitor::CancelContentJSExecutionIfRunning(
mHangMonitorActor, aBrowserParent, aNavigationType,
aCancelContentJSOptions);
}
void ContentParent::SetMainThreadQoSPriority(
nsIThread::QoSPriority aQoSPriority) {
if (!mHangMonitorActor) {
return;
}
ProcessHangMonitor::SetMainThreadQoSPriority(mHangMonitorActor, aQoSPriority);
}
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);
}
// We need to add the Site to the secondary keys of interest here.
// This allows site-scoped permission updates to propogate when the
// port is non-standard.
nsAutoCString siteKey;
nsresult rv =
PermissionManager::GetKeyForPrincipal(aPrincipal, false, true, siteKey);
if (NS_SUCCEEDED(rv) && !siteKey.IsEmpty()) {
mActiveSecondaryPermissionKeys.EnsureInserted(siteKey);
}
return NS_OK;
}
void ContentParent::TransmitBlobURLsForPrincipal(nsIPrincipal* aPrincipal) {
// If we're already broadcasting BlobURLs with this principal, we don't need
// to send them here.
if (BlobURLProtocolHandler::IsBlobURLBroadcastPrincipal(aPrincipal)) {
return;
}
// We shouldn't have any Blob URLs with expanded principals, so transmit URLs
// for each principal in the AllowList instead.
if (nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aPrincipal)) {
for (const auto& prin : ep->AllowList()) {
TransmitBlobURLsForPrincipal(prin);
}
return;
}
uint64_t originHash = ComputeLoadedOriginHash(aPrincipal);
if (!mLoadedOriginHashes.Contains(originHash)) {
mLoadedOriginHashes.AppendElement(originHash);
nsTArray<BlobURLRegistrationData> registrations;
BlobURLProtocolHandler::ForEachBlobURL(
[&](BlobImpl* aBlobImpl, nsIPrincipal* aBlobPrincipal,
const nsCString& aPartitionKey, const nsACString& aURI,
bool aRevoked) {
// This check uses `ComputeLoadedOriginHash` to compare, rather than
// doing the more accurate `Equals` check, as it needs to match the
// behaviour of the logic to broadcast new registrations.
if (originHash != ComputeLoadedOriginHash(aBlobPrincipal)) {
return true;
}
IPCBlob ipcBlob;
nsresult rv = IPCBlobUtils::Serialize(aBlobImpl, ipcBlob);
if (NS_WARN_IF(NS_FAILED(rv))) {
return false;
}
registrations.AppendElement(
BlobURLRegistrationData(nsCString(aURI), ipcBlob, aPrincipal,
nsCString(aPartitionKey), 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);
}
bool ContentParent::NeedsSecondaryKeyPermissionsUpdate(
const nsACString& aPermissionKey) const {
return mActiveSecondaryPermissionKeys.Contains(aPermissionKey);
}
mozilla::ipc::IPCResult ContentParent::RecvAccumulateChildHistograms(
nsTArray<HistogramAccumulation>&& aAccumulations) {
TelemetryIPC::AccumulateChildHistograms(GetTelemetryProcessID(mRemoteType),
aAccumulations);
return IPC_OK();
}
mozilla::ipc::IPCResult ContentParent::RecvAccumulateChildKeyedHistograms(
nsTArray<KeyedHistogramAccumulation>&& aAccumulations) {
TelemetryIPC::AccumulateChildKeyedHistograms(
GetTelemetryProcessID(mRemoteType), aAccumulations);
return IPC_OK();
}
mozilla::ipc::IPCResult ContentParent::RecvUpdateChildScalars(
nsTArray<ScalarAction>&& aScalarActions) {
TelemetryIPC::UpdateChildScalars(GetTelemetryProcessID(mRemoteType),
aScalarActions);
return IPC_OK();
}
mozilla::ipc::IPCResult ContentParent::RecvUpdateChildKeyedScalars(
nsTArray<KeyedScalarAction>&& aScalarActions) {
TelemetryIPC::UpdateChildKeyedScalars(GetTelemetryProcessID(mRemoteType),
aScalarActions);
return IPC_OK();
}
mozilla::ipc::IPCResult ContentParent::RecvRecordChildEvents(
nsTArray<mozilla::Telemetry::ChildEventData>&& aEvents) {
TelemetryIPC::RecordChildEvents(GetTelemetryProcessID(mRemoteType), aEvents);
return IPC_OK();
}
mozilla::ipc::IPCResult ContentParent::RecvRecordDiscardedData(
const mozilla::Telemetry::DiscardedData& aDiscardedData) {
TelemetryIPC::RecordDiscardedData(GetTelemetryProcessID(mRemoteType),
aDiscardedData);
return IPC_OK();
}
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;
}
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,
const bool& aFrameOnly,
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, aFrameOnly)
->Then(GetCurrentSerialEventTarget(), __func__,
[aResolver = std::move(aResolver)](
StorageAccessAPIHelper::ParentAccessGrantPromise::
ResolveOrRejectValue&& aValue) {
bool success =
aValue.IsResolve() && NS_SUCCEEDED(aValue.ResolveValue());
aResolver(success);
});
return IPC_OK();
}
mozilla::ipc::IPCResult ContentParent::RecvCompleteAllowAccessFor(
const MaybeDiscarded<BrowsingContext>& aParentContext,
uint64_t aTopLevelWindowId, nsIPrincipal* aTrackingPrincipal,
const nsACString& aTrackingOrigin, uint32_t aCookieBehavior,
const ContentBlockingNotifier::StorageAccessPermissionGrantedReason&
aReason,
CompleteAllowAccessForResolver&& aResolver) {
if (aParentContext.IsNullOrDiscarded()) {
return IPC_OK();
}
StorageAccessAPIHelper::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());
{
RecursiveMutexAutoLock 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 nsCString& aPartitionKey,
BlobURLDataRequestResolver&& aResolver) {
RefPtr<BlobImpl> blobImpl;
// Since revoked blobs are also retrieved, it is possible that the blob no
// longer exists (due to the 5 second timeout) when execution reaches here
if (!BlobURLProtocolHandler::GetDataEntry(
aBlobURL, getter_AddRefs(blobImpl), aLoadingPrincipal,
aTriggeringPrincipal, aOriginAttributes, aInnerWindowId,
aPartitionKey, true /* AlsoIfRevoked */)) {
aResolver(NS_ERROR_DOM_BAD_URI);
return IPC_OK();
}
IPCBlob ipcBlob;
nsresult rv = IPCBlobUtils::Serialize(blobImpl, ipcBlob);
if (NS_WARN_IF(NS_FAILED(rv))) {
aResolver(rv);
return IPC_OK();
}
aResolver(ipcBlob);
return IPC_OK();
}
mozilla::ipc::IPCResult ContentParent::RecvReportServiceWorkerShutdownProgress(
uint32_t aShutdownStateId, ServiceWorkerShutdownState::Progress aProgress) {
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
MOZ_RELEASE_ASSERT(swm, "ServiceWorkers should shutdown before SWM.");
swm->ReportServiceWorkerShutdownProgress(aShutdownStateId, aProgress);
return IPC_OK();
}
mozilla::ipc::IPCResult ContentParent::RecvNotifyOnHistoryReload(
const MaybeDiscarded<BrowsingContext>& aContext, const bool& aForceReload,
NotifyOnHistoryReloadResolver&& aResolver) {
bool canReload = false;
Maybe<NotNull<RefPtr<nsDocShellLoadState>>> loadState;
Maybe<bool> reloadActiveEntry;
if (!aContext.IsNullOrDiscarded()) {
aContext.get_canonical()->NotifyOnHistoryReload(
aForceReload, canReload, loadState, reloadActiveEntry);
}
aResolver(
std::tuple<const bool&,
const Maybe<NotNull<RefPtr<nsDocShellLoadState>>>&,
const Maybe<bool>&>(canReload, loadState, reloadActiveEntry));
return IPC_OK();
}
mozilla::ipc::IPCResult ContentParent::RecvHistoryCommit(
const MaybeDiscarded<BrowsingContext>& aContext, const uint64_t& aLoadID,
const nsID& aChangeID, const uint32_t& aLoadType, const bool& 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.IsNullOrDiscarded()) {
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.IsNullOrDiscarded()) {
aContext.get_canonical()->SetActiveSessionHistoryEntry(
aPreviousScrollPos, &aInfo, aLoadType, aUpdatedCacheKey, aChangeID);
}
return IPC_OK();
}
mozilla::ipc::IPCResult ContentParent::RecvReplaceActiveSessionHistoryEntry(
const MaybeDiscarded<BrowsingContext>& aContext,
SessionHistoryInfo&& aInfo) {
if (!aContext.IsNullOrDiscarded()) {
aContext.get_canonical()->ReplaceActiveSessionHistoryEntry(&aInfo);
}
return IPC_OK();
}
mozilla::ipc::IPCResult
ContentParent::RecvRemoveDynEntriesFromActiveSessionHistoryEntry(
const MaybeDiscarded<BrowsingContext>& aContext) {
if (!aContext.IsNullOrDiscarded()) {
aContext.get_canonical()->RemoveDynEntriesFromActiveSessionHistoryEntry();
}
return IPC_OK();
}
mozilla::ipc::IPCResult ContentParent::RecvRemoveFromSessionHistory(
const MaybeDiscarded<BrowsingContext>& aContext, const nsID& aChangeID) {
if (!aContext.IsNullOrDiscarded()) {
aContext.get_canonical()->RemoveFromSessionHistory(aChangeID);
}
return IPC_OK();
}
mozilla::ipc::IPCResult ContentParent::RecvHistoryReload(
const MaybeDiscarded<BrowsingContext>& aContext,
const uint32_t aReloadFlags) {
if (!aContext.IsNullOrDiscarded()) {
nsCOMPtr<nsISHistory> shistory =
aContext.get_canonical()->GetSessionHistory();
if (shistory) {
shistory->Reload(aReloadFlags);
}
}
return IPC_OK();
}
mozilla::ipc::IPCResult ContentParent::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() {
RecursiveMutexAutoLock lock(mMutex);
return mRemoteType;
}
bool ThreadsafeContentParentHandle::MaybeRegisterRemoteWorkerActor(
MoveOnlyFunction<bool(uint32_t, bool)> aCallback) {
RecursiveMutexAutoLock lock(mMutex);
if (aCallback(mRemoteWorkerActorCount, mShutdownStarted)) {
// TODO: I'd wish we could assert here that our ContentParent is alive.
++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;
}
#undef LOGPDM