forked from mirrors/gecko-dev
		
	 d637b3f05e
			
		
	
	
		d637b3f05e
		
	
	
	
	
		
			
			Backed out changeset a5c8f22b7170 (bug 1824465) Backed out changeset b551b655ac72 (bug 1824465) Backed out changeset 0e6768a6419c (bug 1824465) Backed out changeset fb115ebb7fe0 (bug 1824465) Backed out changeset aaa7a2c8aa3b (bug 1824465) Backed out changeset 7ef94bfa90b3 (bug 1824465) Backed out changeset a4238fd6b86f (bug 1824465) Backed out changeset 3a88e4cfbe45 (bug 1824465) Backed out changeset 40c2467d3162 (bug 1824465) Backed out changeset 8f900395c72c (bug 1824465) Backed out changeset 92e4c6e4d73c (bug 1824465) Backed out changeset 445c5d5d9661 (bug 1824465) Backed out changeset de51ed5389d9 (bug 1824465) Backed out changeset 72049d72bcb6 (bug 1824465) Backed out changeset 126773c2427a (bug 1824465) Backed out changeset 886e76bc80be (bug 1824465) Backed out changeset a69a851411f0 (bug 1824465) Backed out changeset 703599cf6189 (bug 1824465) Backed out changeset 11ecb78ebc15 (bug 1824465) Backed out changeset 563255aaa1e1 (bug 1824465) Backed out changeset d1bf32c2a6c6 (bug 1824465) Backed out changeset ef28b2777487 (bug 1824465) Backed out changeset a2015d354bb1 (bug 1824465) Backed out changeset 31d6b53fdc6a (bug 1824465)
		
			
				
	
	
		
			8286 lines
		
	
	
	
		
			279 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			8286 lines
		
	
	
	
		
			279 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 | |
| /* vim: set ts=2 et sw=2 tw=80: */
 | |
| /* This Source Code Form is subject to the terms of the Mozilla Public
 | |
|  * License, v. 2.0. If a copy of the MPL was not distributed with this
 | |
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 | |
| 
 | |
| #ifdef MOZ_WIDGET_ANDROID
 | |
| #  include "AndroidDecoderModule.h"
 | |
| #endif
 | |
| 
 | |
| #include "mozilla/AppShutdown.h"
 | |
| #include "mozilla/DebugOnly.h"
 | |
| 
 | |
| #include "base/basictypes.h"
 | |
| #include "base/shared_memory.h"
 | |
| 
 | |
| #include "ContentParent.h"
 | |
| #include "mozilla/ipc/ProcessUtils.h"
 | |
| #include "mozilla/CmdLineAndEnvUtils.h"
 | |
| #include "BrowserParent.h"
 | |
| 
 | |
| #include "chrome/common/process_watcher.h"
 | |
| #include "mozilla/Result.h"
 | |
| #include "mozilla/XREAppData.h"
 | |
| #include "nsComponentManagerUtils.h"
 | |
| #include "nsIBrowserDOMWindow.h"
 | |
| 
 | |
| #ifdef ACCESSIBILITY
 | |
| #  include "mozilla/a11y/PDocAccessible.h"
 | |
| #endif
 | |
| #include "GMPServiceParent.h"
 | |
| #include "HandlerServiceParent.h"
 | |
| #include "IHistory.h"
 | |
| #if defined(XP_WIN) && defined(ACCESSIBILITY)
 | |
| #  include "mozilla/a11y/AccessibleWrap.h"
 | |
| #  include "mozilla/a11y/Compatibility.h"
 | |
| #  include "mozilla/mscom/ActCtxResource.h"
 | |
| #  include "mozilla/StaticPrefs_accessibility.h"
 | |
| #endif
 | |
| #include <map>
 | |
| #include <utility>
 | |
| 
 | |
| #include "ContentProcessManager.h"
 | |
| #include "GeckoProfiler.h"
 | |
| #include "Geolocation.h"
 | |
| #include "GfxInfoBase.h"
 | |
| #include "MMPrinter.h"
 | |
| #include "PreallocatedProcessManager.h"
 | |
| #include "ProcessPriorityManager.h"
 | |
| #include "ProfilerParent.h"
 | |
| #include "SandboxHal.h"
 | |
| #include "SourceSurfaceRawData.h"
 | |
| #include "mozilla/ipc/URIUtils.h"
 | |
| #include "gfxPlatform.h"
 | |
| #include "gfxPlatformFontList.h"
 | |
| #include "nsDNSService2.h"
 | |
| #include "nsPIDNSService.h"
 | |
| #include "mozilla/AntiTrackingUtils.h"
 | |
| #include "mozilla/AppShutdown.h"
 | |
| #include "mozilla/AutoRestore.h"
 | |
| #include "mozilla/BasePrincipal.h"
 | |
| #include "mozilla/BenchmarkStorageParent.h"
 | |
| #include "mozilla/Casting.h"
 | |
| #include "mozilla/ContentBlockingUserInteraction.h"
 | |
| #include "mozilla/ClearOnShutdown.h"
 | |
| #include "mozilla/FOGIPC.h"
 | |
| #include "mozilla/GlobalStyleSheetCache.h"
 | |
| #include "mozilla/GeckoArgs.h"
 | |
| #include "mozilla/HangDetails.h"
 | |
| #include "mozilla/LoginReputationIPC.h"
 | |
| #include "mozilla/LookAndFeel.h"
 | |
| #include "mozilla/Maybe.h"
 | |
| #include "mozilla/NullPrincipal.h"
 | |
| #include "mozilla/PerformanceMetricsCollector.h"
 | |
| #include "mozilla/Preferences.h"
 | |
| #include "mozilla/PresShell.h"
 | |
| #include "mozilla/ProcessHangMonitor.h"
 | |
| #include "mozilla/ProcessHangMonitorIPC.h"
 | |
| #include "mozilla/ProfilerLabels.h"
 | |
| #include "mozilla/ProfilerMarkers.h"
 | |
| #include "mozilla/ScopeExit.h"
 | |
| #include "mozilla/ScriptPreloader.h"
 | |
| #include "mozilla/Components.h"
 | |
| #include "mozilla/Sprintf.h"
 | |
| #include "mozilla/StaticPrefs_dom.h"
 | |
| #include "mozilla/StaticPrefs_fission.h"
 | |
| #include "mozilla/StaticPrefs_media.h"
 | |
| #include "mozilla/StaticPrefs_widget.h"
 | |
| #include "mozilla/StorageAccessAPIHelper.h"
 | |
| #include "mozilla/StyleSheet.h"
 | |
| #include "mozilla/StyleSheetInlines.h"
 | |
| #include "mozilla/TaskController.h"
 | |
| #include "mozilla/Telemetry.h"
 | |
| #include "mozilla/TelemetryIPC.h"
 | |
| #include "mozilla/Unused.h"
 | |
| #include "mozilla/WebBrowserPersistDocumentParent.h"
 | |
| #include "mozilla/devtools/HeapSnapshotTempFileHelperParent.h"
 | |
| #include "mozilla/dom/BlobURLProtocolHandler.h"
 | |
| #include "mozilla/dom/BrowserHost.h"
 | |
| #include "mozilla/dom/BrowsingContext.h"
 | |
| #include "mozilla/dom/BrowsingContextGroup.h"
 | |
| #include "mozilla/dom/CancelContentJSOptionsBinding.h"
 | |
| #include "mozilla/dom/CanonicalBrowsingContext.h"
 | |
| #include "mozilla/dom/ClientManager.h"
 | |
| #include "mozilla/dom/ContentChild.h"
 | |
| #include "mozilla/dom/DataTransfer.h"
 | |
| #include "mozilla/dom/Document.h"
 | |
| #include "mozilla/dom/Element.h"
 | |
| #include "mozilla/dom/ExternalHelperAppParent.h"
 | |
| #include "mozilla/dom/File.h"
 | |
| #include "mozilla/dom/FileSystemSecurity.h"
 | |
| #include "mozilla/dom/GeolocationBinding.h"
 | |
| #include "mozilla/dom/GeolocationPositionError.h"
 | |
| #include "mozilla/dom/GetFilesHelper.h"
 | |
| #include "mozilla/dom/IPCBlobUtils.h"
 | |
| #include "mozilla/dom/JSActorService.h"
 | |
| #include "mozilla/dom/JSProcessActorBinding.h"
 | |
| #include "mozilla/dom/LocalStorageCommon.h"
 | |
| #include "mozilla/dom/MediaController.h"
 | |
| #include "mozilla/dom/MemoryReportRequest.h"
 | |
| #include "mozilla/dom/MediaStatusManager.h"
 | |
| #include "mozilla/dom/Notification.h"
 | |
| #include "mozilla/dom/PContentPermissionRequestParent.h"
 | |
| #include "mozilla/dom/PCycleCollectWithLogsParent.h"
 | |
| #include "mozilla/dom/ParentProcessMessageManager.h"
 | |
| #include "mozilla/dom/Permissions.h"
 | |
| #include "mozilla/dom/ProcessMessageManager.h"
 | |
| #include "mozilla/dom/PushNotifier.h"
 | |
| #include "mozilla/dom/ServiceWorkerManager.h"
 | |
| #include "mozilla/dom/ServiceWorkerRegistrar.h"
 | |
| #include "mozilla/dom/ServiceWorkerUtils.h"
 | |
| #include "mozilla/dom/SessionHistoryEntry.h"
 | |
| #include "mozilla/dom/SessionStorageManager.h"
 | |
| #include "mozilla/dom/StorageIPC.h"
 | |
| #include "mozilla/dom/URLClassifierParent.h"
 | |
| #include "mozilla/dom/WakeLock.h"
 | |
| #include "mozilla/dom/WindowGlobalParent.h"
 | |
| #include "mozilla/dom/ipc/SharedMap.h"
 | |
| #include "mozilla/dom/ipc/StructuredCloneData.h"
 | |
| #include "mozilla/dom/nsMixedContentBlocker.h"
 | |
| #include "mozilla/dom/power/PowerManagerService.h"
 | |
| #include "mozilla/dom/quota/QuotaManagerService.h"
 | |
| #include "mozilla/extensions/ExtensionsParent.h"
 | |
| #include "mozilla/extensions/StreamFilterParent.h"
 | |
| #include "mozilla/gfx/GPUProcessManager.h"
 | |
| #include "mozilla/gfx/gfxVars.h"
 | |
| #include "mozilla/glean/GleanPings.h"
 | |
| #include "mozilla/hal_sandbox/PHalParent.h"
 | |
| #include "mozilla/intl/L10nRegistry.h"
 | |
| #include "mozilla/intl/LocaleService.h"
 | |
| #include "mozilla/ipc/BackgroundChild.h"
 | |
| #include "mozilla/ipc/BackgroundParent.h"
 | |
| #include "mozilla/ipc/ByteBuf.h"
 | |
| #include "mozilla/ipc/CrashReporterHost.h"
 | |
| #include "mozilla/ipc/Endpoint.h"
 | |
| #include "mozilla/ipc/FileDescriptorUtils.h"
 | |
| #include "mozilla/ipc/IPCStreamUtils.h"
 | |
| #include "mozilla/ipc/TestShellParent.h"
 | |
| #include "mozilla/layers/CompositorThread.h"
 | |
| #include "mozilla/layers/ImageBridgeParent.h"
 | |
| #include "mozilla/layers/LayerTreeOwnerTracker.h"
 | |
| #include "mozilla/layers/PAPZParent.h"
 | |
| #include "mozilla/loader/ScriptCacheActors.h"
 | |
| #include "mozilla/media/MediaParent.h"
 | |
| #include "mozilla/mozSpellChecker.h"
 | |
| #include "mozilla/net/CookieServiceParent.h"
 | |
| #include "mozilla/net/NeckoMessageUtils.h"
 | |
| #include "mozilla/net/NeckoParent.h"
 | |
| #include "mozilla/net/PCookieServiceParent.h"
 | |
| #include "mozilla/net/CookieKey.h"
 | |
| #include "mozilla/net/TRRService.h"
 | |
| #include "mozilla/TelemetryComms.h"
 | |
| #include "mozilla/TelemetryEventEnums.h"
 | |
| #include "mozilla/RemoteLazyInputStreamParent.h"
 | |
| #include "mozilla/widget/RemoteLookAndFeel.h"
 | |
| #include "mozilla/widget/ScreenManager.h"
 | |
| #include "mozilla/widget/TextRecognition.h"
 | |
| #include "nsAnonymousTemporaryFile.h"
 | |
| #include "nsAppRunner.h"
 | |
| #include "nsCExternalHandlerService.h"
 | |
| #include "nsCOMPtr.h"
 | |
| #include "nsChromeRegistryChrome.h"
 | |
| #include "nsConsoleMessage.h"
 | |
| #include "nsConsoleService.h"
 | |
| #include "nsContentPermissionHelper.h"
 | |
| #include "nsContentUtils.h"
 | |
| #include "nsCRT.h"
 | |
| #include "nsDebugImpl.h"
 | |
| #include "nsDirectoryServiceDefs.h"
 | |
| #include "nsDocShell.h"
 | |
| #include "nsEmbedCID.h"
 | |
| #include "nsFocusManager.h"
 | |
| #include "nsFrameLoader.h"
 | |
| #include "nsFrameMessageManager.h"
 | |
| #include "nsHashPropertyBag.h"
 | |
| #include "nsHyphenationManager.h"
 | |
| #include "nsIAlertsService.h"
 | |
| #include "nsIAppShell.h"
 | |
| #include "nsIAppWindow.h"
 | |
| #include "nsIAsyncInputStream.h"
 | |
| #include "nsIBidiKeyboard.h"
 | |
| #include "nsICaptivePortalService.h"
 | |
| #include "nsICertOverrideService.h"
 | |
| #include "nsIClipboard.h"
 | |
| #include "nsIContentProcess.h"
 | |
| #include "nsIContentSecurityPolicy.h"
 | |
| #include "nsICookie.h"
 | |
| #include "nsICrashService.h"
 | |
| #include "nsICycleCollectorListener.h"
 | |
| #include "nsIDOMChromeWindow.h"
 | |
| #include "nsIDocShell.h"
 | |
| #include "nsIDocShellTreeOwner.h"
 | |
| #include "nsIDragService.h"
 | |
| #include "nsIExternalProtocolService.h"
 | |
| #include "nsIGfxInfo.h"
 | |
| #include "nsIUserIdleService.h"
 | |
| #include "nsIInterfaceRequestorUtils.h"
 | |
| #include "nsILocalStorageManager.h"
 | |
| #include "nsIMemoryInfoDumper.h"
 | |
| #include "nsIMemoryReporter.h"
 | |
| #include "nsIMozBrowserFrame.h"
 | |
| #include "nsINetworkLinkService.h"
 | |
| #include "nsIObserverService.h"
 | |
| #include "nsIParentChannel.h"
 | |
| #include "nsIScriptError.h"
 | |
| #include "nsIScriptSecurityManager.h"
 | |
| #include "nsIServiceWorkerManager.h"
 | |
| #include "nsISiteSecurityService.h"
 | |
| #include "nsISound.h"
 | |
| #include "nsIStringBundle.h"
 | |
| #include "nsITimer.h"
 | |
| #include "nsIURL.h"
 | |
| #include "nsIWebBrowserChrome.h"
 | |
| #include "nsIX509Cert.h"
 | |
| #include "nsIXULRuntime.h"
 | |
| #if defined(MOZ_WIDGET_GTK) || defined(XP_WIN)
 | |
| #  include "nsIconChannel.h"
 | |
| #endif
 | |
| #include "nsMemoryInfoDumper.h"
 | |
| #include "nsMemoryReporterManager.h"
 | |
| #include "nsOpenURIInFrameParams.h"
 | |
| #include "nsPIWindowWatcher.h"
 | |
| #include "nsPluginHost.h"
 | |
| #include "nsPluginTags.h"
 | |
| #include "nsQueryObject.h"
 | |
| #include "nsReadableUtils.h"
 | |
| #include "nsSHistory.h"
 | |
| #include "nsScriptError.h"
 | |
| #include "nsServiceManagerUtils.h"
 | |
| #include "nsStreamUtils.h"
 | |
| #include "nsStyleSheetService.h"
 | |
| #include "nsThread.h"
 | |
| #include "nsThreadUtils.h"
 | |
| #include "nsWidgetsCID.h"
 | |
| #include "nsWindowWatcher.h"
 | |
| #include "prio.h"
 | |
| #include "private/pprio.h"
 | |
| #include "xpcpublic.h"
 | |
| #include "nsOpenWindowInfo.h"
 | |
| #include "nsFrameLoaderOwner.h"
 | |
| 
 | |
| #ifdef MOZ_WEBRTC
 | |
| #  include "jsapi/WebrtcGlobalParent.h"
 | |
| #endif
 | |
| 
 | |
| #if defined(XP_MACOSX)
 | |
| #  include "nsMacUtilsImpl.h"
 | |
| #  include "mozilla/AvailableMemoryWatcher.h"
 | |
| #endif
 | |
| 
 | |
| #if defined(ANDROID) || defined(LINUX)
 | |
| #  include "nsSystemInfo.h"
 | |
| #endif
 | |
| 
 | |
| #if defined(XP_LINUX)
 | |
| #  include "mozilla/Hal.h"
 | |
| #endif
 | |
| 
 | |
| #ifdef ANDROID
 | |
| #  include "gfxAndroidPlatform.h"
 | |
| #endif
 | |
| 
 | |
| #include "mozilla/PermissionManager.h"
 | |
| 
 | |
| #ifdef MOZ_WIDGET_ANDROID
 | |
| #  include "AndroidBridge.h"
 | |
| #  include "mozilla/java/GeckoProcessManagerWrappers.h"
 | |
| #  include "mozilla/java/GeckoProcessTypeWrappers.h"
 | |
| #endif
 | |
| 
 | |
| #ifdef MOZ_WIDGET_GTK
 | |
| #  include <gdk/gdk.h>
 | |
| #  include "mozilla/WidgetUtilsGtk.h"
 | |
| #endif
 | |
| 
 | |
| #include "mozilla/RemoteSpellCheckEngineParent.h"
 | |
| 
 | |
| #include "Crypto.h"
 | |
| 
 | |
| #ifdef MOZ_WEBSPEECH
 | |
| #  include "mozilla/dom/SpeechSynthesisParent.h"
 | |
| #endif
 | |
| 
 | |
| #if defined(MOZ_SANDBOX)
 | |
| #  include "mozilla/SandboxSettings.h"
 | |
| #  if defined(XP_LINUX)
 | |
| #    include "mozilla/SandboxInfo.h"
 | |
| #    include "mozilla/SandboxBroker.h"
 | |
| #    include "mozilla/SandboxBrokerPolicyFactory.h"
 | |
| #  endif
 | |
| #  if defined(XP_MACOSX)
 | |
| #    include "mozilla/Sandbox.h"
 | |
| #  endif
 | |
| #endif
 | |
| 
 | |
| #ifdef XP_WIN
 | |
| #  include "mozilla/widget/AudioSession.h"
 | |
| #  include "mozilla/WinDllServices.h"
 | |
| #endif
 | |
| 
 | |
| #ifdef ACCESSIBILITY
 | |
| #  include "nsAccessibilityService.h"
 | |
| #endif
 | |
| 
 | |
| #ifdef MOZ_CODE_COVERAGE
 | |
| #  include "mozilla/CodeCoverageHandler.h"
 | |
| #endif
 | |
| 
 | |
| #ifdef FUZZING_SNAPSHOT
 | |
| #  include "mozilla/fuzzing/IPCFuzzController.h"
 | |
| #endif
 | |
| 
 | |
| // For VP9Benchmark::sBenchmarkFpsPref
 | |
| #include "Benchmark.h"
 | |
| 
 | |
| #include "nsIToolkitProfileService.h"
 | |
| #include "nsIToolkitProfile.h"
 | |
| 
 | |
| static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
 | |
| 
 | |
| using base::KillProcess;
 | |
| 
 | |
| using namespace CrashReporter;
 | |
| using namespace mozilla::dom::power;
 | |
| using namespace mozilla::media;
 | |
| using namespace mozilla::embedding;
 | |
| using namespace mozilla::gfx;
 | |
| using namespace mozilla::gmp;
 | |
| using namespace mozilla::hal;
 | |
| using namespace mozilla::ipc;
 | |
| using namespace mozilla::intl;
 | |
| using namespace mozilla::layers;
 | |
| using namespace mozilla::layout;
 | |
| using namespace mozilla::net;
 | |
| using namespace mozilla::psm;
 | |
| using namespace mozilla::widget;
 | |
| using namespace mozilla::Telemetry;
 | |
| using mozilla::loader::PScriptCacheParent;
 | |
| using mozilla::Telemetry::ProcessID;
 | |
| 
 | |
| extern mozilla::LazyLogModule gFocusLog;
 | |
| 
 | |
| #define LOGFOCUS(args) MOZ_LOG(gFocusLog, mozilla::LogLevel::Debug, args)
 | |
| 
 | |
| namespace mozilla {
 | |
| namespace CubebUtils {
 | |
| extern FileDescriptor CreateAudioIPCConnection();
 | |
| }
 | |
| 
 | |
| namespace dom {
 | |
| 
 | |
| LazyLogModule gProcessLog("Process");
 | |
| 
 | |
| static std::map<RemoteDecodeIn, media::MediaCodecsSupported> sCodecsSupported;
 | |
| 
 | |
| /* static */
 | |
| uint32_t ContentParent::sMaxContentProcesses = 0;
 | |
| 
 | |
| /* static */
 | |
| LogModule* ContentParent::GetLog() { return gProcessLog; }
 | |
| 
 | |
| /* static */
 | |
| uint32_t ContentParent::sPageLoadEventCounter = 0;
 | |
| 
 | |
| #define NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC "ipc:network:set-offline"
 | |
| #define NS_IPC_IOSERVICE_SET_CONNECTIVITY_TOPIC "ipc:network:set-connectivity"
 | |
| 
 | |
| // IPC receiver for remote GC/CC logging.
 | |
| class CycleCollectWithLogsParent final : public PCycleCollectWithLogsParent {
 | |
|  public:
 | |
|   MOZ_COUNTED_DTOR(CycleCollectWithLogsParent)
 | |
| 
 | |
|   static bool AllocAndSendConstructor(ContentParent* aManager,
 | |
|                                       bool aDumpAllTraces,
 | |
|                                       nsICycleCollectorLogSink* aSink,
 | |
|                                       nsIDumpGCAndCCLogsCallback* aCallback) {
 | |
|     CycleCollectWithLogsParent* actor;
 | |
|     FILE* gcLog;
 | |
|     FILE* ccLog;
 | |
|     nsresult rv;
 | |
| 
 | |
|     actor = new CycleCollectWithLogsParent(aSink, aCallback);
 | |
|     rv = actor->mSink->Open(&gcLog, &ccLog);
 | |
|     if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|       delete actor;
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     return aManager->SendPCycleCollectWithLogsConstructor(
 | |
|         actor, aDumpAllTraces, FILEToFileDescriptor(gcLog),
 | |
|         FILEToFileDescriptor(ccLog));
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   virtual mozilla::ipc::IPCResult RecvCloseGCLog() override {
 | |
|     Unused << mSink->CloseGCLog();
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   virtual mozilla::ipc::IPCResult RecvCloseCCLog() override {
 | |
|     Unused << mSink->CloseCCLog();
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   virtual mozilla::ipc::IPCResult Recv__delete__() override {
 | |
|     // Report completion to mCallback only on successful
 | |
|     // completion of the protocol.
 | |
|     nsCOMPtr<nsIFile> gcLog, ccLog;
 | |
|     mSink->GetGcLog(getter_AddRefs(gcLog));
 | |
|     mSink->GetCcLog(getter_AddRefs(ccLog));
 | |
|     Unused << mCallback->OnDump(gcLog, ccLog, /* parent = */ false);
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   virtual void ActorDestroy(ActorDestroyReason aReason) override {
 | |
|     // If the actor is unexpectedly destroyed, we deliberately
 | |
|     // don't call Close[GC]CLog on the sink, because the logs may
 | |
|     // be incomplete.  See also the nsCycleCollectorLogSinkToFile
 | |
|     // implementaiton of those methods, and its destructor.
 | |
|   }
 | |
| 
 | |
|   CycleCollectWithLogsParent(nsICycleCollectorLogSink* aSink,
 | |
|                              nsIDumpGCAndCCLogsCallback* aCallback)
 | |
|       : mSink(aSink), mCallback(aCallback) {
 | |
|     MOZ_COUNT_CTOR(CycleCollectWithLogsParent);
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsICycleCollectorLogSink> mSink;
 | |
|   nsCOMPtr<nsIDumpGCAndCCLogsCallback> mCallback;
 | |
| };
 | |
| 
 | |
| // A memory reporter for ContentParent objects themselves.
 | |
| class ContentParentsMemoryReporter final : public nsIMemoryReporter {
 | |
|   ~ContentParentsMemoryReporter() = default;
 | |
| 
 | |
|  public:
 | |
|   NS_DECL_ISUPPORTS
 | |
|   NS_DECL_NSIMEMORYREPORTER
 | |
| };
 | |
| 
 | |
| NS_IMPL_ISUPPORTS(ContentParentsMemoryReporter, nsIMemoryReporter)
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| ContentParentsMemoryReporter::CollectReports(
 | |
|     nsIHandleReportCallback* aHandleReport, nsISupports* aData,
 | |
|     bool aAnonymize) {
 | |
|   AutoTArray<ContentParent*, 16> cps;
 | |
|   ContentParent::GetAllEvenIfDead(cps);
 | |
| 
 | |
|   for (uint32_t i = 0; i < cps.Length(); i++) {
 | |
|     ContentParent* cp = cps[i];
 | |
|     MessageChannel* channel = cp->GetIPCChannel();
 | |
| 
 | |
|     nsString friendlyName;
 | |
|     cp->FriendlyName(friendlyName, aAnonymize);
 | |
| 
 | |
|     cp->AddRef();
 | |
|     nsrefcnt refcnt = cp->Release();
 | |
| 
 | |
|     const char* channelStr = "no channel";
 | |
|     uint32_t numQueuedMessages = 0;
 | |
|     if (channel) {
 | |
|       if (channel->IsClosed()) {
 | |
|         channelStr = "closed channel";
 | |
|       } else {
 | |
|         channelStr = "open channel";
 | |
|       }
 | |
|       numQueuedMessages =
 | |
|           0;  // XXX was channel->Unsound_NumQueuedMessages(); Bug 1754876
 | |
|     }
 | |
| 
 | |
|     nsPrintfCString path(
 | |
|         "queued-ipc-messages/content-parent"
 | |
|         "(%s, pid=%d, %s, 0x%p, refcnt=%" PRIuPTR ")",
 | |
|         NS_ConvertUTF16toUTF8(friendlyName).get(), cp->Pid(), channelStr,
 | |
|         static_cast<nsIObserver*>(cp), refcnt);
 | |
| 
 | |
|     constexpr auto desc =
 | |
|         "The number of unset IPC messages held in this ContentParent's "
 | |
|         "channel.  A large value here might indicate that we're leaking "
 | |
|         "messages.  Similarly, a ContentParent object for a process that's no "
 | |
|         "longer running could indicate that we're leaking ContentParents."_ns;
 | |
| 
 | |
|     aHandleReport->Callback(/* process */ ""_ns, path, KIND_OTHER, UNITS_COUNT,
 | |
|                             numQueuedMessages, desc, aData);
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| // A hashtable (by type) of processes/ContentParents.  This includes
 | |
| // processes that are in the Preallocator cache (which would be type
 | |
| // 'prealloc'), and recycled processes ('web' and in the future
 | |
| // eTLD+1-locked) processes).
 | |
| nsClassHashtable<nsCStringHashKey, nsTArray<ContentParent*>>*
 | |
|     ContentParent::sBrowserContentParents;
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| uint64_t ComputeLoadedOriginHash(nsIPrincipal* aPrincipal) {
 | |
|   uint32_t originNoSuffix =
 | |
|       BasePrincipal::Cast(aPrincipal)->GetOriginNoSuffixHash();
 | |
|   uint32_t originSuffix =
 | |
|       BasePrincipal::Cast(aPrincipal)->GetOriginSuffixHash();
 | |
| 
 | |
|   return ((uint64_t)originNoSuffix) << 32 | originSuffix;
 | |
| }
 | |
| 
 | |
| class ScriptableCPInfo final : public nsIContentProcessInfo {
 | |
|  public:
 | |
|   explicit ScriptableCPInfo(ContentParent* aParent) : mContentParent(aParent) {
 | |
|     MOZ_ASSERT(mContentParent);
 | |
|   }
 | |
| 
 | |
|   NS_DECL_ISUPPORTS
 | |
|   NS_DECL_NSICONTENTPROCESSINFO
 | |
| 
 | |
|   void ProcessDied() { mContentParent = nullptr; }
 | |
| 
 | |
|  private:
 | |
|   ~ScriptableCPInfo() { MOZ_ASSERT(!mContentParent, "must call ProcessDied"); }
 | |
| 
 | |
|   ContentParent* mContentParent;
 | |
| };
 | |
| 
 | |
| NS_IMPL_ISUPPORTS(ScriptableCPInfo, nsIContentProcessInfo)
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| ScriptableCPInfo::GetIsAlive(bool* aIsAlive) {
 | |
|   *aIsAlive = mContentParent != nullptr;
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| ScriptableCPInfo::GetProcessId(int32_t* aPID) {
 | |
|   if (!mContentParent) {
 | |
|     *aPID = -1;
 | |
|     return NS_ERROR_NOT_INITIALIZED;
 | |
|   }
 | |
| 
 | |
|   *aPID = mContentParent->Pid();
 | |
|   if (*aPID == -1) {
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| ScriptableCPInfo::GetTabCount(int32_t* aTabCount) {
 | |
|   if (!mContentParent) {
 | |
|     return NS_ERROR_NOT_INITIALIZED;
 | |
|   }
 | |
| 
 | |
|   ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
 | |
|   *aTabCount = cpm->GetBrowserParentCountByProcessId(mContentParent->ChildID());
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| ScriptableCPInfo::GetMessageManager(nsISupports** aMessenger) {
 | |
|   *aMessenger = nullptr;
 | |
|   if (!mContentParent) {
 | |
|     return NS_ERROR_NOT_INITIALIZED;
 | |
|   }
 | |
| 
 | |
|   RefPtr<ProcessMessageManager> manager = mContentParent->GetMessageManager();
 | |
|   manager.forget(aMessenger);
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| ProcessID GetTelemetryProcessID(const nsACString& remoteType) {
 | |
|   // OOP WebExtensions run in a content process.
 | |
|   // For Telemetry though we want to break out collected data from the
 | |
|   // WebExtensions process into a separate bucket, to make sure we can analyze
 | |
|   // it separately and avoid skewing normal content process metrics.
 | |
|   return remoteType == EXTENSION_REMOTE_TYPE ? ProcessID::Extension
 | |
|                                              : ProcessID::Content;
 | |
| }
 | |
| 
 | |
| }  // anonymous namespace
 | |
| 
 | |
| StaticAutoPtr<nsTHashMap<nsUint32HashKey, ContentParent*>>
 | |
|     ContentParent::sJSPluginContentParents;
 | |
| StaticAutoPtr<LinkedList<ContentParent>> ContentParent::sContentParents;
 | |
| StaticRefPtr<ContentParent> ContentParent::sRecycledE10SProcess;
 | |
| #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
 | |
| StaticAutoPtr<SandboxBrokerPolicyFactory>
 | |
|     ContentParent::sSandboxBrokerPolicyFactory;
 | |
| #endif
 | |
| #if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
 | |
| StaticAutoPtr<std::vector<std::string>> ContentParent::sMacSandboxParams;
 | |
| #endif
 | |
| 
 | |
| // Set to true when the first content process gets created.
 | |
| static bool sCreatedFirstContentProcess = false;
 | |
| 
 | |
| #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
 | |
| // True when we're running the process selection code, and do not expect to
 | |
| // enter code paths where processes may die.
 | |
| static bool sInProcessSelector = false;
 | |
| #endif
 | |
| 
 | |
| // The first content child has ID 1, so the chrome process can have ID 0.
 | |
| static uint64_t gContentChildID = 1;
 | |
| 
 | |
| static const char* sObserverTopics[] = {
 | |
|     NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC,
 | |
|     NS_IPC_IOSERVICE_SET_CONNECTIVITY_TOPIC,
 | |
|     NS_IPC_CAPTIVE_PORTAL_SET_STATE,
 | |
|     "application-background",
 | |
|     "application-foreground",
 | |
|     "memory-pressure",
 | |
|     "child-gc-request",
 | |
|     "child-cc-request",
 | |
|     "child-mmu-request",
 | |
|     "child-ghost-request",
 | |
|     "last-pb-context-exited",
 | |
|     "file-watcher-update",
 | |
| #ifdef ACCESSIBILITY
 | |
|     "a11y-init-or-shutdown",
 | |
| #endif
 | |
|     "cacheservice:empty-cache",
 | |
|     "intl:app-locales-changed",
 | |
|     "intl:requested-locales-changed",
 | |
|     "cookie-changed",
 | |
|     "private-cookie-changed",
 | |
|     NS_NETWORK_LINK_TYPE_TOPIC,
 | |
|     NS_NETWORK_TRR_MODE_CHANGED_TOPIC,
 | |
|     "network:socket-process-crashed",
 | |
|     DEFAULT_TIMEZONE_CHANGED_OBSERVER_TOPIC,
 | |
| };
 | |
| 
 | |
| // PreallocateProcess is called by the PreallocatedProcessManager.
 | |
| // ContentParent then takes this process back within GetNewOrUsedBrowserProcess.
 | |
| /*static*/ already_AddRefed<ContentParent>
 | |
| ContentParent::MakePreallocProcess() {
 | |
|   RefPtr<ContentParent> process = new ContentParent(PREALLOC_REMOTE_TYPE);
 | |
|   return process.forget();
 | |
| }
 | |
| 
 | |
| /*static*/
 | |
| void ContentParent::StartUp() {
 | |
|   // FIXME Bug 1023701 - Stop using ContentParent static methods in
 | |
|   // child process
 | |
|   if (!XRE_IsParentProcess()) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // From this point on, NS_WARNING, NS_ASSERTION, etc. should print out the
 | |
|   // PID along with the warning.
 | |
|   nsDebugImpl::SetMultiprocessMode("Parent");
 | |
| 
 | |
|   // Note: This reporter measures all ContentParents.
 | |
|   RegisterStrongMemoryReporter(new ContentParentsMemoryReporter());
 | |
| 
 | |
|   BackgroundChild::Startup();
 | |
|   ClientManager::Startup();
 | |
| 
 | |
|   Preferences::RegisterCallbackAndCall(&OnFissionBlocklistPrefChange,
 | |
|                                        kFissionEnforceBlockList);
 | |
|   Preferences::RegisterCallbackAndCall(&OnFissionBlocklistPrefChange,
 | |
|                                        kFissionOmitBlockListValues);
 | |
| 
 | |
| #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
 | |
|   sSandboxBrokerPolicyFactory = new SandboxBrokerPolicyFactory();
 | |
| #endif
 | |
| 
 | |
| #if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
 | |
|   sMacSandboxParams = new std::vector<std::string>();
 | |
| #endif
 | |
| }
 | |
| 
 | |
| /*static*/
 | |
| void ContentParent::ShutDown() {
 | |
|   // For the most, we rely on normal process shutdown and
 | |
|   // ClearOnShutdown() to clean up our state.
 | |
| 
 | |
| #if defined(XP_LINUX) && defined(MOZ_SANDBOX)
 | |
|   sSandboxBrokerPolicyFactory = nullptr;
 | |
| #endif
 | |
| 
 | |
| #if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
 | |
|   sMacSandboxParams = nullptr;
 | |
| #endif
 | |
| }
 | |
| 
 | |
| /*static*/
 | |
| uint32_t ContentParent::GetPoolSize(const nsACString& aContentProcessType) {
 | |
|   if (!sBrowserContentParents) {
 | |
|     return 0;
 | |
|   }
 | |
| 
 | |
|   nsTArray<ContentParent*>* parents =
 | |
|       sBrowserContentParents->Get(aContentProcessType);
 | |
| 
 | |
|   return parents ? parents->Length() : 0;
 | |
| }
 | |
| 
 | |
| /*static*/ nsTArray<ContentParent*>& ContentParent::GetOrCreatePool(
 | |
|     const nsACString& aContentProcessType) {
 | |
|   if (!sBrowserContentParents) {
 | |
|     sBrowserContentParents =
 | |
|         new nsClassHashtable<nsCStringHashKey, nsTArray<ContentParent*>>;
 | |
|   }
 | |
| 
 | |
|   return *sBrowserContentParents->GetOrInsertNew(aContentProcessType);
 | |
| }
 | |
| 
 | |
| const nsDependentCSubstring RemoteTypePrefix(
 | |
|     const nsACString& aContentProcessType) {
 | |
|   // The suffix after a `=` in a remoteType is dynamic, and used to control the
 | |
|   // process pool to use.
 | |
|   int32_t equalIdx = aContentProcessType.FindChar(L'=');
 | |
|   if (equalIdx == kNotFound) {
 | |
|     equalIdx = aContentProcessType.Length();
 | |
|   }
 | |
|   return StringHead(aContentProcessType, equalIdx);
 | |
| }
 | |
| 
 | |
| bool IsWebRemoteType(const nsACString& aContentProcessType) {
 | |
|   // Note: matches webIsolated, web, and webCOOP+COEP types.
 | |
|   return StringBeginsWith(aContentProcessType, DEFAULT_REMOTE_TYPE);
 | |
| }
 | |
| 
 | |
| bool IsWebCoopCoepRemoteType(const nsACString& aContentProcessType) {
 | |
|   return StringBeginsWith(aContentProcessType,
 | |
|                           WITH_COOP_COEP_REMOTE_TYPE_PREFIX);
 | |
| }
 | |
| 
 | |
| bool IsPrivilegedMozillaRemoteType(const nsACString& aContentProcessType) {
 | |
|   return aContentProcessType == PRIVILEGEDMOZILLA_REMOTE_TYPE;
 | |
| }
 | |
| 
 | |
| bool IsExtensionRemoteType(const nsACString& aContentProcessType) {
 | |
|   return aContentProcessType == EXTENSION_REMOTE_TYPE;
 | |
| }
 | |
| 
 | |
| /*static*/
 | |
| uint32_t ContentParent::GetMaxProcessCount(
 | |
|     const nsACString& aContentProcessType) {
 | |
|   // Max process count is based only on the prefix.
 | |
|   const nsDependentCSubstring processTypePrefix =
 | |
|       RemoteTypePrefix(aContentProcessType);
 | |
| 
 | |
|   // Check for the default remote type of "web", as it uses different prefs.
 | |
|   if (processTypePrefix == DEFAULT_REMOTE_TYPE) {
 | |
|     return GetMaxWebProcessCount();
 | |
|   }
 | |
| 
 | |
|   // Read the pref controling this remote type. `dom.ipc.processCount` is not
 | |
|   // used as a fallback, as it is intended to control the number of "web"
 | |
|   // content processes, checked in `mozilla::GetMaxWebProcessCount()`.
 | |
|   nsAutoCString processCountPref("dom.ipc.processCount.");
 | |
|   processCountPref.Append(processTypePrefix);
 | |
| 
 | |
|   int32_t maxContentParents = Preferences::GetInt(processCountPref.get(), 1);
 | |
|   if (maxContentParents < 1) {
 | |
|     maxContentParents = 1;
 | |
|   }
 | |
| 
 | |
|   return static_cast<uint32_t>(maxContentParents);
 | |
| }
 | |
| 
 | |
| /*static*/
 | |
| bool ContentParent::IsMaxProcessCountReached(
 | |
|     const nsACString& aContentProcessType) {
 | |
|   return GetPoolSize(aContentProcessType) >=
 | |
|          GetMaxProcessCount(aContentProcessType);
 | |
| }
 | |
| 
 | |
| // Really more ReleaseUnneededProcesses()
 | |
| /*static*/
 | |
| void ContentParent::ReleaseCachedProcesses() {
 | |
|   MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
 | |
|           ("ReleaseCachedProcesses:"));
 | |
|   if (!sBrowserContentParents) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
| #ifdef DEBUG
 | |
|   for (const auto& cps : *sBrowserContentParents) {
 | |
|     MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
 | |
|             ("%s: %zu processes", PromiseFlatCString(cps.GetKey()).get(),
 | |
|              cps.GetData()->Length()));
 | |
|   }
 | |
| #endif
 | |
|   // We process the toRelease array outside of the iteration to avoid modifying
 | |
|   // the list (via RemoveFromList()) while we're iterating it.
 | |
|   nsTArray<ContentParent*> toRelease;
 | |
|   for (const auto& contentParents : sBrowserContentParents->Values()) {
 | |
|     // Shutting down these processes will change the array so let's use another
 | |
|     // array for the removal.
 | |
|     for (auto* cp : *contentParents) {
 | |
|       if (cp->ManagedPBrowserParent().Count() == 0 &&
 | |
|           !cp->HasActiveWorkerOrJSPlugin() &&
 | |
|           cp->mRemoteType == DEFAULT_REMOTE_TYPE) {
 | |
|         toRelease.AppendElement(cp);
 | |
|       } else {
 | |
|         MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
 | |
|                 ("  Skipping %p (%s), count %d, HasActiveWorkerOrJSPlugin %d",
 | |
|                  cp, cp->mRemoteType.get(), cp->ManagedPBrowserParent().Count(),
 | |
|                  cp->HasActiveWorkerOrJSPlugin()));
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   for (auto* cp : toRelease) {
 | |
|     MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
 | |
|             ("  Shutdown %p (%s)", cp, cp->mRemoteType.get()));
 | |
|     PreallocatedProcessManager::Erase(cp);
 | |
|     // Start a soft shutdown.
 | |
|     cp->ShutDownProcess(SEND_SHUTDOWN_MESSAGE);
 | |
|     // Make sure we don't select this process for new tabs.
 | |
|     cp->MarkAsDead();
 | |
|     // Make sure that this process is no longer accessible from JS by its
 | |
|     // message manager.
 | |
|     cp->ShutDownMessageManager();
 | |
|   }
 | |
| }
 | |
| 
 | |
| /*static*/
 | |
| already_AddRefed<ContentParent> ContentParent::MinTabSelect(
 | |
|     const nsTArray<ContentParent*>& aContentParents,
 | |
|     int32_t aMaxContentParents) {
 | |
|   uint32_t maxSelectable =
 | |
|       std::min(static_cast<uint32_t>(aContentParents.Length()),
 | |
|                static_cast<uint32_t>(aMaxContentParents));
 | |
|   uint32_t min = INT_MAX;
 | |
|   RefPtr<ContentParent> candidate;
 | |
|   ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
 | |
| 
 | |
|   for (uint32_t i = 0; i < maxSelectable; i++) {
 | |
|     ContentParent* p = aContentParents[i];
 | |
|     MOZ_DIAGNOSTIC_ASSERT(!p->IsDead());
 | |
| 
 | |
|     // Ignore processes that were slated for removal but not yet removed from
 | |
|     // the pool (see also GetUsedBrowserProcess and BlockShutdown).
 | |
|     if (!p->IsShuttingDown()) {
 | |
|       uint32_t tabCount = cpm->GetBrowserParentCountByProcessId(p->ChildID());
 | |
|       if (tabCount < min) {
 | |
|         candidate = p;
 | |
|         min = tabCount;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // If all current processes have at least one tab and we have not yet reached
 | |
|   // the maximum, use a new process.
 | |
|   if (min > 0 &&
 | |
|       aContentParents.Length() < static_cast<uint32_t>(aMaxContentParents)) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   // Otherwise we return candidate.
 | |
|   return candidate.forget();
 | |
| }
 | |
| 
 | |
| /* static */
 | |
| already_AddRefed<nsIPrincipal>
 | |
| ContentParent::CreateRemoteTypeIsolationPrincipal(
 | |
|     const nsACString& aRemoteType) {
 | |
|   if ((RemoteTypePrefix(aRemoteType) != FISSION_WEB_REMOTE_TYPE) &&
 | |
|       !StringBeginsWith(aRemoteType, WITH_COOP_COEP_REMOTE_TYPE_PREFIX)) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   int32_t offset = aRemoteType.FindChar('=') + 1;
 | |
|   MOZ_ASSERT(offset > 1, "can not extract origin from that remote type");
 | |
|   nsAutoCString origin(
 | |
|       Substring(aRemoteType, offset, aRemoteType.Length() - offset));
 | |
| 
 | |
|   nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
 | |
|   nsCOMPtr<nsIPrincipal> principal;
 | |
|   ssm->CreateContentPrincipalFromOrigin(origin, getter_AddRefs(principal));
 | |
|   return principal.forget();
 | |
| }
 | |
| 
 | |
| /*static*/
 | |
| already_AddRefed<ContentParent> ContentParent::GetUsedBrowserProcess(
 | |
|     const nsACString& aRemoteType, nsTArray<ContentParent*>& aContentParents,
 | |
|     uint32_t aMaxContentParents, bool aPreferUsed, ProcessPriority aPriority) {
 | |
| #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
 | |
|   AutoRestore ar(sInProcessSelector);
 | |
|   sInProcessSelector = true;
 | |
| #endif
 | |
| 
 | |
|   uint32_t numberOfParents = aContentParents.Length();
 | |
|   nsTArray<RefPtr<nsIContentProcessInfo>> infos(numberOfParents);
 | |
|   for (auto* cp : aContentParents) {
 | |
|     infos.AppendElement(cp->mScriptableHelper);
 | |
|   }
 | |
| 
 | |
|   if (aPreferUsed && numberOfParents) {
 | |
|     // If we prefer re-using existing content processes, we don't want to create
 | |
|     // a new process, and instead re-use an existing one, so pretend the process
 | |
|     // limit is at the current number of processes.
 | |
|     aMaxContentParents = numberOfParents;
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsIContentProcessProvider> cpp =
 | |
|       do_GetService("@mozilla.org/ipc/processselector;1");
 | |
|   int32_t index;
 | |
|   if (cpp && NS_SUCCEEDED(cpp->ProvideProcess(aRemoteType, infos,
 | |
|                                               aMaxContentParents, &index))) {
 | |
|     // If the provider returned an existing ContentParent, use that one.
 | |
|     if (0 <= index && static_cast<uint32_t>(index) <= aMaxContentParents) {
 | |
|       RefPtr<ContentParent> retval = aContentParents[index];
 | |
|       // Ignore processes that were slated for removal but not yet removed from
 | |
|       // the pool.
 | |
|       if (!retval->IsShuttingDown()) {
 | |
|         if (profiler_thread_is_being_profiled_for_markers()) {
 | |
|           nsPrintfCString marker("Reused process %u",
 | |
|                                  (unsigned int)retval->ChildID());
 | |
|           PROFILER_MARKER_TEXT("Process", DOM, {}, marker);
 | |
|         }
 | |
|         MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
 | |
|                 ("GetUsedProcess: Reused process %p (%u) for %s", retval.get(),
 | |
|                  (unsigned int)retval->ChildID(),
 | |
|                  PromiseFlatCString(aRemoteType).get()));
 | |
|         retval->AssertAlive();
 | |
|         retval->StopRecyclingE10SOnly(true);
 | |
|         return retval.forget();
 | |
|       }
 | |
|     }
 | |
|   } else {
 | |
|     // If there was a problem with the JS chooser, fall back to a random
 | |
|     // selection.
 | |
|     NS_WARNING("nsIContentProcessProvider failed to return a process");
 | |
|     RefPtr<ContentParent> random;
 | |
|     if ((random = MinTabSelect(aContentParents, aMaxContentParents))) {
 | |
|       MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
 | |
|               ("GetUsedProcess: Reused random process %p (%d) for %s",
 | |
|                random.get(), (unsigned int)random->ChildID(),
 | |
|                PromiseFlatCString(aRemoteType).get()));
 | |
|       random->AssertAlive();
 | |
|       random->StopRecyclingE10SOnly(true);
 | |
|       return random.forget();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // If we are loading into the "web" remote type, are choosing to launch a new
 | |
|   // tab, and have a recycled E10S process, we should launch into that process.
 | |
|   if (aRemoteType == DEFAULT_REMOTE_TYPE && sRecycledE10SProcess) {
 | |
|     RefPtr<ContentParent> recycled = sRecycledE10SProcess;
 | |
|     MOZ_DIAGNOSTIC_ASSERT(recycled->GetRemoteType() == DEFAULT_REMOTE_TYPE);
 | |
|     recycled->AssertAlive();
 | |
|     recycled->StopRecyclingE10SOnly(true);
 | |
|     if (profiler_thread_is_being_profiled_for_markers()) {
 | |
|       nsPrintfCString marker("Recycled process %u (%p)",
 | |
|                              (unsigned int)recycled->ChildID(), recycled.get());
 | |
|       PROFILER_MARKER_TEXT("Process", DOM, {}, marker);
 | |
|     }
 | |
|     MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
 | |
|             ("Recycled process %p", recycled.get()));
 | |
| 
 | |
|     return recycled.forget();
 | |
|   }
 | |
| 
 | |
|   // Try to take a preallocated process except for certain remote types.
 | |
|   // Note: this process may not have finished launching yet
 | |
|   RefPtr<ContentParent> preallocated;
 | |
|   if (aRemoteType != FILE_REMOTE_TYPE &&
 | |
|       aRemoteType != PRIVILEGEDABOUT_REMOTE_TYPE &&
 | |
|       aRemoteType != EXTENSION_REMOTE_TYPE &&  // Bug 1638119
 | |
|       (preallocated = PreallocatedProcessManager::Take(aRemoteType))) {
 | |
|     MOZ_DIAGNOSTIC_ASSERT(preallocated->GetRemoteType() ==
 | |
|                           PREALLOC_REMOTE_TYPE);
 | |
|     MOZ_DIAGNOSTIC_ASSERT(sRecycledE10SProcess != preallocated);
 | |
|     preallocated->AssertAlive();
 | |
| 
 | |
|     if (profiler_thread_is_being_profiled_for_markers()) {
 | |
|       nsPrintfCString marker(
 | |
|           "Assigned preallocated process %u%s",
 | |
|           (unsigned int)preallocated->ChildID(),
 | |
|           preallocated->IsLaunching() ? " (still launching)" : "");
 | |
|       PROFILER_MARKER_TEXT("Process", DOM, {}, marker);
 | |
|     }
 | |
|     MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
 | |
|             ("Adopted preallocated process %p for type %s%s",
 | |
|              preallocated.get(), PromiseFlatCString(aRemoteType).get(),
 | |
|              preallocated->IsLaunching() ? " (still launching)" : ""));
 | |
| 
 | |
|     // This ensures that the preallocator won't shut down the process once
 | |
|     // it finishes starting
 | |
|     preallocated->mRemoteType.Assign(aRemoteType);
 | |
|     {
 | |
|       MutexAutoLock lock(preallocated->mThreadsafeHandle->mMutex);
 | |
|       preallocated->mThreadsafeHandle->mRemoteType = preallocated->mRemoteType;
 | |
|     }
 | |
|     preallocated->mRemoteTypeIsolationPrincipal =
 | |
|         CreateRemoteTypeIsolationPrincipal(aRemoteType);
 | |
|     preallocated->mActivateTS = TimeStamp::Now();
 | |
|     preallocated->AddToPool(aContentParents);
 | |
| 
 | |
|     // rare, but will happen
 | |
|     if (!preallocated->IsLaunching()) {
 | |
|       // Specialize this process for the appropriate remote type, and activate
 | |
|       // it.
 | |
| 
 | |
|       Unused << preallocated->SendRemoteType(preallocated->mRemoteType,
 | |
|                                              preallocated->mProfile);
 | |
| 
 | |
|       nsCOMPtr<nsIObserverService> obs =
 | |
|           mozilla::services::GetObserverService();
 | |
|       if (obs) {
 | |
|         nsAutoString cpId;
 | |
|         cpId.AppendInt(static_cast<uint64_t>(preallocated->ChildID()));
 | |
|         obs->NotifyObservers(static_cast<nsIObserver*>(preallocated),
 | |
|                              "process-type-set", cpId.get());
 | |
|         preallocated->AssertAlive();
 | |
|       }
 | |
|     }
 | |
|     return preallocated.forget();
 | |
|   }
 | |
| 
 | |
|   return nullptr;
 | |
| }
 | |
| 
 | |
| /*static*/
 | |
| already_AddRefed<ContentParent>
 | |
| ContentParent::GetNewOrUsedLaunchingBrowserProcess(
 | |
|     const nsACString& aRemoteType, BrowsingContextGroup* aGroup,
 | |
|     ProcessPriority aPriority, bool aPreferUsed) {
 | |
|   MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
 | |
|           ("GetNewOrUsedProcess for type %s",
 | |
|            PromiseFlatCString(aRemoteType).get()));
 | |
| 
 | |
|   // Fallback check (we really want our callers to avoid this).
 | |
|   if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
 | |
|     MOZ_DIAGNOSTIC_ASSERT(
 | |
|         false, "Late attempt to GetNewOrUsedLaunchingBrowserProcess!");
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   // If we have an existing host process attached to this BrowsingContextGroup,
 | |
|   // always return it, as we can never have multiple host processes within a
 | |
|   // single BrowsingContextGroup.
 | |
|   RefPtr<ContentParent> contentParent;
 | |
|   if (aGroup) {
 | |
|     contentParent = aGroup->GetHostProcess(aRemoteType);
 | |
|     Unused << NS_WARN_IF(contentParent && contentParent->IsShuttingDown());
 | |
|     if (contentParent && !contentParent->IsShuttingDown()) {
 | |
|       MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
 | |
|               ("GetNewOrUsedProcess: Existing host process %p (launching %d)",
 | |
|                contentParent.get(), contentParent->IsLaunching()));
 | |
|       contentParent->AssertAlive();
 | |
|       contentParent->StopRecyclingE10SOnly(true);
 | |
|       return contentParent.forget();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   nsTArray<ContentParent*>& contentParents = GetOrCreatePool(aRemoteType);
 | |
|   uint32_t maxContentParents = GetMaxProcessCount(aRemoteType);
 | |
| 
 | |
|   // Let's try and reuse an existing process.
 | |
|   contentParent = GetUsedBrowserProcess(
 | |
|       aRemoteType, contentParents, maxContentParents, aPreferUsed, aPriority);
 | |
| 
 | |
|   if (!contentParent) {
 | |
|     // No reusable process. Let's create and launch one.
 | |
|     // The life cycle will be set to `LifecycleState::LAUNCHING`.
 | |
|     MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
 | |
|             ("Launching new process immediately for type %s",
 | |
|              PromiseFlatCString(aRemoteType).get()));
 | |
| 
 | |
|     contentParent = new ContentParent(aRemoteType);
 | |
|     if (NS_WARN_IF(!contentParent->BeginSubprocessLaunch(aPriority))) {
 | |
|       // Launch aborted because of shutdown. Bailout.
 | |
|       contentParent->LaunchSubprocessReject();
 | |
|       return nullptr;
 | |
|     }
 | |
|     // Until the new process is ready let's not allow to start up any
 | |
|     // preallocated processes. The blocker will be removed once we receive
 | |
|     // the first idle message.
 | |
|     contentParent->mIsAPreallocBlocker = true;
 | |
|     PreallocatedProcessManager::AddBlocker(aRemoteType, contentParent);
 | |
| 
 | |
|     // Store this process for future reuse.
 | |
|     contentParent->AddToPool(contentParents);
 | |
| 
 | |
|     MOZ_LOG(
 | |
|         ContentParent::GetLog(), LogLevel::Debug,
 | |
|         ("GetNewOrUsedProcess: new immediate process %p", contentParent.get()));
 | |
|   }
 | |
|   // else we have an existing or preallocated process (which may be
 | |
|   // still launching)
 | |
| 
 | |
|   contentParent->AssertAlive();
 | |
|   contentParent->StopRecyclingE10SOnly(true);
 | |
|   if (aGroup) {
 | |
|     aGroup->EnsureHostProcess(contentParent);
 | |
|   }
 | |
|   return contentParent.forget();
 | |
| }
 | |
| 
 | |
| /*static*/
 | |
| RefPtr<ContentParent::LaunchPromise>
 | |
| ContentParent::GetNewOrUsedBrowserProcessAsync(const nsACString& aRemoteType,
 | |
|                                                BrowsingContextGroup* aGroup,
 | |
|                                                ProcessPriority aPriority,
 | |
|                                                bool aPreferUsed) {
 | |
|   // Obtain a `ContentParent` launched asynchronously.
 | |
|   RefPtr<ContentParent> contentParent = GetNewOrUsedLaunchingBrowserProcess(
 | |
|       aRemoteType, aGroup, aPriority, aPreferUsed);
 | |
|   if (!contentParent) {
 | |
|     // In case of launch error, stop here.
 | |
|     return LaunchPromise::CreateAndReject(LaunchError(), __func__);
 | |
|   }
 | |
|   return contentParent->WaitForLaunchAsync(aPriority);
 | |
| }
 | |
| 
 | |
| /*static*/
 | |
| already_AddRefed<ContentParent> ContentParent::GetNewOrUsedBrowserProcess(
 | |
|     const nsACString& aRemoteType, BrowsingContextGroup* aGroup,
 | |
|     ProcessPriority aPriority, bool aPreferUsed) {
 | |
|   RefPtr<ContentParent> contentParent = GetNewOrUsedLaunchingBrowserProcess(
 | |
|       aRemoteType, aGroup, aPriority, aPreferUsed);
 | |
|   if (!contentParent || !contentParent->WaitForLaunchSync(aPriority)) {
 | |
|     // In case of launch error, stop here.
 | |
|     return nullptr;
 | |
|   }
 | |
|   return contentParent.forget();
 | |
| }
 | |
| 
 | |
| RefPtr<ContentParent::LaunchPromise> ContentParent::WaitForLaunchAsync(
 | |
|     ProcessPriority aPriority) {
 | |
|   MOZ_DIAGNOSTIC_ASSERT(!IsDead());
 | |
|   if (!IsLaunching()) {
 | |
|     MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
 | |
|             ("WaitForLaunchAsync: launched"));
 | |
|     return LaunchPromise::CreateAndResolve(this, __func__);
 | |
|   }
 | |
| 
 | |
|   // We've started an async content process launch.
 | |
|   Telemetry::Accumulate(Telemetry::CONTENT_PROCESS_LAUNCH_IS_SYNC, 0);
 | |
| 
 | |
|   // We have located a process that hasn't finished initializing, then attempt
 | |
|   // to finish initializing. Both `LaunchSubprocessResolve` and
 | |
|   // `LaunchSubprocessReject` are safe to call multiple times if we race with
 | |
|   // other `WaitForLaunchAsync` callbacks.
 | |
|   return mSubprocess->WhenProcessHandleReady()->Then(
 | |
|       GetCurrentSerialEventTarget(), __func__,
 | |
|       [self = RefPtr{this}, aPriority] {
 | |
|         if (self->LaunchSubprocessResolve(/* aIsSync = */ false, aPriority)) {
 | |
|           MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
 | |
|                   ("WaitForLaunchAsync: async, now launched"));
 | |
|           self->mActivateTS = TimeStamp::Now();
 | |
|           return LaunchPromise::CreateAndResolve(self, __func__);
 | |
|         }
 | |
| 
 | |
|         self->LaunchSubprocessReject();
 | |
|         return LaunchPromise::CreateAndReject(LaunchError(), __func__);
 | |
|       },
 | |
|       [self = RefPtr{this}] {
 | |
|         MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
 | |
|                 ("WaitForLaunchAsync: async, rejected"));
 | |
|         self->LaunchSubprocessReject();
 | |
|         return LaunchPromise::CreateAndReject(LaunchError(), __func__);
 | |
|       });
 | |
| }
 | |
| 
 | |
| bool ContentParent::WaitForLaunchSync(ProcessPriority aPriority) {
 | |
|   MOZ_DIAGNOSTIC_ASSERT(!IsDead());
 | |
|   if (!IsLaunching()) {
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   // We've started a sync content process launch.
 | |
|   Telemetry::Accumulate(Telemetry::CONTENT_PROCESS_LAUNCH_IS_SYNC, 1);
 | |
| 
 | |
|   // We're a process which hasn't finished initializing. We may be racing
 | |
|   // against whoever launched it (and whoever else is already racing). Since
 | |
|   // we're sync, we win the race and finish the initialization.
 | |
|   bool launchSuccess = mSubprocess->WaitForProcessHandle();
 | |
|   if (launchSuccess &&
 | |
|       LaunchSubprocessResolve(/* aIsSync = */ true, aPriority)) {
 | |
|     mActivateTS = TimeStamp::Now();
 | |
|     return true;
 | |
|   }
 | |
|   // In case of failure.
 | |
|   LaunchSubprocessReject();
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| /*static*/
 | |
| already_AddRefed<ContentParent> ContentParent::GetNewOrUsedJSPluginProcess(
 | |
|     uint32_t aPluginID, const hal::ProcessPriority& aPriority) {
 | |
|   RefPtr<ContentParent> p;
 | |
|   if (sJSPluginContentParents) {
 | |
|     p = sJSPluginContentParents->Get(aPluginID);
 | |
|   } else {
 | |
|     sJSPluginContentParents = new nsTHashMap<nsUint32HashKey, ContentParent*>();
 | |
|   }
 | |
| 
 | |
|   if (p) {
 | |
|     return p.forget();
 | |
|   }
 | |
| 
 | |
|   p = new ContentParent(aPluginID);
 | |
| 
 | |
|   if (!p->LaunchSubprocessSync(aPriority)) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   sJSPluginContentParents->InsertOrUpdate(aPluginID, p);
 | |
| 
 | |
|   return p.forget();
 | |
| }
 | |
| 
 | |
| static nsIDocShell* GetOpenerDocShellHelper(Element* aFrameElement) {
 | |
|   // Propagate the private-browsing status of the element's parent
 | |
|   // docshell to the remote docshell, via the chrome flags.
 | |
|   MOZ_ASSERT(aFrameElement);
 | |
|   nsPIDOMWindowOuter* win = aFrameElement->OwnerDoc()->GetWindow();
 | |
|   if (!win) {
 | |
|     NS_WARNING("Remote frame has no window");
 | |
|     return nullptr;
 | |
|   }
 | |
|   nsIDocShell* docShell = win->GetDocShell();
 | |
|   if (!docShell) {
 | |
|     NS_WARNING("Remote frame has no docshell");
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   return docShell;
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvCreateGMPService() {
 | |
|   Endpoint<PGMPServiceParent> parent;
 | |
|   Endpoint<PGMPServiceChild> child;
 | |
| 
 | |
|   if (mGMPCreated) {
 | |
|     return IPC_FAIL(this, "GMP Service already created");
 | |
|   }
 | |
| 
 | |
|   nsresult rv;
 | |
|   rv = PGMPService::CreateEndpoints(base::GetCurrentProcId(), OtherPid(),
 | |
|                                     &parent, &child);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     return IPC_FAIL(this, "CreateEndpoints failed");
 | |
|   }
 | |
| 
 | |
|   if (!GMPServiceParent::Create(std::move(parent))) {
 | |
|     return IPC_FAIL(this, "GMPServiceParent::Create failed");
 | |
|   }
 | |
| 
 | |
|   if (!SendInitGMPService(std::move(child))) {
 | |
|     return IPC_FAIL(this, "SendInitGMPService failed");
 | |
|   }
 | |
| 
 | |
|   mGMPCreated = true;
 | |
| 
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| Atomic<bool, mozilla::Relaxed> sContentParentTelemetryEventEnabled(false);
 | |
| 
 | |
| /*static*/
 | |
| void ContentParent::LogAndAssertFailedPrincipalValidationInfo(
 | |
|     nsIPrincipal* aPrincipal, const char* aMethod) {
 | |
|   // nsContentSecurityManager may also enable this same event, but that's okay
 | |
|   if (!sContentParentTelemetryEventEnabled.exchange(true)) {
 | |
|     sContentParentTelemetryEventEnabled = true;
 | |
|     Telemetry::SetEventRecordingEnabled("security"_ns, true);
 | |
|   }
 | |
| 
 | |
|   // Send Telemetry
 | |
|   nsAutoCString principalScheme, principalType, spec;
 | |
|   CopyableTArray<EventExtraEntry> extra(2);
 | |
| 
 | |
|   if (!aPrincipal) {
 | |
|     principalType.AssignLiteral("NullPtr");
 | |
|   } else if (aPrincipal->IsSystemPrincipal()) {
 | |
|     principalType.AssignLiteral("SystemPrincipal");
 | |
|   } else if (aPrincipal->GetIsExpandedPrincipal()) {
 | |
|     principalType.AssignLiteral("ExpandedPrincipal");
 | |
|   } else if (aPrincipal->GetIsContentPrincipal()) {
 | |
|     principalType.AssignLiteral("ContentPrincipal");
 | |
|     aPrincipal->GetSpec(spec);
 | |
|     aPrincipal->GetScheme(principalScheme);
 | |
| 
 | |
|     extra.AppendElement(EventExtraEntry{"scheme"_ns, principalScheme});
 | |
|   } else {
 | |
|     principalType.AssignLiteral("Unknown");
 | |
|   }
 | |
| 
 | |
|   extra.AppendElement(EventExtraEntry{"principalType"_ns, principalType});
 | |
| 
 | |
|   // Do not send telemetry when chrome-debugging is enabled
 | |
|   bool isChromeDebuggingEnabled =
 | |
|       Preferences::GetBool("devtools.chrome.enabled", false);
 | |
|   if (!isChromeDebuggingEnabled) {
 | |
|     Telemetry::EventID eventType =
 | |
|         Telemetry::EventID::Security_Fissionprincipals_Contentparent;
 | |
|     Telemetry::RecordEvent(eventType, mozilla::Some(aMethod),
 | |
|                            mozilla::Some(extra));
 | |
|   }
 | |
| 
 | |
|   // And log it
 | |
|   MOZ_LOG(
 | |
|       ContentParent::GetLog(), LogLevel::Error,
 | |
|       ("  Receiving unexpected Principal (%s) within %s",
 | |
|        aPrincipal && aPrincipal->GetIsContentPrincipal() ? spec.get()
 | |
|                                                          : principalType.get(),
 | |
|        aMethod));
 | |
| 
 | |
| #ifdef DEBUG
 | |
|   // Not only log but also ensure we do not receive an unexpected
 | |
|   // principal when running in debug mode.
 | |
|   MOZ_ASSERT(false, "Receiving unexpected Principal");
 | |
| #endif
 | |
| }
 | |
| 
 | |
| bool ContentParent::ValidatePrincipal(
 | |
|     nsIPrincipal* aPrincipal,
 | |
|     const EnumSet<ValidatePrincipalOptions>& aOptions) {
 | |
|   // If the pref says we should not validate, then there is nothing to do
 | |
|   if (!StaticPrefs::dom_security_enforceIPCBasedPrincipalVetting()) {
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   // If there is no principal, then there is nothing to validate!
 | |
|   if (!aPrincipal) {
 | |
|     return aOptions.contains(ValidatePrincipalOptions::AllowNullPtr);
 | |
|   }
 | |
| 
 | |
|   // We currently do not track relationships between specific null principals
 | |
|   // and content processes, so we can not validate much here - just allow all
 | |
|   // null principals we see because they are generally safe anyway!
 | |
|   if (aPrincipal->GetIsNullPrincipal()) {
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   // Only allow the system principal if the passed in options flags
 | |
|   // request permitting the system principal.
 | |
|   if (aPrincipal->IsSystemPrincipal()) {
 | |
|     return aOptions.contains(ValidatePrincipalOptions::AllowSystem);
 | |
|   }
 | |
| 
 | |
|   // XXXckerschb: we should eliminate the resource carve-out here and always
 | |
|   // validate the Principal, see Bug 1686200: Investigate Principal for pdf.js
 | |
|   if (aPrincipal->SchemeIs("resource")) {
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   // Validate each inner principal individually, allowing us to catch expanded
 | |
|   // principals containing the system principal, etc.
 | |
|   if (aPrincipal->GetIsExpandedPrincipal()) {
 | |
|     if (!aOptions.contains(ValidatePrincipalOptions::AllowExpanded)) {
 | |
|       return false;
 | |
|     }
 | |
|     // FIXME: There are more constraints on expanded principals in-practice,
 | |
|     // such as the structure of extension expanded principals. This may need
 | |
|     // to be investigated more in the future.
 | |
|     nsCOMPtr<nsIExpandedPrincipal> expandedPrincipal =
 | |
|         do_QueryInterface(aPrincipal);
 | |
|     const auto& allowList = expandedPrincipal->AllowList();
 | |
|     for (const auto& innerPrincipal : allowList) {
 | |
|       if (!ValidatePrincipal(innerPrincipal, aOptions)) {
 | |
|         return false;
 | |
|       }
 | |
|     }
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   // A URI with a file:// scheme can never load in a non-file content process
 | |
|   // due to sandboxing.
 | |
|   if (aPrincipal->SchemeIs("file")) {
 | |
|     // If we don't support a separate 'file' process, then we can return here.
 | |
|     if (!StaticPrefs::browser_tabs_remote_separateFileUriProcess()) {
 | |
|       return true;
 | |
|     }
 | |
|     return mRemoteType == FILE_REMOTE_TYPE;
 | |
|   }
 | |
| 
 | |
|   if (aPrincipal->SchemeIs("about")) {
 | |
|     uint32_t flags = 0;
 | |
|     if (NS_FAILED(aPrincipal->GetAboutModuleFlags(&flags))) {
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     // Block principals for about: URIs which can't load in this process.
 | |
|     if (!(flags & (nsIAboutModule::URI_CAN_LOAD_IN_CHILD |
 | |
|                    nsIAboutModule::URI_MUST_LOAD_IN_CHILD))) {
 | |
|       return false;
 | |
|     }
 | |
|     if (flags & nsIAboutModule::URI_MUST_LOAD_IN_EXTENSION_PROCESS) {
 | |
|       return mRemoteType == EXTENSION_REMOTE_TYPE;
 | |
|     }
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   if (!mRemoteTypeIsolationPrincipal ||
 | |
|       RemoteTypePrefix(mRemoteType) != FISSION_WEB_REMOTE_TYPE) {
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   // Web content can contain extension content frames, so a content process may
 | |
|   // send us an extension's principal.
 | |
|   auto* addonPolicy = BasePrincipal::Cast(aPrincipal)->AddonPolicy();
 | |
|   if (addonPolicy) {
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   // Ensure that the expected site-origin matches the one specified by our
 | |
|   // mRemoteTypeIsolationPrincipal.
 | |
|   nsAutoCString siteOriginNoSuffix;
 | |
|   if (NS_FAILED(aPrincipal->GetSiteOriginNoSuffix(siteOriginNoSuffix))) {
 | |
|     return false;
 | |
|   }
 | |
|   nsAutoCString remoteTypeSiteOriginNoSuffix;
 | |
|   if (NS_FAILED(mRemoteTypeIsolationPrincipal->GetSiteOriginNoSuffix(
 | |
|           remoteTypeSiteOriginNoSuffix))) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   return remoteTypeSiteOriginNoSuffix.Equals(siteOriginNoSuffix);
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvRemovePermission(
 | |
|     nsIPrincipal* aPrincipal, const nsACString& aPermissionType,
 | |
|     nsresult* aRv) {
 | |
|   if (!aPrincipal) {
 | |
|     return IPC_FAIL(this, "No principal");
 | |
|   }
 | |
| 
 | |
|   if (!ValidatePrincipal(aPrincipal)) {
 | |
|     LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __func__);
 | |
|   }
 | |
|   *aRv = Permissions::RemovePermission(aPrincipal, aPermissionType);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| /*static*/
 | |
| already_AddRefed<RemoteBrowser> ContentParent::CreateBrowser(
 | |
|     const TabContext& aContext, Element* aFrameElement,
 | |
|     const nsACString& aRemoteType, BrowsingContext* aBrowsingContext,
 | |
|     ContentParent* aOpenerContentParent) {
 | |
|   AUTO_PROFILER_LABEL("ContentParent::CreateBrowser", OTHER);
 | |
| 
 | |
|   MOZ_DIAGNOSTIC_ASSERT(
 | |
|       !aBrowsingContext->Canonical()->GetBrowserParent(),
 | |
|       "BrowsingContext must not have BrowserParent, or have previous "
 | |
|       "BrowserParent cleared");
 | |
| 
 | |
|   nsAutoCString remoteType(aRemoteType);
 | |
|   if (remoteType.IsEmpty()) {
 | |
|     remoteType = DEFAULT_REMOTE_TYPE;
 | |
|   }
 | |
| 
 | |
|   TabId tabId(nsContentUtils::GenerateTabId());
 | |
| 
 | |
|   nsIDocShell* docShell = GetOpenerDocShellHelper(aFrameElement);
 | |
|   TabId openerTabId;
 | |
|   if (docShell) {
 | |
|     openerTabId = BrowserParent::GetTabIdFrom(docShell);
 | |
|   }
 | |
| 
 | |
|   RefPtr<ContentParent> constructorSender;
 | |
|   MOZ_RELEASE_ASSERT(XRE_IsParentProcess(),
 | |
|                      "Cannot allocate BrowserParent in content process");
 | |
|   if (aOpenerContentParent && !aOpenerContentParent->IsShuttingDown()) {
 | |
|     constructorSender = aOpenerContentParent;
 | |
|   } else {
 | |
|     if (aContext.IsJSPlugin()) {
 | |
|       constructorSender = GetNewOrUsedJSPluginProcess(
 | |
|           aContext.JSPluginId(), PROCESS_PRIORITY_FOREGROUND);
 | |
|     } else {
 | |
|       constructorSender = GetNewOrUsedBrowserProcess(
 | |
|           remoteType, aBrowsingContext->Group(), PROCESS_PRIORITY_FOREGROUND);
 | |
|     }
 | |
|     if (!constructorSender) {
 | |
|       return nullptr;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   aBrowsingContext->SetEmbedderElement(aFrameElement);
 | |
| 
 | |
|   // Ensure that the process which we're using to launch is set as the host
 | |
|   // process for this BrowsingContextGroup.
 | |
|   aBrowsingContext->Group()->EnsureHostProcess(constructorSender);
 | |
| 
 | |
|   nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
 | |
|   docShell->GetTreeOwner(getter_AddRefs(treeOwner));
 | |
|   if (!treeOwner) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsIWebBrowserChrome> wbc = do_GetInterface(treeOwner);
 | |
|   if (!wbc) {
 | |
|     return nullptr;
 | |
|   }
 | |
|   uint32_t chromeFlags = 0;
 | |
|   wbc->GetChromeFlags(&chromeFlags);
 | |
| 
 | |
|   nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
 | |
|   if (loadContext && loadContext->UsePrivateBrowsing()) {
 | |
|     chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW;
 | |
|   }
 | |
|   if (loadContext && loadContext->UseRemoteTabs()) {
 | |
|     chromeFlags |= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW;
 | |
|   }
 | |
|   if (loadContext && loadContext->UseRemoteSubframes()) {
 | |
|     chromeFlags |= nsIWebBrowserChrome::CHROME_FISSION_WINDOW;
 | |
|   }
 | |
| 
 | |
|   if (tabId == 0) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   aBrowsingContext->Canonical()->SetOwnerProcessId(
 | |
|       constructorSender->ChildID());
 | |
| 
 | |
|   RefPtr<BrowserParent> browserParent =
 | |
|       new BrowserParent(constructorSender, tabId, aContext,
 | |
|                         aBrowsingContext->Canonical(), chromeFlags);
 | |
| 
 | |
|   // Open a remote endpoint for our PBrowser actor.
 | |
|   ManagedEndpoint<PBrowserChild> childEp =
 | |
|       constructorSender->OpenPBrowserEndpoint(browserParent);
 | |
|   if (NS_WARN_IF(!childEp.IsValid())) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
 | |
|   if (NS_WARN_IF(!cpm)) {
 | |
|     return nullptr;
 | |
|   }
 | |
|   cpm->RegisterRemoteFrame(browserParent);
 | |
| 
 | |
|   nsCOMPtr<nsIPrincipal> initialPrincipal =
 | |
|       NullPrincipal::Create(aBrowsingContext->OriginAttributesRef());
 | |
|   WindowGlobalInit windowInit = WindowGlobalActor::AboutBlankInitializer(
 | |
|       aBrowsingContext, initialPrincipal);
 | |
| 
 | |
|   RefPtr<WindowGlobalParent> windowParent =
 | |
|       WindowGlobalParent::CreateDisconnected(windowInit);
 | |
|   if (NS_WARN_IF(!windowParent)) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   // Open a remote endpoint for the initial PWindowGlobal actor.
 | |
|   ManagedEndpoint<PWindowGlobalChild> windowEp =
 | |
|       browserParent->OpenPWindowGlobalEndpoint(windowParent);
 | |
|   if (NS_WARN_IF(!windowEp.IsValid())) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   // Tell the content process to set up its PBrowserChild.
 | |
|   bool ok = constructorSender->SendConstructBrowser(
 | |
|       std::move(childEp), std::move(windowEp), tabId,
 | |
|       aContext.AsIPCTabContext(), windowInit, chromeFlags,
 | |
|       constructorSender->ChildID(), constructorSender->IsForBrowser(),
 | |
|       /* aIsTopLevel */ true);
 | |
|   if (NS_WARN_IF(!ok)) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   // Ensure that we're marked as the current BrowserParent on our
 | |
|   // CanonicalBrowsingContext.
 | |
|   aBrowsingContext->Canonical()->SetCurrentBrowserParent(browserParent);
 | |
| 
 | |
|   windowParent->Init();
 | |
| 
 | |
|   RefPtr<BrowserHost> browserHost = new BrowserHost(browserParent);
 | |
|   browserParent->SetOwnerElement(aFrameElement);
 | |
|   return browserHost.forget();
 | |
| }
 | |
| 
 | |
| void ContentParent::GetAll(nsTArray<ContentParent*>& aArray) {
 | |
|   aArray.Clear();
 | |
| 
 | |
|   for (auto* cp : AllProcesses(eLive)) {
 | |
|     aArray.AppendElement(cp);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void ContentParent::GetAllEvenIfDead(nsTArray<ContentParent*>& aArray) {
 | |
|   aArray.Clear();
 | |
| 
 | |
|   for (auto* cp : AllProcesses(eAll)) {
 | |
|     aArray.AppendElement(cp);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void ContentParent::BroadcastStringBundle(
 | |
|     const StringBundleDescriptor& aBundle) {
 | |
|   AutoTArray<StringBundleDescriptor, 1> array;
 | |
|   array.AppendElement(aBundle);
 | |
| 
 | |
|   for (auto* cp : AllProcesses(eLive)) {
 | |
|     Unused << cp->SendRegisterStringBundles(array);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void ContentParent::BroadcastFontListChanged() {
 | |
|   for (auto* cp : AllProcesses(eLive)) {
 | |
|     Unused << cp->SendFontListChanged();
 | |
|   }
 | |
| }
 | |
| 
 | |
| void ContentParent::BroadcastShmBlockAdded(uint32_t aGeneration,
 | |
|                                            uint32_t aIndex) {
 | |
|   auto* pfl = gfxPlatformFontList::PlatformFontList();
 | |
|   for (auto* cp : AllProcesses(eLive)) {
 | |
|     base::SharedMemoryHandle handle =
 | |
|         pfl->ShareShmBlockToProcess(aIndex, cp->Pid());
 | |
|     if (handle == base::SharedMemory::NULLHandle()) {
 | |
|       // If something went wrong here, we just skip it; the child will need to
 | |
|       // request the block as needed, at some performance cost.
 | |
|       continue;
 | |
|     }
 | |
|     Unused << cp->SendFontListShmBlockAdded(aGeneration, aIndex,
 | |
|                                             std::move(handle));
 | |
|   }
 | |
| }
 | |
| 
 | |
| void ContentParent::BroadcastThemeUpdate(widget::ThemeChangeKind aKind) {
 | |
|   const FullLookAndFeel& lnf = *RemoteLookAndFeel::ExtractData();
 | |
|   for (auto* cp : AllProcesses(eLive)) {
 | |
|     Unused << cp->SendThemeChanged(lnf, aKind);
 | |
|   }
 | |
| }
 | |
| 
 | |
| /*static */
 | |
| void ContentParent::BroadcastMediaCodecsSupportedUpdate(
 | |
|     RemoteDecodeIn aLocation, const media::MediaCodecsSupported& aSupported) {
 | |
|   // Merge incoming codec support with existing support list
 | |
|   media::MCSInfo::AddSupport(aSupported);
 | |
|   auto support = media::MCSInfo::GetSupport();
 | |
| 
 | |
|   // Update processes
 | |
|   sCodecsSupported[aLocation] = support;
 | |
|   for (auto* cp : AllProcesses(eAll)) {
 | |
|     Unused << cp->SendUpdateMediaCodecsSupported(aLocation, support);
 | |
|   }
 | |
| 
 | |
|   // Generate + save support string for display in about:support
 | |
|   nsCString supportString;
 | |
|   media::MCSInfo::GetMediaCodecsSupportedString(supportString, support);
 | |
|   gfx::gfxVars::SetCodecSupportInfo(supportString);
 | |
| }
 | |
| 
 | |
| const nsACString& ContentParent::GetRemoteType() const { return mRemoteType; }
 | |
| 
 | |
| static StaticRefPtr<nsIAsyncShutdownClient> sXPCOMShutdownClient;
 | |
| static StaticRefPtr<nsIAsyncShutdownClient> sProfileBeforeChangeClient;
 | |
| static StaticRefPtr<nsIAsyncShutdownClient> sQuitApplicationGrantedClient;
 | |
| 
 | |
| void ContentParent::Init() {
 | |
|   MOZ_ASSERT(sXPCOMShutdownClient);
 | |
| 
 | |
|   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
 | |
|   if (obs) {
 | |
|     size_t length = ArrayLength(sObserverTopics);
 | |
|     for (size_t i = 0; i < length; ++i) {
 | |
|       obs->AddObserver(this, sObserverTopics[i], false);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (obs) {
 | |
|     nsAutoString cpId;
 | |
|     cpId.AppendInt(static_cast<uint64_t>(this->ChildID()));
 | |
|     obs->NotifyObservers(static_cast<nsIObserver*>(this), "ipc:content-created",
 | |
|                          cpId.get());
 | |
|   }
 | |
| 
 | |
| #ifdef ACCESSIBILITY
 | |
|   // If accessibility is running in chrome process then start it in content
 | |
|   // process.
 | |
|   if (GetAccService()) {
 | |
| #  if defined(XP_WIN)
 | |
|     // Don't init content a11y if we detect an incompat version of JAWS in use.
 | |
|     if (!mozilla::a11y::Compatibility::IsOldJAWS()) {
 | |
|       Unused << SendActivateA11y(
 | |
|           ::GetCurrentThreadId(),
 | |
|           a11y::MsaaAccessible::GetContentProcessIdFor(ChildID()));
 | |
|     }
 | |
| #  else
 | |
|     Unused << SendActivateA11y(0, 0);
 | |
| #  endif
 | |
|   }
 | |
| #endif  // #ifdef ACCESSIBILITY
 | |
| 
 | |
|   Unused << SendInitProfiler(ProfilerParent::CreateForProcess(OtherPid()));
 | |
| 
 | |
|   RefPtr<GeckoMediaPluginServiceParent> gmps(
 | |
|       GeckoMediaPluginServiceParent::GetSingleton());
 | |
|   gmps->UpdateContentProcessGMPCapabilities(this);
 | |
| 
 | |
|   // Flush any pref updates that happened during launch and weren't
 | |
|   // included in the blobs set up in BeginSubprocessLaunch.
 | |
|   for (const Pref& pref : mQueuedPrefs) {
 | |
|     Unused << NS_WARN_IF(!SendPreferenceUpdate(pref));
 | |
|   }
 | |
|   mQueuedPrefs.Clear();
 | |
| 
 | |
|   Unused << SendInitNextGenLocalStorageEnabled(NextGenLocalStorageEnabled());
 | |
| }
 | |
| 
 | |
| // Note that for E10S we can get a false here that will be overruled by
 | |
| // TryToRecycleE10SOnly as late as MaybeBeginShutdown. We cannot really
 | |
| // foresee its result here.
 | |
| bool ContentParent::CheckTabDestroyWillKeepAlive(
 | |
|     uint32_t aExpectedBrowserCount) {
 | |
|   return ManagedPBrowserParent().Count() != aExpectedBrowserCount ||
 | |
|          ShouldKeepProcessAlive();
 | |
| }
 | |
| 
 | |
| void ContentParent::NotifyTabWillDestroy() {
 | |
|   if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)
 | |
| #if !defined(MOZ_WIDGET_ANDROID)
 | |
|       /* on Android we keep processes alive more agressively, see
 | |
|          NotifyTabDestroying where we omit MaybeBeginShutdown */
 | |
|       || (/* we cannot trust CheckTabDestroyWillKeepAlive in E10S mode */
 | |
|           mozilla::FissionAutostart() &&
 | |
|           !CheckTabDestroyWillKeepAlive(mNumDestroyingTabs + 1))
 | |
| #endif
 | |
|   ) {
 | |
|     // Once we notify the impending shutdown, the content process will stop
 | |
|     // to process content JS on interrupt (among other things), so we need to
 | |
|     // be sure that the process will not be re-used after this point.
 | |
|     // The inverse is harmless, that is if we decide later to shut it down
 | |
|     // but did not notify here, it will be just notified later (but in rare
 | |
|     // cases too late to avoid a hang).
 | |
|     NotifyImpendingShutdown();
 | |
| #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
 | |
|     mNotifiedImpendingShutdownOnTabWillDestroy = true;
 | |
| #endif
 | |
|   }
 | |
| }
 | |
| 
 | |
| void ContentParent::MaybeBeginShutDown(uint32_t aExpectedBrowserCount,
 | |
|                                        bool aSendShutDown) {
 | |
|   MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
 | |
|           ("MaybeBeginShutdown %p, %u vs %u", this,
 | |
|            ManagedPBrowserParent().Count(), aExpectedBrowserCount));
 | |
|   MOZ_ASSERT(NS_IsMainThread());
 | |
| 
 | |
|   // Both CheckTabDestroyWillKeepAlive and TryToRecycleE10SOnly will return
 | |
|   // false if IsInOrBeyond(AppShutdownConfirmed), so if the parent shuts
 | |
|   // down we will always shutdown the child.
 | |
|   if (CheckTabDestroyWillKeepAlive(aExpectedBrowserCount) ||
 | |
|       TryToRecycleE10SOnly()) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   MOZ_LOG(
 | |
|       ContentParent::GetLog(), LogLevel::Debug,
 | |
|       ("Beginning ContentParent Shutdown %p (%s)", this, mRemoteType.get()));
 | |
| 
 | |
|   // We're dying now, prevent anything from re-using this process.
 | |
|   MarkAsDead();
 | |
|   SignalImpendingShutdownToContentJS();
 | |
|   StartForceKillTimer();
 | |
| 
 | |
|   if (aSendShutDown) {
 | |
|     MaybeAsyncSendShutDownMessage();
 | |
|   }
 | |
| }
 | |
| 
 | |
| void ContentParent::MaybeAsyncSendShutDownMessage() {
 | |
|   MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
 | |
|           ("MaybeAsyncSendShutDownMessage %p", this));
 | |
|   MOZ_ASSERT(NS_IsMainThread());
 | |
|   MOZ_ASSERT(sRecycledE10SProcess != this);
 | |
| 
 | |
| #ifdef DEBUG
 | |
|   // Calling this below while the lock is acquired will deadlock.
 | |
|   bool shouldKeepProcessAlive = ShouldKeepProcessAlive();
 | |
| #endif
 | |
| 
 | |
|   {
 | |
|     MutexAutoLock lock(mThreadsafeHandle->mMutex);
 | |
|     MOZ_ASSERT_IF(!mThreadsafeHandle->mRemoteWorkerActorCount,
 | |
|                   !shouldKeepProcessAlive);
 | |
| 
 | |
|     if (mThreadsafeHandle->mRemoteWorkerActorCount) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     MOZ_ASSERT(!mThreadsafeHandle->mShutdownStarted);
 | |
|     mThreadsafeHandle->mShutdownStarted = true;
 | |
|   }
 | |
| 
 | |
|   // In the case of normal shutdown, send a shutdown message to child to
 | |
|   // allow it to perform shutdown tasks.
 | |
|   GetCurrentSerialEventTarget()->Dispatch(NewRunnableMethod<ShutDownMethod>(
 | |
|       "dom::ContentParent::ShutDownProcess", this,
 | |
|       &ContentParent::ShutDownProcess, SEND_SHUTDOWN_MESSAGE));
 | |
| }
 | |
| 
 | |
| void MaybeLogBlockShutdownDiagnostics(ContentParent* aSelf, const char* aMsg,
 | |
|                                       const char* aFile, int32_t aLine) {
 | |
| #if defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
 | |
|   if (aSelf->IsBlockingShutdown()) {
 | |
|     MOZ_LOG(ContentParent::GetLog(), LogLevel::Info,
 | |
|             ("ContentParent: id=%p pid=%d - %s at %s(%d)", aSelf, aSelf->Pid(),
 | |
|              aMsg, aFile, aLine));
 | |
|   }
 | |
| #else
 | |
|   Unused << aSelf;
 | |
|   Unused << aMsg;
 | |
|   Unused << aFile;
 | |
|   Unused << aLine;
 | |
| #endif
 | |
| }
 | |
| 
 | |
| bool ContentParent::ShutDownProcess(ShutDownMethod aMethod) {
 | |
|   bool result = false;
 | |
|   MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
 | |
|           ("ShutDownProcess: %p", this));
 | |
|   // NB: must MarkAsDead() here so that this isn't accidentally
 | |
|   // returned from Get*() while in the midst of shutdown.
 | |
|   MarkAsDead();
 | |
| 
 | |
|   // Shutting down by sending a shutdown message works differently than the
 | |
|   // other methods. We first call Shutdown() in the child. After the child is
 | |
|   // ready, it calls FinishShutdown() on us. Then we close the channel.
 | |
|   if (aMethod == SEND_SHUTDOWN_MESSAGE) {
 | |
|     if (!mShutdownPending) {
 | |
|       if (CanSend()) {
 | |
|         // Stop sending input events with input priority when shutting down.
 | |
|         SetInputPriorityEventEnabled(false);
 | |
|         // Send a high priority announcement first. If this fails, SendShutdown
 | |
|         // will also fail.
 | |
|         Unused << SendShutdownConfirmedHP();
 | |
|         // Send the definite message with normal priority.
 | |
|         if (SendShutdown()) {
 | |
|           MaybeLogBlockShutdownDiagnostics(
 | |
|               this, "ShutDownProcess: Sent shutdown message.", __FILE__,
 | |
|               __LINE__);
 | |
|           mShutdownPending = true;
 | |
|           // Start the force-kill timer if we haven't already.
 | |
|           // This can happen if we shutdown a process while launching or
 | |
|           // because it is removed from the cached processes pool.
 | |
|           if (!mForceKillTimer) {
 | |
|             SignalImpendingShutdownToContentJS();
 | |
|             StartForceKillTimer();
 | |
|           }
 | |
|           result = true;
 | |
|         } else {
 | |
|           MaybeLogBlockShutdownDiagnostics(
 | |
|               this, "ShutDownProcess: !!! Send shutdown message failed! !!!",
 | |
|               __FILE__, __LINE__);
 | |
|         }
 | |
|       } else {
 | |
|         MaybeLogBlockShutdownDiagnostics(
 | |
|             this, "ShutDownProcess: !!! !CanSend !!!", __FILE__, __LINE__);
 | |
|       }
 | |
|     } else {
 | |
|       MaybeLogBlockShutdownDiagnostics(
 | |
|           this, "ShutDownProcess: Shutdown already pending.", __FILE__,
 | |
|           __LINE__);
 | |
| 
 | |
|       result = true;
 | |
|     }
 | |
|     // If call was not successful, the channel must have been broken
 | |
|     // somehow, and we will clean up the error in ActorDestroy.
 | |
|     return result;
 | |
|   }
 | |
| 
 | |
|   using mozilla::dom::quota::QuotaManagerService;
 | |
| 
 | |
|   if (QuotaManagerService* qms = QuotaManagerService::GetOrCreate()) {
 | |
|     qms->AbortOperationsForProcess(mChildID);
 | |
|   }
 | |
| 
 | |
|   // If Close() fails with an error, we'll end up back in this function, but
 | |
|   // with aMethod = CLOSE_CHANNEL_WITH_ERROR.
 | |
| 
 | |
|   if (aMethod == CLOSE_CHANNEL) {
 | |
|     if (!mCalledClose) {
 | |
|       MaybeLogBlockShutdownDiagnostics(
 | |
|           this, "ShutDownProcess: Closing channel.", __FILE__, __LINE__);
 | |
|       // Close() can only be called once: It kicks off the destruction
 | |
|       // sequence.
 | |
|       mCalledClose = true;
 | |
|       Close();
 | |
|     }
 | |
|     result = true;
 | |
|   }
 | |
| 
 | |
|   // A ContentParent object might not get freed until after XPCOM shutdown has
 | |
|   // shut down the cycle collector.  But by then it's too late to release any
 | |
|   // CC'ed objects, so we need to null them out here, while we still can.  See
 | |
|   // bug 899761.
 | |
|   ShutDownMessageManager();
 | |
|   return result;
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvNotifyShutdownSuccess() {
 | |
|   if (!mShutdownPending) {
 | |
|     return IPC_FAIL(this, "RecvNotifyShutdownSuccess without mShutdownPending");
 | |
|   }
 | |
| 
 | |
|   mIsNotifiedShutdownSuccess = true;
 | |
| 
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvFinishShutdown() {
 | |
|   if (!mShutdownPending) {
 | |
|     return IPC_FAIL(this, "RecvFinishShutdown without mShutdownPending");
 | |
|   }
 | |
| 
 | |
|   // At this point, we already called ShutDownProcess once with
 | |
|   // SEND_SHUTDOWN_MESSAGE. To actually close the channel, we call
 | |
|   // ShutDownProcess again with CLOSE_CHANNEL.
 | |
|   if (mCalledClose) {
 | |
|     MaybeLogBlockShutdownDiagnostics(
 | |
|         this, "RecvFinishShutdown: Channel already closed.", __FILE__,
 | |
|         __LINE__);
 | |
|   }
 | |
| 
 | |
|   ShutDownProcess(CLOSE_CHANNEL);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| void ContentParent::ShutDownMessageManager() {
 | |
|   if (!mMessageManager) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   mMessageManager->ReceiveMessage(mMessageManager, nullptr,
 | |
|                                   CHILD_PROCESS_SHUTDOWN_MESSAGE, false,
 | |
|                                   nullptr, nullptr, IgnoreErrors());
 | |
| 
 | |
|   mMessageManager->SetOsPid(-1);
 | |
|   mMessageManager->Disconnect();
 | |
|   mMessageManager = nullptr;
 | |
| }
 | |
| 
 | |
| void ContentParent::AddToPool(nsTArray<ContentParent*>& aPool) {
 | |
|   MOZ_DIAGNOSTIC_ASSERT(!mIsInPool);
 | |
|   AssertAlive();
 | |
|   MOZ_DIAGNOSTIC_ASSERT(!mCalledKillHard);
 | |
|   aPool.AppendElement(this);
 | |
|   mIsInPool = true;
 | |
| }
 | |
| 
 | |
| void ContentParent::RemoveFromPool(nsTArray<ContentParent*>& aPool) {
 | |
|   MOZ_DIAGNOSTIC_ASSERT(mIsInPool);
 | |
|   aPool.RemoveElement(this);
 | |
|   mIsInPool = false;
 | |
| }
 | |
| 
 | |
| void ContentParent::AssertNotInPool() {
 | |
|   MOZ_RELEASE_ASSERT(!mIsInPool);
 | |
| 
 | |
|   MOZ_RELEASE_ASSERT(sRecycledE10SProcess != this);
 | |
|   if (IsForJSPlugin()) {
 | |
|     MOZ_RELEASE_ASSERT(!sJSPluginContentParents ||
 | |
|                        !sJSPluginContentParents->Get(mJSPluginID));
 | |
|   } else {
 | |
|     MOZ_RELEASE_ASSERT(
 | |
|         !sBrowserContentParents ||
 | |
|         !sBrowserContentParents->Contains(mRemoteType) ||
 | |
|         !sBrowserContentParents->Get(mRemoteType)->Contains(this));
 | |
| 
 | |
|     for (const auto& group : mGroups) {
 | |
|       MOZ_RELEASE_ASSERT(group->GetHostProcess(mRemoteType) != this,
 | |
|                          "still a host process for one of our groups?");
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void ContentParent::AssertAlive() {
 | |
|   MOZ_DIAGNOSTIC_ASSERT(!mNotifiedImpendingShutdownOnTabWillDestroy);
 | |
|   MOZ_DIAGNOSTIC_ASSERT(!mIsSignaledImpendingShutdown);
 | |
|   MOZ_DIAGNOSTIC_ASSERT(!IsDead());
 | |
| }
 | |
| 
 | |
| void ContentParent::RemoveFromList() {
 | |
|   if (IsForJSPlugin()) {
 | |
|     if (sJSPluginContentParents) {
 | |
|       sJSPluginContentParents->Remove(mJSPluginID);
 | |
|       if (!sJSPluginContentParents->Count()) {
 | |
|         sJSPluginContentParents = nullptr;
 | |
|       }
 | |
|     }
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (!mIsInPool) {
 | |
| #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
 | |
|     AssertNotInPool();
 | |
| #endif
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // Ensure that this BrowsingContextGroup is no longer used to host new
 | |
|   // documents from any associated BrowsingContextGroups. It may become a host
 | |
|   // again in the future, if it is restored to the pool.
 | |
|   for (const auto& group : mGroups) {
 | |
|     group->RemoveHostProcess(this);
 | |
|   }
 | |
| 
 | |
|   StopRecyclingE10SOnly(/* aForeground */ false);
 | |
| 
 | |
|   if (sBrowserContentParents) {
 | |
|     if (auto entry = sBrowserContentParents->Lookup(mRemoteType)) {
 | |
|       const auto& contentParents = entry.Data();
 | |
|       RemoveFromPool(*contentParents);
 | |
|       if (contentParents->IsEmpty()) {
 | |
|         entry.Remove();
 | |
|       }
 | |
|     }
 | |
|     if (sBrowserContentParents->IsEmpty()) {
 | |
|       delete sBrowserContentParents;
 | |
|       sBrowserContentParents = nullptr;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void ContentParent::MarkAsDead() {
 | |
|   MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
 | |
|           ("Marking ContentProcess %p as dead", this));
 | |
|   MOZ_DIAGNOSTIC_ASSERT(!sInProcessSelector);
 | |
|   RemoveFromList();
 | |
| 
 | |
|   // Prevent this process from being re-used.
 | |
|   PreallocatedProcessManager::Erase(this);
 | |
|   StopRecyclingE10SOnly(false);
 | |
| 
 | |
| #ifdef MOZ_WIDGET_ANDROID
 | |
|   if (IsAlive()) {
 | |
|     // We're intentionally killing the content process at this point to ensure
 | |
|     // that we never have a "dead" content process sitting around and occupying
 | |
|     // an Android Service.
 | |
|     nsCOMPtr<nsIEventTarget> launcherThread(GetIPCLauncher());
 | |
|     MOZ_ASSERT(launcherThread);
 | |
| 
 | |
|     auto procType = java::GeckoProcessType::CONTENT();
 | |
|     auto selector =
 | |
|         java::GeckoProcessManager::Selector::New(procType, OtherPid());
 | |
| 
 | |
|     launcherThread->Dispatch(NS_NewRunnableFunction(
 | |
|         "ContentParent::MarkAsDead",
 | |
|         [selector =
 | |
|              java::GeckoProcessManager::Selector::GlobalRef(selector)]() {
 | |
|           java::GeckoProcessManager::ShutdownProcess(selector);
 | |
|         }));
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   if (mScriptableHelper) {
 | |
|     static_cast<ScriptableCPInfo*>(mScriptableHelper.get())->ProcessDied();
 | |
|     mScriptableHelper = nullptr;
 | |
|   }
 | |
| 
 | |
|   mLifecycleState = LifecycleState::DEAD;
 | |
| }
 | |
| 
 | |
| void ContentParent::OnChannelError() {
 | |
|   RefPtr<ContentParent> kungFuDeathGrip(this);
 | |
|   PContentParent::OnChannelError();
 | |
| }
 | |
| 
 | |
| void ContentParent::ProcessingError(Result aCode, const char* aReason) {
 | |
|   if (MsgDropped == aCode) {
 | |
|     return;
 | |
|   }
 | |
| #ifndef FUZZING
 | |
|   // Other errors are big deals.
 | |
|   KillHard(aReason);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| void ContentParent::ActorDestroy(ActorDestroyReason why) {
 | |
|   MOZ_RELEASE_ASSERT(mSelfRef);
 | |
| 
 | |
|   if (mForceKillTimer) {
 | |
|     mForceKillTimer->Cancel();
 | |
|     mForceKillTimer = nullptr;
 | |
|   }
 | |
| 
 | |
|   // Signal shutdown completion regardless of error state, so we can
 | |
|   // finish waiting in the xpcom-shutdown/profile-before-change observer.
 | |
|   RemoveShutdownBlockers();
 | |
| 
 | |
|   if (mHangMonitorActor) {
 | |
|     ProcessHangMonitor::RemoveProcess(mHangMonitorActor);
 | |
|     mHangMonitorActor = nullptr;
 | |
|   }
 | |
| 
 | |
|   RefPtr<FileSystemSecurity> fss = FileSystemSecurity::Get();
 | |
|   if (fss) {
 | |
|     fss->Forget(ChildID());
 | |
|   }
 | |
| 
 | |
|   if (why == NormalShutdown && !mCalledClose) {
 | |
|     // If we shut down normally but haven't called Close, assume somebody
 | |
|     // else called Close on us. In that case, we still need to call
 | |
|     // ShutDownProcess below to perform other necessary clean up.
 | |
|     mCalledClose = true;
 | |
|   }
 | |
| 
 | |
|   // Make sure we always clean up.
 | |
|   ShutDownProcess(why == NormalShutdown ? CLOSE_CHANNEL
 | |
|                                         : CLOSE_CHANNEL_WITH_ERROR);
 | |
| 
 | |
|   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
 | |
|   if (obs) {
 | |
|     size_t length = ArrayLength(sObserverTopics);
 | |
|     for (size_t i = 0; i < length; ++i) {
 | |
|       obs->RemoveObserver(static_cast<nsIObserver*>(this), sObserverTopics[i]);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // remove the global remote preferences observers
 | |
|   Preferences::RemoveObserver(this, "");
 | |
|   gfxVars::RemoveReceiver(this);
 | |
| 
 | |
|   if (GPUProcessManager* gpu = GPUProcessManager::Get()) {
 | |
|     // Note: the manager could have shutdown already.
 | |
|     gpu->RemoveListener(this);
 | |
|   }
 | |
| 
 | |
|   RecvRemoveGeolocationListener();
 | |
| 
 | |
|   // Destroy our JSProcessActors, and reject any pending queries.
 | |
|   JSActorDidDestroy();
 | |
| 
 | |
|   if (obs) {
 | |
|     RefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();
 | |
| 
 | |
|     props->SetPropertyAsUint64(u"childID"_ns, mChildID);
 | |
| 
 | |
|     if (AbnormalShutdown == why) {
 | |
|       Telemetry::Accumulate(Telemetry::SUBPROCESS_ABNORMAL_ABORT, "content"_ns,
 | |
|                             1);
 | |
| 
 | |
|       props->SetPropertyAsBool(u"abnormal"_ns, true);
 | |
| 
 | |
|       nsAutoString dumpID;
 | |
|       // There's a window in which child processes can crash
 | |
|       // after IPC is established, but before a crash reporter
 | |
|       // is created.
 | |
|       if (mCrashReporter) {
 | |
|         // if mCreatedPairedMinidumps is true, we've already generated
 | |
|         // parent/child dumps for desktop crashes.
 | |
|         if (!mCreatedPairedMinidumps) {
 | |
| #if defined(XP_MACOSX)
 | |
|           RefPtr<nsAvailableMemoryWatcherBase> memWatcher;
 | |
|           memWatcher = nsAvailableMemoryWatcherBase::GetSingleton();
 | |
|           memWatcher->AddChildAnnotations(mCrashReporter);
 | |
| #endif
 | |
| 
 | |
|           mCrashReporter->GenerateCrashReport(OtherPid());
 | |
|         }
 | |
| 
 | |
|         if (mCrashReporter->HasMinidump()) {
 | |
|           dumpID = mCrashReporter->MinidumpID();
 | |
|         }
 | |
|       } else {
 | |
|         HandleOrphanedMinidump(&dumpID);
 | |
|       }
 | |
| 
 | |
|       if (!dumpID.IsEmpty()) {
 | |
|         props->SetPropertyAsAString(u"dumpID"_ns, dumpID);
 | |
|       }
 | |
|     }
 | |
|     nsAutoString cpId;
 | |
|     cpId.AppendInt(static_cast<uint64_t>(this->ChildID()));
 | |
|     obs->NotifyObservers((nsIPropertyBag2*)props, "ipc:content-shutdown",
 | |
|                          cpId.get());
 | |
|   }
 | |
| 
 | |
|   // Remove any and all idle listeners.
 | |
|   if (mIdleListeners.Length() > 0) {
 | |
|     nsCOMPtr<nsIUserIdleService> idleService =
 | |
|         do_GetService("@mozilla.org/widget/useridleservice;1");
 | |
|     if (idleService) {
 | |
|       RefPtr<ParentIdleListener> listener;
 | |
|       for (const auto& lentry : mIdleListeners) {
 | |
|         listener = static_cast<ParentIdleListener*>(lentry.get());
 | |
|         idleService->RemoveIdleObserver(listener, listener->mTime);
 | |
|       }
 | |
|     }
 | |
|     mIdleListeners.Clear();
 | |
|   }
 | |
| 
 | |
|   MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
 | |
|           ("destroying Subprocess in ActorDestroy: ContentParent %p "
 | |
|            "mSubprocess %p handle %" PRIuPTR,
 | |
|            this, mSubprocess,
 | |
|            mSubprocess ? (uintptr_t)mSubprocess->GetChildProcessHandle() : -1));
 | |
|   // FIXME (bug 1520997): does this really need an additional dispatch?
 | |
|   if (GetCurrentSerialEventTarget()) {
 | |
|     GetCurrentSerialEventTarget()->Dispatch(NS_NewRunnableFunction(
 | |
|         "DelayedDeleteSubprocessRunnable", [subprocess = mSubprocess] {
 | |
|           MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
 | |
|                   ("destroyed Subprocess in ActorDestroy: Subprocess %p handle "
 | |
|                    "%" PRIuPTR,
 | |
|                    subprocess,
 | |
|                    subprocess ? (uintptr_t)subprocess->GetChildProcessHandle()
 | |
|                               : -1));
 | |
|           subprocess->Destroy();
 | |
|         }));
 | |
|   }
 | |
|   mSubprocess = nullptr;
 | |
| 
 | |
|   ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
 | |
|   if (cpm) {
 | |
|     cpm->RemoveContentProcess(this->ChildID());
 | |
|   }
 | |
| 
 | |
|   if (mDriverCrashGuard) {
 | |
|     mDriverCrashGuard->NotifyCrashed();
 | |
|   }
 | |
| 
 | |
|   // Unregister all the BlobURLs registered by the ContentChild.
 | |
|   for (uint32_t i = 0; i < mBlobURLs.Length(); ++i) {
 | |
|     BlobURLProtocolHandler::RemoveDataEntry(mBlobURLs[i]);
 | |
|   }
 | |
| 
 | |
|   mBlobURLs.Clear();
 | |
| 
 | |
| #if defined(XP_WIN) && defined(ACCESSIBILITY)
 | |
|   a11y::MsaaAccessible::ReleaseContentProcessIdFor(ChildID());
 | |
| #endif
 | |
| 
 | |
| #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();
 | |
| }
 | |
| 
 | |
| void ContentParent::ActorDealloc() { mSelfRef = nullptr; }
 | |
| 
 | |
| bool ContentParent::TryToRecycleE10SOnly() {
 | |
|   // Only try to recycle "web" content processes, as other remote types are
 | |
|   // generally more unique, and cannot be effectively re-used. This is disabled
 | |
|   // with Fission, as "web" content processes are no longer frequently used.
 | |
|   //
 | |
|   // Disabling the process pre-allocator will also disable process recycling,
 | |
|   // allowing for more consistent process counts under testing.
 | |
|   if (mRemoteType != DEFAULT_REMOTE_TYPE || mozilla::FissionAutostart() ||
 | |
|       !PreallocatedProcessManager::Enabled()) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   // This life time check should be replaced by a memory health check (memory
 | |
|   // usage + fragmentation).
 | |
| 
 | |
|   // Note that this is specifically to help with edge cases that rapidly
 | |
|   // create-and-destroy processes
 | |
|   const double kMaxLifeSpan = 5;
 | |
|   MOZ_LOG(
 | |
|       ContentParent::GetLog(), LogLevel::Debug,
 | |
|       ("TryToRecycle ContentProcess %p (%u) with lifespan %f seconds", this,
 | |
|        (unsigned int)ChildID(), (TimeStamp::Now() - mActivateTS).ToSeconds()));
 | |
| 
 | |
|   if (mCalledKillHard || !IsAlive() ||
 | |
|       (TimeStamp::Now() - mActivateTS).ToSeconds() > kMaxLifeSpan) {
 | |
|     MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
 | |
|             ("TryToRecycle did not recycle %p", this));
 | |
| 
 | |
|     // It's possible that the process was already cached, and we're being called
 | |
|     // from a different path, and we're now past kMaxLifeSpan (or some other).
 | |
|     // Ensure that if we're going to kill this process we don't recycle it.
 | |
|     StopRecyclingE10SOnly(/* aForeground */ false);
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   if (!sRecycledE10SProcess) {
 | |
|     MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
 | |
|             ("TryToRecycle began recycling %p", this));
 | |
|     sRecycledE10SProcess = this;
 | |
| 
 | |
|     ProcessPriorityManager::SetProcessPriority(this,
 | |
|                                                PROCESS_PRIORITY_BACKGROUND);
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   if (sRecycledE10SProcess == this) {
 | |
|     MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
 | |
|             ("TryToRecycle continue recycling %p", this));
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   // Some other process is already being recycled, just shut this one down.
 | |
|   MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
 | |
|           ("TryToRecycle did not recycle %p (already recycling %p)", this,
 | |
|            sRecycledE10SProcess.get()));
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| void ContentParent::StopRecyclingE10SOnly(bool aForeground) {
 | |
|   if (sRecycledE10SProcess != this) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   sRecycledE10SProcess = nullptr;
 | |
|   if (aForeground) {
 | |
|     ProcessPriorityManager::SetProcessPriority(this,
 | |
|                                                PROCESS_PRIORITY_FOREGROUND);
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool ContentParent::HasActiveWorkerOrJSPlugin() {
 | |
|   if (IsForJSPlugin()) {
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   // If we have active workers, we need to stay alive.
 | |
|   {
 | |
|     MutexAutoLock lock(mThreadsafeHandle->mMutex);
 | |
|     if (mThreadsafeHandle->mRemoteWorkerActorCount) {
 | |
|       return true;
 | |
|     }
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| bool ContentParent::ShouldKeepProcessAlive() {
 | |
|   if (HasActiveWorkerOrJSPlugin()) {
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   if (mNumKeepaliveCalls > 0) {
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   if (IsLaunching()) {
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   // If we have already been marked as dead, don't prevent shutdown.
 | |
|   if (IsDead()) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   // If everything is going down, there is no need to keep us alive, neither.
 | |
|   if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   if (!sBrowserContentParents) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   auto contentParents = sBrowserContentParents->Get(mRemoteType);
 | |
|   if (!contentParents) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   // We might want to keep some content processes alive for performance reasons.
 | |
|   // e.g. test runs and privileged content process for some about: pages.
 | |
|   // We don't want to alter behavior if the pref is not set, so default to 0.
 | |
|   int32_t processesToKeepAlive = 0;
 | |
| 
 | |
|   nsAutoCString keepAlivePref("dom.ipc.keepProcessesAlive.");
 | |
| 
 | |
|   if (StringBeginsWith(mRemoteType, FISSION_WEB_REMOTE_TYPE) &&
 | |
|       xpc::IsInAutomation()) {
 | |
|     keepAlivePref.Append(FISSION_WEB_REMOTE_TYPE);
 | |
|     keepAlivePref.AppendLiteral(".perOrigin");
 | |
|   } else {
 | |
|     keepAlivePref.Append(mRemoteType);
 | |
|   }
 | |
|   if (NS_FAILED(
 | |
|           Preferences::GetInt(keepAlivePref.get(), &processesToKeepAlive))) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   int32_t numberOfAliveProcesses = contentParents->Length();
 | |
| 
 | |
|   return numberOfAliveProcesses <= processesToKeepAlive;
 | |
| }
 | |
| 
 | |
| void ContentParent::NotifyTabDestroying() {
 | |
|   MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
 | |
|           ("NotifyTabDestroying %p:", this));
 | |
|   // There can be more than one PBrowser for a given app process
 | |
|   // because of popup windows.  PBrowsers can also destroy
 | |
|   // concurrently.  When all the PBrowsers are destroying, kick off
 | |
|   // another task to ensure the child process *really* shuts down,
 | |
|   // even if the PBrowsers themselves never finish destroying.
 | |
|   ++mNumDestroyingTabs;
 | |
| 
 | |
|   /**
 | |
|    * We intentionally skip this code on Android:
 | |
|    * 1. Android has a fixed upper bound on the number of content processes, so
 | |
|    *    we prefer to re-use them whenever possible (as opposed to letting an
 | |
|    *    old process wind down while we launch a new one).
 | |
|    * 2. GeckoView always hard-kills content processes (and if it does not,
 | |
|    *    Android itself will), so we don't concern ourselves with the ForceKill
 | |
|    *    timer either.
 | |
|    */
 | |
| #if !defined(MOZ_WIDGET_ANDROID)
 | |
|   MaybeBeginShutDown(/* aExpectedBrowserCount */ mNumDestroyingTabs,
 | |
|                      /* aSendShutDown */ false);
 | |
| #endif  // !defined(MOZ_WIDGET_ANDROID)
 | |
| }
 | |
| 
 | |
| void ContentParent::AddKeepAlive() {
 | |
|   AssertAlive();
 | |
|   // Something wants to keep this content process alive.
 | |
|   ++mNumKeepaliveCalls;
 | |
| }
 | |
| 
 | |
| void ContentParent::RemoveKeepAlive() {
 | |
|   MOZ_DIAGNOSTIC_ASSERT(mNumKeepaliveCalls > 0);
 | |
|   --mNumKeepaliveCalls;
 | |
| 
 | |
|   MaybeBeginShutDown();
 | |
| }
 | |
| 
 | |
| void ContentParent::StartForceKillTimer() {
 | |
|   if (mForceKillTimer || !CanSend()) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   int32_t timeoutSecs = StaticPrefs::dom_ipc_tabs_shutdownTimeoutSecs();
 | |
|   if (timeoutSecs > 0) {
 | |
|     NS_NewTimerWithFuncCallback(getter_AddRefs(mForceKillTimer),
 | |
|                                 ContentParent::ForceKillTimerCallback, this,
 | |
|                                 timeoutSecs * 1000, nsITimer::TYPE_ONE_SHOT,
 | |
|                                 "dom::ContentParent::StartForceKillTimer");
 | |
|     MOZ_ASSERT(mForceKillTimer);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void ContentParent::NotifyTabDestroyed(const TabId& aTabId,
 | |
|                                        bool aNotifiedDestroying) {
 | |
|   if (aNotifiedDestroying) {
 | |
|     --mNumDestroyingTabs;
 | |
|   }
 | |
| 
 | |
|   nsTArray<PContentPermissionRequestParent*> parentArray =
 | |
|       nsContentPermissionUtils::GetContentPermissionRequestParentById(aTabId);
 | |
| 
 | |
|   // Need to close undeleted ContentPermissionRequestParents before tab is
 | |
|   // closed.
 | |
|   for (auto& permissionRequestParent : parentArray) {
 | |
|     Unused << PContentPermissionRequestParent::Send__delete__(
 | |
|         permissionRequestParent);
 | |
|   }
 | |
| 
 | |
|   // There can be more than one PBrowser for a given app process
 | |
|   // because of popup windows.  When the last one closes, shut
 | |
|   // us down.
 | |
|   MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
 | |
|           ("NotifyTabDestroyed %p", this));
 | |
| 
 | |
|   MaybeBeginShutDown(/* aExpectedBrowserCount */ 1);
 | |
| }
 | |
| 
 | |
| TestShellParent* ContentParent::CreateTestShell() {
 | |
|   return static_cast<TestShellParent*>(SendPTestShellConstructor());
 | |
| }
 | |
| 
 | |
| bool ContentParent::DestroyTestShell(TestShellParent* aTestShell) {
 | |
|   return PTestShellParent::Send__delete__(aTestShell);
 | |
| }
 | |
| 
 | |
| TestShellParent* ContentParent::GetTestShellSingleton() {
 | |
|   PTestShellParent* p = LoneManagedOrNullAsserts(ManagedPTestShellParent());
 | |
|   return static_cast<TestShellParent*>(p);
 | |
| }
 | |
| 
 | |
| #if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
 | |
| // Append the sandbox command line parameters that are not static. i.e.,
 | |
| // parameters that can be different for different child processes.
 | |
| void ContentParent::AppendDynamicSandboxParams(
 | |
|     std::vector<std::string>& aArgs) {
 | |
|   // For file content processes
 | |
|   if (GetRemoteType() == FILE_REMOTE_TYPE) {
 | |
|     MacSandboxInfo::AppendFileAccessParam(aArgs, true);
 | |
|   }
 | |
| }
 | |
| 
 | |
| // Generate the static sandbox command line parameters and store
 | |
| // them in the provided params vector to be used each time a new
 | |
| // content process is launched.
 | |
| static void CacheSandboxParams(std::vector<std::string>& aCachedParams) {
 | |
|   // This must only be called once and we should
 | |
|   // be starting with an empty list of parameters.
 | |
|   MOZ_ASSERT(aCachedParams.empty());
 | |
| 
 | |
|   MacSandboxInfo info;
 | |
|   info.type = MacSandboxType_Content;
 | |
|   info.level = GetEffectiveContentSandboxLevel();
 | |
| 
 | |
|   // Sandbox logging
 | |
|   if (Preferences::GetBool("security.sandbox.logging.enabled") ||
 | |
|       PR_GetEnv("MOZ_SANDBOX_LOGGING")) {
 | |
|     info.shouldLog = true;
 | |
|   }
 | |
| 
 | |
|   // Audio access
 | |
|   if (!StaticPrefs::media_cubeb_sandbox()) {
 | |
|     info.hasAudio = true;
 | |
|   }
 | |
| 
 | |
|   // Window server access. If the disconnect-windowserver pref is not
 | |
|   // "true" or out-of-process WebGL is not enabled, allow window server
 | |
|   // access in the sandbox policy.
 | |
|   if (!Preferences::GetBool(
 | |
|           "security.sandbox.content.mac.disconnect-windowserver") ||
 | |
|       !Preferences::GetBool("webgl.out-of-process")) {
 | |
|     info.hasWindowServer = true;
 | |
|   }
 | |
| 
 | |
|   // .app path (normalized)
 | |
|   nsAutoCString appPath;
 | |
|   if (!nsMacUtilsImpl::GetAppPath(appPath)) {
 | |
|     MOZ_CRASH("Failed to get app dir paths");
 | |
|   }
 | |
|   info.appPath = appPath.get();
 | |
| 
 | |
|   // TESTING_READ_PATH1
 | |
|   nsAutoCString testingReadPath1;
 | |
|   Preferences::GetCString("security.sandbox.content.mac.testing_read_path1",
 | |
|                           testingReadPath1);
 | |
|   if (!testingReadPath1.IsEmpty()) {
 | |
|     info.testingReadPath1 = testingReadPath1.get();
 | |
|   }
 | |
| 
 | |
|   // TESTING_READ_PATH2
 | |
|   nsAutoCString testingReadPath2;
 | |
|   Preferences::GetCString("security.sandbox.content.mac.testing_read_path2",
 | |
|                           testingReadPath2);
 | |
|   if (!testingReadPath2.IsEmpty()) {
 | |
|     info.testingReadPath2 = testingReadPath2.get();
 | |
|   }
 | |
| 
 | |
|   // TESTING_READ_PATH3, TESTING_READ_PATH4. In non-packaged builds,
 | |
|   // these are used to whitelist the repo dir and object dir respectively.
 | |
|   nsresult rv;
 | |
|   if (!mozilla::IsPackagedBuild()) {
 | |
|     // Repo dir
 | |
|     nsCOMPtr<nsIFile> repoDir;
 | |
|     rv = nsMacUtilsImpl::GetRepoDir(getter_AddRefs(repoDir));
 | |
|     if (NS_FAILED(rv)) {
 | |
|       MOZ_CRASH("Failed to get path to repo dir");
 | |
|     }
 | |
|     nsCString repoDirPath;
 | |
|     Unused << repoDir->GetNativePath(repoDirPath);
 | |
|     info.testingReadPath3 = repoDirPath.get();
 | |
| 
 | |
|     // Object dir
 | |
|     nsCOMPtr<nsIFile> objDir;
 | |
|     rv = nsMacUtilsImpl::GetObjDir(getter_AddRefs(objDir));
 | |
|     if (NS_FAILED(rv)) {
 | |
|       MOZ_CRASH("Failed to get path to build object dir");
 | |
|     }
 | |
|     nsCString objDirPath;
 | |
|     Unused << objDir->GetNativePath(objDirPath);
 | |
|     info.testingReadPath4 = objDirPath.get();
 | |
|   }
 | |
| 
 | |
|   // DEBUG_WRITE_DIR
 | |
| #  ifdef DEBUG
 | |
|   // For bloat/leak logging or when a content process dies intentionally
 | |
|   // (|NoteIntentionalCrash|) for tests, it wants to log that it did this.
 | |
|   // Allow writing to this location.
 | |
|   nsAutoCString bloatLogDirPath;
 | |
|   if (NS_SUCCEEDED(nsMacUtilsImpl::GetBloatLogDir(bloatLogDirPath))) {
 | |
|     info.debugWriteDir = bloatLogDirPath.get();
 | |
|   }
 | |
| #  endif  // DEBUG
 | |
| 
 | |
|   info.AppendAsParams(aCachedParams);
 | |
| }
 | |
| 
 | |
| // Append sandboxing command line parameters.
 | |
| void ContentParent::AppendSandboxParams(std::vector<std::string>& aArgs) {
 | |
|   MOZ_ASSERT(sMacSandboxParams != nullptr);
 | |
| 
 | |
|   // An empty sMacSandboxParams indicates this is the
 | |
|   // first invocation and we don't have cached params yet.
 | |
|   if (sMacSandboxParams->empty()) {
 | |
|     CacheSandboxParams(*sMacSandboxParams);
 | |
|     MOZ_ASSERT(!sMacSandboxParams->empty());
 | |
|   }
 | |
| 
 | |
|   // Append cached arguments.
 | |
|   aArgs.insert(aArgs.end(), sMacSandboxParams->begin(),
 | |
|                sMacSandboxParams->end());
 | |
| 
 | |
|   // Append remaining arguments.
 | |
|   AppendDynamicSandboxParams(aArgs);
 | |
| }
 | |
| #endif  // XP_MACOSX && MOZ_SANDBOX
 | |
| 
 | |
| bool ContentParent::BeginSubprocessLaunch(ProcessPriority aPriority) {
 | |
|   AUTO_PROFILER_LABEL("ContentParent::LaunchSubprocess", OTHER);
 | |
| 
 | |
|   // Ensure we will not rush through our shutdown phases while launching.
 | |
|   // LaunchSubprocessReject will remove them in case of failure,
 | |
|   // otherwise ActorDestroy will take care.
 | |
|   AddShutdownBlockers();
 | |
| 
 | |
|   if (!ContentProcessManager::GetSingleton()) {
 | |
|     MOZ_ASSERT(false, "Unable to acquire ContentProcessManager singleton!");
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   std::vector<std::string> extraArgs;
 | |
|   geckoargs::sChildID.Put(mChildID, extraArgs);
 | |
|   geckoargs::sIsForBrowser.Put(IsForBrowser(), extraArgs);
 | |
|   geckoargs::sNotForBrowser.Put(!IsForBrowser(), extraArgs);
 | |
| 
 | |
|   // Prefs information is passed via anonymous shared memory to avoid bloating
 | |
|   // the command line.
 | |
| 
 | |
|   // Instantiate the pref serializer. It will be cleaned up in
 | |
|   // `LaunchSubprocessReject`/`LaunchSubprocessResolve`.
 | |
|   mPrefSerializer = MakeUnique<mozilla::ipc::SharedPreferenceSerializer>();
 | |
|   if (!mPrefSerializer->SerializeToSharedMemory(GeckoProcessType_Content,
 | |
|                                                 GetRemoteType())) {
 | |
|     NS_WARNING("SharedPreferenceSerializer::SerializeToSharedMemory failed");
 | |
|     MarkAsDead();
 | |
|     return false;
 | |
|   }
 | |
|   mPrefSerializer->AddSharedPrefCmdLineArgs(*mSubprocess, extraArgs);
 | |
| 
 | |
|   // The JS engine does some computation during the initialization which can be
 | |
|   // shared across processes. We add command line arguments to pass a file
 | |
|   // handle and its content length, to minimize the startup time of content
 | |
|   // processes.
 | |
|   ::mozilla::ipc::ExportSharedJSInit(*mSubprocess, extraArgs);
 | |
| 
 | |
| #if defined(XP_WIN) && defined(ACCESSIBILITY)
 | |
|   // Determining the accessibility resource ID causes problems with the sandbox,
 | |
|   // so we pass it on the command line as it is required very early in process
 | |
|   // start up. It is not required when the caching mechanism is being used.
 | |
|   if (!StaticPrefs::accessibility_cache_enabled_AtStartup()) {
 | |
|     // The accessibility resource ID may not be set in some cases, for example
 | |
|     // in xpcshell tests.
 | |
|     auto resourceId = mscom::ActCtxResource::GetAccessibilityResourceId();
 | |
|     if (resourceId) {
 | |
|       geckoargs::sA11yResourceId.Put(resourceId, extraArgs);
 | |
|     }
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   // Register ContentParent as an observer for changes to any pref
 | |
|   // whose prefix matches the empty string, i.e. all of them.  The
 | |
|   // observation starts here in order to capture pref updates that
 | |
|   // happen during async launch.
 | |
|   Preferences::AddStrongObserver(this, "");
 | |
| 
 | |
|   if (gSafeMode) {
 | |
|     geckoargs::sSafeMode.Put(extraArgs);
 | |
|   }
 | |
| 
 | |
| #if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
 | |
|   if (IsContentSandboxEnabled()) {
 | |
|     AppendSandboxParams(extraArgs);
 | |
|     mSubprocess->DisableOSActivityMode();
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   nsCString parentBuildID(mozilla::PlatformBuildID());
 | |
|   geckoargs::sParentBuildID.Put(parentBuildID.get(), extraArgs);
 | |
| 
 | |
| #ifdef MOZ_WIDGET_GTK
 | |
|   // This is X11-only pending a solution for WebGL in Wayland mode.
 | |
|   if (StaticPrefs::dom_ipc_avoid_gtk() &&
 | |
|       StaticPrefs::widget_non_native_theme_enabled() &&
 | |
|       widget::GdkIsX11Display()) {
 | |
|     mSubprocess->SetEnv("MOZ_HEADLESS", "1");
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   // See also ActorDealloc.
 | |
|   mSelfRef = this;
 | |
|   mLaunchYieldTS = TimeStamp::Now();
 | |
|   return mSubprocess->AsyncLaunch(std::move(extraArgs));
 | |
| }
 | |
| 
 | |
| void ContentParent::LaunchSubprocessReject() {
 | |
|   NS_WARNING("failed to launch child in the parent");
 | |
|   MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
 | |
|           ("failed to launch child in the parent"));
 | |
|   // Now that communication with the child is complete, we can cleanup
 | |
|   // the preference serializer.
 | |
|   mPrefSerializer = nullptr;
 | |
|   if (mIsAPreallocBlocker) {
 | |
|     PreallocatedProcessManager::RemoveBlocker(mRemoteType, this);
 | |
|     mIsAPreallocBlocker = false;
 | |
|   }
 | |
|   MarkAsDead();
 | |
|   RemoveShutdownBlockers();
 | |
| }
 | |
| 
 | |
| bool ContentParent::LaunchSubprocessResolve(bool aIsSync,
 | |
|                                             ProcessPriority aPriority) {
 | |
|   AUTO_PROFILER_LABEL("ContentParent::LaunchSubprocess::resolve", OTHER);
 | |
| 
 | |
|   if (mLaunchResolved) {
 | |
|     // We've already been called, return.
 | |
|     MOZ_ASSERT(sCreatedFirstContentProcess);
 | |
|     MOZ_ASSERT(!mPrefSerializer);
 | |
|     MOZ_ASSERT(mLifecycleState != LifecycleState::LAUNCHING);
 | |
|     return mLaunchResolvedOk;
 | |
|   }
 | |
|   mLaunchResolved = true;
 | |
| 
 | |
|   // Now that communication with the child is complete, we can cleanup
 | |
|   // the preference serializer.
 | |
|   mPrefSerializer = nullptr;
 | |
| 
 | |
|   const auto launchResumeTS = TimeStamp::Now();
 | |
|   if (profiler_thread_is_being_profiled_for_markers()) {
 | |
|     nsPrintfCString marker("Process start%s for %u",
 | |
|                            mIsAPreallocBlocker ? " (immediate)" : "",
 | |
|                            (unsigned int)ChildID());
 | |
|     PROFILER_MARKER_TEXT(
 | |
|         mIsAPreallocBlocker ? ProfilerString8View("Process Immediate Launch")
 | |
|                             : ProfilerString8View("Process Launch"),
 | |
|         DOM, MarkerTiming::Interval(mLaunchTS, launchResumeTS), marker);
 | |
|   }
 | |
| 
 | |
|   if (!sCreatedFirstContentProcess) {
 | |
|     nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
 | |
|     obs->NotifyObservers(nullptr, "ipc:first-content-process-created", nullptr);
 | |
|     sCreatedFirstContentProcess = true;
 | |
|   }
 | |
| 
 | |
|   mSubprocess->TakeInitialEndpoint().Bind(this);
 | |
| 
 | |
|   ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
 | |
|   if (!cpm) {
 | |
|     NS_WARNING("immediately shutting-down caused by our shutdown");
 | |
|     ShutDownProcess(SEND_SHUTDOWN_MESSAGE);
 | |
|     return false;
 | |
|   }
 | |
|   cpm->AddContentProcess(this);
 | |
| 
 | |
| #ifdef MOZ_CODE_COVERAGE
 | |
|   Unused << SendShareCodeCoverageMutex(
 | |
|       CodeCoverageHandler::Get()->GetMutexHandle());
 | |
| #endif
 | |
| 
 | |
|   // We must be in the LAUNCHING state still. If we've somehow already been
 | |
|   // marked as DEAD, fail the process launch, and immediately begin tearing down
 | |
|   // the content process.
 | |
|   if (IsDead()) {
 | |
|     NS_WARNING("immediately shutting-down already-dead process");
 | |
|     ShutDownProcess(SEND_SHUTDOWN_MESSAGE);
 | |
|     return false;
 | |
|   }
 | |
|   MOZ_ASSERT(mLifecycleState == LifecycleState::LAUNCHING);
 | |
|   mLifecycleState = LifecycleState::ALIVE;
 | |
| 
 | |
|   if (!InitInternal(aPriority)) {
 | |
|     NS_WARNING("failed to initialize child in the parent");
 | |
|     // We've already called Open() by this point, so we need to close the
 | |
|     // channel to avoid leaking the process.
 | |
|     ShutDownProcess(SEND_SHUTDOWN_MESSAGE);
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   mHangMonitorActor = ProcessHangMonitor::AddProcess(this);
 | |
| 
 | |
|   // Set a reply timeout for CPOWs.
 | |
|   SetReplyTimeoutMs(StaticPrefs::dom_ipc_cpow_timeout());
 | |
| 
 | |
|   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
 | |
|   if (obs) {
 | |
|     nsAutoString cpId;
 | |
|     cpId.AppendInt(static_cast<uint64_t>(this->ChildID()));
 | |
|     obs->NotifyObservers(static_cast<nsIObserver*>(this),
 | |
|                          "ipc:content-initializing", cpId.get());
 | |
|   }
 | |
| 
 | |
|   Init();
 | |
| 
 | |
|   mLifecycleState = LifecycleState::INITIALIZED;
 | |
| 
 | |
|   if (aIsSync) {
 | |
|     Telemetry::AccumulateTimeDelta(Telemetry::CONTENT_PROCESS_SYNC_LAUNCH_MS,
 | |
|                                    mLaunchTS);
 | |
|   } else {
 | |
|     Telemetry::AccumulateTimeDelta(Telemetry::CONTENT_PROCESS_LAUNCH_TOTAL_MS,
 | |
|                                    mLaunchTS);
 | |
| 
 | |
|     Telemetry::Accumulate(
 | |
|         Telemetry::CONTENT_PROCESS_LAUNCH_MAINTHREAD_MS,
 | |
|         static_cast<uint32_t>(
 | |
|             ((mLaunchYieldTS - mLaunchTS) + (TimeStamp::Now() - launchResumeTS))
 | |
|                 .ToMilliseconds()));
 | |
|   }
 | |
| 
 | |
|   mLaunchResolvedOk = true;
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool ContentParent::LaunchSubprocessSync(
 | |
|     hal::ProcessPriority aInitialPriority) {
 | |
|   // We've started a sync content process launch.
 | |
|   Telemetry::Accumulate(Telemetry::CONTENT_PROCESS_LAUNCH_IS_SYNC, 1);
 | |
| 
 | |
|   if (BeginSubprocessLaunch(aInitialPriority)) {
 | |
|     const bool ok = mSubprocess->WaitForProcessHandle();
 | |
|     if (ok && LaunchSubprocessResolve(/* aIsSync = */ true, aInitialPriority)) {
 | |
|       return true;
 | |
|     }
 | |
|   }
 | |
|   LaunchSubprocessReject();
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| RefPtr<ContentParent::LaunchPromise> ContentParent::LaunchSubprocessAsync(
 | |
|     hal::ProcessPriority aInitialPriority) {
 | |
|   // We've started an async content process launch.
 | |
|   Telemetry::Accumulate(Telemetry::CONTENT_PROCESS_LAUNCH_IS_SYNC, 0);
 | |
| 
 | |
|   if (!BeginSubprocessLaunch(aInitialPriority)) {
 | |
|     // Launch aborted because of shutdown. Bailout.
 | |
|     LaunchSubprocessReject();
 | |
|     return LaunchPromise::CreateAndReject(LaunchError(), __func__);
 | |
|   }
 | |
| 
 | |
|   // Otherwise, wait until the process is ready.
 | |
|   RefPtr<ProcessHandlePromise> ready = mSubprocess->WhenProcessHandleReady();
 | |
|   RefPtr<ContentParent> self = this;
 | |
|   mLaunchYieldTS = TimeStamp::Now();
 | |
| 
 | |
|   return ready->Then(
 | |
|       GetCurrentSerialEventTarget(), __func__,
 | |
|       [self, aInitialPriority](
 | |
|           const ProcessHandlePromise::ResolveOrRejectValue& aValue) {
 | |
|         if (aValue.IsResolve() &&
 | |
|             self->LaunchSubprocessResolve(/* aIsSync = */ false,
 | |
|                                           aInitialPriority)) {
 | |
|           return LaunchPromise::CreateAndResolve(self, __func__);
 | |
|         }
 | |
|         self->LaunchSubprocessReject();
 | |
|         return LaunchPromise::CreateAndReject(LaunchError(), __func__);
 | |
|       });
 | |
| }
 | |
| 
 | |
| ContentParent::ContentParent(const nsACString& aRemoteType, int32_t aJSPluginID)
 | |
|     : mSelfRef(nullptr),
 | |
|       mSubprocess(nullptr),
 | |
|       mLaunchTS(TimeStamp::Now()),
 | |
|       mLaunchYieldTS(mLaunchTS),
 | |
|       mActivateTS(mLaunchTS),
 | |
|       mIsAPreallocBlocker(false),
 | |
|       mRemoteType(aRemoteType),
 | |
|       mChildID(gContentChildID++),
 | |
|       mGeolocationWatchID(-1),
 | |
|       mJSPluginID(aJSPluginID),
 | |
|       mThreadsafeHandle(
 | |
|           new ThreadsafeContentParentHandle(this, mChildID, mRemoteType)),
 | |
|       mNumDestroyingTabs(0),
 | |
|       mNumKeepaliveCalls(0),
 | |
|       mLifecycleState(LifecycleState::LAUNCHING),
 | |
|       mIsForBrowser(!mRemoteType.IsEmpty()),
 | |
|       mCalledClose(false),
 | |
|       mCalledKillHard(false),
 | |
|       mCreatedPairedMinidumps(false),
 | |
|       mShutdownPending(false),
 | |
|       mLaunchResolved(false),
 | |
|       mLaunchResolvedOk(false),
 | |
|       mIsRemoteInputEventQueueEnabled(false),
 | |
|       mIsInputPriorityEventEnabled(false),
 | |
|       mIsInPool(false),
 | |
|       mGMPCreated(false),
 | |
| #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
 | |
|       mBlockShutdownCalled(false),
 | |
| #endif
 | |
|       mHangMonitorActor(nullptr) {
 | |
|   MOZ_DIAGNOSTIC_ASSERT(!IsForJSPlugin(),
 | |
|                         "XXX(nika): How are we creating a JSPlugin?");
 | |
| 
 | |
|   mRemoteTypeIsolationPrincipal =
 | |
|       CreateRemoteTypeIsolationPrincipal(aRemoteType);
 | |
| 
 | |
|   // Insert ourselves into the global linked list of ContentParent objects.
 | |
|   if (!sContentParents) {
 | |
|     sContentParents = new LinkedList<ContentParent>();
 | |
|   }
 | |
|   sContentParents->insertBack(this);
 | |
| 
 | |
|   mMessageManager = nsFrameMessageManager::NewProcessMessageManager(true);
 | |
| 
 | |
| #if defined(XP_WIN)
 | |
|   // Request Windows message deferral behavior on our side of the PContent
 | |
|   // channel. Generally only applies to the situation where we get caught in
 | |
|   // a deadlock with the plugin process when sending CPOWs.
 | |
|   GetIPCChannel()->SetChannelFlags(
 | |
|       MessageChannel::REQUIRE_DEFERRED_MESSAGE_PROTECTION);
 | |
| #endif
 | |
| 
 | |
|   NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
 | |
|   bool isFile = mRemoteType == FILE_REMOTE_TYPE;
 | |
|   mSubprocess = new GeckoChildProcessHost(GeckoProcessType_Content, isFile);
 | |
|   MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
 | |
|           ("CreateSubprocess: ContentParent %p mSubprocess %p handle %" PRIuPTR,
 | |
|            this, mSubprocess,
 | |
|            mSubprocess ? (uintptr_t)mSubprocess->GetChildProcessHandle() : -1));
 | |
| 
 | |
|   // This is safe to do in the constructor, as it doesn't take a strong
 | |
|   // reference.
 | |
|   mScriptableHelper = new ScriptableCPInfo(this);
 | |
| }
 | |
| 
 | |
| ContentParent::~ContentParent() {
 | |
|   if (mForceKillTimer) {
 | |
|     mForceKillTimer->Cancel();
 | |
|   }
 | |
| 
 | |
|   AssertIsOnMainThread();
 | |
| 
 | |
|   // Clear the weak reference from the threadsafe handle back to this actor.
 | |
|   mThreadsafeHandle->mWeakActor = nullptr;
 | |
| 
 | |
|   if (mIsAPreallocBlocker) {
 | |
|     MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
 | |
|             ("Removing blocker on ContentProcess destruction"));
 | |
|     PreallocatedProcessManager::RemoveBlocker(mRemoteType, this);
 | |
|     mIsAPreallocBlocker = false;
 | |
|   }
 | |
| 
 | |
|   // We should be removed from all these lists in ActorDestroy.
 | |
|   AssertNotInPool();
 | |
| 
 | |
|   // Normally mSubprocess is destroyed in ActorDestroy, but that won't
 | |
|   // happen if the process wasn't launched or if it failed to launch.
 | |
|   if (mSubprocess) {
 | |
|     MOZ_LOG(
 | |
|         ContentParent::GetLog(), LogLevel::Verbose,
 | |
|         ("DestroySubprocess: ContentParent %p mSubprocess %p handle %" PRIuPTR,
 | |
|          this, mSubprocess,
 | |
|          mSubprocess ? (uintptr_t)mSubprocess->GetChildProcessHandle() : -1));
 | |
|     mSubprocess->Destroy();
 | |
|   }
 | |
| 
 | |
|   // Make sure to clear the connection from `mScriptableHelper` if it hasn't
 | |
|   // been cleared yet.
 | |
|   if (mScriptableHelper) {
 | |
|     static_cast<ScriptableCPInfo*>(mScriptableHelper.get())->ProcessDied();
 | |
|     mScriptableHelper = nullptr;
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool ContentParent::InitInternal(ProcessPriority aInitialPriority) {
 | |
|   // We can't access the locale service after shutdown has started. Since we
 | |
|   // can't init the process without it, and since we're going to be canceling
 | |
|   // whatever load attempt that initiated this process creation anyway, just
 | |
|   // bail out now if shutdown has already started.
 | |
|   if (PastShutdownPhase(ShutdownPhase::XPCOMShutdown)) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   XPCOMInitData xpcomInit;
 | |
| 
 | |
|   MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
 | |
|           ("ContentParent::InitInternal: %p", (void*)this));
 | |
|   nsCOMPtr<nsIIOService> io(do_GetIOService());
 | |
|   MOZ_ASSERT(io, "No IO service?");
 | |
|   DebugOnly<nsresult> rv = io->GetOffline(&xpcomInit.isOffline());
 | |
|   MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed getting offline?");
 | |
| 
 | |
|   rv = io->GetConnectivity(&xpcomInit.isConnected());
 | |
|   MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed getting connectivity?");
 | |
| 
 | |
|   xpcomInit.captivePortalState() = nsICaptivePortalService::UNKNOWN;
 | |
|   nsCOMPtr<nsICaptivePortalService> cps =
 | |
|       do_GetService(NS_CAPTIVEPORTAL_CONTRACTID);
 | |
|   if (cps) {
 | |
|     cps->GetState(&xpcomInit.captivePortalState());
 | |
|   }
 | |
| 
 | |
|   if (StaticPrefs::fission_processProfileName()) {
 | |
|     nsCOMPtr<nsIToolkitProfileService> profileSvc =
 | |
|         do_GetService(NS_PROFILESERVICE_CONTRACTID);
 | |
|     if (profileSvc) {
 | |
|       nsCOMPtr<nsIToolkitProfile> currentProfile;
 | |
|       nsresult rv =
 | |
|           profileSvc->GetCurrentProfile(getter_AddRefs(currentProfile));
 | |
|       if (NS_SUCCEEDED(rv) && currentProfile) {
 | |
|         currentProfile->GetName(mProfile);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   nsIBidiKeyboard* bidi = nsContentUtils::GetBidiKeyboard();
 | |
| 
 | |
|   xpcomInit.isLangRTL() = false;
 | |
|   xpcomInit.haveBidiKeyboards() = false;
 | |
|   if (bidi) {
 | |
|     bidi->IsLangRTL(&xpcomInit.isLangRTL());
 | |
|     bidi->GetHaveBidiKeyboards(&xpcomInit.haveBidiKeyboards());
 | |
|   }
 | |
| 
 | |
|   RefPtr<mozSpellChecker> spellChecker(mozSpellChecker::Create());
 | |
|   MOZ_ASSERT(spellChecker, "No spell checker?");
 | |
| 
 | |
|   spellChecker->GetDictionaryList(&xpcomInit.dictionaries());
 | |
| 
 | |
|   LocaleService::GetInstance()->GetAppLocalesAsBCP47(xpcomInit.appLocales());
 | |
|   LocaleService::GetInstance()->GetRequestedLocales(
 | |
|       xpcomInit.requestedLocales());
 | |
| 
 | |
|   L10nRegistry::GetParentProcessFileSourceDescriptors(
 | |
|       xpcomInit.l10nFileSources());
 | |
| 
 | |
|   nsCOMPtr<nsIClipboard> clipboard(
 | |
|       do_GetService("@mozilla.org/widget/clipboard;1"));
 | |
|   MOZ_ASSERT(clipboard, "No clipboard?");
 | |
|   MOZ_ASSERT(
 | |
|       clipboard->IsClipboardTypeSupported(nsIClipboard::kGlobalClipboard),
 | |
|       "We should always support the global clipboard.");
 | |
| 
 | |
|   xpcomInit.clipboardCaps().supportsSelectionClipboard() =
 | |
|       clipboard->IsClipboardTypeSupported(nsIClipboard::kSelectionClipboard);
 | |
| 
 | |
|   xpcomInit.clipboardCaps().supportsFindClipboard() =
 | |
|       clipboard->IsClipboardTypeSupported(nsIClipboard::kFindClipboard);
 | |
| 
 | |
|   xpcomInit.clipboardCaps().supportsSelectionCache() =
 | |
|       clipboard->IsClipboardTypeSupported(nsIClipboard::kSelectionCache);
 | |
| 
 | |
|   // Let's copy the domain policy from the parent to the child (if it's active).
 | |
|   StructuredCloneData initialData;
 | |
|   nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
 | |
|   if (ssm) {
 | |
|     ssm->CloneDomainPolicy(&xpcomInit.domainPolicy());
 | |
| 
 | |
|     if (ParentProcessMessageManager* mm =
 | |
|             nsFrameMessageManager::sParentProcessManager) {
 | |
|       AutoJSAPI jsapi;
 | |
|       if (NS_WARN_IF(!jsapi.Init(xpc::PrivilegedJunkScope()))) {
 | |
|         MOZ_CRASH();
 | |
|       }
 | |
|       JS::Rooted<JS::Value> init(jsapi.cx());
 | |
|       // We'll crash on failure, so use a IgnoredErrorResult (which also
 | |
|       // auto-suppresses exceptions).
 | |
|       IgnoredErrorResult rv;
 | |
|       mm->GetInitialProcessData(jsapi.cx(), &init, rv);
 | |
|       if (NS_WARN_IF(rv.Failed())) {
 | |
|         MOZ_CRASH();
 | |
|       }
 | |
| 
 | |
|       initialData.Write(jsapi.cx(), init, rv);
 | |
|       if (NS_WARN_IF(rv.Failed())) {
 | |
|         MOZ_CRASH();
 | |
|       }
 | |
|     }
 | |
|   }
 | |
|   // This is only implemented (returns a non-empty list) by MacOSX and Linux
 | |
|   // at present.
 | |
|   SystemFontList fontList;
 | |
|   gfxPlatform::GetPlatform()->ReadSystemFontList(&fontList);
 | |
| 
 | |
|   const FullLookAndFeel& lnf = *RemoteLookAndFeel::ExtractData();
 | |
| 
 | |
|   // If the shared fontlist is in use, collect its shmem block handles to pass
 | |
|   // to the child.
 | |
|   nsTArray<SharedMemoryHandle> sharedFontListBlocks;
 | |
|   gfxPlatformFontList::PlatformFontList()->ShareFontListToProcess(
 | |
|       &sharedFontListBlocks, OtherPid());
 | |
| 
 | |
|   // Content processes have no permission to access profile directory, so we
 | |
|   // send the file URL instead.
 | |
|   auto* sheetCache = GlobalStyleSheetCache::Singleton();
 | |
|   if (StyleSheet* ucs = sheetCache->GetUserContentSheet()) {
 | |
|     xpcomInit.userContentSheetURL() = ucs->GetSheetURI();
 | |
|   } else {
 | |
|     xpcomInit.userContentSheetURL() = nullptr;
 | |
|   }
 | |
| 
 | |
|   // 1. Build ContentDeviceData first, as it may affect some gfxVars.
 | |
|   gfxPlatform::GetPlatform()->BuildContentDeviceData(
 | |
|       &xpcomInit.contentDeviceData());
 | |
|   // 2. Gather non-default gfxVars.
 | |
|   xpcomInit.gfxNonDefaultVarUpdates() = gfxVars::FetchNonDefaultVars();
 | |
|   // 3. Start listening for gfxVars updates, to notify content process later on.
 | |
|   gfxVars::AddReceiver(this);
 | |
| 
 | |
|   nsCOMPtr<nsIGfxInfo> gfxInfo = components::GfxInfo::Service();
 | |
|   if (gfxInfo) {
 | |
|     GfxInfoBase* gfxInfoRaw = static_cast<GfxInfoBase*>(gfxInfo.get());
 | |
|     xpcomInit.gfxFeatureStatus() = gfxInfoRaw->GetAllFeatures();
 | |
|   }
 | |
| 
 | |
|   // Send the dynamic scalar definitions to the new process.
 | |
|   TelemetryIPC::GetDynamicScalarDefinitions(xpcomInit.dynamicScalarDefs());
 | |
| 
 | |
|   for (auto const& [location, supported] : sCodecsSupported) {
 | |
|     Unused << SendUpdateMediaCodecsSupported(location, supported);
 | |
|   }
 | |
| 
 | |
| #ifdef MOZ_WIDGET_ANDROID
 | |
|   if (!(StaticPrefs::media_utility_process_enabled() &&
 | |
|         StaticPrefs::media_utility_android_media_codec_enabled())) {
 | |
|     Unused << SendDecoderSupportedMimeTypes(
 | |
|         AndroidDecoderModule::GetSupportedMimeTypes());
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   // Must send screen info before send initialData
 | |
|   ScreenManager& screenManager = ScreenManager::GetSingleton();
 | |
|   screenManager.CopyScreensToRemote(this);
 | |
| 
 | |
|   // Send the UA sheet shared memory buffer and the address it is mapped at.
 | |
|   Maybe<SharedMemoryHandle> sharedUASheetHandle;
 | |
|   uintptr_t sharedUASheetAddress = sheetCache->GetSharedMemoryAddress();
 | |
| 
 | |
|   if (SharedMemoryHandle handle = sheetCache->CloneHandle()) {
 | |
|     sharedUASheetHandle.emplace(std::move(handle));
 | |
|   } else {
 | |
|     sharedUASheetAddress = 0;
 | |
|   }
 | |
| 
 | |
|   bool isReadyForBackgroundProcessing = false;
 | |
| #if defined(XP_WIN)
 | |
|   RefPtr<DllServices> dllSvc(DllServices::Get());
 | |
|   isReadyForBackgroundProcessing = dllSvc->IsReadyForBackgroundProcessing();
 | |
| #endif
 | |
| 
 | |
|   xpcomInit.perfStatsMask() = PerfStats::GetCollectionMask();
 | |
| 
 | |
|   nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
 | |
|   dns->GetTrrDomain(xpcomInit.trrDomain());
 | |
| 
 | |
|   nsIDNSService::ResolverMode mode;
 | |
|   dns->GetCurrentTrrMode(&mode);
 | |
|   xpcomInit.trrMode() = mode;
 | |
| 
 | |
|   Unused << SendSetXPCOMProcessAttributes(
 | |
|       xpcomInit, initialData, lnf, fontList, std::move(sharedUASheetHandle),
 | |
|       sharedUASheetAddress, std::move(sharedFontListBlocks),
 | |
|       isReadyForBackgroundProcessing);
 | |
| 
 | |
|   ipc::WritableSharedMap* sharedData =
 | |
|       nsFrameMessageManager::sParentProcessManager->SharedData();
 | |
|   sharedData->Flush();
 | |
|   sharedData->SendTo(this);
 | |
| 
 | |
|   nsCOMPtr<nsIChromeRegistry> registrySvc = nsChromeRegistry::GetService();
 | |
|   nsChromeRegistryChrome* chromeRegistry =
 | |
|       static_cast<nsChromeRegistryChrome*>(registrySvc.get());
 | |
|   chromeRegistry->SendRegisteredChrome(this);
 | |
| 
 | |
|   nsCOMPtr<nsIStringBundleService> stringBundleService =
 | |
|       components::StringBundle::Service();
 | |
|   stringBundleService->SendContentBundles(this);
 | |
| 
 | |
|   if (gAppData) {
 | |
|     nsCString version(gAppData->version);
 | |
|     nsCString buildID(gAppData->buildID);
 | |
|     nsCString name(gAppData->name);
 | |
|     nsCString UAName(gAppData->UAName);
 | |
|     nsCString ID(gAppData->ID);
 | |
|     nsCString vendor(gAppData->vendor);
 | |
|     nsCString sourceURL(gAppData->sourceURL);
 | |
|     nsCString updateURL(gAppData->updateURL);
 | |
| 
 | |
|     // Sending all information to content process.
 | |
|     Unused << SendAppInfo(version, buildID, name, UAName, ID, vendor, sourceURL,
 | |
|                           updateURL);
 | |
|   }
 | |
| 
 | |
|   // Send the child its remote type. On Mac, this needs to be sent prior
 | |
|   // to the message we send to enable the Sandbox (SendStartProcessSandbox)
 | |
|   // because different remote types require different sandbox privileges.
 | |
| 
 | |
|   Unused << SendRemoteType(mRemoteType, mProfile);
 | |
| 
 | |
|   ScriptPreloader::InitContentChild(*this);
 | |
| 
 | |
|   // Initialize the message manager (and load delayed scripts) now that we
 | |
|   // have established communications with the child.
 | |
|   mMessageManager->InitWithCallback(this);
 | |
|   mMessageManager->SetOsPid(Pid());
 | |
| 
 | |
|   // Set the subprocess's priority.  We do this early on because we're likely
 | |
|   // /lowering/ the process's CPU and memory priority, which it has inherited
 | |
|   // from this process.
 | |
|   //
 | |
|   // This call can cause us to send IPC messages to the child process, so it
 | |
|   // must come after the Open() call above.
 | |
|   ProcessPriorityManager::SetProcessPriority(this, aInitialPriority);
 | |
| 
 | |
|   // NB: internally, this will send an IPC message to the child
 | |
|   // process to get it to create the CompositorBridgeChild.  This
 | |
|   // message goes through the regular IPC queue for this
 | |
|   // channel, so delivery will happen-before any other messages
 | |
|   // we send.  The CompositorBridgeChild must be created before any
 | |
|   // PBrowsers are created, because they rely on the Compositor
 | |
|   // already being around.  (Creation is async, so can't happen
 | |
|   // on demand.)
 | |
|   GPUProcessManager* gpm = GPUProcessManager::Get();
 | |
| 
 | |
|   Endpoint<PCompositorManagerChild> compositor;
 | |
|   Endpoint<PImageBridgeChild> imageBridge;
 | |
|   Endpoint<PVRManagerChild> vrBridge;
 | |
|   Endpoint<PRemoteDecoderManagerChild> videoManager;
 | |
|   AutoTArray<uint32_t, 3> namespaces;
 | |
| 
 | |
|   if (!gpm->CreateContentBridges(OtherPid(), &compositor, &imageBridge,
 | |
|                                  &vrBridge, &videoManager, &namespaces)) {
 | |
|     // This can fail if we've already started shutting down the compositor
 | |
|     // thread. See Bug 1562763 comment 8.
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   Unused << SendInitRendering(std::move(compositor), std::move(imageBridge),
 | |
|                               std::move(vrBridge), std::move(videoManager),
 | |
|                               namespaces);
 | |
| 
 | |
|   gpm->AddListener(this);
 | |
| 
 | |
|   nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance();
 | |
|   if (sheetService) {
 | |
|     // This looks like a lot of work, but in a normal browser session we just
 | |
|     // send two loads.
 | |
|     //
 | |
|     // The URIs of the Gecko and Servo sheets should be the same, so it
 | |
|     // shouldn't matter which we look at.
 | |
| 
 | |
|     for (StyleSheet* sheet : *sheetService->AgentStyleSheets()) {
 | |
|       Unused << SendLoadAndRegisterSheet(sheet->GetSheetURI(),
 | |
|                                          nsIStyleSheetService::AGENT_SHEET);
 | |
|     }
 | |
| 
 | |
|     for (StyleSheet* sheet : *sheetService->UserStyleSheets()) {
 | |
|       Unused << SendLoadAndRegisterSheet(sheet->GetSheetURI(),
 | |
|                                          nsIStyleSheetService::USER_SHEET);
 | |
|     }
 | |
| 
 | |
|     for (StyleSheet* sheet : *sheetService->AuthorStyleSheets()) {
 | |
|       Unused << SendLoadAndRegisterSheet(sheet->GetSheetURI(),
 | |
|                                          nsIStyleSheetService::AUTHOR_SHEET);
 | |
|     }
 | |
|   }
 | |
| 
 | |
| #ifdef MOZ_SANDBOX
 | |
|   bool shouldSandbox = true;
 | |
|   Maybe<FileDescriptor> brokerFd;
 | |
|   // XXX: Checking the pref here makes it possible to enable/disable sandboxing
 | |
|   // during an active session. Currently the pref is only used for testing
 | |
|   // purpose. If the decision is made to permanently rely on the pref, this
 | |
|   // should be changed so that it is required to restart firefox for the change
 | |
|   // of value to take effect. Always send SetProcessSandbox message on macOS.
 | |
| #  if !defined(XP_MACOSX)
 | |
|   shouldSandbox = IsContentSandboxEnabled();
 | |
| #  endif
 | |
| 
 | |
| #  ifdef XP_LINUX
 | |
|   if (shouldSandbox) {
 | |
|     MOZ_ASSERT(!mSandboxBroker);
 | |
|     bool isFileProcess = mRemoteType == FILE_REMOTE_TYPE;
 | |
|     UniquePtr<SandboxBroker::Policy> policy =
 | |
|         sSandboxBrokerPolicyFactory->GetContentPolicy(Pid(), isFileProcess);
 | |
|     if (policy) {
 | |
|       brokerFd = Some(FileDescriptor());
 | |
|       mSandboxBroker =
 | |
|           SandboxBroker::Create(std::move(policy), Pid(), brokerFd.ref());
 | |
|       if (!mSandboxBroker) {
 | |
|         KillHard("SandboxBroker::Create failed");
 | |
|         return false;
 | |
|       }
 | |
|       MOZ_ASSERT(brokerFd.ref().IsValid());
 | |
|     }
 | |
|   }
 | |
| #  endif
 | |
|   if (shouldSandbox && !SendSetProcessSandbox(brokerFd)) {
 | |
|     KillHard("SandboxInitFailed");
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   // Ensure that the default set of permissions are avaliable in the content
 | |
|   // process before we try to load any URIs in it.
 | |
|   //
 | |
|   // NOTE: All default permissions has to be transmitted to the child process
 | |
|   // before the blob urls in the for loop below (See Bug 1738713 comment 12).
 | |
|   EnsurePermissionsByKey(""_ns, ""_ns);
 | |
| 
 | |
|   {
 | |
|     nsTArray<BlobURLRegistrationData> registrations;
 | |
|     BlobURLProtocolHandler::ForEachBlobURL(
 | |
|         [&](BlobImpl* aBlobImpl, nsIPrincipal* aPrincipal,
 | |
|             const Maybe<nsID>& aAgentClusterId, const nsACString& aURI,
 | |
|             bool aRevoked) {
 | |
|           // We send all moz-extension Blob URL's to all content processes
 | |
|           // because content scripts mean that a moz-extension can live in any
 | |
|           // process. Same thing for system principal Blob URLs. Content Blob
 | |
|           // URL's are sent for content principals on-demand by
 | |
|           // AboutToLoadHttpFtpDocumentForChild and RemoteWorkerManager.
 | |
|           if (!BlobURLProtocolHandler::IsBlobURLBroadcastPrincipal(
 | |
|                   aPrincipal)) {
 | |
|             return true;
 | |
|           }
 | |
| 
 | |
|           IPCBlob ipcBlob;
 | |
|           nsresult rv = IPCBlobUtils::Serialize(aBlobImpl, ipcBlob);
 | |
|           if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|             return false;
 | |
|           }
 | |
| 
 | |
|           registrations.AppendElement(BlobURLRegistrationData(
 | |
|               nsCString(aURI), ipcBlob, aPrincipal, aAgentClusterId, aRevoked));
 | |
| 
 | |
|           rv = TransmitPermissionsForPrincipal(aPrincipal);
 | |
|           Unused << NS_WARN_IF(NS_FAILED(rv));
 | |
|           return true;
 | |
|         });
 | |
| 
 | |
|     if (!registrations.IsEmpty()) {
 | |
|       Unused << SendInitBlobURLs(registrations);
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // Send down { Parent, Window }ActorOptions at startup to content process.
 | |
|   RefPtr<JSActorService> actorSvc = JSActorService::GetSingleton();
 | |
|   if (actorSvc) {
 | |
|     nsTArray<JSProcessActorInfo> contentInfos;
 | |
|     actorSvc->GetJSProcessActorInfos(contentInfos);
 | |
| 
 | |
|     nsTArray<JSWindowActorInfo> windowInfos;
 | |
|     actorSvc->GetJSWindowActorInfos(windowInfos);
 | |
| 
 | |
|     Unused << SendInitJSActorInfos(contentInfos, windowInfos);
 | |
|   }
 | |
| 
 | |
|   // Begin subscribing to any BrowsingContextGroups which were hosted by this
 | |
|   // process before it finished launching.
 | |
|   for (const auto& group : mGroups) {
 | |
|     group->Subscribe(this);
 | |
|   }
 | |
| 
 | |
|   MaybeEnableRemoteInputEventQueue();
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool ContentParent::IsAlive() const {
 | |
|   return mLifecycleState == LifecycleState::ALIVE ||
 | |
|          mLifecycleState == LifecycleState::INITIALIZED;
 | |
| }
 | |
| 
 | |
| bool ContentParent::IsInitialized() const {
 | |
|   return mLifecycleState == LifecycleState::INITIALIZED;
 | |
| }
 | |
| 
 | |
| int32_t ContentParent::Pid() const {
 | |
|   if (!mSubprocess) {
 | |
|     return -1;
 | |
|   }
 | |
|   auto pid = mSubprocess->GetChildProcessId();
 | |
|   if (pid == 0) {
 | |
|     return -1;
 | |
|   }
 | |
|   return ReleaseAssertedCast<int32_t>(pid);
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvGetGfxVars(
 | |
|     nsTArray<GfxVarUpdate>* aVars) {
 | |
|   // Ensure gfxVars is initialized (for xpcshell tests).
 | |
|   gfxVars::Initialize();
 | |
| 
 | |
|   *aVars = gfxVars::FetchNonDefaultVars();
 | |
| 
 | |
|   // Now that content has initialized gfxVars, we can start listening for
 | |
|   // updates.
 | |
|   gfxVars::AddReceiver(this);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| void ContentParent::OnCompositorUnexpectedShutdown() {
 | |
|   GPUProcessManager* gpm = GPUProcessManager::Get();
 | |
| 
 | |
|   Endpoint<PCompositorManagerChild> compositor;
 | |
|   Endpoint<PImageBridgeChild> imageBridge;
 | |
|   Endpoint<PVRManagerChild> vrBridge;
 | |
|   Endpoint<PRemoteDecoderManagerChild> videoManager;
 | |
|   AutoTArray<uint32_t, 3> namespaces;
 | |
| 
 | |
|   DebugOnly<bool> opened =
 | |
|       gpm->CreateContentBridges(OtherPid(), &compositor, &imageBridge,
 | |
|                                 &vrBridge, &videoManager, &namespaces);
 | |
|   MOZ_ASSERT(opened);
 | |
| 
 | |
|   Unused << SendReinitRendering(std::move(compositor), std::move(imageBridge),
 | |
|                                 std::move(vrBridge), std::move(videoManager),
 | |
|                                 namespaces);
 | |
| }
 | |
| 
 | |
| void ContentParent::OnCompositorDeviceReset() {
 | |
|   Unused << SendReinitRenderingForDeviceReset();
 | |
| }
 | |
| 
 | |
| void ContentParent::MaybeEnableRemoteInputEventQueue() {
 | |
|   MOZ_ASSERT(!mIsRemoteInputEventQueueEnabled);
 | |
|   if (!IsInputEventQueueSupported()) {
 | |
|     return;
 | |
|   }
 | |
|   mIsRemoteInputEventQueueEnabled = true;
 | |
|   Unused << SendSetInputEventQueueEnabled();
 | |
|   SetInputPriorityEventEnabled(true);
 | |
| }
 | |
| 
 | |
| void ContentParent::SetInputPriorityEventEnabled(bool aEnabled) {
 | |
|   if (!IsInputEventQueueSupported() || !mIsRemoteInputEventQueueEnabled ||
 | |
|       mIsInputPriorityEventEnabled == aEnabled) {
 | |
|     return;
 | |
|   }
 | |
|   mIsInputPriorityEventEnabled = aEnabled;
 | |
|   // Send IPC messages to flush the pending events in the input event queue and
 | |
|   // the normal event queue. See PContent.ipdl for more details.
 | |
|   Unused << SendSuspendInputEventQueue();
 | |
|   Unused << SendFlushInputEventQueue();
 | |
|   Unused << SendResumeInputEventQueue();
 | |
| }
 | |
| 
 | |
| /*static*/
 | |
| bool ContentParent::IsInputEventQueueSupported() {
 | |
|   static bool sSupported = false;
 | |
|   static bool sInitialized = false;
 | |
|   if (!sInitialized) {
 | |
|     MOZ_ASSERT(Preferences::IsServiceAvailable());
 | |
|     sSupported = Preferences::GetBool("input_event_queue.supported", false);
 | |
|     sInitialized = true;
 | |
|   }
 | |
|   return sSupported;
 | |
| }
 | |
| 
 | |
| void ContentParent::OnVarChanged(const GfxVarUpdate& aVar) {
 | |
|   if (!CanSend()) {
 | |
|     return;
 | |
|   }
 | |
|   Unused << SendVarUpdate(aVar);
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvSetClipboard(
 | |
|     const IPCDataTransfer& aDataTransfer, const bool& aIsPrivateData,
 | |
|     nsIPrincipal* aRequestingPrincipal,
 | |
|     const nsContentPolicyType& aContentPolicyType,
 | |
|     nsIReferrerInfo* aReferrerInfo, const int32_t& aWhichClipboard) {
 | |
|   // aRequestingPrincipal is allowed to be nullptr here.
 | |
| 
 | |
|   if (!ValidatePrincipal(aRequestingPrincipal,
 | |
|                          {ValidatePrincipalOptions::AllowNullPtr})) {
 | |
|     LogAndAssertFailedPrincipalValidationInfo(aRequestingPrincipal, __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);
 | |
|   trans->SetReferrerInfo(aReferrerInfo);
 | |
| 
 | |
|   rv = nsContentUtils::IPCTransferableToTransferable(
 | |
|       aDataTransfer, aIsPrivateData, aRequestingPrincipal, aContentPolicyType,
 | |
|       true /* aAddDataFlavor */, trans, true /* aFilterUnknownFlavors */);
 | |
|   NS_ENSURE_SUCCESS(rv, IPC_OK());
 | |
| 
 | |
|   clipboard->SetData(trans, nullptr, aWhichClipboard);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| namespace {
 | |
| 
 | |
| static Result<nsCOMPtr<nsITransferable>, nsresult> CreateTransferable(
 | |
|     const nsTArray<nsCString>& aTypes) {
 | |
|   nsresult rv;
 | |
|   nsCOMPtr<nsITransferable> trans =
 | |
|       do_CreateInstance("@mozilla.org/widget/transferable;1", &rv);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     return Err(rv);
 | |
|   }
 | |
| 
 | |
|   MOZ_TRY(trans->Init(nullptr));
 | |
|   // The private flag is only used to prevent the data from being cached to the
 | |
|   // disk. The flag is not exported to the IPCDataTransfer object.
 | |
|   // The flag is set because we are not sure whether the clipboard data is used
 | |
|   // in a private browsing context. The transferable is only used in this scope,
 | |
|   // so the cache would not reduce memory consumption anyway.
 | |
|   trans->SetIsPrivateData(true);
 | |
|   // Fill out flavors for transferable
 | |
|   for (uint32_t t = 0; t < aTypes.Length(); t++) {
 | |
|     MOZ_TRY(trans->AddDataFlavor(aTypes[t].get()));
 | |
|   }
 | |
| 
 | |
|   return std::move(trans);
 | |
| }
 | |
| 
 | |
| }  // anonymous namespace
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvGetClipboard(
 | |
|     nsTArray<nsCString>&& aTypes, const int32_t& aWhichClipboard,
 | |
|     IPCDataTransfer* aDataTransfer) {
 | |
|   nsresult rv;
 | |
|   // Retrieve clipboard
 | |
|   nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
 | |
|   if (NS_FAILED(rv)) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   // Create transferable
 | |
|   auto result = CreateTransferable(aTypes);
 | |
|   if (result.isErr()) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   // Get data from clipboard
 | |
|   nsCOMPtr<nsITransferable> trans = result.unwrap();
 | |
|   clipboard->GetData(trans, aWhichClipboard);
 | |
| 
 | |
|   nsContentUtils::TransferableToIPCTransferable(
 | |
|       trans, aDataTransfer, true /* aInSyncMessage */, this);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvEmptyClipboard(
 | |
|     const int32_t& aWhichClipboard) {
 | |
|   nsresult rv;
 | |
|   nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
 | |
|   NS_ENSURE_SUCCESS(rv, IPC_OK());
 | |
| 
 | |
|   clipboard->EmptyClipboard(aWhichClipboard);
 | |
| 
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvClipboardHasType(
 | |
|     nsTArray<nsCString>&& aTypes, const int32_t& aWhichClipboard,
 | |
|     bool* aHasType) {
 | |
|   nsresult rv;
 | |
|   nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
 | |
|   NS_ENSURE_SUCCESS(rv, IPC_OK());
 | |
| 
 | |
|   clipboard->HasDataMatchingFlavors(aTypes, aWhichClipboard, aHasType);
 | |
| 
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvClipboardHasTypesAsync(
 | |
|     nsTArray<nsCString>&& aTypes, const int32_t& aWhichClipboard,
 | |
|     ClipboardHasTypesAsyncResolver&& aResolver) {
 | |
|   nsresult rv;
 | |
|   nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
 | |
|   if (NS_FAILED(rv)) {
 | |
|     return IPC_FAIL(this, "RecvGetClipboardTypes failed.");
 | |
|   }
 | |
| 
 | |
|   clipboard->AsyncHasDataMatchingFlavors(aTypes, aWhichClipboard)
 | |
|       ->Then(
 | |
|           GetMainThreadSerialEventTarget(), __func__,
 | |
|           /* resolve */
 | |
|           [aResolver](nsTArray<nsCString> types) { aResolver(types); },
 | |
|           /* reject */
 | |
|           [aResolver](nsresult rv) { aResolver(nsTArray<nsCString>{}); });
 | |
| 
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvGetExternalClipboardFormats(
 | |
|     const int32_t& aWhichClipboard, const bool& aPlainTextOnly,
 | |
|     nsTArray<nsCString>* aTypes) {
 | |
|   MOZ_ASSERT(aTypes);
 | |
|   DataTransfer::GetExternalClipboardFormats(aWhichClipboard, aPlainTextOnly,
 | |
|                                             aTypes);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvGetClipboardAsync(
 | |
|     nsTArray<nsCString>&& aTypes, const int32_t& aWhichClipboard,
 | |
|     GetClipboardAsyncResolver&& aResolver) {
 | |
|   nsresult rv;
 | |
|   // Retrieve clipboard
 | |
|   nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
 | |
|   if (NS_FAILED(rv)) {
 | |
|     aResolver(rv);
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   // Create transferable
 | |
|   auto result = CreateTransferable(aTypes);
 | |
|   if (result.isErr()) {
 | |
|     aResolver(result.unwrapErr());
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   // Get data from clipboard
 | |
|   nsCOMPtr<nsITransferable> trans = result.unwrap();
 | |
|   clipboard->AsyncGetData(trans, nsIClipboard::kGlobalClipboard)
 | |
|       ->Then(GetMainThreadSerialEventTarget(), __func__,
 | |
|              [trans, aResolver, self = RefPtr{this}](
 | |
|                  GenericPromise::ResolveOrRejectValue&& aValue) {
 | |
|                IPCDataTransfer ipcDataTransfer;
 | |
|                nsContentUtils::TransferableToIPCTransferable(
 | |
|                    trans, &ipcDataTransfer, false /* aInSyncMessage */, self);
 | |
|                aResolver(std::move(ipcDataTransfer));
 | |
|              });
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvPlaySound(nsIURI* aURI) {
 | |
|   // If the check here fails, it can only mean that this message was spoofed.
 | |
|   if (!aURI || !aURI->SchemeIs("chrome")) {
 | |
|     // PlaySound only accepts a valid chrome URI.
 | |
|     return IPC_FAIL(this, "Invalid aURI passed.");
 | |
|   }
 | |
|   nsCOMPtr<nsIURL> soundURL(do_QueryInterface(aURI));
 | |
|   if (!soundURL) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   nsresult rv;
 | |
|   nsCOMPtr<nsISound> sound(do_GetService(NS_SOUND_CID, &rv));
 | |
|   NS_ENSURE_SUCCESS(rv, IPC_OK());
 | |
| 
 | |
|   sound->Play(soundURL);
 | |
| 
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvBeep() {
 | |
|   nsresult rv;
 | |
|   nsCOMPtr<nsISound> sound(do_GetService(NS_SOUND_CID, &rv));
 | |
|   NS_ENSURE_SUCCESS(rv, IPC_OK());
 | |
| 
 | |
|   sound->Beep();
 | |
| 
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvPlayEventSound(
 | |
|     const uint32_t& aEventId) {
 | |
|   nsresult rv;
 | |
|   nsCOMPtr<nsISound> sound(do_GetService(NS_SOUND_CID, &rv));
 | |
|   NS_ENSURE_SUCCESS(rv, IPC_OK());
 | |
| 
 | |
|   sound->PlayEventSound(aEventId);
 | |
| 
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvGetIconForExtension(
 | |
|     const nsACString& aFileExt, const uint32_t& aIconSize,
 | |
|     nsTArray<uint8_t>* bits) {
 | |
| #ifdef MOZ_WIDGET_ANDROID
 | |
|   NS_ASSERTION(AndroidBridge::Bridge() != nullptr,
 | |
|                "AndroidBridge is not available");
 | |
|   if (AndroidBridge::Bridge() == nullptr) {
 | |
|     // Do not fail - just no icon will be shown
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   bits->AppendElements(aIconSize * aIconSize * 4);
 | |
| 
 | |
|   AndroidBridge::Bridge()->GetIconForExtension(aFileExt, aIconSize,
 | |
|                                                bits->Elements());
 | |
| #endif
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvFirstIdle() {
 | |
|   // When the ContentChild goes idle, it sends us a FirstIdle message
 | |
|   // which we use as a good time to signal the PreallocatedProcessManager
 | |
|   // that it can start allocating processes from now on.
 | |
|   if (mIsAPreallocBlocker) {
 | |
|     MOZ_LOG(
 | |
|         ContentParent::GetLog(), LogLevel::Verbose,
 | |
|         ("RecvFirstIdle %p: Removing Blocker for %s", this, mRemoteType.get()));
 | |
|     PreallocatedProcessManager::RemoveBlocker(mRemoteType, this);
 | |
|     mIsAPreallocBlocker = false;
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| already_AddRefed<nsDocShellLoadState> ContentParent::TakePendingLoadStateForId(
 | |
|     uint64_t aLoadIdentifier) {
 | |
|   return mPendingLoadStates.Extract(aLoadIdentifier).valueOr(nullptr).forget();
 | |
| }
 | |
| 
 | |
| void ContentParent::StorePendingLoadState(nsDocShellLoadState* aLoadState) {
 | |
|   MOZ_DIAGNOSTIC_ASSERT(
 | |
|       !mPendingLoadStates.Contains(aLoadState->GetLoadIdentifier()),
 | |
|       "The same nsDocShellLoadState was sent to the same content process "
 | |
|       "twice? This will mess with cross-process tracking of loads");
 | |
|   mPendingLoadStates.InsertOrUpdate(aLoadState->GetLoadIdentifier(),
 | |
|                                     aLoadState);
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvCleanupPendingLoadState(
 | |
|     uint64_t aLoadIdentifier) {
 | |
|   mPendingLoadStates.Remove(aLoadIdentifier);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| // We want ContentParent to show up in CC logs for debugging purposes, but we
 | |
| // don't actually cycle collect it.
 | |
| NS_IMPL_CYCLE_COLLECTION_0(ContentParent)
 | |
| 
 | |
| NS_IMPL_CYCLE_COLLECTING_ADDREF(ContentParent)
 | |
| NS_IMPL_CYCLE_COLLECTING_RELEASE(ContentParent)
 | |
| 
 | |
| NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ContentParent)
 | |
|   NS_INTERFACE_MAP_ENTRY_CONCRETE(ContentParent)
 | |
|   NS_INTERFACE_MAP_ENTRY(nsIDOMProcessParent)
 | |
|   NS_INTERFACE_MAP_ENTRY(nsIObserver)
 | |
|   NS_INTERFACE_MAP_ENTRY(nsIDOMGeoPositionCallback)
 | |
|   NS_INTERFACE_MAP_ENTRY(nsIDOMGeoPositionErrorCallback)
 | |
|   NS_INTERFACE_MAP_ENTRY(nsIAsyncShutdownBlocker)
 | |
|   NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
 | |
|   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMProcessParent)
 | |
| NS_INTERFACE_MAP_END
 | |
| 
 | |
| class RequestContentJSInterruptRunnable final : public Runnable {
 | |
|  public:
 | |
|   explicit RequestContentJSInterruptRunnable(PProcessHangMonitorParent* aActor)
 | |
|       : Runnable("dom::RequestContentJSInterruptRunnable"),
 | |
|         mHangMonitorActor(aActor) {}
 | |
| 
 | |
|   NS_IMETHOD Run() override {
 | |
|     MOZ_ASSERT(mHangMonitorActor);
 | |
|     Unused << mHangMonitorActor->SendRequestContentJSInterrupt();
 | |
| 
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   // The end-of-life of ContentParent::mHangMonitorActor is bound to
 | |
|   // ContentParent::ActorDestroy and then HangMonitorParent::Shutdown
 | |
|   // dispatches a shutdown runnable to this queue and waits for it to be
 | |
|   // executed. So the runnable needs not to care about keeping it alive,
 | |
|   // as it is surely dispatched earlier than the
 | |
|   // HangMonitorParent::ShutdownOnThread.
 | |
|   PProcessHangMonitorParent* mHangMonitorActor;
 | |
| };
 | |
| 
 | |
| void ContentParent::SignalImpendingShutdownToContentJS() {
 | |
|   if (!mIsSignaledImpendingShutdown &&
 | |
|       !AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdown)) {
 | |
|     MaybeLogBlockShutdownDiagnostics(
 | |
|         this, "BlockShutdown: NotifyImpendingShutdown.", __FILE__, __LINE__);
 | |
|     NotifyImpendingShutdown();
 | |
|     mIsSignaledImpendingShutdown = true;
 | |
|     if (mHangMonitorActor &&
 | |
|         StaticPrefs::dom_abort_script_on_child_shutdown()) {
 | |
|       MaybeLogBlockShutdownDiagnostics(
 | |
|           this, "BlockShutdown: RequestContentJSInterrupt.", __FILE__,
 | |
|           __LINE__);
 | |
|       RefPtr<RequestContentJSInterruptRunnable> r =
 | |
|           new RequestContentJSInterruptRunnable(mHangMonitorActor);
 | |
|       ProcessHangMonitor::Get()->Dispatch(r.forget());
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| // Async shutdown blocker
 | |
| NS_IMETHODIMP
 | |
| ContentParent::BlockShutdown(nsIAsyncShutdownClient* aClient) {
 | |
|   if (!AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdown)) {
 | |
| #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
 | |
|     mBlockShutdownCalled = true;
 | |
| #endif
 | |
|     // Our real shutdown has not yet started. Just notify the impending
 | |
|     // shutdown and eventually cancel content JS.
 | |
|     SignalImpendingShutdownToContentJS();
 | |
|     // This will make our process unusable for normal content, so we need to
 | |
|     // ensure we won't get re-used by GetUsedBrowserProcess as we have not yet
 | |
|     // done MarkAsDead.
 | |
|     PreallocatedProcessManager::Erase(this);
 | |
|     StopRecyclingE10SOnly(false);
 | |
| 
 | |
|     if (sQuitApplicationGrantedClient) {
 | |
|       Unused << sQuitApplicationGrantedClient->RemoveBlocker(this);
 | |
|     }
 | |
| #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
 | |
|     mBlockShutdownCalled = false;
 | |
| #endif
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
| #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
 | |
|   // We register two final shutdown blockers and both would call us, but if
 | |
|   // things go well we will unregister both as (delayed) reaction to the first
 | |
|   // call we get and thus never receive a second call. Thus we believe that we
 | |
|   // will get called only once except for quit-application-granted, which is
 | |
|   // handled above.
 | |
|   MOZ_ASSERT(!mBlockShutdownCalled);
 | |
|   mBlockShutdownCalled = true;
 | |
| #endif
 | |
| 
 | |
|   if (CanSend()) {
 | |
|     MaybeLogBlockShutdownDiagnostics(this, "BlockShutdown: CanSend.", __FILE__,
 | |
|                                      __LINE__);
 | |
| 
 | |
|     // Make sure that our process will get scheduled.
 | |
|     ProcessPriorityManager::SetProcessPriority(this,
 | |
|                                                PROCESS_PRIORITY_FOREGROUND);
 | |
|     // The normal shutdown sequence is to send a shutdown message
 | |
|     // to the child and then just wait for ActorDestroy which will
 | |
|     // cleanup everything and remove our blockers.
 | |
|     if (!ShutDownProcess(SEND_SHUTDOWN_MESSAGE)) {
 | |
|       KillHard("Failed to send Shutdown message. Destroying the process...");
 | |
|       return NS_OK;
 | |
|     }
 | |
|   } else if (IsLaunching()) {
 | |
|     MaybeLogBlockShutdownDiagnostics(
 | |
|         this, "BlockShutdown: !CanSend && IsLaunching.", __FILE__, __LINE__);
 | |
| 
 | |
|     // If we get here while we are launching, we must wait for the child to
 | |
|     // be able to react on our commands. Mark this process as dead. This
 | |
|     // will make bail out LaunchSubprocessResolve and kick off the normal
 | |
|     // shutdown sequence.
 | |
|     MarkAsDead();
 | |
|   } else {
 | |
|     MOZ_ASSERT(IsDead());
 | |
|     if (!IsDead()) {
 | |
|       MaybeLogBlockShutdownDiagnostics(
 | |
|           this, "BlockShutdown: !!! !CanSend && !IsLaunching && !IsDead !!!",
 | |
|           __FILE__, __LINE__);
 | |
|     } else {
 | |
|       MaybeLogBlockShutdownDiagnostics(
 | |
|           this, "BlockShutdown: !CanSend && !IsLaunching && IsDead.", __FILE__,
 | |
|           __LINE__);
 | |
|     }
 | |
|     // Nothing left we can do. We must assume that we race with an ongoing
 | |
|     // process shutdown, such that we can expect our shutdown blockers to be
 | |
|     // removed normally.
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| ContentParent::GetName(nsAString& aName) {
 | |
|   aName.AssignLiteral("ContentParent:");
 | |
|   aName.AppendPrintf(" id=%p", this);
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| ContentParent::GetState(nsIPropertyBag** aResult) {
 | |
|   auto props = MakeRefPtr<nsHashPropertyBag>();
 | |
|   props->SetPropertyAsACString(u"remoteTypePrefix"_ns,
 | |
|                                RemoteTypePrefix(mRemoteType));
 | |
|   *aResult = props.forget().downcast<nsIWritablePropertyBag>().take();
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| static void InitShutdownClients() {
 | |
|   if (!sXPCOMShutdownClient) {
 | |
|     nsresult rv;
 | |
|     nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdownService();
 | |
|     if (!svc) {
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     nsCOMPtr<nsIAsyncShutdownClient> client;
 | |
|     // TODO: It seems as if getPhase from AsyncShutdown.sys.mjs does not check
 | |
|     // if we are beyond our phase already. See bug 1762840.
 | |
|     if (!AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMWillShutdown)) {
 | |
|       rv = svc->GetXpcomWillShutdown(getter_AddRefs(client));
 | |
|       if (NS_SUCCEEDED(rv)) {
 | |
|         sXPCOMShutdownClient = client.forget();
 | |
|         ClearOnShutdown(&sXPCOMShutdownClient);
 | |
|       }
 | |
|     }
 | |
|     if (!AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdown)) {
 | |
|       rv = svc->GetProfileBeforeChange(getter_AddRefs(client));
 | |
|       if (NS_SUCCEEDED(rv)) {
 | |
|         sProfileBeforeChangeClient = client.forget();
 | |
|         ClearOnShutdown(&sProfileBeforeChangeClient);
 | |
|       }
 | |
|     }
 | |
|     // TODO: ShutdownPhase::AppShutdownConfirmed is not mapping to
 | |
|     // QuitApplicationGranted, see bug 1762840 comment 4.
 | |
|     if (!AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
 | |
|       rv = svc->GetQuitApplicationGranted(getter_AddRefs(client));
 | |
|       if (NS_SUCCEEDED(rv)) {
 | |
|         sQuitApplicationGrantedClient = client.forget();
 | |
|         ClearOnShutdown(&sQuitApplicationGrantedClient);
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void ContentParent::AddShutdownBlockers() {
 | |
|   InitShutdownClients();
 | |
|   MOZ_ASSERT(sXPCOMShutdownClient);
 | |
|   MOZ_ASSERT(sProfileBeforeChangeClient);
 | |
| 
 | |
|   if (sXPCOMShutdownClient) {
 | |
|     sXPCOMShutdownClient->AddBlocker(
 | |
|         this, NS_LITERAL_STRING_FROM_CSTRING(__FILE__), __LINE__, u""_ns);
 | |
|   }
 | |
|   if (sProfileBeforeChangeClient) {
 | |
|     sProfileBeforeChangeClient->AddBlocker(
 | |
|         this, NS_LITERAL_STRING_FROM_CSTRING(__FILE__), __LINE__, u""_ns);
 | |
|   }
 | |
|   if (sQuitApplicationGrantedClient) {
 | |
|     sQuitApplicationGrantedClient->AddBlocker(
 | |
|         this, NS_LITERAL_STRING_FROM_CSTRING(__FILE__), __LINE__, u""_ns);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void ContentParent::RemoveShutdownBlockers() {
 | |
|   MOZ_ASSERT(sXPCOMShutdownClient);
 | |
|   MOZ_ASSERT(sProfileBeforeChangeClient);
 | |
| 
 | |
|   MaybeLogBlockShutdownDiagnostics(this, "RemoveShutdownBlockers", __FILE__,
 | |
|                                    __LINE__);
 | |
| #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
 | |
|   mBlockShutdownCalled = false;
 | |
| #endif
 | |
| 
 | |
|   if (sXPCOMShutdownClient) {
 | |
|     Unused << sXPCOMShutdownClient->RemoveBlocker(this);
 | |
|   }
 | |
|   if (sProfileBeforeChangeClient) {
 | |
|     Unused << sProfileBeforeChangeClient->RemoveBlocker(this);
 | |
|   }
 | |
|   if (sQuitApplicationGrantedClient) {
 | |
|     Unused << sQuitApplicationGrantedClient->RemoveBlocker(this);
 | |
|   }
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| ContentParent::Observe(nsISupports* aSubject, const char* aTopic,
 | |
|                        const char16_t* aData) {
 | |
|   if (IsDead() || !mSubprocess) {
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   if (!strcmp(aTopic, "nsPref:changed")) {
 | |
|     // We know prefs are ASCII here.
 | |
|     NS_LossyConvertUTF16toASCII strData(aData);
 | |
| 
 | |
|     Pref pref(strData, /* isLocked */ false,
 | |
|               /* isSanitized */ false, Nothing(), Nothing());
 | |
| 
 | |
|     Preferences::GetPreference(&pref, GeckoProcessType_Content,
 | |
|                                GetRemoteType());
 | |
| 
 | |
|     // This check is a bit of a hack.  We want to avoid sending excessive
 | |
|     // preference updates to subprocesses for performance reasons, but we
 | |
|     // currently don't have a great mechanism for doing so. (See Bug 1819714)
 | |
|     // We're going to hijack the sanitization mechanism to accomplish our goal
 | |
|     // but it imposes the following complications:
 | |
|     // 1) It doesn't avoid sending anything to other (non-web-content)
 | |
|     //    subprocesses so we're not getting any perf gains there
 | |
|     // 2) It confuses the subprocesses w.r.t. sanitization.  The point of
 | |
|     //    sending a preference update of a sanitized preference is so that
 | |
|     //    content process knows when it's asked to resolve a sanitized
 | |
|     //    preference, and it can send telemetry and/or crash.  With this
 | |
|     //    change, a sanitized pref that is created during the browser session
 | |
|     //    will not be sent to the content process, and therefore the content
 | |
|     //    process won't know it should telemetry/crash on access - it'll just
 | |
|     //    silently fail to resolve it.  After browser restart, the sanitized
 | |
|     //    pref will be populated into the content process via the shared pref
 | |
|     //    map and _then_ if it is accessed, the content process will crash.
 | |
|     //    We're seeing good telemetry/crash rates right now, so we're okay with
 | |
|     //    this limitation.
 | |
|     if (pref.isSanitized()) {
 | |
|       return NS_OK;
 | |
|     }
 | |
| 
 | |
|     if (IsInitialized()) {
 | |
|       MOZ_ASSERT(mQueuedPrefs.IsEmpty());
 | |
|       if (!SendPreferenceUpdate(pref)) {
 | |
|         return NS_ERROR_NOT_AVAILABLE;
 | |
|       }
 | |
|     } else {
 | |
|       MOZ_ASSERT(!IsDead());
 | |
|       mQueuedPrefs.AppendElement(pref);
 | |
|     }
 | |
| 
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   if (!IsAlive()) {
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   // listening for memory pressure event
 | |
|   if (!strcmp(aTopic, "memory-pressure")) {
 | |
|     Unused << SendFlushMemory(nsDependentString(aData));
 | |
|   } else if (!strcmp(aTopic, "application-background")) {
 | |
|     Unused << SendApplicationBackground();
 | |
|   } else if (!strcmp(aTopic, "application-foreground")) {
 | |
|     Unused << SendApplicationForeground();
 | |
|   } else if (!strcmp(aTopic, NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC)) {
 | |
|     NS_ConvertUTF16toUTF8 dataStr(aData);
 | |
|     const char* offline = dataStr.get();
 | |
|     if (!SendSetOffline(!strcmp(offline, "true"))) {
 | |
|       return NS_ERROR_NOT_AVAILABLE;
 | |
|     }
 | |
|   } else if (!strcmp(aTopic, NS_IPC_IOSERVICE_SET_CONNECTIVITY_TOPIC)) {
 | |
|     if (!SendSetConnectivity(u"true"_ns.Equals(aData))) {
 | |
|       return NS_ERROR_NOT_AVAILABLE;
 | |
|     }
 | |
|   } else if (!strcmp(aTopic, NS_IPC_CAPTIVE_PORTAL_SET_STATE)) {
 | |
|     nsCOMPtr<nsICaptivePortalService> cps = do_QueryInterface(aSubject);
 | |
|     MOZ_ASSERT(cps, "Should QI to a captive portal service");
 | |
|     if (!cps) {
 | |
|       return NS_ERROR_FAILURE;
 | |
|     }
 | |
|     int32_t state;
 | |
|     cps->GetState(&state);
 | |
|     if (!SendSetCaptivePortalState(state)) {
 | |
|       return NS_ERROR_NOT_AVAILABLE;
 | |
|     }
 | |
|   }
 | |
|   // listening for alert notifications
 | |
|   else if (!strcmp(aTopic, "alertfinished") ||
 | |
|            !strcmp(aTopic, "alertclickcallback") ||
 | |
|            !strcmp(aTopic, "alertshow") ||
 | |
|            !strcmp(aTopic, "alertdisablecallback") ||
 | |
|            !strcmp(aTopic, "alertsettingscallback")) {
 | |
|     if (!SendNotifyAlertsObserver(nsDependentCString(aTopic),
 | |
|                                   nsDependentString(aData)))
 | |
|       return NS_ERROR_NOT_AVAILABLE;
 | |
|   } else if (!strcmp(aTopic, "child-gc-request")) {
 | |
|     Unused << SendGarbageCollect();
 | |
|   } else if (!strcmp(aTopic, "child-cc-request")) {
 | |
|     Unused << SendCycleCollect();
 | |
|   } else if (!strcmp(aTopic, "child-mmu-request")) {
 | |
|     Unused << SendMinimizeMemoryUsage();
 | |
|   } else if (!strcmp(aTopic, "child-ghost-request")) {
 | |
|     Unused << SendUnlinkGhosts();
 | |
|   } else if (!strcmp(aTopic, "last-pb-context-exited")) {
 | |
|     Unused << SendLastPrivateDocShellDestroyed();
 | |
|   }
 | |
| #ifdef ACCESSIBILITY
 | |
|   else if (aData && !strcmp(aTopic, "a11y-init-or-shutdown")) {
 | |
|     if (*aData == '1') {
 | |
|       // Make sure accessibility is running in content process when
 | |
|       // accessibility gets initiated in chrome process.
 | |
| #  if defined(XP_WIN)
 | |
|       // Don't init content a11y if we detect an incompat version of JAWS in
 | |
|       // use.
 | |
|       if (!mozilla::a11y::Compatibility::IsOldJAWS()) {
 | |
|         Unused << SendActivateA11y(
 | |
|             ::GetCurrentThreadId(),
 | |
|             a11y::MsaaAccessible::GetContentProcessIdFor(ChildID()));
 | |
|       }
 | |
| #  else
 | |
|       Unused << SendActivateA11y(0, 0);
 | |
| #  endif
 | |
|     } else {
 | |
|       // If possible, shut down accessibility in content process when
 | |
|       // accessibility gets shutdown in chrome process.
 | |
|       Unused << SendShutdownA11y();
 | |
|     }
 | |
|   }
 | |
| #endif
 | |
|   else if (!strcmp(aTopic, "cacheservice:empty-cache")) {
 | |
|     Unused << SendNotifyEmptyHTTPCache();
 | |
|   } else if (!strcmp(aTopic, "intl:app-locales-changed")) {
 | |
|     nsTArray<nsCString> appLocales;
 | |
|     LocaleService::GetInstance()->GetAppLocalesAsBCP47(appLocales);
 | |
|     Unused << SendUpdateAppLocales(appLocales);
 | |
|   } else if (!strcmp(aTopic, "intl:requested-locales-changed")) {
 | |
|     nsTArray<nsCString> requestedLocales;
 | |
|     LocaleService::GetInstance()->GetRequestedLocales(requestedLocales);
 | |
|     Unused << SendUpdateRequestedLocales(requestedLocales);
 | |
|   } else if (!strcmp(aTopic, "cookie-changed") ||
 | |
|              !strcmp(aTopic, "private-cookie-changed")) {
 | |
|     if (!aData) {
 | |
|       return NS_ERROR_UNEXPECTED;
 | |
|     }
 | |
|     PNeckoParent* neckoParent = LoneManagedOrNullAsserts(ManagedPNeckoParent());
 | |
|     if (!neckoParent) {
 | |
|       return NS_OK;
 | |
|     }
 | |
|     PCookieServiceParent* csParent =
 | |
|         LoneManagedOrNullAsserts(neckoParent->ManagedPCookieServiceParent());
 | |
|     if (!csParent) {
 | |
|       return NS_OK;
 | |
|     }
 | |
|     auto* cs = static_cast<CookieServiceParent*>(csParent);
 | |
|     // Do not push these cookie updates to the same process they originated
 | |
|     // from.
 | |
|     if (cs->ProcessingCookie()) {
 | |
|       return NS_OK;
 | |
|     }
 | |
|     if (!nsCRT::strcmp(aData, u"batch-deleted")) {
 | |
|       nsCOMPtr<nsIArray> cookieList = do_QueryInterface(aSubject);
 | |
|       NS_ASSERTION(cookieList, "couldn't get cookie list");
 | |
|       cs->RemoveBatchDeletedCookies(cookieList);
 | |
|       return NS_OK;
 | |
|     }
 | |
| 
 | |
|     if (!nsCRT::strcmp(aData, u"cleared")) {
 | |
|       cs->RemoveAll();
 | |
|       return NS_OK;
 | |
|     }
 | |
| 
 | |
|     nsCOMPtr<nsICookie> xpcCookie = do_QueryInterface(aSubject);
 | |
|     NS_ASSERTION(xpcCookie, "couldn't get cookie");
 | |
| 
 | |
|     // only broadcast the cookie change to content processes that need it
 | |
|     const Cookie& cookie = xpcCookie->AsCookie();
 | |
| 
 | |
|     // do not send cookie if content process does not have similar cookie
 | |
|     if (!cs->ContentProcessHasCookie(cookie)) {
 | |
|       return NS_OK;
 | |
|     }
 | |
| 
 | |
|     if (!nsCRT::strcmp(aData, u"deleted")) {
 | |
|       cs->RemoveCookie(cookie);
 | |
|     } else if ((!nsCRT::strcmp(aData, u"added")) ||
 | |
|                (!nsCRT::strcmp(aData, u"changed"))) {
 | |
|       cs->AddCookie(cookie);
 | |
|     }
 | |
|   } else if (!strcmp(aTopic, NS_NETWORK_LINK_TYPE_TOPIC)) {
 | |
|     UpdateNetworkLinkType();
 | |
|   } else if (!strcmp(aTopic, "network:socket-process-crashed")) {
 | |
|     Unused << SendSocketProcessCrashed();
 | |
|   } else if (!strcmp(aTopic, DEFAULT_TIMEZONE_CHANGED_OBSERVER_TOPIC)) {
 | |
|     Unused << SendSystemTimezoneChanged();
 | |
|   } else if (!strcmp(aTopic, NS_NETWORK_TRR_MODE_CHANGED_TOPIC)) {
 | |
|     nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
 | |
|     nsIDNSService::ResolverMode mode;
 | |
|     dns->GetCurrentTrrMode(&mode);
 | |
|     Unused << SendSetTRRMode(mode);
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| void ContentParent::UpdateNetworkLinkType() {
 | |
|   nsresult rv;
 | |
|   nsCOMPtr<nsINetworkLinkService> nls =
 | |
|       do_GetService(NS_NETWORK_LINK_SERVICE_CONTRACTID, &rv);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   uint32_t linkType = nsINetworkLinkService::LINK_TYPE_UNKNOWN;
 | |
|   rv = nls->GetLinkType(&linkType);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   Unused << SendNetworkLinkTypeChange(linkType);
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| ContentParent::GetInterface(const nsIID& aIID, void** aResult) {
 | |
|   NS_ENSURE_ARG_POINTER(aResult);
 | |
| 
 | |
|   if (aIID.Equals(NS_GET_IID(nsIMessageSender))) {
 | |
|     nsCOMPtr<nsIMessageSender> mm = GetMessageManager();
 | |
|     mm.forget(aResult);
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   return NS_NOINTERFACE;
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvInitBackground(
 | |
|     Endpoint<PBackgroundStarterParent>&& aEndpoint) {
 | |
|   if (!BackgroundParent::AllocStarter(this, std::move(aEndpoint))) {
 | |
|     NS_WARNING("BackgroundParent::Alloc failed");
 | |
|   }
 | |
| 
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| bool ContentParent::CanOpenBrowser(const IPCTabContext& aContext) {
 | |
|   // (PopupIPCTabContext lets the child process prove that it has access to
 | |
|   // the app it's trying to open.)
 | |
|   // On e10s we also allow UnsafeTabContext to allow service workers to open
 | |
|   // windows. This is enforced in MaybeInvalidTabContext.
 | |
|   if (aContext.type() != IPCTabContext::TPopupIPCTabContext) {
 | |
|     MOZ_CRASH_UNLESS_FUZZING(
 | |
|         "Unexpected IPCTabContext type.  Aborting AllocPBrowserParent.");
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   if (aContext.type() == IPCTabContext::TPopupIPCTabContext) {
 | |
|     const PopupIPCTabContext& popupContext = aContext.get_PopupIPCTabContext();
 | |
| 
 | |
|     auto opener = BrowserParent::GetFrom(popupContext.opener().AsParent());
 | |
|     if (!opener) {
 | |
|       MOZ_CRASH_UNLESS_FUZZING(
 | |
|           "Got null opener from child; aborting AllocPBrowserParent.");
 | |
|       return false;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   MaybeInvalidTabContext tc(aContext);
 | |
|   if (!tc.IsValid()) {
 | |
|     NS_ERROR(nsPrintfCString("Child passed us an invalid TabContext.  (%s)  "
 | |
|                              "Aborting AllocPBrowserParent.",
 | |
|                              tc.GetInvalidReason())
 | |
|                  .get());
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| static bool CloneIsLegal(ContentParent* aCp, CanonicalBrowsingContext& aSource,
 | |
|                          CanonicalBrowsingContext& aTarget) {
 | |
|   // Source and target must be in the same BCG
 | |
|   if (NS_WARN_IF(aSource.Group() != aTarget.Group())) {
 | |
|     return false;
 | |
|   }
 | |
|   // The source and target must be in different toplevel <browser>s
 | |
|   if (NS_WARN_IF(aSource.Top() == aTarget.Top())) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   // Neither source nor target must be toplevel.
 | |
|   if (NS_WARN_IF(aSource.IsTop()) || NS_WARN_IF(aTarget.IsTop())) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   // Both should be embedded by the same process.
 | |
|   auto* sourceEmbedder = aSource.GetParentWindowContext();
 | |
|   if (NS_WARN_IF(!sourceEmbedder) ||
 | |
|       NS_WARN_IF(sourceEmbedder->GetContentParent() != aCp)) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   auto* targetEmbedder = aSource.GetParentWindowContext();
 | |
|   if (NS_WARN_IF(!targetEmbedder) ||
 | |
|       NS_WARN_IF(targetEmbedder->GetContentParent() != aCp)) {
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   // All seems sane.
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvCloneDocumentTreeInto(
 | |
|     const MaybeDiscarded<BrowsingContext>& aSource,
 | |
|     const MaybeDiscarded<BrowsingContext>& aTarget, PrintData&& aPrintData) {
 | |
|   if (aSource.IsNullOrDiscarded() || aTarget.IsNullOrDiscarded()) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
 | |
|     // All existing processes have potentially been slated for removal already,
 | |
|     // such that any subsequent call to GetNewOrUsedLaunchingBrowserProcess
 | |
|     // (normally supposed to find an existing process here) will try to create
 | |
|     // a new process (but fail) that nobody would ever really use. Let's avoid
 | |
|     // this together with the expensive CloneDocumentTreeInto operation.
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   auto* source = aSource.get_canonical();
 | |
|   auto* target = aTarget.get_canonical();
 | |
| 
 | |
|   if (!CloneIsLegal(this, *source, *target)) {
 | |
|     return IPC_FAIL(this, "Illegal subframe clone");
 | |
|   }
 | |
| 
 | |
|   ContentParent* cp = source->GetContentParent();
 | |
|   if (NS_WARN_IF(!cp)) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   if (NS_WARN_IF(cp->GetRemoteType() == GetRemoteType())) {
 | |
|     // Wanted to switch to a target browsing context that's already local again.
 | |
|     // See bug 1676996 for how this can happen.
 | |
|     //
 | |
|     // Dropping the switch on the floor seems fine for this case, though we
 | |
|     // could also try to clone the local document.
 | |
|     //
 | |
|     // If the remote type matches & it's in the same group (which was confirmed
 | |
|     // by CloneIsLegal), it must be the exact same process.
 | |
|     MOZ_DIAGNOSTIC_ASSERT(cp == this);
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   target->CloneDocumentTreeInto(source, cp->GetRemoteType(),
 | |
|                                 std::move(aPrintData));
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvUpdateRemotePrintSettings(
 | |
|     const MaybeDiscarded<BrowsingContext>& aTarget, PrintData&& aPrintData) {
 | |
|   if (aTarget.IsNullOrDiscarded()) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   auto* target = aTarget.get_canonical();
 | |
|   auto* bp = target->GetBrowserParent();
 | |
|   if (NS_WARN_IF(!bp)) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   Unused << bp->SendUpdateRemotePrintSettings(std::move(aPrintData));
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvConstructPopupBrowser(
 | |
|     ManagedEndpoint<PBrowserParent>&& aBrowserEp,
 | |
|     ManagedEndpoint<PWindowGlobalParent>&& aWindowEp, const TabId& aTabId,
 | |
|     const IPCTabContext& aContext, const WindowGlobalInit& aInitialWindowInit,
 | |
|     const uint32_t& aChromeFlags) {
 | |
|   MOZ_ASSERT(XRE_IsParentProcess());
 | |
| 
 | |
|   if (!CanOpenBrowser(aContext)) {
 | |
|     return IPC_FAIL(this, "CanOpenBrowser Failed");
 | |
|   }
 | |
| 
 | |
|   RefPtr<CanonicalBrowsingContext> browsingContext =
 | |
|       CanonicalBrowsingContext::Get(
 | |
|           aInitialWindowInit.context().mBrowsingContextId);
 | |
|   if (!browsingContext || browsingContext->IsDiscarded()) {
 | |
|     return IPC_FAIL(this, "Null or discarded initial BrowsingContext");
 | |
|   }
 | |
|   if (!aInitialWindowInit.principal()) {
 | |
|     return IPC_FAIL(this, "Cannot create without valid initial principal");
 | |
|   }
 | |
| 
 | |
|   if (!ValidatePrincipal(aInitialWindowInit.principal())) {
 | |
|     LogAndAssertFailedPrincipalValidationInfo(aInitialWindowInit.principal(),
 | |
|                                               __func__);
 | |
|   }
 | |
| 
 | |
|   if (browsingContext->GetBrowserParent()) {
 | |
|     return IPC_FAIL(this, "BrowsingContext already has a BrowserParent");
 | |
|   }
 | |
| 
 | |
|   uint32_t chromeFlags = aChromeFlags;
 | |
|   TabId openerTabId(0);
 | |
|   ContentParentId openerCpId(0);
 | |
|   if (aContext.type() == IPCTabContext::TPopupIPCTabContext) {
 | |
|     // CanOpenBrowser has ensured that the IPCTabContext is of
 | |
|     // type PopupIPCTabContext, and that the opener BrowserParent is
 | |
|     // reachable.
 | |
|     const PopupIPCTabContext& popupContext = aContext.get_PopupIPCTabContext();
 | |
|     auto opener = BrowserParent::GetFrom(popupContext.opener().AsParent());
 | |
|     openerTabId = opener->GetTabId();
 | |
|     openerCpId = opener->Manager()->ChildID();
 | |
| 
 | |
|     // We must ensure that the private browsing and remoteness flags
 | |
|     // match those of the opener.
 | |
|     nsCOMPtr<nsILoadContext> loadContext = opener->GetLoadContext();
 | |
|     if (!loadContext) {
 | |
|       return IPC_FAIL(this, "Missing Opener LoadContext");
 | |
|     }
 | |
|     if (loadContext->UsePrivateBrowsing()) {
 | |
|       chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW;
 | |
|     }
 | |
|     if (loadContext->UseRemoteSubframes()) {
 | |
|       chromeFlags |= nsIWebBrowserChrome::CHROME_FISSION_WINDOW;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   // And because we're allocating a remote browser, of course the
 | |
|   // window is remote.
 | |
|   chromeFlags |= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW;
 | |
| 
 | |
|   if (NS_WARN_IF(!browsingContext->IsOwnedByProcess(ChildID()))) {
 | |
|     return IPC_FAIL(this, "BrowsingContext Owned by Incorrect Process!");
 | |
|   }
 | |
| 
 | |
|   MaybeInvalidTabContext tc(aContext);
 | |
|   MOZ_ASSERT(tc.IsValid());
 | |
| 
 | |
|   RefPtr<WindowGlobalParent> initialWindow =
 | |
|       WindowGlobalParent::CreateDisconnected(aInitialWindowInit);
 | |
|   if (!initialWindow) {
 | |
|     return IPC_FAIL(this, "Failed to create WindowGlobalParent");
 | |
|   }
 | |
| 
 | |
|   auto parent = MakeRefPtr<BrowserParent>(this, aTabId, tc.GetTabContext(),
 | |
|                                           browsingContext, chromeFlags);
 | |
| 
 | |
|   // Bind the created BrowserParent to IPC to actually link the actor.
 | |
|   if (NS_WARN_IF(!BindPBrowserEndpoint(std::move(aBrowserEp), parent))) {
 | |
|     return IPC_FAIL(this, "BindPBrowserEndpoint failed");
 | |
|   }
 | |
| 
 | |
|   // XXX: Why are we checking these requirements? It seems we should register
 | |
|   // the created frame unconditionally?
 | |
|   if (openerTabId > 0) {
 | |
|     // The creation of PBrowser was triggered from content process through
 | |
|     // window.open().
 | |
|     // We need to register remote frame with the child generated tab id.
 | |
|     auto* cpm = ContentProcessManager::GetSingleton();
 | |
|     if (!cpm || !cpm->RegisterRemoteFrame(parent)) {
 | |
|       return IPC_FAIL(this, "RegisterRemoteFrame Failed");
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (NS_WARN_IF(!parent->BindPWindowGlobalEndpoint(std::move(aWindowEp),
 | |
|                                                     initialWindow))) {
 | |
|     return IPC_FAIL(this, "BindPWindowGlobalEndpoint failed");
 | |
|   }
 | |
| 
 | |
|   browsingContext->SetCurrentBrowserParent(parent);
 | |
| 
 | |
|   initialWindow->Init();
 | |
| 
 | |
|   // When enabling input event prioritization, input events may preempt other
 | |
|   // normal priority IPC messages. To prevent the input events preempt
 | |
|   // PBrowserConstructor, we use an IPC 'RemoteIsReadyToHandleInputEvents' to
 | |
|   // notify parent that BrowserChild is created. In this case, PBrowser is
 | |
|   // initiated from content so that we can set BrowserParent as ready to handle
 | |
|   // input
 | |
|   parent->SetReadyToHandleInputEvents();
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::PRemoteSpellcheckEngineParent*
 | |
| ContentParent::AllocPRemoteSpellcheckEngineParent() {
 | |
|   mozilla::RemoteSpellcheckEngineParent* parent =
 | |
|       new mozilla::RemoteSpellcheckEngineParent();
 | |
|   return parent;
 | |
| }
 | |
| 
 | |
| bool ContentParent::DeallocPRemoteSpellcheckEngineParent(
 | |
|     PRemoteSpellcheckEngineParent* parent) {
 | |
|   delete parent;
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| /* static */
 | |
| void ContentParent::ForceKillTimerCallback(nsITimer* aTimer, void* aClosure) {
 | |
|   // We don't want to time out the content process during XPCShell tests. This
 | |
|   // is the easiest way to ensure that.
 | |
|   if (PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR")) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   auto self = static_cast<ContentParent*>(aClosure);
 | |
|   self->KillHard("ShutDownKill");
 | |
| }
 | |
| 
 | |
| void ContentParent::GeneratePairedMinidump(const char* aReason) {
 | |
|   // We're about to kill the child process associated with this content.
 | |
|   // Something has gone wrong to get us here, so we generate a minidump
 | |
|   // of the parent and child for submission to the crash server unless we're
 | |
|   // already shutting down.
 | |
|   if (mCrashReporter &&
 | |
|       !AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed) &&
 | |
|       StaticPrefs::dom_ipc_tabs_createKillHardCrashReports_AtStartup()) {
 | |
|     // GeneratePairedMinidump creates two minidumps for us - the main
 | |
|     // one is for the content process we're about to kill, and the other
 | |
|     // one is for the main browser process. That second one is the extra
 | |
|     // minidump tagging along, so we have to tell the crash reporter that
 | |
|     // it exists and is being appended.
 | |
|     nsAutoCString additionalDumps("browser");
 | |
|     mCrashReporter->AddAnnotation(
 | |
|         CrashReporter::Annotation::additional_minidumps, additionalDumps);
 | |
|     nsDependentCString reason(aReason);
 | |
|     mCrashReporter->AddAnnotation(CrashReporter::Annotation::ipc_channel_error,
 | |
|                                   reason);
 | |
| 
 | |
|     // Generate the report and insert into the queue for submittal.
 | |
|     if (mCrashReporter->GenerateMinidumpAndPair(this, "browser"_ns)) {
 | |
|       mCrashReporter->FinalizeCrashReport();
 | |
|       mCreatedPairedMinidumps = true;
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void ContentParent::HandleOrphanedMinidump(nsString* aDumpId) {
 | |
|   if (CrashReporter::FinalizeOrphanedMinidump(
 | |
|           OtherPid(), GeckoProcessType_Content, aDumpId)) {
 | |
|     CrashReporterHost::RecordCrash(GeckoProcessType_Content,
 | |
|                                    nsICrashService::CRASH_TYPE_CRASH, *aDumpId);
 | |
|   } else {
 | |
|     NS_WARNING(nsPrintfCString("content process pid = %" PRIPID
 | |
|                                " crashed without leaving a minidump behind",
 | |
|                                OtherPid())
 | |
|                    .get());
 | |
|   }
 | |
| }
 | |
| 
 | |
| // WARNING: aReason appears in telemetry, so any new value passed in requires
 | |
| // data review.
 | |
| void ContentParent::KillHard(const char* aReason) {
 | |
|   AUTO_PROFILER_LABEL("ContentParent::KillHard", OTHER);
 | |
| 
 | |
|   // On Windows, calling KillHard multiple times causes problems - the
 | |
|   // process handle becomes invalid on the first call, causing a second call
 | |
|   // to crash our process - more details in bug 890840.
 | |
|   if (mCalledKillHard) {
 | |
|     return;
 | |
|   }
 | |
|   mCalledKillHard = true;
 | |
|   mForceKillTimer = nullptr;
 | |
| 
 | |
|   RemoveShutdownBlockers();
 | |
|   nsCString reason = nsDependentCString(aReason);
 | |
| 
 | |
|   // If we find mIsNotifiedShutdownSuccess there is no reason to blame this
 | |
|   // content process, most probably our parent process is just slow in
 | |
|   // processing its own main thread queue.
 | |
|   if (!mIsNotifiedShutdownSuccess) {
 | |
|     GeneratePairedMinidump(aReason);
 | |
|   } else {
 | |
|     reason = nsDependentCString("KillHard after IsNotifiedShutdownSuccess.");
 | |
|   }
 | |
|   Telemetry::Accumulate(Telemetry::SUBPROCESS_KILL_HARD, reason, 1);
 | |
| 
 | |
|   ProcessHandle otherProcessHandle;
 | |
|   if (!base::OpenProcessHandle(OtherPid(), &otherProcessHandle)) {
 | |
|     NS_ERROR("Failed to open child process when attempting kill.");
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (!KillProcess(otherProcessHandle, base::PROCESS_END_KILLED_BY_USER)) {
 | |
|     if (mCrashReporter) {
 | |
|       mCrashReporter->DeleteCrashReport();
 | |
|     }
 | |
|     NS_WARNING("failed to kill subprocess!");
 | |
|   }
 | |
| 
 | |
|   if (mSubprocess) {
 | |
|     MOZ_LOG(
 | |
|         ContentParent::GetLog(), LogLevel::Verbose,
 | |
|         ("KillHard Subprocess(%s): ContentParent %p mSubprocess %p handle "
 | |
|          "%" PRIuPTR,
 | |
|          aReason, this, mSubprocess,
 | |
|          mSubprocess ? (uintptr_t)mSubprocess->GetChildProcessHandle() : -1));
 | |
|     mSubprocess->SetAlreadyDead();
 | |
|   }
 | |
| 
 | |
|   // EnsureProcessTerminated has responsibilty for closing otherProcessHandle.
 | |
|   XRE_GetIOMessageLoop()->PostTask(
 | |
|       NewRunnableFunction("EnsureProcessTerminatedRunnable",
 | |
|                           &ProcessWatcher::EnsureProcessTerminated,
 | |
|                           otherProcessHandle, /*force=*/true));
 | |
| }
 | |
| 
 | |
| void ContentParent::FriendlyName(nsAString& aName, bool aAnonymize) {
 | |
|   aName.Truncate();
 | |
|   if (mIsForBrowser) {
 | |
|     aName.AssignLiteral("Browser");
 | |
|   } else if (aAnonymize) {
 | |
|     aName.AssignLiteral("<anonymized-name>");
 | |
|   } else {
 | |
|     aName.AssignLiteral("???");
 | |
|   }
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvInitCrashReporter(
 | |
|     const NativeThreadId& aThreadId) {
 | |
|   mCrashReporter =
 | |
|       MakeUnique<CrashReporterHost>(GeckoProcessType_Content, aThreadId);
 | |
| 
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| hal_sandbox::PHalParent* ContentParent::AllocPHalParent() {
 | |
|   return hal_sandbox::CreateHalParent();
 | |
| }
 | |
| 
 | |
| bool ContentParent::DeallocPHalParent(hal_sandbox::PHalParent* aHal) {
 | |
|   delete aHal;
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| devtools::PHeapSnapshotTempFileHelperParent*
 | |
| ContentParent::AllocPHeapSnapshotTempFileHelperParent() {
 | |
|   return devtools::HeapSnapshotTempFileHelperParent::Create();
 | |
| }
 | |
| 
 | |
| bool ContentParent::DeallocPHeapSnapshotTempFileHelperParent(
 | |
|     devtools::PHeapSnapshotTempFileHelperParent* aHeapSnapshotHelper) {
 | |
|   delete aHeapSnapshotHelper;
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool ContentParent::SendRequestMemoryReport(
 | |
|     const uint32_t& aGeneration, const bool& aAnonymize,
 | |
|     const bool& aMinimizeMemoryUsage, const Maybe<FileDescriptor>& aDMDFile) {
 | |
|   // This automatically cancels the previous request.
 | |
|   mMemoryReportRequest = MakeUnique<MemoryReportRequestHost>(aGeneration);
 | |
|   // If we run the callback in response to a reply, then by definition |this|
 | |
|   // is still alive, so the ref pointer is redundant, but it seems easier
 | |
|   // to hold a strong reference than to worry about that.
 | |
|   RefPtr<ContentParent> self(this);
 | |
|   PContentParent::SendRequestMemoryReport(
 | |
|       aGeneration, aAnonymize, aMinimizeMemoryUsage, aDMDFile,
 | |
|       [&, self](const uint32_t& aGeneration2) {
 | |
|         if (self->mMemoryReportRequest) {
 | |
|           self->mMemoryReportRequest->Finish(aGeneration2);
 | |
|           self->mMemoryReportRequest = nullptr;
 | |
|         }
 | |
|       },
 | |
|       [&, self](mozilla::ipc::ResponseRejectReason) {
 | |
|         self->mMemoryReportRequest = nullptr;
 | |
|       });
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvAddMemoryReport(
 | |
|     const MemoryReport& aReport) {
 | |
|   if (mMemoryReportRequest) {
 | |
|     mMemoryReportRequest->RecvReport(aReport);
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvAddPerformanceMetrics(
 | |
|     const nsID& aID, nsTArray<PerformanceInfo>&& aMetrics) {
 | |
|   nsresult rv = PerformanceMetricsCollector::DataReceived(aID, aMetrics);
 | |
|   Unused << NS_WARN_IF(NS_FAILED(rv));
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| PCycleCollectWithLogsParent* ContentParent::AllocPCycleCollectWithLogsParent(
 | |
|     const bool& aDumpAllTraces, const FileDescriptor& aGCLog,
 | |
|     const FileDescriptor& aCCLog) {
 | |
|   MOZ_CRASH("Don't call this; use ContentParent::CycleCollectWithLogs");
 | |
| }
 | |
| 
 | |
| bool ContentParent::DeallocPCycleCollectWithLogsParent(
 | |
|     PCycleCollectWithLogsParent* aActor) {
 | |
|   delete aActor;
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| bool ContentParent::CycleCollectWithLogs(
 | |
|     bool aDumpAllTraces, nsICycleCollectorLogSink* aSink,
 | |
|     nsIDumpGCAndCCLogsCallback* aCallback) {
 | |
|   return CycleCollectWithLogsParent::AllocAndSendConstructor(
 | |
|       this, aDumpAllTraces, aSink, aCallback);
 | |
| }
 | |
| 
 | |
| PTestShellParent* ContentParent::AllocPTestShellParent() {
 | |
|   return new TestShellParent();
 | |
| }
 | |
| 
 | |
| bool ContentParent::DeallocPTestShellParent(PTestShellParent* shell) {
 | |
|   delete shell;
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| PScriptCacheParent* ContentParent::AllocPScriptCacheParent(
 | |
|     const FileDescOrError& cacheFile, const bool& wantCacheData) {
 | |
|   return new loader::ScriptCacheParent(wantCacheData);
 | |
| }
 | |
| 
 | |
| bool ContentParent::DeallocPScriptCacheParent(PScriptCacheParent* cache) {
 | |
|   delete static_cast<loader::ScriptCacheParent*>(cache);
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| already_AddRefed<PNeckoParent> ContentParent::AllocPNeckoParent() {
 | |
|   RefPtr<NeckoParent> actor = new NeckoParent();
 | |
|   return actor.forget();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvInitStreamFilter(
 | |
|     const uint64_t& aChannelId, const nsAString& aAddonId,
 | |
|     InitStreamFilterResolver&& aResolver) {
 | |
|   extensions::StreamFilterParent::Create(this, aChannelId, aAddonId)
 | |
|       ->Then(
 | |
|           GetCurrentSerialEventTarget(), __func__,
 | |
|           [aResolver](mozilla::ipc::Endpoint<PStreamFilterChild>&& aEndpoint) {
 | |
|             aResolver(std::move(aEndpoint));
 | |
|           },
 | |
|           [aResolver](bool aDummy) {
 | |
|             aResolver(mozilla::ipc::Endpoint<PStreamFilterChild>());
 | |
|           });
 | |
| 
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvAddSecurityState(
 | |
|     const MaybeDiscarded<WindowContext>& aContext, uint32_t aStateFlags) {
 | |
|   if (aContext.IsNullOrDiscarded()) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   aContext.get()->AddSecurityState(aStateFlags);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| already_AddRefed<PExternalHelperAppParent>
 | |
| ContentParent::AllocPExternalHelperAppParent(
 | |
|     nsIURI* uri, const Maybe<mozilla::net::LoadInfoArgs>& aLoadInfoArgs,
 | |
|     const nsACString& aMimeContentType, const nsACString& aContentDisposition,
 | |
|     const uint32_t& aContentDispositionHint,
 | |
|     const nsAString& aContentDispositionFilename, const bool& aForceSave,
 | |
|     const int64_t& aContentLength, const bool& aWasFileChannel,
 | |
|     nsIURI* aReferrer, const MaybeDiscarded<BrowsingContext>& aContext,
 | |
|     const bool& aShouldCloseWindow) {
 | |
|   RefPtr<ExternalHelperAppParent> parent = new ExternalHelperAppParent(
 | |
|       uri, aContentLength, aWasFileChannel, aContentDisposition,
 | |
|       aContentDispositionHint, aContentDispositionFilename);
 | |
|   return parent.forget();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvPExternalHelperAppConstructor(
 | |
|     PExternalHelperAppParent* actor, nsIURI* uri,
 | |
|     const Maybe<LoadInfoArgs>& loadInfoArgs, const nsACString& aMimeContentType,
 | |
|     const nsACString& aContentDisposition,
 | |
|     const uint32_t& aContentDispositionHint,
 | |
|     const nsAString& aContentDispositionFilename, const bool& aForceSave,
 | |
|     const int64_t& aContentLength, const bool& aWasFileChannel,
 | |
|     nsIURI* aReferrer, const MaybeDiscarded<BrowsingContext>& aContext,
 | |
|     const bool& aShouldCloseWindow) {
 | |
|   BrowsingContext* context = aContext.IsDiscarded() ? nullptr : aContext.get();
 | |
|   if (!static_cast<ExternalHelperAppParent*>(actor)->Init(
 | |
|           loadInfoArgs, aMimeContentType, aForceSave, aReferrer, context,
 | |
|           aShouldCloseWindow)) {
 | |
|     return IPC_FAIL(this, "Init failed.");
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| already_AddRefed<PHandlerServiceParent>
 | |
| ContentParent::AllocPHandlerServiceParent() {
 | |
|   RefPtr<HandlerServiceParent> actor = new HandlerServiceParent();
 | |
|   return actor.forget();
 | |
| }
 | |
| 
 | |
| media::PMediaParent* ContentParent::AllocPMediaParent() {
 | |
|   return media::AllocPMediaParent();
 | |
| }
 | |
| 
 | |
| bool ContentParent::DeallocPMediaParent(media::PMediaParent* aActor) {
 | |
|   return media::DeallocPMediaParent(aActor);
 | |
| }
 | |
| 
 | |
| PBenchmarkStorageParent* ContentParent::AllocPBenchmarkStorageParent() {
 | |
|   return new BenchmarkStorageParent;
 | |
| }
 | |
| 
 | |
| bool ContentParent::DeallocPBenchmarkStorageParent(
 | |
|     PBenchmarkStorageParent* aActor) {
 | |
|   delete aActor;
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| #ifdef MOZ_WEBSPEECH
 | |
| PSpeechSynthesisParent* ContentParent::AllocPSpeechSynthesisParent() {
 | |
|   if (!StaticPrefs::media_webspeech_synth_enabled()) {
 | |
|     return nullptr;
 | |
|   }
 | |
|   return new mozilla::dom::SpeechSynthesisParent();
 | |
| }
 | |
| 
 | |
| bool ContentParent::DeallocPSpeechSynthesisParent(
 | |
|     PSpeechSynthesisParent* aActor) {
 | |
|   delete aActor;
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvPSpeechSynthesisConstructor(
 | |
|     PSpeechSynthesisParent* aActor) {
 | |
|   if (!static_cast<SpeechSynthesisParent*>(aActor)->SendInit()) {
 | |
|     return IPC_FAIL(this, "SpeechSynthesisParent::SendInit failed.");
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| #endif
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvStartVisitedQueries(
 | |
|     const nsTArray<RefPtr<nsIURI>>& aUris) {
 | |
|   nsCOMPtr<IHistory> history = components::History::Service();
 | |
|   if (!history) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
|   for (const auto& uri : aUris) {
 | |
|     if (NS_WARN_IF(!uri)) {
 | |
|       continue;
 | |
|     }
 | |
|     history->ScheduleVisitedQuery(uri, this);
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvSetURITitle(nsIURI* uri,
 | |
|                                                        const nsAString& title) {
 | |
|   if (!uri) {
 | |
|     return IPC_FAIL(this, "uri must not be null.");
 | |
|   }
 | |
|   nsCOMPtr<IHistory> history = components::History::Service();
 | |
|   if (history) {
 | |
|     history->SetURITitle(uri, title);
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvIsSecureURI(
 | |
|     nsIURI* aURI, const OriginAttributes& aOriginAttributes,
 | |
|     bool* aIsSecureURI) {
 | |
|   nsCOMPtr<nsISiteSecurityService> sss(do_GetService(NS_SSSERVICE_CONTRACTID));
 | |
|   if (!sss) {
 | |
|     return IPC_FAIL(this, "Failed to get nsISiteSecurityService.");
 | |
|   }
 | |
|   if (!aURI) {
 | |
|     return IPC_FAIL(this, "aURI must not be null.");
 | |
|   }
 | |
|   nsresult rv = sss->IsSecureURI(aURI, aOriginAttributes, aIsSecureURI);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     return IPC_FAIL(this, "IsSecureURI failed.");
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvAccumulateMixedContentHSTS(
 | |
|     nsIURI* aURI, const bool& aActive,
 | |
|     const OriginAttributes& aOriginAttributes) {
 | |
|   if (!aURI) {
 | |
|     return IPC_FAIL(this, "aURI must not be null.");
 | |
|   }
 | |
|   nsMixedContentBlocker::AccumulateMixedContentHSTS(aURI, aActive,
 | |
|                                                     aOriginAttributes);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvLoadURIExternal(
 | |
|     nsIURI* uri, nsIPrincipal* aTriggeringPrincipal,
 | |
|     nsIPrincipal* aRedirectPrincipal,
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext,
 | |
|     bool aWasExternallyTriggered, bool aHasValidUserGestureActivation) {
 | |
|   if (aContext.IsDiscarded()) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsIExternalProtocolService> extProtService(
 | |
|       do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID));
 | |
|   if (!extProtService) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   if (!uri) {
 | |
|     return IPC_FAIL(this, "uri must not be null.");
 | |
|   }
 | |
| 
 | |
|   BrowsingContext* bc = aContext.get();
 | |
|   extProtService->LoadURI(uri, aTriggeringPrincipal, aRedirectPrincipal, bc,
 | |
|                           aWasExternallyTriggered,
 | |
|                           aHasValidUserGestureActivation);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvExtProtocolChannelConnectParent(
 | |
|     const uint64_t& registrarId) {
 | |
|   nsresult rv;
 | |
| 
 | |
|   // First get the real channel created before redirect on the parent.
 | |
|   nsCOMPtr<nsIChannel> channel;
 | |
|   rv = NS_LinkRedirectChannels(registrarId, nullptr, getter_AddRefs(channel));
 | |
|   NS_ENSURE_SUCCESS(rv, IPC_OK());
 | |
| 
 | |
|   nsCOMPtr<nsIParentChannel> parent = do_QueryInterface(channel, &rv);
 | |
|   NS_ENSURE_SUCCESS(rv, IPC_OK());
 | |
| 
 | |
|   // The channel itself is its own (faked) parent, link it.
 | |
|   rv = NS_LinkRedirectChannels(registrarId, parent, getter_AddRefs(channel));
 | |
|   NS_ENSURE_SUCCESS(rv, IPC_OK());
 | |
| 
 | |
|   // Signal the parent channel that it's a redirect-to parent.  This will
 | |
|   // make AsyncOpen on it do nothing (what we want).
 | |
|   // Yes, this is a bit of a hack, but I don't think it's necessary to invent
 | |
|   // a new interface just to set this flag on the channel.
 | |
|   parent->SetParentListener(nullptr);
 | |
| 
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvShowAlert(
 | |
|     nsIAlertNotification* aAlert) {
 | |
|   if (!aAlert) {
 | |
|     return IPC_FAIL(this, "aAlert must not be null.");
 | |
|   }
 | |
|   nsCOMPtr<nsIAlertsService> sysAlerts(components::Alerts::Service());
 | |
|   if (sysAlerts) {
 | |
|     sysAlerts->ShowAlert(aAlert, this);
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvCloseAlert(const nsAString& aName,
 | |
|                                                       bool aContextClosed) {
 | |
|   nsCOMPtr<nsIAlertsService> sysAlerts(components::Alerts::Service());
 | |
|   if (sysAlerts) {
 | |
|     sysAlerts->CloseAlert(aName, aContextClosed);
 | |
|   }
 | |
| 
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvDisableNotifications(
 | |
|     nsIPrincipal* aPrincipal) {
 | |
|   if (!aPrincipal) {
 | |
|     return IPC_FAIL(this, "No principal");
 | |
|   }
 | |
| 
 | |
|   if (!ValidatePrincipal(aPrincipal)) {
 | |
|     LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __func__);
 | |
|   }
 | |
|   Unused << Notification::RemovePermission(aPrincipal);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvOpenNotificationSettings(
 | |
|     nsIPrincipal* aPrincipal) {
 | |
|   if (!aPrincipal) {
 | |
|     return IPC_FAIL(this, "No principal");
 | |
|   }
 | |
| 
 | |
|   if (!ValidatePrincipal(aPrincipal)) {
 | |
|     LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __func__);
 | |
|   }
 | |
|   Unused << Notification::OpenSettings(aPrincipal);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvNotificationEvent(
 | |
|     const nsAString& aType, const NotificationEventData& aData) {
 | |
|   nsCOMPtr<nsIServiceWorkerManager> swm =
 | |
|       mozilla::components::ServiceWorkerManager::Service();
 | |
|   if (NS_WARN_IF(!swm)) {
 | |
|     // Probably shouldn't happen, but no need to crash the child process.
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   if (aType.EqualsLiteral("click")) {
 | |
|     nsresult rv = swm->SendNotificationClickEvent(
 | |
|         aData.originSuffix(), aData.scope(), aData.ID(), aData.title(),
 | |
|         aData.dir(), aData.lang(), aData.body(), aData.tag(), aData.icon(),
 | |
|         aData.data(), aData.behavior());
 | |
|     Unused << NS_WARN_IF(NS_FAILED(rv));
 | |
|   } else {
 | |
|     MOZ_ASSERT(aType.EqualsLiteral("close"));
 | |
|     nsresult rv = swm->SendNotificationCloseEvent(
 | |
|         aData.originSuffix(), aData.scope(), aData.ID(), aData.title(),
 | |
|         aData.dir(), aData.lang(), aData.body(), aData.tag(), aData.icon(),
 | |
|         aData.data(), aData.behavior());
 | |
|     Unused << NS_WARN_IF(NS_FAILED(rv));
 | |
|   }
 | |
| 
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvSyncMessage(
 | |
|     const nsAString& aMsg, const ClonedMessageData& aData,
 | |
|     nsTArray<StructuredCloneData>* aRetvals) {
 | |
|   AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING("ContentParent::RecvSyncMessage",
 | |
|                                              OTHER, aMsg);
 | |
|   MMPrinter::Print("ContentParent::RecvSyncMessage", aMsg, aData);
 | |
| 
 | |
|   RefPtr<nsFrameMessageManager> ppm = mMessageManager;
 | |
|   if (ppm) {
 | |
|     ipc::StructuredCloneData data;
 | |
|     ipc::UnpackClonedMessageData(aData, data);
 | |
| 
 | |
|     ppm->ReceiveMessage(ppm, nullptr, aMsg, true, &data, aRetvals,
 | |
|                         IgnoreErrors());
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvAsyncMessage(
 | |
|     const nsAString& aMsg, const ClonedMessageData& aData) {
 | |
|   AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING("ContentParent::RecvAsyncMessage",
 | |
|                                              OTHER, aMsg);
 | |
|   MMPrinter::Print("ContentParent::RecvAsyncMessage", aMsg, aData);
 | |
| 
 | |
|   RefPtr<nsFrameMessageManager> ppm = mMessageManager;
 | |
|   if (ppm) {
 | |
|     ipc::StructuredCloneData data;
 | |
|     ipc::UnpackClonedMessageData(aData, data);
 | |
| 
 | |
|     ppm->ReceiveMessage(ppm, nullptr, aMsg, false, &data, nullptr,
 | |
|                         IgnoreErrors());
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| MOZ_CAN_RUN_SCRIPT
 | |
| static int32_t AddGeolocationListener(
 | |
|     nsIDOMGeoPositionCallback* watcher,
 | |
|     nsIDOMGeoPositionErrorCallback* errorCallBack, bool highAccuracy) {
 | |
|   RefPtr<Geolocation> geo = Geolocation::NonWindowSingleton();
 | |
| 
 | |
|   UniquePtr<PositionOptions> options = MakeUnique<PositionOptions>();
 | |
|   options->mTimeout = 0;
 | |
|   options->mMaximumAge = 0;
 | |
|   options->mEnableHighAccuracy = highAccuracy;
 | |
|   return geo->WatchPosition(watcher, errorCallBack, std::move(options));
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvAddGeolocationListener(
 | |
|     const bool& aHighAccuracy) {
 | |
|   // To ensure no geolocation updates are skipped, we always force the
 | |
|   // creation of a new listener.
 | |
|   RecvRemoveGeolocationListener();
 | |
|   mGeolocationWatchID = AddGeolocationListener(this, this, aHighAccuracy);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvRemoveGeolocationListener() {
 | |
|   if (mGeolocationWatchID != -1) {
 | |
|     RefPtr<Geolocation> geo = Geolocation::NonWindowSingleton();
 | |
|     if (geo) {
 | |
|       geo->ClearWatch(mGeolocationWatchID);
 | |
|     }
 | |
|     mGeolocationWatchID = -1;
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvSetGeolocationHigherAccuracy(
 | |
|     const bool& aEnable) {
 | |
|   // This should never be called without a listener already present,
 | |
|   // so this check allows us to forgo securing privileges.
 | |
|   if (mGeolocationWatchID != -1) {
 | |
|     RecvRemoveGeolocationListener();
 | |
|     mGeolocationWatchID = AddGeolocationListener(this, this, aEnable);
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| ContentParent::HandleEvent(nsIDOMGeoPosition* postion) {
 | |
|   Unused << SendGeolocationUpdate(postion);
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| ContentParent::HandleEvent(GeolocationPositionError* positionError) {
 | |
|   Unused << SendGeolocationError(positionError->Code());
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvConsoleMessage(
 | |
|     const nsAString& aMessage) {
 | |
|   nsresult rv;
 | |
|   nsCOMPtr<nsIConsoleService> consoleService =
 | |
|       do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv);
 | |
|   if (NS_SUCCEEDED(rv)) {
 | |
|     RefPtr<nsConsoleMessage> msg(new nsConsoleMessage(aMessage));
 | |
|     msg->SetIsForwardedFromContentProcess(true);
 | |
|     consoleService->LogMessageWithMode(msg, nsIConsoleService::SuppressLog);
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvReportFrameTimingData(
 | |
|     const mozilla::Maybe<LoadInfoArgs>& loadInfoArgs,
 | |
|     const nsAString& entryName, const nsAString& initiatorType,
 | |
|     UniquePtr<PerformanceTimingData>&& aData) {
 | |
|   if (!aData) {
 | |
|     return IPC_FAIL(this, "aData should not be null");
 | |
|   }
 | |
| 
 | |
|   if (loadInfoArgs.isNothing()) {
 | |
|     return IPC_FAIL(this, "loadInfoArgs should not be null");
 | |
|   }
 | |
| 
 | |
|   RefPtr<WindowGlobalParent> parent =
 | |
|       WindowGlobalParent::GetByInnerWindowId(loadInfoArgs->innerWindowID());
 | |
|   if (!parent || !parent->GetContentParent()) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   MOZ_ASSERT(parent->GetContentParent() != this,
 | |
|              "No need to bounce around if in the same process");
 | |
| 
 | |
|   Unused << parent->GetContentParent()->SendReportFrameTimingData(
 | |
|       loadInfoArgs, entryName, initiatorType, std::move(aData));
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvScriptError(
 | |
|     const nsAString& aMessage, const nsAString& aSourceName,
 | |
|     const nsAString& aSourceLine, const uint32_t& aLineNumber,
 | |
|     const uint32_t& aColNumber, const uint32_t& aFlags,
 | |
|     const nsACString& aCategory, const bool& aFromPrivateWindow,
 | |
|     const uint64_t& aInnerWindowId, const bool& aFromChromeContext) {
 | |
|   return RecvScriptErrorInternal(aMessage, aSourceName, aSourceLine,
 | |
|                                  aLineNumber, aColNumber, aFlags, aCategory,
 | |
|                                  aFromPrivateWindow, aFromChromeContext);
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvScriptErrorWithStack(
 | |
|     const nsAString& aMessage, const nsAString& aSourceName,
 | |
|     const nsAString& aSourceLine, const uint32_t& aLineNumber,
 | |
|     const uint32_t& aColNumber, const uint32_t& aFlags,
 | |
|     const nsACString& aCategory, const bool& aFromPrivateWindow,
 | |
|     const bool& aFromChromeContext, const ClonedMessageData& aFrame) {
 | |
|   return RecvScriptErrorInternal(
 | |
|       aMessage, aSourceName, aSourceLine, aLineNumber, aColNumber, aFlags,
 | |
|       aCategory, aFromPrivateWindow, aFromChromeContext, &aFrame);
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvScriptErrorInternal(
 | |
|     const nsAString& aMessage, const nsAString& aSourceName,
 | |
|     const nsAString& aSourceLine, const uint32_t& aLineNumber,
 | |
|     const uint32_t& aColNumber, const uint32_t& aFlags,
 | |
|     const nsACString& aCategory, const bool& aFromPrivateWindow,
 | |
|     const bool& aFromChromeContext, const ClonedMessageData* aStack) {
 | |
|   nsresult rv;
 | |
|   nsCOMPtr<nsIConsoleService> consoleService =
 | |
|       do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsIScriptError> msg;
 | |
| 
 | |
|   if (aStack) {
 | |
|     StructuredCloneData data;
 | |
|     UnpackClonedMessageData(*aStack, data);
 | |
| 
 | |
|     AutoJSAPI jsapi;
 | |
|     if (NS_WARN_IF(!jsapi.Init(xpc::PrivilegedJunkScope()))) {
 | |
|       MOZ_CRASH();
 | |
|     }
 | |
|     JSContext* cx = jsapi.cx();
 | |
| 
 | |
|     JS::Rooted<JS::Value> stack(cx);
 | |
|     ErrorResult rv;
 | |
|     data.Read(cx, &stack, rv);
 | |
|     if (rv.Failed() || !stack.isObject()) {
 | |
|       rv.SuppressException();
 | |
|       return IPC_OK();
 | |
|     }
 | |
| 
 | |
|     JS::Rooted<JSObject*> stackObj(cx, &stack.toObject());
 | |
|     MOZ_ASSERT(JS::IsUnwrappedSavedFrame(stackObj));
 | |
| 
 | |
|     JS::Rooted<JSObject*> stackGlobal(cx, JS::GetNonCCWObjectGlobal(stackObj));
 | |
|     msg = new nsScriptErrorWithStack(JS::NothingHandleValue, stackObj,
 | |
|                                      stackGlobal);
 | |
|   } else {
 | |
|     msg = new nsScriptError();
 | |
|   }
 | |
| 
 | |
|   rv = msg->Init(aMessage, aSourceName, aSourceLine, aLineNumber, aColNumber,
 | |
|                  aFlags, aCategory, aFromPrivateWindow, aFromChromeContext);
 | |
|   if (NS_FAILED(rv)) return IPC_OK();
 | |
| 
 | |
|   msg->SetIsForwardedFromContentProcess(true);
 | |
| 
 | |
|   consoleService->LogMessageWithMode(msg, nsIConsoleService::SuppressLog);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| bool ContentParent::DoLoadMessageManagerScript(const nsAString& aURL,
 | |
|                                                bool aRunInGlobalScope) {
 | |
|   MOZ_ASSERT(!aRunInGlobalScope);
 | |
|   return SendLoadProcessScript(aURL);
 | |
| }
 | |
| 
 | |
| nsresult ContentParent::DoSendAsyncMessage(const nsAString& aMessage,
 | |
|                                            StructuredCloneData& aHelper) {
 | |
|   ClonedMessageData data;
 | |
|   if (!BuildClonedMessageData(aHelper, data)) {
 | |
|     return NS_ERROR_DOM_DATA_CLONE_ERR;
 | |
|   }
 | |
|   if (!SendAsyncMessage(aMessage, data)) {
 | |
|     return NS_ERROR_UNEXPECTED;
 | |
|   }
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvCopyFavicon(
 | |
|     nsIURI* aOldURI, nsIURI* aNewURI, const bool& aInPrivateBrowsing) {
 | |
|   if (!aOldURI) {
 | |
|     return IPC_FAIL(this, "aOldURI should not be null");
 | |
|   }
 | |
|   if (!aNewURI) {
 | |
|     return IPC_FAIL(this, "aNewURI should not be null");
 | |
|   }
 | |
| 
 | |
|   nsDocShell::CopyFavicon(aOldURI, aNewURI, aInPrivateBrowsing);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvFindImageText(
 | |
|     IPCImage&& aImage, nsTArray<nsCString>&& aLanguages,
 | |
|     FindImageTextResolver&& aResolver) {
 | |
|   RefPtr<DataSourceSurface> surf =
 | |
|       nsContentUtils::IPCImageToSurface(std::move(aImage));
 | |
|   if (!surf) {
 | |
|     aResolver(TextRecognitionResultOrError("Failed to read image"_ns));
 | |
|     return IPC_OK();
 | |
|   }
 | |
|   TextRecognition::FindText(*surf, aLanguages)
 | |
|       ->Then(
 | |
|           GetCurrentSerialEventTarget(), __func__,
 | |
|           [resolver = std::move(aResolver)](
 | |
|               TextRecognition::NativePromise::ResolveOrRejectValue&& aValue) {
 | |
|             if (aValue.IsResolve()) {
 | |
|               resolver(TextRecognitionResultOrError(aValue.ResolveValue()));
 | |
|             } else {
 | |
|               resolver(TextRecognitionResultOrError(aValue.RejectValue()));
 | |
|             }
 | |
|           });
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| bool ContentParent::ShouldContinueFromReplyTimeout() {
 | |
|   RefPtr<ProcessHangMonitor> monitor = ProcessHangMonitor::Get();
 | |
|   return !monitor || !monitor->ShouldTimeOutCPOWs();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvRecordingDeviceEvents(
 | |
|     const nsAString& aRecordingStatus, const nsAString& aPageURL,
 | |
|     const bool& aIsAudio, const bool& aIsVideo) {
 | |
|   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
 | |
|   if (obs) {
 | |
|     // recording-device-ipc-events needs to gather more information from content
 | |
|     // process
 | |
|     RefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();
 | |
|     props->SetPropertyAsUint64(u"childID"_ns, ChildID());
 | |
|     props->SetPropertyAsBool(u"isAudio"_ns, aIsAudio);
 | |
|     props->SetPropertyAsBool(u"isVideo"_ns, aIsVideo);
 | |
|     props->SetPropertyAsAString(u"requestURL"_ns, aPageURL);
 | |
| 
 | |
|     obs->NotifyObservers((nsIPropertyBag2*)props, "recording-device-ipc-events",
 | |
|                          PromiseFlatString(aRecordingStatus).get());
 | |
|   } else {
 | |
|     NS_WARNING(
 | |
|         "Could not get the Observer service for "
 | |
|         "ContentParent::RecvRecordingDeviceEvents.");
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvAddIdleObserver(
 | |
|     const uint64_t& aObserver, const uint32_t& aIdleTimeInS) {
 | |
|   nsresult rv;
 | |
|   nsCOMPtr<nsIUserIdleService> idleService =
 | |
|       do_GetService("@mozilla.org/widget/useridleservice;1", &rv);
 | |
|   NS_ENSURE_SUCCESS(rv, IPC_FAIL(this, "Failed to get UserIdleService."));
 | |
| 
 | |
|   RefPtr<ParentIdleListener> listener =
 | |
|       new ParentIdleListener(this, aObserver, aIdleTimeInS);
 | |
|   rv = idleService->AddIdleObserver(listener, aIdleTimeInS);
 | |
|   NS_ENSURE_SUCCESS(rv, IPC_FAIL(this, "AddIdleObserver failed."));
 | |
|   mIdleListeners.AppendElement(listener);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvRemoveIdleObserver(
 | |
|     const uint64_t& aObserver, const uint32_t& aIdleTimeInS) {
 | |
|   RefPtr<ParentIdleListener> listener;
 | |
|   for (int32_t i = mIdleListeners.Length() - 1; i >= 0; --i) {
 | |
|     listener = static_cast<ParentIdleListener*>(mIdleListeners[i].get());
 | |
|     if (listener->mObserver == aObserver && listener->mTime == aIdleTimeInS) {
 | |
|       nsresult rv;
 | |
|       nsCOMPtr<nsIUserIdleService> idleService =
 | |
|           do_GetService("@mozilla.org/widget/useridleservice;1", &rv);
 | |
|       NS_ENSURE_SUCCESS(rv, IPC_FAIL(this, "Failed to get UserIdleService."));
 | |
|       idleService->RemoveIdleObserver(listener, aIdleTimeInS);
 | |
|       mIdleListeners.RemoveElementAt(i);
 | |
|       break;
 | |
|     }
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvBackUpXResources(
 | |
|     const FileDescriptor& aXSocketFd) {
 | |
| #ifndef MOZ_X11
 | |
|   MOZ_CRASH("This message only makes sense on X11 platforms");
 | |
| #else
 | |
|   MOZ_ASSERT(0 > mChildXSocketFdDup.get(), "Already backed up X resources??");
 | |
|   if (aXSocketFd.IsValid()) {
 | |
|     auto rawFD = aXSocketFd.ClonePlatformHandle();
 | |
|     mChildXSocketFdDup.reset(rawFD.release());
 | |
|   }
 | |
| #endif
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| class AnonymousTemporaryFileRequestor final : public Runnable {
 | |
|  public:
 | |
|   AnonymousTemporaryFileRequestor(ContentParent* aCP, const uint64_t& aID)
 | |
|       : Runnable("dom::AnonymousTemporaryFileRequestor"),
 | |
|         mCP(aCP),
 | |
|         mID(aID),
 | |
|         mRv(NS_OK),
 | |
|         mPRFD(nullptr) {}
 | |
| 
 | |
|   NS_IMETHOD Run() override {
 | |
|     if (NS_IsMainThread()) {
 | |
|       FileDescOrError result;
 | |
|       if (NS_WARN_IF(NS_FAILED(mRv))) {
 | |
|         // Returning false will kill the child process; instead
 | |
|         // propagate the error and let the child handle it.
 | |
|         result = mRv;
 | |
|       } else {
 | |
|         result = FileDescriptor(FileDescriptor::PlatformHandleType(
 | |
|             PR_FileDesc2NativeHandle(mPRFD)));
 | |
|         // The FileDescriptor object owns a duplicate of the file handle; we
 | |
|         // must close the original (and clean up the NSPR descriptor).
 | |
|         PR_Close(mPRFD);
 | |
|       }
 | |
|       Unused << mCP->SendProvideAnonymousTemporaryFile(mID, result);
 | |
|       // It's important to release this reference while wr're on the main
 | |
|       // thread!
 | |
|       mCP = nullptr;
 | |
|     } else {
 | |
|       mRv = NS_OpenAnonymousTemporaryFile(&mPRFD);
 | |
|       NS_DispatchToMainThread(this);
 | |
|     }
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|  private:
 | |
|   RefPtr<ContentParent> mCP;
 | |
|   uint64_t mID;
 | |
|   nsresult mRv;
 | |
|   PRFileDesc* mPRFD;
 | |
| };
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvRequestAnonymousTemporaryFile(
 | |
|     const uint64_t& aID) {
 | |
|   // Make sure to send a callback to the child if we bail out early.
 | |
|   nsresult rv = NS_OK;
 | |
|   RefPtr<ContentParent> self(this);
 | |
|   auto autoNotifyChildOnError = MakeScopeExit([&, self]() {
 | |
|     if (NS_FAILED(rv)) {
 | |
|       FileDescOrError result(rv);
 | |
|       Unused << self->SendProvideAnonymousTemporaryFile(aID, result);
 | |
|     }
 | |
|   });
 | |
| 
 | |
|   // We use a helper runnable to open the anonymous temporary file on the IO
 | |
|   // thread.  The same runnable will call us back on the main thread when the
 | |
|   // file has been opened.
 | |
|   nsCOMPtr<nsIEventTarget> target =
 | |
|       do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
 | |
|   if (!target) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   rv = target->Dispatch(new AnonymousTemporaryFileRequestor(this, aID),
 | |
|                         NS_DISPATCH_NORMAL);
 | |
|   if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   rv = NS_OK;
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvCreateAudioIPCConnection(
 | |
|     CreateAudioIPCConnectionResolver&& aResolver) {
 | |
|   FileDescriptor fd = CubebUtils::CreateAudioIPCConnection();
 | |
|   FileDescOrError result;
 | |
|   if (fd.IsValid()) {
 | |
|     result = fd;
 | |
|   } else {
 | |
|     result = NS_ERROR_FAILURE;
 | |
|   }
 | |
|   aResolver(std::move(result));
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| already_AddRefed<extensions::PExtensionsParent>
 | |
| ContentParent::AllocPExtensionsParent() {
 | |
|   return MakeAndAddRef<extensions::ExtensionsParent>();
 | |
| }
 | |
| 
 | |
| void ContentParent::NotifyUpdatedDictionaries() {
 | |
|   RefPtr<mozSpellChecker> spellChecker(mozSpellChecker::Create());
 | |
|   MOZ_ASSERT(spellChecker, "No spell checker?");
 | |
| 
 | |
|   nsTArray<nsCString> dictionaries;
 | |
|   spellChecker->GetDictionaryList(&dictionaries);
 | |
| 
 | |
|   for (auto* cp : AllProcesses(eLive)) {
 | |
|     Unused << cp->SendUpdateDictionaryList(dictionaries);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void ContentParent::NotifyUpdatedFonts(bool aFullRebuild) {
 | |
|   if (gfxPlatformFontList::PlatformFontList()->SharedFontList()) {
 | |
|     for (auto* cp : AllProcesses(eLive)) {
 | |
|       Unused << cp->SendRebuildFontList(aFullRebuild);
 | |
|     }
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   SystemFontList fontList;
 | |
|   gfxPlatform::GetPlatform()->ReadSystemFontList(&fontList);
 | |
| 
 | |
|   for (auto* cp : AllProcesses(eLive)) {
 | |
|     Unused << cp->SendUpdateFontList(fontList);
 | |
|   }
 | |
| }
 | |
| 
 | |
| #ifdef MOZ_WEBRTC
 | |
| PWebrtcGlobalParent* ContentParent::AllocPWebrtcGlobalParent() {
 | |
|   return WebrtcGlobalParent::Alloc();
 | |
| }
 | |
| 
 | |
| bool ContentParent::DeallocPWebrtcGlobalParent(PWebrtcGlobalParent* aActor) {
 | |
|   WebrtcGlobalParent::Dealloc(static_cast<WebrtcGlobalParent*>(aActor));
 | |
|   return true;
 | |
| }
 | |
| #endif
 | |
| 
 | |
| void ContentParent::MaybeInvokeDragSession(BrowserParent* aParent) {
 | |
|   // dnd uses IPCBlob to transfer data to the content process and the IPC
 | |
|   // message is sent as normal priority. When sending input events with input
 | |
|   // priority, the message may be preempted by the later dnd events. To make
 | |
|   // sure the input events and the blob message are processed in time order
 | |
|   // on the content process, we temporarily send the input events with normal
 | |
|   // priority when there is an active dnd session.
 | |
|   SetInputPriorityEventEnabled(false);
 | |
| 
 | |
|   nsCOMPtr<nsIDragService> dragService =
 | |
|       do_GetService("@mozilla.org/widget/dragservice;1");
 | |
|   if (dragService && dragService->MaybeAddChildProcess(this)) {
 | |
|     // We need to send transferable data to child process.
 | |
|     nsCOMPtr<nsIDragSession> session;
 | |
|     dragService->GetCurrentSession(getter_AddRefs(session));
 | |
|     if (session) {
 | |
|       nsTArray<IPCDataTransfer> dataTransfers;
 | |
|       RefPtr<DataTransfer> transfer = session->GetDataTransfer();
 | |
|       if (!transfer) {
 | |
|         // Pass eDrop to get DataTransfer with external
 | |
|         // drag formats cached.
 | |
|         transfer = new DataTransfer(nullptr, eDrop, true, -1);
 | |
|         session->SetDataTransfer(transfer);
 | |
|       }
 | |
|       // Note, even though this fills the DataTransfer object with
 | |
|       // external data, the data is usually transfered over IPC lazily when
 | |
|       // needed.
 | |
|       transfer->FillAllExternalData();
 | |
|       nsCOMPtr<nsILoadContext> lc =
 | |
|           aParent ? aParent->GetLoadContext() : nullptr;
 | |
|       nsCOMPtr<nsIArray> transferables = transfer->GetTransferables(lc);
 | |
|       nsContentUtils::TransferablesToIPCTransferables(
 | |
|           transferables, dataTransfers, false, this);
 | |
|       uint32_t action;
 | |
|       session->GetDragAction(&action);
 | |
| 
 | |
|       RefPtr<WindowContext> sourceWC;
 | |
|       session->GetSourceWindowContext(getter_AddRefs(sourceWC));
 | |
|       RefPtr<WindowContext> sourceTopWC;
 | |
|       session->GetSourceTopWindowContext(getter_AddRefs(sourceTopWC));
 | |
|       mozilla::Unused << SendInvokeDragSession(
 | |
|           sourceWC, sourceTopWC, std::move(dataTransfers), action);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvUpdateDropEffect(
 | |
|     const uint32_t& aDragAction, const uint32_t& aDropEffect) {
 | |
|   nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
 | |
|   if (dragSession) {
 | |
|     dragSession->SetDragAction(aDragAction);
 | |
|     RefPtr<DataTransfer> dt = dragSession->GetDataTransfer();
 | |
|     if (dt) {
 | |
|       dt->SetDropEffectInt(aDropEffect);
 | |
|     }
 | |
|     dragSession->UpdateDragEffect();
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| PContentPermissionRequestParent*
 | |
| ContentParent::AllocPContentPermissionRequestParent(
 | |
|     const nsTArray<PermissionRequest>& aRequests, nsIPrincipal* aPrincipal,
 | |
|     nsIPrincipal* aTopLevelPrincipal, const bool& aIsHandlingUserInput,
 | |
|     const bool& aMaybeUnsafePermissionDelegate, const TabId& aTabId) {
 | |
|   RefPtr<BrowserParent> tp;
 | |
|   ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
 | |
|   if (cpm) {
 | |
|     tp =
 | |
|         cpm->GetTopLevelBrowserParentByProcessAndTabId(this->ChildID(), aTabId);
 | |
|   }
 | |
|   if (!tp) {
 | |
|     return nullptr;
 | |
|   }
 | |
| 
 | |
|   nsIPrincipal* topPrincipal = aTopLevelPrincipal;
 | |
|   if (!topPrincipal) {
 | |
|     nsCOMPtr<nsIPrincipal> principal = tp->GetContentPrincipal();
 | |
|     topPrincipal = principal;
 | |
|   }
 | |
|   return nsContentPermissionUtils::CreateContentPermissionRequestParent(
 | |
|       aRequests, tp->GetOwnerElement(), aPrincipal, topPrincipal,
 | |
|       aIsHandlingUserInput, aMaybeUnsafePermissionDelegate, aTabId);
 | |
| }
 | |
| 
 | |
| bool ContentParent::DeallocPContentPermissionRequestParent(
 | |
|     PContentPermissionRequestParent* actor) {
 | |
|   nsContentPermissionUtils::NotifyRemoveContentPermissionRequestParent(actor);
 | |
|   delete actor;
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| PWebBrowserPersistDocumentParent*
 | |
| ContentParent::AllocPWebBrowserPersistDocumentParent(
 | |
|     PBrowserParent* aBrowser, const MaybeDiscarded<BrowsingContext>& aContext) {
 | |
|   return new WebBrowserPersistDocumentParent();
 | |
| }
 | |
| 
 | |
| bool ContentParent::DeallocPWebBrowserPersistDocumentParent(
 | |
|     PWebBrowserPersistDocumentParent* aActor) {
 | |
|   delete aActor;
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::CommonCreateWindow(
 | |
|     PBrowserParent* aThisTab, BrowsingContext& aParent, bool aSetOpener,
 | |
|     const uint32_t& aChromeFlags, const bool& aCalledFromJS,
 | |
|     const bool& aForPrinting, const bool& aForWindowDotPrint,
 | |
|     nsIURI* aURIToLoad, const nsACString& aFeatures,
 | |
|     BrowserParent* aNextRemoteBrowser, const nsAString& aName,
 | |
|     nsresult& aResult, nsCOMPtr<nsIRemoteTab>& aNewRemoteTab,
 | |
|     bool* aWindowIsNew, int32_t& aOpenLocation,
 | |
|     nsIPrincipal* aTriggeringPrincipal, nsIReferrerInfo* aReferrerInfo,
 | |
|     bool aLoadURI, nsIContentSecurityPolicy* aCsp,
 | |
|     const OriginAttributes& aOriginAttributes) {
 | |
|   // The content process should never be in charge of computing whether or
 | |
|   // not a window should be private - the parent will do that.
 | |
|   const uint32_t badFlags = nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW |
 | |
|                             nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW |
 | |
|                             nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME;
 | |
|   if (!!(aChromeFlags & badFlags)) {
 | |
|     return IPC_FAIL(this, "Forbidden aChromeFlags passed");
 | |
|   }
 | |
| 
 | |
|   RefPtr<nsOpenWindowInfo> openInfo = new nsOpenWindowInfo();
 | |
|   openInfo->mForceNoOpener = !aSetOpener;
 | |
|   openInfo->mParent = &aParent;
 | |
|   openInfo->mIsRemote = true;
 | |
|   openInfo->mIsForPrinting = aForPrinting;
 | |
|   openInfo->mIsForWindowDotPrint = aForWindowDotPrint;
 | |
|   openInfo->mNextRemoteBrowser = aNextRemoteBrowser;
 | |
|   openInfo->mOriginAttributes = aOriginAttributes;
 | |
| 
 | |
|   MOZ_ASSERT_IF(aForWindowDotPrint, aForPrinting);
 | |
| 
 | |
|   RefPtr<BrowserParent> topParent = BrowserParent::GetFrom(aThisTab);
 | |
|   while (topParent && topParent->GetBrowserBridgeParent()) {
 | |
|     topParent = topParent->GetBrowserBridgeParent()->Manager();
 | |
|   }
 | |
|   RefPtr<BrowserHost> thisBrowserHost =
 | |
|       topParent ? topParent->GetBrowserHost() : nullptr;
 | |
|   MOZ_ASSERT_IF(topParent, thisBrowserHost);
 | |
|   RefPtr<BrowsingContext> topBC =
 | |
|       topParent ? topParent->GetBrowsingContext() : nullptr;
 | |
|   MOZ_ASSERT_IF(topParent, topBC);
 | |
| 
 | |
|   // The content process should have set its remote and fission flags correctly.
 | |
|   if (topBC) {
 | |
|     if ((!!(aChromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW) !=
 | |
|          topBC->UseRemoteTabs()) ||
 | |
|         (!!(aChromeFlags & nsIWebBrowserChrome::CHROME_FISSION_WINDOW) !=
 | |
|          topBC->UseRemoteSubframes())) {
 | |
|       return IPC_FAIL(this, "Unexpected aChromeFlags passed");
 | |
|     }
 | |
| 
 | |
|     if (!aOriginAttributes.EqualsIgnoringFPD(topBC->OriginAttributesRef())) {
 | |
|       return IPC_FAIL(this, "Passed-in OriginAttributes does not match opener");
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsIContent> frame;
 | |
|   if (topParent) {
 | |
|     frame = topParent->GetOwnerElement();
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsPIDOMWindowOuter> outerWin;
 | |
|   if (frame) {
 | |
|     outerWin = frame->OwnerDoc()->GetWindow();
 | |
| 
 | |
|     // If our chrome window is in the process of closing, don't try to open a
 | |
|     // new tab in it.
 | |
|     if (outerWin && outerWin->Closed()) {
 | |
|       outerWin = nullptr;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsIBrowserDOMWindow> browserDOMWin;
 | |
|   if (topParent) {
 | |
|     browserDOMWin = topParent->GetBrowserDOMWindow();
 | |
|   }
 | |
| 
 | |
|   // If we haven't found a chrome window to open in, just use the most recently
 | |
|   // opened one.
 | |
|   if (!outerWin) {
 | |
|     outerWin = nsContentUtils::GetMostRecentNonPBWindow();
 | |
|     if (NS_WARN_IF(!outerWin)) {
 | |
|       aResult = NS_ERROR_FAILURE;
 | |
|       return IPC_OK();
 | |
|     }
 | |
| 
 | |
|     nsCOMPtr<nsIDOMChromeWindow> rootChromeWin = do_QueryInterface(outerWin);
 | |
|     if (rootChromeWin) {
 | |
|       rootChromeWin->GetBrowserDOMWindow(getter_AddRefs(browserDOMWin));
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   aOpenLocation = nsWindowWatcher::GetWindowOpenLocation(
 | |
|       outerWin, aChromeFlags, aCalledFromJS, aForPrinting);
 | |
| 
 | |
|   MOZ_ASSERT(aOpenLocation == nsIBrowserDOMWindow::OPEN_NEWTAB ||
 | |
|              aOpenLocation == nsIBrowserDOMWindow::OPEN_NEWWINDOW ||
 | |
|              aOpenLocation == nsIBrowserDOMWindow::OPEN_PRINT_BROWSER);
 | |
| 
 | |
|   if (NS_WARN_IF(!browserDOMWin)) {
 | |
|     // Opening in the same window or headless requires an nsIBrowserDOMWindow.
 | |
|     aOpenLocation = nsIBrowserDOMWindow::OPEN_NEWWINDOW;
 | |
|   }
 | |
| 
 | |
|   if (aOpenLocation == nsIBrowserDOMWindow::OPEN_NEWTAB ||
 | |
|       aOpenLocation == nsIBrowserDOMWindow::OPEN_PRINT_BROWSER) {
 | |
|     RefPtr<Element> openerElement = do_QueryObject(frame);
 | |
| 
 | |
|     nsCOMPtr<nsIOpenURIInFrameParams> params =
 | |
|         new nsOpenURIInFrameParams(openInfo, openerElement);
 | |
|     params->SetReferrerInfo(aReferrerInfo);
 | |
|     MOZ_ASSERT(aTriggeringPrincipal, "need a valid triggeringPrincipal");
 | |
|     params->SetTriggeringPrincipal(aTriggeringPrincipal);
 | |
|     params->SetCsp(aCsp);
 | |
| 
 | |
|     RefPtr<Element> el;
 | |
| 
 | |
|     if (aLoadURI) {
 | |
|       aResult = browserDOMWin->OpenURIInFrame(aURIToLoad, params, aOpenLocation,
 | |
|                                               nsIBrowserDOMWindow::OPEN_NEW,
 | |
|                                               aName, getter_AddRefs(el));
 | |
|     } else {
 | |
|       aResult = browserDOMWin->CreateContentWindowInFrame(
 | |
|           aURIToLoad, params, aOpenLocation, nsIBrowserDOMWindow::OPEN_NEW,
 | |
|           aName, getter_AddRefs(el));
 | |
|     }
 | |
|     RefPtr<nsFrameLoaderOwner> frameLoaderOwner = do_QueryObject(el);
 | |
|     if (NS_SUCCEEDED(aResult) && frameLoaderOwner) {
 | |
|       RefPtr<nsFrameLoader> frameLoader = frameLoaderOwner->GetFrameLoader();
 | |
|       if (frameLoader) {
 | |
|         aNewRemoteTab = frameLoader->GetRemoteTab();
 | |
|         // At this point, it's possible the inserted frameloader hasn't gone
 | |
|         // through layout yet. To ensure that the dimensions that we send down
 | |
|         // when telling the frameloader to display will be correct (instead of
 | |
|         // falling back to a 10x10 default), we force layout if necessary to get
 | |
|         // the most up-to-date dimensions. See bug 1358712 for details.
 | |
|         frameLoader->ForceLayoutIfNecessary();
 | |
|       }
 | |
|     } else if (NS_SUCCEEDED(aResult) && !frameLoaderOwner) {
 | |
|       // Fall through to the normal window opening code path when there is no
 | |
|       // window which we can open a new tab in.
 | |
|       aOpenLocation = nsIBrowserDOMWindow::OPEN_NEWWINDOW;
 | |
|     } else {
 | |
|       *aWindowIsNew = false;
 | |
|     }
 | |
| 
 | |
|     // If we didn't retarget our window open into a new window, we should return
 | |
|     // now.
 | |
|     if (aOpenLocation != nsIBrowserDOMWindow::OPEN_NEWWINDOW) {
 | |
|       return IPC_OK();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsPIWindowWatcher> pwwatch =
 | |
|       do_GetService(NS_WINDOWWATCHER_CONTRACTID, &aResult);
 | |
|   if (NS_WARN_IF(NS_FAILED(aResult))) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   WindowFeatures features;
 | |
|   features.Tokenize(aFeatures);
 | |
| 
 | |
|   aResult = pwwatch->OpenWindowWithRemoteTab(
 | |
|       thisBrowserHost, features, aCalledFromJS, aParent.FullZoom(), openInfo,
 | |
|       getter_AddRefs(aNewRemoteTab));
 | |
|   if (NS_WARN_IF(NS_FAILED(aResult))) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   MOZ_ASSERT(aNewRemoteTab);
 | |
|   RefPtr<BrowserHost> newBrowserHost = BrowserHost::GetFrom(aNewRemoteTab);
 | |
|   RefPtr<BrowserParent> newBrowserParent = newBrowserHost->GetActor();
 | |
| 
 | |
|   // At this point, it's possible the inserted frameloader hasn't gone through
 | |
|   // layout yet. To ensure that the dimensions that we send down when telling
 | |
|   // the frameloader to display will be correct (instead of falling back to a
 | |
|   // 10x10 default), we force layout if necessary to get the most up-to-date
 | |
|   // dimensions. See bug 1358712 for details.
 | |
|   nsCOMPtr<Element> frameElement = newBrowserHost->GetOwnerElement();
 | |
|   MOZ_ASSERT(frameElement);
 | |
|   if (nsWindowWatcher::HaveSpecifiedSize(features)) {
 | |
|     // We want to flush the layout anyway because of the resize to the specified
 | |
|     // size. (Bug 1793605).
 | |
|     RefPtr<Document> chromeDoc = frameElement->OwnerDoc();
 | |
|     MOZ_ASSERT(chromeDoc);
 | |
|     chromeDoc->FlushPendingNotifications(FlushType::Layout);
 | |
|   } else {
 | |
|     RefPtr<nsFrameLoaderOwner> frameLoaderOwner = do_QueryObject(frameElement);
 | |
|     MOZ_ASSERT(frameLoaderOwner);
 | |
|     RefPtr<nsFrameLoader> frameLoader = frameLoaderOwner->GetFrameLoader();
 | |
|     MOZ_ASSERT(frameLoader);
 | |
|     frameLoader->ForceLayoutIfNecessary();
 | |
|   }
 | |
| 
 | |
|   // If we were passed a name for the window which would override the default,
 | |
|   // we should send it down to the new tab.
 | |
|   if (nsContentUtils::IsOverridingWindowName(aName)) {
 | |
|     MOZ_ALWAYS_SUCCEEDS(newBrowserHost->GetBrowsingContext()->SetName(aName));
 | |
|   }
 | |
| 
 | |
|   MOZ_ASSERT(newBrowserHost->GetBrowsingContext()->OriginAttributesRef() ==
 | |
|              aOriginAttributes);
 | |
| 
 | |
|   if (aURIToLoad && aLoadURI) {
 | |
|     nsCOMPtr<mozIDOMWindowProxy> openerWindow;
 | |
|     if (aSetOpener && topParent) {
 | |
|       openerWindow = topParent->GetParentWindowOuter();
 | |
|     }
 | |
|     nsCOMPtr<nsIBrowserDOMWindow> newBrowserDOMWin =
 | |
|         newBrowserParent->GetBrowserDOMWindow();
 | |
|     if (NS_WARN_IF(!newBrowserDOMWin)) {
 | |
|       aResult = NS_ERROR_ABORT;
 | |
|       return IPC_OK();
 | |
|     }
 | |
|     RefPtr<BrowsingContext> bc;
 | |
|     aResult = newBrowserDOMWin->OpenURI(
 | |
|         aURIToLoad, openInfo, nsIBrowserDOMWindow::OPEN_CURRENTWINDOW,
 | |
|         nsIBrowserDOMWindow::OPEN_NEW, aTriggeringPrincipal, aCsp,
 | |
|         getter_AddRefs(bc));
 | |
|   }
 | |
| 
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvCreateWindow(
 | |
|     PBrowserParent* aThisTab, const MaybeDiscarded<BrowsingContext>& aParent,
 | |
|     PBrowserParent* aNewTab, const uint32_t& aChromeFlags,
 | |
|     const bool& aCalledFromJS, const bool& aForPrinting,
 | |
|     const bool& aForPrintPreview, nsIURI* aURIToLoad,
 | |
|     const nsACString& aFeatures, nsIPrincipal* aTriggeringPrincipal,
 | |
|     nsIContentSecurityPolicy* aCsp, nsIReferrerInfo* aReferrerInfo,
 | |
|     const OriginAttributes& aOriginAttributes,
 | |
|     CreateWindowResolver&& aResolve) {
 | |
|   if (!aTriggeringPrincipal) {
 | |
|     return IPC_FAIL(this, "No principal");
 | |
|   }
 | |
| 
 | |
|   if (!ValidatePrincipal(aTriggeringPrincipal)) {
 | |
|     LogAndAssertFailedPrincipalValidationInfo(aTriggeringPrincipal, __func__);
 | |
|   }
 | |
| 
 | |
|   nsresult rv = NS_OK;
 | |
|   CreatedWindowInfo cwi;
 | |
| 
 | |
|   // We always expect to open a new window here. If we don't, it's an error.
 | |
|   cwi.windowOpened() = true;
 | |
|   cwi.maxTouchPoints() = 0;
 | |
| 
 | |
|   // Make sure to resolve the resolver when this function exits, even if we
 | |
|   // failed to generate a valid response.
 | |
|   auto resolveOnExit = MakeScopeExit([&] {
 | |
|     // Copy over the nsresult, and then resolve.
 | |
|     cwi.rv() = rv;
 | |
|     aResolve(cwi);
 | |
|   });
 | |
| 
 | |
|   RefPtr<BrowserParent> thisTab = BrowserParent::GetFrom(aThisTab);
 | |
|   RefPtr<BrowserParent> newTab = BrowserParent::GetFrom(aNewTab);
 | |
|   MOZ_ASSERT(newTab);
 | |
| 
 | |
|   auto destroyNewTabOnError = MakeScopeExit([&] {
 | |
|     // We always expect to open a new window here. If we don't, it's an error.
 | |
|     if (!cwi.windowOpened() || NS_FAILED(rv)) {
 | |
|       if (newTab) {
 | |
|         newTab->Destroy();
 | |
|       }
 | |
|     }
 | |
|   });
 | |
| 
 | |
|   // Don't continue to try to create a new window if we've been fully discarded.
 | |
|   RefPtr<BrowsingContext> parent = aParent.GetMaybeDiscarded();
 | |
|   if (NS_WARN_IF(!parent)) {
 | |
|     rv = NS_ERROR_FAILURE;
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   // Validate that our new BrowsingContext looks as we would expect it.
 | |
|   RefPtr<BrowsingContext> newBC = newTab->GetBrowsingContext();
 | |
|   if (!newBC) {
 | |
|     return IPC_FAIL(this, "Missing BrowsingContext for new tab");
 | |
|   }
 | |
| 
 | |
|   uint64_t newBCOpenerId = newBC->GetOpenerId();
 | |
|   if (newBCOpenerId != 0 && parent->Id() != newBCOpenerId) {
 | |
|     return IPC_FAIL(this, "Invalid opener BrowsingContext for new tab");
 | |
|   }
 | |
|   if (newBC->GetParent() != nullptr) {
 | |
|     return IPC_FAIL(this,
 | |
|                     "Unexpected non-toplevel BrowsingContext for new tab");
 | |
|   }
 | |
|   if (!!(aChromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW) !=
 | |
|           newBC->UseRemoteTabs() ||
 | |
|       !!(aChromeFlags & nsIWebBrowserChrome::CHROME_FISSION_WINDOW) !=
 | |
|           newBC->UseRemoteSubframes()) {
 | |
|     return IPC_FAIL(this, "Unexpected aChromeFlags passed");
 | |
|   }
 | |
|   if (!aOriginAttributes.EqualsIgnoringFPD(newBC->OriginAttributesRef())) {
 | |
|     return IPC_FAIL(this, "Opened tab has mismatched OriginAttributes");
 | |
|   }
 | |
| 
 | |
|   if (thisTab && BrowserParent::GetFrom(thisTab)->GetBrowsingContext()) {
 | |
|     BrowsingContext* thisTabBC = thisTab->GetBrowsingContext();
 | |
|     if (thisTabBC->UseRemoteTabs() != newBC->UseRemoteTabs() ||
 | |
|         thisTabBC->UseRemoteSubframes() != newBC->UseRemoteSubframes() ||
 | |
|         thisTabBC->UsePrivateBrowsing() != newBC->UsePrivateBrowsing()) {
 | |
|       return IPC_FAIL(this, "New BrowsingContext has mismatched LoadContext");
 | |
|     }
 | |
|   }
 | |
|   BrowserParent::AutoUseNewTab aunt(newTab);
 | |
| 
 | |
|   nsCOMPtr<nsIRemoteTab> newRemoteTab;
 | |
|   int32_t openLocation = nsIBrowserDOMWindow::OPEN_NEWWINDOW;
 | |
|   mozilla::ipc::IPCResult ipcResult = CommonCreateWindow(
 | |
|       aThisTab, *parent, newBCOpenerId != 0, aChromeFlags, aCalledFromJS,
 | |
|       aForPrinting, aForPrintPreview, aURIToLoad, aFeatures, newTab,
 | |
|       VoidString(), rv, newRemoteTab, &cwi.windowOpened(), openLocation,
 | |
|       aTriggeringPrincipal, aReferrerInfo, /* aLoadUri = */ false, aCsp,
 | |
|       aOriginAttributes);
 | |
|   if (!ipcResult) {
 | |
|     return ipcResult;
 | |
|   }
 | |
| 
 | |
|   if (NS_WARN_IF(NS_FAILED(rv)) || !newRemoteTab) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   MOZ_ASSERT(BrowserHost::GetFrom(newRemoteTab.get()) ==
 | |
|              newTab->GetBrowserHost());
 | |
| 
 | |
|   // This used to happen in the child - there may now be a better place to
 | |
|   // do this work.
 | |
|   MOZ_ALWAYS_SUCCEEDS(
 | |
|       newBC->SetHasSiblings(openLocation == nsIBrowserDOMWindow::OPEN_NEWTAB));
 | |
| 
 | |
|   newTab->SwapFrameScriptsFrom(cwi.frameScripts());
 | |
|   newTab->MaybeShowFrame();
 | |
| 
 | |
|   nsCOMPtr<nsIWidget> widget = newTab->GetWidget();
 | |
|   if (widget) {
 | |
|     cwi.dimensions() = newTab->GetDimensionInfo();
 | |
|   }
 | |
| 
 | |
|   cwi.maxTouchPoints() = newTab->GetMaxTouchPoints();
 | |
| 
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvCreateWindowInDifferentProcess(
 | |
|     PBrowserParent* aThisTab, const MaybeDiscarded<BrowsingContext>& aParent,
 | |
|     const uint32_t& aChromeFlags, const bool& aCalledFromJS, nsIURI* aURIToLoad,
 | |
|     const nsACString& aFeatures, const nsAString& aName,
 | |
|     nsIPrincipal* aTriggeringPrincipal, nsIContentSecurityPolicy* aCsp,
 | |
|     nsIReferrerInfo* aReferrerInfo, const OriginAttributes& aOriginAttributes) {
 | |
|   MOZ_DIAGNOSTIC_ASSERT(!nsContentUtils::IsSpecialName(aName));
 | |
| 
 | |
|   // Don't continue to try to create a new window if we've been fully discarded.
 | |
|   RefPtr<BrowsingContext> parent = aParent.GetMaybeDiscarded();
 | |
|   if (NS_WARN_IF(!parent)) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsIRemoteTab> newRemoteTab;
 | |
|   bool windowIsNew;
 | |
|   int32_t openLocation = nsIBrowserDOMWindow::OPEN_NEWWINDOW;
 | |
| 
 | |
|   // If we have enough data, check the schemes of the loader and loadee
 | |
|   // to make sure they make sense.
 | |
|   if (aURIToLoad && aURIToLoad->SchemeIs("file") &&
 | |
|       GetRemoteType() != FILE_REMOTE_TYPE &&
 | |
|       Preferences::GetBool("browser.tabs.remote.enforceRemoteTypeRestrictions",
 | |
|                            false)) {
 | |
| #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
 | |
| #  ifdef DEBUG
 | |
|     nsAutoCString uriToLoadStr;
 | |
|     nsAutoCString triggeringUriStr;
 | |
|     aURIToLoad->GetAsciiSpec(uriToLoadStr);
 | |
|     aTriggeringPrincipal->GetAsciiSpec(triggeringUriStr);
 | |
| 
 | |
|     NS_WARNING(nsPrintfCString(
 | |
|                    "RecvCreateWindowInDifferentProcess blocked loading file "
 | |
|                    "scheme from non-file remotetype: %s tried to load %s",
 | |
|                    triggeringUriStr.get(), uriToLoadStr.get())
 | |
|                    .get());
 | |
| #  endif
 | |
|     MOZ_CRASH(
 | |
|         "RecvCreateWindowInDifferentProcess blocked loading improper scheme");
 | |
| #endif
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   nsresult rv;
 | |
|   mozilla::ipc::IPCResult ipcResult = CommonCreateWindow(
 | |
|       aThisTab, *parent, /* aSetOpener = */ false, aChromeFlags, aCalledFromJS,
 | |
|       /* aForPrinting = */ false,
 | |
|       /* aForPrintPreview = */ false, aURIToLoad, aFeatures,
 | |
|       /* aNextRemoteBrowser = */ nullptr, aName, rv, newRemoteTab, &windowIsNew,
 | |
|       openLocation, aTriggeringPrincipal, aReferrerInfo,
 | |
|       /* aLoadUri = */ true, aCsp, aOriginAttributes);
 | |
|   if (!ipcResult) {
 | |
|     return ipcResult;
 | |
|   }
 | |
| 
 | |
|   if (NS_FAILED(rv)) {
 | |
|     NS_WARNING("Call to CommonCreateWindow failed.");
 | |
|   }
 | |
| 
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvShutdownProfile(
 | |
|     const nsACString& aProfile) {
 | |
|   profiler_received_exit_profile(aProfile);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvShutdownPerfStats(
 | |
|     const nsACString& aPerfStats) {
 | |
|   PerfStats::StorePerfStats(this, aPerfStats);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvGetGraphicsDeviceInitData(
 | |
|     ContentDeviceData* aOut) {
 | |
|   gfxPlatform::GetPlatform()->BuildContentDeviceData(aOut);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvGetOutputColorProfileData(
 | |
|     nsTArray<uint8_t>* aOutputColorProfileData) {
 | |
|   (*aOutputColorProfileData) =
 | |
|       gfxPlatform::GetPlatform()->GetPlatformCMSOutputProfileData();
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvGetFontListShmBlock(
 | |
|     const uint32_t& aGeneration, const uint32_t& aIndex,
 | |
|     base::SharedMemoryHandle* aOut) {
 | |
|   auto* fontList = gfxPlatformFontList::PlatformFontList();
 | |
|   MOZ_RELEASE_ASSERT(fontList, "gfxPlatformFontList not initialized?");
 | |
|   fontList->ShareFontListShmBlockToProcess(aGeneration, aIndex, Pid(), aOut);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvInitializeFamily(
 | |
|     const uint32_t& aGeneration, const uint32_t& aFamilyIndex,
 | |
|     const bool& aLoadCmaps) {
 | |
|   auto* fontList = gfxPlatformFontList::PlatformFontList();
 | |
|   MOZ_RELEASE_ASSERT(fontList, "gfxPlatformFontList not initialized?");
 | |
|   fontList->InitializeFamily(aGeneration, aFamilyIndex, aLoadCmaps);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvSetCharacterMap(
 | |
|     const uint32_t& aGeneration, const mozilla::fontlist::Pointer& aFacePtr,
 | |
|     const gfxSparseBitSet& aMap) {
 | |
|   auto* fontList = gfxPlatformFontList::PlatformFontList();
 | |
|   MOZ_RELEASE_ASSERT(fontList, "gfxPlatformFontList not initialized?");
 | |
|   fontList->SetCharacterMap(aGeneration, aFacePtr, aMap);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvInitOtherFamilyNames(
 | |
|     const uint32_t& aGeneration, const bool& aDefer, bool* aLoaded) {
 | |
|   auto* fontList = gfxPlatformFontList::PlatformFontList();
 | |
|   MOZ_RELEASE_ASSERT(fontList, "gfxPlatformFontList not initialized?");
 | |
|   *aLoaded = fontList->InitOtherFamilyNames(aGeneration, aDefer);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvSetupFamilyCharMap(
 | |
|     const uint32_t& aGeneration, const mozilla::fontlist::Pointer& aFamilyPtr) {
 | |
|   auto* fontList = gfxPlatformFontList::PlatformFontList();
 | |
|   MOZ_RELEASE_ASSERT(fontList, "gfxPlatformFontList not initialized?");
 | |
|   fontList->SetupFamilyCharMap(aGeneration, aFamilyPtr);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvStartCmapLoading(
 | |
|     const uint32_t& aGeneration, const uint32_t& aStartIndex) {
 | |
|   auto* fontList = gfxPlatformFontList::PlatformFontList();
 | |
|   MOZ_RELEASE_ASSERT(fontList, "gfxPlatformFontList not initialized?");
 | |
|   fontList->StartCmapLoading(aGeneration, aStartIndex);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvGetHyphDict(
 | |
|     nsIURI* aURI, base::SharedMemoryHandle* aOutHandle, uint32_t* aOutSize) {
 | |
|   if (!aURI) {
 | |
|     return IPC_FAIL(this, "aURI must not be null.");
 | |
|   }
 | |
|   nsHyphenationManager::Instance()->ShareHyphDictToProcess(
 | |
|       aURI, Pid(), aOutHandle, aOutSize);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvGraphicsError(
 | |
|     const nsACString& aError) {
 | |
|   if (gfx::LogForwarder* lf = gfx::Factory::GetLogForwarder()) {
 | |
|     std::stringstream message;
 | |
|     message << "CP+" << aError;
 | |
|     lf->UpdateStringsVector(message.str());
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvBeginDriverCrashGuard(
 | |
|     const uint32_t& aGuardType, bool* aOutCrashed) {
 | |
|   // Only one driver crash guard should be active at a time, per-process.
 | |
|   MOZ_ASSERT(!mDriverCrashGuard);
 | |
| 
 | |
|   UniquePtr<gfx::DriverCrashGuard> guard;
 | |
|   switch (gfx::CrashGuardType(aGuardType)) {
 | |
|     case gfx::CrashGuardType::D3D11Layers:
 | |
|       guard = MakeUnique<gfx::D3D11LayersCrashGuard>(this);
 | |
|       break;
 | |
|     case gfx::CrashGuardType::GLContext:
 | |
|       guard = MakeUnique<gfx::GLContextCrashGuard>(this);
 | |
|       break;
 | |
|     case gfx::CrashGuardType::WMFVPXVideo:
 | |
|       guard = MakeUnique<gfx::WMFVPXVideoCrashGuard>(this);
 | |
|       break;
 | |
|     default:
 | |
|       return IPC_FAIL(this, "unknown crash guard type");
 | |
|   }
 | |
| 
 | |
|   if (guard->Crashed()) {
 | |
|     *aOutCrashed = true;
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   *aOutCrashed = false;
 | |
|   mDriverCrashGuard = std::move(guard);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvEndDriverCrashGuard(
 | |
|     const uint32_t& aGuardType) {
 | |
|   mDriverCrashGuard = nullptr;
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvNotifyBenchmarkResult(
 | |
|     const nsAString& aCodecName, const uint32_t& aDecodeFPS)
 | |
| 
 | |
| {
 | |
|   if (aCodecName.EqualsLiteral("VP9")) {
 | |
|     Preferences::SetUint(VP9Benchmark::sBenchmarkFpsPref, aDecodeFPS);
 | |
|     Preferences::SetUint(VP9Benchmark::sBenchmarkFpsVersionCheck,
 | |
|                          VP9Benchmark::sBenchmarkVersionID);
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvNotifyPushObservers(
 | |
|     const nsACString& aScope, nsIPrincipal* aPrincipal,
 | |
|     const nsAString& aMessageId) {
 | |
|   if (!aPrincipal) {
 | |
|     return IPC_FAIL(this, "No principal");
 | |
|   }
 | |
| 
 | |
|   if (!ValidatePrincipal(aPrincipal)) {
 | |
|     LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __func__);
 | |
|   }
 | |
|   PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId, Nothing());
 | |
|   Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers()));
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvNotifyPushObserversWithData(
 | |
|     const nsACString& aScope, nsIPrincipal* aPrincipal,
 | |
|     const nsAString& aMessageId, nsTArray<uint8_t>&& aData) {
 | |
|   if (!aPrincipal) {
 | |
|     return IPC_FAIL(this, "No principal");
 | |
|   }
 | |
| 
 | |
|   if (!ValidatePrincipal(aPrincipal)) {
 | |
|     LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __func__);
 | |
|   }
 | |
|   PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId,
 | |
|                                    Some(std::move(aData)));
 | |
|   Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers()));
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult
 | |
| ContentParent::RecvNotifyPushSubscriptionChangeObservers(
 | |
|     const nsACString& aScope, nsIPrincipal* aPrincipal) {
 | |
|   if (!aPrincipal) {
 | |
|     return IPC_FAIL(this, "No principal");
 | |
|   }
 | |
| 
 | |
|   if (!ValidatePrincipal(aPrincipal)) {
 | |
|     LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __func__);
 | |
|   }
 | |
|   PushSubscriptionChangeDispatcher dispatcher(aScope, aPrincipal);
 | |
|   Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers()));
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvPushError(const nsACString& aScope,
 | |
|                                                      nsIPrincipal* aPrincipal,
 | |
|                                                      const nsAString& aMessage,
 | |
|                                                      const uint32_t& aFlags) {
 | |
|   if (!aPrincipal) {
 | |
|     return IPC_FAIL(this, "No principal");
 | |
|   }
 | |
| 
 | |
|   if (!ValidatePrincipal(aPrincipal)) {
 | |
|     LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __func__);
 | |
|   }
 | |
|   PushErrorDispatcher dispatcher(aScope, aPrincipal, aMessage, aFlags);
 | |
|   Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers()));
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult
 | |
| ContentParent::RecvNotifyPushSubscriptionModifiedObservers(
 | |
|     const nsACString& aScope, nsIPrincipal* aPrincipal) {
 | |
|   if (!aPrincipal) {
 | |
|     return IPC_FAIL(this, "No principal");
 | |
|   }
 | |
| 
 | |
|   if (!ValidatePrincipal(aPrincipal)) {
 | |
|     LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __func__);
 | |
|   }
 | |
|   PushSubscriptionModifiedDispatcher dispatcher(aScope, aPrincipal);
 | |
|   Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObservers()));
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| /* static */
 | |
| void ContentParent::BroadcastBlobURLRegistration(
 | |
|     const nsACString& aURI, BlobImpl* aBlobImpl, nsIPrincipal* aPrincipal,
 | |
|     const Maybe<nsID>& aAgentClusterId, ContentParent* aIgnoreThisCP) {
 | |
|   uint64_t originHash = ComputeLoadedOriginHash(aPrincipal);
 | |
| 
 | |
|   bool toBeSent =
 | |
|       BlobURLProtocolHandler::IsBlobURLBroadcastPrincipal(aPrincipal);
 | |
| 
 | |
|   nsCString uri(aURI);
 | |
| 
 | |
|   for (auto* cp : AllProcesses(eLive)) {
 | |
|     if (cp != aIgnoreThisCP) {
 | |
|       if (!toBeSent && !cp->mLoadedOriginHashes.Contains(originHash)) {
 | |
|         continue;
 | |
|       }
 | |
| 
 | |
|       nsresult rv = cp->TransmitPermissionsForPrincipal(aPrincipal);
 | |
|       if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       IPCBlob ipcBlob;
 | |
|       rv = IPCBlobUtils::Serialize(aBlobImpl, ipcBlob);
 | |
|       if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       Unused << cp->SendBlobURLRegistration(uri, ipcBlob, aPrincipal,
 | |
|                                             aAgentClusterId);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| /* static */
 | |
| void ContentParent::BroadcastBlobURLUnregistration(
 | |
|     const nsACString& aURI, nsIPrincipal* aPrincipal,
 | |
|     ContentParent* aIgnoreThisCP) {
 | |
|   uint64_t originHash = ComputeLoadedOriginHash(aPrincipal);
 | |
| 
 | |
|   bool toBeSent =
 | |
|       BlobURLProtocolHandler::IsBlobURLBroadcastPrincipal(aPrincipal);
 | |
| 
 | |
|   nsCString uri(aURI);
 | |
| 
 | |
|   for (auto* cp : AllProcesses(eLive)) {
 | |
|     if (cp != aIgnoreThisCP &&
 | |
|         (toBeSent || cp->mLoadedOriginHashes.Contains(originHash))) {
 | |
|       Unused << cp->SendBlobURLUnregistration(uri);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvStoreAndBroadcastBlobURLRegistration(
 | |
|     const nsACString& aURI, const IPCBlob& aBlob, nsIPrincipal* aPrincipal,
 | |
|     const Maybe<nsID>& aAgentClusterId) {
 | |
|   if (!aPrincipal) {
 | |
|     return IPC_FAIL(this, "No principal");
 | |
|   }
 | |
| 
 | |
|   if (!ValidatePrincipal(aPrincipal, {ValidatePrincipalOptions::AllowSystem})) {
 | |
|     LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __func__);
 | |
|   }
 | |
|   RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(aBlob);
 | |
|   if (NS_WARN_IF(!blobImpl)) {
 | |
|     return IPC_FAIL(this, "Blob deserialization failed.");
 | |
|   }
 | |
| 
 | |
|   BlobURLProtocolHandler::AddDataEntry(aURI, aPrincipal, aAgentClusterId,
 | |
|                                        blobImpl);
 | |
|   BroadcastBlobURLRegistration(aURI, blobImpl, aPrincipal, aAgentClusterId,
 | |
|                                this);
 | |
| 
 | |
|   // We want to store this blobURL, so we can unregister it if the child
 | |
|   // crashes.
 | |
|   mBlobURLs.AppendElement(aURI);
 | |
| 
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult
 | |
| ContentParent::RecvUnstoreAndBroadcastBlobURLUnregistration(
 | |
|     const nsACString& aURI, nsIPrincipal* aPrincipal) {
 | |
|   if (!aPrincipal) {
 | |
|     return IPC_FAIL(this, "No principal");
 | |
|   }
 | |
| 
 | |
|   if (!ValidatePrincipal(aPrincipal, {ValidatePrincipalOptions::AllowSystem})) {
 | |
|     LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __func__);
 | |
|   }
 | |
|   BlobURLProtocolHandler::RemoveDataEntry(aURI, false /* Don't broadcast */);
 | |
|   BroadcastBlobURLUnregistration(aURI, aPrincipal, this);
 | |
|   mBlobURLs.RemoveElement(aURI);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvGetA11yContentId(
 | |
|     uint32_t* aContentId) {
 | |
| #if defined(XP_WIN) && defined(ACCESSIBILITY)
 | |
|   *aContentId = a11y::MsaaAccessible::GetContentProcessIdFor(ChildID());
 | |
|   MOZ_ASSERT(*aContentId);
 | |
|   return IPC_OK();
 | |
| #else
 | |
|   return IPC_FAIL_NO_REASON(this);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvA11yHandlerControl(
 | |
|     const uint32_t& aPid, const IHandlerControlHolder& aHandlerControl) {
 | |
| #if defined(XP_WIN) && defined(ACCESSIBILITY)
 | |
|   MOZ_ASSERT(!aHandlerControl.IsNull());
 | |
|   RefPtr<IHandlerControl> proxy(aHandlerControl.Get());
 | |
|   a11y::AccessibleWrap::SetHandlerControl(aPid, std::move(proxy));
 | |
|   return IPC_OK();
 | |
| #else
 | |
|   return IPC_FAIL_NO_REASON(this);
 | |
| #endif
 | |
| }
 | |
| 
 | |
| bool ContentParent::HandleWindowsMessages(const Message& aMsg) const {
 | |
|   MOZ_ASSERT(aMsg.is_sync());
 | |
| 
 | |
| #ifdef ACCESSIBILITY
 | |
|   // a11y messages can be triggered by windows messages, which means if we
 | |
|   // allow handling windows messages while we wait for the response to a sync
 | |
|   // a11y message we can reenter the ipc message sending code.
 | |
|   if (a11y::PDocAccessible::PDocAccessibleStart < aMsg.type() &&
 | |
|       a11y::PDocAccessible::PDocAccessibleEnd > aMsg.type()) {
 | |
|     return false;
 | |
|   }
 | |
| #endif
 | |
| 
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvGetFilesRequest(
 | |
|     const nsID& aUUID, const nsAString& aDirectoryPath,
 | |
|     const bool& aRecursiveFlag) {
 | |
|   MOZ_ASSERT(!mGetFilesPendingRequests.GetWeak(aUUID));
 | |
| 
 | |
|   if (!mozilla::Preferences::GetBool("dom.filesystem.pathcheck.disabled",
 | |
|                                      false)) {
 | |
|     RefPtr<FileSystemSecurity> fss = FileSystemSecurity::Get();
 | |
|     if (!fss) {
 | |
|       return IPC_FAIL(this, "Failed to get FileSystemSecurity.");
 | |
|     }
 | |
| 
 | |
|     if (!fss->ContentProcessHasAccessTo(ChildID(), aDirectoryPath)) {
 | |
|       return IPC_FAIL(this, "ContentProcessHasAccessTo failed.");
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   ErrorResult rv;
 | |
|   RefPtr<GetFilesHelper> helper = GetFilesHelperParent::Create(
 | |
|       aUUID, aDirectoryPath, aRecursiveFlag, this, rv);
 | |
| 
 | |
|   if (NS_WARN_IF(rv.Failed())) {
 | |
|     if (!SendGetFilesResponse(aUUID,
 | |
|                               GetFilesResponseFailure(rv.StealNSResult()))) {
 | |
|       return IPC_FAIL(this, "SendGetFilesResponse failed.");
 | |
|     }
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   mGetFilesPendingRequests.InsertOrUpdate(aUUID, std::move(helper));
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvDeleteGetFilesRequest(
 | |
|     const nsID& aUUID) {
 | |
|   mGetFilesPendingRequests.Remove(aUUID);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| void ContentParent::SendGetFilesResponseAndForget(
 | |
|     const nsID& aUUID, const GetFilesResponseResult& aResult) {
 | |
|   if (mGetFilesPendingRequests.Remove(aUUID)) {
 | |
|     Unused << SendGetFilesResponse(aUUID, aResult);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void ContentParent::PaintTabWhileInterruptingJS(
 | |
|     BrowserParent* aBrowserParent, const layers::LayersObserverEpoch& aEpoch) {
 | |
|   if (!mHangMonitorActor) {
 | |
|     return;
 | |
|   }
 | |
|   ProcessHangMonitor::PaintWhileInterruptingJS(mHangMonitorActor,
 | |
|                                                aBrowserParent, aEpoch);
 | |
| }
 | |
| 
 | |
| void ContentParent::UnloadLayersWhileInterruptingJS(
 | |
|     BrowserParent* aBrowserParent, const layers::LayersObserverEpoch& aEpoch) {
 | |
|   if (!mHangMonitorActor) {
 | |
|     return;
 | |
|   }
 | |
|   ProcessHangMonitor::UnloadLayersWhileInterruptingJS(mHangMonitorActor,
 | |
|                                                       aBrowserParent, aEpoch);
 | |
| }
 | |
| 
 | |
| void ContentParent::CancelContentJSExecutionIfRunning(
 | |
|     BrowserParent* aBrowserParent, nsIRemoteTab::NavigationType aNavigationType,
 | |
|     const CancelContentJSOptions& aCancelContentJSOptions) {
 | |
|   if (!mHangMonitorActor) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   ProcessHangMonitor::CancelContentJSExecutionIfRunning(
 | |
|       mHangMonitorActor, aBrowserParent, aNavigationType,
 | |
|       aCancelContentJSOptions);
 | |
| }
 | |
| 
 | |
| void ContentParent::UpdateCookieStatus(nsIChannel* aChannel) {
 | |
|   PNeckoParent* neckoParent = LoneManagedOrNullAsserts(ManagedPNeckoParent());
 | |
|   PCookieServiceParent* csParent =
 | |
|       LoneManagedOrNullAsserts(neckoParent->ManagedPCookieServiceParent());
 | |
|   if (csParent) {
 | |
|     auto* cs = static_cast<CookieServiceParent*>(csParent);
 | |
|     cs->TrackCookieLoad(aChannel);
 | |
|   }
 | |
| }
 | |
| 
 | |
| nsresult ContentParent::AboutToLoadHttpFtpDocumentForChild(
 | |
|     nsIChannel* aChannel, bool* aShouldWaitForPermissionCookieUpdate) {
 | |
|   MOZ_ASSERT(aChannel);
 | |
| 
 | |
|   if (aShouldWaitForPermissionCookieUpdate) {
 | |
|     *aShouldWaitForPermissionCookieUpdate = false;
 | |
|   }
 | |
| 
 | |
|   nsresult rv;
 | |
|   bool isDocument = aChannel->IsDocument();
 | |
|   if (!isDocument) {
 | |
|     // We may be looking at a nsIHttpChannel which has isMainDocumentChannel set
 | |
|     // (e.g. the internal http channel for a view-source: load.).
 | |
|     nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
 | |
|     if (httpChannel) {
 | |
|       rv = httpChannel->GetIsMainDocumentChannel(&isDocument);
 | |
|       NS_ENSURE_SUCCESS(rv, rv);
 | |
|     }
 | |
|   }
 | |
|   if (!isDocument) {
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   // Get the principal for the channel result, so that we can get the permission
 | |
|   // key for the document which will be created from this response.
 | |
|   nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
 | |
|   if (NS_WARN_IF(!ssm)) {
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsIPrincipal> principal;
 | |
|   nsCOMPtr<nsIPrincipal> partitionedPrincipal;
 | |
|   rv = ssm->GetChannelResultPrincipals(aChannel, getter_AddRefs(principal),
 | |
|                                        getter_AddRefs(partitionedPrincipal));
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|   // Let the caller know we're going to send main thread IPC for updating
 | |
|   // permisssions/cookies.
 | |
|   if (aShouldWaitForPermissionCookieUpdate) {
 | |
|     *aShouldWaitForPermissionCookieUpdate = true;
 | |
|   }
 | |
| 
 | |
|   TransmitBlobURLsForPrincipal(principal);
 | |
| 
 | |
|   // Tranmit permissions for both regular and partitioned principal so that the
 | |
|   // content process can get permissions for the partitioned principal. For
 | |
|   // example, the desk-notification permission for a partitioned service worker.
 | |
|   rv = TransmitPermissionsForPrincipal(principal);
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|   rv = TransmitPermissionsForPrincipal(partitionedPrincipal);
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|   nsLoadFlags newLoadFlags;
 | |
|   aChannel->GetLoadFlags(&newLoadFlags);
 | |
|   if (newLoadFlags & nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE) {
 | |
|     UpdateCookieStatus(aChannel);
 | |
|   }
 | |
| 
 | |
|   RefPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
 | |
|   RefPtr<BrowsingContext> browsingContext;
 | |
|   rv = loadInfo->GetTargetBrowsingContext(getter_AddRefs(browsingContext));
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|   if (!NextGenLocalStorageEnabled()) {
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   if (principal->GetIsContentPrincipal()) {
 | |
|     nsCOMPtr<nsILocalStorageManager> lsm =
 | |
|         do_GetService("@mozilla.org/dom/localStorage-manager;1");
 | |
|     if (NS_WARN_IF(!lsm)) {
 | |
|       return NS_ERROR_FAILURE;
 | |
|     }
 | |
| 
 | |
|     nsCOMPtr<nsIPrincipal> storagePrincipal;
 | |
|     rv = ssm->GetChannelResultStoragePrincipal(
 | |
|         aChannel, getter_AddRefs(storagePrincipal));
 | |
|     NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|     RefPtr<Promise> dummy;
 | |
|     rv = lsm->Preload(storagePrincipal, nullptr, getter_AddRefs(dummy));
 | |
|     if (NS_FAILED(rv)) {
 | |
|       NS_WARNING("Failed to preload local storage!");
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult ContentParent::TransmitPermissionsForPrincipal(
 | |
|     nsIPrincipal* aPrincipal) {
 | |
|   // Create the key, and send it down to the content process.
 | |
|   nsTArray<std::pair<nsCString, nsCString>> pairs =
 | |
|       PermissionManager::GetAllKeysForPrincipal(aPrincipal);
 | |
|   MOZ_ASSERT(pairs.Length() >= 1);
 | |
|   for (auto& pair : pairs) {
 | |
|     EnsurePermissionsByKey(pair.first, pair.second);
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| void ContentParent::TransmitBlobURLsForPrincipal(nsIPrincipal* aPrincipal) {
 | |
|   // If we're already broadcasting BlobURLs with this principal, we don't need
 | |
|   // to send them here.
 | |
|   if (BlobURLProtocolHandler::IsBlobURLBroadcastPrincipal(aPrincipal)) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   // We shouldn't have any Blob URLs with expanded principals, so transmit URLs
 | |
|   // for each principal in the AllowList instead.
 | |
|   if (nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aPrincipal)) {
 | |
|     for (const auto& prin : ep->AllowList()) {
 | |
|       TransmitBlobURLsForPrincipal(prin);
 | |
|     }
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   uint64_t originHash = ComputeLoadedOriginHash(aPrincipal);
 | |
| 
 | |
|   if (!mLoadedOriginHashes.Contains(originHash)) {
 | |
|     mLoadedOriginHashes.AppendElement(originHash);
 | |
| 
 | |
|     nsTArray<BlobURLRegistrationData> registrations;
 | |
|     BlobURLProtocolHandler::ForEachBlobURL(
 | |
|         [&](BlobImpl* aBlobImpl, nsIPrincipal* aBlobPrincipal,
 | |
|             const Maybe<nsID>& aAgentClusterId, const nsACString& aURI,
 | |
|             bool aRevoked) {
 | |
|           // This check uses `ComputeLoadedOriginHash` to compare, rather than
 | |
|           // doing the more accurate `Equals` check, as it needs to match the
 | |
|           // behaviour of the logic to broadcast new registrations.
 | |
|           if (originHash != ComputeLoadedOriginHash(aBlobPrincipal)) {
 | |
|             return true;
 | |
|           }
 | |
| 
 | |
|           IPCBlob ipcBlob;
 | |
|           nsresult rv = IPCBlobUtils::Serialize(aBlobImpl, ipcBlob);
 | |
|           if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|             return false;
 | |
|           }
 | |
| 
 | |
|           registrations.AppendElement(
 | |
|               BlobURLRegistrationData(nsCString(aURI), ipcBlob, aBlobPrincipal,
 | |
|                                       aAgentClusterId, aRevoked));
 | |
| 
 | |
|           rv = TransmitPermissionsForPrincipal(aBlobPrincipal);
 | |
|           Unused << NS_WARN_IF(NS_FAILED(rv));
 | |
|           return true;
 | |
|         });
 | |
| 
 | |
|     if (!registrations.IsEmpty()) {
 | |
|       Unused << SendInitBlobURLs(registrations);
 | |
|     }
 | |
|   }
 | |
| }
 | |
| 
 | |
| void ContentParent::TransmitBlobDataIfBlobURL(nsIURI* aURI) {
 | |
|   MOZ_ASSERT(aURI);
 | |
| 
 | |
|   nsCOMPtr<nsIPrincipal> principal;
 | |
|   if (BlobURLProtocolHandler::GetBlobURLPrincipal(aURI,
 | |
|                                                   getter_AddRefs(principal))) {
 | |
|     TransmitBlobURLsForPrincipal(principal);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void ContentParent::EnsurePermissionsByKey(const nsACString& aKey,
 | |
|                                            const nsACString& aOrigin) {
 | |
|   // NOTE: Make sure to initialize the permission manager before updating the
 | |
|   // mActivePermissionKeys list. If the permission manager is being initialized
 | |
|   // by this call to GetPermissionManager, and we've added the key to
 | |
|   // mActivePermissionKeys, then the permission manager will send down a
 | |
|   // SendAddPermission before receiving the SendSetPermissionsWithKey message.
 | |
|   RefPtr<PermissionManager> permManager = PermissionManager::GetInstance();
 | |
|   if (!permManager) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   if (!mActivePermissionKeys.EnsureInserted(aKey)) {
 | |
|     return;
 | |
|   }
 | |
| 
 | |
|   nsTArray<IPC::Permission> perms;
 | |
|   if (permManager->GetPermissionsFromOriginOrKey(aOrigin, aKey, perms)) {
 | |
|     Unused << SendSetPermissionsWithKey(aKey, perms);
 | |
|   }
 | |
| }
 | |
| 
 | |
| bool ContentParent::NeedsPermissionsUpdate(
 | |
|     const nsACString& aPermissionKey) const {
 | |
|   return mActivePermissionKeys.Contains(aPermissionKey);
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvAccumulateChildHistograms(
 | |
|     nsTArray<HistogramAccumulation>&& aAccumulations) {
 | |
|   TelemetryIPC::AccumulateChildHistograms(GetTelemetryProcessID(mRemoteType),
 | |
|                                           aAccumulations);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvAccumulateChildKeyedHistograms(
 | |
|     nsTArray<KeyedHistogramAccumulation>&& aAccumulations) {
 | |
|   TelemetryIPC::AccumulateChildKeyedHistograms(
 | |
|       GetTelemetryProcessID(mRemoteType), aAccumulations);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvUpdateChildScalars(
 | |
|     nsTArray<ScalarAction>&& aScalarActions) {
 | |
|   TelemetryIPC::UpdateChildScalars(GetTelemetryProcessID(mRemoteType),
 | |
|                                    aScalarActions);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvUpdateChildKeyedScalars(
 | |
|     nsTArray<KeyedScalarAction>&& aScalarActions) {
 | |
|   TelemetryIPC::UpdateChildKeyedScalars(GetTelemetryProcessID(mRemoteType),
 | |
|                                         aScalarActions);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvRecordChildEvents(
 | |
|     nsTArray<mozilla::Telemetry::ChildEventData>&& aEvents) {
 | |
|   TelemetryIPC::RecordChildEvents(GetTelemetryProcessID(mRemoteType), aEvents);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvRecordDiscardedData(
 | |
|     const mozilla::Telemetry::DiscardedData& aDiscardedData) {
 | |
|   TelemetryIPC::RecordDiscardedData(GetTelemetryProcessID(mRemoteType),
 | |
|                                     aDiscardedData);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvRecordPageLoadEvent(
 | |
|     const mozilla::glean::perf::PageLoadExtra& aPageLoadEventExtra) {
 | |
|   mozilla::glean::perf::page_load.Record(mozilla::Some(aPageLoadEventExtra));
 | |
| 
 | |
|   // Send the PageLoadPing after every 30 page loads, or on startup.
 | |
|   if (++sPageLoadEventCounter >= 30) {
 | |
|     NS_SUCCEEDED(NS_DispatchToMainThreadQueue(
 | |
|         NS_NewRunnableFunction(
 | |
|             "PageLoadPingIdleTask",
 | |
|             [] { mozilla::glean_pings::Pageload.Submit("threshold"_ns); }),
 | |
|         EventQueuePriority::Idle));
 | |
|     sPageLoadEventCounter = 0;
 | |
|   }
 | |
| 
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| //////////////////////////////////////////////////////////////////
 | |
| // PURLClassifierParent
 | |
| 
 | |
| PURLClassifierParent* ContentParent::AllocPURLClassifierParent(
 | |
|     nsIPrincipal* aPrincipal, bool* aSuccess) {
 | |
|   MOZ_ASSERT(NS_IsMainThread());
 | |
| 
 | |
|   *aSuccess = true;
 | |
|   RefPtr<URLClassifierParent> actor = new URLClassifierParent();
 | |
|   return actor.forget().take();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvPURLClassifierConstructor(
 | |
|     PURLClassifierParent* aActor, nsIPrincipal* aPrincipal, bool* aSuccess) {
 | |
|   MOZ_ASSERT(NS_IsMainThread());
 | |
|   MOZ_ASSERT(aActor);
 | |
|   *aSuccess = false;
 | |
| 
 | |
|   auto* actor = static_cast<URLClassifierParent*>(aActor);
 | |
|   nsCOMPtr<nsIPrincipal> principal(aPrincipal);
 | |
|   if (!principal) {
 | |
|     actor->ClassificationFailed();
 | |
|     return IPC_OK();
 | |
|   }
 | |
|   if (!ValidatePrincipal(aPrincipal)) {
 | |
|     LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __func__);
 | |
|   }
 | |
|   return actor->StartClassify(principal, aSuccess);
 | |
| }
 | |
| 
 | |
| bool ContentParent::DeallocPURLClassifierParent(PURLClassifierParent* aActor) {
 | |
|   MOZ_ASSERT(NS_IsMainThread());
 | |
|   MOZ_ASSERT(aActor);
 | |
| 
 | |
|   RefPtr<URLClassifierParent> actor =
 | |
|       dont_AddRef(static_cast<URLClassifierParent*>(aActor));
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| //////////////////////////////////////////////////////////////////
 | |
| // PURLClassifierLocalParent
 | |
| 
 | |
| PURLClassifierLocalParent* ContentParent::AllocPURLClassifierLocalParent(
 | |
|     nsIURI* aURI, const nsTArray<IPCURLClassifierFeature>& aFeatures) {
 | |
|   MOZ_ASSERT(NS_IsMainThread());
 | |
| 
 | |
|   RefPtr<URLClassifierLocalParent> actor = new URLClassifierLocalParent();
 | |
|   return actor.forget().take();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvPURLClassifierLocalConstructor(
 | |
|     PURLClassifierLocalParent* aActor, nsIURI* aURI,
 | |
|     nsTArray<IPCURLClassifierFeature>&& aFeatures) {
 | |
|   MOZ_ASSERT(NS_IsMainThread());
 | |
|   MOZ_ASSERT(aActor);
 | |
| 
 | |
|   nsTArray<IPCURLClassifierFeature> features = std::move(aFeatures);
 | |
| 
 | |
|   if (!aURI) {
 | |
|     return IPC_FAIL(this, "aURI should not be null");
 | |
|   }
 | |
| 
 | |
|   auto* actor = static_cast<URLClassifierLocalParent*>(aActor);
 | |
|   return actor->StartClassify(aURI, features);
 | |
| }
 | |
| 
 | |
| bool ContentParent::DeallocPURLClassifierLocalParent(
 | |
|     PURLClassifierLocalParent* aActor) {
 | |
|   MOZ_ASSERT(NS_IsMainThread());
 | |
|   MOZ_ASSERT(aActor);
 | |
| 
 | |
|   RefPtr<URLClassifierLocalParent> actor =
 | |
|       dont_AddRef(static_cast<URLClassifierLocalParent*>(aActor));
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| PLoginReputationParent* ContentParent::AllocPLoginReputationParent(
 | |
|     nsIURI* aURI) {
 | |
|   MOZ_ASSERT(NS_IsMainThread());
 | |
| 
 | |
|   RefPtr<LoginReputationParent> actor = new LoginReputationParent();
 | |
|   return actor.forget().take();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvPLoginReputationConstructor(
 | |
|     PLoginReputationParent* aActor, nsIURI* aURI) {
 | |
|   MOZ_ASSERT(NS_IsMainThread());
 | |
|   MOZ_ASSERT(aActor);
 | |
| 
 | |
|   if (!aURI) {
 | |
|     return IPC_FAIL(this, "aURI should not be null");
 | |
|   }
 | |
| 
 | |
|   auto* actor = static_cast<LoginReputationParent*>(aActor);
 | |
|   return actor->QueryReputation(aURI);
 | |
| }
 | |
| 
 | |
| bool ContentParent::DeallocPLoginReputationParent(
 | |
|     PLoginReputationParent* aActor) {
 | |
|   MOZ_ASSERT(NS_IsMainThread());
 | |
|   MOZ_ASSERT(aActor);
 | |
| 
 | |
|   RefPtr<LoginReputationParent> actor =
 | |
|       dont_AddRef(static_cast<LoginReputationParent*>(aActor));
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| PSessionStorageObserverParent*
 | |
| ContentParent::AllocPSessionStorageObserverParent() {
 | |
|   MOZ_ASSERT(NS_IsMainThread());
 | |
| 
 | |
|   return mozilla::dom::AllocPSessionStorageObserverParent();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvPSessionStorageObserverConstructor(
 | |
|     PSessionStorageObserverParent* aActor) {
 | |
|   MOZ_ASSERT(NS_IsMainThread());
 | |
|   MOZ_ASSERT(aActor);
 | |
| 
 | |
|   if (!mozilla::dom::RecvPSessionStorageObserverConstructor(aActor)) {
 | |
|     return IPC_FAIL(this, "RecvPSessionStorageObserverConstructor failed.");
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| bool ContentParent::DeallocPSessionStorageObserverParent(
 | |
|     PSessionStorageObserverParent* aActor) {
 | |
|   MOZ_ASSERT(NS_IsMainThread());
 | |
|   MOZ_ASSERT(aActor);
 | |
| 
 | |
|   return mozilla::dom::DeallocPSessionStorageObserverParent(aActor);
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvDeviceReset() {
 | |
|   GPUProcessManager* pm = GPUProcessManager::Get();
 | |
|   if (pm) {
 | |
|     pm->SimulateDeviceReset();
 | |
|   }
 | |
| 
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvBHRThreadHang(
 | |
|     const HangDetails& aDetails) {
 | |
|   nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
 | |
|   if (obs) {
 | |
|     // Copy the HangDetails recieved over the network into a nsIHangDetails, and
 | |
|     // then fire our own observer notification.
 | |
|     // XXX: We should be able to avoid this potentially expensive copy here by
 | |
|     // moving our deserialized argument.
 | |
|     nsCOMPtr<nsIHangDetails> hangDetails =
 | |
|         new nsHangDetails(HangDetails(aDetails), PersistedToDisk::No);
 | |
|     obs->NotifyObservers(hangDetails, "bhr-thread-hang", nullptr);
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvAddCertException(
 | |
|     nsIX509Cert* aCert, const nsACString& aHostName, int32_t aPort,
 | |
|     const OriginAttributes& aOriginAttributes, bool aIsTemporary,
 | |
|     AddCertExceptionResolver&& aResolver) {
 | |
|   nsCOMPtr<nsICertOverrideService> overrideService =
 | |
|       do_GetService(NS_CERTOVERRIDE_CONTRACTID);
 | |
|   if (!overrideService) {
 | |
|     aResolver(NS_ERROR_FAILURE);
 | |
|     return IPC_OK();
 | |
|   }
 | |
|   nsresult rv = overrideService->RememberValidityOverride(
 | |
|       aHostName, aPort, aOriginAttributes, aCert, aIsTemporary);
 | |
|   aResolver(rv);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult
 | |
| ContentParent::RecvAutomaticStorageAccessPermissionCanBeGranted(
 | |
|     nsIPrincipal* aPrincipal,
 | |
|     AutomaticStorageAccessPermissionCanBeGrantedResolver&& aResolver) {
 | |
|   if (!aPrincipal) {
 | |
|     return IPC_FAIL(this, "No principal");
 | |
|   }
 | |
| 
 | |
|   if (!ValidatePrincipal(aPrincipal)) {
 | |
|     LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __func__);
 | |
|   }
 | |
|   aResolver(Document::AutomaticStorageAccessPermissionCanBeGranted(aPrincipal));
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult
 | |
| ContentParent::RecvStorageAccessPermissionGrantedForOrigin(
 | |
|     uint64_t aTopLevelWindowId,
 | |
|     const MaybeDiscarded<BrowsingContext>& aParentContext,
 | |
|     nsIPrincipal* aTrackingPrincipal, const nsACString& aTrackingOrigin,
 | |
|     const int& aAllowMode,
 | |
|     const Maybe<ContentBlockingNotifier::StorageAccessPermissionGrantedReason>&
 | |
|         aReason,
 | |
|     StorageAccessPermissionGrantedForOriginResolver&& aResolver) {
 | |
|   if (aParentContext.IsNullOrDiscarded()) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   if (!aTrackingPrincipal) {
 | |
|     return IPC_FAIL(this, "No principal");
 | |
|   }
 | |
| 
 | |
|   // We only report here if we cannot report the console directly in the content
 | |
|   // process. In that case, the `aReason` would be given a value. Otherwise, it
 | |
|   // will be nothing.
 | |
|   if (aReason) {
 | |
|     ContentBlockingNotifier::ReportUnblockingToConsole(
 | |
|         aParentContext.get_canonical(), NS_ConvertUTF8toUTF16(aTrackingOrigin),
 | |
|         aReason.value());
 | |
|   }
 | |
| 
 | |
|   StorageAccessAPIHelper::SaveAccessForOriginOnParentProcess(
 | |
|       aTopLevelWindowId, aParentContext.get_canonical(), aTrackingPrincipal,
 | |
|       aAllowMode)
 | |
|       ->Then(GetCurrentSerialEventTarget(), __func__,
 | |
|              [aResolver = std::move(aResolver)](
 | |
|                  StorageAccessAPIHelper::ParentAccessGrantPromise::
 | |
|                      ResolveOrRejectValue&& aValue) {
 | |
|                bool success =
 | |
|                    aValue.IsResolve() && NS_SUCCEEDED(aValue.ResolveValue());
 | |
|                aResolver(success);
 | |
|              });
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvCompleteAllowAccessFor(
 | |
|     const MaybeDiscarded<BrowsingContext>& aParentContext,
 | |
|     uint64_t aTopLevelWindowId, nsIPrincipal* aTrackingPrincipal,
 | |
|     const nsACString& aTrackingOrigin, uint32_t aCookieBehavior,
 | |
|     const ContentBlockingNotifier::StorageAccessPermissionGrantedReason&
 | |
|         aReason,
 | |
|     CompleteAllowAccessForResolver&& aResolver) {
 | |
|   if (aParentContext.IsNullOrDiscarded()) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   StorageAccessAPIHelper::CompleteAllowAccessFor(
 | |
|       aParentContext.get_canonical(), aTopLevelWindowId, aTrackingPrincipal,
 | |
|       aTrackingOrigin, aCookieBehavior, aReason, nullptr)
 | |
|       ->Then(GetCurrentSerialEventTarget(), __func__,
 | |
|              [aResolver = std::move(aResolver)](
 | |
|                  StorageAccessAPIHelper::StorageAccessPermissionGrantPromise::
 | |
|                      ResolveOrRejectValue&& aValue) {
 | |
|                Maybe<StorageAccessPromptChoices> choice;
 | |
|                if (aValue.IsResolve()) {
 | |
|                  choice.emplace(static_cast<StorageAccessPromptChoices>(
 | |
|                      aValue.ResolveValue()));
 | |
|                }
 | |
|                aResolver(choice);
 | |
|              });
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvSetAllowStorageAccessRequestFlag(
 | |
|     nsIPrincipal* aEmbeddedPrincipal, nsIURI* aEmbeddingOrigin,
 | |
|     SetAllowStorageAccessRequestFlagResolver&& aResolver) {
 | |
|   MOZ_ASSERT(aEmbeddedPrincipal);
 | |
|   MOZ_ASSERT(aEmbeddingOrigin);
 | |
| 
 | |
|   if (!aEmbeddedPrincipal || !aEmbeddingOrigin) {
 | |
|     aResolver(false);
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   // Get the permission manager and build the key.
 | |
|   RefPtr<PermissionManager> permManager = PermissionManager::GetInstance();
 | |
|   if (!permManager) {
 | |
|     aResolver(false);
 | |
|     return IPC_OK();
 | |
|   }
 | |
|   nsCOMPtr<nsIURI> embeddedURI = aEmbeddedPrincipal->GetURI();
 | |
|   nsCString permissionKey;
 | |
|   bool success = AntiTrackingUtils::CreateStorageRequestPermissionKey(
 | |
|       embeddedURI, permissionKey);
 | |
|   if (!success) {
 | |
|     aResolver(false);
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   // Set the permission to ALLOW for a prefence specified amount of seconds.
 | |
|   // Time units are inconsistent, be careful
 | |
|   int64_t when = (PR_Now() / PR_USEC_PER_MSEC) +
 | |
|                  StaticPrefs::dom_storage_access_forward_declared_lifetime() *
 | |
|                      PR_MSEC_PER_SEC;
 | |
|   nsCOMPtr<nsIPrincipal> principal = BasePrincipal::CreateContentPrincipal(
 | |
|       aEmbeddingOrigin, aEmbeddedPrincipal->OriginAttributesRef());
 | |
|   nsresult rv = permManager->AddFromPrincipal(
 | |
|       principal, permissionKey, nsIPermissionManager::ALLOW_ACTION,
 | |
|       nsIPermissionManager::EXPIRE_TIME, when);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     aResolver(false);
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   // Resolve with success if we set the permission.
 | |
|   aResolver(true);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvTestAllowStorageAccessRequestFlag(
 | |
|     nsIPrincipal* aEmbeddingPrincipal, nsIURI* aEmbeddedOrigin,
 | |
|     TestAllowStorageAccessRequestFlagResolver&& aResolver) {
 | |
|   MOZ_ASSERT(aEmbeddingPrincipal);
 | |
|   MOZ_ASSERT(aEmbeddedOrigin);
 | |
| 
 | |
|   // Get the permission manager and build the key.
 | |
|   RefPtr<PermissionManager> permManager = PermissionManager::GetInstance();
 | |
|   if (!permManager) {
 | |
|     aResolver(false);
 | |
|     return IPC_OK();
 | |
|   }
 | |
|   nsCString requestPermissionKey;
 | |
|   bool success = AntiTrackingUtils::CreateStorageRequestPermissionKey(
 | |
|       aEmbeddedOrigin, requestPermissionKey);
 | |
|   if (!success) {
 | |
|     aResolver(false);
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   // Get the permission and resolve false if it is not set to ALLOW.
 | |
|   uint32_t access = nsIPermissionManager::UNKNOWN_ACTION;
 | |
|   nsresult rv = permManager->TestPermissionFromPrincipal(
 | |
|       aEmbeddingPrincipal, requestPermissionKey, &access);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     aResolver(false);
 | |
|     return IPC_OK();
 | |
|   }
 | |
|   if (access != nsIPermissionManager::ALLOW_ACTION) {
 | |
|     aResolver(false);
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   // Remove the permission, failing if the permission manager fails
 | |
|   rv = permManager->RemoveFromPrincipal(aEmbeddingPrincipal,
 | |
|                                         requestPermissionKey);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     aResolver(false);
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   // At this point, signal to our caller that the permission was set
 | |
|   aResolver(true);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvStoreUserInteractionAsPermission(
 | |
|     nsIPrincipal* aPrincipal) {
 | |
|   if (!aPrincipal) {
 | |
|     return IPC_FAIL(this, "No principal");
 | |
|   }
 | |
| 
 | |
|   if (!ValidatePrincipal(aPrincipal)) {
 | |
|     LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __func__);
 | |
|   }
 | |
|   ContentBlockingUserInteraction::Observe(aPrincipal);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvTestCookiePermissionDecided(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext, nsIPrincipal* aPrincipal,
 | |
|     const TestCookiePermissionDecidedResolver&& aResolver) {
 | |
|   if (aContext.IsNullOrDiscarded()) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   if (!aPrincipal) {
 | |
|     return IPC_FAIL(this, "No principal");
 | |
|   }
 | |
| 
 | |
|   RefPtr<WindowGlobalParent> wgp =
 | |
|       aContext.get_canonical()->GetCurrentWindowGlobal();
 | |
|   nsCOMPtr<nsICookieJarSettings> cjs = wgp->CookieJarSettings();
 | |
| 
 | |
|   Maybe<bool> result =
 | |
|       StorageAccessAPIHelper::CheckCookiesPermittedDecidesStorageAccessAPI(
 | |
|           cjs, aPrincipal);
 | |
|   aResolver(result);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvTestStorageAccessPermission(
 | |
|     nsIPrincipal* aEmbeddingPrincipal, const nsCString& aEmbeddedOrigin,
 | |
|     const TestStorageAccessPermissionResolver&& aResolver) {
 | |
|   // Get the permission manager and build the key.
 | |
|   RefPtr<PermissionManager> permManager = PermissionManager::GetInstance();
 | |
|   if (!permManager) {
 | |
|     aResolver(Nothing());
 | |
|     return IPC_OK();
 | |
|   }
 | |
|   nsCString requestPermissionKey;
 | |
|   AntiTrackingUtils::CreateStoragePermissionKey(aEmbeddedOrigin,
 | |
|                                                 requestPermissionKey);
 | |
| 
 | |
|   // Test the permission
 | |
|   uint32_t access = nsIPermissionManager::UNKNOWN_ACTION;
 | |
|   nsresult rv = permManager->TestPermissionFromPrincipal(
 | |
|       aEmbeddingPrincipal, requestPermissionKey, &access);
 | |
|   if (NS_FAILED(rv)) {
 | |
|     aResolver(Nothing());
 | |
|     return IPC_OK();
 | |
|   }
 | |
|   if (access == nsIPermissionManager::ALLOW_ACTION) {
 | |
|     aResolver(Some(true));
 | |
|   } else if (access == nsIPermissionManager::DENY_ACTION) {
 | |
|     aResolver(Some(false));
 | |
|   } else {
 | |
|     aResolver(Nothing());
 | |
|   }
 | |
| 
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvNotifyMediaPlaybackChanged(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext,
 | |
|     MediaPlaybackState aState) {
 | |
|   if (aContext.IsNullOrDiscarded()) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
|   if (RefPtr<IMediaInfoUpdater> updater =
 | |
|           aContext.get_canonical()->GetMediaController()) {
 | |
|     updater->NotifyMediaPlaybackChanged(aContext.ContextId(), aState);
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvNotifyMediaAudibleChanged(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext, MediaAudibleState aState) {
 | |
|   if (aContext.IsNullOrDiscarded()) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
|   if (RefPtr<IMediaInfoUpdater> updater =
 | |
|           aContext.get_canonical()->GetMediaController()) {
 | |
|     updater->NotifyMediaAudibleChanged(aContext.ContextId(), aState);
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvNotifyPictureInPictureModeChanged(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext, bool aEnabled) {
 | |
|   if (aContext.IsNullOrDiscarded()) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
|   if (RefPtr<MediaController> controller =
 | |
|           aContext.get_canonical()->GetMediaController()) {
 | |
|     controller->SetIsInPictureInPictureMode(aContext.ContextId(), aEnabled);
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvAbortOtherOrientationPendingPromises(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext) {
 | |
|   if (aContext.IsNullOrDiscarded()) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   CanonicalBrowsingContext* context = aContext.get_canonical();
 | |
| 
 | |
|   context->Group()->EachOtherParent(this, [&](ContentParent* aParent) {
 | |
|     Unused << aParent->SendAbortOrientationPendingPromises(context);
 | |
|   });
 | |
| 
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvNotifyMediaSessionUpdated(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext, bool aIsCreated) {
 | |
|   if (aContext.IsNullOrDiscarded()) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   RefPtr<IMediaInfoUpdater> updater =
 | |
|       aContext.get_canonical()->GetMediaController();
 | |
|   if (!updater) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
|   if (aIsCreated) {
 | |
|     updater->NotifySessionCreated(aContext->Id());
 | |
|   } else {
 | |
|     updater->NotifySessionDestroyed(aContext->Id());
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvNotifyUpdateMediaMetadata(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext,
 | |
|     const Maybe<MediaMetadataBase>& aMetadata) {
 | |
|   if (aContext.IsNullOrDiscarded()) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
|   if (RefPtr<IMediaInfoUpdater> updater =
 | |
|           aContext.get_canonical()->GetMediaController()) {
 | |
|     updater->UpdateMetadata(aContext.ContextId(), aMetadata);
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult
 | |
| ContentParent::RecvNotifyMediaSessionPlaybackStateChanged(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext,
 | |
|     MediaSessionPlaybackState aPlaybackState) {
 | |
|   if (aContext.IsNullOrDiscarded()) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
|   if (RefPtr<IMediaInfoUpdater> updater =
 | |
|           aContext.get_canonical()->GetMediaController()) {
 | |
|     updater->SetDeclaredPlaybackState(aContext.ContextId(), aPlaybackState);
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult
 | |
| ContentParent::RecvNotifyMediaSessionSupportedActionChanged(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext, MediaSessionAction aAction,
 | |
|     bool aEnabled) {
 | |
|   if (aContext.IsNullOrDiscarded()) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
|   RefPtr<IMediaInfoUpdater> updater =
 | |
|       aContext.get_canonical()->GetMediaController();
 | |
|   if (!updater) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
|   if (aEnabled) {
 | |
|     updater->EnableAction(aContext.ContextId(), aAction);
 | |
|   } else {
 | |
|     updater->DisableAction(aContext.ContextId(), aAction);
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvNotifyMediaFullScreenState(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext, bool aIsInFullScreen) {
 | |
|   if (aContext.IsNullOrDiscarded()) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
|   if (RefPtr<IMediaInfoUpdater> updater =
 | |
|           aContext.get_canonical()->GetMediaController()) {
 | |
|     updater->NotifyMediaFullScreenState(aContext.ContextId(), aIsInFullScreen);
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvNotifyPositionStateChanged(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext,
 | |
|     const PositionState& aState) {
 | |
|   if (aContext.IsNullOrDiscarded()) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
|   if (RefPtr<IMediaInfoUpdater> updater =
 | |
|           aContext.get_canonical()->GetMediaController()) {
 | |
|     updater->UpdatePositionState(aContext.ContextId(), aState);
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvAddOrRemovePageAwakeRequest(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext,
 | |
|     const bool& aShouldAddCount) {
 | |
|   if (aContext.IsNullOrDiscarded()) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
|   if (aShouldAddCount) {
 | |
|     aContext.get_canonical()->AddPageAwakeRequest();
 | |
|   } else {
 | |
|     aContext.get_canonical()->RemovePageAwakeRequest();
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| #if defined(XP_WIN)
 | |
| mozilla::ipc::IPCResult ContentParent::RecvGetModulesTrust(
 | |
|     ModulePaths&& aModPaths, bool aRunAtNormalPriority,
 | |
|     GetModulesTrustResolver&& aResolver) {
 | |
|   RefPtr<DllServices> dllSvc(DllServices::Get());
 | |
|   dllSvc->GetModulesTrust(std::move(aModPaths), aRunAtNormalPriority)
 | |
|       ->Then(
 | |
|           GetMainThreadSerialEventTarget(), __func__,
 | |
|           [aResolver](ModulesMapResult&& aResult) {
 | |
|             aResolver(Some(ModulesMapResult(std::move(aResult))));
 | |
|           },
 | |
|           [aResolver](nsresult aRv) { aResolver(Nothing()); });
 | |
|   return IPC_OK();
 | |
| }
 | |
| #endif  // defined(XP_WIN)
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvCreateBrowsingContext(
 | |
|     uint64_t aGroupId, BrowsingContext::IPCInitializer&& aInit) {
 | |
|   RefPtr<WindowGlobalParent> parent;
 | |
|   if (aInit.mParentId != 0) {
 | |
|     parent = WindowGlobalParent::GetByInnerWindowId(aInit.mParentId);
 | |
|     if (!parent) {
 | |
|       return IPC_FAIL(this, "Parent doesn't exist in parent process");
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (parent && parent->GetContentParent() != this) {
 | |
|     // We're trying attach a child BrowsingContext to a parent
 | |
|     // WindowContext in another process. This is illegal since the
 | |
|     // only thing that could create that child BrowsingContext is the parent
 | |
|     // window's process.
 | |
|     return IPC_FAIL(this,
 | |
|                     "Must create BrowsingContext from the parent's process");
 | |
|   }
 | |
| 
 | |
|   RefPtr<BrowsingContext> opener;
 | |
|   if (aInit.GetOpenerId() != 0) {
 | |
|     opener = BrowsingContext::Get(aInit.GetOpenerId());
 | |
|     if (!opener) {
 | |
|       return IPC_FAIL(this, "Opener doesn't exist in parent process");
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   RefPtr<BrowsingContext> child = BrowsingContext::Get(aInit.mId);
 | |
|   if (child) {
 | |
|     // This is highly suspicious. BrowsingContexts should only be created once,
 | |
|     // so finding one indicates that someone is doing something they shouldn't.
 | |
|     return IPC_FAIL(this, "A BrowsingContext with this ID already exists");
 | |
|   }
 | |
| 
 | |
|   // Ensure that the passed-in BrowsingContextGroup is valid.
 | |
|   RefPtr<BrowsingContextGroup> group =
 | |
|       BrowsingContextGroup::GetOrCreate(aGroupId);
 | |
|   if (parent && parent->Group() != group) {
 | |
|     if (parent->Group()->Id() != aGroupId) {
 | |
|       return IPC_FAIL(this, "Parent has different group ID");
 | |
|     } else {
 | |
|       return IPC_FAIL(this, "Parent has different group object");
 | |
|     }
 | |
|   }
 | |
|   if (opener && opener->Group() != group) {
 | |
|     if (opener->Group()->Id() != aGroupId) {
 | |
|       return IPC_FAIL(this, "Opener has different group ID");
 | |
|     } else {
 | |
|       return IPC_FAIL(this, "Opener has different group object");
 | |
|     }
 | |
|   }
 | |
|   if (!parent && !opener && !group->Toplevels().IsEmpty()) {
 | |
|     return IPC_FAIL(this, "Unrelated context from child in stale group");
 | |
|   }
 | |
| 
 | |
|   BrowsingContext::CreateFromIPC(std::move(aInit), group, this);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| bool ContentParent::CheckBrowsingContextEmbedder(CanonicalBrowsingContext* aBC,
 | |
|                                                  const char* aOperation) const {
 | |
|   if (!aBC->IsEmbeddedInProcess(ChildID())) {
 | |
|     MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Warning,
 | |
|             ("ParentIPC: Trying to %s out of process context 0x%08" PRIx64,
 | |
|              aOperation, aBC->Id()));
 | |
|     return false;
 | |
|   }
 | |
|   return true;
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvDiscardBrowsingContext(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext, bool aDoDiscard,
 | |
|     DiscardBrowsingContextResolver&& aResolve) {
 | |
|   if (CanonicalBrowsingContext* context =
 | |
|           CanonicalBrowsingContext::Cast(aContext.GetMaybeDiscarded())) {
 | |
|     if (aDoDiscard && !context->IsDiscarded()) {
 | |
|       if (!CheckBrowsingContextEmbedder(context, "discard")) {
 | |
|         return IPC_FAIL(this, "Illegal Discard attempt");
 | |
|       }
 | |
| 
 | |
|       context->Detach(/* aFromIPC */ true);
 | |
|     }
 | |
|     context->AddFinalDiscardListener(aResolve);
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   // Resolve the promise, as we've received and handled the message. This will
 | |
|   // allow the content process to fully-discard references to this BC.
 | |
|   aResolve(true);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| void ContentParent::UnregisterRemoveWorkerActor() {
 | |
|   MOZ_ASSERT(NS_IsMainThread());
 | |
| 
 | |
|   {
 | |
|     MutexAutoLock lock(mThreadsafeHandle->mMutex);
 | |
|     if (--mThreadsafeHandle->mRemoteWorkerActorCount) {
 | |
|       return;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
 | |
|           ("UnregisterRemoveWorkerActor %p", this));
 | |
|   MaybeBeginShutDown();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvWindowClose(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext, bool aTrustedCaller) {
 | |
|   if (aContext.IsNullOrDiscarded()) {
 | |
|     MOZ_LOG(
 | |
|         BrowsingContext::GetLog(), LogLevel::Debug,
 | |
|         ("ParentIPC: Trying to send a message to dead or detached context"));
 | |
|     return IPC_OK();
 | |
|   }
 | |
|   CanonicalBrowsingContext* context = aContext.get_canonical();
 | |
| 
 | |
|   // FIXME Need to check that the sending process has access to the unit of
 | |
|   // related
 | |
|   //       browsing contexts of bc.
 | |
| 
 | |
|   if (ContentParent* cp = context->GetContentParent()) {
 | |
|     Unused << cp->SendWindowClose(context, aTrustedCaller);
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvWindowFocus(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext, CallerType aCallerType,
 | |
|     uint64_t aActionId) {
 | |
|   if (aContext.IsNullOrDiscarded()) {
 | |
|     MOZ_LOG(
 | |
|         BrowsingContext::GetLog(), LogLevel::Debug,
 | |
|         ("ParentIPC: Trying to send a message to dead or detached context"));
 | |
|     return IPC_OK();
 | |
|   }
 | |
|   LOGFOCUS(("ContentParent::RecvWindowFocus actionid: %" PRIu64, aActionId));
 | |
|   CanonicalBrowsingContext* context = aContext.get_canonical();
 | |
| 
 | |
|   if (ContentParent* cp = context->GetContentParent()) {
 | |
|     Unused << cp->SendWindowFocus(context, aCallerType, aActionId);
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvWindowBlur(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext, CallerType aCallerType) {
 | |
|   if (aContext.IsNullOrDiscarded()) {
 | |
|     MOZ_LOG(
 | |
|         BrowsingContext::GetLog(), LogLevel::Debug,
 | |
|         ("ParentIPC: Trying to send a message to dead or detached context"));
 | |
|     return IPC_OK();
 | |
|   }
 | |
|   CanonicalBrowsingContext* context = aContext.get_canonical();
 | |
| 
 | |
|   if (ContentParent* cp = context->GetContentParent()) {
 | |
|     Unused << cp->SendWindowBlur(context, aCallerType);
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvRaiseWindow(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext, CallerType aCallerType,
 | |
|     uint64_t aActionId) {
 | |
|   if (aContext.IsNullOrDiscarded()) {
 | |
|     MOZ_LOG(
 | |
|         BrowsingContext::GetLog(), LogLevel::Debug,
 | |
|         ("ParentIPC: Trying to send a message to dead or detached context"));
 | |
|     return IPC_OK();
 | |
|   }
 | |
|   LOGFOCUS(("ContentParent::RecvRaiseWindow actionid: %" PRIu64, aActionId));
 | |
| 
 | |
|   CanonicalBrowsingContext* context = aContext.get_canonical();
 | |
| 
 | |
|   if (ContentParent* cp = context->GetContentParent()) {
 | |
|     Unused << cp->SendRaiseWindow(context, aCallerType, aActionId);
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvAdjustWindowFocus(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext, bool aIsVisible,
 | |
|     uint64_t aActionId) {
 | |
|   if (aContext.IsNullOrDiscarded()) {
 | |
|     MOZ_LOG(
 | |
|         BrowsingContext::GetLog(), LogLevel::Debug,
 | |
|         ("ParentIPC: Trying to send a message to dead or detached context"));
 | |
|     return IPC_OK();
 | |
|   }
 | |
|   LOGFOCUS(
 | |
|       ("ContentParent::RecvAdjustWindowFocus isVisible %d actionid: %" PRIu64,
 | |
|        aIsVisible, aActionId));
 | |
| 
 | |
|   nsTHashMap<nsPtrHashKey<ContentParent>, bool> processes(2);
 | |
|   processes.InsertOrUpdate(this, true);
 | |
| 
 | |
|   ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
 | |
|   if (cpm) {
 | |
|     CanonicalBrowsingContext* context = aContext.get_canonical();
 | |
|     while (context) {
 | |
|       BrowsingContext* parent = context->GetParent();
 | |
|       if (!parent) {
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|       CanonicalBrowsingContext* canonicalParent = parent->Canonical();
 | |
|       ContentParent* cp = cpm->GetContentProcessById(
 | |
|           ContentParentId(canonicalParent->OwnerProcessId()));
 | |
|       if (cp && !processes.Get(cp)) {
 | |
|         Unused << cp->SendAdjustWindowFocus(context, aIsVisible, aActionId);
 | |
|         processes.InsertOrUpdate(cp, true);
 | |
|       }
 | |
|       context = canonicalParent;
 | |
|     }
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvClearFocus(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext) {
 | |
|   if (aContext.IsNullOrDiscarded()) {
 | |
|     MOZ_LOG(
 | |
|         BrowsingContext::GetLog(), LogLevel::Debug,
 | |
|         ("ParentIPC: Trying to send a message to dead or detached context"));
 | |
|     return IPC_OK();
 | |
|   }
 | |
|   CanonicalBrowsingContext* context = aContext.get_canonical();
 | |
| 
 | |
|   if (ContentParent* cp = context->GetContentParent()) {
 | |
|     Unused << cp->SendClearFocus(context);
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvSetFocusedBrowsingContext(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext, uint64_t aActionId) {
 | |
|   if (aContext.IsNullOrDiscarded()) {
 | |
|     MOZ_LOG(
 | |
|         BrowsingContext::GetLog(), LogLevel::Debug,
 | |
|         ("ParentIPC: Trying to send a message to dead or detached context"));
 | |
|     return IPC_OK();
 | |
|   }
 | |
|   LOGFOCUS(("ContentParent::RecvSetFocusedBrowsingContext actionid: %" PRIu64,
 | |
|             aActionId));
 | |
|   CanonicalBrowsingContext* context = aContext.get_canonical();
 | |
| 
 | |
|   nsFocusManager* fm = nsFocusManager::GetFocusManager();
 | |
|   if (!fm) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   if (!fm->SetFocusedBrowsingContextInChrome(context, aActionId)) {
 | |
|     LOGFOCUS((
 | |
|         "Ignoring out-of-sequence attempt [%p] to set focused browsing context "
 | |
|         "in parent.",
 | |
|         context));
 | |
|     Unused << SendReviseFocusedBrowsingContext(
 | |
|         aActionId, fm->GetFocusedBrowsingContextInChrome(),
 | |
|         fm->GetActionIdForFocusedBrowsingContextInChrome());
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   BrowserParent::UpdateFocusFromBrowsingContext();
 | |
| 
 | |
|   context->Group()->EachOtherParent(this, [&](ContentParent* aParent) {
 | |
|     Unused << aParent->SendSetFocusedBrowsingContext(context, aActionId);
 | |
|   });
 | |
| 
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvSetActiveBrowsingContext(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext, uint64_t aActionId) {
 | |
|   if (aContext.IsNullOrDiscarded()) {
 | |
|     MOZ_LOG(
 | |
|         BrowsingContext::GetLog(), LogLevel::Debug,
 | |
|         ("ParentIPC: Trying to send a message to dead or detached context"));
 | |
|     return IPC_OK();
 | |
|   }
 | |
|   LOGFOCUS(("ContentParent::RecvSetActiveBrowsingContext actionid: %" PRIu64,
 | |
|             aActionId));
 | |
|   CanonicalBrowsingContext* context = aContext.get_canonical();
 | |
| 
 | |
|   nsFocusManager* fm = nsFocusManager::GetFocusManager();
 | |
|   if (!fm) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   if (!fm->SetActiveBrowsingContextInChrome(context, aActionId)) {
 | |
|     LOGFOCUS(
 | |
|         ("Ignoring out-of-sequence attempt [%p] to set active browsing context "
 | |
|          "in parent.",
 | |
|          context));
 | |
|     Unused << SendReviseActiveBrowsingContext(
 | |
|         aActionId, fm->GetActiveBrowsingContextInChrome(),
 | |
|         fm->GetActionIdForActiveBrowsingContextInChrome());
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   context->Group()->EachOtherParent(this, [&](ContentParent* aParent) {
 | |
|     Unused << aParent->SendSetActiveBrowsingContext(context, aActionId);
 | |
|   });
 | |
| 
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvUnsetActiveBrowsingContext(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext, uint64_t aActionId) {
 | |
|   if (aContext.IsNullOrDiscarded()) {
 | |
|     MOZ_LOG(
 | |
|         BrowsingContext::GetLog(), LogLevel::Debug,
 | |
|         ("ParentIPC: Trying to send a message to dead or detached context"));
 | |
|     return IPC_OK();
 | |
|   }
 | |
|   LOGFOCUS(("ContentParent::RecvUnsetActiveBrowsingContext actionid: %" PRIu64,
 | |
|             aActionId));
 | |
|   CanonicalBrowsingContext* context = aContext.get_canonical();
 | |
| 
 | |
|   nsFocusManager* fm = nsFocusManager::GetFocusManager();
 | |
|   if (!fm) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   if (!fm->SetActiveBrowsingContextInChrome(nullptr, aActionId)) {
 | |
|     LOGFOCUS(
 | |
|         ("Ignoring out-of-sequence attempt to unset active browsing context in "
 | |
|          "parent [%p].",
 | |
|          context));
 | |
|     Unused << SendReviseActiveBrowsingContext(
 | |
|         aActionId, fm->GetActiveBrowsingContextInChrome(),
 | |
|         fm->GetActionIdForActiveBrowsingContextInChrome());
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   context->Group()->EachOtherParent(this, [&](ContentParent* aParent) {
 | |
|     Unused << aParent->SendUnsetActiveBrowsingContext(context, aActionId);
 | |
|   });
 | |
| 
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvSetFocusedElement(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext, bool aNeedsFocus) {
 | |
|   if (aContext.IsNullOrDiscarded()) {
 | |
|     MOZ_LOG(
 | |
|         BrowsingContext::GetLog(), LogLevel::Debug,
 | |
|         ("ParentIPC: Trying to send a message to dead or detached context"));
 | |
|     return IPC_OK();
 | |
|   }
 | |
|   LOGFOCUS(("ContentParent::RecvSetFocusedElement"));
 | |
|   CanonicalBrowsingContext* context = aContext.get_canonical();
 | |
| 
 | |
|   if (ContentParent* cp = context->GetContentParent()) {
 | |
|     Unused << cp->SendSetFocusedElement(context, aNeedsFocus);
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvFinalizeFocusOuter(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext, bool aCanFocus,
 | |
|     CallerType aCallerType) {
 | |
|   if (aContext.IsNullOrDiscarded()) {
 | |
|     MOZ_LOG(
 | |
|         BrowsingContext::GetLog(), LogLevel::Debug,
 | |
|         ("ParentIPC: Trying to send a message to dead or detached context"));
 | |
|     return IPC_OK();
 | |
|   }
 | |
|   LOGFOCUS(("ContentParent::RecvFinalizeFocusOuter"));
 | |
|   CanonicalBrowsingContext* context = aContext.get_canonical();
 | |
|   ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
 | |
|   if (cpm) {
 | |
|     ContentParent* cp = cpm->GetContentProcessById(
 | |
|         ContentParentId(context->EmbedderProcessId()));
 | |
|     if (cp) {
 | |
|       Unused << cp->SendFinalizeFocusOuter(context, aCanFocus, aCallerType);
 | |
|     }
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvInsertNewFocusActionId(
 | |
|     uint64_t aActionId) {
 | |
|   LOGFOCUS(("ContentParent::RecvInsertNewFocusActionId actionid: %" PRIu64,
 | |
|             aActionId));
 | |
|   nsFocusManager* fm = nsFocusManager::GetFocusManager();
 | |
|   if (fm) {
 | |
|     fm->InsertNewFocusActionId(aActionId);
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvBlurToParent(
 | |
|     const MaybeDiscarded<BrowsingContext>& aFocusedBrowsingContext,
 | |
|     const MaybeDiscarded<BrowsingContext>& aBrowsingContextToClear,
 | |
|     const MaybeDiscarded<BrowsingContext>& aAncestorBrowsingContextToFocus,
 | |
|     bool aIsLeavingDocument, bool aAdjustWidget,
 | |
|     bool aBrowsingContextToClearHandled,
 | |
|     bool aAncestorBrowsingContextToFocusHandled, uint64_t aActionId) {
 | |
|   if (aFocusedBrowsingContext.IsNullOrDiscarded()) {
 | |
|     MOZ_LOG(
 | |
|         BrowsingContext::GetLog(), LogLevel::Debug,
 | |
|         ("ParentIPC: Trying to send a message to dead or detached context"));
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   LOGFOCUS(
 | |
|       ("ContentParent::RecvBlurToParent isLeavingDocument %d adjustWidget %d "
 | |
|        "browsingContextToClearHandled %d ancestorBrowsingContextToFocusHandled "
 | |
|        "%d actionid: %" PRIu64,
 | |
|        aIsLeavingDocument, aAdjustWidget, aBrowsingContextToClearHandled,
 | |
|        aAncestorBrowsingContextToFocusHandled, aActionId));
 | |
| 
 | |
|   CanonicalBrowsingContext* focusedBrowsingContext =
 | |
|       aFocusedBrowsingContext.get_canonical();
 | |
| 
 | |
|   ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
 | |
|   if (!cpm) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   // 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.");
 | |
|     ContentParent* cp = cpm->GetContentProcessById(ContentParentId(
 | |
|         aBrowsingContextToClear.get_canonical()->OwnerProcessId()));
 | |
|     Unused << cp->SendSetFocusedElement(aBrowsingContextToClear, false);
 | |
|   } else if (ancestorDifferent) {
 | |
|     ContentParent* cp = cpm->GetContentProcessById(ContentParentId(
 | |
|         aAncestorBrowsingContextToFocus.get_canonical()->OwnerProcessId()));
 | |
|     Unused << cp->SendSetFocusedElement(aAncestorBrowsingContextToFocus, true);
 | |
|   }
 | |
| 
 | |
|   ContentParent* cp = cpm->GetContentProcessById(
 | |
|       ContentParentId(focusedBrowsingContext->OwnerProcessId()));
 | |
|   Unused << cp->SendBlurToChild(aFocusedBrowsingContext,
 | |
|                                 aBrowsingContextToClear,
 | |
|                                 aAncestorBrowsingContextToFocus,
 | |
|                                 aIsLeavingDocument, aAdjustWidget, aActionId);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvMaybeExitFullscreen(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext) {
 | |
|   if (aContext.IsNullOrDiscarded()) {
 | |
|     MOZ_LOG(
 | |
|         BrowsingContext::GetLog(), LogLevel::Debug,
 | |
|         ("ParentIPC: Trying to send a message to dead or detached context"));
 | |
|     return IPC_OK();
 | |
|   }
 | |
|   CanonicalBrowsingContext* context = aContext.get_canonical();
 | |
| 
 | |
|   if (ContentParent* cp = context->GetContentParent()) {
 | |
|     Unused << cp->SendMaybeExitFullscreen(context);
 | |
|   }
 | |
| 
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvWindowPostMessage(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext,
 | |
|     const ClonedOrErrorMessageData& aMessage, const PostMessageData& aData) {
 | |
|   if (aContext.IsNullOrDiscarded()) {
 | |
|     MOZ_LOG(
 | |
|         BrowsingContext::GetLog(), LogLevel::Debug,
 | |
|         ("ParentIPC: Trying to send a message to dead or detached context"));
 | |
|     return IPC_OK();
 | |
|   }
 | |
|   CanonicalBrowsingContext* context = aContext.get_canonical();
 | |
| 
 | |
|   if (aData.source().IsDiscarded()) {
 | |
|     MOZ_LOG(
 | |
|         BrowsingContext::GetLog(), LogLevel::Debug,
 | |
|         ("ParentIPC: Trying to send a message from dead or detached context"));
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   RefPtr<ContentParent> cp = context->GetContentParent();
 | |
|   if (!cp) {
 | |
|     MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
 | |
|             ("ParentIPC: Trying to send PostMessage to dead content process"));
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   ClonedOrErrorMessageData message;
 | |
|   StructuredCloneData messageFromChild;
 | |
|   if (aMessage.type() == ClonedOrErrorMessageData::TClonedMessageData) {
 | |
|     UnpackClonedMessageData(aMessage, messageFromChild);
 | |
| 
 | |
|     ClonedMessageData clonedMessageData;
 | |
|     if (BuildClonedMessageData(messageFromChild, clonedMessageData)) {
 | |
|       message = std::move(clonedMessageData);
 | |
|     } else {
 | |
|       // FIXME Logging?
 | |
|       message = ErrorMessageData();
 | |
|     }
 | |
|   } else {
 | |
|     MOZ_ASSERT(aMessage.type() == ClonedOrErrorMessageData::TErrorMessageData);
 | |
|     message = ErrorMessageData();
 | |
|   }
 | |
| 
 | |
|   Unused << cp->SendWindowPostMessage(context, message, aData);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| void ContentParent::AddBrowsingContextGroup(BrowsingContextGroup* aGroup) {
 | |
|   MOZ_DIAGNOSTIC_ASSERT(aGroup);
 | |
|   // Ensure that the group has been inserted, and if we're not launching
 | |
|   // anymore, also begin subscribing. Launching processes will be subscribed if
 | |
|   // they finish launching in `LaunchSubprocessResolve`.
 | |
|   if (mGroups.EnsureInserted(aGroup) && !IsLaunching()) {
 | |
|     aGroup->Subscribe(this);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void ContentParent::RemoveBrowsingContextGroup(BrowsingContextGroup* aGroup) {
 | |
|   MOZ_DIAGNOSTIC_ASSERT(aGroup);
 | |
|   // Remove the group from our list. This is called from the
 | |
|   // BrowsingContextGroup when unsubscribing, so we don't need to do it here.
 | |
|   if (mGroups.EnsureRemoved(aGroup) && CanSend()) {
 | |
|     // If we're removing the entry for the first time, tell the content process
 | |
|     // to clean up the group.
 | |
|     Unused << SendDestroyBrowsingContextGroup(aGroup->Id());
 | |
|   }
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvCommitBrowsingContextTransaction(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext,
 | |
|     BrowsingContext::BaseTransaction&& aTransaction, uint64_t aEpoch) {
 | |
|   // Record the new BrowsingContextFieldEpoch associated with this transaction.
 | |
|   // This should be done unconditionally, so that we're always in-sync.
 | |
|   //
 | |
|   // The order the parent process receives transactions is considered the
 | |
|   // "canonical" ordering, so we don't need to worry about doing any
 | |
|   // epoch-related validation.
 | |
|   MOZ_ASSERT(aEpoch == mBrowsingContextFieldEpoch + 1,
 | |
|              "Child process skipped an epoch?");
 | |
|   mBrowsingContextFieldEpoch = aEpoch;
 | |
| 
 | |
|   return aTransaction.CommitFromIPC(aContext, this);
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvBlobURLDataRequest(
 | |
|     const nsACString& aBlobURL, nsIPrincipal* aTriggeringPrincipal,
 | |
|     nsIPrincipal* aLoadingPrincipal, const OriginAttributes& aOriginAttributes,
 | |
|     uint64_t aInnerWindowId, const Maybe<nsID>& aAgentClusterId,
 | |
|     BlobURLDataRequestResolver&& aResolver) {
 | |
|   RefPtr<BlobImpl> blobImpl;
 | |
| 
 | |
|   // Since revoked blobs are also retrieved, it is possible that the blob no
 | |
|   // longer exists (due to the 5 second timeout) when execution reaches here
 | |
|   if (!BlobURLProtocolHandler::GetDataEntry(
 | |
|           aBlobURL, getter_AddRefs(blobImpl), aLoadingPrincipal,
 | |
|           aTriggeringPrincipal, aOriginAttributes, aInnerWindowId,
 | |
|           aAgentClusterId, true /* AlsoIfRevoked */)) {
 | |
|     aResolver(NS_ERROR_DOM_BAD_URI);
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   IPCBlob ipcBlob;
 | |
|   nsresult rv = IPCBlobUtils::Serialize(blobImpl, ipcBlob);
 | |
| 
 | |
|   if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|     aResolver(rv);
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   aResolver(ipcBlob);
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvReportServiceWorkerShutdownProgress(
 | |
|     uint32_t aShutdownStateId, ServiceWorkerShutdownState::Progress aProgress) {
 | |
|   RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
 | |
|   MOZ_RELEASE_ASSERT(swm, "ServiceWorkers should shutdown before SWM.");
 | |
| 
 | |
|   swm->ReportServiceWorkerShutdownProgress(aShutdownStateId, aProgress);
 | |
| 
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvNotifyOnHistoryReload(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext, const bool& aForceReload,
 | |
|     NotifyOnHistoryReloadResolver&& aResolver) {
 | |
|   bool canReload = false;
 | |
|   Maybe<NotNull<RefPtr<nsDocShellLoadState>>> loadState;
 | |
|   Maybe<bool> reloadActiveEntry;
 | |
|   if (!aContext.IsDiscarded()) {
 | |
|     aContext.get_canonical()->NotifyOnHistoryReload(
 | |
|         aForceReload, canReload, loadState, reloadActiveEntry);
 | |
|   }
 | |
|   aResolver(
 | |
|       std::tuple<const bool&,
 | |
|                  const Maybe<NotNull<RefPtr<nsDocShellLoadState>>>&,
 | |
|                  const Maybe<bool>&>(canReload, loadState, reloadActiveEntry));
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvHistoryCommit(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext, const uint64_t& aLoadID,
 | |
|     const nsID& aChangeID, const uint32_t& aLoadType, const bool& aPersist,
 | |
|     const bool& aCloneEntryChildren, const bool& aChannelExpired,
 | |
|     const uint32_t& aCacheKey) {
 | |
|   if (!aContext.IsDiscarded()) {
 | |
|     CanonicalBrowsingContext* canonical = aContext.get_canonical();
 | |
|     if (!canonical) {
 | |
|       return IPC_FAIL(
 | |
|           this, "Could not get canonical. aContext.get_canonical() fails.");
 | |
|     }
 | |
|     canonical->SessionHistoryCommit(aLoadID, aChangeID, aLoadType, aPersist,
 | |
|                                     aCloneEntryChildren, aChannelExpired,
 | |
|                                     aCacheKey);
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvHistoryGo(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext, int32_t aOffset,
 | |
|     uint64_t aHistoryEpoch, bool aRequireUserInteraction, bool aUserActivation,
 | |
|     HistoryGoResolver&& aResolveRequestedIndex) {
 | |
|   if (!aContext.IsDiscarded()) {
 | |
|     aResolveRequestedIndex(aContext.get_canonical()->HistoryGo(
 | |
|         aOffset, aHistoryEpoch, aRequireUserInteraction, aUserActivation,
 | |
|         Some(ChildID())));
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvSynchronizeLayoutHistoryState(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext,
 | |
|     nsILayoutHistoryState* aState) {
 | |
|   if (aContext.IsNull()) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   BrowsingContext* bc = aContext.GetMaybeDiscarded();
 | |
|   if (!bc) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
|   SessionHistoryEntry* entry = bc->Canonical()->GetActiveSessionHistoryEntry();
 | |
|   if (entry) {
 | |
|     entry->SetLayoutHistoryState(aState);
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvSessionHistoryEntryTitle(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext, const nsAString& aTitle) {
 | |
|   if (aContext.IsNullOrDiscarded()) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   SessionHistoryEntry* entry =
 | |
|       aContext.get_canonical()->GetActiveSessionHistoryEntry();
 | |
|   if (entry) {
 | |
|     entry->SetTitle(aTitle);
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult
 | |
| ContentParent::RecvSessionHistoryEntryScrollRestorationIsManual(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext, const bool& aIsManual) {
 | |
|   if (aContext.IsNullOrDiscarded()) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   SessionHistoryEntry* entry =
 | |
|       aContext.get_canonical()->GetActiveSessionHistoryEntry();
 | |
|   if (entry) {
 | |
|     entry->SetScrollRestorationIsManual(aIsManual);
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvSessionHistoryEntryScrollPosition(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext, const int32_t& aX,
 | |
|     const int32_t& aY) {
 | |
|   if (aContext.IsNullOrDiscarded()) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   SessionHistoryEntry* entry =
 | |
|       aContext.get_canonical()->GetActiveSessionHistoryEntry();
 | |
|   if (entry) {
 | |
|     entry->SetScrollPosition(aX, aY);
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult
 | |
| ContentParent::RecvSessionHistoryEntryStoreWindowNameInContiguousEntries(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext, const nsAString& aName) {
 | |
|   if (aContext.IsNullOrDiscarded()) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   // Per https://html.spec.whatwg.org/#history-traversal 4.2.1, we need to set
 | |
|   // the name to all contiguous entries. This has to be called before
 | |
|   // CanonicalBrowsingContext::SessionHistoryCommit(), so the active entry is
 | |
|   // still the old entry that we want to set.
 | |
| 
 | |
|   SessionHistoryEntry* entry =
 | |
|       aContext.get_canonical()->GetActiveSessionHistoryEntry();
 | |
| 
 | |
|   if (entry) {
 | |
|     nsSHistory::WalkContiguousEntries(
 | |
|         entry, [&](nsISHEntry* aEntry) { aEntry->SetName(aName); });
 | |
|   }
 | |
| 
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvSessionHistoryEntryCacheKey(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext,
 | |
|     const uint32_t& aCacheKey) {
 | |
|   if (aContext.IsNullOrDiscarded()) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   SessionHistoryEntry* entry =
 | |
|       aContext.get_canonical()->GetActiveSessionHistoryEntry();
 | |
|   if (entry) {
 | |
|     entry->SetCacheKey(aCacheKey);
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvSessionHistoryEntryWireframe(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext,
 | |
|     const Wireframe& aWireframe) {
 | |
|   if (aContext.IsNull()) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   BrowsingContext* bc = aContext.GetMaybeDiscarded();
 | |
|   if (!bc) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   SessionHistoryEntry* entry = bc->Canonical()->GetActiveSessionHistoryEntry();
 | |
|   if (entry) {
 | |
|     entry->SetWireframe(Some(aWireframe));
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult
 | |
| ContentParent::RecvGetLoadingSessionHistoryInfoFromParent(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext,
 | |
|     GetLoadingSessionHistoryInfoFromParentResolver&& aResolver) {
 | |
|   if (aContext.IsNullOrDiscarded()) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   Maybe<LoadingSessionHistoryInfo> info;
 | |
|   aContext.get_canonical()->GetLoadingSessionHistoryInfoFromParent(info);
 | |
|   aResolver(info);
 | |
| 
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvRemoveFromBFCache(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext) {
 | |
|   if (aContext.IsNullOrDiscarded()) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsFrameLoaderOwner> owner =
 | |
|       do_QueryInterface(aContext.get_canonical()->GetEmbedderElement());
 | |
|   if (!owner) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   RefPtr<nsFrameLoader> frameLoader = owner->GetFrameLoader();
 | |
|   if (!frameLoader || !frameLoader->GetMaybePendingBrowsingContext()) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   nsCOMPtr<nsISHistory> shistory = frameLoader->GetMaybePendingBrowsingContext()
 | |
|                                        ->Canonical()
 | |
|                                        ->GetSessionHistory();
 | |
|   if (!shistory) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   uint32_t count = shistory->GetCount();
 | |
|   for (uint32_t i = 0; i < count; ++i) {
 | |
|     nsCOMPtr<nsISHEntry> entry;
 | |
|     shistory->GetEntryAtIndex(i, getter_AddRefs(entry));
 | |
|     nsCOMPtr<SessionHistoryEntry> she = do_QueryInterface(entry);
 | |
|     if (she) {
 | |
|       if (RefPtr<nsFrameLoader> frameLoader = she->GetFrameLoader()) {
 | |
|         if (frameLoader->GetMaybePendingBrowsingContext() == aContext.get()) {
 | |
|           she->SetFrameLoader(nullptr);
 | |
|           frameLoader->Destroy();
 | |
|           break;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvSetActiveSessionHistoryEntry(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext,
 | |
|     const Maybe<nsPoint>& aPreviousScrollPos, SessionHistoryInfo&& aInfo,
 | |
|     uint32_t aLoadType, uint32_t aUpdatedCacheKey, const nsID& aChangeID) {
 | |
|   if (!aContext.IsDiscarded()) {
 | |
|     aContext.get_canonical()->SetActiveSessionHistoryEntry(
 | |
|         aPreviousScrollPos, &aInfo, aLoadType, aUpdatedCacheKey, aChangeID);
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvReplaceActiveSessionHistoryEntry(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext,
 | |
|     SessionHistoryInfo&& aInfo) {
 | |
|   if (!aContext.IsDiscarded()) {
 | |
|     aContext.get_canonical()->ReplaceActiveSessionHistoryEntry(&aInfo);
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult
 | |
| ContentParent::RecvRemoveDynEntriesFromActiveSessionHistoryEntry(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext) {
 | |
|   if (!aContext.IsDiscarded()) {
 | |
|     aContext.get_canonical()->RemoveDynEntriesFromActiveSessionHistoryEntry();
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvRemoveFromSessionHistory(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext, const nsID& aChangeID) {
 | |
|   if (!aContext.IsDiscarded()) {
 | |
|     aContext.get_canonical()->RemoveFromSessionHistory(aChangeID);
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvHistoryReload(
 | |
|     const MaybeDiscarded<BrowsingContext>& aContext,
 | |
|     const uint32_t aReloadFlags) {
 | |
|   if (!aContext.IsDiscarded()) {
 | |
|     nsISHistory* shistory = aContext.get_canonical()->GetSessionHistory();
 | |
|     if (shistory) {
 | |
|       shistory->Reload(aReloadFlags);
 | |
|     }
 | |
|   }
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvCommitWindowContextTransaction(
 | |
|     const MaybeDiscarded<WindowContext>& aContext,
 | |
|     WindowContext::BaseTransaction&& aTransaction, uint64_t aEpoch) {
 | |
|   // Record the new BrowsingContextFieldEpoch associated with this transaction.
 | |
|   // This should be done unconditionally, so that we're always in-sync.
 | |
|   //
 | |
|   // The order the parent process receives transactions is considered the
 | |
|   // "canonical" ordering, so we don't need to worry about doing any
 | |
|   // epoch-related validation.
 | |
|   MOZ_ASSERT(aEpoch == mBrowsingContextFieldEpoch + 1,
 | |
|              "Child process skipped an epoch?");
 | |
|   mBrowsingContextFieldEpoch = aEpoch;
 | |
| 
 | |
|   return aTransaction.CommitFromIPC(aContext, this);
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP ContentParent::GetChildID(uint64_t* aOut) {
 | |
|   *aOut = this->ChildID();
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP ContentParent::GetOsPid(int32_t* aOut) {
 | |
|   *aOut = Pid();
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP ContentParent::GetRemoteType(nsACString& aRemoteType) {
 | |
|   aRemoteType = GetRemoteType();
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| IPCResult ContentParent::RecvRawMessage(
 | |
|     const JSActorMessageMeta& aMeta, const Maybe<ClonedMessageData>& aData,
 | |
|     const Maybe<ClonedMessageData>& aStack) {
 | |
|   Maybe<StructuredCloneData> data;
 | |
|   if (aData) {
 | |
|     data.emplace();
 | |
|     data->BorrowFromClonedMessageData(*aData);
 | |
|   }
 | |
|   Maybe<StructuredCloneData> stack;
 | |
|   if (aStack) {
 | |
|     stack.emplace();
 | |
|     stack->BorrowFromClonedMessageData(*aStack);
 | |
|   }
 | |
|   ReceiveRawMessage(aMeta, std::move(data), std::move(stack));
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP ContentParent::GetActor(const nsACString& aName, JSContext* aCx,
 | |
|                                       JSProcessActorParent** retval) {
 | |
|   ErrorResult error;
 | |
|   RefPtr<JSProcessActorParent> actor =
 | |
|       JSActorManager::GetActor(aCx, aName, error)
 | |
|           .downcast<JSProcessActorParent>();
 | |
|   if (error.MaybeSetPendingException(aCx)) {
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
|   actor.forget(retval);
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP ContentParent::GetExistingActor(const nsACString& aName,
 | |
|                                               JSProcessActorParent** retval) {
 | |
|   RefPtr<JSProcessActorParent> actor =
 | |
|       JSActorManager::GetExistingActor(aName).downcast<JSProcessActorParent>();
 | |
|   actor.forget(retval);
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| already_AddRefed<JSActor> ContentParent::InitJSActor(
 | |
|     JS::Handle<JSObject*> aMaybeActor, const nsACString& aName,
 | |
|     ErrorResult& aRv) {
 | |
|   RefPtr<JSProcessActorParent> actor;
 | |
|   if (aMaybeActor.get()) {
 | |
|     aRv = UNWRAP_OBJECT(JSProcessActorParent, aMaybeActor.get(), actor);
 | |
|     if (aRv.Failed()) {
 | |
|       return nullptr;
 | |
|     }
 | |
|   } else {
 | |
|     actor = new JSProcessActorParent();
 | |
|   }
 | |
| 
 | |
|   MOZ_RELEASE_ASSERT(!actor->Manager(),
 | |
|                      "mManager was already initialized once!");
 | |
|   actor->Init(aName, this);
 | |
|   return actor.forget();
 | |
| }
 | |
| 
 | |
| IPCResult ContentParent::RecvFOGData(ByteBuf&& buf) {
 | |
|   glean::FOGData(std::move(buf));
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| mozilla::ipc::IPCResult ContentParent::RecvSetContainerFeaturePolicy(
 | |
|     const MaybeDiscardedBrowsingContext& aContainerContext,
 | |
|     FeaturePolicy* aContainerFeaturePolicy) {
 | |
|   if (aContainerContext.IsNullOrDiscarded()) {
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
|   auto* context = aContainerContext.get_canonical();
 | |
|   context->SetContainerFeaturePolicy(aContainerFeaturePolicy);
 | |
| 
 | |
|   return IPC_OK();
 | |
| }
 | |
| 
 | |
| NS_IMETHODIMP ContentParent::GetCanSend(bool* aCanSend) {
 | |
|   *aCanSend = CanSend();
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| ContentParent* ContentParent::AsContentParent() { return this; }
 | |
| 
 | |
| JSActorManager* ContentParent::AsJSActorManager() { return this; }
 | |
| 
 | |
| IPCResult ContentParent::RecvGetSystemIcon(nsIURI* aURI,
 | |
|                                            GetSystemIconResolver&& aResolver) {
 | |
|   using ResolverArgs = std::tuple<const nsresult&, mozilla::Maybe<ByteBuf>&&>;
 | |
| 
 | |
|   if (!aURI) {
 | |
|     Maybe<ByteBuf> bytebuf = Nothing();
 | |
|     aResolver(ResolverArgs(NS_ERROR_NULL_POINTER, std::move(bytebuf)));
 | |
|     return IPC_OK();
 | |
|   }
 | |
| 
 | |
| #if defined(MOZ_WIDGET_GTK)
 | |
|   Maybe<ByteBuf> bytebuf = Some(ByteBuf{});
 | |
|   nsresult rv = nsIconChannel::GetIcon(aURI, bytebuf.ptr());
 | |
|   if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|     bytebuf = Nothing();
 | |
|   }
 | |
|   aResolver(ResolverArgs(rv, std::move(bytebuf)));
 | |
|   return IPC_OK();
 | |
| #elif defined(XP_WIN)
 | |
|   nsIconChannel::GetIconAsync(aURI)->Then(
 | |
|       GetCurrentSerialEventTarget(), __func__,
 | |
|       [aResolver](ByteBuf&& aByteBuf) {
 | |
|         Maybe<ByteBuf> bytebuf = Some(std::move(aByteBuf));
 | |
|         aResolver(ResolverArgs(NS_OK, std::move(bytebuf)));
 | |
|       },
 | |
|       [aResolver](nsresult aErr) {
 | |
|         Maybe<ByteBuf> bytebuf = Nothing();
 | |
|         aResolver(ResolverArgs(aErr, std::move(bytebuf)));
 | |
|       });
 | |
|   return IPC_OK();
 | |
| #else
 | |
|   MOZ_CRASH(
 | |
|       "This message is currently implemented only on GTK and Windows "
 | |
|       "platforms");
 | |
| #endif
 | |
| }
 | |
| 
 | |
| #ifdef FUZZING_SNAPSHOT
 | |
| IPCResult ContentParent::RecvSignalFuzzingReady() {
 | |
|   // No action needed here, we already observe this message directly
 | |
|   // on the channel and act accordingly.
 | |
|   return IPC_OK();
 | |
| }
 | |
| #endif
 | |
| 
 | |
| nsCString ThreadsafeContentParentHandle::GetRemoteType() {
 | |
|   MutexAutoLock lock(mMutex);
 | |
|   return mRemoteType;
 | |
| }
 | |
| 
 | |
| bool ThreadsafeContentParentHandle::MaybeRegisterRemoteWorkerActor(
 | |
|     MoveOnlyFunction<bool(uint32_t, bool)> aCallback) {
 | |
|   MutexAutoLock lock(mMutex);
 | |
|   if (aCallback(mRemoteWorkerActorCount, mShutdownStarted)) {
 | |
|     ++mRemoteWorkerActorCount;
 | |
|     return true;
 | |
|   }
 | |
|   return false;
 | |
| }
 | |
| 
 | |
| }  // namespace dom
 | |
| }  // namespace mozilla
 | |
| 
 | |
| NS_IMPL_ISUPPORTS(ParentIdleListener, nsIObserver)
 | |
| 
 | |
| NS_IMETHODIMP
 | |
| ParentIdleListener::Observe(nsISupports*, const char* aTopic,
 | |
|                             const char16_t* aData) {
 | |
|   mozilla::Unused << mParent->SendNotifyIdleObserver(
 | |
|       mObserver, nsDependentCString(aTopic), nsDependentString(aData));
 | |
|   return NS_OK;
 | |
| }
 |