This allows us to shut down the IAudioSessionManager after Cubeb has stopped, so there shouldn't be STA apartment deadlock issues with it. IAudioSessionManager's shutdown always requires the STA. This patch eliminates MTA threads involvement at shutdown, which was previously needed for thread safety. We keep other operations on MTA (background) threads, for performance. This also removes a lot of unused functionality. AudioSession hasn't been needed outside of the parent process since audio remoting landed. Differential Revision: https://phabricator.services.mozilla.com/D251773
8040 lines
276 KiB
C++
8040 lines
276 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/. */
|
|
|
|
#include "mozilla/AppShutdown.h"
|
|
#include "mozilla/DebugOnly.h"
|
|
|
|
#include "base/basictypes.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/Services.h"
|
|
#include "mozilla/XREAppData.h"
|
|
#include "nsComponentManagerUtils.h"
|
|
#include "nsIBrowserDOMWindow.h"
|
|
#include "nsIPrivateAttributionService.h"
|
|
|
|
#include "GMPServiceParent.h"
|
|
#include "HandlerServiceParent.h"
|
|
#include "IHistory.h"
|
|
#include <cstdint>
|
|
#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/ClipboardContentAnalysisParent.h"
|
|
#include "mozilla/BasePrincipal.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/RDDProcessManager.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_threads.h"
|
|
#include "mozilla/StaticPrefs_widget.h"
|
|
#include "mozilla/StorageAccessAPIHelper.h"
|
|
#include "mozilla/StyleSheet.h"
|
|
#include "mozilla/StyleSheetInlines.h"
|
|
#include "mozilla/TaskController.h"
|
|
#include "mozilla/glean/DomMetrics.h"
|
|
#include "mozilla/glean/IpcMetrics.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/GeolocationSystem.h"
|
|
#include "mozilla/dom/GetFilesHelper.h"
|
|
#include "mozilla/dom/IPCBlobUtils.h"
|
|
#include "mozilla/dom/JSActorService.h"
|
|
#include "mozilla/dom/JSProcessActorBinding.h"
|
|
#include "mozilla/dom/LocalStorageCommon.h"
|
|
#include "mozilla/dom/MediaController.h"
|
|
#include "mozilla/dom/MemoryReportRequest.h"
|
|
#include "mozilla/dom/MediaStatusManager.h"
|
|
#include "mozilla/dom/notification/NotificationUtils.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/RemoteWorkerDebuggerManagerParent.h"
|
|
#include "mozilla/dom/RemoteWorkerServiceParent.h"
|
|
#include "mozilla/dom/ServiceWorkerManager.h"
|
|
#include "mozilla/dom/ServiceWorkerRegistrar.h"
|
|
#include "mozilla/dom/ServiceWorkerUtils.h"
|
|
#include "mozilla/dom/SessionHistoryEntry.h"
|
|
#include "mozilla/dom/SessionStorageManager.h"
|
|
#include "mozilla/dom/StorageIPC.h"
|
|
#include "mozilla/dom/UserActivation.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/SharedMemoryHandle.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/UrlClassifierFeatureFactory.h"
|
|
#include "mozilla/net/TRRService.h"
|
|
#include "mozilla/TelemetryComms.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 "nsIAppShell.h"
|
|
#include "nsIAppWindow.h"
|
|
#include "nsIAsyncInputStream.h"
|
|
#include "nsIBidiKeyboard.h"
|
|
#include "nsICaptivePortalService.h"
|
|
#include "nsICertOverrideService.h"
|
|
#include "nsIClipboard.h"
|
|
#include "nsIContentAnalysis.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 "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 "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/WinDllServices.h"
|
|
#endif
|
|
|
|
#ifdef MOZ_CODE_COVERAGE
|
|
# include "mozilla/CodeCoverageHandler.h"
|
|
#endif
|
|
|
|
#ifdef FUZZING_SNAPSHOT
|
|
# include "mozilla/fuzzing/IPCFuzzController.h"
|
|
#endif
|
|
|
|
#ifdef ENABLE_WEBDRIVER
|
|
# include "nsIMarionette.h"
|
|
# include "nsIRemoteAgent.h"
|
|
#endif
|
|
|
|
#include "mozilla/RemoteDecodeUtils.h"
|
|
#include "nsIToolkitProfileService.h"
|
|
#include "nsIToolkitProfile.h"
|
|
|
|
#ifdef MOZ_WMF_CDM
|
|
# include "mozilla/EMEUtils.h"
|
|
# include "nsIWindowsMediaFoundationCDMOriginsListService.h"
|
|
|
|
namespace mozilla {
|
|
class OriginsListLoadCallback final : public nsIOriginsListLoadCallback {
|
|
public:
|
|
explicit OriginsListLoadCallback(ContentParent* aContentParent)
|
|
: mContentParent(aContentParent) {
|
|
MOZ_ASSERT(mContentParent);
|
|
}
|
|
|
|
NS_DECL_ISUPPORTS
|
|
|
|
// nsIOriginsListLoadCallback
|
|
NS_IMETHODIMP OnOriginsListLoaded(nsIArray* aEntries) {
|
|
if (NS_WARN_IF(!mContentParent)) {
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
uint32_t length = 0;
|
|
nsresult rv = aEntries->GetLength(&length);
|
|
if (NS_FAILED(rv)) {
|
|
return rv;
|
|
}
|
|
|
|
nsTArray<dom::IPCOriginStatusEntry> ipcEntries;
|
|
for (uint32_t i = 0; i < length; ++i) {
|
|
nsCOMPtr<nsIOriginStatusEntry> entry;
|
|
aEntries->QueryElementAt(i, NS_GET_IID(nsIOriginStatusEntry),
|
|
getter_AddRefs(entry));
|
|
if (!entry) {
|
|
NS_WARNING("OriginsListLoadCallback, skip bad entry?");
|
|
continue;
|
|
}
|
|
nsAutoCString origin;
|
|
int32_t status = 0;
|
|
entry->GetOrigin(origin);
|
|
entry->GetStatus(&status);
|
|
dom::IPCOriginStatusEntry ipcEntry(origin, status);
|
|
ipcEntries.AppendElement(ipcEntry);
|
|
}
|
|
Unused << mContentParent->SendUpdateMFCDMOriginEntries(ipcEntries);
|
|
return NS_OK;
|
|
}
|
|
|
|
private:
|
|
~OriginsListLoadCallback() = default;
|
|
|
|
RefPtr<ContentParent> mContentParent;
|
|
};
|
|
NS_IMPL_ISUPPORTS(OriginsListLoadCallback, nsIOriginsListLoadCallback)
|
|
} // namespace mozilla
|
|
#endif
|
|
|
|
static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
|
|
|
|
using base::KillProcess;
|
|
|
|
using namespace CrashReporter;
|
|
using namespace mozilla::dom::power;
|
|
using namespace mozilla::media;
|
|
using namespace mozilla::embedding;
|
|
using namespace mozilla::gfx;
|
|
using namespace mozilla::gmp;
|
|
using namespace mozilla::hal;
|
|
using namespace mozilla::ipc;
|
|
using namespace mozilla::intl;
|
|
using namespace mozilla::layers;
|
|
using namespace mozilla::layout;
|
|
using namespace mozilla::net;
|
|
using namespace mozilla::psm;
|
|
using namespace mozilla::widget;
|
|
using namespace mozilla::Telemetry;
|
|
using mozilla::loader::PScriptCacheParent;
|
|
using mozilla::Telemetry::ProcessID;
|
|
|
|
extern mozilla::LazyLogModule gFocusLog;
|
|
|
|
#define LOGFOCUS(args) MOZ_LOG(gFocusLog, mozilla::LogLevel::Debug, args)
|
|
|
|
extern mozilla::LazyLogModule sPDMLog;
|
|
#define LOGPDM(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
|
|
|
|
namespace mozilla {
|
|
namespace CubebUtils {
|
|
extern FileDescriptor CreateAudioIPCConnection();
|
|
}
|
|
|
|
namespace dom {
|
|
|
|
LazyLogModule gProcessLog("Process");
|
|
|
|
MOZ_RUNINIT 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;
|
|
}
|
|
|
|
ProcessID GetTelemetryProcessID(const nsACString& remoteType) {
|
|
// OOP WebExtensions run in a content process.
|
|
// For Telemetry though we want to break out collected data from the
|
|
// WebExtensions process into a separate bucket, to make sure we can analyze
|
|
// it separately and avoid skewing normal content process metrics.
|
|
return remoteType == EXTENSION_REMOTE_TYPE ? ProcessID::Extension
|
|
: ProcessID::Content;
|
|
}
|
|
|
|
} // anonymous namespace
|
|
|
|
StaticAutoPtr<LinkedList<ContentParent>> ContentParent::sContentParents;
|
|
#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
|
|
StaticAutoPtr<SandboxBrokerPolicyFactory>
|
|
ContentParent::sSandboxBrokerPolicyFactory;
|
|
#endif
|
|
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
|
|
StaticAutoPtr<std::vector<std::string>> ContentParent::sMacSandboxParams;
|
|
#endif
|
|
|
|
// Set to true when the first content process gets created.
|
|
static bool sCreatedFirstContentProcess = false;
|
|
|
|
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
|
// True when we're running the process selection code, and do not expect to
|
|
// enter code paths where processes may die.
|
|
static bool sInProcessSelector = false;
|
|
#endif
|
|
|
|
static const char* sObserverTopics[] = {
|
|
NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC,
|
|
NS_IPC_IOSERVICE_SET_CONNECTIVITY_TOPIC,
|
|
NS_IPC_CAPTIVE_PORTAL_SET_STATE,
|
|
"application-background",
|
|
"application-foreground",
|
|
"memory-pressure",
|
|
"child-gc-request",
|
|
"child-cc-request",
|
|
"child-mmu-request",
|
|
"child-ghost-request",
|
|
"last-pb-context-exited",
|
|
"file-watcher-update",
|
|
#ifdef ACCESSIBILITY
|
|
"a11y-init-or-shutdown",
|
|
#endif
|
|
"cacheservice:empty-cache",
|
|
"intl:app-locales-changed",
|
|
"intl:requested-locales-changed",
|
|
"cookie-changed",
|
|
"private-cookie-changed",
|
|
NS_NETWORK_LINK_TYPE_TOPIC,
|
|
NS_NETWORK_TRR_MODE_CHANGED_TOPIC,
|
|
"network:socket-process-crashed",
|
|
DEFAULT_TIMEZONE_CHANGED_OBSERVER_TOPIC,
|
|
};
|
|
|
|
void ContentParent_NotifyUpdatedDictionaries() {
|
|
ContentParent::NotifyUpdatedDictionaries();
|
|
}
|
|
|
|
// PreallocateProcess is called by the PreallocatedProcessManager.
|
|
// ContentParent then takes this process back within GetNewOrUsedBrowserProcess.
|
|
/*static*/ UniqueContentParentKeepAlive ContentParent::MakePreallocProcess() {
|
|
RefPtr<ContentParent> process = new ContentParent(PREALLOC_REMOTE_TYPE);
|
|
if (NS_WARN_IF(!process->BeginSubprocessLaunch(PROCESS_PRIORITY_PREALLOC))) {
|
|
process->LaunchSubprocessReject();
|
|
return nullptr;
|
|
}
|
|
return process->AddKeepAlive(/* aBrowserId */ 0);
|
|
}
|
|
|
|
/*static*/
|
|
void ContentParent::StartUp() {
|
|
// FIXME Bug 1023701 - Stop using ContentParent static methods in
|
|
// child process
|
|
if (!XRE_IsParentProcess()) {
|
|
return;
|
|
}
|
|
|
|
// From this point on, NS_WARNING, NS_ASSERTION, etc. should print out the
|
|
// PID along with the warning.
|
|
nsDebugImpl::SetMultiprocessMode("Parent");
|
|
|
|
// Note: This reporter measures all ContentParents.
|
|
RegisterStrongMemoryReporter(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);
|
|
}
|
|
|
|
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) {
|
|
cp->MaybeBeginShutDown(/* aImmediate */ true,
|
|
/* aIgnoreKeepAlivePref */ true);
|
|
if (cp->IsDead()) {
|
|
// Make sure that this process is no longer accessible from JS by its
|
|
// message manager.
|
|
cp->ShutDownMessageManager();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*static*/
|
|
already_AddRefed<ContentParent> ContentParent::MinTabSelect(
|
|
const nsTArray<ContentParent*>& aContentParents, int32_t aMaxContentParents,
|
|
uint64_t aBrowserId) {
|
|
uint32_t maxSelectable =
|
|
std::min(static_cast<uint32_t>(aContentParents.Length()),
|
|
static_cast<uint32_t>(aMaxContentParents));
|
|
uint32_t min = INT_MAX;
|
|
RefPtr<ContentParent> candidate;
|
|
|
|
for (uint32_t i = 0; i < maxSelectable; i++) {
|
|
ContentParent* p = aContentParents[i];
|
|
MOZ_DIAGNOSTIC_ASSERT(!p->IsDead());
|
|
if (p->IsShuttingDown()) {
|
|
continue;
|
|
}
|
|
|
|
// Check how many other tabs are already hosted by this process. Ignore
|
|
// keepalives without a BrowserId as well as keepalives corresponding to
|
|
// `aBrowserId` when doing this calculation.
|
|
ThreadsafeContentParentHandle* handle = p->ThreadsafeHandle();
|
|
RecursiveMutexAutoLock lock(handle->mMutex);
|
|
uint32_t keepAliveCount = handle->mKeepAlivesPerBrowserId.Count();
|
|
if (handle->mKeepAlivesPerBrowserId.Contains(0)) {
|
|
--keepAliveCount;
|
|
}
|
|
if (aBrowserId != 0 &&
|
|
handle->mKeepAlivesPerBrowserId.Contains(aBrowserId)) {
|
|
--keepAliveCount;
|
|
}
|
|
|
|
if (keepAliveCount < min) {
|
|
candidate = p;
|
|
min = keepAliveCount;
|
|
}
|
|
}
|
|
|
|
// If all current processes have at least one tab and we have not yet reached
|
|
// the maximum, use a new process.
|
|
if (min > 0 &&
|
|
aContentParents.Length() < static_cast<uint32_t>(aMaxContentParents)) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Otherwise we return candidate.
|
|
return candidate.forget();
|
|
}
|
|
|
|
/* static */
|
|
already_AddRefed<nsIPrincipal>
|
|
ContentParent::CreateRemoteTypeIsolationPrincipal(
|
|
const nsACString& aRemoteType) {
|
|
if ((RemoteTypePrefix(aRemoteType) != FISSION_WEB_REMOTE_TYPE) &&
|
|
!StringBeginsWith(aRemoteType, WITH_COOP_COEP_REMOTE_TYPE_PREFIX)) {
|
|
return nullptr;
|
|
}
|
|
|
|
int32_t offset = aRemoteType.FindChar('=') + 1;
|
|
MOZ_ASSERT(offset > 1, "can not extract origin from that remote type");
|
|
nsAutoCString origin(
|
|
Substring(aRemoteType, offset, aRemoteType.Length() - offset));
|
|
|
|
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
|
|
nsCOMPtr<nsIPrincipal> principal;
|
|
ssm->CreateContentPrincipalFromOrigin(origin, getter_AddRefs(principal));
|
|
return principal.forget();
|
|
}
|
|
|
|
/*static*/
|
|
UniqueContentParentKeepAlive ContentParent::GetUsedBrowserProcess(
|
|
const nsACString& aRemoteType, nsTArray<ContentParent*>& aContentParents,
|
|
uint32_t aMaxContentParents, bool aPreferUsed, ProcessPriority aPriority,
|
|
uint64_t aBrowserId) {
|
|
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
|
AutoRestore ar(sInProcessSelector);
|
|
sInProcessSelector = true;
|
|
#endif
|
|
|
|
uint32_t numberOfParents = aContentParents.Length();
|
|
if (aPreferUsed && numberOfParents) {
|
|
// If we prefer re-using existing content processes, we don't want to create
|
|
// a new process, and instead re-use an existing one, so pretend the process
|
|
// limit is at the current number of processes.
|
|
aMaxContentParents = numberOfParents;
|
|
}
|
|
|
|
// Use MinTabSelect to choose a content process unless content process re-use
|
|
// has been disabled.
|
|
RefPtr<ContentParent> selected;
|
|
if (!StaticPrefs::dom_ipc_disableContentProcessReuse() &&
|
|
(selected =
|
|
MinTabSelect(aContentParents, aMaxContentParents, aBrowserId))) {
|
|
if (profiler_thread_is_being_profiled_for_markers()) {
|
|
nsPrintfCString marker("Reused process %u",
|
|
(unsigned int)selected->ChildID());
|
|
PROFILER_MARKER_TEXT("Process", DOM, {}, marker);
|
|
}
|
|
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
|
|
("GetUsedProcess: Reused process id=%p childID=%" PRIu64 " for %s",
|
|
selected.get(), (uint64_t)selected->ChildID(),
|
|
PromiseFlatCString(aRemoteType).get()));
|
|
selected->AssertAlive();
|
|
return selected->AddKeepAlive(aBrowserId);
|
|
}
|
|
|
|
// Try to take a preallocated process except for certain remote types.
|
|
// Note: this process may not have finished launching yet
|
|
UniqueContentParentKeepAlive preallocated;
|
|
if (aRemoteType != FILE_REMOTE_TYPE &&
|
|
aRemoteType != PRIVILEGEDABOUT_REMOTE_TYPE &&
|
|
aRemoteType != EXTENSION_REMOTE_TYPE && // Bug 1638119
|
|
(preallocated = PreallocatedProcessManager::Take(aRemoteType))) {
|
|
MOZ_DIAGNOSTIC_ASSERT(preallocated->GetRemoteType() ==
|
|
PREALLOC_REMOTE_TYPE);
|
|
preallocated->AssertAlive();
|
|
|
|
if (profiler_thread_is_being_profiled_for_markers()) {
|
|
nsPrintfCString marker(
|
|
"Assigned preallocated process %u%s",
|
|
(unsigned int)preallocated->ChildID(),
|
|
preallocated->IsLaunching() ? " (still launching)" : "");
|
|
PROFILER_MARKER_TEXT("Process", DOM, {}, marker);
|
|
}
|
|
MOZ_LOG(
|
|
ContentParent::GetLog(), LogLevel::Debug,
|
|
("Adopted preallocated process id=%p childID=%" PRIu64 " for type %s%s",
|
|
preallocated.get(), (uint64_t)preallocated->ChildID(),
|
|
PromiseFlatCString(aRemoteType).get(),
|
|
preallocated->IsLaunching() ? " (still launching)" : ""));
|
|
|
|
// This ensures that the preallocator won't shut down the process once
|
|
// it finishes starting
|
|
preallocated->mRemoteType.Assign(aRemoteType);
|
|
{
|
|
RecursiveMutexAutoLock lock(preallocated->mThreadsafeHandle->mMutex);
|
|
preallocated->mThreadsafeHandle->mRemoteType = preallocated->mRemoteType;
|
|
}
|
|
preallocated->mRemoteTypeIsolationPrincipal =
|
|
CreateRemoteTypeIsolationPrincipal(aRemoteType);
|
|
preallocated->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);
|
|
|
|
preallocated->StartRemoteWorkerService();
|
|
|
|
nsCOMPtr<nsIObserverService> obs =
|
|
mozilla::services::GetObserverService();
|
|
if (obs) {
|
|
nsAutoString cpId;
|
|
cpId.AppendInt(static_cast<uint64_t>(preallocated->ChildID()));
|
|
obs->NotifyObservers(static_cast<nsIObserver*>(preallocated.get()),
|
|
"process-type-set", cpId.get());
|
|
preallocated->AssertAlive();
|
|
}
|
|
}
|
|
// NOTE: Make sure to return a keepalive for the requested aBrowserId. The
|
|
// keepalive used by the preallocated process manager will be released upon
|
|
// returning.
|
|
return preallocated->AddKeepAlive(aBrowserId);
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
/*static*/
|
|
UniqueContentParentKeepAlive ContentParent::GetNewOrUsedLaunchingBrowserProcess(
|
|
const nsACString& aRemoteType, BrowsingContextGroup* aGroup,
|
|
ProcessPriority aPriority, bool aPreferUsed, uint64_t aBrowserId) {
|
|
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
|
|
("GetNewOrUsedProcess for type %s",
|
|
PromiseFlatCString(aRemoteType).get()));
|
|
|
|
if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
|
|
return nullptr;
|
|
}
|
|
|
|
// If we have an existing host process attached to this BrowsingContextGroup,
|
|
// always return it, as we can never have multiple host processes within a
|
|
// single BrowsingContextGroup.
|
|
UniqueContentParentKeepAlive contentParent;
|
|
if (aGroup) {
|
|
if (RefPtr<ContentParent> candidate = aGroup->GetHostProcess(aRemoteType)) {
|
|
MOZ_DIAGNOSTIC_ASSERT(!candidate->IsShuttingDown());
|
|
MOZ_LOG(
|
|
ContentParent::GetLog(), LogLevel::Debug,
|
|
("GetNewOrUsedProcess: Existing host process id=%p childID=%" PRIu64
|
|
" (launching %d)",
|
|
candidate.get(), (uint64_t)candidate->ChildID(),
|
|
candidate->IsLaunching()));
|
|
contentParent = candidate->TryAddKeepAlive(aBrowserId);
|
|
}
|
|
}
|
|
|
|
nsTArray<ContentParent*>& contentParents = GetOrCreatePool(aRemoteType);
|
|
|
|
if (!contentParent) {
|
|
// No host process. Let's try to re-use an existing process.
|
|
uint32_t maxContentParents = GetMaxProcessCount(aRemoteType);
|
|
|
|
contentParent =
|
|
GetUsedBrowserProcess(aRemoteType, contentParents, maxContentParents,
|
|
aPreferUsed, aPriority, aBrowserId);
|
|
MOZ_DIAGNOSTIC_ASSERT_IF(contentParent, !contentParent->IsShuttingDown());
|
|
}
|
|
|
|
if (!contentParent) {
|
|
// No reusable process. Let's create and launch one.
|
|
// The life cycle will be set to `LifecycleState::LAUNCHING`.
|
|
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
|
|
("Launching new process immediately for type %s",
|
|
PromiseFlatCString(aRemoteType).get()));
|
|
|
|
RefPtr<ContentParent> newCp = new ContentParent(aRemoteType);
|
|
if (NS_WARN_IF(!newCp->BeginSubprocessLaunch(aPriority))) {
|
|
// Launch aborted because of shutdown. Bailout.
|
|
newCp->LaunchSubprocessReject();
|
|
return nullptr;
|
|
}
|
|
contentParent = newCp->AddKeepAlive(aBrowserId);
|
|
|
|
// Until the new process is ready let's not allow to start up any
|
|
// preallocated processes. The blocker will be removed once we receive
|
|
// the first idle message.
|
|
contentParent->mIsAPreallocBlocker = true;
|
|
PreallocatedProcessManager::AddBlocker(aRemoteType, contentParent.get());
|
|
|
|
// Store this process for future reuse.
|
|
contentParent->AddToPool(contentParents);
|
|
|
|
MOZ_LOG(
|
|
ContentParent::GetLog(), LogLevel::Debug,
|
|
("GetNewOrUsedProcess: new immediate process id=%p childID=%" PRIu64,
|
|
contentParent.get(), (uint64_t)contentParent->ChildID()));
|
|
}
|
|
// else we have an existing or preallocated process (which may be
|
|
// still launching)
|
|
|
|
contentParent->AssertAlive();
|
|
if (aGroup) {
|
|
aGroup->EnsureHostProcess(contentParent.get());
|
|
}
|
|
return contentParent;
|
|
}
|
|
|
|
/*static*/
|
|
RefPtr<ContentParent::LaunchPromise>
|
|
ContentParent::GetNewOrUsedBrowserProcessAsync(const nsACString& aRemoteType,
|
|
BrowsingContextGroup* aGroup,
|
|
ProcessPriority aPriority,
|
|
bool aPreferUsed,
|
|
uint64_t aBrowserId) {
|
|
// Obtain a `ContentParent` launched asynchronously.
|
|
UniqueContentParentKeepAlive contentParent =
|
|
GetNewOrUsedLaunchingBrowserProcess(aRemoteType, aGroup, aPriority,
|
|
aPreferUsed, aBrowserId);
|
|
if (!contentParent) {
|
|
// In case of launch error, stop here.
|
|
return LaunchPromise::CreateAndReject(NS_ERROR_ILLEGAL_DURING_SHUTDOWN,
|
|
__func__);
|
|
}
|
|
return contentParent->WaitForLaunchAsync(aPriority, aBrowserId);
|
|
}
|
|
|
|
/*static*/
|
|
UniqueContentParentKeepAlive ContentParent::GetNewOrUsedBrowserProcess(
|
|
const nsACString& aRemoteType, BrowsingContextGroup* aGroup,
|
|
ProcessPriority aPriority, bool aPreferUsed, uint64_t aBrowserId) {
|
|
UniqueContentParentKeepAlive contentParent =
|
|
GetNewOrUsedLaunchingBrowserProcess(aRemoteType, aGroup, aPriority,
|
|
aPreferUsed, aBrowserId);
|
|
if (!contentParent || !contentParent->WaitForLaunchSync(aPriority)) {
|
|
// In case of launch error, stop here.
|
|
return nullptr;
|
|
}
|
|
return contentParent;
|
|
}
|
|
|
|
RefPtr<ContentParent::LaunchPromise> ContentParent::WaitForLaunchAsync(
|
|
ProcessPriority aPriority, uint64_t aBrowserId) {
|
|
MOZ_DIAGNOSTIC_ASSERT(!IsDead());
|
|
UniqueContentParentKeepAlive self = AddKeepAlive(aBrowserId);
|
|
|
|
if (!IsLaunching()) {
|
|
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
|
|
("WaitForLaunchAsync: launched"));
|
|
return LaunchPromise::CreateAndResolve(std::move(self), __func__);
|
|
}
|
|
|
|
// We've started an async content process launch.
|
|
glean::dom_contentprocess::launch_is_sync
|
|
.EnumGet(glean::dom_contentprocess::LaunchIsSyncLabel::eFalse)
|
|
.Add();
|
|
|
|
// We have located a process that hasn't finished initializing, then attempt
|
|
// to finish initializing. Both `LaunchSubprocessResolve` and
|
|
// `LaunchSubprocessReject` are safe to call multiple times if we race with
|
|
// other `WaitForLaunchAsync` callbacks.
|
|
return mSubprocess->WhenProcessHandleReady()->Then(
|
|
GetCurrentSerialEventTarget(), __func__,
|
|
[self = std::move(self), aPriority](
|
|
const ProcessHandlePromise::ResolveOrRejectValue& aValue) mutable {
|
|
if (aValue.IsResolve() &&
|
|
self->LaunchSubprocessResolve(/* aIsSync = */ false, aPriority)) {
|
|
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
|
|
("WaitForLaunchAsync: async, now launched, process id=%p, "
|
|
"childID=%" PRIu64,
|
|
self.get(), (uint64_t)self->ChildID()));
|
|
self->mActivateTS = TimeStamp::Now();
|
|
return LaunchPromise::CreateAndResolve(std::move(self), __func__);
|
|
}
|
|
|
|
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
|
|
("WaitForLaunchAsync: async, rejected"));
|
|
self->LaunchSubprocessReject();
|
|
return LaunchPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
|
|
});
|
|
}
|
|
|
|
bool ContentParent::WaitForLaunchSync(ProcessPriority aPriority) {
|
|
MOZ_DIAGNOSTIC_ASSERT(!IsDead());
|
|
if (!IsLaunching()) {
|
|
return true;
|
|
}
|
|
|
|
// We've started a sync content process launch.
|
|
glean::dom_contentprocess::launch_is_sync
|
|
.EnumGet(glean::dom_contentprocess::LaunchIsSyncLabel::eTrue)
|
|
.Add();
|
|
|
|
// We're a process which hasn't finished initializing. We may be racing
|
|
// against whoever launched it (and whoever else is already racing). Since
|
|
// we're sync, we win the race and finish the initialization.
|
|
bool launchSuccess = mSubprocess->WaitForProcessHandle();
|
|
if (launchSuccess &&
|
|
LaunchSubprocessResolve(/* aIsSync = */ true, aPriority)) {
|
|
mActivateTS = TimeStamp::Now();
|
|
return true;
|
|
}
|
|
// In case of failure.
|
|
LaunchSubprocessReject();
|
|
return false;
|
|
}
|
|
|
|
static nsIDocShell* GetOpenerDocShellHelper(Element* aFrameElement) {
|
|
// Propagate the private-browsing status of the element's parent
|
|
// docshell to the remote docshell, via the chrome flags.
|
|
MOZ_ASSERT(aFrameElement);
|
|
nsPIDOMWindowOuter* win = aFrameElement->OwnerDoc()->GetWindow();
|
|
if (!win) {
|
|
NS_WARNING("Remote frame has no window");
|
|
return nullptr;
|
|
}
|
|
nsIDocShell* docShell = win->GetDocShell();
|
|
if (!docShell) {
|
|
NS_WARNING("Remote frame has no docshell");
|
|
return nullptr;
|
|
}
|
|
|
|
return docShell;
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvCreateClipboardContentAnalysis(
|
|
Endpoint<PClipboardContentAnalysisParent>&& aParentEndpoint) {
|
|
if (mClipboardContentAnalysisCreated) {
|
|
return IPC_FAIL(this, "ClipboardContentAnalysisParent already created");
|
|
}
|
|
mClipboardContentAnalysisCreated = true;
|
|
|
|
if (!mClipboardContentAnalysisThread) {
|
|
nsresult rv = NS_NewNamedThread(
|
|
"BkgrndClipboard", getter_AddRefs(mClipboardContentAnalysisThread));
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return IPC_FAIL(this, "NS_NewNamedThread failed");
|
|
}
|
|
}
|
|
|
|
// Bind the new endpoint to the backgroundClipboardContentAnalysis thread.
|
|
mClipboardContentAnalysisThread->Dispatch(
|
|
NS_NewRunnableFunction(
|
|
"Create ClipboardContentAnalysisParent",
|
|
[threadsafeHandle = RefPtr{ThreadsafeHandle()},
|
|
parentEndpoint = std::move(aParentEndpoint)]() mutable {
|
|
// Use a threadsafe handle here, so that it can
|
|
// be used to validate that the WindowContext comes from the
|
|
// correct content process.
|
|
RefPtr<ClipboardContentAnalysisParent> actor =
|
|
new ClipboardContentAnalysisParent(std::move(threadsafeHandle));
|
|
parentEndpoint.Bind(actor);
|
|
}),
|
|
NS_DISPATCH_NORMAL);
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvCreateGMPService() {
|
|
Endpoint<PGMPServiceParent> parent;
|
|
Endpoint<PGMPServiceChild> child;
|
|
|
|
if (mGMPCreated) {
|
|
return IPC_FAIL(this, "GMP Service already created");
|
|
}
|
|
|
|
nsresult rv;
|
|
rv = PGMPService::CreateEndpoints(EndpointProcInfo::Current(),
|
|
OtherEndpointProcInfo(), &parent, &child);
|
|
if (NS_FAILED(rv)) {
|
|
return IPC_FAIL(this, "CreateEndpoints failed");
|
|
}
|
|
|
|
if (!GMPServiceParent::Create(std::move(parent))) {
|
|
return IPC_FAIL(this, "GMPServiceParent::Create failed");
|
|
}
|
|
|
|
if (!SendInitGMPService(std::move(child))) {
|
|
return IPC_FAIL(this, "SendInitGMPService failed");
|
|
}
|
|
|
|
mGMPCreated = true;
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
IPCResult ContentParent::RecvAttributionEvent(
|
|
const nsACString& aHost, PrivateAttributionImpressionType aType,
|
|
uint32_t aIndex, const nsAString& aAd, const nsACString& aTargetHost) {
|
|
nsCOMPtr<nsIPrivateAttributionService> pa =
|
|
components::PrivateAttribution::Service();
|
|
if (NS_WARN_IF(!pa)) {
|
|
return IPC_OK();
|
|
}
|
|
pa->OnAttributionEvent(aHost, GetEnumString(aType), aIndex, aAd, aTargetHost);
|
|
return IPC_OK();
|
|
}
|
|
|
|
IPCResult ContentParent::RecvAttributionConversion(
|
|
const nsACString& aHost, const nsAString& aTask, uint32_t aHistogramSize,
|
|
const Maybe<uint32_t>& aLookbackDays,
|
|
const Maybe<PrivateAttributionImpressionType>& aImpressionType,
|
|
const nsTArray<nsString>& aAds, const nsTArray<nsCString>& aSourceHosts) {
|
|
nsCOMPtr<nsIPrivateAttributionService> pa =
|
|
components::PrivateAttribution::Service();
|
|
if (NS_WARN_IF(!pa)) {
|
|
return IPC_OK();
|
|
}
|
|
pa->OnAttributionConversion(
|
|
aHost, aTask, aHistogramSize, aLookbackDays.valueOr(0),
|
|
aImpressionType ? GetEnumString(*aImpressionType) : EmptyCString(), aAds,
|
|
aSourceHosts);
|
|
return IPC_OK();
|
|
}
|
|
|
|
/*static*/
|
|
void ContentParent::LogAndAssertFailedPrincipalValidationInfo(
|
|
nsIPrincipal* aPrincipal, const char* aMethod) {
|
|
// Send Telemetry
|
|
nsAutoCString principalScheme, principalType, spec;
|
|
mozilla::glean::security::FissionPrincipalsExtra extra = {};
|
|
|
|
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.scheme = Some(principalScheme);
|
|
} else {
|
|
principalType.AssignLiteral("Unknown");
|
|
}
|
|
extra.principaltype = Some(principalType);
|
|
extra.value = Some(aMethod);
|
|
|
|
// Do not send telemetry when chrome-debugging is enabled
|
|
bool isChromeDebuggingEnabled =
|
|
Preferences::GetBool("devtools.chrome.enabled", false);
|
|
if (!isChromeDebuggingEnabled) {
|
|
glean::security::fission_principals.Record(mozilla::Some(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) {
|
|
return ValidatePrincipalCouldPotentiallyBeLoadedBy(aPrincipal, mRemoteType,
|
|
aOptions);
|
|
}
|
|
|
|
/*static*/
|
|
already_AddRefed<RemoteBrowser> ContentParent::CreateBrowser(
|
|
const TabContext& aContext, Element* aFrameElement,
|
|
const nsACString& aRemoteType, BrowsingContext* aBrowsingContext,
|
|
ContentParent* aOpenerContentParent) {
|
|
AUTO_PROFILER_LABEL("ContentParent::CreateBrowser", OTHER);
|
|
|
|
MOZ_DIAGNOSTIC_ASSERT(
|
|
!aBrowsingContext->Canonical()->GetBrowserParent(),
|
|
"BrowsingContext must not have BrowserParent, or have previous "
|
|
"BrowserParent cleared");
|
|
|
|
// Don't bother creating new content browsers after entering shutdown. This
|
|
// could lead to starting a new content process, which may significantly delay
|
|
// shutdown, and the content is unlikely to be displayed.
|
|
if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
|
|
NS_WARNING("Ignoring remote browser creation request during shutdown");
|
|
return nullptr;
|
|
}
|
|
|
|
nsAutoCString remoteType(aRemoteType);
|
|
if (remoteType.IsEmpty()) {
|
|
remoteType = DEFAULT_REMOTE_TYPE;
|
|
}
|
|
|
|
TabId tabId(nsContentUtils::GenerateTabId());
|
|
|
|
nsIDocShell* docShell = GetOpenerDocShellHelper(aFrameElement);
|
|
TabId openerTabId;
|
|
if (docShell) {
|
|
openerTabId = BrowserParent::GetTabIdFrom(docShell);
|
|
}
|
|
|
|
// Hold a KeepAlive on our ContentParent throughout this function. Once the
|
|
// `BrowserParent` has been created, it can be cleared, as that BrowserParent
|
|
// will establish its own KeepAlive.
|
|
UniqueContentParentKeepAlive constructorSender;
|
|
MOZ_RELEASE_ASSERT(XRE_IsParentProcess(),
|
|
"Cannot allocate BrowserParent in content process");
|
|
if (aOpenerContentParent && !aOpenerContentParent->IsShuttingDown()) {
|
|
constructorSender =
|
|
aOpenerContentParent->AddKeepAlive(aBrowsingContext->BrowserId());
|
|
} else {
|
|
constructorSender = GetNewOrUsedBrowserProcess(
|
|
remoteType, aBrowsingContext->Group(), PROCESS_PRIORITY_FOREGROUND,
|
|
/* aPreferUsed */ false,
|
|
/* aBrowserId */ aBrowsingContext->BrowserId());
|
|
if (!constructorSender) {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
aBrowsingContext->SetEmbedderElement(aFrameElement);
|
|
|
|
// Ensure that the process which we're using to launch is set as the host
|
|
// process for this BrowsingContextGroup.
|
|
aBrowsingContext->Group()->EnsureHostProcess(constructorSender.get());
|
|
|
|
nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
|
|
docShell->GetTreeOwner(getter_AddRefs(treeOwner));
|
|
if (!treeOwner) {
|
|
return nullptr;
|
|
}
|
|
|
|
nsCOMPtr<nsIWebBrowserChrome> wbc = do_GetInterface(treeOwner);
|
|
if (!wbc) {
|
|
return nullptr;
|
|
}
|
|
uint32_t chromeFlags = 0;
|
|
wbc->GetChromeFlags(&chromeFlags);
|
|
|
|
nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
|
|
if (loadContext && loadContext->UsePrivateBrowsing()) {
|
|
chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW;
|
|
}
|
|
if (loadContext && loadContext->UseRemoteTabs()) {
|
|
chromeFlags |= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW;
|
|
}
|
|
if (loadContext && loadContext->UseRemoteSubframes()) {
|
|
chromeFlags |= nsIWebBrowserChrome::CHROME_FISSION_WINDOW;
|
|
}
|
|
|
|
if (tabId == 0) {
|
|
return nullptr;
|
|
}
|
|
|
|
aBrowsingContext->Canonical()->SetOwnerProcessId(
|
|
constructorSender->ChildID());
|
|
|
|
RefPtr<BrowserParent> browserParent =
|
|
new BrowserParent(constructorSender.get(), tabId, aContext,
|
|
aBrowsingContext->Canonical(), chromeFlags);
|
|
|
|
ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
|
|
if (NS_WARN_IF(!cpm)) {
|
|
return nullptr;
|
|
}
|
|
cpm->RegisterRemoteFrame(browserParent);
|
|
|
|
// Open a remote endpoint for our PBrowser actor.
|
|
ManagedEndpoint<PBrowserChild> childEp =
|
|
constructorSender->OpenPBrowserEndpoint(browserParent);
|
|
if (NS_WARN_IF(!childEp.IsValid())) {
|
|
return nullptr;
|
|
}
|
|
|
|
nsCOMPtr<nsIPrincipal> initialPrincipal =
|
|
NullPrincipal::Create(aBrowsingContext->OriginAttributesRef());
|
|
WindowGlobalInit windowInit = WindowGlobalActor::AboutBlankInitializer(
|
|
aBrowsingContext, initialPrincipal);
|
|
|
|
RefPtr<WindowGlobalParent> windowParent =
|
|
WindowGlobalParent::CreateDisconnected(windowInit);
|
|
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) {
|
|
for (auto* cp : AllProcesses(eLive)) {
|
|
AutoTArray<StringBundleDescriptor, 1> array;
|
|
array.AppendElement(StringBundleDescriptor(aBundle.bundleURL(),
|
|
aBundle.mapHandle().Clone()));
|
|
Unused << cp->SendRegisterStringBundles(std::move(array));
|
|
}
|
|
}
|
|
|
|
void ContentParent::BroadcastShmBlockAdded(uint32_t aGeneration,
|
|
uint32_t aIndex) {
|
|
auto* pfl = gfxPlatformFontList::PlatformFontList();
|
|
for (auto* cp : AllProcesses(eLive)) {
|
|
ReadOnlySharedMemoryHandle handle =
|
|
pfl->ShareShmBlockToProcess(aIndex, cp->Pid());
|
|
if (!handle.IsValid()) {
|
|
// If something went wrong here, we just skip it; the child will need to
|
|
// request the block as needed, at some performance cost.
|
|
continue;
|
|
}
|
|
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) {
|
|
// Update processes and print the support info from the given location.
|
|
sCodecsSupported[aLocation] = aSupported;
|
|
for (auto* cp : AllProcesses(eAll)) {
|
|
Unused << cp->SendUpdateMediaCodecsSupported(aLocation, aSupported);
|
|
}
|
|
nsCString supportString;
|
|
media::MCSInfo::GetMediaCodecsSupportedString(supportString, aSupported);
|
|
LOGPDM("Broadcast support from '%s', support=%s",
|
|
RemoteDecodeInToStr(aLocation), supportString.get());
|
|
|
|
// Merge incoming support with existing support list from other locations
|
|
media::MCSInfo::AddSupport(aSupported);
|
|
auto fullSupport = media::MCSInfo::GetSupport();
|
|
|
|
// Generate + save FULL support string for display in about:support
|
|
supportString.Truncate();
|
|
media::MCSInfo::GetMediaCodecsSupportedString(supportString, fullSupport);
|
|
gfx::gfxVars::SetCodecSupportInfo(supportString);
|
|
}
|
|
|
|
const nsACString& ContentParent::GetRemoteType() const { return mRemoteType; }
|
|
|
|
static StaticRefPtr<nsIAsyncShutdownClient> sXPCOMShutdownClient;
|
|
static StaticRefPtr<nsIAsyncShutdownClient> sProfileBeforeChangeClient;
|
|
static StaticRefPtr<nsIAsyncShutdownClient> sAppShutdownConfirmedClient;
|
|
|
|
void ContentParent::Init() {
|
|
MOZ_ASSERT(sXPCOMShutdownClient);
|
|
|
|
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
|
if (obs) {
|
|
size_t length = std::size(sObserverTopics);
|
|
for (size_t i = 0; i < length; ++i) {
|
|
obs->AddObserver(this, sObserverTopics[i], false);
|
|
}
|
|
}
|
|
|
|
if (obs) {
|
|
nsAutoString cpId;
|
|
cpId.AppendInt(static_cast<uint64_t>(this->ChildID()));
|
|
obs->NotifyObservers(static_cast<nsIObserver*>(this), "ipc:content-created",
|
|
cpId.get());
|
|
}
|
|
|
|
#ifdef ACCESSIBILITY
|
|
// If accessibility is running in chrome process then start it in content
|
|
// process.
|
|
if (GetAccService()) {
|
|
Unused << SendActivateA11y(nsAccessibilityService::GetActiveCacheDomains());
|
|
}
|
|
#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());
|
|
|
|
// sending only the remote settings schemes to the content process
|
|
nsCOMPtr<nsIIOService> io(do_GetIOService());
|
|
MOZ_ASSERT(io, "No IO service for SimpleURI scheme broadcast to content");
|
|
nsTArray<nsCString> remoteSchemes;
|
|
MOZ_ALWAYS_SUCCEEDS(io->GetSimpleURIUnknownRemoteSchemes(remoteSchemes));
|
|
Unused << SendSimpleURIUnknownRemoteSchemes(std::move(remoteSchemes));
|
|
}
|
|
|
|
void ContentParent::AsyncSendShutDownMessage() {
|
|
MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
|
|
("AsyncSendShutDownMessage id=%p, childID=%" PRIu64, this,
|
|
(uint64_t)this->ChildID()));
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(IsDead());
|
|
if (mShutdownPending || !CanSend()) {
|
|
// Don't bother dispatching a runnable if shutdown is already pending or the
|
|
// channel has already been disconnected, as it won't do anything.
|
|
// We could be very late in shutdown and unable to dispatch.
|
|
return;
|
|
}
|
|
|
|
// In the case of normal shutdown, send a shutdown message to child to
|
|
// allow it to perform shutdown tasks.
|
|
GetCurrentSerialEventTarget()->Dispatch(NewRunnableMethod<ShutDownMethod>(
|
|
"dom::ContentParent::ShutDownProcess", this,
|
|
&ContentParent::ShutDownProcess, SEND_SHUTDOWN_MESSAGE));
|
|
}
|
|
|
|
void MaybeLogBlockShutdownDiagnostics(ContentParent* aSelf, const char* aMsg,
|
|
const char* aFile, int32_t aLine) {
|
|
#if defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
|
|
if (aSelf->IsBlockingShutdown()) {
|
|
MOZ_LOG(
|
|
ContentParent::GetLog(), LogLevel::Info,
|
|
("ContentParent: id=%p childID=%" PRIu64 " pid=%d - %s at %s(%d)",
|
|
aSelf, (uint64_t)aSelf->ChildID(), aSelf->Pid(), aMsg, aFile, aLine));
|
|
}
|
|
#else
|
|
Unused << aSelf;
|
|
Unused << aMsg;
|
|
Unused << aFile;
|
|
Unused << aLine;
|
|
#endif
|
|
}
|
|
|
|
bool ContentParent::ShutDownProcess(ShutDownMethod aMethod) {
|
|
bool result = false;
|
|
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
|
|
("ShutDownProcess: id=%p childID=%" PRIu64, this,
|
|
(uint64_t)this->ChildID()));
|
|
// NB: must MarkAsDead() here so that this isn't accidentally
|
|
// returned from Get*() while in the midst of shutdown.
|
|
MarkAsDead();
|
|
|
|
// Shutting down by sending a shutdown message works differently than the
|
|
// other methods. We first call Shutdown() in the child. After the child is
|
|
// ready, it calls FinishShutdown() on us. Then we close the channel.
|
|
if (aMethod == SEND_SHUTDOWN_MESSAGE) {
|
|
if (!mShutdownPending) {
|
|
if (CanSend()) {
|
|
// Stop sending input events with input priority when shutting down.
|
|
SetInputPriorityEventEnabled(false);
|
|
// If we did not earlier, let's signal the shutdown to JS now.
|
|
SignalImpendingShutdownToContentJS();
|
|
|
|
// Adjust the QoS priorities for shutdown, if they exist.
|
|
if (StaticPrefs::threads_use_low_power_enabled() &&
|
|
StaticPrefs::
|
|
threads_lower_mainthread_priority_in_background_enabled()) {
|
|
SetMainThreadQoSPriority(nsIThread::QOS_PRIORITY_NORMAL);
|
|
}
|
|
|
|
// Send 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->SetOsPid(-1);
|
|
mMessageManager->Disconnect();
|
|
mMessageManager = nullptr;
|
|
}
|
|
|
|
void ContentParent::AddToPool(nsTArray<ContentParent*>& aPool) {
|
|
MOZ_DIAGNOSTIC_ASSERT(!mIsInPool);
|
|
AssertAlive();
|
|
MOZ_DIAGNOSTIC_ASSERT(!mCalledKillHard);
|
|
aPool.AppendElement(this);
|
|
mIsInPool = true;
|
|
}
|
|
|
|
void ContentParent::RemoveFromPool(nsTArray<ContentParent*>& aPool) {
|
|
MOZ_DIAGNOSTIC_ASSERT(mIsInPool);
|
|
aPool.RemoveElement(this);
|
|
mIsInPool = false;
|
|
}
|
|
|
|
void ContentParent::AssertNotInPool() {
|
|
MOZ_RELEASE_ASSERT(!mIsInPool);
|
|
|
|
MOZ_RELEASE_ASSERT(!sBrowserContentParents ||
|
|
!sBrowserContentParents->Contains(mRemoteType) ||
|
|
!sBrowserContentParents->Get(mRemoteType)->Contains(this));
|
|
|
|
for (const auto& group : mGroups) {
|
|
MOZ_RELEASE_ASSERT(group->GetHostProcess(mRemoteType) != this,
|
|
"still a host process for one of our groups?");
|
|
}
|
|
}
|
|
|
|
void ContentParent::AssertAlive() {
|
|
MOZ_DIAGNOSTIC_ASSERT(!mIsSignaledImpendingShutdown);
|
|
MOZ_DIAGNOSTIC_ASSERT(!IsDead());
|
|
}
|
|
|
|
void ContentParent::RemoveFromList() {
|
|
if (!mIsInPool) {
|
|
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
|
AssertNotInPool();
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
// Ensure that this BrowsingContextGroup is no longer used to host new
|
|
// documents from any associated BrowsingContextGroups. It may become a host
|
|
// again in the future, if it is restored to the pool.
|
|
for (const auto& group : mGroups) {
|
|
group->RemoveHostProcess(this);
|
|
}
|
|
|
|
if (sBrowserContentParents) {
|
|
if (auto entry = sBrowserContentParents->Lookup(mRemoteType)) {
|
|
const auto& contentParents = entry.Data();
|
|
RemoveFromPool(*contentParents);
|
|
if (contentParents->IsEmpty()) {
|
|
entry.Remove();
|
|
}
|
|
}
|
|
if (sBrowserContentParents->IsEmpty()) {
|
|
delete sBrowserContentParents;
|
|
sBrowserContentParents = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ContentParent::MarkAsDead() {
|
|
MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
|
|
("Marking ContentProcess id=%p childID=%" PRIu64 " as dead", this,
|
|
(uint64_t)this->ChildID()));
|
|
MOZ_DIAGNOSTIC_ASSERT(!sInProcessSelector);
|
|
RemoveFromList();
|
|
|
|
// Flag shutdown has started for us to our threadsafe handle.
|
|
{
|
|
// Depending on how we get here, the lock might or might not be set.
|
|
RecursiveMutexAutoLock lock(mThreadsafeHandle->mMutex);
|
|
|
|
mThreadsafeHandle->mShutdownStarted = true;
|
|
}
|
|
|
|
// Prevent this process from being re-used.
|
|
PreallocatedProcessManager::Erase(this);
|
|
|
|
#if defined(MOZ_WIDGET_ANDROID) && !defined(MOZ_PROFILE_GENERATE)
|
|
if (IsAlive()) {
|
|
// We're intentionally killing the content process at this point to ensure
|
|
// that we never have a "dead" content process sitting around and occupying
|
|
// an Android Service.
|
|
//
|
|
// The exception is in MOZ_PROFILE_GENERATE builds where we must allow the
|
|
// process to shutdown cleanly so that profile data can be dumped. This is
|
|
// okay as we will not reach our process limit during the profile run.
|
|
nsCOMPtr<nsIEventTarget> launcherThread(GetIPCLauncher());
|
|
MOZ_ASSERT(launcherThread);
|
|
|
|
auto procType = java::GeckoProcessType::CONTENT();
|
|
auto selector =
|
|
java::GeckoProcessManager::Selector::New(procType, OtherPid());
|
|
|
|
launcherThread->Dispatch(NS_NewRunnableFunction(
|
|
"ContentParent::MarkAsDead",
|
|
[selector =
|
|
java::GeckoProcessManager::Selector::GlobalRef(selector)]() {
|
|
java::GeckoProcessManager::ShutdownProcess(selector);
|
|
}));
|
|
}
|
|
#endif
|
|
|
|
mLifecycleState = LifecycleState::DEAD;
|
|
}
|
|
|
|
void ContentParent::ProcessingError(Result aCode, const char* aReason) {
|
|
if (MsgDropped == aCode) {
|
|
return;
|
|
}
|
|
// Other errors are big deals.
|
|
#ifndef FUZZING
|
|
KillHard(aReason);
|
|
#endif
|
|
if (CanSend()) {
|
|
GetIPCChannel()->InduceConnectionError();
|
|
}
|
|
}
|
|
|
|
void ContentParent::ActorDestroy(ActorDestroyReason why) {
|
|
#ifdef FUZZING_SNAPSHOT
|
|
MOZ_FUZZING_IPC_DROP_PEER("ContentParent::ActorDestroy");
|
|
#endif
|
|
|
|
// Gather process lifetime telemetry.
|
|
if (StringBeginsWith(mRemoteType, WEB_REMOTE_TYPE) ||
|
|
mRemoteType == FILE_REMOTE_TYPE || mRemoteType == EXTENSION_REMOTE_TYPE) {
|
|
TimeDuration runtime = TimeStamp::Now() - mActivateTS;
|
|
glean::process::lifetime.AccumulateRawDuration(runtime);
|
|
}
|
|
|
|
if (mSendShutdownTimer) {
|
|
mSendShutdownTimer->Cancel();
|
|
mSendShutdownTimer = nullptr;
|
|
}
|
|
if (mForceKillTimer) {
|
|
mForceKillTimer->Cancel();
|
|
mForceKillTimer = nullptr;
|
|
}
|
|
|
|
// Signal shutdown completion regardless of error state, so we can
|
|
// finish waiting in the xpcom-shutdown/profile-before-change observer.
|
|
RemoveShutdownBlockers();
|
|
|
|
if (mHangMonitorActor) {
|
|
ProcessHangMonitor::RemoveProcess(mHangMonitorActor);
|
|
mHangMonitorActor = nullptr;
|
|
}
|
|
|
|
RefPtr<FileSystemSecurity> fss = FileSystemSecurity::Get();
|
|
if (fss) {
|
|
fss->Forget(ChildID());
|
|
}
|
|
|
|
if (why == NormalShutdown && !mCalledClose) {
|
|
// If we shut down normally but haven't called Close, assume somebody
|
|
// else called Close on us. In that case, we still need to call
|
|
// ShutDownProcess below to perform other necessary clean up.
|
|
mCalledClose = true;
|
|
}
|
|
|
|
// Make sure we always clean up.
|
|
ShutDownProcess(CLOSE_CHANNEL);
|
|
|
|
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
|
if (obs) {
|
|
size_t length = std::size(sObserverTopics);
|
|
for (size_t i = 0; i < length; ++i) {
|
|
obs->RemoveObserver(static_cast<nsIObserver*>(this), sObserverTopics[i]);
|
|
}
|
|
}
|
|
|
|
// remove the global remote preferences observers
|
|
Preferences::RemoveObserver(this, "");
|
|
gfxVars::RemoveReceiver(this);
|
|
|
|
if (GPUProcessManager* gpu = GPUProcessManager::Get()) {
|
|
// Note: the manager could have shutdown already.
|
|
gpu->RemoveListener(this);
|
|
}
|
|
|
|
RecvRemoveGeolocationListener();
|
|
|
|
#ifdef MOZ_WMF_CDM
|
|
if (mOriginsListCallback) {
|
|
nsCOMPtr<nsIWindowsMediaFoundationCDMOriginsListService> rsService =
|
|
do_GetService("@mozilla.org/media/wmfcdm-origins-list;1");
|
|
if (rsService) {
|
|
rsService->RemoveCallback(mOriginsListCallback);
|
|
}
|
|
mOriginsListCallback = nullptr;
|
|
}
|
|
#endif
|
|
|
|
// Destroy our JSProcessActors, and reject any pending queries.
|
|
JSActorDidDestroy();
|
|
|
|
if (obs) {
|
|
RefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();
|
|
|
|
props->SetPropertyAsUint64(u"childID"_ns, mChildID);
|
|
|
|
if (AbnormalShutdown == why) {
|
|
glean::subprocess::abnormal_abort.Get("content"_ns).Add(1);
|
|
|
|
props->SetPropertyAsBool(u"abnormal"_ns, true);
|
|
|
|
nsAutoString dumpID;
|
|
nsAutoCString processType;
|
|
// There's a window in which child processes can crash
|
|
// after IPC is established, but before a crash reporter
|
|
// is created.
|
|
if (mCrashReporter) {
|
|
// if mCreatedPairedMinidumps is true, we've already generated
|
|
// parent/child dumps for desktop crashes.
|
|
if (!mCreatedPairedMinidumps) {
|
|
#if defined(XP_MACOSX)
|
|
RefPtr<nsAvailableMemoryWatcherBase> memWatcher;
|
|
memWatcher = nsAvailableMemoryWatcherBase::GetSingleton();
|
|
memWatcher->AddChildAnnotations(mCrashReporter);
|
|
#endif
|
|
|
|
mCrashReporter->GenerateCrashReport();
|
|
}
|
|
|
|
if (mCrashReporter->HasMinidump()) {
|
|
dumpID = mCrashReporter->MinidumpID();
|
|
}
|
|
processType = mCrashReporter->ProcessType();
|
|
} else {
|
|
HandleOrphanedMinidump(&dumpID);
|
|
processType = XRE_GeckoProcessTypeToString(GeckoProcessType_Content);
|
|
}
|
|
|
|
if (!dumpID.IsEmpty()) {
|
|
props->SetPropertyAsAString(u"dumpID"_ns, dumpID);
|
|
}
|
|
if (!processType.IsEmpty()) {
|
|
props->SetPropertyAsACString(u"processType"_ns, processType);
|
|
}
|
|
}
|
|
nsAutoString cpId;
|
|
cpId.AppendInt(static_cast<uint64_t>(this->ChildID()));
|
|
obs->NotifyObservers((nsIPropertyBag2*)props, "ipc:content-shutdown",
|
|
cpId.get());
|
|
}
|
|
|
|
// Remove any and all idle listeners.
|
|
if (mIdleListeners.Length() > 0) {
|
|
nsCOMPtr<nsIUserIdleService> idleService =
|
|
do_GetService("@mozilla.org/widget/useridleservice;1");
|
|
if (idleService) {
|
|
RefPtr<ParentIdleListener> listener;
|
|
for (const auto& lentry : mIdleListeners) {
|
|
listener = static_cast<ParentIdleListener*>(lentry.get());
|
|
idleService->RemoveIdleObserver(listener, listener->mTime);
|
|
}
|
|
}
|
|
mIdleListeners.Clear();
|
|
}
|
|
|
|
MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
|
|
("destroying Subprocess in ActorDestroy: ContentParent id=%p "
|
|
"mSubprocess id=%p handle %" PRIuPTR,
|
|
this, mSubprocess,
|
|
mSubprocess ? (uintptr_t)mSubprocess->GetChildProcessHandle() : -1));
|
|
// FIXME (bug 1520997): does this really need an additional dispatch?
|
|
if (GetCurrentSerialEventTarget()) {
|
|
GetCurrentSerialEventTarget()->Dispatch(NS_NewRunnableFunction(
|
|
"DelayedDeleteSubprocessRunnable", [subprocess = mSubprocess] {
|
|
MOZ_LOG(
|
|
ContentParent::GetLog(), LogLevel::Debug,
|
|
("destroyed Subprocess in ActorDestroy: Subprocess id=%p handle "
|
|
"%" PRIuPTR,
|
|
subprocess,
|
|
subprocess ? (uintptr_t)subprocess->GetChildProcessHandle()
|
|
: -1));
|
|
subprocess->Destroy();
|
|
}));
|
|
}
|
|
mSubprocess = nullptr;
|
|
|
|
ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
|
|
if (cpm) {
|
|
cpm->RemoveContentProcess(this->ChildID());
|
|
}
|
|
|
|
if (mDriverCrashGuard) {
|
|
mDriverCrashGuard->NotifyCrashed();
|
|
}
|
|
|
|
// Unregister all the BlobURLs registered by the ContentChild.
|
|
BlobURLProtocolHandler::RemoveDataEntriesPerContentParent(ChildID());
|
|
|
|
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
|
AssertNotInPool();
|
|
#endif
|
|
|
|
// As this process is going away, ensure that every BrowsingContext hosted by
|
|
// it has been detached, and every BrowsingContextGroup has been fully
|
|
// unsubscribed.
|
|
BrowsingContext::DiscardFromContentParent(this);
|
|
|
|
const nsTHashSet<RefPtr<BrowsingContextGroup>> groups = std::move(mGroups);
|
|
for (const auto& group : groups) {
|
|
group->Unsubscribe(this);
|
|
}
|
|
MOZ_DIAGNOSTIC_ASSERT(mGroups.IsEmpty());
|
|
|
|
mPendingLoadStates.Clear();
|
|
}
|
|
|
|
UniqueContentParentKeepAlive ContentParent::TryAddKeepAlive(
|
|
uint64_t aBrowserId) {
|
|
UniqueContentParentKeepAlive keepAlive =
|
|
UniqueContentParentKeepAliveFromThreadsafe(
|
|
mThreadsafeHandle->TryAddKeepAlive(aBrowserId));
|
|
// If we successfully added a KeepAlive, we can cancel any pending
|
|
// MaybeBeginShutDown call (as it will no longer begin process shutdown due to
|
|
// outstanding KeepAlives).
|
|
// This is just an optimization and the MaybeBeginShutDown call will be a
|
|
// no-op if it is called with the KeepAlive held.
|
|
if (keepAlive && mMaybeBeginShutdownRunner) {
|
|
mMaybeBeginShutdownRunner->Cancel();
|
|
mMaybeBeginShutdownRunner = nullptr;
|
|
}
|
|
return keepAlive;
|
|
}
|
|
|
|
UniqueContentParentKeepAlive ContentParent::AddKeepAlive(uint64_t aBrowserId) {
|
|
UniqueContentParentKeepAlive keepAlive = TryAddKeepAlive(aBrowserId);
|
|
MOZ_DIAGNOSTIC_ASSERT(keepAlive, "ContentParent is already dead");
|
|
return keepAlive;
|
|
}
|
|
|
|
void ContentParent::RemoveKeepAlive(uint64_t aBrowserId) {
|
|
AssertIsOnMainThread();
|
|
|
|
{
|
|
RecursiveMutexAutoLock lock(mThreadsafeHandle->mMutex);
|
|
auto entry = mThreadsafeHandle->mKeepAlivesPerBrowserId.Lookup(aBrowserId);
|
|
MOZ_RELEASE_ASSERT(entry, "No KeepAlive for this BrowserId");
|
|
if (!--entry.Data()) {
|
|
entry.Remove();
|
|
}
|
|
}
|
|
|
|
MaybeBeginShutDown();
|
|
}
|
|
|
|
void ContentParent::MaybeBeginShutDown(bool aImmediate,
|
|
bool aIgnoreKeepAlivePref) {
|
|
AssertIsOnMainThread();
|
|
MOZ_ASSERT(!aIgnoreKeepAlivePref || aImmediate,
|
|
"aIgnoreKeepAlivePref requires aImmediate");
|
|
|
|
// Don't bother waiting, even if `aImmediate` is not true, if the process
|
|
// can no longer be re-used (e.g. because it is dead, or we're in shutdown).
|
|
bool immediate =
|
|
aImmediate || IsDead() ||
|
|
AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed) ||
|
|
StaticPrefs::dom_ipc_processReuse_unusedGraceMs() == 0;
|
|
|
|
// Clean up any scheduled idle task unless we schedule a new one.
|
|
auto cancelIdleTask = MakeScopeExit([&] {
|
|
if (mMaybeBeginShutdownRunner) {
|
|
mMaybeBeginShutdownRunner->Cancel();
|
|
mMaybeBeginShutdownRunner = nullptr;
|
|
}
|
|
});
|
|
|
|
{
|
|
RecursiveMutexAutoLock lock(mThreadsafeHandle->mMutex);
|
|
// If we still have keepalives or are still launching, we're not shutting
|
|
// down. Return.
|
|
if (IsLaunching() ||
|
|
!mThreadsafeHandle->mKeepAlivesPerBrowserId.IsEmpty()) {
|
|
return;
|
|
}
|
|
|
|
// If we're not in main process shutdown, we might want to keep some content
|
|
// processes alive for performance reasons (e.g. test runs and privileged
|
|
// content process for some about: pages). We don't want to alter behavior
|
|
// if the pref is not set, so default to 0.
|
|
if (!aIgnoreKeepAlivePref && mIsInPool && !mRemoteType.Contains('=') &&
|
|
!AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
|
|
auto* contentParents = sBrowserContentParents->Get(mRemoteType);
|
|
MOZ_RELEASE_ASSERT(
|
|
contentParents,
|
|
"mIsInPool, yet no entry for mRemoteType in sBrowserContentParents?");
|
|
|
|
nsAutoCString keepAlivePref("dom.ipc.keepProcessesAlive.");
|
|
keepAlivePref.Append(mRemoteType);
|
|
|
|
int32_t processesToKeepAlive = 0;
|
|
if (NS_SUCCEEDED(Preferences::GetInt(keepAlivePref.get(),
|
|
&processesToKeepAlive)) &&
|
|
contentParents->Length() <=
|
|
static_cast<size_t>(processesToKeepAlive)) {
|
|
// We're keeping this process alive even though there are no keepalives
|
|
// for it due to the keepalive pref.
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (immediate) {
|
|
// We're not keeping this process alive, begin shutdown.
|
|
mThreadsafeHandle->mShutdownStarted = true;
|
|
}
|
|
}
|
|
|
|
// If we're not beginning shutdown immediately, make sure an idle task runner
|
|
// is scheduled to call us back. This delay is intended to avoid unnecessary
|
|
// process churn when a process becomes momentarily unused (which can happen
|
|
// frequently when running tests).
|
|
if (!immediate) {
|
|
// We want an idle task to call us back, don't cancel it.
|
|
cancelIdleTask.release();
|
|
|
|
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
|
|
("MaybeBeginShutDown(%d) would begin shutdown, %s", OtherChildID(),
|
|
mMaybeBeginShutdownRunner ? "already delayed" : "delaying"));
|
|
|
|
if (!mMaybeBeginShutdownRunner) {
|
|
TimeDuration startDelay = TimeDuration::FromMilliseconds(
|
|
StaticPrefs::dom_ipc_processReuse_unusedGraceMs());
|
|
TimeDuration maxDelay = startDelay + TimeDuration::FromSeconds(1);
|
|
mMaybeBeginShutdownRunner = IdleTaskRunner::Create(
|
|
[self = RefPtr{this}](TimeStamp) -> bool {
|
|
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
|
|
("MaybeBeginShutDown(%d) resuming after delay",
|
|
self->OtherChildID()));
|
|
self->MaybeBeginShutDown(/* aImmediate */ true);
|
|
return true;
|
|
},
|
|
"ContentParent::IdleMaybeBeginShutdown", startDelay, maxDelay,
|
|
/* aMinimumUsefulBudget */ TimeDuration::FromMilliseconds(3),
|
|
/* aRepeating */ false, [] { return false; });
|
|
}
|
|
return;
|
|
}
|
|
|
|
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
|
|
("MaybeBeginShutDown(%d) shutdown starting (%u bps)", OtherChildID(),
|
|
ManagedPBrowserParent().Count()));
|
|
|
|
MarkAsDead();
|
|
SignalImpendingShutdownToContentJS();
|
|
|
|
if (ManagedPBrowserParent().Count() > 0) {
|
|
// We still have PBrowser instances which have not been shut down.
|
|
// Wait for them to be destroyed before we follow-through and shut down this
|
|
// process, but start a shutdown timer to kill them if this takes too long.
|
|
StartSendShutdownTimer();
|
|
} else {
|
|
// All tabs are dead, we can fully begin shutting down.
|
|
AsyncSendShutDownMessage();
|
|
}
|
|
}
|
|
|
|
void ContentParent::StartSendShutdownTimer() {
|
|
if (mSendShutdownTimer || !CanSend()) {
|
|
return;
|
|
}
|
|
|
|
uint32_t timeoutSecs = StaticPrefs::dom_ipc_tabs_shutdownTimeoutSecs();
|
|
if (timeoutSecs > 0) {
|
|
NS_NewTimerWithFuncCallback(getter_AddRefs(mSendShutdownTimer),
|
|
ContentParent::SendShutdownTimerCallback, this,
|
|
timeoutSecs * 1000, nsITimer::TYPE_ONE_SHOT,
|
|
"dom::ContentParent::StartSendShutdownTimer");
|
|
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);
|
|
}
|
|
}
|
|
|
|
TestShellParent* ContentParent::CreateTestShell() {
|
|
RefPtr<TestShellParent> actor = new TestShellParent();
|
|
if (!SendPTestShellConstructor(actor)) {
|
|
return nullptr;
|
|
}
|
|
return actor;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
geckoargs::ChildProcessArgs extraArgs;
|
|
geckoargs::sIsForBrowser.Put(IsForBrowser(), extraArgs);
|
|
geckoargs::sNotForBrowser.Put(!IsForBrowser(), extraArgs);
|
|
|
|
// Prefs information is passed via anonymous shared memory to avoid bloating
|
|
// the command line.
|
|
|
|
// Instantiate the pref serializer. It will be cleaned up in
|
|
// `LaunchSubprocessReject`/`LaunchSubprocessResolve`.
|
|
mPrefSerializer = MakeUnique<mozilla::ipc::SharedPreferenceSerializer>();
|
|
if (!mPrefSerializer->SerializeToSharedMemory(GeckoProcessType_Content,
|
|
GetRemoteType())) {
|
|
NS_WARNING("SharedPreferenceSerializer::SerializeToSharedMemory failed");
|
|
MarkAsDead();
|
|
return false;
|
|
}
|
|
mPrefSerializer->AddSharedPrefCmdLineArgs(*mSubprocess, extraArgs);
|
|
|
|
// The JS engine does some computation during the initialization which can be
|
|
// shared across processes. We add command line arguments to pass a file
|
|
// handle and its content length, to minimize the startup time of content
|
|
// processes.
|
|
::mozilla::ipc::ExportSharedJSInit(*mSubprocess, extraArgs);
|
|
|
|
// Register ContentParent as an observer for changes to any pref
|
|
// whose prefix matches the empty string, i.e. all of them. The
|
|
// observation starts here in order to capture pref updates that
|
|
// happen during async launch.
|
|
Preferences::AddStrongObserver(this, "");
|
|
|
|
geckoargs::sSafeMode.Put(gSafeMode, extraArgs);
|
|
|
|
#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
|
|
if (IsContentSandboxEnabled()) {
|
|
AppendSandboxParams(extraArgs.mArgs);
|
|
mSubprocess->DisableOSActivityMode();
|
|
}
|
|
#endif
|
|
|
|
nsCString parentBuildID(mozilla::PlatformBuildID());
|
|
geckoargs::sParentBuildID.Put(parentBuildID.get(), extraArgs);
|
|
|
|
#ifdef MOZ_WIDGET_GTK
|
|
// This is X11-only pending a solution for WebGL in Wayland mode.
|
|
if (StaticPrefs::dom_ipc_avoid_gtk() && widget::GdkIsX11Display()) {
|
|
mSubprocess->SetEnv("MOZ_HEADLESS", "1");
|
|
}
|
|
#endif
|
|
|
|
mLaunchYieldTS = TimeStamp::Now();
|
|
return mSubprocess->AsyncLaunch(std::move(extraArgs));
|
|
}
|
|
|
|
void ContentParent::LaunchSubprocessReject() {
|
|
NS_WARNING("failed to launch child in the parent");
|
|
MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
|
|
("failed to launch child in the parent"));
|
|
// Now that communication with the child is complete, we can cleanup
|
|
// the preference serializer.
|
|
mPrefSerializer = nullptr;
|
|
if (mIsAPreallocBlocker) {
|
|
PreallocatedProcessManager::RemoveBlocker(mRemoteType, this);
|
|
mIsAPreallocBlocker = false;
|
|
}
|
|
MarkAsDead();
|
|
RemoveShutdownBlockers();
|
|
}
|
|
|
|
bool ContentParent::LaunchSubprocessResolve(bool aIsSync,
|
|
ProcessPriority aPriority) {
|
|
AUTO_PROFILER_LABEL("ContentParent::LaunchSubprocess::resolve", OTHER);
|
|
|
|
if (mLaunchResolved) {
|
|
// We've already been called, return.
|
|
MOZ_ASSERT(sCreatedFirstContentProcess);
|
|
MOZ_ASSERT(!mPrefSerializer);
|
|
MOZ_ASSERT(mLifecycleState != LifecycleState::LAUNCHING);
|
|
return mLaunchResolvedOk;
|
|
}
|
|
mLaunchResolved = true;
|
|
|
|
// Now that communication with the child is complete, we can cleanup
|
|
// the preference serializer.
|
|
mPrefSerializer = nullptr;
|
|
|
|
const auto launchResumeTS = TimeStamp::Now();
|
|
if (profiler_thread_is_being_profiled_for_markers()) {
|
|
nsPrintfCString marker("Process start%s for %u",
|
|
mIsAPreallocBlocker ? " (immediate)" : "",
|
|
(unsigned int)ChildID());
|
|
PROFILER_MARKER_TEXT(
|
|
mIsAPreallocBlocker ? ProfilerString8View("Process Immediate Launch")
|
|
: ProfilerString8View("Process Launch"),
|
|
DOM, MarkerTiming::Interval(mLaunchTS, launchResumeTS), marker);
|
|
}
|
|
|
|
if (!sCreatedFirstContentProcess) {
|
|
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
|
obs->NotifyObservers(nullptr, "ipc:first-content-process-created", nullptr);
|
|
sCreatedFirstContentProcess = true;
|
|
}
|
|
|
|
mSubprocess->TakeInitialEndpoint().Bind(this);
|
|
|
|
ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
|
|
if (!cpm) {
|
|
NS_WARNING("immediately shutting-down caused by our shutdown");
|
|
ShutDownProcess(SEND_SHUTDOWN_MESSAGE);
|
|
return false;
|
|
}
|
|
cpm->AddContentProcess(this);
|
|
|
|
#ifdef MOZ_CODE_COVERAGE
|
|
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) {
|
|
glean::dom_contentprocess::sync_launch.AccumulateRawDuration(
|
|
TimeStamp::Now() - mLaunchTS);
|
|
} else {
|
|
glean::dom_contentprocess::launch_total.AccumulateRawDuration(
|
|
TimeStamp::Now() - mLaunchTS);
|
|
|
|
glean::dom_contentprocess::launch_mainthread.AccumulateRawDuration(
|
|
(mLaunchYieldTS - mLaunchTS) + (TimeStamp::Now() - launchResumeTS));
|
|
}
|
|
|
|
mLaunchResolvedOk = true;
|
|
return true;
|
|
}
|
|
|
|
static bool IsFileContent(const nsACString& aRemoteType) {
|
|
return aRemoteType == FILE_REMOTE_TYPE;
|
|
}
|
|
|
|
ContentParent::ContentParent(const nsACString& aRemoteType)
|
|
: mSubprocess(new GeckoChildProcessHost(GeckoProcessType_Content,
|
|
IsFileContent(aRemoteType))),
|
|
mLaunchTS(TimeStamp::Now()),
|
|
mLaunchYieldTS(mLaunchTS),
|
|
mActivateTS(mLaunchTS),
|
|
mIsAPreallocBlocker(false),
|
|
mRemoteType(aRemoteType),
|
|
mChildID(mSubprocess->GetChildID()),
|
|
mGeolocationWatchID(-1),
|
|
mThreadsafeHandle(
|
|
new ThreadsafeContentParentHandle(this, mChildID, mRemoteType)),
|
|
mLifecycleState(LifecycleState::LAUNCHING),
|
|
mIsForBrowser(!mRemoteType.IsEmpty()),
|
|
mCalledClose(false),
|
|
mCalledKillHard(false),
|
|
mCreatedPairedMinidumps(false),
|
|
mShutdownPending(false),
|
|
mLaunchResolved(false),
|
|
mLaunchResolvedOk(false),
|
|
mIsRemoteInputEventQueueEnabled(false),
|
|
mIsInputPriorityEventEnabled(false),
|
|
mIsInPool(false),
|
|
mGMPCreated(false),
|
|
mClipboardContentAnalysisCreated(false),
|
|
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
|
mBlockShutdownCalled(false),
|
|
#endif
|
|
mHangMonitorActor(nullptr) {
|
|
MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
|
|
|
|
mRemoteTypeIsolationPrincipal =
|
|
CreateRemoteTypeIsolationPrincipal(aRemoteType);
|
|
|
|
// Insert ourselves into the global linked list of ContentParent objects.
|
|
if (!sContentParents) {
|
|
sContentParents = new LinkedList<ContentParent>();
|
|
}
|
|
sContentParents->insertBack(this);
|
|
|
|
mMessageManager = nsFrameMessageManager::NewProcessMessageManager(true);
|
|
|
|
#if defined(XP_WIN)
|
|
// Request Windows message deferral behavior on our side of the PContent
|
|
// channel. Generally only applies to the situation where we get caught in
|
|
// a deadlock with the plugin process when sending CPOWs.
|
|
GetIPCChannel()->SetChannelFlags(
|
|
MessageChannel::REQUIRE_DEFERRED_MESSAGE_PROTECTION);
|
|
#endif
|
|
|
|
MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
|
|
("CreateSubprocess: ContentParent id=%p mSubprocess id=%p childID=%d",
|
|
this, mSubprocess, mSubprocess->GetChildID()));
|
|
}
|
|
|
|
ContentParent::~ContentParent() {
|
|
if (mSendShutdownTimer) {
|
|
mSendShutdownTimer->Cancel();
|
|
}
|
|
if (mForceKillTimer) {
|
|
mForceKillTimer->Cancel();
|
|
}
|
|
|
|
AssertIsOnMainThread();
|
|
|
|
// Clear the weak reference from the threadsafe handle back to this actor.
|
|
mThreadsafeHandle->mWeakActor = nullptr;
|
|
|
|
if (mIsAPreallocBlocker) {
|
|
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
|
|
("Removing blocker on ContentProcess id=%p childID=%" PRIu64
|
|
" destruction",
|
|
this, (uint64_t)this->ChildID()));
|
|
PreallocatedProcessManager::RemoveBlocker(mRemoteType, this);
|
|
mIsAPreallocBlocker = false;
|
|
}
|
|
|
|
// We should be removed from all these lists in ActorDestroy.
|
|
AssertNotInPool();
|
|
|
|
// Normally mSubprocess is destroyed in ActorDestroy, but that won't
|
|
// happen if the process wasn't launched or if it failed to launch.
|
|
if (mSubprocess) {
|
|
MOZ_LOG(
|
|
ContentParent::GetLog(), LogLevel::Verbose,
|
|
("DestroySubprocess: ContentParent id=%p childID=%" PRIu64
|
|
" mSubprocess id=%p handle "
|
|
"%" PRIuPTR,
|
|
this, (uint64_t)this->ChildID(), mSubprocess,
|
|
mSubprocess ? (uintptr_t)mSubprocess->GetChildProcessHandle() : -1));
|
|
mSubprocess->Destroy();
|
|
}
|
|
}
|
|
|
|
bool ContentParent::InitInternal(ProcessPriority aInitialPriority) {
|
|
// We can't access the locale service after shutdown has started. Since we
|
|
// can't init the process without it, and since we're going to be canceling
|
|
// whatever load attempt that initiated this process creation anyway, just
|
|
// bail out now if shutdown has already started.
|
|
if (PastShutdownPhase(ShutdownPhase::XPCOMShutdown)) {
|
|
return false;
|
|
}
|
|
|
|
XPCOMInitData xpcomInit;
|
|
|
|
MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
|
|
("ContentParent::InitInternal: id=%p, childID=%" PRIu64, (void*)this,
|
|
(uint64_t)this->ChildID()));
|
|
nsCOMPtr<nsIIOService> io(do_GetIOService());
|
|
MOZ_ASSERT(io, "No IO service?");
|
|
DebugOnly<nsresult> rv = io->GetOffline(&xpcomInit.isOffline());
|
|
MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed getting offline?");
|
|
|
|
rv = io->GetConnectivity(&xpcomInit.isConnected());
|
|
MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed getting connectivity?");
|
|
|
|
xpcomInit.captivePortalState() = nsICaptivePortalService::UNKNOWN;
|
|
nsCOMPtr<nsICaptivePortalService> cps =
|
|
do_GetService(NS_CAPTIVEPORTAL_CONTRACTID);
|
|
if (cps) {
|
|
cps->GetState(&xpcomInit.captivePortalState());
|
|
}
|
|
|
|
if (StaticPrefs::fission_processProfileName()) {
|
|
nsCOMPtr<nsIToolkitProfileService> profileSvc =
|
|
do_GetService(NS_PROFILESERVICE_CONTRACTID);
|
|
if (profileSvc) {
|
|
nsCOMPtr<nsIToolkitProfile> currentProfile;
|
|
nsresult rv =
|
|
profileSvc->GetCurrentProfile(getter_AddRefs(currentProfile));
|
|
if (NS_SUCCEEDED(rv) && currentProfile) {
|
|
currentProfile->GetName(mProfile);
|
|
}
|
|
}
|
|
}
|
|
|
|
nsIBidiKeyboard* bidi = nsContentUtils::GetBidiKeyboard();
|
|
|
|
xpcomInit.isLangRTL() = false;
|
|
xpcomInit.haveBidiKeyboards() = false;
|
|
if (bidi) {
|
|
bidi->IsLangRTL(&xpcomInit.isLangRTL());
|
|
bidi->GetHaveBidiKeyboards(&xpcomInit.haveBidiKeyboards());
|
|
}
|
|
|
|
RefPtr<mozSpellChecker> spellChecker(mozSpellChecker::Create());
|
|
MOZ_ASSERT(spellChecker, "No spell checker?");
|
|
|
|
spellChecker->GetDictionaryList(&xpcomInit.dictionaries());
|
|
|
|
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<ReadOnlySharedMemoryHandle> sharedFontListBlocks;
|
|
gfxPlatformFontList::PlatformFontList()->ShareFontListToProcess(
|
|
&sharedFontListBlocks, OtherPid());
|
|
|
|
// Content processes have no permission to access profile directory, so we
|
|
// send the file URL instead.
|
|
auto* sheetCache = GlobalStyleSheetCache::Singleton();
|
|
if (StyleSheet* ucs = sheetCache->GetUserContentSheet()) {
|
|
xpcomInit.userContentSheetURL() = ucs->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);
|
|
}
|
|
|
|
// Must send screen info before send initialData
|
|
ScreenManager& screenManager = ScreenManager::GetSingleton();
|
|
screenManager.CopyScreensToRemote(this);
|
|
|
|
// Send the UA sheet shared memory buffer and the address it is mapped at.
|
|
Maybe<ReadOnlySharedMemoryHandle> sharedUASheetHandle;
|
|
uintptr_t sharedUASheetAddress = sheetCache->GetSharedMemoryAddress();
|
|
|
|
if (ReadOnlySharedMemoryHandle handle = sheetCache->CloneHandle()) {
|
|
sharedUASheetHandle.emplace(std::move(handle));
|
|
} else {
|
|
sharedUASheetAddress = 0;
|
|
}
|
|
|
|
bool isReadyForBackgroundProcessing = false;
|
|
#if defined(XP_WIN)
|
|
RefPtr<DllServices> dllSvc(DllServices::Get());
|
|
isReadyForBackgroundProcessing = dllSvc->IsReadyForBackgroundProcessing();
|
|
#endif
|
|
|
|
xpcomInit.perfStatsMask() = PerfStats::GetCollectionMask();
|
|
|
|
nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
|
|
dns->GetTrrDomain(xpcomInit.trrDomain());
|
|
|
|
nsIDNSService::ResolverMode mode;
|
|
dns->GetCurrentTrrMode(&mode);
|
|
xpcomInit.trrMode() = mode;
|
|
xpcomInit.trrModeFromPref() =
|
|
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);
|
|
|
|
if (mRemoteType != PREALLOC_REMOTE_TYPE) {
|
|
StartRemoteWorkerService();
|
|
}
|
|
|
|
ScriptPreloader::InitContentChild(*this);
|
|
|
|
// Initialize the message manager (and load delayed scripts) now that we
|
|
// have established communications with the child.
|
|
mMessageManager->InitWithCallback(this);
|
|
mMessageManager->SetOsPid(Pid());
|
|
|
|
// Set the subprocess's priority. We do this early on because we're likely
|
|
// /lowering/ the process's CPU and memory priority, which it has inherited
|
|
// from this process.
|
|
//
|
|
// This call can cause us to send IPC messages to the child process, so it
|
|
// must come after the Open() call above.
|
|
ProcessPriorityManager::SetProcessPriority(this, aInitialPriority);
|
|
|
|
// NB: internally, this will send an IPC message to the child
|
|
// process to get it to create the CompositorBridgeChild. This
|
|
// message goes through the regular IPC queue for this
|
|
// channel, so delivery will happen-before any other messages
|
|
// we send. The CompositorBridgeChild must be created before any
|
|
// PBrowsers are created, because they rely on the Compositor
|
|
// already being around. (Creation is async, so can't happen
|
|
// on demand.)
|
|
GPUProcessManager* gpm = GPUProcessManager::Get();
|
|
|
|
Endpoint<PCompositorManagerChild> compositor;
|
|
Endpoint<PImageBridgeChild> imageBridge;
|
|
Endpoint<PVRManagerChild> vrBridge;
|
|
Endpoint<PRemoteDecoderManagerChild> videoManager;
|
|
AutoTArray<uint32_t, 3> namespaces;
|
|
|
|
if (!gpm->CreateContentBridges(OtherEndpointProcInfo(), &compositor,
|
|
&imageBridge, &vrBridge, &videoManager,
|
|
mChildID, &namespaces)) {
|
|
// This can fail if we've already started shutting down the compositor
|
|
// thread. See Bug 1562763 comment 8.
|
|
MOZ_ASSERT(AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdown));
|
|
return false;
|
|
}
|
|
|
|
Unused << SendInitRendering(std::move(compositor), std::move(imageBridge),
|
|
std::move(vrBridge), std::move(videoManager),
|
|
namespaces);
|
|
|
|
gpm->AddListener(this);
|
|
|
|
if (StaticPrefs::media_rdd_process_enabled()) {
|
|
// Ensure the RDD process has been started.
|
|
RDDProcessManager* rdd = RDDProcessManager::Get();
|
|
rdd->LaunchRDDProcess();
|
|
}
|
|
|
|
nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance();
|
|
if (sheetService) {
|
|
// This looks like a lot of work, but in a normal browser session we just
|
|
// send two loads.
|
|
//
|
|
// The URIs of the Gecko and Servo sheets should be the same, so it
|
|
// shouldn't matter which we look at.
|
|
|
|
for (StyleSheet* sheet : *sheetService->AgentStyleSheets()) {
|
|
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_WMF_CDM
|
|
if (!mOriginsListCallback && IsMediaFoundationCDMPlaybackEnabled()) {
|
|
mOriginsListCallback = new OriginsListLoadCallback(this);
|
|
nsCOMPtr<nsIWindowsMediaFoundationCDMOriginsListService> rsService =
|
|
do_GetService("@mozilla.org/media/wmfcdm-origins-list;1");
|
|
if (rsService) {
|
|
rsService->SetCallback(mOriginsListCallback);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#ifdef MOZ_SANDBOX
|
|
bool shouldSandbox = true;
|
|
Maybe<FileDescriptor> brokerFd;
|
|
// XXX: Checking the pref here makes it possible to enable/disable sandboxing
|
|
// during an active session. Currently the pref is only used for testing
|
|
// purpose. If the decision is made to permanently rely on the pref, this
|
|
// should be changed so that it is required to restart firefox for the change
|
|
// of value to take effect. Always send SetProcessSandbox message on macOS.
|
|
# if !defined(XP_MACOSX)
|
|
shouldSandbox = IsContentSandboxEnabled();
|
|
# endif
|
|
|
|
# ifdef XP_LINUX
|
|
if (shouldSandbox) {
|
|
MOZ_ASSERT(!mSandboxBroker);
|
|
bool isFileProcess = mRemoteType == FILE_REMOTE_TYPE;
|
|
UniquePtr<SandboxBroker::Policy> policy =
|
|
sSandboxBrokerPolicyFactory->GetContentPolicy(Pid(), isFileProcess);
|
|
if (policy) {
|
|
brokerFd = Some(FileDescriptor());
|
|
mSandboxBroker =
|
|
SandboxBroker::Create(std::move(policy), Pid(), brokerFd.ref());
|
|
if (!mSandboxBroker) {
|
|
KillHard("SandboxBroker::Create failed");
|
|
return false;
|
|
}
|
|
MOZ_ASSERT(brokerFd.ref().IsValid());
|
|
}
|
|
}
|
|
# endif
|
|
if (shouldSandbox && !SendSetProcessSandbox(brokerFd)) {
|
|
KillHard("SandboxInitFailed");
|
|
}
|
|
#endif
|
|
|
|
// Ensure that the default set of permissions are avaliable in the content
|
|
// process before we try to load any URIs in it.
|
|
//
|
|
// NOTE: All default permissions has to be transmitted to the child process
|
|
// before the blob urls in the for loop below (See Bug 1738713 comment 12).
|
|
EnsurePermissionsByKey(""_ns, ""_ns);
|
|
|
|
{
|
|
nsTArray<BlobURLRegistrationData> registrations;
|
|
BlobURLProtocolHandler::ForEachBlobURL([&](BlobImpl* aBlobImpl,
|
|
nsIPrincipal* aPrincipal,
|
|
const nsCString& aPartitionKey,
|
|
const nsACString& aURI,
|
|
bool aRevoked) {
|
|
// We send all moz-extension Blob URL's to all content processes
|
|
// because content scripts mean that a moz-extension can live in any
|
|
// process. Same thing for system principal Blob URLs. Content Blob
|
|
// URL's are sent for content principals on-demand by
|
|
// AboutToLoadHttpDocumentForChild 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, WrapNotNull(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);
|
|
}
|
|
|
|
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(OtherEndpointProcInfo(), &compositor,
|
|
&imageBridge, &vrBridge, &videoManager,
|
|
mChildID, &namespaces)) {
|
|
MOZ_ASSERT(AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdown));
|
|
return;
|
|
}
|
|
|
|
Unused << SendReinitRendering(std::move(compositor), std::move(imageBridge),
|
|
std::move(vrBridge), std::move(videoManager),
|
|
namespaces);
|
|
}
|
|
|
|
void ContentParent::OnCompositorDeviceReset() {
|
|
Unused << SendReinitRenderingForDeviceReset();
|
|
}
|
|
|
|
void ContentParent::MaybeEnableRemoteInputEventQueue() {
|
|
MOZ_ASSERT(!mIsRemoteInputEventQueueEnabled);
|
|
mIsRemoteInputEventQueueEnabled = true;
|
|
Unused << SendSetInputEventQueueEnabled();
|
|
SetInputPriorityEventEnabled(true);
|
|
}
|
|
|
|
void ContentParent::SetInputPriorityEventEnabled(bool aEnabled) {
|
|
if (!mIsRemoteInputEventQueueEnabled ||
|
|
mIsInputPriorityEventEnabled == aEnabled) {
|
|
return;
|
|
}
|
|
mIsInputPriorityEventEnabled = aEnabled;
|
|
// Send IPC messages to flush the pending events in the input event queue and
|
|
// the normal event queue. See PContent.ipdl for more details.
|
|
Unused << SendSuspendInputEventQueue();
|
|
Unused << SendFlushInputEventQueue();
|
|
Unused << SendResumeInputEventQueue();
|
|
}
|
|
|
|
void ContentParent::OnVarChanged(const GfxVarUpdate& aVar) {
|
|
if (!CanSend()) {
|
|
return;
|
|
}
|
|
Unused << SendVarUpdate(aVar);
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvSetClipboard(
|
|
const IPCTransferable& aTransferable,
|
|
const nsIClipboard::ClipboardType& aWhichClipboard,
|
|
const MaybeDiscarded<WindowContext>& aRequestingWindowContext) {
|
|
// aRequestingPrincipal is allowed to be nullptr here.
|
|
|
|
if (!ValidatePrincipal(aTransferable.dataPrincipal(),
|
|
{ValidatePrincipalOptions::AllowNullPtr,
|
|
ValidatePrincipalOptions::AllowExpanded,
|
|
ValidatePrincipalOptions::AllowSystem})) {
|
|
LogAndAssertFailedPrincipalValidationInfo(aTransferable.dataPrincipal(),
|
|
__func__);
|
|
}
|
|
|
|
nsresult rv;
|
|
nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
|
|
NS_ENSURE_SUCCESS(rv, IPC_OK());
|
|
|
|
nsCOMPtr<nsITransferable> trans =
|
|
do_CreateInstance("@mozilla.org/widget/transferable;1", &rv);
|
|
NS_ENSURE_SUCCESS(rv, IPC_OK());
|
|
trans->Init(nullptr);
|
|
|
|
rv = nsContentUtils::IPCTransferableToTransferable(
|
|
aTransferable, true /* aAddDataFlavor */, trans,
|
|
true /* aFilterUnknownFlavors */);
|
|
NS_ENSURE_SUCCESS(rv, IPC_OK());
|
|
|
|
// OK if this is null
|
|
RefPtr<WindowGlobalParent> window;
|
|
if (!aRequestingWindowContext.IsDiscarded()) {
|
|
window = aRequestingWindowContext.get_canonical();
|
|
}
|
|
clipboard->SetData(trans, nullptr, aWhichClipboard, window);
|
|
return IPC_OK();
|
|
}
|
|
|
|
/* static */ Result<nsCOMPtr<nsITransferable>, nsresult>
|
|
ContentParent::CreateClipboardTransferable(const nsTArray<nsCString>& aTypes) {
|
|
nsresult rv;
|
|
nsCOMPtr<nsITransferable> trans =
|
|
do_CreateInstance("@mozilla.org/widget/transferable;1", &rv);
|
|
if (NS_FAILED(rv)) {
|
|
return Err(rv);
|
|
}
|
|
|
|
MOZ_TRY(trans->Init(nullptr));
|
|
// The private flag is only used to prevent the data from being cached to the
|
|
// disk. The flag is not exported to the IPCDataTransfer object.
|
|
// The flag is set because we are not sure whether the clipboard data is used
|
|
// in a private browsing context. The transferable is only used in this scope,
|
|
// so the cache would not reduce memory consumption anyway.
|
|
trans->SetIsPrivateData(true);
|
|
// Fill out flavors for transferable
|
|
for (uint32_t t = 0; t < aTypes.Length(); t++) {
|
|
MOZ_TRY(trans->AddDataFlavor(aTypes[t].get()));
|
|
}
|
|
|
|
return std::move(trans);
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvGetClipboard(
|
|
nsTArray<nsCString>&& aTypes,
|
|
const nsIClipboard::ClipboardType& aWhichClipboard,
|
|
const MaybeDiscarded<WindowContext>& aRequestingWindowContext,
|
|
IPCTransferableDataOrError* aTransferableDataOrError) {
|
|
nsresult rv;
|
|
// We expect content processes to always pass a non-null window so Content
|
|
// Analysis can analyze it. (if Content Analysis is active)
|
|
// There may be some cases when a window is closing, etc., in
|
|
// which case returning no clipboard content should not be a problem.
|
|
if (aRequestingWindowContext.IsDiscarded()) {
|
|
NS_WARNING(
|
|
"discarded window passed to RecvGetClipboard(); returning no clipboard "
|
|
"content");
|
|
*aTransferableDataOrError = NS_ERROR_FAILURE;
|
|
return IPC_OK();
|
|
}
|
|
if (aRequestingWindowContext.IsNull()) {
|
|
return IPC_FAIL(this, "passed null window to RecvGetClipboard()");
|
|
}
|
|
RefPtr<WindowGlobalParent> window = aRequestingWindowContext.get_canonical();
|
|
// Retrieve clipboard
|
|
nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
|
|
if (NS_FAILED(rv)) {
|
|
*aTransferableDataOrError = rv;
|
|
return IPC_OK();
|
|
}
|
|
|
|
// Create transferable
|
|
auto result = CreateClipboardTransferable(aTypes);
|
|
if (result.isErr()) {
|
|
*aTransferableDataOrError = result.unwrapErr();
|
|
return IPC_OK();
|
|
}
|
|
|
|
// Get data from clipboard
|
|
nsCOMPtr<nsITransferable> trans = result.unwrap();
|
|
rv = clipboard->GetData(trans, aWhichClipboard, window);
|
|
if (NS_FAILED(rv)) {
|
|
*aTransferableDataOrError = rv;
|
|
return IPC_OK();
|
|
}
|
|
|
|
IPCTransferableData transferableData;
|
|
nsContentUtils::TransferableToIPCTransferableData(
|
|
trans, &transferableData, true /* aInSyncMessage */, this);
|
|
*aTransferableDataOrError = std::move(transferableData);
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvEmptyClipboard(
|
|
const nsIClipboard::ClipboardType& aWhichClipboard) {
|
|
nsresult rv;
|
|
nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
|
|
NS_ENSURE_SUCCESS(rv, IPC_OK());
|
|
|
|
clipboard->EmptyClipboard(aWhichClipboard);
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvClipboardHasType(
|
|
nsTArray<nsCString>&& aTypes,
|
|
const nsIClipboard::ClipboardType& aWhichClipboard, bool* aHasType) {
|
|
nsresult rv;
|
|
nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
|
|
NS_ENSURE_SUCCESS(rv, IPC_OK());
|
|
|
|
clipboard->HasDataMatchingFlavors(aTypes, aWhichClipboard, aHasType);
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
namespace {
|
|
static Result<ClipboardReadRequest, nsresult> CreateClipboardReadRequest(
|
|
ContentParent& aContentParent,
|
|
nsIClipboardDataSnapshot& aClipboardDataSnapshot) {
|
|
nsTArray<nsCString> flavors;
|
|
nsresult rv = aClipboardDataSnapshot.GetFlavorList(flavors);
|
|
if (NS_FAILED(rv)) {
|
|
return Err(rv);
|
|
}
|
|
|
|
auto requestParent = MakeNotNull<RefPtr<ClipboardReadRequestParent>>(
|
|
&aContentParent, &aClipboardDataSnapshot);
|
|
|
|
// Open a remote endpoint for our PClipboardReadRequest actor.
|
|
ManagedEndpoint<PClipboardReadRequestChild> childEndpoint =
|
|
aContentParent.OpenPClipboardReadRequestEndpoint(requestParent);
|
|
if (NS_WARN_IF(!childEndpoint.IsValid())) {
|
|
return Err(NS_ERROR_FAILURE);
|
|
}
|
|
|
|
return ClipboardReadRequest(std::move(childEndpoint), std::move(flavors));
|
|
}
|
|
|
|
class ClipboardGetCallback final : public nsIClipboardGetDataSnapshotCallback {
|
|
public:
|
|
ClipboardGetCallback(
|
|
ContentParent* aContentParent,
|
|
ContentParent::GetClipboardDataSnapshotResolver&& aResolver)
|
|
: mContentParent(aContentParent), mResolver(std::move(aResolver)) {}
|
|
|
|
// This object will never be held by a cycle-collected object, so it doesn't
|
|
// need to be cycle-collected despite holding alive cycle-collected objects.
|
|
NS_DECL_ISUPPORTS
|
|
|
|
// nsIClipboardGetDataSnapshotCallback
|
|
NS_IMETHOD OnSuccess(
|
|
nsIClipboardDataSnapshot* aClipboardDataSnapshot) override {
|
|
MOZ_ASSERT(mContentParent);
|
|
MOZ_ASSERT(aClipboardDataSnapshot);
|
|
|
|
auto result =
|
|
CreateClipboardReadRequest(*mContentParent, *aClipboardDataSnapshot);
|
|
if (result.isErr()) {
|
|
return OnError(result.unwrapErr());
|
|
}
|
|
|
|
mResolver(result.unwrap());
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHOD OnError(nsresult aResult) override {
|
|
mResolver(aResult);
|
|
return NS_OK;
|
|
}
|
|
|
|
protected:
|
|
~ClipboardGetCallback() = default;
|
|
|
|
RefPtr<ContentParent> mContentParent;
|
|
ContentParent::GetClipboardDataSnapshotResolver mResolver;
|
|
};
|
|
|
|
NS_IMPL_ISUPPORTS(ClipboardGetCallback, nsIClipboardGetDataSnapshotCallback)
|
|
|
|
} // namespace
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvGetClipboardDataSnapshot(
|
|
nsTArray<nsCString>&& aTypes,
|
|
const nsIClipboard::ClipboardType& aWhichClipboard,
|
|
const MaybeDiscarded<WindowContext>& aRequestingWindowContext,
|
|
mozilla::NotNull<nsIPrincipal*> aRequestingPrincipal,
|
|
GetClipboardDataSnapshotResolver&& aResolver) {
|
|
if (!ValidatePrincipal(aRequestingPrincipal,
|
|
{ValidatePrincipalOptions::AllowSystem,
|
|
ValidatePrincipalOptions::AllowExpanded})) {
|
|
LogAndAssertFailedPrincipalValidationInfo(aRequestingPrincipal, __func__);
|
|
}
|
|
|
|
// If the requesting context has been discarded, cancel the paste.
|
|
if (aRequestingWindowContext.IsDiscarded()) {
|
|
aResolver(NS_ERROR_NOT_AVAILABLE);
|
|
return IPC_OK();
|
|
}
|
|
|
|
RefPtr<WindowGlobalParent> requestingWindow =
|
|
aRequestingWindowContext.get_canonical();
|
|
if (requestingWindow && requestingWindow->GetContentParent() != this) {
|
|
return IPC_FAIL(
|
|
this, "attempt to paste into WindowContext loaded in another process");
|
|
}
|
|
|
|
nsresult rv;
|
|
// Retrieve clipboard
|
|
nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
|
|
if (NS_FAILED(rv)) {
|
|
aResolver(rv);
|
|
return IPC_OK();
|
|
}
|
|
|
|
auto callback = MakeRefPtr<ClipboardGetCallback>(this, std::move(aResolver));
|
|
rv = clipboard->GetDataSnapshot(aTypes, aWhichClipboard, requestingWindow,
|
|
aRequestingPrincipal, callback);
|
|
if (NS_FAILED(rv)) {
|
|
callback->OnError(rv);
|
|
return IPC_OK();
|
|
}
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvGetClipboardDataSnapshotSync(
|
|
nsTArray<nsCString>&& aTypes,
|
|
const nsIClipboard::ClipboardType& aWhichClipboard,
|
|
const MaybeDiscarded<WindowContext>& aRequestingWindowContext,
|
|
ClipboardReadRequestOrError* aRequestOrError) {
|
|
// If the requesting context has been discarded, cancel the paste.
|
|
if (aRequestingWindowContext.IsDiscarded()) {
|
|
*aRequestOrError = NS_ERROR_FAILURE;
|
|
return IPC_OK();
|
|
}
|
|
|
|
RefPtr<WindowGlobalParent> requestingWindow =
|
|
aRequestingWindowContext.get_canonical();
|
|
if (requestingWindow && requestingWindow->GetContentParent() != this) {
|
|
return IPC_FAIL(
|
|
this, "attempt to paste into WindowContext loaded in another process");
|
|
}
|
|
|
|
nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID));
|
|
if (!clipboard) {
|
|
*aRequestOrError = NS_ERROR_FAILURE;
|
|
return IPC_OK();
|
|
}
|
|
|
|
nsCOMPtr<nsIClipboardDataSnapshot> clipboardDataSnapshot;
|
|
nsresult rv =
|
|
clipboard->GetDataSnapshotSync(aTypes, aWhichClipboard, requestingWindow,
|
|
getter_AddRefs(clipboardDataSnapshot));
|
|
if (NS_FAILED(rv)) {
|
|
*aRequestOrError = rv;
|
|
return IPC_OK();
|
|
}
|
|
|
|
auto result = CreateClipboardReadRequest(*this, *clipboardDataSnapshot);
|
|
if (result.isErr()) {
|
|
*aRequestOrError = result.unwrapErr();
|
|
return IPC_OK();
|
|
}
|
|
|
|
*aRequestOrError = result.unwrap();
|
|
return IPC_OK();
|
|
}
|
|
|
|
already_AddRefed<PClipboardWriteRequestParent>
|
|
ContentParent::AllocPClipboardWriteRequestParent(
|
|
const nsIClipboard::ClipboardType& aClipboardType,
|
|
const MaybeDiscarded<WindowContext>& aSettingWindowContext) {
|
|
WindowContext* settingWindowContext = nullptr;
|
|
if (!aSettingWindowContext.IsDiscarded()) {
|
|
settingWindowContext = aSettingWindowContext.get();
|
|
}
|
|
RefPtr<ClipboardWriteRequestParent> request =
|
|
MakeAndAddRef<ClipboardWriteRequestParent>(this);
|
|
request->Init(aClipboardType, settingWindowContext);
|
|
return request.forget();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvGetIconForExtension(
|
|
const nsACString& aFileExt, const uint32_t& aIconSize,
|
|
nsTArray<uint8_t>* bits) {
|
|
#ifdef MOZ_WIDGET_ANDROID
|
|
NS_ASSERTION(AndroidBridge::Bridge() != nullptr,
|
|
"AndroidBridge is not available");
|
|
if (AndroidBridge::Bridge() == nullptr) {
|
|
// Do not fail - just no icon will be shown
|
|
return IPC_OK();
|
|
}
|
|
|
|
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 id=%p childID=%" PRIu64 ": Removing Blocker for %s",
|
|
this, (uint64_t)this->ChildID(), mRemoteType.get()));
|
|
PreallocatedProcessManager::RemoveBlocker(mRemoteType, this);
|
|
mIsAPreallocBlocker = false;
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
already_AddRefed<nsDocShellLoadState> ContentParent::TakePendingLoadStateForId(
|
|
uint64_t aLoadIdentifier) {
|
|
return mPendingLoadStates.Extract(aLoadIdentifier).valueOr(nullptr).forget();
|
|
}
|
|
|
|
void ContentParent::StorePendingLoadState(nsDocShellLoadState* aLoadState) {
|
|
MOZ_DIAGNOSTIC_ASSERT(
|
|
!mPendingLoadStates.Contains(aLoadState->GetLoadIdentifier()),
|
|
"The same nsDocShellLoadState was sent to the same content process "
|
|
"twice? This will mess with cross-process tracking of loads");
|
|
mPendingLoadStates.InsertOrUpdate(aLoadState->GetLoadIdentifier(),
|
|
aLoadState);
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvCleanupPendingLoadState(
|
|
uint64_t aLoadIdentifier) {
|
|
mPendingLoadStates.Remove(aLoadIdentifier);
|
|
return IPC_OK();
|
|
}
|
|
|
|
// 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
|
|
// This will make our process unusable for normal content, so we need to
|
|
// ensure we won't get re-used by GetUsedBrowserProcess as we have not yet
|
|
// done MarkAsDead.
|
|
PreallocatedProcessManager::Erase(this);
|
|
{
|
|
RecursiveMutexAutoLock lock(mThreadsafeHandle->mMutex);
|
|
mThreadsafeHandle->mShutdownStarted = true;
|
|
}
|
|
// Our real shutdown has not yet started. Just notify the impending
|
|
// shutdown and eventually cancel content JS.
|
|
SignalImpendingShutdownToContentJS();
|
|
|
|
if (sAppShutdownConfirmedClient) {
|
|
Unused << sAppShutdownConfirmedClient->RemoveBlocker(this);
|
|
}
|
|
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
|
mBlockShutdownCalled = false;
|
|
#endif
|
|
return NS_OK;
|
|
}
|
|
|
|
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
|
// We register two final shutdown blockers and both would call us, but if
|
|
// things go well we will unregister both as (delayed) reaction to the first
|
|
// call we get and thus never receive a second call. Thus we believe that we
|
|
// will get called only once except for quit-application-granted, which is
|
|
// handled above.
|
|
MOZ_ASSERT(!mBlockShutdownCalled);
|
|
mBlockShutdownCalled = true;
|
|
#endif
|
|
|
|
if (CanSend()) {
|
|
MaybeLogBlockShutdownDiagnostics(this, "BlockShutdown: CanSend.", __FILE__,
|
|
__LINE__);
|
|
|
|
// Make sure that our process will get scheduled.
|
|
ProcessPriorityManager::SetProcessPriority(this,
|
|
PROCESS_PRIORITY_FOREGROUND);
|
|
// The normal shutdown sequence is to send a shutdown message
|
|
// to the child and then just wait for ActorDestroy which will
|
|
// cleanup everything and remove our blockers.
|
|
if (!ShutDownProcess(SEND_SHUTDOWN_MESSAGE)) {
|
|
KillHard("Failed to send Shutdown message. Destroying the process...");
|
|
return NS_OK;
|
|
}
|
|
} else if (IsLaunching()) {
|
|
MaybeLogBlockShutdownDiagnostics(
|
|
this, "BlockShutdown: !CanSend && IsLaunching.", __FILE__, __LINE__);
|
|
|
|
// If we get here while we are launching, we must wait for the child to
|
|
// be able to react on our commands. Mark this process as dead. This
|
|
// will make bail out LaunchSubprocessResolve and kick off the normal
|
|
// shutdown sequence.
|
|
MarkAsDead();
|
|
} else {
|
|
MOZ_ASSERT(IsDead());
|
|
if (!IsDead()) {
|
|
MaybeLogBlockShutdownDiagnostics(
|
|
this, "BlockShutdown: !!! !CanSend && !IsLaunching && !IsDead !!!",
|
|
__FILE__, __LINE__);
|
|
} else {
|
|
MaybeLogBlockShutdownDiagnostics(
|
|
this, "BlockShutdown: !CanSend && !IsLaunching && IsDead.", __FILE__,
|
|
__LINE__);
|
|
}
|
|
// Nothing left we can do. We must assume that we race with an ongoing
|
|
// process shutdown, such that we can expect our shutdown blockers to be
|
|
// removed normally.
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ContentParent::GetName(nsAString& aName) {
|
|
aName.AssignLiteral("ContentParent:");
|
|
aName.AppendPrintf(" id=%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);
|
|
}
|
|
}
|
|
if (!AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
|
|
rv = svc->GetAppShutdownConfirmed(getter_AddRefs(client));
|
|
if (NS_SUCCEEDED(rv)) {
|
|
sAppShutdownConfirmedClient = client.forget();
|
|
ClearOnShutdown(&sAppShutdownConfirmedClient);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ContentParent::AddShutdownBlockers() {
|
|
InitShutdownClients();
|
|
MOZ_ASSERT(sXPCOMShutdownClient);
|
|
MOZ_ASSERT(sProfileBeforeChangeClient);
|
|
|
|
if (sXPCOMShutdownClient) {
|
|
sXPCOMShutdownClient->AddBlocker(
|
|
this, NS_LITERAL_STRING_FROM_CSTRING(__FILE__), __LINE__, u""_ns);
|
|
}
|
|
if (sProfileBeforeChangeClient) {
|
|
sProfileBeforeChangeClient->AddBlocker(
|
|
this, NS_LITERAL_STRING_FROM_CSTRING(__FILE__), __LINE__, u""_ns);
|
|
}
|
|
if (sAppShutdownConfirmedClient) {
|
|
sAppShutdownConfirmedClient->AddBlocker(
|
|
this, NS_LITERAL_STRING_FROM_CSTRING(__FILE__), __LINE__, u""_ns);
|
|
}
|
|
}
|
|
|
|
void ContentParent::RemoveShutdownBlockers() {
|
|
MOZ_ASSERT(sXPCOMShutdownClient);
|
|
MOZ_ASSERT(sProfileBeforeChangeClient);
|
|
|
|
MaybeLogBlockShutdownDiagnostics(this, "RemoveShutdownBlockers", __FILE__,
|
|
__LINE__);
|
|
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
|
mBlockShutdownCalled = false;
|
|
#endif
|
|
|
|
if (sXPCOMShutdownClient) {
|
|
Unused << sXPCOMShutdownClient->RemoveBlocker(this);
|
|
}
|
|
if (sProfileBeforeChangeClient) {
|
|
Unused << sProfileBeforeChangeClient->RemoveBlocker(this);
|
|
}
|
|
if (sAppShutdownConfirmedClient) {
|
|
Unused << sAppShutdownConfirmedClient->RemoveBlocker(this);
|
|
}
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ContentParent::Observe(nsISupports* aSubject, const char* aTopic,
|
|
const char16_t* aData) {
|
|
if (IsDead() || !mSubprocess) {
|
|
return NS_OK;
|
|
}
|
|
|
|
if (!strcmp(aTopic, "nsPref:changed")) {
|
|
// We know prefs are ASCII here.
|
|
NS_LossyConvertUTF16toASCII strData(aData);
|
|
|
|
Pref pref(strData, /* isLocked */ false,
|
|
/* isSanitized */ false, Nothing(), Nothing());
|
|
|
|
Preferences::GetPreference(&pref, GeckoProcessType_Content,
|
|
GetRemoteType());
|
|
|
|
// This check is a bit of a hack. We want to avoid sending excessive
|
|
// preference updates to subprocesses for performance reasons, but we
|
|
// currently don't have a great mechanism for doing so. (See Bug 1819714)
|
|
// We're going to hijack the sanitization mechanism to accomplish our goal
|
|
// but it imposes the following complications:
|
|
// 1) It doesn't avoid sending anything to other (non-web-content)
|
|
// subprocesses so we're not getting any perf gains there
|
|
// 2) It confuses the subprocesses w.r.t. sanitization. The point of
|
|
// sending a preference update of a sanitized preference is so that
|
|
// content process knows when it's asked to resolve a sanitized
|
|
// preference, and it can send telemetry and/or crash. With this
|
|
// change, a sanitized pref that is created during the browser session
|
|
// will not be sent to the content process, and therefore the content
|
|
// process won't know it should telemetry/crash on access - it'll just
|
|
// silently fail to resolve it. After browser restart, the sanitized
|
|
// pref will be populated into the content process via the shared pref
|
|
// map and _then_ if it is accessed, the content process will crash.
|
|
// We're seeing good telemetry/crash rates right now, so we're okay with
|
|
// this limitation.
|
|
if (pref.isSanitized()) {
|
|
return NS_OK;
|
|
}
|
|
|
|
if (IsInitialized()) {
|
|
MOZ_ASSERT(mQueuedPrefs.IsEmpty());
|
|
if (!SendPreferenceUpdate(pref)) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
} else {
|
|
MOZ_ASSERT(!IsDead());
|
|
mQueuedPrefs.AppendElement(pref);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
if (!IsAlive()) {
|
|
return NS_OK;
|
|
}
|
|
|
|
// listening for memory pressure event
|
|
if (!strcmp(aTopic, "memory-pressure")) {
|
|
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;
|
|
}
|
|
} 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(
|
|
nsAccessibilityService::GetActiveCacheDomains());
|
|
} 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);
|
|
MOZ_ASSERT(mCookieInContentListCache.IsEmpty());
|
|
|
|
if (action == nsICookieNotification::COOKIES_BATCH_DELETED) {
|
|
nsCOMPtr<nsIArray> cookieList;
|
|
DebugOnly<nsresult> rv =
|
|
notification->GetBatchDeletedCookies(getter_AddRefs(cookieList));
|
|
NS_ASSERTION(NS_SUCCEEDED(rv) && cookieList, "couldn't get cookie list");
|
|
cs->RemoveBatchDeletedCookies(cookieList);
|
|
return NS_OK;
|
|
}
|
|
|
|
if (action == nsICookieNotification::ALL_COOKIES_CLEARED) {
|
|
cs->RemoveAll();
|
|
return NS_OK;
|
|
}
|
|
|
|
// Do not push these cookie updates to the same process they originated
|
|
// from.
|
|
if (cs->ProcessingCookie()) {
|
|
return NS_OK;
|
|
}
|
|
|
|
nsCOMPtr<nsICookie> xpcCookie;
|
|
nsresult rv = notification->GetCookie(getter_AddRefs(xpcCookie));
|
|
NS_ASSERTION(NS_SUCCEEDED(rv) && xpcCookie, "couldn't get cookie");
|
|
|
|
// only broadcast the cookie change to content processes that need it
|
|
const Cookie& cookie = xpcCookie->AsCookie();
|
|
|
|
// do not send cookie if content process does not have similar cookie
|
|
if (!cs->ContentProcessHasCookie(cookie)) {
|
|
return NS_OK;
|
|
}
|
|
|
|
nsID* operationID = nullptr;
|
|
rv = notification->GetOperationID(&operationID);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return NS_OK;
|
|
}
|
|
|
|
if (action == nsICookieNotification::COOKIE_DELETED) {
|
|
cs->RemoveCookie(cookie, operationID);
|
|
} else if (action == nsICookieNotification::COOKIE_ADDED ||
|
|
action == nsICookieNotification::COOKIE_CHANGED) {
|
|
cs->AddCookie(cookie, operationID);
|
|
}
|
|
} 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);
|
|
nsIDNSService::ResolverMode modeFromPref =
|
|
static_cast<nsIDNSService::ResolverMode>(
|
|
StaticPrefs::network_trr_mode());
|
|
if (modeFromPref > nsIDNSService::MODE_TRROFF) {
|
|
modeFromPref = nsIDNSService::MODE_TRROFF;
|
|
}
|
|
Unused << SendSetTRRMode(mode, modeFromPref);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
void ContentParent::UpdateNetworkLinkType() {
|
|
nsresult rv;
|
|
nsCOMPtr<nsINetworkLinkService> nls =
|
|
do_GetService(NS_NETWORK_LINK_SERVICE_CONTRACTID, &rv);
|
|
if (NS_FAILED(rv)) {
|
|
return;
|
|
}
|
|
|
|
uint32_t linkType = nsINetworkLinkService::LINK_TYPE_UNKNOWN;
|
|
rv = nls->GetLinkType(&linkType);
|
|
if (NS_FAILED(rv)) {
|
|
return;
|
|
}
|
|
|
|
Unused << SendNetworkLinkTypeChange(linkType);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
ContentParent::GetInterface(const nsIID& aIID, void** aResult) {
|
|
NS_ENSURE_ARG_POINTER(aResult);
|
|
|
|
if (aIID.Equals(NS_GET_IID(nsIMessageSender))) {
|
|
nsCOMPtr<nsIMessageSender> mm = GetMessageManager();
|
|
mm.forget(aResult);
|
|
return NS_OK;
|
|
}
|
|
|
|
return NS_NOINTERFACE;
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvInitBackground(
|
|
Endpoint<PBackgroundStarterParent>&& aEndpoint) {
|
|
if (!BackgroundParent::AllocStarter(this, std::move(aEndpoint))) {
|
|
NS_WARNING("BackgroundParent::Alloc failed");
|
|
}
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
bool ContentParent::CanOpenBrowser(const IPCTabContext& aContext) {
|
|
// (PopupIPCTabContext lets the child process prove that it has access to
|
|
// the app it's trying to open.)
|
|
// On e10s we also allow UnsafeTabContext to allow service workers to open
|
|
// windows. This is enforced in MaybeInvalidTabContext.
|
|
if (aContext.type() != IPCTabContext::TPopupIPCTabContext) {
|
|
MOZ_CRASH_UNLESS_FUZZING(
|
|
"Unexpected IPCTabContext type. Aborting AllocPBrowserParent.");
|
|
return false;
|
|
}
|
|
|
|
if (aContext.type() == IPCTabContext::TPopupIPCTabContext) {
|
|
const PopupIPCTabContext& popupContext = aContext.get_PopupIPCTabContext();
|
|
|
|
auto* opener = BrowserParent::GetFrom(popupContext.opener().AsParent());
|
|
if (!opener) {
|
|
MOZ_CRASH_UNLESS_FUZZING(
|
|
"Got null opener from child; aborting AllocPBrowserParent.");
|
|
return false;
|
|
}
|
|
}
|
|
|
|
MaybeInvalidTabContext tc(aContext);
|
|
if (!tc.IsValid()) {
|
|
NS_ERROR(nsPrintfCString("Child passed us an invalid TabContext. (%s) "
|
|
"Aborting AllocPBrowserParent.",
|
|
tc.GetInvalidReason())
|
|
.get());
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool CloneIsLegal(ContentParent* aCp, CanonicalBrowsingContext& aSource,
|
|
CanonicalBrowsingContext& aTarget) {
|
|
// Source and target must be in the same BCG
|
|
if (NS_WARN_IF(aSource.Group() != aTarget.Group())) {
|
|
return false;
|
|
}
|
|
// The source and target must be in different toplevel <browser>s
|
|
if (NS_WARN_IF(aSource.Top() == aTarget.Top())) {
|
|
return false;
|
|
}
|
|
|
|
// Neither source nor target must be toplevel.
|
|
if (NS_WARN_IF(aSource.IsTop()) || NS_WARN_IF(aTarget.IsTop())) {
|
|
return false;
|
|
}
|
|
|
|
// Both should be embedded by the same process.
|
|
auto* sourceEmbedder = aSource.GetParentWindowContext();
|
|
if (NS_WARN_IF(!sourceEmbedder) ||
|
|
NS_WARN_IF(sourceEmbedder->GetContentParent() != aCp)) {
|
|
return false;
|
|
}
|
|
|
|
auto* targetEmbedder = aTarget.GetParentWindowContext();
|
|
if (NS_WARN_IF(!targetEmbedder) ||
|
|
NS_WARN_IF(targetEmbedder->GetContentParent() != aCp)) {
|
|
return false;
|
|
}
|
|
|
|
// All seems sane.
|
|
return true;
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvCloneDocumentTreeInto(
|
|
const MaybeDiscarded<BrowsingContext>& aSource,
|
|
const MaybeDiscarded<BrowsingContext>& aTarget, PrintData&& aPrintData) {
|
|
if (aSource.IsNullOrDiscarded() || aTarget.IsNullOrDiscarded()) {
|
|
return IPC_OK();
|
|
}
|
|
|
|
if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
|
|
// All existing processes have potentially been slated for removal already,
|
|
// such that any subsequent call to GetNewOrUsedLaunchingBrowserProcess
|
|
// (normally supposed to find an existing process here) will try to create
|
|
// a new process (but fail) that nobody would ever really use. Let's avoid
|
|
// this together with the expensive CloneDocumentTreeInto operation.
|
|
return IPC_OK();
|
|
}
|
|
|
|
RefPtr source = aSource.get_canonical();
|
|
RefPtr 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(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);
|
|
|
|
// The creation of PBrowser was triggered from content process through
|
|
// window.open().
|
|
// We need to register remote frame with the child generated tab id.
|
|
ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
|
|
if (!cpm || !cpm->RegisterRemoteFrame(parent)) {
|
|
return IPC_FAIL(this, "RegisterRemoteFrame Failed");
|
|
}
|
|
|
|
// Bind the created BrowserParent to IPC to actually link the actor.
|
|
if (NS_WARN_IF(!BindPBrowserEndpoint(std::move(aBrowserEp), parent))) {
|
|
return IPC_FAIL(this, "BindPBrowserEndpoint failed");
|
|
}
|
|
|
|
if (NS_WARN_IF(!parent->BindPWindowGlobalEndpoint(std::move(aWindowEp),
|
|
initialWindow))) {
|
|
return IPC_FAIL(this, "BindPWindowGlobalEndpoint failed");
|
|
}
|
|
|
|
browsingContext->SetCurrentBrowserParent(parent);
|
|
|
|
initialWindow->Init();
|
|
|
|
// When enabling input event prioritization, input events may preempt other
|
|
// normal priority IPC messages. To prevent the input events preempt
|
|
// PBrowserConstructor, we use an IPC 'RemoteIsReadyToHandleInputEvents' to
|
|
// notify parent that BrowserChild is created. In this case, PBrowser is
|
|
// initiated from content so that we can set BrowserParent as ready to handle
|
|
// input
|
|
parent->SetReadyToHandleInputEvents();
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::PRemoteSpellcheckEngineParent*
|
|
ContentParent::AllocPRemoteSpellcheckEngineParent() {
|
|
mozilla::RemoteSpellcheckEngineParent* parent =
|
|
new mozilla::RemoteSpellcheckEngineParent();
|
|
return parent;
|
|
}
|
|
|
|
bool ContentParent::DeallocPRemoteSpellcheckEngineParent(
|
|
PRemoteSpellcheckEngineParent* parent) {
|
|
delete parent;
|
|
return true;
|
|
}
|
|
|
|
/* static */
|
|
void ContentParent::SendShutdownTimerCallback(nsITimer* aTimer,
|
|
void* aClosure) {
|
|
auto* self = static_cast<ContentParent*>(aClosure);
|
|
self->AsyncSendShutDownMessage();
|
|
}
|
|
|
|
/* static */
|
|
void ContentParent::ForceKillTimerCallback(nsITimer* aTimer, void* aClosure) {
|
|
// We don't want to time out the content process during XPCShell tests. This
|
|
// is the easiest way to ensure that.
|
|
if (PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR")) {
|
|
return;
|
|
}
|
|
|
|
auto* self = static_cast<ContentParent*>(aClosure);
|
|
self->KillHard("ShutDownKill");
|
|
}
|
|
|
|
void ContentParent::GeneratePairedMinidump(const char* aReason) {
|
|
// We're about to kill the child process associated with this content.
|
|
// Something has gone wrong to get us here, so we generate a minidump
|
|
// of the parent and child for submission to the crash server unless we're
|
|
// already shutting down.
|
|
if (mCrashReporter &&
|
|
!AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed) &&
|
|
StaticPrefs::dom_ipc_tabs_createKillHardCrashReports_AtStartup()) {
|
|
// GeneratePairedMinidump creates two minidumps for us - the main
|
|
// one is for the content process we're about to kill, and the other
|
|
// one is for the main browser process. That second one is the extra
|
|
// minidump tagging along, so we have to tell the crash reporter that
|
|
// it exists and is being appended.
|
|
nsAutoCString additionalDumps("browser");
|
|
mCrashReporter->AddAnnotationNSCString(
|
|
CrashReporter::Annotation::additional_minidumps, additionalDumps);
|
|
nsDependentCString reason(aReason);
|
|
mCrashReporter->AddAnnotationNSCString(
|
|
CrashReporter::Annotation::ipc_channel_error, reason);
|
|
|
|
// Generate the report and insert into the queue for submittal.
|
|
if (mCrashReporter->GenerateMinidumpAndPair(mSubprocess, "browser"_ns)) {
|
|
mCrashReporter->FinalizeCrashReport();
|
|
mCreatedPairedMinidumps = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ContentParent::HandleOrphanedMinidump(nsString* aDumpId) {
|
|
if (CrashReporter::FinalizeOrphanedMinidump(
|
|
OtherPid(), GeckoProcessType_Content, aDumpId)) {
|
|
CrashReporterHost::RecordCrash(GeckoProcessType_Content,
|
|
nsICrashService::CRASH_TYPE_CRASH, *aDumpId);
|
|
} else {
|
|
NS_WARNING(nsPrintfCString("content process childID = %d pid = %" PRIPID
|
|
" crashed without leaving a minidump behind",
|
|
OtherChildID(), OtherPid())
|
|
.get());
|
|
}
|
|
}
|
|
|
|
// WARNING: aReason appears in telemetry, so any new value passed in requires
|
|
// data review.
|
|
void ContentParent::KillHard(const char* aReason) {
|
|
AUTO_PROFILER_LABEL("ContentParent::KillHard", OTHER);
|
|
|
|
// On Windows, calling KillHard multiple times causes problems - the
|
|
// process handle becomes invalid on the first call, causing a second call
|
|
// to crash our process - more details in bug 890840.
|
|
if (mCalledKillHard) {
|
|
return;
|
|
}
|
|
mCalledKillHard = true;
|
|
if (mSendShutdownTimer) {
|
|
mSendShutdownTimer->Cancel();
|
|
mSendShutdownTimer = nullptr;
|
|
}
|
|
if (mForceKillTimer) {
|
|
mForceKillTimer->Cancel();
|
|
mForceKillTimer = nullptr;
|
|
}
|
|
|
|
RemoveShutdownBlockers();
|
|
nsCString reason = nsDependentCString(aReason);
|
|
|
|
// If we find mIsNotifiedShutdownSuccess there is no reason to blame this
|
|
// content process, most probably our parent process is just slow in
|
|
// processing its own main thread queue.
|
|
if (!mIsNotifiedShutdownSuccess) {
|
|
GeneratePairedMinidump(aReason);
|
|
} else {
|
|
reason = nsDependentCString("KillHard after IsNotifiedShutdownSuccess.");
|
|
}
|
|
glean::subprocess::kill_hard.Get(reason).Add(1);
|
|
|
|
ProcessHandle otherProcessHandle;
|
|
if (!base::OpenProcessHandle(OtherPid(), &otherProcessHandle)) {
|
|
NS_ERROR("Failed to open child process when attempting kill.");
|
|
if (CanSend()) {
|
|
GetIPCChannel()->InduceConnectionError();
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (!KillProcess(otherProcessHandle, base::PROCESS_END_KILLED_BY_USER)) {
|
|
if (mCrashReporter) {
|
|
mCrashReporter->DeleteCrashReport();
|
|
}
|
|
NS_WARNING("failed to kill subprocess!");
|
|
}
|
|
|
|
if (mSubprocess) {
|
|
MOZ_LOG(
|
|
ContentParent::GetLog(), LogLevel::Verbose,
|
|
("KillHard Subprocess(%s): ContentParent id=%p mSubprocess id=%p "
|
|
"handle "
|
|
"%" PRIuPTR,
|
|
aReason, this, mSubprocess,
|
|
mSubprocess ? (uintptr_t)mSubprocess->GetChildProcessHandle() : -1));
|
|
mSubprocess->SetAlreadyDead();
|
|
}
|
|
|
|
// After we've killed the remote process, also ensure we induce a connection
|
|
// error in the IPC channel to immediately stop all IPC communication on this
|
|
// channel.
|
|
if (CanSend()) {
|
|
GetIPCChannel()->InduceConnectionError();
|
|
}
|
|
|
|
// EnsureProcessTerminated has responsibilty for closing otherProcessHandle.
|
|
XRE_GetAsyncIOEventTarget()->Dispatch(
|
|
NewRunnableFunction("EnsureProcessTerminatedRunnable",
|
|
&ProcessWatcher::EnsureProcessTerminated,
|
|
otherProcessHandle, /*force=*/true));
|
|
}
|
|
|
|
void ContentParent::FriendlyName(nsAString& aName, bool aAnonymize) {
|
|
aName.Truncate();
|
|
if (mIsForBrowser) {
|
|
aName.AssignLiteral("Browser");
|
|
} else if (aAnonymize) {
|
|
aName.AssignLiteral("<anonymized-name>");
|
|
} else {
|
|
aName.AssignLiteral("???");
|
|
}
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvInitCrashReporter(
|
|
const CrashReporter::CrashReporterInitArgs& aInitArgs) {
|
|
mCrashReporter = MakeUnique<CrashReporterHost>(GeckoProcessType_Content,
|
|
OtherPid(), aInitArgs);
|
|
return IPC_OK();
|
|
}
|
|
|
|
hal_sandbox::PHalParent* ContentParent::AllocPHalParent() {
|
|
return hal_sandbox::CreateHalParent();
|
|
}
|
|
|
|
bool ContentParent::DeallocPHalParent(hal_sandbox::PHalParent* aHal) {
|
|
delete aHal;
|
|
return true;
|
|
}
|
|
|
|
devtools::PHeapSnapshotTempFileHelperParent*
|
|
ContentParent::AllocPHeapSnapshotTempFileHelperParent() {
|
|
return devtools::HeapSnapshotTempFileHelperParent::Create();
|
|
}
|
|
|
|
bool ContentParent::DeallocPHeapSnapshotTempFileHelperParent(
|
|
devtools::PHeapSnapshotTempFileHelperParent* aHeapSnapshotHelper) {
|
|
delete aHeapSnapshotHelper;
|
|
return true;
|
|
}
|
|
|
|
bool ContentParent::SendRequestMemoryReport(
|
|
const uint32_t& aGeneration, const bool& aAnonymize,
|
|
const bool& aMinimizeMemoryUsage, const Maybe<FileDescriptor>& aDMDFile) {
|
|
// This automatically cancels the previous request.
|
|
mMemoryReportRequest = MakeUnique<MemoryReportRequestHost>(aGeneration);
|
|
// 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);
|
|
}
|
|
|
|
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) {
|
|
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) {
|
|
BrowsingContext* context = aContext.IsDiscarded() ? nullptr : aContext.get();
|
|
if (!static_cast<ExternalHelperAppParent*>(actor)->Init(
|
|
loadInfoArgs, aMimeContentType, aForceSave, aReferrer, context)) {
|
|
return IPC_FAIL(this, "Init failed.");
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
already_AddRefed<PHandlerServiceParent>
|
|
ContentParent::AllocPHandlerServiceParent() {
|
|
RefPtr<HandlerServiceParent> actor = new HandlerServiceParent();
|
|
return actor.forget();
|
|
}
|
|
|
|
media::PMediaParent* ContentParent::AllocPMediaParent() {
|
|
return media::AllocPMediaParent();
|
|
}
|
|
|
|
bool ContentParent::DeallocPMediaParent(media::PMediaParent* aActor) {
|
|
return media::DeallocPMediaParent(aActor);
|
|
}
|
|
|
|
#ifdef MOZ_WEBSPEECH
|
|
already_AddRefed<PSpeechSynthesisParent>
|
|
ContentParent::AllocPSpeechSynthesisParent() {
|
|
if (!StaticPrefs::media_webspeech_synth_enabled()) {
|
|
return nullptr;
|
|
}
|
|
RefPtr<SpeechSynthesisParent> actor = new SpeechSynthesisParent();
|
|
return actor.forget();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvPSpeechSynthesisConstructor(
|
|
PSpeechSynthesisParent* aActor) {
|
|
if (!static_cast<SpeechSynthesisParent*>(aActor)->SendInit()) {
|
|
return IPC_FAIL(this, "SpeechSynthesisParent::SendInit failed.");
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
#endif
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvStartVisitedQueries(
|
|
const nsTArray<RefPtr<nsIURI>>& aUris) {
|
|
nsCOMPtr<IHistory> history = components::History::Service();
|
|
if (!history) {
|
|
return IPC_OK();
|
|
}
|
|
for (const auto& uri : aUris) {
|
|
if (NS_WARN_IF(!uri)) {
|
|
continue;
|
|
}
|
|
history->ScheduleVisitedQuery(uri, this);
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvSetURITitle(nsIURI* uri,
|
|
const nsAString& title) {
|
|
if (!uri) {
|
|
return IPC_FAIL(this, "uri must not be null.");
|
|
}
|
|
nsCOMPtr<IHistory> history = components::History::Service();
|
|
if (history) {
|
|
history->SetURITitle(uri, title);
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvIsSecureURI(
|
|
nsIURI* aURI, const OriginAttributes& aOriginAttributes,
|
|
bool* aIsSecureURI) {
|
|
nsCOMPtr<nsISiteSecurityService> sss(do_GetService(NS_SSSERVICE_CONTRACTID));
|
|
if (!sss) {
|
|
return IPC_FAIL(this, "Failed to get nsISiteSecurityService.");
|
|
}
|
|
if (!aURI) {
|
|
return IPC_FAIL(this, "aURI must not be null.");
|
|
}
|
|
nsresult rv = sss->IsSecureURI(aURI, aOriginAttributes, aIsSecureURI);
|
|
if (NS_FAILED(rv)) {
|
|
return IPC_FAIL(this, "IsSecureURI failed.");
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvAccumulateMixedContentHSTS(
|
|
nsIURI* aURI, const bool& aActive,
|
|
const OriginAttributes& aOriginAttributes) {
|
|
if (!aURI) {
|
|
return IPC_FAIL(this, "aURI must not be null.");
|
|
}
|
|
nsMixedContentBlocker::AccumulateMixedContentHSTS(aURI, aActive,
|
|
aOriginAttributes);
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvLoadURIExternal(
|
|
nsIURI* uri, nsIPrincipal* aTriggeringPrincipal,
|
|
nsIPrincipal* aRedirectPrincipal,
|
|
const MaybeDiscarded<BrowsingContext>& aContext,
|
|
bool aWasExternallyTriggered, bool aHasValidUserGestureActivation,
|
|
bool aNewWindowTarget) {
|
|
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, aNewWindowTarget);
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvExtProtocolChannelConnectParent(
|
|
const uint64_t& registrarId) {
|
|
nsresult rv;
|
|
|
|
// First get the real channel created before redirect on the parent.
|
|
nsCOMPtr<nsIChannel> channel;
|
|
rv = NS_LinkRedirectChannels(registrarId, 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::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, aData);
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvScriptError(
|
|
const nsAString& aMessage, const nsACString& aSourceName,
|
|
const uint32_t& aLineNumber, const uint32_t& aColNumber,
|
|
const uint32_t& aFlags, const nsACString& aCategory,
|
|
const bool& aIsFromPrivateWindow, const uint64_t& aInnerWindowId,
|
|
const bool& aIsFromChromeContext) {
|
|
return RecvScriptErrorInternal(aMessage, aSourceName, aLineNumber, aColNumber,
|
|
aFlags, aCategory, aIsFromPrivateWindow,
|
|
aIsFromChromeContext);
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvScriptErrorWithStack(
|
|
const nsAString& aMessage, const nsACString& aSourceName,
|
|
const uint32_t& aLineNumber, const uint32_t& aColNumber,
|
|
const uint32_t& aFlags, const nsACString& aCategory,
|
|
const bool& aIsFromPrivateWindow, const bool& aIsFromChromeContext,
|
|
const ClonedMessageData& aStack) {
|
|
return RecvScriptErrorInternal(aMessage, aSourceName, aLineNumber, aColNumber,
|
|
aFlags, aCategory, aIsFromPrivateWindow,
|
|
aIsFromChromeContext, &aStack);
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvScriptErrorInternal(
|
|
const nsAString& aMessage, const nsACString& aSourceName,
|
|
const uint32_t& aLineNumber, const uint32_t& aColNumber,
|
|
const uint32_t& aFlags, const nsACString& aCategory,
|
|
const bool& aIsFromPrivateWindow, const bool& aIsFromChromeContext,
|
|
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());
|
|
if (!JS::IsUnwrappedSavedFrame(stackObj)) {
|
|
return IPC_FAIL(this, "Unexpected object");
|
|
}
|
|
|
|
JS::Rooted<JSObject*> stackGlobal(cx, JS::GetNonCCWObjectGlobal(stackObj));
|
|
msg = new nsScriptErrorWithStack(JS::NothingHandleValue, stackObj,
|
|
stackGlobal);
|
|
} else {
|
|
msg = new nsScriptError();
|
|
}
|
|
|
|
rv = msg->Init(aMessage, aSourceName, aLineNumber, aColNumber, aFlags,
|
|
aCategory, aIsFromPrivateWindow, aIsFromChromeContext);
|
|
if (NS_FAILED(rv)) return IPC_OK();
|
|
|
|
msg->SetIsForwardedFromContentProcess(true);
|
|
|
|
consoleService->LogMessageWithMode(msg, nsIConsoleService::SuppressLog);
|
|
return IPC_OK();
|
|
}
|
|
|
|
bool ContentParent::DoLoadMessageManagerScript(const nsAString& aURL,
|
|
bool aRunInGlobalScope) {
|
|
MOZ_ASSERT(!aRunInGlobalScope);
|
|
return SendLoadProcessScript(aURL);
|
|
}
|
|
|
|
nsresult ContentParent::DoSendAsyncMessage(const nsAString& aMessage,
|
|
StructuredCloneData& aData) {
|
|
ClonedMessageData data;
|
|
if (!BuildClonedMessageData(aData, 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(aImage);
|
|
if (!surf) {
|
|
aResolver(TextRecognitionResultOrError("Failed to read image"_ns));
|
|
return IPC_OK();
|
|
}
|
|
TextRecognition::FindText(*surf, aLanguages)
|
|
->Then(
|
|
GetCurrentSerialEventTarget(), __func__,
|
|
[resolver = std::move(aResolver)](
|
|
TextRecognition::NativePromise::ResolveOrRejectValue&& aValue) {
|
|
if (aValue.IsResolve()) {
|
|
resolver(TextRecognitionResultOrError(aValue.ResolveValue()));
|
|
} else {
|
|
resolver(TextRecognitionResultOrError(aValue.RejectValue()));
|
|
}
|
|
});
|
|
return IPC_OK();
|
|
}
|
|
|
|
bool ContentParent::ShouldContinueFromReplyTimeout() {
|
|
RefPtr<ProcessHangMonitor> monitor = ProcessHangMonitor::Get();
|
|
return !monitor || !monitor->ShouldTimeOutCPOWs();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvAddIdleObserver(
|
|
const uint64_t& aObserverId, const uint32_t& aIdleTimeInS) {
|
|
nsresult rv;
|
|
nsCOMPtr<nsIUserIdleService> idleService =
|
|
do_GetService("@mozilla.org/widget/useridleservice;1", &rv);
|
|
NS_ENSURE_SUCCESS(rv, IPC_FAIL(this, "Failed to get UserIdleService."));
|
|
|
|
RefPtr<ParentIdleListener> listener =
|
|
new ParentIdleListener(this, aObserverId, aIdleTimeInS);
|
|
rv = idleService->AddIdleObserver(listener, aIdleTimeInS);
|
|
NS_ENSURE_SUCCESS(rv, IPC_FAIL(this, "AddIdleObserver failed."));
|
|
mIdleListeners.AppendElement(listener);
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvRemoveIdleObserver(
|
|
const uint64_t& aObserverId, const uint32_t& aIdleTimeInS) {
|
|
RefPtr<ParentIdleListener> listener;
|
|
for (int32_t i = mIdleListeners.Length() - 1; i >= 0; --i) {
|
|
listener = static_cast<ParentIdleListener*>(mIdleListeners[i].get());
|
|
if (listener->mObserver == aObserverId && listener->mTime == aIdleTimeInS) {
|
|
nsresult rv;
|
|
nsCOMPtr<nsIUserIdleService> idleService =
|
|
do_GetService("@mozilla.org/widget/useridleservice;1", &rv);
|
|
NS_ENSURE_SUCCESS(rv, IPC_FAIL(this, "Failed to get UserIdleService."));
|
|
idleService->RemoveIdleObserver(listener, aIdleTimeInS);
|
|
mIdleListeners.RemoveElementAt(i);
|
|
break;
|
|
}
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvBackUpXResources(
|
|
const FileDescriptor& aXSocketFd) {
|
|
#ifndef MOZ_X11
|
|
MOZ_CRASH("This message only makes sense on X11 platforms");
|
|
#else
|
|
MOZ_ASSERT(!mChildXSocketFdDup, "Already backed up X resources??");
|
|
if (aXSocketFd.IsValid()) {
|
|
mChildXSocketFdDup = aXSocketFd.ClonePlatformHandle();
|
|
}
|
|
#endif
|
|
return IPC_OK();
|
|
}
|
|
|
|
class AnonymousTemporaryFileRequestor final : public Runnable {
|
|
public:
|
|
AnonymousTemporaryFileRequestor(ContentParent* aCP, const uint64_t& aID)
|
|
: Runnable("dom::AnonymousTemporaryFileRequestor"),
|
|
mCP(aCP),
|
|
mID(aID),
|
|
mRv(NS_OK),
|
|
mPRFD(nullptr) {}
|
|
|
|
NS_IMETHOD Run() override {
|
|
if (NS_IsMainThread()) {
|
|
FileDescOrError result;
|
|
if (NS_WARN_IF(NS_FAILED(mRv))) {
|
|
// Returning false will kill the child process; instead
|
|
// propagate the error and let the child handle it.
|
|
result = mRv;
|
|
} else {
|
|
result = FileDescriptor(FileDescriptor::PlatformHandleType(
|
|
PR_FileDesc2NativeHandle(mPRFD)));
|
|
// The FileDescriptor object owns a duplicate of the file handle; we
|
|
// must close the original (and clean up the NSPR descriptor).
|
|
PR_Close(mPRFD);
|
|
}
|
|
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(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
|
|
|
|
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;
|
|
}
|
|
|
|
already_AddRefed<PWebBrowserPersistDocumentParent>
|
|
ContentParent::AllocPWebBrowserPersistDocumentParent(
|
|
PBrowserParent* aBrowser, const MaybeDiscarded<BrowsingContext>& aContext) {
|
|
return MakeAndAddRef<WebBrowserPersistDocumentParent>();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::CommonCreateWindow(
|
|
PBrowserParent* aThisTab, BrowsingContext& aParent, bool aSetOpener,
|
|
const uint32_t& aChromeFlags, const bool& aCalledFromJS,
|
|
const bool& aForPrinting, const bool& aForWindowDotPrint,
|
|
const bool& aIsTopLevelCreatedByWebContent, nsIURI* aURIToLoad,
|
|
const nsACString& aFeatures, const UserActivation::Modifiers& aModifiers,
|
|
BrowserParent* aNextRemoteBrowser, const nsAString& aName,
|
|
nsresult& aResult, nsCOMPtr<nsIRemoteTab>& aNewRemoteTab,
|
|
bool* aWindowIsNew, int32_t& aOpenLocation,
|
|
nsIPrincipal* aTriggeringPrincipal, nsIReferrerInfo* aReferrerInfo,
|
|
bool aLoadURI, nsIContentSecurityPolicy* aCsp,
|
|
const OriginAttributes& aOriginAttributes, bool aUserActivation,
|
|
bool aTextDirectiveUserActivation) {
|
|
// The content process should never be in charge of computing whether or
|
|
// not a window should be private - the parent will do that.
|
|
const uint32_t badFlags = nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW |
|
|
nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW |
|
|
nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME;
|
|
if (!!(aChromeFlags & badFlags)) {
|
|
return IPC_FAIL(this, "Forbidden aChromeFlags passed");
|
|
}
|
|
|
|
RefPtr<nsOpenWindowInfo> openInfo = new nsOpenWindowInfo();
|
|
openInfo->mForceNoOpener = !aSetOpener;
|
|
openInfo->mParent = &aParent;
|
|
openInfo->mIsRemote = true;
|
|
openInfo->mIsForPrinting = aForPrinting;
|
|
openInfo->mIsForWindowDotPrint = aForWindowDotPrint;
|
|
openInfo->mNextRemoteBrowser = aNextRemoteBrowser;
|
|
openInfo->mOriginAttributes = aOriginAttributes;
|
|
openInfo->mIsTopLevelCreatedByWebContent = aIsTopLevelCreatedByWebContent;
|
|
openInfo->mHasValidUserGestureActivation = aUserActivation;
|
|
openInfo->mTextDirectiveUserActivation = aTextDirectiveUserActivation;
|
|
|
|
MOZ_ASSERT_IF(aForWindowDotPrint, aForPrinting);
|
|
|
|
RefPtr<BrowserParent> topParent = BrowserParent::GetFrom(aThisTab);
|
|
while (topParent && topParent->GetBrowserBridgeParent()) {
|
|
topParent = topParent->GetBrowserBridgeParent()->Manager();
|
|
}
|
|
RefPtr<BrowserHost> thisBrowserHost =
|
|
topParent ? topParent->GetBrowserHost() : nullptr;
|
|
MOZ_ASSERT_IF(topParent, thisBrowserHost);
|
|
RefPtr<BrowsingContext> topBC =
|
|
topParent ? topParent->GetBrowsingContext() : nullptr;
|
|
MOZ_ASSERT_IF(topParent, topBC);
|
|
|
|
// The content process should have set its remote and fission flags correctly.
|
|
if (topBC) {
|
|
if ((!!(aChromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW) !=
|
|
topBC->UseRemoteTabs()) ||
|
|
(!!(aChromeFlags & nsIWebBrowserChrome::CHROME_FISSION_WINDOW) !=
|
|
topBC->UseRemoteSubframes())) {
|
|
return IPC_FAIL(this, "Unexpected aChromeFlags passed");
|
|
}
|
|
|
|
if (!aOriginAttributes.EqualsIgnoringFPD(topBC->OriginAttributesRef())) {
|
|
return IPC_FAIL(this, "Passed-in OriginAttributes does not match opener");
|
|
}
|
|
}
|
|
|
|
nsCOMPtr<nsIContent> frame;
|
|
if (topParent) {
|
|
frame = topParent->GetOwnerElement();
|
|
}
|
|
|
|
nsCOMPtr<nsPIDOMWindowOuter> outerWin;
|
|
if (frame) {
|
|
outerWin = frame->OwnerDoc()->GetWindow();
|
|
|
|
// If our chrome window is in the process of closing, don't try to open a
|
|
// new tab in it.
|
|
if (outerWin && outerWin->Closed()) {
|
|
outerWin = nullptr;
|
|
}
|
|
}
|
|
|
|
nsCOMPtr<nsIBrowserDOMWindow> browserDOMWin;
|
|
if (topParent) {
|
|
browserDOMWin = topParent->GetBrowserDOMWindow();
|
|
}
|
|
|
|
// If we haven't found a chrome window to open in, just use the most recently
|
|
// opened non PBM window.
|
|
if (!outerWin) {
|
|
// The parent was a private window but it's no longer available.
|
|
if (aOriginAttributes.mPrivateBrowsingId !=
|
|
nsIScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID) {
|
|
aResult = NS_ERROR_FAILURE;
|
|
return IPC_OK();
|
|
}
|
|
|
|
outerWin = nsContentUtils::GetMostRecentNonPBWindow();
|
|
if (NS_WARN_IF(!outerWin)) {
|
|
aResult = NS_ERROR_FAILURE;
|
|
return IPC_OK();
|
|
}
|
|
|
|
if (nsGlobalWindowOuter::Cast(outerWin)->IsChromeWindow()) {
|
|
browserDOMWin =
|
|
nsGlobalWindowOuter::Cast(outerWin)->GetBrowserDOMWindow();
|
|
}
|
|
}
|
|
|
|
aOpenLocation = nsWindowWatcher::GetWindowOpenLocation(
|
|
outerWin, aChromeFlags, aModifiers, aCalledFromJS, aForPrinting);
|
|
|
|
MOZ_ASSERT(aOpenLocation == nsIBrowserDOMWindow::OPEN_NEWTAB ||
|
|
aOpenLocation == nsIBrowserDOMWindow::OPEN_NEWTAB_BACKGROUND ||
|
|
aOpenLocation == nsIBrowserDOMWindow::OPEN_NEWTAB_FOREGROUND ||
|
|
aOpenLocation == nsIBrowserDOMWindow::OPEN_NEWWINDOW ||
|
|
aOpenLocation == nsIBrowserDOMWindow::OPEN_PRINT_BROWSER);
|
|
|
|
if (NS_WARN_IF(!browserDOMWin)) {
|
|
#ifdef MOZ_GECKOVIEW
|
|
// This might be print preview, but GeckoView doesn't support yet.
|
|
aResult = NS_ERROR_FAILURE;
|
|
return IPC_OK();
|
|
#else
|
|
// Opening in the same window or headless requires an nsIBrowserDOMWindow.
|
|
aOpenLocation = nsIBrowserDOMWindow::OPEN_NEWWINDOW;
|
|
#endif
|
|
}
|
|
|
|
if (aOpenLocation == nsIBrowserDOMWindow::OPEN_NEWTAB ||
|
|
aOpenLocation == nsIBrowserDOMWindow::OPEN_NEWTAB_BACKGROUND ||
|
|
aOpenLocation == nsIBrowserDOMWindow::OPEN_NEWTAB_FOREGROUND ||
|
|
aOpenLocation == nsIBrowserDOMWindow::OPEN_PRINT_BROWSER) {
|
|
RefPtr<Element> openerElement = do_QueryObject(frame);
|
|
|
|
nsCOMPtr<nsIOpenURIInFrameParams> params =
|
|
new nsOpenURIInFrameParams(openInfo, openerElement);
|
|
params->SetReferrerInfo(aReferrerInfo);
|
|
MOZ_ASSERT(aTriggeringPrincipal, "need a valid triggeringPrincipal");
|
|
params->SetTriggeringPrincipal(aTriggeringPrincipal);
|
|
params->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, aModifiers, aCalledFromJS, aParent.FullZoom(),
|
|
openInfo, getter_AddRefs(aNewRemoteTab));
|
|
if (NS_WARN_IF(NS_FAILED(aResult))) {
|
|
return IPC_OK();
|
|
}
|
|
|
|
MOZ_ASSERT(aNewRemoteTab);
|
|
RefPtr<BrowserHost> newBrowserHost = BrowserHost::GetFrom(aNewRemoteTab);
|
|
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& aForWindowDotPrint, const bool& aIsTopLevelCreatedByWebContent,
|
|
nsIURI* aURIToLoad, const nsACString& aFeatures,
|
|
const UserActivation::Modifiers& aModifiers,
|
|
nsIPrincipal* aTriggeringPrincipal, nsIContentSecurityPolicy* aCsp,
|
|
nsIReferrerInfo* aReferrerInfo, const OriginAttributes& aOriginAttributes,
|
|
bool aUserActivation, bool aTextDirectiveUserActivation,
|
|
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, aForWindowDotPrint, aIsTopLevelCreatedByWebContent,
|
|
aURIToLoad, aFeatures, aModifiers, newTab, VoidString(), rv, newRemoteTab,
|
|
&cwi.windowOpened(), openLocation, aTriggeringPrincipal, aReferrerInfo,
|
|
/* aLoadUri = */ false, aCsp, aOriginAttributes, aUserActivation,
|
|
aTextDirectiveUserActivation);
|
|
if (!ipcResult) {
|
|
return ipcResult;
|
|
}
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv)) || !newRemoteTab) {
|
|
return IPC_OK();
|
|
}
|
|
|
|
MOZ_ASSERT(BrowserHost::GetFrom(newRemoteTab.get()) ==
|
|
newTab->GetBrowserHost());
|
|
|
|
// This used to happen in the child - there may now be a better place to
|
|
// do this work.
|
|
MOZ_ALWAYS_SUCCEEDS(newBC->SetHasSiblings(
|
|
openLocation == nsIBrowserDOMWindow::OPEN_NEWTAB ||
|
|
openLocation == nsIBrowserDOMWindow::OPEN_NEWTAB_BACKGROUND ||
|
|
openLocation == nsIBrowserDOMWindow::OPEN_NEWTAB_FOREGROUND));
|
|
|
|
newTab->SwapFrameScriptsFrom(cwi.frameScripts());
|
|
newTab->MaybeShowFrame();
|
|
|
|
nsCOMPtr<nsIWidget> widget = newTab->GetWidget();
|
|
if (widget) {
|
|
cwi.dimensions() = newTab->GetDimensionInfo();
|
|
}
|
|
|
|
cwi.maxTouchPoints() = newTab->GetMaxTouchPoints();
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvCreateWindowInDifferentProcess(
|
|
PBrowserParent* aThisTab, const MaybeDiscarded<BrowsingContext>& aParent,
|
|
const uint32_t& aChromeFlags, const bool& aCalledFromJS,
|
|
const bool& aIsTopLevelCreatedByWebContent, nsIURI* aURIToLoad,
|
|
const nsACString& aFeatures, const UserActivation::Modifiers& aModifiers,
|
|
const nsAString& aName, nsIPrincipal* aTriggeringPrincipal,
|
|
nsIContentSecurityPolicy* aCsp, nsIReferrerInfo* aReferrerInfo,
|
|
const OriginAttributes& aOriginAttributes, bool aUserActivation,
|
|
bool aTextDirectiveUserActivation) {
|
|
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,
|
|
/* aForWindowDotPrint = */ false, aIsTopLevelCreatedByWebContent,
|
|
aURIToLoad, aFeatures, aModifiers,
|
|
/* aNextRemoteBrowser = */ nullptr, aName, rv, newRemoteTab, &windowIsNew,
|
|
openLocation, aTriggeringPrincipal, aReferrerInfo,
|
|
/* aLoadUri = */ true, aCsp, aOriginAttributes, aUserActivation,
|
|
aTextDirectiveUserActivation);
|
|
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::RecvGetFontListShmBlock(
|
|
const uint32_t& aGeneration, const uint32_t& aIndex,
|
|
ReadOnlySharedMemoryHandle* aOut) {
|
|
auto* fontList = gfxPlatformFontList::PlatformFontList();
|
|
MOZ_RELEASE_ASSERT(fontList, "gfxPlatformFontList not initialized?");
|
|
fontList->ShareFontListShmBlockToProcess(aGeneration, aIndex, Pid(), aOut);
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvInitializeFamily(
|
|
const uint32_t& aGeneration, const uint32_t& aFamilyIndex,
|
|
const bool& aLoadCmaps) {
|
|
auto* fontList = gfxPlatformFontList::PlatformFontList();
|
|
MOZ_RELEASE_ASSERT(fontList, "gfxPlatformFontList not initialized?");
|
|
fontList->InitializeFamily(aGeneration, aFamilyIndex, aLoadCmaps);
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvSetCharacterMap(
|
|
const uint32_t& aGeneration, const uint32_t& aFamilyIndex,
|
|
const bool& aAlias, const uint32_t& aFaceIndex,
|
|
const gfxSparseBitSet& aMap) {
|
|
auto* fontList = gfxPlatformFontList::PlatformFontList();
|
|
MOZ_RELEASE_ASSERT(fontList, "gfxPlatformFontList not initialized?");
|
|
fontList->SetCharacterMap(aGeneration, aFamilyIndex, aAlias, aFaceIndex,
|
|
aMap);
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvInitOtherFamilyNames(
|
|
const uint32_t& aGeneration, const bool& aDefer, bool* aLoaded) {
|
|
auto* fontList = gfxPlatformFontList::PlatformFontList();
|
|
MOZ_RELEASE_ASSERT(fontList, "gfxPlatformFontList not initialized?");
|
|
*aLoaded = fontList->InitOtherFamilyNames(aGeneration, aDefer);
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvSetupFamilyCharMap(
|
|
const uint32_t& aGeneration, const uint32_t& aIndex, const bool& aAlias) {
|
|
auto* fontList = gfxPlatformFontList::PlatformFontList();
|
|
MOZ_RELEASE_ASSERT(fontList, "gfxPlatformFontList not initialized?");
|
|
fontList->SetupFamilyCharMap(aGeneration, aIndex, aAlias);
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvStartCmapLoading(
|
|
const uint32_t& aGeneration, const uint32_t& aStartIndex) {
|
|
auto* fontList = gfxPlatformFontList::PlatformFontList();
|
|
MOZ_RELEASE_ASSERT(fontList, "gfxPlatformFontList not initialized?");
|
|
fontList->StartCmapLoading(aGeneration, aStartIndex);
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvGetHyphDict(
|
|
nsIURI* aURI, ReadOnlySharedMemoryHandle* aOutHandle) {
|
|
if (!aURI) {
|
|
return IPC_FAIL(this, "aURI must not be null.");
|
|
}
|
|
nsHyphenationManager::Instance()->ShareHyphDictToProcess(aURI, Pid(),
|
|
aOutHandle);
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvGraphicsError(
|
|
const nsACString& aError) {
|
|
if (gfx::LogForwarder* lf = gfx::Factory::GetLogForwarder()) {
|
|
std::stringstream message;
|
|
message << "CP+" << aError;
|
|
lf->UpdateStringsVector(message.str());
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvBeginDriverCrashGuard(
|
|
const 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::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::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 nsTArray<BroadcastBlobURLUnregistrationRequest>& aRequests,
|
|
ContentParent* aIgnoreThisCP) {
|
|
struct DataRequest {
|
|
const BroadcastBlobURLUnregistrationRequest& mRequest;
|
|
uint64_t mOriginHash;
|
|
bool mToBeSent;
|
|
};
|
|
|
|
nsTArray<DataRequest> dataRequests(aRequests.Length());
|
|
for (const BroadcastBlobURLUnregistrationRequest& request : aRequests) {
|
|
uint64_t originHash = ComputeLoadedOriginHash(request.principal());
|
|
|
|
bool toBeSent = BlobURLProtocolHandler::IsBlobURLBroadcastPrincipal(
|
|
request.principal());
|
|
|
|
dataRequests.AppendElement(DataRequest{request, originHash, toBeSent});
|
|
}
|
|
|
|
for (auto* cp : AllProcesses(eLive)) {
|
|
if (cp == aIgnoreThisCP) {
|
|
continue;
|
|
}
|
|
|
|
nsTArray<nsCString> urls;
|
|
for (const DataRequest& data : dataRequests) {
|
|
if (data.mToBeSent ||
|
|
cp->mLoadedOriginHashes.Contains(data.mOriginHash)) {
|
|
urls.AppendElement(data.mRequest.url());
|
|
}
|
|
}
|
|
|
|
if (!urls.IsEmpty()) {
|
|
Unused << cp->SendBlobURLUnregistration(urls);
|
|
}
|
|
}
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvStoreAndBroadcastBlobURLRegistration(
|
|
const nsACString& aURI, const IPCBlob& aBlob, nsIPrincipal* aPrincipal,
|
|
const nsCString& aPartitionKey) {
|
|
if (!aPrincipal) {
|
|
return IPC_FAIL(this, "No principal");
|
|
}
|
|
|
|
if (!ValidatePrincipal(aPrincipal, {ValidatePrincipalOptions::AllowSystem})) {
|
|
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, Some(ChildID()));
|
|
BroadcastBlobURLRegistration(aURI, blobImpl, aPrincipal, aPartitionKey, this);
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult
|
|
ContentParent::RecvUnstoreAndBroadcastBlobURLUnregistration(
|
|
const nsTArray<BroadcastBlobURLUnregistrationRequest>& aRequests) {
|
|
nsTArray<nsCString> uris;
|
|
|
|
for (const BroadcastBlobURLUnregistrationRequest& request : aRequests) {
|
|
if (!ValidatePrincipal(request.principal(),
|
|
{ValidatePrincipalOptions::AllowSystem})) {
|
|
LogAndAssertFailedPrincipalValidationInfo(request.principal(), __func__);
|
|
}
|
|
|
|
uris.AppendElement(request.url());
|
|
}
|
|
|
|
BroadcastBlobURLUnregistration(aRequests, this);
|
|
BlobURLProtocolHandler::RemoveDataEntries(uris, false /* Don't broadcast */);
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvGetFilesRequest(
|
|
const nsID& aID, nsTArray<nsString>&& aDirectoryPaths,
|
|
const bool& aRecursiveFlag) {
|
|
MOZ_ASSERT(!mGetFilesPendingRequests.GetWeak(aID));
|
|
|
|
if (!mozilla::Preferences::GetBool("dom.filesystem.pathcheck.disabled",
|
|
false)) {
|
|
RefPtr<FileSystemSecurity> fss = FileSystemSecurity::Get();
|
|
if (!fss) {
|
|
return IPC_FAIL(this, "Failed to get FileSystemSecurity.");
|
|
}
|
|
|
|
for (const auto& directoryPath : aDirectoryPaths) {
|
|
if (!fss->ContentProcessHasAccessTo(ChildID(), directoryPath)) {
|
|
return IPC_FAIL(this, "ContentProcessHasAccessTo failed.");
|
|
}
|
|
}
|
|
}
|
|
|
|
ErrorResult rv;
|
|
RefPtr<GetFilesHelper> helper = GetFilesHelperParent::Create(
|
|
aID, std::move(aDirectoryPaths), aRecursiveFlag, this, rv);
|
|
|
|
if (NS_WARN_IF(rv.Failed())) {
|
|
if (!SendGetFilesResponse(aID,
|
|
GetFilesResponseFailure(rv.StealNSResult()))) {
|
|
return IPC_FAIL(this, "SendGetFilesResponse failed.");
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
mGetFilesPendingRequests.InsertOrUpdate(aID, std::move(helper));
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvDeleteGetFilesRequest(
|
|
const nsID& aID) {
|
|
mGetFilesPendingRequests.Remove(aID);
|
|
return IPC_OK();
|
|
}
|
|
|
|
void ContentParent::SendGetFilesResponseAndForget(
|
|
const nsID& aID, const GetFilesResponseResult& aResult) {
|
|
if (mGetFilesPendingRequests.Remove(aID)) {
|
|
Unused << SendGetFilesResponse(aID, aResult);
|
|
}
|
|
}
|
|
|
|
void ContentParent::PaintTabWhileInterruptingJS(BrowserParent* aBrowserParent) {
|
|
if (!mHangMonitorActor) {
|
|
return;
|
|
}
|
|
ProcessHangMonitor::PaintWhileInterruptingJS(mHangMonitorActor,
|
|
aBrowserParent);
|
|
}
|
|
|
|
void ContentParent::UnloadLayersWhileInterruptingJS(
|
|
BrowserParent* aBrowserParent) {
|
|
if (!mHangMonitorActor) {
|
|
return;
|
|
}
|
|
ProcessHangMonitor::UnloadLayersWhileInterruptingJS(mHangMonitorActor,
|
|
aBrowserParent);
|
|
}
|
|
|
|
void ContentParent::CancelContentJSExecutionIfRunning(
|
|
BrowserParent* aBrowserParent, nsIRemoteTab::NavigationType aNavigationType,
|
|
const CancelContentJSOptions& aCancelContentJSOptions) {
|
|
if (!mHangMonitorActor) {
|
|
return;
|
|
}
|
|
|
|
ProcessHangMonitor::CancelContentJSExecutionIfRunning(
|
|
mHangMonitorActor, aBrowserParent, aNavigationType,
|
|
aCancelContentJSOptions);
|
|
}
|
|
|
|
void ContentParent::SetMainThreadQoSPriority(
|
|
nsIThread::QoSPriority aQoSPriority) {
|
|
if (!mHangMonitorActor) {
|
|
return;
|
|
}
|
|
|
|
ProcessHangMonitor::SetMainThreadQoSPriority(mHangMonitorActor, aQoSPriority);
|
|
}
|
|
|
|
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::AboutToLoadHttpDocumentForChild(
|
|
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::AddPrincipalToCookieInProcessCache(
|
|
nsIPrincipal* aPrincipal) {
|
|
MOZ_ASSERT(aPrincipal);
|
|
mCookieInContentListCache.AppendElement(aPrincipal);
|
|
}
|
|
|
|
void ContentParent::TakeCookieInProcessCache(
|
|
nsTArray<nsCOMPtr<nsIPrincipal>>& aList) {
|
|
aList.SwapElements(mCookieInContentListCache);
|
|
}
|
|
|
|
void ContentParent::TransmitBlobURLsForPrincipal(nsIPrincipal* aPrincipal) {
|
|
// If we're already broadcasting BlobURLs with this principal, we don't need
|
|
// to send them here.
|
|
if (BlobURLProtocolHandler::IsBlobURLBroadcastPrincipal(aPrincipal)) {
|
|
return;
|
|
}
|
|
|
|
// We shouldn't have any Blob URLs with expanded principals, so transmit URLs
|
|
// for each principal in the AllowList instead.
|
|
if (nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aPrincipal)) {
|
|
for (const auto& prin : ep->AllowList()) {
|
|
TransmitBlobURLsForPrincipal(prin);
|
|
}
|
|
return;
|
|
}
|
|
|
|
uint64_t originHash = ComputeLoadedOriginHash(aPrincipal);
|
|
|
|
if (!mLoadedOriginHashes.Contains(originHash)) {
|
|
mLoadedOriginHashes.AppendElement(originHash);
|
|
|
|
nsTArray<BlobURLRegistrationData> registrations;
|
|
BlobURLProtocolHandler::ForEachBlobURL(
|
|
[&](BlobImpl* aBlobImpl, nsIPrincipal* aBlobPrincipal,
|
|
const nsCString& aPartitionKey, const nsACString& aURI,
|
|
bool aRevoked) {
|
|
// This check uses `ComputeLoadedOriginHash` to compare, rather than
|
|
// doing the more accurate `Equals` check, as it needs to match the
|
|
// behaviour of the logic to broadcast new registrations.
|
|
if (originHash != ComputeLoadedOriginHash(aBlobPrincipal)) {
|
|
return true;
|
|
}
|
|
|
|
IPCBlob ipcBlob;
|
|
nsresult rv = IPCBlobUtils::Serialize(aBlobImpl, ipcBlob);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
return false;
|
|
}
|
|
|
|
registrations.AppendElement(BlobURLRegistrationData(
|
|
nsCString(aURI), ipcBlob, WrapNotNull(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();
|
|
}
|
|
|
|
static bool WebdriverRunning() {
|
|
#ifdef ENABLE_WEBDRIVER
|
|
nsCOMPtr<nsIMarionette> marionette = do_GetService(NS_MARIONETTE_CONTRACTID);
|
|
if (marionette) {
|
|
bool marionetteRunning = false;
|
|
marionette->GetRunning(&marionetteRunning);
|
|
if (marionetteRunning) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
nsCOMPtr<nsIRemoteAgent> agent = do_GetService(NS_REMOTEAGENT_CONTRACTID);
|
|
if (agent) {
|
|
bool remoteAgentRunning = false;
|
|
agent->GetRunning(&remoteAgentRunning);
|
|
if (remoteAgentRunning) {
|
|
return true;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
return false;
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvRecordPageLoadEvent(
|
|
mozilla::glean::perf::PageLoadExtra&& aPageLoadEventExtra) {
|
|
// Check whether a webdriver is running.
|
|
aPageLoadEventExtra.usingWebdriver = mozilla::Some(WebdriverRunning());
|
|
|
|
#if defined(XP_WIN)
|
|
// The "hasSSD" property is only set on Windows during the first
|
|
// call to nsISystemInfo.diskInfo - which is done before
|
|
// browser-startup-idle-tasks-finished
|
|
// Other platforms do not compute hasSSD, so there's no reason to
|
|
// query this on other platforms.
|
|
nsresult rv;
|
|
nsCOMPtr<nsIPropertyBag2> infoService =
|
|
mozilla::components::SystemInfo::Service();
|
|
bool hasSSD;
|
|
rv = infoService->GetPropertyAsBool(u"hasSSD"_ns, &hasSSD);
|
|
if (NS_SUCCEEDED(rv)) {
|
|
aPageLoadEventExtra.hasSsd = Some(hasSSD);
|
|
}
|
|
#endif
|
|
mozilla::glean::perf::page_load.Record(mozilla::Some(aPageLoadEventExtra));
|
|
|
|
// Send the PageLoadPing after every 30 page loads, or on startup.
|
|
if (++sPageLoadEventCounter >= 30) {
|
|
Unused << NS_WARN_IF(NS_FAILED(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;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////
|
|
// PURLClassifierLocalByNameParent
|
|
|
|
PURLClassifierLocalByNameParent*
|
|
ContentParent::AllocPURLClassifierLocalByNameParent(
|
|
nsIURI* aURI, const nsTArray<nsCString>& aFeatures,
|
|
const nsIUrlClassifierFeature::listType& aListType) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
RefPtr<URLClassifierLocalByNameParent> actor =
|
|
new URLClassifierLocalByNameParent();
|
|
return actor.forget().take();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvPURLClassifierLocalByNameConstructor(
|
|
PURLClassifierLocalByNameParent* aActor, nsIURI* aURI,
|
|
nsTArray<nsCString>&& aFeatureNames,
|
|
const nsIUrlClassifierFeature::listType& aListType) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(aActor);
|
|
if (!aURI) {
|
|
return IPC_FAIL(this, "aURI should not be null");
|
|
}
|
|
|
|
nsTArray<IPCURLClassifierFeature> ipcFeatures;
|
|
for (nsCString& featureName : aFeatureNames) {
|
|
RefPtr<nsIUrlClassifierFeature> feature =
|
|
UrlClassifierFeatureFactory::GetFeatureByName(featureName);
|
|
nsAutoCString name;
|
|
nsresult rv = feature->GetName(name);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
continue;
|
|
}
|
|
|
|
nsTArray<nsCString> tables;
|
|
rv = feature->GetTables(aListType, tables);
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
continue;
|
|
}
|
|
|
|
ipcFeatures.AppendElement(IPCURLClassifierFeature(name, tables));
|
|
}
|
|
|
|
auto* actor = static_cast<URLClassifierLocalByNameParent*>(aActor);
|
|
return actor->StartClassify(aURI, ipcFeatures, aListType);
|
|
}
|
|
|
|
bool ContentParent::DeallocPURLClassifierLocalByNameParent(
|
|
PURLClassifierLocalByNameParent* aActor) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(aActor);
|
|
|
|
RefPtr<URLClassifierLocalByNameParent> actor =
|
|
dont_AddRef(static_cast<URLClassifierLocalByNameParent*>(aActor));
|
|
return true;
|
|
}
|
|
|
|
PSessionStorageObserverParent*
|
|
ContentParent::AllocPSessionStorageObserverParent() {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
|
|
return mozilla::dom::AllocPSessionStorageObserverParent();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvPSessionStorageObserverConstructor(
|
|
PSessionStorageObserverParent* aActor) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(aActor);
|
|
|
|
if (!mozilla::dom::RecvPSessionStorageObserverConstructor(aActor)) {
|
|
return IPC_FAIL(this, "RecvPSessionStorageObserverConstructor failed.");
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
bool ContentParent::DeallocPSessionStorageObserverParent(
|
|
PSessionStorageObserverParent* aActor) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(aActor);
|
|
|
|
return mozilla::dom::DeallocPSessionStorageObserverParent(aActor);
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvBHRThreadHang(
|
|
const HangDetails& aHangDetails) {
|
|
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
|
if (obs) {
|
|
// Copy the HangDetails recieved over the network into a nsIHangDetails, and
|
|
// then fire our own observer notification.
|
|
// XXX: We should be able to avoid this potentially expensive copy here by
|
|
// moving our deserialized argument.
|
|
nsCOMPtr<nsIHangDetails> hangDetails =
|
|
new nsHangDetails(HangDetails(aHangDetails), PersistedToDisk::No);
|
|
obs->NotifyObservers(hangDetails, "bhr-thread-hang", nullptr);
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::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::CompleteAllowAccessForOnParentProcess(
|
|
aParentContext.get_canonical(), aTopLevelWindowId, aTrackingPrincipal,
|
|
aTrackingOrigin, aCookieBehavior, aReason, nullptr)
|
|
->Then(GetCurrentSerialEventTarget(), __func__,
|
|
[aResolver = std::move(aResolver)](
|
|
StorageAccessAPIHelper::StorageAccessPermissionGrantPromise::
|
|
ResolveOrRejectValue&& aValue) {
|
|
Maybe<StorageAccessPromptChoices> choice;
|
|
if (aValue.IsResolve()) {
|
|
choice.emplace(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 Maybe<PositionState>& aState) {
|
|
if (aContext.IsNullOrDiscarded()) {
|
|
return IPC_OK();
|
|
}
|
|
if (RefPtr<IMediaInfoUpdater> updater =
|
|
aContext.get_canonical()->GetMediaController()) {
|
|
updater->UpdatePositionState(aContext.ContextId(), aState);
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvNotifyGuessedPositionStateChanged(
|
|
const MaybeDiscarded<BrowsingContext>& aContext, const nsID& aMediaId,
|
|
const Maybe<PositionState>& aState) {
|
|
if (aContext.IsNullOrDiscarded()) {
|
|
return IPC_OK();
|
|
}
|
|
if (RefPtr<IMediaInfoUpdater> updater =
|
|
aContext.get_canonical()->GetMediaController()) {
|
|
updater->UpdateGuessedPositionState(aContext.ContextId(), aMediaId, aState);
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvAddOrRemovePageAwakeRequest(
|
|
const MaybeDiscarded<BrowsingContext>& aContext,
|
|
const bool& aShouldAddCount) {
|
|
if (aContext.IsNullOrDiscarded()) {
|
|
return IPC_OK();
|
|
}
|
|
if (aShouldAddCount) {
|
|
aContext.get_canonical()->AddPageAwakeRequest();
|
|
} else {
|
|
aContext.get_canonical()->RemovePageAwakeRequest();
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
#if defined(XP_WIN)
|
|
mozilla::ipc::IPCResult ContentParent::RecvGetModulesTrust(
|
|
ModulePaths&& aModPaths, bool aRunAtNormalPriority,
|
|
GetModulesTrustResolver&& aResolver) {
|
|
RefPtr<DllServices> dllSvc(DllServices::Get());
|
|
dllSvc->GetModulesTrust(std::move(aModPaths), aRunAtNormalPriority)
|
|
->Then(
|
|
GetMainThreadSerialEventTarget(), __func__,
|
|
[aResolver](ModulesMapResult&& aResult) {
|
|
aResolver(Some(ModulesMapResult(std::move(aResult))));
|
|
},
|
|
[aResolver](nsresult aRv) { aResolver(Nothing()); });
|
|
return IPC_OK();
|
|
}
|
|
#endif // defined(XP_WIN)
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvCreateBrowsingContext(
|
|
uint64_t aGroupId, BrowsingContext::IPCInitializer&& aInit) {
|
|
RefPtr<WindowGlobalParent> parent;
|
|
if (aInit.mParentId != 0) {
|
|
parent = WindowGlobalParent::GetByInnerWindowId(aInit.mParentId);
|
|
if (!parent) {
|
|
return IPC_FAIL(this, "Parent doesn't exist in parent process");
|
|
}
|
|
}
|
|
|
|
if (parent && parent->GetContentParent() != this) {
|
|
// We're trying attach a child BrowsingContext to a parent
|
|
// WindowContext in another process. This is illegal since the
|
|
// only thing that could create that child BrowsingContext is the parent
|
|
// window's process.
|
|
return IPC_FAIL(this,
|
|
"Must create BrowsingContext from the parent's process");
|
|
}
|
|
|
|
RefPtr<BrowsingContext> opener;
|
|
if (aInit.GetOpenerId() != 0) {
|
|
opener = BrowsingContext::Get(aInit.GetOpenerId());
|
|
if (!opener) {
|
|
return IPC_FAIL(this, "Opener doesn't exist in parent process");
|
|
}
|
|
}
|
|
|
|
RefPtr<BrowsingContext> child = BrowsingContext::Get(aInit.mId);
|
|
if (child) {
|
|
// This is highly suspicious. BrowsingContexts should only be created once,
|
|
// so finding one indicates that someone is doing something they shouldn't.
|
|
return IPC_FAIL(this, "A BrowsingContext with this ID already exists");
|
|
}
|
|
|
|
// Ensure that the passed-in BrowsingContextGroup is valid.
|
|
RefPtr<BrowsingContextGroup> group =
|
|
BrowsingContextGroup::GetOrCreate(aGroupId);
|
|
if (parent) {
|
|
if (parent->Group()->Id() != aGroupId) {
|
|
return IPC_FAIL(this, "Parent has different group ID");
|
|
}
|
|
if (parent->IsDiscarded()) {
|
|
return IPC_FAIL(this, "Parent is discarded");
|
|
}
|
|
if (parent->Group() != group) {
|
|
return IPC_FAIL(this, "Parent has different group object");
|
|
}
|
|
}
|
|
if (opener && opener->Group() != group) {
|
|
if (opener->Group()->Id() != aGroupId) {
|
|
return IPC_FAIL(this, "Opener has different group ID");
|
|
}
|
|
return IPC_FAIL(this, "Opener has different group object");
|
|
}
|
|
if (!parent && !opener && !group->Toplevels().IsEmpty()) {
|
|
return IPC_FAIL(this, "Unrelated context from child in stale group");
|
|
}
|
|
|
|
return BrowsingContext::CreateFromIPC(std::move(aInit), group, this);
|
|
}
|
|
|
|
bool ContentParent::CheckBrowsingContextEmbedder(CanonicalBrowsingContext* aBC,
|
|
const char* aOperation) const {
|
|
if (!aBC->IsEmbeddedInProcess(ChildID())) {
|
|
MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Warning,
|
|
("ParentIPC: Trying to %s out of process context 0x%08" PRIx64,
|
|
aOperation, aBC->Id()));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvDiscardBrowsingContext(
|
|
const MaybeDiscarded<BrowsingContext>& aContext, bool aDoDiscard,
|
|
DiscardBrowsingContextResolver&& aResolve) {
|
|
if (CanonicalBrowsingContext* context =
|
|
CanonicalBrowsingContext::Cast(aContext.GetMaybeDiscarded())) {
|
|
if (aDoDiscard && !context->IsDiscarded()) {
|
|
if (!CheckBrowsingContextEmbedder(context, "discard")) {
|
|
return IPC_FAIL(this, "Illegal Discard attempt");
|
|
}
|
|
|
|
context->Detach(/* aFromIPC */ true);
|
|
}
|
|
context->AddFinalDiscardListener(aResolve);
|
|
return IPC_OK();
|
|
}
|
|
|
|
// Resolve the promise, as we've received and handled the message. This will
|
|
// allow the content process to fully-discard references to this BC.
|
|
aResolve(true);
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvWindowClose(
|
|
const MaybeDiscarded<BrowsingContext>& aContext, bool aTrustedCaller) {
|
|
if (aContext.IsNullOrDiscarded()) {
|
|
MOZ_LOG(
|
|
BrowsingContext::GetLog(), LogLevel::Debug,
|
|
("ParentIPC: Trying to send a message to dead or detached context"));
|
|
return IPC_OK();
|
|
}
|
|
CanonicalBrowsingContext* context = aContext.get_canonical();
|
|
|
|
// 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, bool aShouldClearFocus,
|
|
const MaybeDiscarded<BrowsingContext>& aAncestorBrowsingContextToFocus) {
|
|
if (aContext.IsNullOrDiscarded()) {
|
|
MOZ_LOG(
|
|
BrowsingContext::GetLog(), LogLevel::Debug,
|
|
("ParentIPC: Trying to send a message to dead or detached context"));
|
|
return IPC_OK();
|
|
}
|
|
LOGFOCUS(
|
|
("ContentParent::RecvAdjustWindowFocus isVisible %d actionid: %" PRIu64,
|
|
aIsVisible, aActionId));
|
|
|
|
nsTHashMap<nsPtrHashKey<ContentParent>, bool> processes(2);
|
|
processes.InsertOrUpdate(this, true);
|
|
|
|
ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
|
|
if (cpm) {
|
|
CanonicalBrowsingContext* context = aContext.get_canonical();
|
|
while (context) {
|
|
BrowsingContext* parent = context->GetParent();
|
|
if (!parent) {
|
|
break;
|
|
}
|
|
|
|
CanonicalBrowsingContext* canonicalParent = parent->Canonical();
|
|
ContentParent* cp = cpm->GetContentProcessById(
|
|
ContentParentId(canonicalParent->OwnerProcessId()));
|
|
if (cp && !processes.Get(cp)) {
|
|
Unused << cp->SendAdjustWindowFocus(context, aIsVisible, aActionId,
|
|
aShouldClearFocus,
|
|
aAncestorBrowsingContextToFocus);
|
|
processes.InsertOrUpdate(cp, true);
|
|
}
|
|
context = canonicalParent;
|
|
}
|
|
}
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvClearFocus(
|
|
const MaybeDiscarded<BrowsingContext>& aContext) {
|
|
if (aContext.IsNullOrDiscarded()) {
|
|
MOZ_LOG(
|
|
BrowsingContext::GetLog(), LogLevel::Debug,
|
|
("ParentIPC: Trying to send a message to dead or detached context"));
|
|
return IPC_OK();
|
|
}
|
|
CanonicalBrowsingContext* context = aContext.get_canonical();
|
|
|
|
if (ContentParent* cp = context->GetContentParent()) {
|
|
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::RecvGetContiguousSessionHistoryInfos(
|
|
const MaybeDiscarded<BrowsingContext>& aContext, SessionHistoryInfo&& aInfo,
|
|
GetContiguousSessionHistoryInfosResolver&& aResolver) {
|
|
if (aContext.IsNullOrDiscarded()) {
|
|
return IPC_OK();
|
|
}
|
|
|
|
aResolver(aContext.get_canonical()->GetContiguousSessionHistoryInfos(aInfo));
|
|
|
|
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::RecvConsumeHistoryActivation(
|
|
const MaybeDiscarded<BrowsingContext>& aTop) {
|
|
if (aTop.IsNullOrDiscarded()) {
|
|
return IPC_OK();
|
|
}
|
|
aTop->Group()->EachOtherParent(this, [aTop](ContentParent* aParent) {
|
|
Unused << aParent->SendConsumeHistoryActivation(aTop);
|
|
});
|
|
return IPC_OK();
|
|
}
|
|
|
|
mozilla::ipc::IPCResult ContentParent::RecvCommitWindowContextTransaction(
|
|
const MaybeDiscarded<WindowContext>& aContext,
|
|
WindowContext::BaseTransaction&& aTransaction, uint64_t aEpoch) {
|
|
// Record the new BrowsingContextFieldEpoch associated with this transaction.
|
|
// This should be done unconditionally, so that we're always in-sync.
|
|
//
|
|
// The order the parent process receives transactions is considered the
|
|
// "canonical" ordering, so we don't need to worry about doing any
|
|
// epoch-related validation.
|
|
MOZ_ASSERT(aEpoch == mBrowsingContextFieldEpoch + 1,
|
|
"Child process skipped an epoch?");
|
|
mBrowsingContextFieldEpoch = aEpoch;
|
|
|
|
return aTransaction.CommitFromIPC(aContext, this);
|
|
}
|
|
|
|
NS_IMETHODIMP ContentParent::GetChildID(uint64_t* aOut) {
|
|
*aOut = this->ChildID();
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP ContentParent::GetOsPid(int32_t* aOut) {
|
|
*aOut = Pid();
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP ContentParent::GetRemoteType(nsACString& aRemoteType) {
|
|
aRemoteType = GetRemoteType();
|
|
return NS_OK;
|
|
}
|
|
|
|
void ContentParent::StartRemoteWorkerService() {
|
|
MOZ_ASSERT(!mRemoteWorkerServiceActor);
|
|
MOZ_ASSERT(mRemoteType != PREALLOC_REMOTE_TYPE);
|
|
|
|
Endpoint<PRemoteWorkerServiceChild> childEp;
|
|
mRemoteWorkerServiceActor =
|
|
RemoteWorkerServiceParent::CreateForProcess(this, &childEp);
|
|
|
|
Endpoint<PRemoteWorkerDebuggerManagerChild> remoteDebuggerChildEp;
|
|
mRemoteWorkerDebuggerManagerActor =
|
|
RemoteWorkerDebuggerManagerParent::CreateForProcess(
|
|
&remoteDebuggerChildEp);
|
|
if (mRemoteWorkerServiceActor) {
|
|
Unused << SendInitRemoteWorkerService(std::move(childEp),
|
|
std::move(remoteDebuggerChildEp));
|
|
}
|
|
}
|
|
|
|
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);
|
|
}
|
|
MMPrinter::Print("ContentParent::RecvRawMessage", aMeta.actorName(),
|
|
aMeta.messageName(), aData);
|
|
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,
|
|
MaybeFeaturePolicyInfo&& aContainerFeaturePolicyInfo) {
|
|
if (aContainerContext.IsNullOrDiscarded()) {
|
|
return IPC_OK();
|
|
}
|
|
|
|
auto* context = aContainerContext.get_canonical();
|
|
context->SetContainerFeaturePolicy(std::move(aContainerFeaturePolicyInfo));
|
|
|
|
return IPC_OK();
|
|
}
|
|
|
|
NS_IMETHODIMP ContentParent::GetCanSend(bool* aCanSend) {
|
|
*aCanSend = CanSend();
|
|
return NS_OK;
|
|
}
|
|
|
|
ContentParent* ContentParent::AsContentParent() { return this; }
|
|
|
|
JSActorManager* ContentParent::AsJSActorManager() { return this; }
|
|
|
|
IPCResult ContentParent::RecvGetSystemIcon(nsIURI* aURI,
|
|
GetSystemIconResolver&& aResolver) {
|
|
using ResolverArgs = std::tuple<const nsresult&, mozilla::Maybe<ByteBuf>&&>;
|
|
|
|
if (!aURI) {
|
|
Maybe<ByteBuf> bytebuf = Nothing();
|
|
aResolver(ResolverArgs(NS_ERROR_NULL_POINTER, std::move(bytebuf)));
|
|
return IPC_OK();
|
|
}
|
|
|
|
#if defined(MOZ_WIDGET_GTK)
|
|
Maybe<ByteBuf> bytebuf = Some(ByteBuf{});
|
|
nsresult rv = nsIconChannel::GetIcon(aURI, bytebuf.ptr());
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
bytebuf = Nothing();
|
|
}
|
|
aResolver(ResolverArgs(rv, std::move(bytebuf)));
|
|
return IPC_OK();
|
|
#elif defined(XP_WIN)
|
|
nsIconChannel::GetIconAsync(aURI)->Then(
|
|
GetCurrentSerialEventTarget(), __func__,
|
|
[aResolver](ByteBuf&& aByteBuf) {
|
|
Maybe<ByteBuf> bytebuf = Some(std::move(aByteBuf));
|
|
aResolver(ResolverArgs(NS_OK, std::move(bytebuf)));
|
|
},
|
|
[aResolver](nsresult aErr) {
|
|
Maybe<ByteBuf> bytebuf = Nothing();
|
|
aResolver(ResolverArgs(aErr, std::move(bytebuf)));
|
|
});
|
|
return IPC_OK();
|
|
#else
|
|
MOZ_CRASH(
|
|
"This message is currently implemented only on GTK and Windows "
|
|
"platforms");
|
|
#endif
|
|
}
|
|
|
|
IPCResult ContentParent::RecvGetSystemGeolocationPermissionBehavior(
|
|
GetSystemGeolocationPermissionBehaviorResolver&& aResolver) {
|
|
aResolver(Geolocation::GetLocationOSPermission());
|
|
return IPC_OK();
|
|
}
|
|
|
|
IPCResult ContentParent::RecvRequestGeolocationPermissionFromUser(
|
|
const MaybeDiscardedBrowsingContext& aBrowsingContext,
|
|
RequestGeolocationPermissionFromUserResolver&& aResolver) {
|
|
if (MOZ_UNLIKELY(aBrowsingContext.IsNullOrDiscarded())) {
|
|
aResolver(GeolocationPermissionStatus::Error);
|
|
return IPC_OK();
|
|
}
|
|
RefPtr<BrowsingContext> browsingContext = aBrowsingContext.get();
|
|
|
|
Geolocation::ReallowWithSystemPermissionOrCancel(browsingContext,
|
|
std::move(aResolver));
|
|
return IPC_OK();
|
|
}
|
|
|
|
#ifdef FUZZING_SNAPSHOT
|
|
IPCResult ContentParent::RecvSignalFuzzingReady() {
|
|
// No action needed here, we already observe this message directly
|
|
// on the channel and act accordingly.
|
|
return IPC_OK();
|
|
}
|
|
#endif
|
|
|
|
nsCString ThreadsafeContentParentHandle::GetRemoteType() {
|
|
RecursiveMutexAutoLock lock(mMutex);
|
|
return mRemoteType;
|
|
}
|
|
|
|
UniqueThreadsafeContentParentKeepAlive
|
|
ThreadsafeContentParentHandle::TryAddKeepAlive(uint64_t aBrowserId) {
|
|
RecursiveMutexAutoLock lock(mMutex);
|
|
// If shutdown has already started, we can't keep this ContentParent alive
|
|
// anymore.
|
|
if (mShutdownStarted) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Otherwise, ensure there is an entry for this BrowserId, and increment it.
|
|
++mKeepAlivesPerBrowserId.LookupOrInsert(aBrowserId, 0);
|
|
return UniqueThreadsafeContentParentKeepAlive{do_AddRef(this).take(),
|
|
{.mBrowserId = aBrowserId}};
|
|
}
|
|
|
|
} // namespace dom
|
|
} // namespace mozilla
|
|
|
|
NS_IMPL_ISUPPORTS(ParentIdleListener, nsIObserver)
|
|
|
|
NS_IMETHODIMP
|
|
ParentIdleListener::Observe(nsISupports*, const char* aTopic,
|
|
const char16_t* aData) {
|
|
mozilla::Unused << mParent->SendNotifyIdleObserver(
|
|
mObserver, nsDependentCString(aTopic), nsDependentString(aData));
|
|
return NS_OK;
|
|
}
|
|
|
|
#undef LOGPDM
|