summaryrefslogtreecommitdiffstats
path: root/dom/ipc
diff options
context:
space:
mode:
Diffstat (limited to 'dom/ipc')
-rw-r--r--dom/ipc/BrowserBridgeChild.cpp249
-rw-r--r--dom/ipc/BrowserBridgeChild.h119
-rw-r--r--dom/ipc/BrowserBridgeHost.cpp91
-rw-r--r--dom/ipc/BrowserBridgeHost.h68
-rw-r--r--dom/ipc/BrowserBridgeParent.cpp315
-rw-r--r--dom/ipc/BrowserBridgeParent.h123
-rw-r--r--dom/ipc/BrowserChild.cpp3771
-rw-r--r--dom/ipc/BrowserChild.h850
-rw-r--r--dom/ipc/BrowserHost.cpp287
-rw-r--r--dom/ipc/BrowserHost.h109
-rw-r--r--dom/ipc/BrowserParent.cpp4118
-rw-r--r--dom/ipc/BrowserParent.h1013
-rw-r--r--dom/ipc/CSPMessageUtils.cpp50
-rw-r--r--dom/ipc/CSPMessageUtils.h25
-rw-r--r--dom/ipc/ClonedErrorHolder.cpp373
-rw-r--r--dom/ipc/ClonedErrorHolder.h104
-rw-r--r--dom/ipc/CoalescedInputData.cpp47
-rw-r--r--dom/ipc/CoalescedInputData.h74
-rw-r--r--dom/ipc/CoalescedMouseData.cpp83
-rw-r--r--dom/ipc/CoalescedMouseData.h45
-rw-r--r--dom/ipc/CoalescedTouchData.cpp132
-rw-r--r--dom/ipc/CoalescedTouchData.h46
-rw-r--r--dom/ipc/CoalescedWheelData.cpp46
-rw-r--r--dom/ipc/CoalescedWheelData.h28
-rw-r--r--dom/ipc/ColorPickerParent.cpp77
-rw-r--r--dom/ipc/ColorPickerParent.h58
-rw-r--r--dom/ipc/ContentChild.cpp5081
-rw-r--r--dom/ipc/ContentChild.h911
-rw-r--r--dom/ipc/ContentParent.cpp8184
-rw-r--r--dom/ipc/ContentParent.h1699
-rw-r--r--dom/ipc/ContentProcess.cpp192
-rw-r--r--dom/ipc/ContentProcess.h46
-rw-r--r--dom/ipc/ContentProcessManager.cpp142
-rw-r--r--dom/ipc/ContentProcessManager.h90
-rw-r--r--dom/ipc/CustomElementTypes.ipdlh39
-rw-r--r--dom/ipc/DOMTypes.ipdlh329
-rw-r--r--dom/ipc/DocShellMessageUtils.cpp46
-rw-r--r--dom/ipc/DocShellMessageUtils.h48
-rw-r--r--dom/ipc/EffectsInfo.h67
-rw-r--r--dom/ipc/FilePickerMessageUtils.h34
-rw-r--r--dom/ipc/FilePickerParent.cpp304
-rw-r--r--dom/ipc/FilePickerParent.h101
-rw-r--r--dom/ipc/IPCTransferable.ipdlh93
-rw-r--r--dom/ipc/IdType.h64
-rw-r--r--dom/ipc/InProcessChild.h69
-rw-r--r--dom/ipc/InProcessImpl.cpp324
-rw-r--r--dom/ipc/InProcessParent.h74
-rw-r--r--dom/ipc/JSOracleChild.cpp51
-rw-r--r--dom/ipc/JSOracleChild.h69
-rw-r--r--dom/ipc/JSOracleParent.cpp81
-rw-r--r--dom/ipc/JSOracleParent.h48
-rw-r--r--dom/ipc/JSValidatorChild.cpp242
-rw-r--r--dom/ipc/JSValidatorChild.h57
-rw-r--r--dom/ipc/JSValidatorParent.cpp99
-rw-r--r--dom/ipc/JSValidatorParent.h40
-rw-r--r--dom/ipc/JSValidatorUtils.cpp25
-rw-r--r--dom/ipc/JSValidatorUtils.h26
-rw-r--r--dom/ipc/LoginDetectionService.cpp158
-rw-r--r--dom/ipc/LoginDetectionService.h60
-rw-r--r--dom/ipc/MMPrinter.cpp92
-rw-r--r--dom/ipc/MMPrinter.h32
-rw-r--r--dom/ipc/ManifestMessagesChild.sys.mjs112
-rw-r--r--dom/ipc/MaybeDiscarded.h133
-rw-r--r--dom/ipc/MemMapSnapshot.cpp45
-rw-r--r--dom/ipc/MemMapSnapshot.h54
-rw-r--r--dom/ipc/MemoryReportRequest.cpp170
-rw-r--r--dom/ipc/MemoryReportRequest.h73
-rw-r--r--dom/ipc/MemoryReportTypes.ipdlh22
-rw-r--r--dom/ipc/NativeThreadId.h16
-rw-r--r--dom/ipc/PBrowser.ipdl1023
-rw-r--r--dom/ipc/PBrowserBridge.ipdl134
-rw-r--r--dom/ipc/PColorPicker.ipdl28
-rw-r--r--dom/ipc/PContent.ipdl1957
-rw-r--r--dom/ipc/PContentPermission.ipdlh19
-rw-r--r--dom/ipc/PContentPermissionRequest.ipdl29
-rw-r--r--dom/ipc/PCycleCollectWithLogs.ipdl23
-rw-r--r--dom/ipc/PFilePicker.ipdl48
-rw-r--r--dom/ipc/PInProcess.ipdl33
-rw-r--r--dom/ipc/PJSOracle.ipdl21
-rw-r--r--dom/ipc/PJSValidator.ipdl32
-rw-r--r--dom/ipc/PProcessHangMonitor.ipdl52
-rw-r--r--dom/ipc/PTabContext.ipdlh47
-rw-r--r--dom/ipc/PURLClassifier.ipdl24
-rw-r--r--dom/ipc/PURLClassifierInfo.ipdlh15
-rw-r--r--dom/ipc/PURLClassifierLocal.ipdl38
-rw-r--r--dom/ipc/PVsync.ipdl40
-rw-r--r--dom/ipc/PWindowGlobal.ipdl227
-rw-r--r--dom/ipc/PageLoadEventUtils.h52
-rw-r--r--dom/ipc/PermissionMessageUtils.cpp51
-rw-r--r--dom/ipc/PermissionMessageUtils.h38
-rw-r--r--dom/ipc/PreallocatedProcessManager.cpp440
-rw-r--r--dom/ipc/PreallocatedProcessManager.h73
-rw-r--r--dom/ipc/PrefsTypes.ipdlh32
-rw-r--r--dom/ipc/ProcessActor.cpp37
-rw-r--r--dom/ipc/ProcessActor.h36
-rw-r--r--dom/ipc/ProcessHangMonitor.cpp1399
-rw-r--r--dom/ipc/ProcessHangMonitor.h97
-rw-r--r--dom/ipc/ProcessHangMonitorIPC.h28
-rw-r--r--dom/ipc/ProcessIsolation.cpp1139
-rw-r--r--dom/ipc/ProcessIsolation.h111
-rw-r--r--dom/ipc/ProcessPriorityManager.cpp1067
-rw-r--r--dom/ipc/ProcessPriorityManager.h95
-rw-r--r--dom/ipc/PropertyBagUtils.cpp261
-rw-r--r--dom/ipc/PropertyBagUtils.h38
-rw-r--r--dom/ipc/RefMessageBodyService.cpp161
-rw-r--r--dom/ipc/RefMessageBodyService.h137
-rw-r--r--dom/ipc/ReferrerInfoUtils.cpp54
-rw-r--r--dom/ipc/ReferrerInfoUtils.h24
-rw-r--r--dom/ipc/RemoteBrowser.cpp31
-rw-r--r--dom/ipc/RemoteBrowser.h72
-rw-r--r--dom/ipc/RemoteType.h35
-rw-r--r--dom/ipc/RemoteWebProgressRequest.cpp270
-rw-r--r--dom/ipc/RemoteWebProgressRequest.h36
-rw-r--r--dom/ipc/ServiceWorkerConfiguration.ipdlh18
-rw-r--r--dom/ipc/SharedMap.cpp467
-rw-r--r--dom/ipc/SharedMap.h361
-rw-r--r--dom/ipc/SharedMapChangeEvent.h46
-rw-r--r--dom/ipc/SharedMessageBody.cpp298
-rw-r--r--dom/ipc/SharedMessageBody.h112
-rw-r--r--dom/ipc/SharedStringMap.cpp140
-rw-r--r--dom/ipc/SharedStringMap.h220
-rw-r--r--dom/ipc/StringTable.h116
-rw-r--r--dom/ipc/StructuredCloneData.cpp329
-rw-r--r--dom/ipc/StructuredCloneData.h268
-rw-r--r--dom/ipc/TabContext.cpp106
-rw-r--r--dom/ipc/TabContext.h171
-rw-r--r--dom/ipc/TabMessageTypes.h23
-rw-r--r--dom/ipc/TabMessageUtils.h115
-rw-r--r--dom/ipc/URLClassifierChild.h90
-rw-r--r--dom/ipc/URLClassifierParent.cpp186
-rw-r--r--dom/ipc/URLClassifierParent.h87
-rw-r--r--dom/ipc/UserActivationIPCUtils.h28
-rw-r--r--dom/ipc/VsyncChild.h24
-rw-r--r--dom/ipc/VsyncMainChild.cpp73
-rw-r--r--dom/ipc/VsyncMainChild.h51
-rw-r--r--dom/ipc/VsyncParent.cpp103
-rw-r--r--dom/ipc/VsyncParent.h54
-rw-r--r--dom/ipc/VsyncWorkerChild.cpp67
-rw-r--r--dom/ipc/VsyncWorkerChild.h52
-rw-r--r--dom/ipc/WindowGlobalActor.cpp191
-rw-r--r--dom/ipc/WindowGlobalActor.h55
-rw-r--r--dom/ipc/WindowGlobalChild.cpp893
-rw-r--r--dom/ipc/WindowGlobalChild.h225
-rw-r--r--dom/ipc/WindowGlobalParent.cpp1724
-rw-r--r--dom/ipc/WindowGlobalParent.h444
-rw-r--r--dom/ipc/WindowGlobalTypes.ipdlh40
-rw-r--r--dom/ipc/components.conf17
-rw-r--r--dom/ipc/gtest/ProcessIsolationTest.cpp234
-rw-r--r--dom/ipc/gtest/moz.build13
-rw-r--r--dom/ipc/jar.mn7
-rw-r--r--dom/ipc/jsactor/JSActor.cpp503
-rw-r--r--dom/ipc/jsactor/JSActor.h174
-rw-r--r--dom/ipc/jsactor/JSActorManager.cpp266
-rw-r--r--dom/ipc/jsactor/JSActorManager.h100
-rw-r--r--dom/ipc/jsactor/JSActorProtocolUtils.h121
-rw-r--r--dom/ipc/jsactor/JSActorService.cpp322
-rw-r--r--dom/ipc/jsactor/JSActorService.h113
-rw-r--r--dom/ipc/jsactor/JSProcessActorChild.cpp86
-rw-r--r--dom/ipc/jsactor/JSProcessActorChild.h54
-rw-r--r--dom/ipc/jsactor/JSProcessActorParent.cpp90
-rw-r--r--dom/ipc/jsactor/JSProcessActorParent.h63
-rw-r--r--dom/ipc/jsactor/JSProcessActorProtocol.cpp149
-rw-r--r--dom/ipc/jsactor/JSProcessActorProtocol.h79
-rw-r--r--dom/ipc/jsactor/JSWindowActorChild.cpp160
-rw-r--r--dom/ipc/jsactor/JSWindowActorChild.h82
-rw-r--r--dom/ipc/jsactor/JSWindowActorParent.cpp102
-rw-r--r--dom/ipc/jsactor/JSWindowActorParent.h66
-rw-r--r--dom/ipc/jsactor/JSWindowActorProtocol.cpp380
-rw-r--r--dom/ipc/jsactor/JSWindowActorProtocol.h103
-rw-r--r--dom/ipc/jsactor/moz.build42
-rw-r--r--dom/ipc/jsactor/nsQueryActor.h90
-rw-r--r--dom/ipc/moz.build276
-rw-r--r--dom/ipc/nsIDOMProcessChild.idl59
-rw-r--r--dom/ipc/nsIDOMProcessParent.idl58
-rw-r--r--dom/ipc/nsIHangReport.idl51
-rw-r--r--dom/ipc/nsILoginDetectionService.idl22
-rw-r--r--dom/ipc/tests/JSProcessActor/browser.toml18
-rw-r--r--dom/ipc/tests/JSProcessActor/browser_devtools_loader.js57
-rw-r--r--dom/ipc/tests/JSProcessActor/browser_getActor.js47
-rw-r--r--dom/ipc/tests/JSProcessActor/browser_getActor_filter.js80
-rw-r--r--dom/ipc/tests/JSProcessActor/browser_observer_notification.js41
-rw-r--r--dom/ipc/tests/JSProcessActor/browser_registerProcessActor.js16
-rw-r--r--dom/ipc/tests/JSProcessActor/browser_sendAsyncMessage.js51
-rw-r--r--dom/ipc/tests/JSProcessActor/browser_sendQuery.js99
-rw-r--r--dom/ipc/tests/JSProcessActor/browser_uri_combination.js81
-rw-r--r--dom/ipc/tests/JSProcessActor/head.js88
-rw-r--r--dom/ipc/tests/JSWindowActor/audio.oggbin0 -> 14293 bytes
-rw-r--r--dom/ipc/tests/JSWindowActor/browser.toml32
-rw-r--r--dom/ipc/tests/JSWindowActor/browser_contentWindow.js35
-rw-r--r--dom/ipc/tests/JSWindowActor/browser_crash_report.js114
-rw-r--r--dom/ipc/tests/JSWindowActor/browser_destroy_callbacks.js193
-rw-r--r--dom/ipc/tests/JSWindowActor/browser_event_listener.js171
-rw-r--r--dom/ipc/tests/JSWindowActor/browser_getActor.js36
-rw-r--r--dom/ipc/tests/JSWindowActor/browser_getActor_filter.js259
-rw-r--r--dom/ipc/tests/JSWindowActor/browser_observer_notification.js118
-rw-r--r--dom/ipc/tests/JSWindowActor/browser_process_childid.js27
-rw-r--r--dom/ipc/tests/JSWindowActor/browser_registerWindowActor.js16
-rw-r--r--dom/ipc/tests/JSWindowActor/browser_sendAsyncMessage.js71
-rw-r--r--dom/ipc/tests/JSWindowActor/browser_sendQuery.js131
-rw-r--r--dom/ipc/tests/JSWindowActor/browser_uri_combination.js81
-rw-r--r--dom/ipc/tests/JSWindowActor/file_dummyChromePage.html1
-rw-r--r--dom/ipc/tests/JSWindowActor/file_mediaPlayback.html2
-rw-r--r--dom/ipc/tests/JSWindowActor/head.js82
-rw-r--r--dom/ipc/tests/blob_verify.sjs26
-rw-r--r--dom/ipc/tests/browser.toml90
-rw-r--r--dom/ipc/tests/browser_CrashService_crash.js72
-rw-r--r--dom/ipc/tests/browser_ProcessPriorityManager.js963
-rw-r--r--dom/ipc/tests/browser_bug1646088.js71
-rw-r--r--dom/ipc/tests/browser_bug1686194.js47
-rw-r--r--dom/ipc/tests/browser_cancel_content_js.js65
-rw-r--r--dom/ipc/tests/browser_child_clipboard_restricted.js93
-rw-r--r--dom/ipc/tests/browser_content_shutdown_with_endless_js.js86
-rw-r--r--dom/ipc/tests/browser_crash_oopiframe.js245
-rw-r--r--dom/ipc/tests/browser_domainPolicy.js187
-rw-r--r--dom/ipc/tests/browser_gc_schedule.js379
-rw-r--r--dom/ipc/tests/browser_hide_tooltip.js38
-rw-r--r--dom/ipc/tests/browser_layers_unloaded_while_interruptingJS.js32
-rw-r--r--dom/ipc/tests/browser_memory_distribution_telemetry.js93
-rw-r--r--dom/ipc/tests/browser_pbrowser_creation_failure.js57
-rw-r--r--dom/ipc/tests/browser_subframesPreferUsed.js82
-rw-r--r--dom/ipc/tests/browser_very_fission.js38
-rw-r--r--dom/ipc/tests/browser_wpi_base.js305
-rw-r--r--dom/ipc/tests/browser_wpi_isolate_everything.js27
-rw-r--r--dom/ipc/tests/browser_wpi_isolate_high_value.js27
-rw-r--r--dom/ipc/tests/browser_wpi_isolate_nothing.js27
-rw-r--r--dom/ipc/tests/chrome.toml6
-rw-r--r--dom/ipc/tests/file_broadcast_currenturi_onload.html66
-rw-r--r--dom/ipc/tests/file_cancel_content_js.html18
-rw-r--r--dom/ipc/tests/file_cross_frame.html12
-rw-r--r--dom/ipc/tests/file_disableScript.html11
-rw-r--r--dom/ipc/tests/file_domainPolicy_base.html8
-rw-r--r--dom/ipc/tests/file_dummy.html7
-rw-r--r--dom/ipc/tests/file_endless_js.html17
-rw-r--r--dom/ipc/tests/mochitest.toml30
-rw-r--r--dom/ipc/tests/process_error.xhtml60
-rw-r--r--dom/ipc/tests/test_Preallocated.html51
-rw-r--r--dom/ipc/tests/test_bcg_processes.html45
-rw-r--r--dom/ipc/tests/test_blob_sliced_from_child_process.js140
-rw-r--r--dom/ipc/tests/test_blob_sliced_from_parent_process.js167
-rw-r--r--dom/ipc/tests/test_browsingcontext_currenturi.html131
-rw-r--r--dom/ipc/tests/test_bug1086684.js99
-rw-r--r--dom/ipc/tests/test_child_docshell.js90
-rw-r--r--dom/ipc/tests/test_process_error.xhtml22
-rw-r--r--dom/ipc/tests/test_sharedMap.js377
-rw-r--r--dom/ipc/tests/test_temporaryfile_stream.html84
-rw-r--r--dom/ipc/tests/test_window_open_discarded_bc.html40
-rw-r--r--dom/ipc/tests/xpcshell.toml16
247 files changed, 59100 insertions, 0 deletions
diff --git a/dom/ipc/BrowserBridgeChild.cpp b/dom/ipc/BrowserBridgeChild.cpp
new file mode 100644
index 0000000000..3a2f7d8f84
--- /dev/null
+++ b/dom/ipc/BrowserBridgeChild.cpp
@@ -0,0 +1,249 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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 ACCESSIBILITY
+# include "mozilla/a11y/DocAccessible.h"
+# include "mozilla/a11y/DocManager.h"
+# include "mozilla/a11y/OuterDocAccessible.h"
+#endif
+#include "mozilla/dom/BrowserBridgeChild.h"
+#include "mozilla/dom/BrowserBridgeHost.h"
+#include "mozilla/dom/BrowsingContext.h"
+#include "mozilla/dom/MozFrameLoaderOwnerBinding.h"
+#include "mozilla/PresShell.h"
+#include "nsFocusManager.h"
+#include "nsFrameLoader.h"
+#include "nsFrameLoaderOwner.h"
+#include "nsObjectLoadingContent.h"
+#include "nsQueryObject.h"
+#include "nsSubDocumentFrame.h"
+#include "nsView.h"
+
+using namespace mozilla::ipc;
+
+mozilla::LazyLogModule gBrowserChildFocusLog("BrowserChildFocus");
+
+#define LOGBROWSERCHILDFOCUS(args) \
+ MOZ_LOG(gBrowserChildFocusLog, mozilla::LogLevel::Debug, args)
+
+namespace mozilla::dom {
+
+BrowserBridgeChild::BrowserBridgeChild(BrowsingContext* aBrowsingContext,
+ TabId aId, const LayersId& aLayersId)
+ : mId{aId}, mLayersId{aLayersId}, mBrowsingContext(aBrowsingContext) {}
+
+BrowserBridgeChild::~BrowserBridgeChild() {}
+
+already_AddRefed<BrowserBridgeHost> BrowserBridgeChild::FinishInit(
+ nsFrameLoader* aFrameLoader) {
+ MOZ_DIAGNOSTIC_ASSERT(!mFrameLoader);
+ mFrameLoader = aFrameLoader;
+
+ RefPtr<Element> owner = mFrameLoader->GetOwnerContent();
+ Document* doc = owner->OwnerDoc();
+ doc->OOPChildLoadStarted(this);
+
+#if defined(ACCESSIBILITY)
+ if (a11y::DocAccessible* docAcc = a11y::GetExistingDocAccessible(doc)) {
+ if (a11y::LocalAccessible* ownerAcc = docAcc->GetAccessible(owner)) {
+ if (a11y::OuterDocAccessible* outerAcc = ownerAcc->AsOuterDoc()) {
+ outerAcc->SendEmbedderAccessible(this);
+ }
+ }
+ }
+#endif // defined(ACCESSIBILITY)
+
+ return MakeAndAddRef<BrowserBridgeHost>(this);
+}
+
+nsILoadContext* BrowserBridgeChild::GetLoadContext() {
+ return mBrowsingContext;
+}
+
+void BrowserBridgeChild::NavigateByKey(bool aForward,
+ bool aForDocumentNavigation) {
+ Unused << SendNavigateByKey(aForward, aForDocumentNavigation);
+}
+
+void BrowserBridgeChild::Activate(uint64_t aActionId) {
+ LOGBROWSERCHILDFOCUS(
+ ("BrowserBridgeChild::Activate actionid: %" PRIu64, aActionId));
+ Unused << SendActivate(aActionId);
+}
+
+void BrowserBridgeChild::Deactivate(bool aWindowLowering, uint64_t aActionId) {
+ Unused << SendDeactivate(aWindowLowering, aActionId);
+}
+
+/*static*/
+BrowserBridgeChild* BrowserBridgeChild::GetFrom(nsFrameLoader* aFrameLoader) {
+ if (!aFrameLoader) {
+ return nullptr;
+ }
+ return aFrameLoader->GetBrowserBridgeChild();
+}
+
+/*static*/
+BrowserBridgeChild* BrowserBridgeChild::GetFrom(nsIContent* aContent) {
+ RefPtr<nsFrameLoaderOwner> loaderOwner = do_QueryObject(aContent);
+ if (!loaderOwner) {
+ return nullptr;
+ }
+ RefPtr<nsFrameLoader> frameLoader = loaderOwner->GetFrameLoader();
+ return GetFrom(frameLoader);
+}
+
+mozilla::ipc::IPCResult BrowserBridgeChild::RecvRequestFocus(
+ const bool& aCanRaise, const CallerType aCallerType) {
+ // Adapted from BrowserParent
+ RefPtr<Element> owner = mFrameLoader->GetOwnerContent();
+ if (!owner) {
+ return IPC_OK();
+ }
+ nsContentUtils::RequestFrameFocus(*owner, aCanRaise, aCallerType);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserBridgeChild::RecvMoveFocus(
+ const bool& aForward, const bool& aForDocumentNavigation) {
+ // Adapted from BrowserParent
+ RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager();
+ if (!fm) {
+ return IPC_OK();
+ }
+
+ RefPtr<Element> owner = mFrameLoader->GetOwnerContent();
+ if (!owner) {
+ return IPC_OK();
+ }
+
+ RefPtr<Element> dummy;
+
+ uint32_t type =
+ aForward
+ ? (aForDocumentNavigation
+ ? static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_FORWARDDOC)
+ : static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_FORWARD))
+ : (aForDocumentNavigation
+ ? static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_BACKWARDDOC)
+ : static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_BACKWARD));
+ fm->MoveFocus(nullptr, owner, type, nsIFocusManager::FLAG_BYKEY,
+ getter_AddRefs(dummy));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserBridgeChild::RecvMaybeFireEmbedderLoadEvents(
+ EmbedderElementEventType aFireEventAtEmbeddingElement) {
+ RefPtr<Element> owner = mFrameLoader->GetOwnerContent();
+ if (!owner) {
+ return IPC_OK();
+ }
+
+ if (aFireEventAtEmbeddingElement == EmbedderElementEventType::LoadEvent) {
+ nsEventStatus status = nsEventStatus_eIgnore;
+ WidgetEvent event(/* aIsTrusted = */ true, eLoad);
+ event.mFlags.mBubbles = false;
+ event.mFlags.mCancelable = false;
+ EventDispatcher::Dispatch(owner, nullptr, &event, nullptr, &status);
+ } else if (aFireEventAtEmbeddingElement ==
+ EmbedderElementEventType::ErrorEvent) {
+ mFrameLoader->FireErrorEvent();
+ }
+
+ UnblockOwnerDocsLoadEvent();
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserBridgeChild::RecvScrollRectIntoView(
+ const nsRect& aRect, const ScrollAxis& aVertical,
+ const ScrollAxis& aHorizontal, const ScrollFlags& aScrollFlags,
+ const int32_t& aAppUnitsPerDevPixel) {
+ RefPtr<Element> owner = mFrameLoader->GetOwnerContent();
+ if (!owner) {
+ return IPC_OK();
+ }
+
+ nsIFrame* frame = owner->GetPrimaryFrame();
+ if (!frame) {
+ return IPC_OK();
+ }
+
+ nsSubDocumentFrame* subdocumentFrame = do_QueryFrame(frame);
+ if (!subdocumentFrame) {
+ return IPC_OK();
+ }
+
+ nsPoint extraOffset = subdocumentFrame->GetExtraOffset();
+
+ int32_t parentAPD = frame->PresContext()->AppUnitsPerDevPixel();
+ nsRect rect =
+ aRect.ScaleToOtherAppUnitsRoundOut(aAppUnitsPerDevPixel, parentAPD);
+ rect += extraOffset;
+ RefPtr<PresShell> presShell = frame->PresShell();
+ presShell->ScrollFrameIntoView(frame, Some(rect), aVertical, aHorizontal,
+ aScrollFlags);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserBridgeChild::RecvSubFrameCrashed() {
+ if (RefPtr<nsFrameLoaderOwner> frameLoaderOwner =
+ do_QueryObject(mFrameLoader->GetOwnerContent())) {
+ frameLoaderOwner->SubframeCrashed();
+ }
+ return IPC_OK();
+}
+
+void BrowserBridgeChild::ActorDestroy(ActorDestroyReason aWhy) {
+ if (mFrameLoader) {
+ mFrameLoader->DestroyComplete();
+ }
+
+ if (!mBrowsingContext) {
+ // This BBC was never valid, skip teardown.
+ return;
+ }
+
+ // Ensure we unblock our document's 'load' event (in case the OOP-iframe has
+ // been removed before it finished loading, or its subprocess crashed):
+ UnblockOwnerDocsLoadEvent();
+}
+
+void BrowserBridgeChild::UnblockOwnerDocsLoadEvent() {
+ if (!mHadInitialLoad) {
+ mHadInitialLoad = true;
+
+ if (Document* doc = mBrowsingContext->GetParent()->GetExtantDocument()) {
+ doc->OOPChildLoadDone(this);
+ }
+ }
+}
+
+mozilla::ipc::IPCResult BrowserBridgeChild::RecvIntrinsicSizeOrRatioChanged(
+ const Maybe<IntrinsicSize>& aIntrinsicSize,
+ const Maybe<AspectRatio>& aIntrinsicRatio) {
+ if (RefPtr<Element> owner = mFrameLoader->GetOwnerContent()) {
+ if (nsCOMPtr<nsIObjectLoadingContent> olc = do_QueryInterface(owner)) {
+ static_cast<nsObjectLoadingContent*>(olc.get())
+ ->SubdocumentIntrinsicSizeOrRatioChanged(aIntrinsicSize,
+ aIntrinsicRatio);
+ }
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserBridgeChild::RecvImageLoadComplete(
+ const nsresult& aResult) {
+ if (RefPtr<Element> owner = mFrameLoader->GetOwnerContent()) {
+ if (nsCOMPtr<nsIObjectLoadingContent> olc = do_QueryInterface(owner)) {
+ static_cast<nsObjectLoadingContent*>(olc.get())
+ ->SubdocumentImageLoadComplete(aResult);
+ }
+ }
+ return IPC_OK();
+}
+
+} // namespace mozilla::dom
diff --git a/dom/ipc/BrowserBridgeChild.h b/dom/ipc/BrowserBridgeChild.h
new file mode 100644
index 0000000000..60e29d2483
--- /dev/null
+++ b/dom/ipc/BrowserBridgeChild.h
@@ -0,0 +1,119 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_BrowserBridgeChild_h
+#define mozilla_dom_BrowserBridgeChild_h
+
+#include "mozilla/dom/PBrowserBridgeChild.h"
+#include "mozilla/dom/BrowserChild.h"
+#include "mozilla/dom/ipc/IdType.h"
+
+namespace mozilla::dom {
+class BrowsingContext;
+class ContentChild;
+class BrowserBridgeHost;
+
+/**
+ * BrowserBridgeChild implements the child actor part of the PBrowserBridge
+ * protocol. See PBrowserBridge for more information.
+ */
+class BrowserBridgeChild : public PBrowserBridgeChild {
+ public:
+ typedef mozilla::layers::LayersId LayersId;
+
+ NS_INLINE_DECL_REFCOUNTING(BrowserBridgeChild, final);
+
+ BrowserChild* Manager() {
+ MOZ_ASSERT(CanSend());
+ return static_cast<BrowserChild*>(PBrowserBridgeChild::Manager());
+ }
+
+ TabId GetTabId() { return mId; }
+
+ LayersId GetLayersId() { return mLayersId; }
+
+ nsFrameLoader* GetFrameLoader() const { return mFrameLoader; }
+
+ BrowsingContext* GetBrowsingContext() { return mBrowsingContext; }
+
+ nsILoadContext* GetLoadContext();
+
+ void NavigateByKey(bool aForward, bool aForDocumentNavigation);
+
+ void Activate(uint64_t aActionId);
+
+ void Deactivate(bool aWindowLowering, uint64_t aActionId);
+
+ already_AddRefed<BrowserBridgeHost> FinishInit(nsFrameLoader* aFrameLoader);
+
+#if defined(ACCESSIBILITY)
+ void SetEmbedderAccessible(PDocAccessibleChild* aDoc, uint64_t aID) {
+ MOZ_ASSERT((aDoc && aID) || (!aDoc && !aID));
+ mEmbedderAccessibleID = aID;
+ Unused << SendSetEmbedderAccessible(aDoc, aID);
+ }
+
+ uint64_t GetEmbedderAccessibleID() { return mEmbedderAccessibleID; }
+#endif // defined(ACCESSIBILITY)
+
+ static BrowserBridgeChild* GetFrom(nsFrameLoader* aFrameLoader);
+
+ static BrowserBridgeChild* GetFrom(nsIContent* aContent);
+
+ BrowserBridgeChild(BrowsingContext* aBrowsingContext, TabId aId,
+ const LayersId& aLayersId);
+
+ protected:
+ friend class ContentChild;
+ friend class PBrowserBridgeChild;
+
+ mozilla::ipc::IPCResult RecvRequestFocus(const bool& aCanRaise,
+ const CallerType aCallerType);
+
+ mozilla::ipc::IPCResult RecvMoveFocus(const bool& aForward,
+ const bool& aForDocumentNavigation);
+
+ // TODO: Use MOZ_CAN_RUN_SCRIPT when it gains IPDL support (bug 1539864)
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY mozilla::ipc::IPCResult
+ RecvMaybeFireEmbedderLoadEvents(
+ EmbedderElementEventType aFireEventAtEmbeddingElement);
+
+ mozilla::ipc::IPCResult RecvIntrinsicSizeOrRatioChanged(
+ const Maybe<IntrinsicSize>& aIntrinsicSize,
+ const Maybe<AspectRatio>& aIntrinsicRatio);
+
+ mozilla::ipc::IPCResult RecvImageLoadComplete(const nsresult& aResult);
+
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ mozilla::ipc::IPCResult RecvScrollRectIntoView(
+ const nsRect& aRect, const ScrollAxis& aVertical,
+ const ScrollAxis& aHorizontal, const ScrollFlags& aScrollFlags,
+ const int32_t& aAppUnitsPerDevPixel);
+
+ mozilla::ipc::IPCResult RecvSubFrameCrashed();
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ private:
+ ~BrowserBridgeChild();
+
+ void UnblockOwnerDocsLoadEvent();
+
+ TabId mId;
+ LayersId mLayersId;
+ bool mHadInitialLoad = false;
+ RefPtr<nsFrameLoader> mFrameLoader;
+ RefPtr<BrowsingContext> mBrowsingContext;
+#if defined(ACCESSIBILITY)
+ // We need to keep track of the embedder accessible id we last sent to the
+ // parent process.
+ uint64_t mEmbedderAccessibleID = 0;
+#endif // defined(ACCESSIBILITY)
+};
+
+} // namespace mozilla::dom
+
+#endif // !defined(mozilla_dom_BrowserBridgeParent_h)
diff --git a/dom/ipc/BrowserBridgeHost.cpp b/dom/ipc/BrowserBridgeHost.cpp
new file mode 100644
index 0000000000..2cb8decd58
--- /dev/null
+++ b/dom/ipc/BrowserBridgeHost.cpp
@@ -0,0 +1,91 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/BrowserBridgeHost.h"
+
+#include "mozilla/Unused.h"
+#include "mozilla/dom/Element.h"
+#include "nsFrameLoader.h"
+
+namespace mozilla::dom {
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BrowserBridgeHost)
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTION(BrowserBridgeHost)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(BrowserBridgeHost)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(BrowserBridgeHost)
+
+BrowserBridgeHost::BrowserBridgeHost(BrowserBridgeChild* aChild)
+ : mBridge(aChild) {}
+
+TabId BrowserBridgeHost::GetTabId() const { return mBridge->GetTabId(); }
+
+mozilla::layers::LayersId BrowserBridgeHost::GetLayersId() const {
+ return mBridge->GetLayersId();
+}
+
+BrowsingContext* BrowserBridgeHost::GetBrowsingContext() const {
+ return mBridge->GetBrowsingContext();
+}
+
+nsILoadContext* BrowserBridgeHost::GetLoadContext() const {
+ return mBridge->GetLoadContext();
+}
+
+bool BrowserBridgeHost::CanRecv() const {
+ return mBridge && mBridge->CanRecv();
+}
+
+void BrowserBridgeHost::LoadURL(nsDocShellLoadState* aLoadState) {
+ MOZ_ASSERT(aLoadState);
+ Unused << mBridge->SendLoadURL(WrapNotNull(aLoadState));
+}
+
+void BrowserBridgeHost::ResumeLoad(uint64_t aPendingSwitchId) {
+ Unused << mBridge->SendResumeLoad(aPendingSwitchId);
+}
+
+void BrowserBridgeHost::DestroyStart() {
+ // We don't clear the bridge until BrowserBridgeChild::ActorDestroy is called,
+ // which will end up calling DestroyComplete().
+ if (mBridge) {
+ Unused << mBridge->SendBeginDestroy();
+ }
+}
+
+void BrowserBridgeHost::DestroyComplete() { mBridge = nullptr; }
+
+bool BrowserBridgeHost::Show(const OwnerShowInfo& aShowInfo) {
+ Unused << mBridge->SendShow(aShowInfo);
+ return true;
+}
+
+void BrowserBridgeHost::UpdateDimensions(const nsIntRect& aRect,
+ const ScreenIntSize& aSize) {
+ Unused << mBridge->SendUpdateDimensions(aRect, aSize);
+}
+
+void BrowserBridgeHost::UpdateEffects(EffectsInfo aEffects) {
+ if (!mBridge || mEffectsInfo == aEffects) {
+ return;
+ }
+ mEffectsInfo = aEffects;
+ Unused << mBridge->SendUpdateEffects(mEffectsInfo);
+}
+
+already_AddRefed<nsIWidget> BrowserBridgeHost::GetWidget() const {
+ RefPtr<Element> owner = mBridge->GetFrameLoader()->GetOwnerContent();
+ nsCOMPtr<nsIWidget> widget = nsContentUtils::WidgetForContent(owner);
+ if (!widget) {
+ widget = nsContentUtils::WidgetForDocument(owner->OwnerDoc());
+ }
+ return widget.forget();
+}
+
+} // namespace mozilla::dom
diff --git a/dom/ipc/BrowserBridgeHost.h b/dom/ipc/BrowserBridgeHost.h
new file mode 100644
index 0000000000..09f2f4dfdd
--- /dev/null
+++ b/dom/ipc/BrowserBridgeHost.h
@@ -0,0 +1,68 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_BrowserBridgeHost_h
+#define mozilla_dom_BrowserBridgeHost_h
+
+#include "mozilla/dom/RemoteBrowser.h"
+#include "mozilla/dom/BrowserBridgeChild.h"
+
+namespace mozilla::dom {
+
+/**
+ * BrowserBridgeHost manages a remote browser from a content process.
+ *
+ * It is used via the RemoteBrowser interface in nsFrameLoader and proxies
+ * work to the chrome process via PBrowserBridge.
+ *
+ * See `dom/docs/Fission-IPC-Diagram.svg` for an overview of the DOM IPC
+ * actors.
+ */
+class BrowserBridgeHost : public RemoteBrowser {
+ public:
+ typedef mozilla::layers::LayersId LayersId;
+
+ explicit BrowserBridgeHost(BrowserBridgeChild* aChild);
+
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_CLASS(BrowserBridgeHost)
+
+ // Get the IPDL actor for the BrowserBridgeChild.
+ BrowserBridgeChild* GetActor() { return mBridge; }
+
+ BrowserHost* AsBrowserHost() override { return nullptr; }
+ BrowserBridgeHost* AsBrowserBridgeHost() override { return this; }
+
+ TabId GetTabId() const override;
+ LayersId GetLayersId() const override;
+ BrowsingContext* GetBrowsingContext() const override;
+ nsILoadContext* GetLoadContext() const override;
+ bool CanRecv() const override;
+
+ void LoadURL(nsDocShellLoadState* aLoadState) override;
+ void ResumeLoad(uint64_t aPendingSwitchId) override;
+ void DestroyStart() override;
+ void DestroyComplete() override;
+
+ bool Show(const OwnerShowInfo&) override;
+ void UpdateDimensions(const nsIntRect& aRect,
+ const ScreenIntSize& aSize) override;
+
+ void UpdateEffects(EffectsInfo aInfo) override;
+
+ private:
+ virtual ~BrowserBridgeHost() = default;
+
+ already_AddRefed<nsIWidget> GetWidget() const;
+
+ // The IPDL actor for proxying browser operations
+ RefPtr<BrowserBridgeChild> mBridge;
+ EffectsInfo mEffectsInfo;
+};
+
+} // namespace mozilla::dom
+
+#endif // mozilla_dom_BrowserBridgeHost_h
diff --git a/dom/ipc/BrowserBridgeParent.cpp b/dom/ipc/BrowserBridgeParent.cpp
new file mode 100644
index 0000000000..4a687d557d
--- /dev/null
+++ b/dom/ipc/BrowserBridgeParent.cpp
@@ -0,0 +1,315 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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 ACCESSIBILITY
+# include "mozilla/a11y/DocAccessibleParent.h"
+# include "nsAccessibilityService.h"
+#endif
+
+#include "mozilla/Monitor.h"
+#include "mozilla/MouseEvents.h"
+#include "mozilla/dom/BrowserBridgeParent.h"
+#include "mozilla/dom/BrowserParent.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/ContentProcessManager.h"
+#include "mozilla/dom/CanonicalBrowsingContext.h"
+#include "mozilla/dom/BrowsingContextGroup.h"
+#include "mozilla/dom/WindowGlobalParent.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/layers/InputAPZContext.h"
+
+using namespace mozilla::ipc;
+using namespace mozilla::layout;
+using namespace mozilla::hal;
+
+namespace mozilla::dom {
+
+BrowserBridgeParent::BrowserBridgeParent() = default;
+
+BrowserBridgeParent::~BrowserBridgeParent() { Destroy(); }
+
+nsresult BrowserBridgeParent::InitWithProcess(
+ BrowserParent* aParentBrowser, ContentParent* aContentParent,
+ const WindowGlobalInit& aWindowInit, uint32_t aChromeFlags, TabId aTabId) {
+ MOZ_ASSERT(!CanSend(),
+ "This should be called before the object is connected to IPC");
+ MOZ_DIAGNOSTIC_ASSERT(!aContentParent->IsLaunching());
+ MOZ_DIAGNOSTIC_ASSERT(!aContentParent->IsDead());
+
+ RefPtr<CanonicalBrowsingContext> browsingContext =
+ CanonicalBrowsingContext::Get(aWindowInit.context().mBrowsingContextId);
+ if (!browsingContext || browsingContext->IsDiscarded()) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ MOZ_DIAGNOSTIC_ASSERT(
+ !browsingContext->GetBrowserParent(),
+ "BrowsingContext must have had previous BrowserParent cleared");
+
+ MOZ_DIAGNOSTIC_ASSERT(
+ aParentBrowser->Manager() != aContentParent,
+ "Cannot create OOP iframe in the same process as its parent document");
+
+ // Unfortunately, due to the current racy destruction of BrowsingContext
+ // instances when Fission is enabled, while `browsingContext` may not be
+ // discarded, an ancestor might be.
+ //
+ // A discarded ancestor will cause us issues when creating our `BrowserParent`
+ // in the new content process, so abort the attempt if we have one.
+ //
+ // FIXME: We should never have a non-discarded BrowsingContext with discarded
+ // ancestors. (bug 1634759)
+ if (NS_WARN_IF(!browsingContext->AncestorsAreCurrent())) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // Ensure that our content process is subscribed to our newly created
+ // BrowsingContextGroup.
+ browsingContext->Group()->EnsureHostProcess(aContentParent);
+ browsingContext->SetOwnerProcessId(aContentParent->ChildID());
+
+ // Construct the BrowserParent object for our subframe.
+ auto browserParent = MakeRefPtr<BrowserParent>(
+ aContentParent, aTabId, *aParentBrowser, browsingContext, aChromeFlags);
+ browserParent->SetBrowserBridgeParent(this);
+
+ // Open a remote endpoint for our PBrowser actor.
+ ManagedEndpoint<PBrowserChild> childEp =
+ aContentParent->OpenPBrowserEndpoint(browserParent);
+ if (NS_WARN_IF(!childEp.IsValid())) {
+ MOZ_ASSERT(false, "Browser Open Endpoint Failed");
+ return NS_ERROR_FAILURE;
+ }
+
+ ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+ if (!cpm) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ cpm->RegisterRemoteFrame(browserParent);
+
+ RefPtr<WindowGlobalParent> windowParent =
+ WindowGlobalParent::CreateDisconnected(aWindowInit);
+ if (!windowParent) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ ManagedEndpoint<PWindowGlobalChild> windowChildEp =
+ browserParent->OpenPWindowGlobalEndpoint(windowParent);
+ if (NS_WARN_IF(!windowChildEp.IsValid())) {
+ MOZ_ASSERT(false, "WindowGlobal Open Endpoint Failed");
+ return NS_ERROR_FAILURE;
+ }
+
+ MOZ_DIAGNOSTIC_ASSERT(!browsingContext->IsDiscarded(),
+ "bc cannot have become discarded");
+
+ // Tell the content process to set up its PBrowserChild.
+ bool ok = aContentParent->SendConstructBrowser(
+ std::move(childEp), std::move(windowChildEp), aTabId,
+ browserParent->AsIPCTabContext(), aWindowInit, aChromeFlags,
+ aContentParent->ChildID(), aContentParent->IsForBrowser(),
+ /* aIsTopLevel */ false);
+ if (NS_WARN_IF(!ok)) {
+ MOZ_ASSERT(false, "Browser Constructor Failed");
+ return NS_ERROR_FAILURE;
+ }
+
+ // Set our BrowserParent object to the newly created browser.
+ mBrowserParent = std::move(browserParent);
+ mBrowserParent->SetOwnerElement(aParentBrowser->GetOwnerElement());
+ mBrowserParent->InitRendering();
+
+ GetBrowsingContext()->SetCurrentBrowserParent(mBrowserParent);
+
+ windowParent->Init();
+ return NS_OK;
+}
+
+CanonicalBrowsingContext* BrowserBridgeParent::GetBrowsingContext() {
+ return mBrowserParent->GetBrowsingContext();
+}
+
+BrowserParent* BrowserBridgeParent::Manager() {
+ MOZ_ASSERT(CanSend());
+ return static_cast<BrowserParent*>(PBrowserBridgeParent::Manager());
+}
+
+void BrowserBridgeParent::Destroy() {
+ if (mBrowserParent) {
+#ifdef ACCESSIBILITY
+ if (mEmbedderAccessibleDoc && !mEmbedderAccessibleDoc->IsShutdown()) {
+ mEmbedderAccessibleDoc->RemovePendingOOPChildDoc(this);
+ }
+#endif
+ mBrowserParent->Destroy();
+ mBrowserParent->SetBrowserBridgeParent(nullptr);
+ mBrowserParent = nullptr;
+ }
+ if (CanSend()) {
+ Unused << Send__delete__(this);
+ }
+}
+
+IPCResult BrowserBridgeParent::RecvShow(const OwnerShowInfo& aOwnerInfo) {
+ mBrowserParent->AttachWindowRenderer();
+ Unused << mBrowserParent->SendShow(mBrowserParent->GetShowInfo(), aOwnerInfo);
+ return IPC_OK();
+}
+
+IPCResult BrowserBridgeParent::RecvScrollbarPreferenceChanged(
+ ScrollbarPreference aPref) {
+ Unused << mBrowserParent->SendScrollbarPreferenceChanged(aPref);
+ return IPC_OK();
+}
+
+IPCResult BrowserBridgeParent::RecvLoadURL(nsDocShellLoadState* aLoadState) {
+ Unused << mBrowserParent->SendLoadURL(WrapNotNull(aLoadState),
+ mBrowserParent->GetShowInfo());
+ return IPC_OK();
+}
+
+IPCResult BrowserBridgeParent::RecvResumeLoad(uint64_t aPendingSwitchID) {
+ mBrowserParent->ResumeLoad(aPendingSwitchID);
+ return IPC_OK();
+}
+
+IPCResult BrowserBridgeParent::RecvUpdateDimensions(
+ const nsIntRect& aRect, const ScreenIntSize& aSize) {
+ mBrowserParent->UpdateDimensions(aRect, aSize);
+ return IPC_OK();
+}
+
+IPCResult BrowserBridgeParent::RecvUpdateEffects(const EffectsInfo& aEffects) {
+ Unused << mBrowserParent->SendUpdateEffects(aEffects);
+ return IPC_OK();
+}
+
+IPCResult BrowserBridgeParent::RecvUpdateRemotePrintSettings(
+ const embedding::PrintData& aPrintData) {
+ Unused << mBrowserParent->SendUpdateRemotePrintSettings(aPrintData);
+ return IPC_OK();
+}
+
+IPCResult BrowserBridgeParent::RecvRenderLayers(const bool& aEnabled) {
+ Unused << mBrowserParent->SendRenderLayers(aEnabled);
+ return IPC_OK();
+}
+
+IPCResult BrowserBridgeParent::RecvNavigateByKey(
+ const bool& aForward, const bool& aForDocumentNavigation) {
+ Unused << mBrowserParent->SendNavigateByKey(aForward, aForDocumentNavigation);
+ return IPC_OK();
+}
+
+IPCResult BrowserBridgeParent::RecvBeginDestroy() {
+ Destroy();
+ return IPC_OK();
+}
+
+IPCResult BrowserBridgeParent::RecvDispatchSynthesizedMouseEvent(
+ const WidgetMouseEvent& aEvent) {
+ if (aEvent.mMessage != eMouseMove ||
+ aEvent.mReason != WidgetMouseEvent::eSynthesized) {
+ return IPC_FAIL(this, "Unexpected event type");
+ }
+
+ nsCOMPtr<nsIWidget> widget = Manager()->GetWidget();
+ if (!widget) {
+ return IPC_OK();
+ }
+
+ WidgetMouseEvent event = aEvent;
+ event.mWidget = widget;
+ // Convert mRefPoint from the dispatching child process coordinate space
+ // to the parent coordinate space. The SendRealMouseEvent call will convert
+ // it into the dispatchee child process coordinate space
+ event.mRefPoint = Manager()->TransformChildToParent(event.mRefPoint);
+ // We need to set up an InputAPZContext on the stack because
+ // BrowserParent::SendRealMouseEvent requires one. But the only thing in
+ // that context that is actually used in this scenario is the layers id,
+ // and we already have that on the mouse event.
+ layers::InputAPZContext context(
+ layers::ScrollableLayerGuid(event.mLayersId, 0,
+ layers::ScrollableLayerGuid::NULL_SCROLL_ID),
+ 0, nsEventStatus_eIgnore);
+ mBrowserParent->SendRealMouseEvent(event);
+ return IPC_OK();
+}
+
+IPCResult BrowserBridgeParent::RecvWillChangeProcess() {
+ Unused << mBrowserParent->SendWillChangeProcess();
+ return IPC_OK();
+}
+
+IPCResult BrowserBridgeParent::RecvActivate(uint64_t aActionId) {
+ mBrowserParent->Activate(aActionId);
+ return IPC_OK();
+}
+
+IPCResult BrowserBridgeParent::RecvDeactivate(const bool& aWindowLowering,
+ uint64_t aActionId) {
+ mBrowserParent->Deactivate(aWindowLowering, aActionId);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserBridgeParent::RecvUpdateRemoteStyle(
+ const StyleImageRendering& aImageRendering) {
+ Unused << mBrowserParent->SendUpdateRemoteStyle(aImageRendering);
+ return IPC_OK();
+}
+
+#ifdef ACCESSIBILITY
+a11y::DocAccessibleParent* BrowserBridgeParent::GetDocAccessibleParent() {
+ auto* embeddedBrowser = GetBrowserParent();
+ if (!embeddedBrowser) {
+ return nullptr;
+ }
+ a11y::DocAccessibleParent* docAcc =
+ embeddedBrowser->GetTopLevelDocAccessible();
+ return docAcc && !docAcc->IsShutdown() ? docAcc : nullptr;
+}
+
+IPCResult BrowserBridgeParent::RecvSetEmbedderAccessible(
+ PDocAccessibleParent* aDoc, uint64_t aID) {
+# if defined(ANDROID)
+ MonitorAutoLock mal(nsAccessibilityService::GetAndroidMonitor());
+# endif
+ MOZ_ASSERT(aDoc || mEmbedderAccessibleDoc,
+ "Embedder doc shouldn't be cleared if it wasn't set");
+ MOZ_ASSERT(!mEmbedderAccessibleDoc || !aDoc || mEmbedderAccessibleDoc == aDoc,
+ "Embedder doc shouldn't change from one doc to another");
+ if (!aDoc && mEmbedderAccessibleDoc &&
+ !mEmbedderAccessibleDoc->IsShutdown()) {
+ // We're clearing the embedder doc, so remove the pending child doc addition
+ // (if any).
+ mEmbedderAccessibleDoc->RemovePendingOOPChildDoc(this);
+ }
+ mEmbedderAccessibleDoc = static_cast<a11y::DocAccessibleParent*>(aDoc);
+ mEmbedderAccessibleID = aID;
+ if (!aDoc) {
+ MOZ_ASSERT(!aID);
+ return IPC_OK();
+ }
+ MOZ_ASSERT(aID);
+ if (GetDocAccessibleParent()) {
+ // The embedded DocAccessibleParent has already been created. This can
+ // happen if, for example, an iframe is hidden and then shown or
+ // an iframe is reflowed by layout.
+ mEmbedderAccessibleDoc->AddChildDoc(this);
+ }
+ return IPC_OK();
+}
+
+a11y::DocAccessibleParent* BrowserBridgeParent::GetEmbedderAccessibleDoc() {
+ return mEmbedderAccessibleDoc && !mEmbedderAccessibleDoc->IsShutdown()
+ ? mEmbedderAccessibleDoc.get()
+ : nullptr;
+}
+#endif
+
+void BrowserBridgeParent::ActorDestroy(ActorDestroyReason aWhy) { Destroy(); }
+
+} // namespace mozilla::dom
diff --git a/dom/ipc/BrowserBridgeParent.h b/dom/ipc/BrowserBridgeParent.h
new file mode 100644
index 0000000000..35dfd7636f
--- /dev/null
+++ b/dom/ipc/BrowserBridgeParent.h
@@ -0,0 +1,123 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_BrowserBridgeParent_h
+#define mozilla_dom_BrowserBridgeParent_h
+
+#include "mozilla/dom/PBrowserBridgeParent.h"
+#include "mozilla/dom/ipc/IdType.h"
+#include "mozilla/dom/WindowGlobalTypes.h"
+
+namespace mozilla {
+
+namespace a11y {
+class DocAccessibleParent;
+}
+
+namespace embedding {
+class PrintData;
+}
+
+namespace dom {
+
+class BrowserParent;
+
+/**
+ * BrowserBridgeParent implements the parent actor part of the PBrowserBridge
+ * protocol. See PBrowserBridge for more information.
+ */
+class BrowserBridgeParent : public PBrowserBridgeParent {
+ public:
+ NS_INLINE_DECL_REFCOUNTING(BrowserBridgeParent, final);
+
+ BrowserBridgeParent();
+
+ nsresult InitWithProcess(BrowserParent* aParentBrowser,
+ ContentParent* aContentParent,
+ const WindowGlobalInit& aWindowInit,
+ uint32_t aChromeFlags, TabId aTabId);
+
+ BrowserParent* GetBrowserParent() { return mBrowserParent; }
+
+ CanonicalBrowsingContext* GetBrowsingContext();
+
+ // Get our manager actor.
+ BrowserParent* Manager();
+
+#if defined(ACCESSIBILITY)
+ /**
+ * Get the DocAccessibleParent which contains this iframe.
+ */
+ a11y::DocAccessibleParent* GetEmbedderAccessibleDoc();
+
+ /**
+ * Get the unique id of the OuterDocAccessible associated with this iframe.
+ * This is the id of the RemoteAccessible inside the document returned by
+ * GetEmbedderAccessibleDoc.
+ */
+ uint64_t GetEmbedderAccessibleId() { return mEmbedderAccessibleID; }
+
+ /**
+ * Get the DocAccessibleParent for the embedded document.
+ */
+ a11y::DocAccessibleParent* GetDocAccessibleParent();
+#endif // defined(ACCESSIBILITY)
+
+ // Tear down this BrowserBridgeParent.
+ void Destroy();
+
+ protected:
+ friend class PBrowserBridgeParent;
+
+ mozilla::ipc::IPCResult RecvShow(const OwnerShowInfo&);
+ mozilla::ipc::IPCResult RecvScrollbarPreferenceChanged(ScrollbarPreference);
+ mozilla::ipc::IPCResult RecvLoadURL(nsDocShellLoadState* aLoadState);
+ mozilla::ipc::IPCResult RecvResumeLoad(uint64_t aPendingSwitchID);
+ mozilla::ipc::IPCResult RecvUpdateDimensions(const nsIntRect& aRect,
+ const ScreenIntSize& aSize);
+ mozilla::ipc::IPCResult RecvUpdateEffects(const EffectsInfo& aEffects);
+ mozilla::ipc::IPCResult RecvUpdateRemotePrintSettings(
+ const embedding::PrintData&);
+ mozilla::ipc::IPCResult RecvRenderLayers(const bool& aEnabled);
+
+ mozilla::ipc::IPCResult RecvNavigateByKey(const bool& aForward,
+ const bool& aForDocumentNavigation);
+ mozilla::ipc::IPCResult RecvBeginDestroy();
+
+ mozilla::ipc::IPCResult RecvDispatchSynthesizedMouseEvent(
+ const WidgetMouseEvent& aEvent);
+
+ mozilla::ipc::IPCResult RecvWillChangeProcess();
+
+ mozilla::ipc::IPCResult RecvActivate(uint64_t aActionId);
+
+ mozilla::ipc::IPCResult RecvDeactivate(const bool& aWindowLowering,
+ uint64_t aActionId);
+
+ mozilla::ipc::IPCResult RecvUpdateRemoteStyle(
+ const StyleImageRendering& aImageRendering);
+
+#ifdef ACCESSIBILITY
+ mozilla::ipc::IPCResult RecvSetEmbedderAccessible(PDocAccessibleParent* aDoc,
+ uint64_t aID);
+#endif
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ private:
+ ~BrowserBridgeParent();
+
+ RefPtr<BrowserParent> mBrowserParent;
+#ifdef ACCESSIBILITY
+ RefPtr<a11y::DocAccessibleParent> mEmbedderAccessibleDoc;
+ uint64_t mEmbedderAccessibleID = 0;
+#endif // ACCESSIBILITY
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // !defined(mozilla_dom_BrowserBridgeParent_h)
diff --git a/dom/ipc/BrowserChild.cpp b/dom/ipc/BrowserChild.cpp
new file mode 100644
index 0000000000..830ad8579a
--- /dev/null
+++ b/dom/ipc/BrowserChild.cpp
@@ -0,0 +1,3771 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BrowserChild.h"
+
+#ifdef ACCESSIBILITY
+# include "mozilla/a11y/DocAccessibleChild.h"
+#endif
+#include <utility>
+
+#include "BrowserParent.h"
+#include "ContentChild.h"
+#include "EventStateManager.h"
+#include "MMPrinter.h"
+#include "PuppetWidget.h"
+#include "StructuredCloneData.h"
+#include "UnitTransforms.h"
+#include "Units.h"
+#include "VRManagerChild.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/EventForwards.h"
+#include "mozilla/EventListenerManager.h"
+#include "mozilla/HoldDropJSObjects.h"
+#include "mozilla/IMEStateManager.h"
+#include "mozilla/LookAndFeel.h"
+#include "mozilla/MouseEvents.h"
+#include "mozilla/NativeKeyBindingsType.h"
+#include "mozilla/NullPrincipal.h"
+#include "mozilla/PresShell.h"
+#include "mozilla/ProcessHangMonitor.h"
+#include "mozilla/ProfilerLabels.h"
+#include "mozilla/SchedulerGroup.h"
+#include "mozilla/ScopeExit.h"
+#include "mozilla/Services.h"
+#include "mozilla/StaticPrefs_dom.h"
+#include "mozilla/TextEvents.h"
+#include "mozilla/ToString.h"
+#include "mozilla/Unused.h"
+#include "mozilla/dom/AutoPrintEventDispatcher.h"
+#include "mozilla/dom/BrowserBridgeChild.h"
+#include "mozilla/dom/DataTransfer.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/Event.h"
+#include "mozilla/dom/JSWindowActorChild.h"
+#include "mozilla/dom/ImageDocument.h"
+#include "mozilla/dom/LoadURIOptionsBinding.h"
+#include "mozilla/dom/MessageManagerBinding.h"
+#include "mozilla/dom/MouseEventBinding.h"
+#include "mozilla/dom/Nullable.h"
+#include "mozilla/dom/PaymentRequestChild.h"
+#include "mozilla/dom/PBrowser.h"
+#include "mozilla/dom/PointerEventHandler.h"
+#include "mozilla/dom/SessionStoreUtils.h"
+#include "mozilla/dom/SessionStoreChild.h"
+#include "mozilla/dom/UserActivation.h"
+#include "mozilla/dom/WindowGlobalChild.h"
+#include "mozilla/dom/WindowProxyHolder.h"
+#include "mozilla/gfx/CrossProcessPaint.h"
+#include "mozilla/gfx/Matrix.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/BackgroundUtils.h"
+#include "mozilla/ipc/PBackgroundChild.h"
+#include "mozilla/layers/APZCCallbackHelper.h"
+#include "mozilla/layers/TouchActionHelper.h"
+#include "mozilla/layers/APZCTreeManagerChild.h"
+#include "mozilla/layers/APZChild.h"
+#include "mozilla/layers/APZEventState.h"
+#include "mozilla/layers/CompositorBridgeChild.h"
+#include "mozilla/layers/ContentProcessController.h"
+#include "mozilla/layers/DoubleTapToZoom.h"
+#include "mozilla/layers/IAPZCTreeManager.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+#include "mozilla/layers/InputAPZContext.h"
+#include "mozilla/layers/WebRenderLayerManager.h"
+#include "nsBrowserStatusFilter.h"
+#include "nsCommandParams.h"
+#include "nsContentPermissionHelper.h"
+#include "nsContentUtils.h"
+#include "nsDeviceContext.h"
+#include "nsDocShell.h"
+#include "nsDocShellLoadState.h"
+#include "nsExceptionHandler.h"
+#include "nsFilePickerProxy.h"
+#include "nsFocusManager.h"
+#include "nsGlobalWindowOuter.h"
+#include "nsIBaseWindow.h"
+#include "nsIBrowserDOMWindow.h"
+#include "nsIClassifiedChannel.h"
+#include "nsIDocShell.h"
+#include "nsIFrame.h"
+#include "nsILoadContext.h"
+#include "nsISHEntry.h"
+#include "nsISHistory.h"
+#include "nsIScreenManager.h"
+#include "nsIScriptError.h"
+#include "nsIURI.h"
+#include "nsIURIMutator.h"
+#include "nsIWeakReferenceUtils.h"
+#include "nsIWebBrowser.h"
+#include "nsIWebProgress.h"
+#include "nsLayoutUtils.h"
+#include "nsNetUtil.h"
+#include "nsIOpenWindowInfo.h"
+#include "nsPIDOMWindow.h"
+#include "nsPIWindowRoot.h"
+#include "nsPrintfCString.h"
+#include "nsRefreshDriver.h"
+#include "nsThreadManager.h"
+#include "nsThreadUtils.h"
+#include "nsViewManager.h"
+#include "nsWebBrowser.h"
+#include "nsWindowWatcher.h"
+#include "nsIXULRuntime.h"
+
+#ifdef MOZ_WAYLAND
+# include "nsAppRunner.h"
+#endif
+
+#ifdef NS_PRINTING
+# include "mozilla/layout/RemotePrintJobChild.h"
+# include "nsIPrintSettings.h"
+# include "nsIPrintSettingsService.h"
+# include "nsIWebBrowserPrint.h"
+#endif
+
+static mozilla::LazyLogModule sApzChildLog("apz.child");
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace mozilla::dom::ipc;
+using namespace mozilla::ipc;
+using namespace mozilla::layers;
+using namespace mozilla::layout;
+using namespace mozilla::widget;
+using mozilla::layers::GeckoContentController;
+
+static const char BEFORE_FIRST_PAINT[] = "before-first-paint";
+
+static uint32_t sConsecutiveTouchMoveCount = 0;
+
+using BrowserChildMap = nsTHashMap<nsUint64HashKey, BrowserChild*>;
+static BrowserChildMap* sBrowserChildren;
+StaticMutex sBrowserChildrenMutex;
+
+already_AddRefed<Document> BrowserChild::GetTopLevelDocument() const {
+ nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
+ nsCOMPtr<Document> doc = docShell ? docShell->GetExtantDocument() : nullptr;
+ return doc.forget();
+}
+
+PresShell* BrowserChild::GetTopLevelPresShell() const {
+ if (RefPtr<Document> doc = GetTopLevelDocument()) {
+ return doc->GetPresShell();
+ }
+ return nullptr;
+}
+
+bool BrowserChild::UpdateFrame(const RepaintRequest& aRequest) {
+ MOZ_ASSERT(aRequest.GetScrollId() != ScrollableLayerGuid::NULL_SCROLL_ID);
+
+ if (aRequest.IsRootContent()) {
+ if (PresShell* presShell = GetTopLevelPresShell()) {
+ // Guard against stale updates (updates meant for a pres shell which
+ // has since been torn down and destroyed).
+ if (aRequest.GetPresShellId() == presShell->GetPresShellId()) {
+ APZCCallbackHelper::UpdateRootFrame(aRequest);
+ return true;
+ }
+ }
+ } else {
+ // aRequest.mIsRoot is false, so we are trying to update a subframe.
+ // This requires special handling.
+ APZCCallbackHelper::UpdateSubFrame(aRequest);
+ return true;
+ }
+ return true;
+}
+
+class BrowserChild::DelayedDeleteRunnable final : public Runnable,
+ public nsIRunnablePriority {
+ RefPtr<BrowserChild> mBrowserChild;
+
+ // In order to try that this runnable runs after everything that could
+ // possibly touch this tab, we send it through the event queue twice.
+ bool mReadyToDelete = false;
+
+ public:
+ explicit DelayedDeleteRunnable(BrowserChild* aBrowserChild)
+ : Runnable("BrowserChild::DelayedDeleteRunnable"),
+ mBrowserChild(aBrowserChild) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aBrowserChild);
+ }
+
+ NS_DECL_ISUPPORTS_INHERITED
+
+ private:
+ ~DelayedDeleteRunnable() {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!mBrowserChild);
+ }
+
+ NS_IMETHOD GetPriority(uint32_t* aPriority) override {
+ *aPriority = nsIRunnablePriority::PRIORITY_NORMAL;
+ return NS_OK;
+ }
+
+ NS_IMETHOD
+ Run() override {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mBrowserChild);
+
+ if (!mReadyToDelete) {
+ // This time run this runnable at input priority.
+ mReadyToDelete = true;
+ MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(this));
+ return NS_OK;
+ }
+
+ // Check in case ActorDestroy was called after RecvDestroy message.
+ if (mBrowserChild->IPCOpen()) {
+ Unused << PBrowserChild::Send__delete__(mBrowserChild);
+ }
+
+ mBrowserChild = nullptr;
+ return NS_OK;
+ }
+};
+
+NS_IMPL_ISUPPORTS_INHERITED(BrowserChild::DelayedDeleteRunnable, Runnable,
+ nsIRunnablePriority)
+
+namespace {
+std::map<TabId, RefPtr<BrowserChild>>& NestedBrowserChildMap() {
+ MOZ_ASSERT(NS_IsMainThread());
+ static std::map<TabId, RefPtr<BrowserChild>> sNestedBrowserChildMap;
+ return sNestedBrowserChildMap;
+}
+} // namespace
+
+already_AddRefed<BrowserChild> BrowserChild::FindBrowserChild(
+ const TabId& aTabId) {
+ auto iter = NestedBrowserChildMap().find(aTabId);
+ if (iter == NestedBrowserChildMap().end()) {
+ return nullptr;
+ }
+ RefPtr<BrowserChild> browserChild = iter->second;
+ return browserChild.forget();
+}
+
+/*static*/
+already_AddRefed<BrowserChild> BrowserChild::Create(
+ ContentChild* aManager, const TabId& aTabId, const TabContext& aContext,
+ BrowsingContext* aBrowsingContext, uint32_t aChromeFlags,
+ bool aIsTopLevel) {
+ RefPtr<BrowserChild> iframe = new BrowserChild(
+ aManager, aTabId, aContext, aBrowsingContext, aChromeFlags, aIsTopLevel);
+ return iframe.forget();
+}
+
+BrowserChild::BrowserChild(ContentChild* aManager, const TabId& aTabId,
+ const TabContext& aContext,
+ BrowsingContext* aBrowsingContext,
+ uint32_t aChromeFlags, bool aIsTopLevel)
+ : TabContext(aContext),
+ mBrowserChildMessageManager(nullptr),
+ mManager(aManager),
+ mBrowsingContext(aBrowsingContext),
+ mChromeFlags(aChromeFlags),
+ mMaxTouchPoints(0),
+ mLayersId{0},
+ mEffectsInfo{EffectsInfo::FullyHidden()},
+ mDynamicToolbarMaxHeight(0),
+ mUniqueId(aTabId),
+ mDidFakeShow(false),
+ mTriedBrowserInit(false),
+ mIgnoreKeyPressEvent(false),
+ mHasValidInnerSize(false),
+ mDestroyed(false),
+ mIsTopLevel(aIsTopLevel),
+ mIsTransparent(false),
+ mIPCOpen(false),
+ mDidSetRealShowInfo(false),
+ mDidLoadURLInit(false),
+ mSkipKeyPress(false),
+ mShouldSendWebProgressEventsToParent(false),
+ mRenderLayers(true),
+ mIsPreservingLayers(false),
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+ mNativeWindowHandle(0),
+#endif
+ mCancelContentJSEpoch(0) {
+ mozilla::HoldJSObjects(this);
+
+ // preloaded BrowserChild should not be added to child map
+ if (mUniqueId) {
+ MOZ_ASSERT(NestedBrowserChildMap().find(mUniqueId) ==
+ NestedBrowserChildMap().end());
+ NestedBrowserChildMap()[mUniqueId] = this;
+ }
+ mCoalesceMouseMoveEvents = StaticPrefs::dom_events_coalesce_mousemove();
+ if (mCoalesceMouseMoveEvents) {
+ mCoalescedMouseEventFlusher = new CoalescedMouseMoveFlusher(this);
+ }
+
+ if (StaticPrefs::dom_events_coalesce_touchmove()) {
+ mCoalescedTouchMoveEventFlusher = new CoalescedTouchMoveFlusher(this);
+ }
+}
+
+const CompositorOptions& BrowserChild::GetCompositorOptions() const {
+ // If you're calling this before mCompositorOptions is set, well.. don't.
+ MOZ_ASSERT(mCompositorOptions);
+ return mCompositorOptions.ref();
+}
+
+bool BrowserChild::AsyncPanZoomEnabled() const {
+ // This might get called by the TouchEvent::PrefEnabled code before we have
+ // mCompositorOptions populated (bug 1370089). In that case we just assume
+ // APZ is enabled because we're in a content process (because BrowserChild)
+ // and APZ is probably going to be enabled here since e10s is enabled.
+ return mCompositorOptions ? mCompositorOptions->UseAPZ() : true;
+}
+
+NS_IMETHODIMP
+BrowserChild::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData) {
+ if (!strcmp(aTopic, BEFORE_FIRST_PAINT)) {
+ if (AsyncPanZoomEnabled()) {
+ nsCOMPtr<Document> subject(do_QueryInterface(aSubject));
+ nsCOMPtr<Document> doc(GetTopLevelDocument());
+
+ if (subject == doc) {
+ RefPtr<PresShell> presShell = doc->GetPresShell();
+ if (presShell) {
+ presShell->SetIsFirstPaint(true);
+ }
+
+ APZCCallbackHelper::InitializeRootDisplayport(presShell);
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+void BrowserChild::ContentReceivedInputBlock(uint64_t aInputBlockId,
+ bool aPreventDefault) const {
+ if (mApzcTreeManager) {
+ mApzcTreeManager->ContentReceivedInputBlock(aInputBlockId, aPreventDefault);
+ }
+}
+
+void BrowserChild::SetTargetAPZC(
+ uint64_t aInputBlockId,
+ const nsTArray<ScrollableLayerGuid>& aTargets) const {
+ if (mApzcTreeManager) {
+ mApzcTreeManager->SetTargetAPZC(aInputBlockId, aTargets);
+ }
+}
+
+bool BrowserChild::DoUpdateZoomConstraints(
+ const uint32_t& aPresShellId, const ViewID& aViewId,
+ const Maybe<ZoomConstraints>& aConstraints) {
+ if (!mApzcTreeManager || mDestroyed) {
+ return false;
+ }
+
+ ScrollableLayerGuid guid =
+ ScrollableLayerGuid(mLayersId, aPresShellId, aViewId);
+
+ mApzcTreeManager->UpdateZoomConstraints(guid, aConstraints);
+ return true;
+}
+
+nsresult BrowserChild::Init(mozIDOMWindowProxy* aParent,
+ WindowGlobalChild* aInitialWindowChild) {
+ MOZ_ASSERT_IF(aInitialWindowChild,
+ aInitialWindowChild->BrowsingContext() == mBrowsingContext);
+
+ nsCOMPtr<nsIWidget> widget = nsIWidget::CreatePuppetWidget(this);
+ mPuppetWidget = static_cast<PuppetWidget*>(widget.get());
+ if (!mPuppetWidget) {
+ NS_ERROR("couldn't create fake widget");
+ return NS_ERROR_FAILURE;
+ }
+ mPuppetWidget->InfallibleCreate(nullptr,
+ nullptr, // no parents
+ LayoutDeviceIntRect(0, 0, 0, 0),
+ nullptr); // HandleWidgetEvent
+
+ mWebBrowser = nsWebBrowser::Create(this, mPuppetWidget, mBrowsingContext,
+ aInitialWindowChild);
+ nsIWebBrowser* webBrowser = mWebBrowser;
+
+ mWebNav = do_QueryInterface(webBrowser);
+ NS_ASSERTION(mWebNav, "nsWebBrowser doesn't implement nsIWebNavigation?");
+
+ // IPC uses a WebBrowser object for which DNS prefetching is turned off
+ // by default. But here we really want it, so enable it explicitly
+ mWebBrowser->SetAllowDNSPrefetch(true);
+
+ nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
+ MOZ_ASSERT(docShell);
+
+ mStatusFilter = new nsBrowserStatusFilter();
+
+ nsresult rv =
+ mStatusFilter->AddProgressListener(this, nsIWebProgress::NOTIFY_ALL);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ {
+ nsCOMPtr<nsIWebProgress> webProgress = do_QueryInterface(docShell);
+ rv = webProgress->AddProgressListener(mStatusFilter,
+ nsIWebProgress::NOTIFY_ALL);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+
+#ifdef DEBUG
+ nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(WebNavigation());
+ MOZ_ASSERT(loadContext);
+ MOZ_ASSERT(loadContext->UseRemoteTabs() ==
+ !!(mChromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW));
+ MOZ_ASSERT(loadContext->UseRemoteSubframes() ==
+ !!(mChromeFlags & nsIWebBrowserChrome::CHROME_FISSION_WINDOW));
+#endif // defined(DEBUG)
+
+ // Few lines before, baseWindow->Create() will end up creating a new
+ // window root in nsGlobalWindowOuter::SetDocShell.
+ // Then this chrome event handler, will be inherited to inner windows.
+ // We want to also set it to the docshell so that inner windows
+ // and any code that has access to the docshell
+ // can all listen to the same chrome event handler.
+ // XXX: ideally, we would set a chrome event handler earlier,
+ // and all windows, even the root one, will use the docshell one.
+ nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation());
+ NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
+ nsCOMPtr<EventTarget> chromeHandler = window->GetChromeEventHandler();
+ docShell->SetChromeEventHandler(chromeHandler);
+
+ // Window scrollbar flags only affect top level remote frames, not fission
+ // frames.
+ if (mIsTopLevel) {
+ nsContentUtils::SetScrollbarsVisibility(
+ docShell, !!(mChromeFlags & nsIWebBrowserChrome::CHROME_SCROLLBARS));
+ }
+
+ nsWeakPtr weakPtrThis = do_GetWeakReference(
+ static_cast<nsIBrowserChild*>(this)); // for capture by the lambda
+ ContentReceivedInputBlockCallback callback(
+ [weakPtrThis](uint64_t aInputBlockId, bool aPreventDefault) {
+ if (nsCOMPtr<nsIBrowserChild> browserChild =
+ do_QueryReferent(weakPtrThis)) {
+ static_cast<BrowserChild*>(browserChild.get())
+ ->ContentReceivedInputBlock(aInputBlockId, aPreventDefault);
+ }
+ });
+ mAPZEventState = new APZEventState(mPuppetWidget, std::move(callback));
+
+ mIPCOpen = true;
+
+ if (StaticPrefs::browser_sessionstore_platform_collection_AtStartup()) {
+ mSessionStoreChild = SessionStoreChild::GetOrCreate(mBrowsingContext);
+ }
+
+ // We've all set up, make sure our visibility state is consistent. This is
+ // important for OOP iframes, which start off as hidden.
+ UpdateVisibility();
+
+ return NS_OK;
+}
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(BrowserChild)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(BrowserChild)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowserChildMessageManager)
+ tmp->nsMessageManagerScriptExecutor::Unlink();
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mStatusFilter)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mWebNav)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowsingContext)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mSessionStoreChild)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mContentTransformPromise)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(BrowserChild)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowserChildMessageManager)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStatusFilter)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWebNav)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowsingContext)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSessionStoreChild)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContentTransformPromise)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(BrowserChild)
+ tmp->nsMessageManagerScriptExecutor::Trace(aCallbacks, aClosure);
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BrowserChild)
+ NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChrome)
+ NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChromeFocus)
+ NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
+ NS_INTERFACE_MAP_ENTRY(nsIWindowProvider)
+ NS_INTERFACE_MAP_ENTRY(nsIBrowserChild)
+ NS_INTERFACE_MAP_ENTRY(nsIObserver)
+ NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+ NS_INTERFACE_MAP_ENTRY(nsITooltipListener)
+ NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
+ NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener2)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIBrowserChild)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(BrowserChild)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(BrowserChild)
+
+NS_IMETHODIMP
+BrowserChild::GetChromeFlags(uint32_t* aChromeFlags) {
+ *aChromeFlags = mChromeFlags;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BrowserChild::SetChromeFlags(uint32_t aChromeFlags) {
+ NS_WARNING("trying to SetChromeFlags from content process?");
+
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+BrowserChild::RemoteDropLinks(
+ const nsTArray<RefPtr<nsIDroppedLinkItem>>& aLinks) {
+ nsTArray<nsString> linksArray;
+ nsresult rv = NS_OK;
+ for (nsIDroppedLinkItem* link : aLinks) {
+ nsString tmp;
+ rv = link->GetUrl(tmp);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ linksArray.AppendElement(tmp);
+
+ rv = link->GetName(tmp);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ linksArray.AppendElement(tmp);
+
+ rv = link->GetType(tmp);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ linksArray.AppendElement(tmp);
+ }
+ bool sent = SendDropLinks(linksArray);
+
+ return sent ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+BrowserChild::ShowAsModal() {
+ NS_WARNING("BrowserChild::ShowAsModal not supported in BrowserChild");
+
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+BrowserChild::IsWindowModal(bool* aRetVal) {
+ *aRetVal = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BrowserChild::SetLinkStatus(const nsAString& aStatusText) {
+ // We can only send the status after the ipc machinery is set up
+ if (IPCOpen()) {
+ SendSetLinkStatus(aStatusText);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BrowserChild::SetDimensions(DimensionRequest&& aRequest) {
+ // The parent is in charge of the dimension changes. If JS code wants to
+ // change the dimensions (moveTo, screenX, etc.) we send a message to the
+ // parent about the new requested dimension, the parent does the resize/move
+ // then send a message to the child to update itself. For APIs like screenX
+ // this function is called with only the changed values. In a series of calls
+ // like window.screenX = 10; window.screenY = 10; for the second call, since
+ // screenX is not yet updated we might accidentally reset back screenX to it's
+ // old value. To avoid this, if a parameter did not change, we want the parent
+ // to handle the unchanged values.
+
+ double scale = mPuppetWidget ? mPuppetWidget->GetDefaultScale().scale : 1.0;
+ SendSetDimensions(aRequest, scale);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BrowserChild::GetDimensions(DimensionKind aDimensionKind, int32_t* aX,
+ int32_t* aY, int32_t* aCx, int32_t* aCy) {
+ ScreenIntRect rect = GetOuterRect();
+ if (aDimensionKind == DimensionKind::Inner) {
+ if (aX || aY) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+ }
+ rect.SizeTo(GetInnerSize());
+ }
+ if (aX) {
+ *aX = rect.x;
+ }
+ if (aY) {
+ *aY = rect.y;
+ }
+ if (aCx) {
+ *aCx = rect.width;
+ }
+ if (aCy) {
+ *aCy = rect.height;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BrowserChild::Blur() { return NS_ERROR_NOT_IMPLEMENTED; }
+
+NS_IMETHODIMP
+BrowserChild::FocusNextElement(bool aForDocumentNavigation) {
+ SendMoveFocus(true, aForDocumentNavigation);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BrowserChild::FocusPrevElement(bool aForDocumentNavigation) {
+ SendMoveFocus(false, aForDocumentNavigation);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BrowserChild::GetInterface(const nsIID& aIID, void** aSink) {
+ // XXXbz should we restrict the set of interfaces we hand out here?
+ // See bug 537429
+ return QueryInterface(aIID, aSink);
+}
+
+NS_IMETHODIMP
+BrowserChild::ProvideWindow(nsIOpenWindowInfo* aOpenWindowInfo,
+ uint32_t aChromeFlags, bool aCalledFromJS,
+ nsIURI* aURI, const nsAString& aName,
+ const nsACString& aFeatures,
+ const UserActivation::Modifiers& aModifiers,
+ bool aForceNoOpener, bool aForceNoReferrer,
+ bool aIsPopupRequested,
+ nsDocShellLoadState* aLoadState, bool* aWindowIsNew,
+ BrowsingContext** aReturn) {
+ *aReturn = nullptr;
+
+ RefPtr<BrowsingContext> parent = aOpenWindowInfo->GetParent();
+
+ int32_t openLocation = nsWindowWatcher::GetWindowOpenLocation(
+ parent->GetDOMWindow(), aChromeFlags, aModifiers, aCalledFromJS,
+ aOpenWindowInfo->GetIsForPrinting());
+
+ // If it turns out we're opening in the current browser, just hand over the
+ // current browser's docshell.
+ if (openLocation == nsIBrowserDOMWindow::OPEN_CURRENTWINDOW) {
+ nsCOMPtr<nsIWebBrowser> browser = do_GetInterface(WebNavigation());
+ *aWindowIsNew = false;
+
+ nsCOMPtr<mozIDOMWindowProxy> win;
+ MOZ_TRY(browser->GetContentDOMWindow(getter_AddRefs(win)));
+
+ RefPtr<BrowsingContext> bc(
+ nsPIDOMWindowOuter::From(win)->GetBrowsingContext());
+ bc.forget(aReturn);
+ return NS_OK;
+ }
+
+ // Note that ProvideWindowCommon may return NS_ERROR_ABORT if the
+ // open window call was canceled. It's important that we pass this error
+ // code back to our caller.
+ ContentChild* cc = ContentChild::GetSingleton();
+ return cc->ProvideWindowCommon(
+ WrapNotNull(this), aOpenWindowInfo, aChromeFlags, aCalledFromJS, aURI,
+ aName, aFeatures, aModifiers, aForceNoOpener, aForceNoReferrer,
+ aIsPopupRequested, aLoadState, aWindowIsNew, aReturn);
+}
+
+void BrowserChild::DestroyWindow() {
+ mBrowsingContext = nullptr;
+
+ if (mStatusFilter) {
+ if (nsCOMPtr<nsIWebProgress> webProgress =
+ do_QueryInterface(WebNavigation())) {
+ webProgress->RemoveProgressListener(mStatusFilter);
+ }
+
+ mStatusFilter->RemoveProgressListener(this);
+ mStatusFilter = nullptr;
+ }
+
+ if (mCoalescedMouseEventFlusher) {
+ mCoalescedMouseEventFlusher->RemoveObserver();
+ mCoalescedMouseEventFlusher = nullptr;
+ }
+
+ if (mCoalescedTouchMoveEventFlusher) {
+ mCoalescedTouchMoveEventFlusher->RemoveObserver();
+ mCoalescedTouchMoveEventFlusher = nullptr;
+ }
+
+ if (mSessionStoreChild) {
+ mSessionStoreChild->Stop();
+ mSessionStoreChild = nullptr;
+ }
+
+ // In case we don't have chance to process all entries, clean all data in
+ // the queue.
+ while (mToBeDispatchedMouseData.GetSize() > 0) {
+ UniquePtr<CoalescedMouseData> data(
+ static_cast<CoalescedMouseData*>(mToBeDispatchedMouseData.PopFront()));
+ data.reset();
+ }
+
+ nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(WebNavigation());
+ if (baseWindow) baseWindow->Destroy();
+
+ if (mPuppetWidget) {
+ mPuppetWidget->Destroy();
+ }
+
+ mLayersConnected = Nothing();
+
+ if (mLayersId.IsValid()) {
+ StaticMutexAutoLock lock(sBrowserChildrenMutex);
+
+ MOZ_ASSERT(sBrowserChildren);
+ sBrowserChildren->Remove(uint64_t(mLayersId));
+ if (!sBrowserChildren->Count()) {
+ delete sBrowserChildren;
+ sBrowserChildren = nullptr;
+ }
+ mLayersId = layers::LayersId{0};
+ }
+
+ if (mAPZEventState) {
+ mAPZEventState->Destroy();
+ mAPZEventState = nullptr;
+ }
+}
+
+void BrowserChild::ActorDestroy(ActorDestroyReason why) {
+ mIPCOpen = false;
+
+ DestroyWindow();
+
+ if (mBrowserChildMessageManager) {
+ // We should have a message manager if the global is alive, but it
+ // seems sometimes we don't. Assert in aurora/nightly, but don't
+ // crash in release builds.
+ MOZ_DIAGNOSTIC_ASSERT(mBrowserChildMessageManager->GetMessageManager());
+ if (mBrowserChildMessageManager->GetMessageManager()) {
+ // The messageManager relays messages via the BrowserChild which
+ // no longer exists.
+ mBrowserChildMessageManager->DisconnectMessageManager();
+ }
+ }
+
+ if (GetTabId() != 0) {
+ NestedBrowserChildMap().erase(GetTabId());
+ }
+}
+
+BrowserChild::~BrowserChild() {
+ mAnonymousGlobalScopes.Clear();
+
+ DestroyWindow();
+
+ nsCOMPtr<nsIWebBrowser> webBrowser = do_QueryInterface(WebNavigation());
+ if (webBrowser) {
+ webBrowser->SetContainerWindow(nullptr);
+ }
+
+ mozilla::DropJSObjects(this);
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvWillChangeProcess() {
+ if (mWebBrowser) {
+ mWebBrowser->SetWillChangeProcess();
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvLoadURL(
+ nsDocShellLoadState* aLoadState, const ParentShowInfo& aInfo) {
+ if (!mDidLoadURLInit) {
+ mDidLoadURLInit = true;
+ if (!InitBrowserChildMessageManager()) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ ApplyParentShowInfo(aInfo);
+ }
+ nsAutoCString spec;
+ aLoadState->URI()->GetSpec(spec);
+
+ nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
+ if (!docShell) {
+ NS_WARNING("WebNavigation does not have a docshell");
+ return IPC_OK();
+ }
+ docShell->LoadURI(aLoadState, true);
+
+ CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::URL, spec);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvCreateAboutBlankDocumentViewer(
+ nsIPrincipal* aPrincipal, nsIPrincipal* aPartitionedPrincipal) {
+ if (aPrincipal->GetIsExpandedPrincipal() ||
+ aPartitionedPrincipal->GetIsExpandedPrincipal()) {
+ return IPC_FAIL(this, "Cannot create document with an expanded principal");
+ }
+ if (aPrincipal->IsSystemPrincipal() ||
+ aPartitionedPrincipal->IsSystemPrincipal()) {
+ MOZ_ASSERT_UNREACHABLE(
+ "Cannot use CreateAboutBlankDocumentViewer to create system principal "
+ "document in content");
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
+ if (!docShell) {
+ MOZ_ASSERT_UNREACHABLE("WebNavigation does not have a docshell");
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsIURI> currentURI;
+ MOZ_ALWAYS_SUCCEEDS(
+ WebNavigation()->GetCurrentURI(getter_AddRefs(currentURI)));
+ if (!currentURI || !NS_IsAboutBlank(currentURI)) {
+ NS_WARNING("Can't create a ContentViewer unless on about:blank");
+ return IPC_OK();
+ }
+
+ docShell->CreateAboutBlankDocumentViewer(aPrincipal, aPartitionedPrincipal,
+ nullptr);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvResumeLoad(
+ const uint64_t& aPendingSwitchID, const ParentShowInfo& aInfo) {
+ if (!mDidLoadURLInit) {
+ mDidLoadURLInit = true;
+ if (!InitBrowserChildMessageManager()) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ ApplyParentShowInfo(aInfo);
+ }
+
+ nsresult rv = WebNavigation()->ResumeRedirectedLoad(aPendingSwitchID, -1);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("WebNavigation()->ResumeRedirectedLoad failed");
+ }
+
+ return IPC_OK();
+}
+
+nsresult BrowserChild::CloneDocumentTreeIntoSelf(
+ const MaybeDiscarded<BrowsingContext>& aSourceBC,
+ const embedding::PrintData& aPrintData) {
+#ifdef NS_PRINTING
+ if (NS_WARN_IF(aSourceBC.IsNullOrDiscarded())) {
+ return NS_ERROR_FAILURE;
+ }
+ nsCOMPtr<Document> sourceDocument = aSourceBC.get()->GetDocument();
+ if (NS_WARN_IF(!sourceDocument)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIDocShell> ourDocShell = do_GetInterface(WebNavigation());
+ if (NS_WARN_IF(!ourDocShell)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIDocumentViewer> viewer;
+ ourDocShell->GetDocViewer(getter_AddRefs(viewer));
+ if (NS_WARN_IF(!viewer)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIPrintSettingsService> printSettingsSvc =
+ do_GetService("@mozilla.org/gfx/printsettings-service;1");
+ if (NS_WARN_IF(!printSettingsSvc)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIPrintSettings> printSettings;
+ nsresult rv =
+ printSettingsSvc->CreateNewPrintSettings(getter_AddRefs(printSettings));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ printSettingsSvc->DeserializeToPrintSettings(aPrintData, printSettings);
+
+ RefPtr<Document> clone;
+ {
+ AutoPrintEventDispatcher dispatcher(*sourceDocument);
+ nsAutoScriptBlocker scriptBlocker;
+ bool hasInProcessCallbacks = false;
+ clone = sourceDocument->CreateStaticClone(
+ ourDocShell, viewer, printSettings, &hasInProcessCallbacks);
+ if (NS_WARN_IF(!clone)) {
+ return NS_ERROR_FAILURE;
+ }
+ }
+
+ rv = UpdateRemotePrintSettings(aPrintData);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+#endif
+ return NS_OK;
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvCloneDocumentTreeIntoSelf(
+ const MaybeDiscarded<BrowsingContext>& aSourceBC,
+ const embedding::PrintData& aPrintData,
+ CloneDocumentTreeIntoSelfResolver&& aResolve) {
+ nsresult rv = NS_OK;
+
+#ifdef NS_PRINTING
+ rv = CloneDocumentTreeIntoSelf(aSourceBC, aPrintData);
+#endif
+
+ aResolve(NS_SUCCEEDED(rv));
+ return IPC_OK();
+}
+
+nsresult BrowserChild::UpdateRemotePrintSettings(
+ const embedding::PrintData& aPrintData) {
+#ifdef NS_PRINTING
+ nsCOMPtr<nsIDocShell> ourDocShell = do_GetInterface(WebNavigation());
+ if (NS_WARN_IF(!ourDocShell)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<Document> doc = ourDocShell->GetExtantDocument();
+ if (NS_WARN_IF(!doc) || NS_WARN_IF(!doc->IsStaticDocument())) {
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<BrowsingContext> bc = ourDocShell->GetBrowsingContext();
+ if (NS_WARN_IF(!bc)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIPrintSettingsService> printSettingsSvc =
+ do_GetService("@mozilla.org/gfx/printsettings-service;1");
+ if (NS_WARN_IF(!printSettingsSvc)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIPrintSettings> printSettings;
+ nsresult rv =
+ printSettingsSvc->CreateNewPrintSettings(getter_AddRefs(printSettings));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ printSettingsSvc->DeserializeToPrintSettings(aPrintData, printSettings);
+
+ bc->PreOrderWalk([&](BrowsingContext* aBc) {
+ if (nsCOMPtr<nsIDocShell> inProcess = aBc->GetDocShell()) {
+ nsCOMPtr<nsIDocumentViewer> viewer;
+ inProcess->GetDocViewer(getter_AddRefs(viewer));
+ if (NS_WARN_IF(!viewer)) {
+ return BrowsingContext::WalkFlag::Skip;
+ }
+ // The CanRunScript analysis is not smart enough to see across
+ // the std::function PreOrderWalk uses, so we cheat a bit here, but it is
+ // fine because PreOrderWalk does deal with arbitrary script changing the
+ // BC tree, and our code above is simple enough and keeps strong refs to
+ // everything.
+ ([&]() MOZ_CAN_RUN_SCRIPT_BOUNDARY {
+ RefPtr<RemotePrintJobChild> printJob =
+ static_cast<RemotePrintJobChild*>(
+ aPrintData.remotePrintJob().AsChild());
+ viewer->SetPrintSettingsForSubdocument(printSettings, printJob);
+ }());
+ } else if (RefPtr<BrowserBridgeChild> remoteChild =
+ BrowserBridgeChild::GetFrom(aBc->GetEmbedderElement())) {
+ Unused << remoteChild->SendUpdateRemotePrintSettings(aPrintData);
+ return BrowsingContext::WalkFlag::Skip;
+ }
+ return BrowsingContext::WalkFlag::Next;
+ });
+#endif
+
+ return NS_OK;
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvUpdateRemotePrintSettings(
+ const embedding::PrintData& aPrintData) {
+#ifdef NS_PRINTING
+ UpdateRemotePrintSettings(aPrintData);
+#endif
+
+ return IPC_OK();
+}
+
+void BrowserChild::DoFakeShow(const ParentShowInfo& aParentShowInfo) {
+ OwnerShowInfo ownerInfo{ScreenIntSize(), ScrollbarPreference::Auto,
+ nsSizeMode_Normal};
+ RecvShow(aParentShowInfo, ownerInfo);
+ mDidFakeShow = true;
+}
+
+void BrowserChild::ApplyParentShowInfo(const ParentShowInfo& aInfo) {
+ // Even if we already set real show info, the dpi / rounding & scale may still
+ // be invalid (if BrowserParent wasn't able to get widget it would just send
+ // 0). So better to always set up-to-date values here.
+ if (aInfo.dpi() > 0) {
+ mPuppetWidget->UpdateBackingScaleCache(aInfo.dpi(), aInfo.widgetRounding(),
+ aInfo.defaultScale());
+ }
+
+ if (mDidSetRealShowInfo) {
+ return;
+ }
+
+ if (!aInfo.fakeShowInfo()) {
+ // Once we've got one ShowInfo from parent, no need to update the values
+ // anymore.
+ mDidSetRealShowInfo = true;
+ }
+
+ mIsTransparent = aInfo.isTransparent();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvShow(
+ const ParentShowInfo& aParentInfo, const OwnerShowInfo& aOwnerInfo) {
+ bool res = true;
+
+ mPuppetWidget->SetSizeMode(aOwnerInfo.sizeMode());
+ if (!mDidFakeShow) {
+ nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(WebNavigation());
+ if (!baseWindow) {
+ NS_ERROR("WebNavigation() doesn't QI to nsIBaseWindow");
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ baseWindow->SetVisibility(true);
+ res = InitBrowserChildMessageManager();
+ }
+
+ ApplyParentShowInfo(aParentInfo);
+
+ if (!mIsTopLevel) {
+ RecvScrollbarPreferenceChanged(aOwnerInfo.scrollbarPreference());
+ }
+
+ if (!res) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ UpdateVisibility();
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvInitRendering(
+ const TextureFactoryIdentifier& aTextureFactoryIdentifier,
+ const layers::LayersId& aLayersId,
+ const CompositorOptions& aCompositorOptions, const bool& aLayersConnected) {
+ mLayersConnected = Some(aLayersConnected);
+ mLayersConnectRequested = Some(aLayersConnected);
+ InitRenderingState(aTextureFactoryIdentifier, aLayersId, aCompositorOptions);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvScrollbarPreferenceChanged(
+ ScrollbarPreference aPreference) {
+ MOZ_ASSERT(!mIsTopLevel,
+ "Scrollbar visibility should be derived from chrome flags for "
+ "top-level windows");
+ if (nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation())) {
+ nsDocShell::Cast(docShell)->SetScrollbarPreference(aPreference);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvCompositorOptionsChanged(
+ const CompositorOptions& aNewOptions) {
+ MOZ_ASSERT(mCompositorOptions);
+
+ // The only compositor option we currently support changing is APZ
+ // enablement. Even that is only partially supported for now:
+ // * Going from APZ to non-APZ is fine - we just flip the stored flag.
+ // Note that we keep the actors (mApzcTreeManager, and the APZChild
+ // created in InitAPZState()) around (read on for why).
+ // * Going from non-APZ to APZ is only supported if we were using
+ // APZ initially (at InitRendering() time) and we are transitioning
+ // back. In this case, we just reuse the actors which we kept around.
+ // Fully supporting a non-APZ to APZ transition (i.e. even in cases
+ // where we initialized as non-APZ) would require setting up the actors
+ // here. (In that case, we would also have the options of destroying
+ // the actors in the APZ --> non-APZ case, and always re-creating them
+ // during a non-APZ --> APZ transition).
+ mCompositorOptions->SetUseAPZ(aNewOptions.UseAPZ());
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvUpdateDimensions(
+ const DimensionInfo& aDimensionInfo) {
+ if (mLayersConnected.isNothing()) {
+ return IPC_OK();
+ }
+
+ mUnscaledOuterRect = aDimensionInfo.rect();
+ mClientOffset = aDimensionInfo.clientOffset();
+ mChromeOffset = aDimensionInfo.chromeOffset();
+ MOZ_ASSERT_IF(!IsTopLevel(), mChromeOffset == LayoutDeviceIntPoint());
+
+ SetUnscaledInnerSize(aDimensionInfo.size());
+ if (!mHasValidInnerSize && aDimensionInfo.size().width != 0 &&
+ aDimensionInfo.size().height != 0) {
+ mHasValidInnerSize = true;
+ }
+
+ ScreenIntSize screenSize = GetInnerSize();
+ ScreenIntRect screenRect = GetOuterRect();
+
+ // Make sure to set the size on the document viewer first. The
+ // MobileViewportManager needs the content viewer size to be updated before
+ // the reflow, otherwise it gets a stale size when it computes a new CSS
+ // viewport.
+ nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(WebNavigation());
+ baseWin->SetPositionAndSize(0, 0, screenSize.width, screenSize.height,
+ nsIBaseWindow::eRepaint);
+
+ mPuppetWidget->Resize(screenRect.x + mClientOffset.x + mChromeOffset.x,
+ screenRect.y + mClientOffset.y + mChromeOffset.y,
+ screenSize.width, screenSize.height, true);
+
+ RecvSafeAreaInsetsChanged(mPuppetWidget->GetSafeAreaInsets());
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvSizeModeChanged(
+ const nsSizeMode& aSizeMode) {
+ mPuppetWidget->SetSizeMode(aSizeMode);
+ if (!mPuppetWidget->IsVisible()) {
+ return IPC_OK();
+ }
+ nsCOMPtr<Document> document(GetTopLevelDocument());
+ if (!document) {
+ return IPC_OK();
+ }
+ nsPresContext* presContext = document->GetPresContext();
+ if (presContext) {
+ presContext->SizeModeChanged(aSizeMode);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvChildToParentMatrix(
+ const Maybe<gfx::Matrix4x4>& aMatrix,
+ const ScreenRect& aTopLevelViewportVisibleRectInBrowserCoords) {
+ mChildToParentConversionMatrix =
+ LayoutDeviceToLayoutDeviceMatrix4x4::FromUnknownMatrix(aMatrix);
+ mTopLevelViewportVisibleRectInBrowserCoords =
+ aTopLevelViewportVisibleRectInBrowserCoords;
+
+ if (mContentTransformPromise) {
+ mContentTransformPromise->MaybeResolveWithUndefined();
+ mContentTransformPromise = nullptr;
+ }
+
+ // Trigger an intersection observation update since ancestor viewports
+ // changed.
+ if (RefPtr<Document> toplevelDoc = GetTopLevelDocument()) {
+ if (nsPresContext* pc = toplevelDoc->GetPresContext()) {
+ pc->RefreshDriver()->EnsureIntersectionObservationsUpdateHappens();
+ }
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvUpdateRemoteStyle(
+ const StyleImageRendering& aImageRendering) {
+ BrowsingContext* context = GetBrowsingContext();
+ if (!context) {
+ return IPC_OK();
+ }
+
+ Document* document = context->GetDocument();
+ if (!document) {
+ return IPC_OK();
+ }
+
+ if (document->IsImageDocument()) {
+ document->AsImageDocument()->UpdateRemoteStyle(aImageRendering);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvDynamicToolbarMaxHeightChanged(
+ const ScreenIntCoord& aHeight) {
+#if defined(MOZ_WIDGET_ANDROID)
+ mDynamicToolbarMaxHeight = aHeight;
+
+ RefPtr<Document> document = GetTopLevelDocument();
+ if (!document) {
+ return IPC_OK();
+ }
+
+ if (RefPtr<nsPresContext> presContext = document->GetPresContext()) {
+ presContext->SetDynamicToolbarMaxHeight(aHeight);
+ }
+#endif
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvDynamicToolbarOffsetChanged(
+ const ScreenIntCoord& aOffset) {
+#if defined(MOZ_WIDGET_ANDROID)
+ RefPtr<Document> document = GetTopLevelDocument();
+ if (!document) {
+ return IPC_OK();
+ }
+
+ if (nsPresContext* presContext = document->GetPresContext()) {
+ presContext->UpdateDynamicToolbarOffset(aOffset);
+ }
+#endif
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvSuppressDisplayport(
+ const bool& aEnabled) {
+ if (RefPtr<PresShell> presShell = GetTopLevelPresShell()) {
+ presShell->SuppressDisplayport(aEnabled);
+ }
+ return IPC_OK();
+}
+
+void BrowserChild::HandleDoubleTap(const CSSPoint& aPoint,
+ const Modifiers& aModifiers,
+ const ScrollableLayerGuid& aGuid,
+ const DoubleTapToZoomMetrics& aMetrics) {
+ MOZ_LOG(
+ sApzChildLog, LogLevel::Debug,
+ ("Handling double tap at %s with %p %p\n", ToString(aPoint).c_str(),
+ mBrowserChildMessageManager ? mBrowserChildMessageManager->GetWrapper()
+ : nullptr,
+ mBrowserChildMessageManager.get()));
+
+ if (!mBrowserChildMessageManager) {
+ return;
+ }
+
+ // Note: there is nothing to do with the modifiers here, as we are not
+ // synthesizing any sort of mouse event.
+ RefPtr<Document> document = GetTopLevelDocument();
+ ZoomTarget zoomTarget = CalculateRectToZoomTo(document, aPoint, aMetrics);
+ // The double-tap can be dispatched by any scroll frame (so |aGuid| could be
+ // the guid of any scroll frame), but the zoom-to-rect operation must be
+ // performed by the root content scroll frame, so query its identifiers
+ // for the SendZoomToRect() call rather than using the ones from |aGuid|.
+ uint32_t presShellId;
+ ViewID viewId;
+ if (APZCCallbackHelper::GetOrCreateScrollIdentifiers(
+ document->GetDocumentElement(), &presShellId, &viewId) &&
+ mApzcTreeManager) {
+ ScrollableLayerGuid guid(mLayersId, presShellId, viewId);
+
+ mApzcTreeManager->ZoomToRect(guid, zoomTarget,
+ ZoomToRectBehavior::DEFAULT_BEHAVIOR);
+ }
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvHandleTap(
+ const GeckoContentController::TapType& aType,
+ const LayoutDevicePoint& aPoint, const Modifiers& aModifiers,
+ const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId,
+ const Maybe<DoubleTapToZoomMetrics>& aDoubleTapToZoomMetrics) {
+ // IPDL doesn't hold a strong reference to protocols as they're not required
+ // to be refcounted. This function can run script, which may trigger a nested
+ // event loop, which may release this, so we hold a strong reference here.
+ RefPtr<BrowserChild> kungFuDeathGrip(this);
+ RefPtr<PresShell> presShell = GetTopLevelPresShell();
+ if (!presShell || !presShell->GetPresContext() || !mAPZEventState) {
+ return IPC_OK();
+ }
+ CSSToLayoutDeviceScale scale(
+ presShell->GetPresContext()->CSSToDevPixelScale());
+ CSSPoint point = aPoint / scale;
+
+ // Stash the guid in InputAPZContext so that when the visual-to-layout
+ // transform is applied to the event's coordinates, we use the right transform
+ // based on the scroll frame being targeted.
+ // The other values don't really matter.
+ InputAPZContext context(aGuid, aInputBlockId, nsEventStatus_eSentinel);
+
+ switch (aType) {
+ case GeckoContentController::TapType::eSingleTap:
+ if (mBrowserChildMessageManager) {
+ RefPtr<APZEventState> eventState(mAPZEventState);
+ eventState->ProcessSingleTap(point, scale, aModifiers, 1,
+ aInputBlockId);
+ }
+ break;
+ case GeckoContentController::TapType::eDoubleTap:
+ HandleDoubleTap(point, aModifiers, aGuid, *aDoubleTapToZoomMetrics);
+ break;
+ case GeckoContentController::TapType::eSecondTap:
+ if (mBrowserChildMessageManager) {
+ RefPtr<APZEventState> eventState(mAPZEventState);
+ eventState->ProcessSingleTap(point, scale, aModifiers, 2,
+ aInputBlockId);
+ }
+ break;
+ case GeckoContentController::TapType::eLongTap:
+ if (mBrowserChildMessageManager) {
+ RefPtr<APZEventState> eventState(mAPZEventState);
+ eventState->ProcessLongTap(presShell, point, scale, aModifiers,
+ aInputBlockId);
+ }
+ break;
+ case GeckoContentController::TapType::eLongTapUp:
+ if (mBrowserChildMessageManager) {
+ RefPtr<APZEventState> eventState(mAPZEventState);
+ eventState->ProcessLongTapUp(presShell, point, scale, aModifiers);
+ }
+ break;
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvNormalPriorityHandleTap(
+ const GeckoContentController::TapType& aType,
+ const LayoutDevicePoint& aPoint, const Modifiers& aModifiers,
+ const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId,
+ const Maybe<DoubleTapToZoomMetrics>& aDoubleTapToZoomMetrics) {
+ // IPDL doesn't hold a strong reference to protocols as they're not required
+ // to be refcounted. This function can run script, which may trigger a nested
+ // event loop, which may release this, so we hold a strong reference here.
+ RefPtr<BrowserChild> kungFuDeathGrip(this);
+ return RecvHandleTap(aType, aPoint, aModifiers, aGuid, aInputBlockId,
+ aDoubleTapToZoomMetrics);
+}
+
+void BrowserChild::NotifyAPZStateChange(
+ const ViewID& aViewId,
+ const layers::GeckoContentController::APZStateChange& aChange,
+ const int& aArg, Maybe<uint64_t> aInputBlockId) {
+ if (mAPZEventState) {
+ mAPZEventState->ProcessAPZStateChange(aViewId, aChange, aArg,
+ aInputBlockId);
+ }
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ if (aChange ==
+ layers::GeckoContentController::APZStateChange::eTransformEnd) {
+ // This is used by tests to determine when the APZ is done doing whatever
+ // it's doing. XXX generify this as needed when writing additional tests.
+ observerService->NotifyObservers(nullptr, "APZ:TransformEnd", nullptr);
+ observerService->NotifyObservers(nullptr, "PanZoom:StateChange",
+ u"NOTHING");
+ } else if (aChange ==
+ layers::GeckoContentController::APZStateChange::eTransformBegin) {
+ observerService->NotifyObservers(nullptr, "PanZoom:StateChange",
+ u"PANNING");
+ }
+}
+
+void BrowserChild::StartScrollbarDrag(
+ const layers::AsyncDragMetrics& aDragMetrics) {
+ ScrollableLayerGuid guid(mLayersId, aDragMetrics.mPresShellId,
+ aDragMetrics.mViewId);
+
+ if (mApzcTreeManager) {
+ mApzcTreeManager->StartScrollbarDrag(guid, aDragMetrics);
+ }
+}
+
+void BrowserChild::ZoomToRect(const uint32_t& aPresShellId,
+ const ScrollableLayerGuid::ViewID& aViewId,
+ const CSSRect& aRect, const uint32_t& aFlags) {
+ ScrollableLayerGuid guid(mLayersId, aPresShellId, aViewId);
+
+ if (mApzcTreeManager) {
+ mApzcTreeManager->ZoomToRect(guid, ZoomTarget{aRect}, aFlags);
+ }
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvActivate(uint64_t aActionId) {
+ MOZ_ASSERT(mWebBrowser);
+ mWebBrowser->FocusActivate(aActionId);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvDeactivate(uint64_t aActionId) {
+ MOZ_ASSERT(mWebBrowser);
+ mWebBrowser->FocusDeactivate(aActionId);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvStopIMEStateManagement() {
+ IMEStateManager::StopIMEStateManagement();
+ return IPC_OK();
+}
+
+void BrowserChild::ProcessPendingCoalescedTouchData() {
+ MOZ_ASSERT(StaticPrefs::dom_events_coalesce_touchmove());
+
+ if (mCoalescedTouchData.IsEmpty()) {
+ return;
+ }
+
+ if (mCoalescedTouchMoveEventFlusher) {
+ mCoalescedTouchMoveEventFlusher->RemoveObserver();
+ }
+
+ UniquePtr<WidgetTouchEvent> touchMoveEvent =
+ mCoalescedTouchData.TakeCoalescedEvent();
+ Unused << RecvRealTouchEvent(*touchMoveEvent,
+ mCoalescedTouchData.GetScrollableLayerGuid(),
+ mCoalescedTouchData.GetInputBlockId(),
+ mCoalescedTouchData.GetApzResponse());
+}
+
+void BrowserChild::ProcessPendingCoalescedMouseDataAndDispatchEvents() {
+ if (!mCoalesceMouseMoveEvents || !mCoalescedMouseEventFlusher) {
+ // We don't enable mouse coalescing or we are destroying BrowserChild.
+ return;
+ }
+
+ // We may reentry the event loop and push more data to
+ // mToBeDispatchedMouseData while dispatching an event.
+
+ // We may have some pending coalesced data while dispatch an event and reentry
+ // the event loop. In that case we don't have chance to consume the remainding
+ // pending data until we get new mouse events. Get some helps from
+ // mCoalescedMouseEventFlusher to trigger it.
+ mCoalescedMouseEventFlusher->StartObserver();
+
+ while (mToBeDispatchedMouseData.GetSize() > 0) {
+ UniquePtr<CoalescedMouseData> data(
+ static_cast<CoalescedMouseData*>(mToBeDispatchedMouseData.PopFront()));
+
+ UniquePtr<WidgetMouseEvent> event = data->TakeCoalescedEvent();
+ if (event) {
+ // Dispatch the pending events. Using HandleRealMouseButtonEvent
+ // to bypass the coalesce handling in RecvRealMouseMoveEvent. Can't use
+ // RecvRealMouseButtonEvent because we may also put some mouse events
+ // other than mousemove.
+ HandleRealMouseButtonEvent(*event, data->GetScrollableLayerGuid(),
+ data->GetInputBlockId());
+ }
+ }
+ // mCoalescedMouseEventFlusher may be destroyed when reentrying the event
+ // loop.
+ if (mCoalescedMouseEventFlusher) {
+ mCoalescedMouseEventFlusher->RemoveObserver();
+ }
+}
+
+LayoutDeviceToLayoutDeviceMatrix4x4
+BrowserChild::GetChildToParentConversionMatrix() const {
+ if (mChildToParentConversionMatrix) {
+ return *mChildToParentConversionMatrix;
+ }
+ LayoutDevicePoint offset(GetChromeOffset());
+ return LayoutDeviceToLayoutDeviceMatrix4x4::Translation(offset);
+}
+
+Maybe<ScreenRect> BrowserChild::GetTopLevelViewportVisibleRectInBrowserCoords()
+ const {
+ if (!mChildToParentConversionMatrix) {
+ return Nothing();
+ }
+ return Some(mTopLevelViewportVisibleRectInBrowserCoords);
+}
+
+void BrowserChild::FlushAllCoalescedMouseData() {
+ MOZ_ASSERT(mCoalesceMouseMoveEvents);
+
+ // Move all entries from mCoalescedMouseData to mToBeDispatchedMouseData.
+ for (const auto& data : mCoalescedMouseData.Values()) {
+ if (!data || data->IsEmpty()) {
+ continue;
+ }
+ UniquePtr<CoalescedMouseData> dispatchData =
+ MakeUnique<CoalescedMouseData>();
+
+ dispatchData->RetrieveDataFrom(*data);
+ mToBeDispatchedMouseData.Push(dispatchData.release());
+ }
+ mCoalescedMouseData.Clear();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvRealMouseMoveEvent(
+ const WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId) {
+ if (mCoalesceMouseMoveEvents && mCoalescedMouseEventFlusher) {
+ CoalescedMouseData* data =
+ mCoalescedMouseData.GetOrInsertNew(aEvent.pointerId);
+ MOZ_ASSERT(data);
+ if (data->CanCoalesce(aEvent, aGuid, aInputBlockId)) {
+ data->Coalesce(aEvent, aGuid, aInputBlockId);
+ mCoalescedMouseEventFlusher->StartObserver();
+ return IPC_OK();
+ }
+ // Can't coalesce current mousemove event. Put the coalesced mousemove data
+ // with the same pointer id to mToBeDispatchedMouseData, coalesce the
+ // current one, and process all pending data in mToBeDispatchedMouseData.
+ UniquePtr<CoalescedMouseData> dispatchData =
+ MakeUnique<CoalescedMouseData>();
+
+ dispatchData->RetrieveDataFrom(*data);
+ mToBeDispatchedMouseData.Push(dispatchData.release());
+
+ // Put new data to replace the old one in the hash table.
+ CoalescedMouseData* newData =
+ mCoalescedMouseData
+ .InsertOrUpdate(aEvent.pointerId, MakeUnique<CoalescedMouseData>())
+ .get();
+ newData->Coalesce(aEvent, aGuid, aInputBlockId);
+
+ // Dispatch all pending mouse events.
+ ProcessPendingCoalescedMouseDataAndDispatchEvents();
+ mCoalescedMouseEventFlusher->StartObserver();
+ } else if (!RecvRealMouseButtonEvent(aEvent, aGuid, aInputBlockId)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvRealMouseMoveEventForTests(
+ const WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId) {
+ return RecvRealMouseMoveEvent(aEvent, aGuid, aInputBlockId);
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvNormalPriorityRealMouseMoveEvent(
+ const WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId) {
+ return RecvRealMouseMoveEvent(aEvent, aGuid, aInputBlockId);
+}
+
+mozilla::ipc::IPCResult
+BrowserChild::RecvNormalPriorityRealMouseMoveEventForTests(
+ const WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId) {
+ return RecvRealMouseMoveEvent(aEvent, aGuid, aInputBlockId);
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvSynthMouseMoveEvent(
+ const WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId) {
+ if (!RecvRealMouseButtonEvent(aEvent, aGuid, aInputBlockId)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvNormalPrioritySynthMouseMoveEvent(
+ const WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId) {
+ return RecvSynthMouseMoveEvent(aEvent, aGuid, aInputBlockId);
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvRealMouseButtonEvent(
+ const WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId) {
+ if (mCoalesceMouseMoveEvents && mCoalescedMouseEventFlusher &&
+ aEvent.mMessage != eMouseMove) {
+ // When receiving a mouse event other than mousemove, we have to dispatch
+ // all coalesced events before it. However, we can't dispatch all pending
+ // coalesced events directly because we may reentry the event loop while
+ // dispatching. To make sure we won't dispatch disorder events, we move all
+ // coalesced mousemove events and current event to a deque to dispatch them.
+ // When reentrying the event loop and dispatching more events, we put new
+ // events in the end of the nsQueue and dispatch events from the beginning.
+ FlushAllCoalescedMouseData();
+
+ UniquePtr<CoalescedMouseData> dispatchData =
+ MakeUnique<CoalescedMouseData>();
+
+ dispatchData->Coalesce(aEvent, aGuid, aInputBlockId);
+ mToBeDispatchedMouseData.Push(dispatchData.release());
+
+ ProcessPendingCoalescedMouseDataAndDispatchEvents();
+ return IPC_OK();
+ }
+ HandleRealMouseButtonEvent(aEvent, aGuid, aInputBlockId);
+ return IPC_OK();
+}
+
+void BrowserChild::HandleRealMouseButtonEvent(const WidgetMouseEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId) {
+ WidgetMouseEvent localEvent(aEvent);
+ localEvent.mWidget = mPuppetWidget;
+
+ // We need one InputAPZContext here to propagate |aGuid| to places in
+ // SendSetTargetAPZCNotification() which apply the visual-to-layout transform,
+ // and another below to propagate the |postLayerization| flag (whose value
+ // we don't know until SendSetTargetAPZCNotification() returns) into
+ // the event dispatch code.
+ InputAPZContext context1(aGuid, aInputBlockId, nsEventStatus_eSentinel);
+
+ // Mouse events like eMouseEnterIntoWidget, that are created in the parent
+ // process EventStateManager code, have an input block id which they get from
+ // the InputAPZContext in the parent process stack. However, they did not
+ // actually go through the APZ code and so their mHandledByAPZ flag is false.
+ // Since thos events didn't go through APZ, we don't need to send
+ // notifications for them.
+ RefPtr<DisplayportSetListener> postLayerization;
+ if (aInputBlockId && localEvent.mFlags.mHandledByAPZ) {
+ nsCOMPtr<Document> document(GetTopLevelDocument());
+ postLayerization = APZCCallbackHelper::SendSetTargetAPZCNotification(
+ mPuppetWidget, document, localEvent, aGuid.mLayersId, aInputBlockId);
+ }
+
+ InputAPZContext context2(aGuid, aInputBlockId, nsEventStatus_eSentinel,
+ postLayerization != nullptr);
+
+ DispatchWidgetEventViaAPZ(localEvent);
+
+ if (aInputBlockId && localEvent.mFlags.mHandledByAPZ && mAPZEventState) {
+ mAPZEventState->ProcessMouseEvent(localEvent, aInputBlockId);
+ }
+
+ // Do this after the DispatchWidgetEventViaAPZ call above, so that if the
+ // mouse event triggered a post-refresh AsyncDragMetrics message to be sent
+ // to APZ (from scrollbar dragging in nsSliderFrame), then that will reach
+ // APZ before the SetTargetAPZC message. This ensures the drag input block
+ // gets the drag metrics before handling the input events.
+ if (postLayerization) {
+ postLayerization->Register();
+ }
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvNormalPriorityRealMouseButtonEvent(
+ const WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId) {
+ return RecvRealMouseButtonEvent(aEvent, aGuid, aInputBlockId);
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvRealMouseEnterExitWidgetEvent(
+ const WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId) {
+ return RecvRealMouseButtonEvent(aEvent, aGuid, aInputBlockId);
+}
+
+mozilla::ipc::IPCResult
+BrowserChild::RecvNormalPriorityRealMouseEnterExitWidgetEvent(
+ const WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId) {
+ return RecvRealMouseButtonEvent(aEvent, aGuid, aInputBlockId);
+}
+
+nsEventStatus BrowserChild::DispatchWidgetEventViaAPZ(WidgetGUIEvent& aEvent) {
+ aEvent.ResetWaitingReplyFromRemoteProcessState();
+ return APZCCallbackHelper::DispatchWidgetEvent(aEvent);
+}
+
+void BrowserChild::DispatchCoalescedWheelEvent() {
+ UniquePtr<WidgetWheelEvent> wheelEvent =
+ mCoalescedWheelData.TakeCoalescedEvent();
+ MOZ_ASSERT(wheelEvent);
+ DispatchWheelEvent(*wheelEvent, mCoalescedWheelData.GetScrollableLayerGuid(),
+ mCoalescedWheelData.GetInputBlockId());
+}
+
+void BrowserChild::DispatchWheelEvent(const WidgetWheelEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId) {
+ WidgetWheelEvent localEvent(aEvent);
+ if (aInputBlockId && aEvent.mFlags.mHandledByAPZ) {
+ nsCOMPtr<Document> document(GetTopLevelDocument());
+ RefPtr<DisplayportSetListener> postLayerization =
+ APZCCallbackHelper::SendSetTargetAPZCNotification(
+ mPuppetWidget, document, aEvent, aGuid.mLayersId, aInputBlockId);
+ if (postLayerization) {
+ postLayerization->Register();
+ }
+ }
+
+ localEvent.mWidget = mPuppetWidget;
+
+ // Stash the guid in InputAPZContext so that when the visual-to-layout
+ // transform is applied to the event's coordinates, we use the right transform
+ // based on the scroll frame being targeted.
+ // The other values don't really matter.
+ InputAPZContext context(aGuid, aInputBlockId, nsEventStatus_eSentinel);
+
+ DispatchWidgetEventViaAPZ(localEvent);
+
+ if (localEvent.mCanTriggerSwipe) {
+ SendRespondStartSwipeEvent(aInputBlockId, localEvent.TriggersSwipe());
+ }
+
+ if (aInputBlockId && aEvent.mFlags.mHandledByAPZ && mAPZEventState) {
+ mAPZEventState->ProcessWheelEvent(localEvent, aInputBlockId);
+ }
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvMouseWheelEvent(
+ const WidgetWheelEvent& aEvent, const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId) {
+ bool isNextWheelEvent = false;
+ // We only coalesce the current event when
+ // 1. It's eWheel (we don't coalesce eOperationStart and eWheelOperationEnd)
+ // 2. It has same attributes as the coalesced wheel event which is not yet
+ // fired.
+ if (aEvent.mMessage == eWheel) {
+ GetIPCChannel()->PeekMessages(
+ [&isNextWheelEvent](const IPC::Message& aMsg) -> bool {
+ if (aMsg.type() == mozilla::dom::PBrowser::Msg_MouseWheelEvent__ID) {
+ isNextWheelEvent = true;
+ }
+ return false; // Stop peeking.
+ });
+
+ if (!mCoalescedWheelData.IsEmpty() &&
+ !mCoalescedWheelData.CanCoalesce(aEvent, aGuid, aInputBlockId)) {
+ DispatchCoalescedWheelEvent();
+ MOZ_ASSERT(mCoalescedWheelData.IsEmpty());
+ }
+ mCoalescedWheelData.Coalesce(aEvent, aGuid, aInputBlockId);
+
+ MOZ_ASSERT(!mCoalescedWheelData.IsEmpty());
+ // If the next event isn't a wheel event, make sure we dispatch.
+ if (!isNextWheelEvent) {
+ DispatchCoalescedWheelEvent();
+ }
+ } else {
+ DispatchWheelEvent(aEvent, aGuid, aInputBlockId);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvNormalPriorityMouseWheelEvent(
+ const WidgetWheelEvent& aEvent, const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId) {
+ return RecvMouseWheelEvent(aEvent, aGuid, aInputBlockId);
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvRealTouchEvent(
+ const WidgetTouchEvent& aEvent, const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId, const nsEventStatus& aApzResponse) {
+ MOZ_LOG(sApzChildLog, LogLevel::Debug,
+ ("Receiving touch event of type %d\n", aEvent.mMessage));
+
+ if (StaticPrefs::dom_events_coalesce_touchmove()) {
+ if (aEvent.mMessage == eTouchEnd || aEvent.mMessage == eTouchStart) {
+ ProcessPendingCoalescedTouchData();
+ }
+
+ if (aEvent.mMessage != eTouchMove) {
+ sConsecutiveTouchMoveCount = 0;
+ }
+ }
+
+ WidgetTouchEvent localEvent(aEvent);
+ localEvent.mWidget = mPuppetWidget;
+
+ // Stash the guid in InputAPZContext so that when the visual-to-layout
+ // transform is applied to the event's coordinates, we use the right transform
+ // based on the scroll frame being targeted.
+ // The other values don't really matter.
+ InputAPZContext context(aGuid, aInputBlockId, aApzResponse);
+
+ nsTArray<TouchBehaviorFlags> allowedTouchBehaviors;
+ if (localEvent.mMessage == eTouchStart && AsyncPanZoomEnabled()) {
+ nsCOMPtr<Document> document = GetTopLevelDocument();
+ allowedTouchBehaviors = TouchActionHelper::GetAllowedTouchBehavior(
+ mPuppetWidget, document, localEvent);
+ if (!allowedTouchBehaviors.IsEmpty() && mApzcTreeManager) {
+ mApzcTreeManager->SetAllowedTouchBehavior(aInputBlockId,
+ allowedTouchBehaviors);
+ }
+ RefPtr<DisplayportSetListener> postLayerization =
+ APZCCallbackHelper::SendSetTargetAPZCNotification(
+ mPuppetWidget, document, localEvent, aGuid.mLayersId,
+ aInputBlockId);
+ if (postLayerization) {
+ postLayerization->Register();
+ }
+ }
+
+ // Dispatch event to content (potentially a long-running operation)
+ nsEventStatus status = DispatchWidgetEventViaAPZ(localEvent);
+
+ if (!AsyncPanZoomEnabled()) {
+ // We shouldn't have any e10s platforms that have touch events enabled
+ // without APZ.
+ MOZ_ASSERT(false);
+ return IPC_OK();
+ }
+
+ if (mAPZEventState) {
+ mAPZEventState->ProcessTouchEvent(localEvent, aGuid, aInputBlockId,
+ aApzResponse, status,
+ std::move(allowedTouchBehaviors));
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvNormalPriorityRealTouchEvent(
+ const WidgetTouchEvent& aEvent, const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId, const nsEventStatus& aApzResponse) {
+ return RecvRealTouchEvent(aEvent, aGuid, aInputBlockId, aApzResponse);
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvRealTouchMoveEvent(
+ const WidgetTouchEvent& aEvent, const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId, const nsEventStatus& aApzResponse) {
+ if (StaticPrefs::dom_events_coalesce_touchmove()) {
+ ++sConsecutiveTouchMoveCount;
+ if (mCoalescedTouchMoveEventFlusher) {
+ MOZ_ASSERT(aEvent.mMessage == eTouchMove);
+ if (mCoalescedTouchData.IsEmpty() ||
+ mCoalescedTouchData.CanCoalesce(aEvent, aGuid, aInputBlockId,
+ aApzResponse)) {
+ mCoalescedTouchData.Coalesce(aEvent, aGuid, aInputBlockId,
+ aApzResponse);
+ } else {
+ UniquePtr<WidgetTouchEvent> touchMoveEvent =
+ mCoalescedTouchData.TakeCoalescedEvent();
+
+ mCoalescedTouchData.Coalesce(aEvent, aGuid, aInputBlockId,
+ aApzResponse);
+
+ if (!RecvRealTouchEvent(*touchMoveEvent,
+ mCoalescedTouchData.GetScrollableLayerGuid(),
+ mCoalescedTouchData.GetInputBlockId(),
+ mCoalescedTouchData.GetApzResponse())) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ }
+
+ if (sConsecutiveTouchMoveCount > 1) {
+ mCoalescedTouchMoveEventFlusher->StartObserver();
+ } else {
+ // Flush the pending coalesced touch in order to avoid the first
+ // touchmove be overridden by the second one.
+ ProcessPendingCoalescedTouchData();
+ }
+ return IPC_OK();
+ }
+ }
+
+ if (!RecvRealTouchEvent(aEvent, aGuid, aInputBlockId, aApzResponse)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvNormalPriorityRealTouchMoveEvent(
+ const WidgetTouchEvent& aEvent, const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId, const nsEventStatus& aApzResponse) {
+ return RecvRealTouchMoveEvent(aEvent, aGuid, aInputBlockId, aApzResponse);
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvRealDragEvent(
+ const WidgetDragEvent& aEvent, const uint32_t& aDragAction,
+ const uint32_t& aDropEffect, nsIPrincipal* aPrincipal,
+ nsIContentSecurityPolicy* aCsp) {
+ WidgetDragEvent localEvent(aEvent);
+ localEvent.mWidget = mPuppetWidget;
+
+ nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
+ if (dragSession) {
+ dragSession->SetDragAction(aDragAction);
+ dragSession->SetTriggeringPrincipal(aPrincipal);
+ dragSession->SetCsp(aCsp);
+ RefPtr<DataTransfer> initialDataTransfer = dragSession->GetDataTransfer();
+ if (initialDataTransfer) {
+ initialDataTransfer->SetDropEffectInt(aDropEffect);
+ }
+ }
+
+ if (aEvent.mMessage == eDrop) {
+ bool canDrop = true;
+ if (!dragSession || NS_FAILED(dragSession->GetCanDrop(&canDrop)) ||
+ !canDrop) {
+ localEvent.mMessage = eDragExit;
+ }
+ } else if (aEvent.mMessage == eDragOver) {
+ nsCOMPtr<nsIDragService> dragService =
+ do_GetService("@mozilla.org/widget/dragservice;1");
+ if (dragService) {
+ // This will dispatch 'drag' event at the source if the
+ // drag transaction started in this process.
+ dragService->FireDragEventAtSource(eDrag, aEvent.mModifiers);
+ }
+ }
+
+ DispatchWidgetEventViaAPZ(localEvent);
+ return IPC_OK();
+}
+
+void BrowserChild::RequestEditCommands(NativeKeyBindingsType aType,
+ const WidgetKeyboardEvent& aEvent,
+ nsTArray<CommandInt>& aCommands) {
+ MOZ_ASSERT(aCommands.IsEmpty());
+
+ if (NS_WARN_IF(aEvent.IsEditCommandsInitialized(aType))) {
+ aCommands = aEvent.EditCommandsConstRef(aType).Clone();
+ return;
+ }
+
+ switch (aType) {
+ case NativeKeyBindingsType::SingleLineEditor:
+ case NativeKeyBindingsType::MultiLineEditor:
+ case NativeKeyBindingsType::RichTextEditor:
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Invalid native key bindings type");
+ }
+
+ // Don't send aEvent to the parent process directly because it'll be marked
+ // as posted to remote process.
+ WidgetKeyboardEvent localEvent(aEvent);
+ SendRequestNativeKeyBindings(static_cast<uint32_t>(aType), localEvent,
+ &aCommands);
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvNativeSynthesisResponse(
+ const uint64_t& aObserverId, const nsCString& aResponse) {
+ mozilla::widget::AutoObserverNotifier::NotifySavedObserver(aObserverId,
+ aResponse.get());
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvUpdateSHistory() {
+ if (mSessionStoreChild) {
+ mSessionStoreChild->UpdateSHistoryChanges();
+ }
+ return IPC_OK();
+}
+
+// In case handling repeated keys takes much time, we skip firing new ones.
+bool BrowserChild::SkipRepeatedKeyEvent(const WidgetKeyboardEvent& aEvent) {
+ if (mRepeatedKeyEventTime.IsNull() || !aEvent.CanSkipInRemoteProcess() ||
+ (aEvent.mMessage != eKeyDown && aEvent.mMessage != eKeyPress)) {
+ mRepeatedKeyEventTime = TimeStamp();
+ mSkipKeyPress = false;
+ return false;
+ }
+
+ if ((aEvent.mMessage == eKeyDown &&
+ (mRepeatedKeyEventTime > aEvent.mTimeStamp)) ||
+ (mSkipKeyPress && (aEvent.mMessage == eKeyPress))) {
+ // If we skip a keydown event, also the following keypress events should be
+ // skipped.
+ mSkipKeyPress |= aEvent.mMessage == eKeyDown;
+ return true;
+ }
+
+ if (aEvent.mMessage == eKeyDown) {
+ // If keydown wasn't skipped, nor should the possible following keypress.
+ mRepeatedKeyEventTime = TimeStamp();
+ mSkipKeyPress = false;
+ }
+ return false;
+}
+
+void BrowserChild::UpdateRepeatedKeyEventEndTime(
+ const WidgetKeyboardEvent& aEvent) {
+ if (aEvent.mIsRepeat &&
+ (aEvent.mMessage == eKeyDown || aEvent.mMessage == eKeyPress)) {
+ mRepeatedKeyEventTime = TimeStamp::Now();
+ }
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvRealKeyEvent(
+ const WidgetKeyboardEvent& aEvent, const nsID& aUUID) {
+ MOZ_ASSERT_IF(aEvent.mMessage == eKeyPress,
+ aEvent.AreAllEditCommandsInitialized());
+
+ // If content code called preventDefault() on a keydown event, then we don't
+ // want to process any following keypress events.
+ const bool isPrecedingKeyDownEventConsumed =
+ aEvent.mMessage == eKeyPress && mIgnoreKeyPressEvent;
+
+ WidgetKeyboardEvent localEvent(aEvent);
+ localEvent.mWidget = mPuppetWidget;
+ localEvent.mUniqueId = aEvent.mUniqueId;
+
+ if (!SkipRepeatedKeyEvent(aEvent) && !isPrecedingKeyDownEventConsumed) {
+ nsEventStatus status = DispatchWidgetEventViaAPZ(localEvent);
+
+ // Update the end time of the possible repeated event so that we can skip
+ // some incoming events in case event handling took long time.
+ UpdateRepeatedKeyEventEndTime(localEvent);
+
+ if (aEvent.mMessage == eKeyDown) {
+ mIgnoreKeyPressEvent = status == nsEventStatus_eConsumeNoDefault;
+ }
+
+ if (localEvent.mFlags.mIsSuppressedOrDelayed) {
+ localEvent.PreventDefault();
+ }
+
+ // If the event's default isn't prevented but the status is no default,
+ // That means that the event was consumed by EventStateManager or something
+ // which is not a usual event handler. In such case, prevent its default
+ // as a default handler. For example, when an eKeyPress event matches
+ // with a content accesskey, and it's executed, preventDefault() of the
+ // event won't be called but the status is set to "no default". Then,
+ // the event shouldn't be handled by nsMenuBarListener in the main process.
+ if (!localEvent.DefaultPrevented() &&
+ status == nsEventStatus_eConsumeNoDefault) {
+ localEvent.PreventDefault();
+ }
+
+ MOZ_DIAGNOSTIC_ASSERT(!localEvent.PropagationStopped());
+ }
+ // The keyboard event which we ignore should not be handled in the main
+ // process for shortcut key handling. For notifying if we skipped it, we can
+ // use "stop propagation" flag here because it must be cleared by
+ // `EventTargetChainItem` if we've dispatched it.
+ else {
+ localEvent.StopPropagation();
+ }
+
+ // If we don't need to send a rely for the given keyboard event, we do nothing
+ // anymore here.
+ if (!aEvent.WantReplyFromContentProcess()) {
+ return IPC_OK();
+ }
+
+ // This is an ugly hack, mNoRemoteProcessDispatch is set to true when the
+ // event's PreventDefault() or StopScrollProcessForwarding() is called.
+ // And then, it'll be checked by ParamTraits<mozilla::WidgetEvent>::Write()
+ // whether the event is being sent to remote process unexpectedly.
+ // However, unfortunately, it cannot check the destination. Therefore,
+ // we need to clear the flag explicitly here because ParamTraits should
+ // keep checking the flag for avoiding regression.
+ localEvent.mFlags.mNoRemoteProcessDispatch = false;
+ SendReplyKeyEvent(localEvent, aUUID);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvNormalPriorityRealKeyEvent(
+ const WidgetKeyboardEvent& aEvent, const nsID& aUUID) {
+ return RecvRealKeyEvent(aEvent, aUUID);
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvCompositionEvent(
+ const WidgetCompositionEvent& aEvent) {
+ WidgetCompositionEvent localEvent(aEvent);
+ localEvent.mWidget = mPuppetWidget;
+ DispatchWidgetEventViaAPZ(localEvent);
+ Unused << SendOnEventNeedingAckHandled(aEvent.mMessage,
+ localEvent.mCompositionId);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvNormalPriorityCompositionEvent(
+ const WidgetCompositionEvent& aEvent) {
+ return RecvCompositionEvent(aEvent);
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvSelectionEvent(
+ const WidgetSelectionEvent& aEvent) {
+ WidgetSelectionEvent localEvent(aEvent);
+ localEvent.mWidget = mPuppetWidget;
+ DispatchWidgetEventViaAPZ(localEvent);
+ Unused << SendOnEventNeedingAckHandled(aEvent.mMessage, 0u);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvNormalPrioritySelectionEvent(
+ const WidgetSelectionEvent& aEvent) {
+ return RecvSelectionEvent(aEvent);
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvInsertText(
+ const nsAString& aStringToInsert) {
+ // Use normal event path to reach focused document.
+ WidgetContentCommandEvent localEvent(true, eContentCommandInsertText,
+ mPuppetWidget);
+ localEvent.mString = Some(nsString(aStringToInsert));
+ DispatchWidgetEventViaAPZ(localEvent);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvNormalPriorityInsertText(
+ const nsAString& aStringToInsert) {
+ return RecvInsertText(aStringToInsert);
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvPasteTransferable(
+ const IPCTransferable& aTransferable) {
+ nsresult rv;
+ nsCOMPtr<nsITransferable> trans =
+ do_CreateInstance("@mozilla.org/widget/transferable;1", &rv);
+ NS_ENSURE_SUCCESS(rv, IPC_OK());
+ trans->Init(nullptr);
+
+ rv = nsContentUtils::IPCTransferableToTransferable(
+ aTransferable, true /* aAddDataFlavor */, trans,
+ false /* aFilterUnknownFlavors */);
+ NS_ENSURE_SUCCESS(rv, IPC_OK());
+
+ nsCOMPtr<nsIDocShell> ourDocShell = do_GetInterface(WebNavigation());
+ if (NS_WARN_IF(!ourDocShell)) {
+ return IPC_OK();
+ }
+
+ RefPtr<nsCommandParams> params = new nsCommandParams();
+ rv = params->SetISupports("transferable", trans);
+ NS_ENSURE_SUCCESS(rv, IPC_OK());
+
+ ourDocShell->DoCommandWithParams("cmd_pasteTransferable", params);
+ return IPC_OK();
+}
+
+#ifdef ACCESSIBILITY
+a11y::PDocAccessibleChild* BrowserChild::AllocPDocAccessibleChild(
+ PDocAccessibleChild*, const uint64_t&,
+ const MaybeDiscardedBrowsingContext&) {
+ MOZ_ASSERT(false, "should never call this!");
+ return nullptr;
+}
+
+bool BrowserChild::DeallocPDocAccessibleChild(
+ a11y::PDocAccessibleChild* aChild) {
+ delete static_cast<mozilla::a11y::DocAccessibleChild*>(aChild);
+ return true;
+}
+#endif
+
+RefPtr<VsyncMainChild> BrowserChild::GetVsyncChild() {
+ // Initializing mVsyncChild here turns on per-BrowserChild Vsync for a
+ // given platform. Note: this only makes sense if nsWindow returns a
+ // window-specific VsyncSource.
+#if defined(MOZ_WAYLAND)
+ if (IsWaylandEnabled() && !mVsyncChild) {
+ mVsyncChild = MakeRefPtr<VsyncMainChild>();
+ if (!SendPVsyncConstructor(mVsyncChild)) {
+ mVsyncChild = nullptr;
+ }
+ }
+#endif
+ return mVsyncChild;
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvLoadRemoteScript(
+ const nsAString& aURL, const bool& aRunInGlobalScope) {
+ if (!InitBrowserChildMessageManager())
+ // This can happen if we're half-destroyed. It's not a fatal
+ // error.
+ return IPC_OK();
+
+ JS::Rooted<JSObject*> mm(RootingCx(),
+ mBrowserChildMessageManager->GetOrCreateWrapper());
+ if (!mm) {
+ // This can happen if we're half-destroyed. It's not a fatal error.
+ return IPC_OK();
+ }
+
+ LoadScriptInternal(mm, aURL, !aRunInGlobalScope);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvAsyncMessage(
+ const nsAString& aMessage, const ClonedMessageData& aData) {
+ AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING("BrowserChild::RecvAsyncMessage",
+ OTHER, aMessage);
+ MMPrinter::Print("BrowserChild::RecvAsyncMessage", aMessage, aData);
+
+ if (!mBrowserChildMessageManager) {
+ return IPC_OK();
+ }
+
+ RefPtr<nsFrameMessageManager> mm =
+ mBrowserChildMessageManager->GetMessageManager();
+
+ // We should have a message manager if the global is alive, but it
+ // seems sometimes we don't. Assert in aurora/nightly, but don't
+ // crash in release builds.
+ MOZ_DIAGNOSTIC_ASSERT(mm);
+ if (!mm) {
+ return IPC_OK();
+ }
+
+ JS::Rooted<JSObject*> kungFuDeathGrip(
+ dom::RootingCx(), mBrowserChildMessageManager->GetWrapper());
+ StructuredCloneData data;
+ UnpackClonedMessageData(aData, data);
+ mm->ReceiveMessage(static_cast<EventTarget*>(mBrowserChildMessageManager),
+ nullptr, aMessage, false, &data, nullptr, IgnoreErrors());
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvSwappedWithOtherRemoteLoader(
+ const IPCTabContext& aContext) {
+ nsCOMPtr<nsIDocShell> ourDocShell = do_GetInterface(WebNavigation());
+ if (NS_WARN_IF(!ourDocShell)) {
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> ourWindow = ourDocShell->GetWindow();
+ if (NS_WARN_IF(!ourWindow)) {
+ return IPC_OK();
+ }
+
+ RefPtr<nsDocShell> docShell = static_cast<nsDocShell*>(ourDocShell.get());
+
+ nsCOMPtr<EventTarget> ourEventTarget = nsGlobalWindowOuter::Cast(ourWindow);
+
+ docShell->SetInFrameSwap(true);
+
+ nsContentUtils::FirePageShowEventForFrameLoaderSwap(
+ ourDocShell, ourEventTarget, false, true);
+ nsContentUtils::FirePageHideEventForFrameLoaderSwap(ourDocShell,
+ ourEventTarget, true);
+
+ // Owner content type may have changed, so store the possibly updated context
+ // and notify others.
+ MaybeInvalidTabContext maybeContext(aContext);
+ if (!maybeContext.IsValid()) {
+ NS_ERROR(nsPrintfCString("Received an invalid TabContext from "
+ "the parent process. (%s)",
+ maybeContext.GetInvalidReason())
+ .get());
+ MOZ_CRASH("Invalid TabContext received from the parent process.");
+ }
+
+ if (!UpdateTabContextAfterSwap(maybeContext.GetTabContext())) {
+ MOZ_CRASH("Update to TabContext after swap was denied.");
+ }
+
+ // Ignore previous value of mTriedBrowserInit since owner content has changed.
+ mTriedBrowserInit = true;
+
+ nsContentUtils::FirePageShowEventForFrameLoaderSwap(
+ ourDocShell, ourEventTarget, true, true);
+
+ docShell->SetInFrameSwap(false);
+
+ // This is needed to get visibility state right in cases when we swapped a
+ // visible tab (foreground in visible window) with a non-visible tab.
+ if (RefPtr<Document> doc = docShell->GetDocument()) {
+ doc->UpdateVisibilityState();
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvHandleAccessKey(
+ const WidgetKeyboardEvent& aEvent, nsTArray<uint32_t>&& aCharCodes) {
+ nsCOMPtr<Document> document(GetTopLevelDocument());
+ RefPtr<nsPresContext> pc = document->GetPresContext();
+ if (pc) {
+ if (!pc->EventStateManager()->HandleAccessKey(
+ &(const_cast<WidgetKeyboardEvent&>(aEvent)), pc, aCharCodes)) {
+ // If no accesskey was found, inform the parent so that accesskeys on
+ // menus can be handled.
+ WidgetKeyboardEvent localEvent(aEvent);
+ localEvent.mWidget = mPuppetWidget;
+ SendAccessKeyNotHandled(localEvent);
+ }
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvPrintPreview(
+ const PrintData& aPrintData, const MaybeDiscardedBrowsingContext& aSourceBC,
+ PrintPreviewResolver&& aCallback) {
+#ifdef NS_PRINTING
+ // If we didn't succeed in passing off ownership of aCallback, then something
+ // went wrong.
+ auto sendCallbackError = MakeScopeExit([&] {
+ if (aCallback) {
+ // signal error
+ aCallback(PrintPreviewResultInfo(0, 0, false, false, false, {}, {}, {}));
+ }
+ });
+
+ if (NS_WARN_IF(aSourceBC.IsDiscarded())) {
+ return IPC_OK();
+ }
+
+ RefPtr<nsGlobalWindowOuter> sourceWindow;
+ if (!aSourceBC.IsNull()) {
+ sourceWindow = nsGlobalWindowOuter::Cast(aSourceBC.get()->GetDOMWindow());
+ if (NS_WARN_IF(!sourceWindow)) {
+ return IPC_OK();
+ }
+ } else {
+ nsCOMPtr<nsPIDOMWindowOuter> ourWindow = do_GetInterface(WebNavigation());
+ if (NS_WARN_IF(!ourWindow)) {
+ return IPC_OK();
+ }
+ sourceWindow = nsGlobalWindowOuter::Cast(ourWindow);
+ }
+
+ RefPtr<nsIPrintSettings> printSettings;
+ nsCOMPtr<nsIPrintSettingsService> printSettingsSvc =
+ do_GetService("@mozilla.org/gfx/printsettings-service;1");
+ if (NS_WARN_IF(!printSettingsSvc)) {
+ return IPC_OK();
+ }
+ printSettingsSvc->CreateNewPrintSettings(getter_AddRefs(printSettings));
+ if (NS_WARN_IF(!printSettings)) {
+ return IPC_OK();
+ }
+ printSettingsSvc->DeserializeToPrintSettings(aPrintData, printSettings);
+
+ nsCOMPtr<nsIDocShell> docShellToCloneInto;
+ if (!aSourceBC.IsNull()) {
+ docShellToCloneInto = do_GetInterface(WebNavigation());
+ if (NS_WARN_IF(!docShellToCloneInto)) {
+ return IPC_OK();
+ }
+ }
+
+ sourceWindow->Print(printSettings,
+ /* aRemotePrintJob = */ nullptr,
+ /* aListener = */ nullptr, docShellToCloneInto,
+ nsGlobalWindowOuter::IsPreview::Yes,
+ nsGlobalWindowOuter::IsForWindowDotPrint::No,
+ std::move(aCallback), IgnoreErrors());
+#endif
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvExitPrintPreview() {
+#ifdef NS_PRINTING
+ nsCOMPtr<nsIWebBrowserPrint> webBrowserPrint =
+ do_GetInterface(ToSupports(WebNavigation()));
+ if (NS_WARN_IF(!webBrowserPrint)) {
+ return IPC_OK();
+ }
+ webBrowserPrint->ExitPrintPreview();
+#endif
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvPrint(
+ const MaybeDiscardedBrowsingContext& aBc, const PrintData& aPrintData) {
+#ifdef NS_PRINTING
+ if (NS_WARN_IF(aBc.IsNullOrDiscarded())) {
+ return IPC_OK();
+ }
+ RefPtr<nsGlobalWindowOuter> outerWindow =
+ nsGlobalWindowOuter::Cast(aBc.get()->GetDOMWindow());
+ if (NS_WARN_IF(!outerWindow)) {
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsIPrintSettingsService> printSettingsSvc =
+ do_GetService("@mozilla.org/gfx/printsettings-service;1");
+ if (NS_WARN_IF(!printSettingsSvc)) {
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsIPrintSettings> printSettings;
+ nsresult rv =
+ printSettingsSvc->CreateNewPrintSettings(getter_AddRefs(printSettings));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return IPC_OK();
+ }
+
+ printSettingsSvc->DeserializeToPrintSettings(aPrintData, printSettings);
+ {
+ IgnoredErrorResult rv;
+ RefPtr printJob = static_cast<RemotePrintJobChild*>(
+ aPrintData.remotePrintJob().AsChild());
+ outerWindow->Print(printSettings, printJob,
+ /* aListener = */ nullptr,
+ /* aWindowToCloneInto = */ nullptr,
+ nsGlobalWindowOuter::IsPreview::No,
+ nsGlobalWindowOuter::IsForWindowDotPrint::No,
+ /* aPrintPreviewCallback = */ nullptr, rv);
+ if (NS_WARN_IF(rv.Failed())) {
+ return IPC_OK();
+ }
+ }
+#endif
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvUpdateNativeWindowHandle(
+ const uintptr_t& aNewHandle) {
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+ mNativeWindowHandle = aNewHandle;
+ return IPC_OK();
+#else
+ return IPC_FAIL_NO_REASON(this);
+#endif
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvDestroy() {
+ MOZ_ASSERT(!mDestroyed);
+ mDestroyed = true;
+
+ nsTArray<PContentPermissionRequestChild*> childArray =
+ nsContentPermissionUtils::GetContentPermissionRequestChildById(
+ GetTabId());
+
+ // Need to close undeleted ContentPermissionRequestChilds before tab is
+ // closed.
+ for (auto& permissionRequestChild : childArray) {
+ auto* child = static_cast<RemotePermissionRequest*>(permissionRequestChild);
+ child->Destroy();
+ }
+
+ if (mBrowserChildMessageManager) {
+ // Message handlers are called from the event loop, so it better be safe to
+ // run script.
+ MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
+ mBrowserChildMessageManager->DispatchTrustedEvent(u"unload"_ns);
+ }
+
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+
+ observerService->RemoveObserver(this, BEFORE_FIRST_PAINT);
+
+ // XXX what other code in ~BrowserChild() should we be running here?
+ DestroyWindow();
+
+ // Bounce through the event loop once to allow any delayed teardown runnables
+ // that were just generated to have a chance to run.
+ nsCOMPtr<nsIRunnable> deleteRunnable = new DelayedDeleteRunnable(this);
+ MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(deleteRunnable));
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvRenderLayers(const bool& aEnabled) {
+ auto clearPaintWhileInterruptingJS = MakeScopeExit([&] {
+ // We might force a paint, or we might already have painted and this is a
+ // no-op. In either case, once we exit this scope, we need to alert the
+ // ProcessHangMonitor that we've finished responding to what might have
+ // been a request to force paint. This is so that the BackgroundHangMonitor
+ // for force painting can be made to wait again.
+ if (aEnabled) {
+ ProcessHangMonitor::ClearPaintWhileInterruptingJS();
+ }
+ });
+
+ if (aEnabled) {
+ ProcessHangMonitor::MaybeStartPaintWhileInterruptingJS();
+ }
+
+ mRenderLayers = aEnabled;
+ const bool wasVisible = IsVisible();
+
+ UpdateVisibility();
+
+ // If we just became visible, try to trigger a paint as soon as possible.
+ const bool becameVisible = !wasVisible && IsVisible();
+ if (!becameVisible) {
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
+ if (!docShell) {
+ return IPC_OK();
+ }
+
+ // We don't use BrowserChildBase::GetPresShell() here because that would
+ // create a content viewer if one doesn't exist yet. Creating a content
+ // viewer can cause JS to run, which we want to avoid.
+ // nsIDocShell::GetPresShell returns null if no content viewer exists yet.
+ RefPtr<PresShell> presShell = docShell->GetPresShell();
+ if (!presShell) {
+ return IPC_OK();
+ }
+
+ if (nsIFrame* root = presShell->GetRootFrame()) {
+ root->SchedulePaint();
+ }
+
+ Telemetry::AutoTimer<Telemetry::TABCHILD_PAINT_TIME> timer;
+ // If we need to repaint, let's do that right away. No sense waiting until
+ // we get back to the event loop again. We suppress the display port so
+ // that we only paint what's visible. This ensures that the tab we're
+ // switching to paints as quickly as possible.
+ presShell->SuppressDisplayport(true);
+ if (nsContentUtils::IsSafeToRunScript()) {
+ WebWidget()->PaintNowIfNeeded();
+ } else {
+ RefPtr<nsViewManager> vm = presShell->GetViewManager();
+ if (nsView* view = vm->GetRootView()) {
+ presShell->PaintAndRequestComposite(view, PaintFlags::None);
+ }
+ }
+ presShell->SuppressDisplayport(false);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvNavigateByKey(
+ const bool& aForward, const bool& aForDocumentNavigation) {
+ nsFocusManager* fm = nsFocusManager::GetFocusManager();
+ if (!fm) {
+ return IPC_OK();
+ }
+
+ RefPtr<Element> result;
+ nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation());
+
+ // Move to the first or last document.
+ {
+ uint32_t type =
+ aForward
+ ? (aForDocumentNavigation
+ ? static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_FIRSTDOC)
+ : static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_ROOT))
+ : (aForDocumentNavigation
+ ? static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_LASTDOC)
+ : static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_LAST));
+ uint32_t flags = nsIFocusManager::FLAG_BYKEY;
+ if (aForward || aForDocumentNavigation) {
+ flags |= nsIFocusManager::FLAG_NOSCROLL;
+ }
+ fm->MoveFocus(window, nullptr, type, flags, getter_AddRefs(result));
+ }
+
+ // No valid root element was found, so move to the first focusable element.
+ if (!result && aForward && !aForDocumentNavigation) {
+ fm->MoveFocus(window, nullptr, nsIFocusManager::MOVEFOCUS_FIRST,
+ nsIFocusManager::FLAG_BYKEY, getter_AddRefs(result));
+ }
+
+ SendRequestFocus(false, CallerType::System);
+ return IPC_OK();
+}
+
+bool BrowserChild::InitBrowserChildMessageManager() {
+ mShouldSendWebProgressEventsToParent = true;
+
+ if (!mBrowserChildMessageManager) {
+ nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation());
+ NS_ENSURE_TRUE(window, false);
+ nsCOMPtr<EventTarget> chromeHandler = window->GetChromeEventHandler();
+ NS_ENSURE_TRUE(chromeHandler, false);
+
+ RefPtr<BrowserChildMessageManager> scope = mBrowserChildMessageManager =
+ new BrowserChildMessageManager(this);
+
+ MOZ_ALWAYS_TRUE(nsMessageManagerScriptExecutor::Init());
+
+ nsCOMPtr<nsPIWindowRoot> root = do_QueryInterface(chromeHandler);
+ if (NS_WARN_IF(!root)) {
+ mBrowserChildMessageManager = nullptr;
+ return false;
+ }
+ root->SetParentTarget(scope);
+ }
+
+ if (!mTriedBrowserInit) {
+ mTriedBrowserInit = true;
+ }
+
+ return true;
+}
+
+void BrowserChild::InitRenderingState(
+ const TextureFactoryIdentifier& aTextureFactoryIdentifier,
+ const layers::LayersId& aLayersId,
+ const CompositorOptions& aCompositorOptions) {
+ mPuppetWidget->InitIMEState();
+
+ MOZ_ASSERT(aLayersId.IsValid());
+ mTextureFactoryIdentifier = aTextureFactoryIdentifier;
+
+ // Pushing layers transactions directly to a separate
+ // compositor context.
+ PCompositorBridgeChild* compositorChild = CompositorBridgeChild::Get();
+ if (!compositorChild) {
+ mLayersConnected = Some(false);
+ NS_WARNING("failed to get CompositorBridgeChild instance");
+ return;
+ }
+
+ mCompositorOptions = Some(aCompositorOptions);
+
+ if (aLayersId.IsValid()) {
+ StaticMutexAutoLock lock(sBrowserChildrenMutex);
+
+ if (!sBrowserChildren) {
+ sBrowserChildren = new BrowserChildMap;
+ }
+ MOZ_ASSERT(!sBrowserChildren->Contains(uint64_t(aLayersId)));
+ sBrowserChildren->InsertOrUpdate(uint64_t(aLayersId), this);
+ mLayersId = aLayersId;
+ }
+
+ // Depending on timing, we might paint too early and fall back to basic
+ // layers. CreateRemoteLayerManager will destroy us if we manage to get a
+ // remote layer manager though, so that's fine.
+ MOZ_ASSERT(!mPuppetWidget->HasWindowRenderer() ||
+ mPuppetWidget->GetWindowRenderer()->GetBackendType() ==
+ layers::LayersBackend::LAYERS_NONE);
+ bool success = false;
+ if (mLayersConnected == Some(true)) {
+ success = CreateRemoteLayerManager(compositorChild);
+ }
+
+ if (success) {
+ MOZ_ASSERT(mLayersConnected == Some(true));
+ // Succeeded to create "remote" layer manager
+ ImageBridgeChild::IdentifyCompositorTextureHost(mTextureFactoryIdentifier);
+ gfx::VRManagerChild::IdentifyTextureHost(mTextureFactoryIdentifier);
+ InitAPZState();
+ } else {
+ NS_WARNING("Fallback to FallbackRenderer");
+ mLayersConnected = Some(false);
+ }
+
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+
+ if (observerService) {
+ observerService->AddObserver(this, BEFORE_FIRST_PAINT, false);
+ }
+}
+
+bool BrowserChild::CreateRemoteLayerManager(
+ mozilla::layers::PCompositorBridgeChild* aCompositorChild) {
+ MOZ_ASSERT(aCompositorChild);
+
+ return mPuppetWidget->CreateRemoteLayerManager(
+ [&](WebRenderLayerManager* aLayerManager) -> bool {
+ nsCString error;
+ return aLayerManager->Initialize(aCompositorChild,
+ wr::AsPipelineId(mLayersId),
+ &mTextureFactoryIdentifier, error);
+ });
+}
+
+void BrowserChild::InitAPZState() {
+ if (!mCompositorOptions->UseAPZ()) {
+ return;
+ }
+ auto* cbc = CompositorBridgeChild::Get();
+
+ // Initialize the ApzcTreeManager. This takes multiple casts because of ugly
+ // multiple inheritance.
+ PAPZCTreeManagerChild* baseProtocol =
+ cbc->SendPAPZCTreeManagerConstructor(mLayersId);
+ if (!baseProtocol) {
+ MOZ_ASSERT(false,
+ "Allocating a TreeManager should not fail with APZ enabled");
+ return;
+ }
+ APZCTreeManagerChild* derivedProtocol =
+ static_cast<APZCTreeManagerChild*>(baseProtocol);
+
+ mApzcTreeManager = RefPtr<IAPZCTreeManager>(derivedProtocol);
+
+ // Initialize the GeckoContentController for this tab. We don't hold a
+ // reference because we don't need it. The ContentProcessController will hold
+ // a reference to the tab, and will be destroyed by the compositor or ipdl
+ // during destruction.
+ RefPtr<GeckoContentController> contentController =
+ new ContentProcessController(this);
+ APZChild* apzChild = new APZChild(contentController);
+ cbc->SendPAPZConstructor(apzChild, mLayersId);
+}
+
+IPCResult BrowserChild::RecvUpdateEffects(const EffectsInfo& aEffects) {
+ bool needInvalidate = false;
+ if (mEffectsInfo.IsVisible() && aEffects.IsVisible() &&
+ mEffectsInfo != aEffects) {
+ // If we are staying visible and either the visrect or scale changed we need
+ // to invalidate
+ needInvalidate = true;
+ }
+
+ mEffectsInfo = aEffects;
+ UpdateVisibility();
+
+ if (needInvalidate) {
+ if (nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation())) {
+ // We don't use BrowserChildBase::GetPresShell() here because that would
+ // create a content viewer if one doesn't exist yet. Creating a content
+ // viewer can cause JS to run, which we want to avoid.
+ // nsIDocShell::GetPresShell returns null if no content viewer exists yet.
+ if (RefPtr<PresShell> presShell = docShell->GetPresShell()) {
+ if (nsIFrame* root = presShell->GetRootFrame()) {
+ root->InvalidateFrame();
+ }
+ }
+ }
+ }
+
+ return IPC_OK();
+}
+
+bool BrowserChild::IsVisible() {
+ return mPuppetWidget && mPuppetWidget->IsVisible();
+}
+
+void BrowserChild::UpdateVisibility() {
+ const bool shouldBeVisible = [&] {
+ // If we're known to be visibility: hidden / display: none, just return
+ // false here, we're pretty sure we don't want to be considered visible
+ // here.
+ if (mBrowsingContext && mBrowsingContext->IsUnderHiddenEmbedderElement()) {
+ return false;
+ }
+ // For OOP iframes, include viewport visibility. For top-level <browser>
+ // elements we don't use this, because the front-end relies on using
+ // `mRenderLayers` when invisible for tab warming purposes.
+ //
+ // An alternative, maybe more consistent approach would be to add an opt-in
+ // into this behavior for top-level tabs managed by the tab-switcher
+ // instead...
+ if (!mIsTopLevel && !mEffectsInfo.IsVisible()) {
+ return false;
+ }
+ // If we're explicitly told not to render layers, we're also invisible.
+ if (!mRenderLayers) {
+ return false;
+ }
+ return true;
+ }();
+
+ const bool isVisible = IsVisible();
+ if (shouldBeVisible == isVisible) {
+ return;
+ }
+ if (shouldBeVisible) {
+ MakeVisible();
+ } else {
+ MakeHidden();
+ }
+}
+
+void BrowserChild::MakeVisible() {
+ if (IsVisible()) {
+ return;
+ }
+
+ if (mPuppetWidget) {
+ mPuppetWidget->Show(true);
+ }
+
+ PresShellActivenessMaybeChanged();
+}
+
+void BrowserChild::MakeHidden() {
+ if (!IsVisible()) {
+ return;
+ }
+
+ // Due to the nested event loop in ContentChild::ProvideWindowCommon,
+ // it's possible to be told to become hidden before we're finished
+ // setting up a layer manager. We should skip clearing cached layers
+ // in that case, since doing so might accidentally put is into
+ // BasicLayers mode.
+ if (mPuppetWidget) {
+ if (mPuppetWidget->HasWindowRenderer()) {
+ ClearCachedResources();
+ }
+ mPuppetWidget->Show(false);
+ }
+
+ PresShellActivenessMaybeChanged();
+}
+
+IPCResult BrowserChild::RecvPreserveLayers(bool aPreserve) {
+ mIsPreservingLayers = aPreserve;
+
+ PresShellActivenessMaybeChanged();
+
+ return IPC_OK();
+}
+
+void BrowserChild::PresShellActivenessMaybeChanged() {
+ // We don't use BrowserChildBase::GetPresShell() here because that would
+ // create a content viewer if one doesn't exist yet. Creating a content
+ // viewer can cause JS to run, which we want to avoid.
+ // nsIDocShell::GetPresShell returns null if no content viewer exists yet.
+ //
+ // When this method is called we don't want to go through the browsing context
+ // because we don't want to change the visibility state of the document, which
+ // has side effects like firing events to content, unblocking media playback,
+ // unthrottling timeouts... PresShell activeness has a lot less side effects.
+ nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
+ if (!docShell) {
+ return;
+ }
+ RefPtr<PresShell> presShell = docShell->GetPresShell();
+ if (!presShell) {
+ return;
+ }
+ presShell->ActivenessMaybeChanged();
+}
+
+NS_IMETHODIMP
+BrowserChild::GetMessageManager(ContentFrameMessageManager** aResult) {
+ RefPtr<ContentFrameMessageManager> mm(mBrowserChildMessageManager);
+ mm.forget(aResult);
+ return *aResult ? NS_OK : NS_ERROR_FAILURE;
+}
+
+void BrowserChild::SendRequestFocus(bool aCanFocus, CallerType aCallerType) {
+ nsFocusManager* fm = nsFocusManager::GetFocusManager();
+ if (!fm) {
+ return;
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation());
+ if (!window) {
+ return;
+ }
+
+ BrowsingContext* focusedBC = fm->GetFocusedBrowsingContext();
+ if (focusedBC == window->GetBrowsingContext()) {
+ // BrowsingContext has the focus already, do not request again.
+ return;
+ }
+
+ PBrowserChild::SendRequestFocus(aCanFocus, aCallerType);
+}
+
+NS_IMETHODIMP
+BrowserChild::GetTabId(uint64_t* aId) {
+ *aId = GetTabId();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BrowserChild::GetChromeOuterWindowID(uint64_t* aId) {
+ *aId = ChromeOuterWindowID();
+ return NS_OK;
+}
+
+bool BrowserChild::DoSendBlockingMessage(
+ const nsAString& aMessage, StructuredCloneData& aData,
+ nsTArray<StructuredCloneData>* aRetVal) {
+ ClonedMessageData data;
+ if (!BuildClonedMessageData(aData, data)) {
+ return false;
+ }
+ return SendSyncMessage(PromiseFlatString(aMessage), data, aRetVal);
+}
+
+nsresult BrowserChild::DoSendAsyncMessage(const nsAString& aMessage,
+ StructuredCloneData& aData) {
+ ClonedMessageData data;
+ if (!BuildClonedMessageData(aData, data)) {
+ return NS_ERROR_DOM_DATA_CLONE_ERR;
+ }
+ if (!SendAsyncMessage(PromiseFlatString(aMessage), data)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ return NS_OK;
+}
+
+/* static */
+nsTArray<RefPtr<BrowserChild>> BrowserChild::GetAll() {
+ StaticMutexAutoLock lock(sBrowserChildrenMutex);
+
+ if (!sBrowserChildren) {
+ return {};
+ }
+
+ return ToTArray<nsTArray<RefPtr<BrowserChild>>>(sBrowserChildren->Values());
+}
+
+BrowserChild* BrowserChild::GetFrom(PresShell* aPresShell) {
+ Document* doc = aPresShell->GetDocument();
+ if (!doc) {
+ return nullptr;
+ }
+ nsCOMPtr<nsIDocShell> docShell(doc->GetDocShell());
+ return GetFrom(docShell);
+}
+
+BrowserChild* BrowserChild::GetFrom(layers::LayersId aLayersId) {
+ StaticMutexAutoLock lock(sBrowserChildrenMutex);
+ if (!sBrowserChildren) {
+ return nullptr;
+ }
+ return sBrowserChildren->Get(uint64_t(aLayersId));
+}
+
+void BrowserChild::DidComposite(mozilla::layers::TransactionId aTransactionId,
+ const TimeStamp& aCompositeStart,
+ const TimeStamp& aCompositeEnd) {
+ MOZ_ASSERT(mPuppetWidget);
+ RefPtr<WebRenderLayerManager> lm =
+ mPuppetWidget->GetWindowRenderer()->AsWebRender();
+ MOZ_ASSERT(lm);
+
+ if (lm) {
+ lm->DidComposite(aTransactionId, aCompositeStart, aCompositeEnd);
+ }
+}
+
+void BrowserChild::ClearCachedResources() {
+ MOZ_ASSERT(mPuppetWidget);
+ RefPtr<WebRenderLayerManager> lm =
+ mPuppetWidget->GetWindowRenderer()->AsWebRender();
+ if (lm) {
+ lm->ClearCachedResources();
+ }
+
+ if (nsCOMPtr<Document> document = GetTopLevelDocument()) {
+ nsPresContext* presContext = document->GetPresContext();
+ if (presContext) {
+ presContext->NotifyPaintStatusReset();
+ }
+ }
+}
+
+void BrowserChild::SchedulePaint() {
+ nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
+ if (!docShell) {
+ return;
+ }
+
+ // We don't use BrowserChildBase::GetPresShell() here because that would
+ // create a content viewer if one doesn't exist yet. Creating a content viewer
+ // can cause JS to run, which we want to avoid. nsIDocShell::GetPresShell
+ // returns null if no content viewer exists yet.
+ if (RefPtr<PresShell> presShell = docShell->GetPresShell()) {
+ if (nsIFrame* root = presShell->GetRootFrame()) {
+ root->SchedulePaint();
+ }
+ }
+}
+
+void BrowserChild::ReinitRendering() {
+ MOZ_ASSERT(mLayersId.IsValid());
+
+ // In some cases, like when we create a windowless browser,
+ // RemoteLayerTreeOwner/BrowserChild is not connected to a compositor.
+ if (mLayersConnectRequested.isNothing() ||
+ mLayersConnectRequested == Some(false)) {
+ return;
+ }
+
+ // Before we establish a new PLayerTransaction, we must connect our layer tree
+ // id, CompositorBridge, and the widget compositor all together again.
+ // Normally this happens in BrowserParent before BrowserChild is given
+ // rendering information.
+ //
+ // In this case, we will send a sync message to our BrowserParent, which in
+ // turn will send a sync message to the Compositor of the widget owning this
+ // tab. This guarantees the correct association is in place before our
+ // PLayerTransaction constructor message arrives on the cross-process
+ // compositor bridge.
+ CompositorOptions options;
+ SendEnsureLayersConnected(&options);
+ mCompositorOptions = Some(options);
+
+ bool success = false;
+ RefPtr<CompositorBridgeChild> cb = CompositorBridgeChild::Get();
+
+ if (cb) {
+ success = CreateRemoteLayerManager(cb);
+ }
+
+ if (!success) {
+ NS_WARNING("failed to recreate layer manager");
+ return;
+ }
+
+ mLayersConnected = Some(true);
+ ImageBridgeChild::IdentifyCompositorTextureHost(mTextureFactoryIdentifier);
+ gfx::VRManagerChild::IdentifyTextureHost(mTextureFactoryIdentifier);
+
+ InitAPZState();
+ if (nsCOMPtr<Document> doc = GetTopLevelDocument()) {
+ doc->NotifyLayerManagerRecreated();
+ }
+
+ if (mRenderLayers) {
+ SchedulePaint();
+ }
+}
+
+void BrowserChild::ReinitRenderingForDeviceReset() {
+ RefPtr<WebRenderLayerManager> lm =
+ mPuppetWidget->GetWindowRenderer()->AsWebRender();
+ if (lm) {
+ lm->DoDestroy(/* aIsSync */ true);
+ }
+
+ // Proceed with destroying and recreating the layer manager.
+ ReinitRendering();
+}
+
+NS_IMETHODIMP
+BrowserChild::OnShowTooltip(int32_t aXCoords, int32_t aYCoords,
+ const nsAString& aTipText,
+ const nsAString& aTipDir) {
+ nsString str(aTipText);
+ nsString dir(aTipDir);
+ SendShowTooltip(aXCoords, aYCoords, str, dir);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BrowserChild::OnHideTooltip() {
+ SendHideTooltip();
+ return NS_OK;
+}
+
+void BrowserChild::NotifyJankedAnimations(
+ const nsTArray<uint64_t>& aJankedAnimations) {
+ MOZ_ASSERT(mPuppetWidget);
+ RefPtr<WebRenderLayerManager> lm =
+ mPuppetWidget->GetWindowRenderer()->AsWebRender();
+ if (lm) {
+ lm->UpdatePartialPrerenderedAnimations(aJankedAnimations);
+ }
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvUIResolutionChanged(
+ const float& aDpi, const int32_t& aRounding, const double& aScale) {
+ ScreenIntSize oldScreenSize = GetInnerSize();
+ if (aDpi > 0) {
+ mPuppetWidget->UpdateBackingScaleCache(aDpi, aRounding, aScale);
+ }
+
+ ScreenIntSize screenSize = GetInnerSize();
+ if (mHasValidInnerSize && oldScreenSize != screenSize) {
+ ScreenIntRect screenRect = GetOuterRect();
+
+ // See RecvUpdateDimensions for the order of these operations.
+ nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(WebNavigation());
+ baseWin->SetPositionAndSize(0, 0, screenSize.width, screenSize.height,
+ nsIBaseWindow::eRepaint);
+
+ mPuppetWidget->Resize(screenRect.x + mClientOffset.x + mChromeOffset.x,
+ screenRect.y + mClientOffset.y + mChromeOffset.y,
+ screenSize.width, screenSize.height, true);
+ }
+
+ nsCOMPtr<Document> document(GetTopLevelDocument());
+ RefPtr<nsPresContext> presContext =
+ document ? document->GetPresContext() : nullptr;
+ if (presContext) {
+ presContext->UIResolutionChangedSync();
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvSafeAreaInsetsChanged(
+ const mozilla::ScreenIntMargin& aSafeAreaInsets) {
+ mPuppetWidget->UpdateSafeAreaInsets(aSafeAreaInsets);
+
+ nsCOMPtr<nsIScreenManager> screenMgr =
+ do_GetService("@mozilla.org/gfx/screenmanager;1");
+ ScreenIntMargin currentSafeAreaInsets;
+ if (screenMgr) {
+ // aSafeAreaInsets is for current screen. But we have to calculate
+ // safe insets for content window.
+ int32_t x, y, cx, cy;
+ GetDimensions(DimensionKind::Outer, &x, &y, &cx, &cy);
+ nsCOMPtr<nsIScreen> screen;
+ screenMgr->ScreenForRect(x, y, cx, cy, getter_AddRefs(screen));
+
+ if (screen) {
+ LayoutDeviceIntRect windowRect(x + mClientOffset.x + mChromeOffset.x,
+ y + mClientOffset.y + mChromeOffset.y, cx,
+ cy);
+ currentSafeAreaInsets = nsContentUtils::GetWindowSafeAreaInsets(
+ screen, aSafeAreaInsets, windowRect);
+ }
+ }
+
+ if (nsCOMPtr<Document> document = GetTopLevelDocument()) {
+ nsPresContext* presContext = document->GetPresContext();
+ if (presContext) {
+ presContext->SetSafeAreaInsets(currentSafeAreaInsets);
+ }
+ }
+
+ // https://github.com/w3c/csswg-drafts/issues/4670
+ // Actually we don't set this value on sub document. This behaviour is
+ // same as Blink that safe area insets isn't set on sub document.
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvAllowScriptsToClose() {
+ nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation());
+ if (window) {
+ nsGlobalWindowOuter::Cast(window)->AllowScriptsToClose();
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvReleaseAllPointerCapture() {
+ PointerEventHandler::ReleaseAllPointerCapture();
+ return IPC_OK();
+}
+
+PPaymentRequestChild* BrowserChild::AllocPPaymentRequestChild() {
+ MOZ_CRASH(
+ "We should never be manually allocating PPaymentRequestChild actors");
+ return nullptr;
+}
+
+bool BrowserChild::DeallocPPaymentRequestChild(PPaymentRequestChild* actor) {
+ delete actor;
+ return true;
+}
+
+ScreenIntSize BrowserChild::GetInnerSize() {
+ LayoutDeviceIntSize innerSize =
+ RoundedToInt(mUnscaledInnerSize * mPuppetWidget->GetDefaultScale());
+ return ViewAs<ScreenPixel>(
+ innerSize, PixelCastJustification::LayoutDeviceIsScreenForTabDims);
+};
+
+Maybe<nsRect> BrowserChild::GetVisibleRect() const {
+ if (mIsTopLevel) {
+ // We are conservative about visible rects for top-level browsers to avoid
+ // artifacts when resizing
+ return Nothing();
+ }
+ return mEffectsInfo.mVisibleRect;
+}
+
+Maybe<LayoutDeviceRect>
+BrowserChild::GetTopLevelViewportVisibleRectInSelfCoords() const {
+ if (mIsTopLevel) {
+ return Nothing();
+ }
+
+ if (!mChildToParentConversionMatrix) {
+ // We have no way to tell this remote document visible rect right now.
+ return Nothing();
+ }
+
+ Maybe<LayoutDeviceToLayoutDeviceMatrix4x4> inverse =
+ mChildToParentConversionMatrix->MaybeInverse();
+ if (!inverse) {
+ return Nothing();
+ }
+
+ // Convert the remote document visible rect to the coordinate system of the
+ // iframe document.
+ Maybe<LayoutDeviceRect> rect = UntransformBy(
+ *inverse,
+ ViewAs<LayoutDevicePixel>(
+ mTopLevelViewportVisibleRectInBrowserCoords,
+ PixelCastJustification::ContentProcessIsLayerInUiProcess),
+ LayoutDeviceRect::MaxIntRect());
+ if (!rect) {
+ return Nothing();
+ }
+
+ return rect;
+}
+
+ScreenIntRect BrowserChild::GetOuterRect() {
+ LayoutDeviceIntRect outerRect =
+ RoundedToInt(mUnscaledOuterRect * mPuppetWidget->GetDefaultScale());
+ return ViewAs<ScreenPixel>(
+ outerRect, PixelCastJustification::LayoutDeviceIsScreenForTabDims);
+}
+
+void BrowserChild::PaintWhileInterruptingJS() {
+ if (!IPCOpen() || !mPuppetWidget || !mPuppetWidget->HasWindowRenderer()) {
+ // Don't bother doing anything now. Better to wait until we receive the
+ // message on the PContent channel.
+ return;
+ }
+
+ MOZ_DIAGNOSTIC_ASSERT(nsContentUtils::IsSafeToRunScript());
+ nsAutoScriptBlocker scriptBlocker;
+ RecvRenderLayers(/* aEnabled = */ true);
+}
+
+void BrowserChild::UnloadLayersWhileInterruptingJS() {
+ if (!IPCOpen() || !mPuppetWidget || !mPuppetWidget->HasWindowRenderer()) {
+ // Don't bother doing anything now. Better to wait until we receive the
+ // message on the PContent channel.
+ return;
+ }
+
+ MOZ_DIAGNOSTIC_ASSERT(nsContentUtils::IsSafeToRunScript());
+ nsAutoScriptBlocker scriptBlocker;
+ RecvRenderLayers(/* aEnabled = */ false);
+}
+
+nsresult BrowserChild::CanCancelContentJS(
+ nsIRemoteTab::NavigationType aNavigationType, int32_t aNavigationIndex,
+ nsIURI* aNavigationURI, int32_t aEpoch, bool* aCanCancel) {
+ nsresult rv;
+ *aCanCancel = false;
+
+ if (aEpoch <= mCancelContentJSEpoch) {
+ // The next page loaded before we got here, so we shouldn't try to cancel
+ // the content JS.
+ return NS_OK;
+ }
+
+ // If we have session history in the parent we've already performed
+ // the checks following, so we can return early.
+ if (mozilla::SessionHistoryInParent()) {
+ *aCanCancel = true;
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
+ nsCOMPtr<nsISHistory> history;
+ if (docShell) {
+ history = nsDocShell::Cast(docShell)->GetSessionHistory()->LegacySHistory();
+ }
+
+ if (!history) {
+ return NS_ERROR_FAILURE;
+ }
+
+ int32_t current;
+ rv = history->GetIndex(&current);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (current == -1) {
+ // This tab has no history! Just return.
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsISHEntry> entry;
+ rv = history->GetEntryAtIndex(current, getter_AddRefs(entry));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIURI> currentURI = entry->GetURI();
+ if (!currentURI->SchemeIs("http") && !currentURI->SchemeIs("https") &&
+ !currentURI->SchemeIs("file")) {
+ // Only cancel content JS for http(s) and file URIs. Other URIs are probably
+ // internal and we should just let them run to completion.
+ return NS_OK;
+ }
+
+ if (aNavigationType == nsIRemoteTab::NAVIGATE_BACK) {
+ aNavigationIndex = current - 1;
+ } else if (aNavigationType == nsIRemoteTab::NAVIGATE_FORWARD) {
+ aNavigationIndex = current + 1;
+ } else if (aNavigationType == nsIRemoteTab::NAVIGATE_URL) {
+ if (!aNavigationURI) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (aNavigationURI->SchemeIs("javascript")) {
+ // "javascript:" URIs don't (necessarily) trigger navigation to a
+ // different page, so don't allow the current page's JS to terminate.
+ return NS_OK;
+ }
+
+ // If navigating directly to a URL (e.g. via hitting Enter in the location
+ // bar), then we can cancel anytime the next URL is different from the
+ // current, *excluding* the ref ("#").
+ bool equals;
+ rv = currentURI->EqualsExceptRef(aNavigationURI, &equals);
+ NS_ENSURE_SUCCESS(rv, rv);
+ *aCanCancel = !equals;
+ return NS_OK;
+ }
+ // Note: aNavigationType may also be NAVIGATE_INDEX, in which case we don't
+ // need to do anything special.
+
+ int32_t delta = aNavigationIndex > current ? 1 : -1;
+ for (int32_t i = current + delta; i != aNavigationIndex + delta; i += delta) {
+ nsCOMPtr<nsISHEntry> nextEntry;
+ // If `i` happens to be negative, this call will fail (which is what we
+ // would want to happen).
+ rv = history->GetEntryAtIndex(i, getter_AddRefs(nextEntry));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISHEntry> laterEntry = delta == 1 ? nextEntry : entry;
+ nsCOMPtr<nsIURI> thisURI = entry->GetURI();
+ nsCOMPtr<nsIURI> nextURI = nextEntry->GetURI();
+
+ // If we changed origin and the load wasn't in a subframe, we know it was
+ // a full document load, so we can cancel the content JS safely.
+ if (!laterEntry->GetIsSubFrame()) {
+ nsAutoCString thisHost;
+ rv = thisURI->GetPrePath(thisHost);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString nextHost;
+ rv = nextURI->GetPrePath(nextHost);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!thisHost.Equals(nextHost)) {
+ *aCanCancel = true;
+ return NS_OK;
+ }
+ }
+
+ entry = nextEntry;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP BrowserChild::OnStateChange(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest,
+ uint32_t aStateFlags,
+ nsresult aStatus) {
+ if (!IPCOpen() || !mShouldSendWebProgressEventsToParent) {
+ return NS_OK;
+ }
+
+ // We shouldn't need to notify the parent of redirect state changes, since
+ // with DocumentChannel that only happens when we switch to the real channel,
+ // and that's an implementation detail that we can hide.
+ if (aStateFlags & nsIWebProgressListener::STATE_IS_REDIRECTED_DOCUMENT) {
+ return NS_OK;
+ }
+
+ // Our OnStateChange call must have provided the nsIDocShell which the source
+ // comes from. We'll use this to locate the corresponding BrowsingContext in
+ // the parent process.
+ nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(aWebProgress);
+ if (!docShell) {
+ MOZ_ASSERT_UNREACHABLE("aWebProgress is null or not a nsIDocShell?");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ WebProgressData webProgressData;
+ Maybe<WebProgressStateChangeData> stateChangeData;
+ RequestData requestData;
+
+ MOZ_TRY(PrepareProgressListenerData(aWebProgress, aRequest, webProgressData,
+ requestData));
+
+ RefPtr<BrowsingContext> browsingContext = docShell->GetBrowsingContext();
+ if (browsingContext->IsTopContent()) {
+ stateChangeData.emplace();
+
+ stateChangeData->isNavigating() = docShell->GetIsNavigating();
+ stateChangeData->mayEnableCharacterEncodingMenu() =
+ docShell->GetMayEnableCharacterEncodingMenu();
+
+ RefPtr<Document> document = browsingContext->GetExtantDocument();
+ if (document && aStateFlags & nsIWebProgressListener::STATE_STOP) {
+ document->GetContentType(stateChangeData->contentType());
+ document->GetCharacterSet(stateChangeData->charset());
+ stateChangeData->documentURI() = document->GetDocumentURIObject();
+ } else {
+ stateChangeData->contentType().SetIsVoid(true);
+ stateChangeData->charset().SetIsVoid(true);
+ }
+ }
+
+ Unused << SendOnStateChange(webProgressData, requestData, aStateFlags,
+ aStatus, stateChangeData);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP BrowserChild::OnProgressChange(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest,
+ int32_t aCurSelfProgress,
+ int32_t aMaxSelfProgress,
+ int32_t aCurTotalProgress,
+ int32_t aMaxTotalProgress) {
+ if (!IPCOpen() || !mShouldSendWebProgressEventsToParent) {
+ return NS_OK;
+ }
+
+ // FIXME: We currently ignore ProgressChange events from out-of-process
+ // subframes both here and in BrowserParent. We may want to change this
+ // behaviour in the future.
+ if (!GetBrowsingContext()->IsTopContent()) {
+ return NS_OK;
+ }
+
+ // As we're being filtered by nsBrowserStatusFilter, we will be passed either
+ // nullptr or 0 for all arguments other than aCurTotalProgress and
+ // aMaxTotalProgress. Don't bother sending them.
+ MOZ_ASSERT(!aWebProgress);
+ MOZ_ASSERT(!aRequest);
+ MOZ_ASSERT(aCurSelfProgress == 0);
+ MOZ_ASSERT(aMaxSelfProgress == 0);
+
+ Unused << SendOnProgressChange(aCurTotalProgress, aMaxTotalProgress);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP BrowserChild::OnLocationChange(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest,
+ nsIURI* aLocation,
+ uint32_t aFlags) {
+ if (!IPCOpen() || !mShouldSendWebProgressEventsToParent) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(aWebProgress);
+ if (!docShell) {
+ MOZ_ASSERT_UNREACHABLE("aWebProgress is null or not a nsIDocShell?");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ RefPtr<BrowsingContext> browsingContext = docShell->GetBrowsingContext();
+ RefPtr<Document> document = browsingContext->GetExtantDocument();
+ if (!document) {
+ return NS_OK;
+ }
+
+ WebProgressData webProgressData;
+ RequestData requestData;
+
+ MOZ_TRY(PrepareProgressListenerData(aWebProgress, aRequest, webProgressData,
+ requestData));
+
+ Maybe<WebProgressLocationChangeData> locationChangeData;
+
+ bool canGoBack = false;
+ bool canGoForward = false;
+ if (!mozilla::SessionHistoryInParent()) {
+ MOZ_TRY(WebNavigation()->GetCanGoBack(&canGoBack));
+ MOZ_TRY(WebNavigation()->GetCanGoForward(&canGoForward));
+ }
+
+ if (browsingContext->IsTopContent()) {
+ MOZ_ASSERT(
+ browsingContext == GetBrowsingContext(),
+ "Toplevel content BrowsingContext which isn't GetBrowsingContext()?");
+
+ locationChangeData.emplace();
+
+ document->GetContentType(locationChangeData->contentType());
+ locationChangeData->isNavigating() = docShell->GetIsNavigating();
+ locationChangeData->documentURI() = document->GetDocumentURIObject();
+ document->GetTitle(locationChangeData->title());
+ document->GetCharacterSet(locationChangeData->charset());
+
+ locationChangeData->mayEnableCharacterEncodingMenu() =
+ docShell->GetMayEnableCharacterEncodingMenu();
+
+ locationChangeData->contentPrincipal() = document->NodePrincipal();
+ locationChangeData->contentPartitionedPrincipal() =
+ document->PartitionedPrincipal();
+ locationChangeData->csp() = document->GetCsp();
+ locationChangeData->referrerInfo() = document->ReferrerInfo();
+ locationChangeData->isSyntheticDocument() = document->IsSyntheticDocument();
+
+ if (nsCOMPtr<nsILoadGroup> loadGroup = document->GetDocumentLoadGroup()) {
+ uint64_t requestContextID = 0;
+ MOZ_TRY(loadGroup->GetRequestContextID(&requestContextID));
+ locationChangeData->requestContextID() = Some(requestContextID);
+ }
+
+#ifdef MOZ_CRASHREPORTER
+ if (CrashReporter::GetEnabled()) {
+ nsCOMPtr<nsIURI> annotationURI;
+
+ nsresult rv =
+ NS_MutateURI(aLocation).SetUserPass(""_ns).Finalize(annotationURI);
+
+ if (NS_FAILED(rv)) {
+ // Ignore failures on about: URIs.
+ annotationURI = aLocation;
+ }
+
+ CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::URL,
+ annotationURI->GetSpecOrDefault());
+ }
+#endif
+ }
+
+ Unused << SendOnLocationChange(webProgressData, requestData, aLocation,
+ aFlags, canGoBack, canGoForward,
+ locationChangeData);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP BrowserChild::OnStatusChange(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest,
+ nsresult aStatus,
+ const char16_t* aMessage) {
+ if (!IPCOpen() || !mShouldSendWebProgressEventsToParent) {
+ return NS_OK;
+ }
+
+ // FIXME: We currently ignore StatusChange from out-of-process subframes both
+ // here and in BrowserParent. We may want to change this behaviour in the
+ // future.
+ if (!GetBrowsingContext()->IsTopContent()) {
+ return NS_OK;
+ }
+
+ // As we're being filtered by nsBrowserStatusFilter, we will be passed either
+ // nullptr or NS_OK for all arguments other than aMessage. Don't bother
+ // sending them.
+ MOZ_ASSERT(!aWebProgress);
+ MOZ_ASSERT(!aRequest);
+ MOZ_ASSERT(aStatus == NS_OK);
+
+ Unused << SendOnStatusChange(nsDependentString(aMessage));
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP BrowserChild::OnSecurityChange(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest,
+ uint32_t aState) {
+ // Security changes are now handled entirely in the parent process
+ // so we don't need to worry about forwarding them (and we shouldn't
+ // be receiving any to forward).
+ return NS_OK;
+}
+
+NS_IMETHODIMP BrowserChild::OnContentBlockingEvent(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest,
+ uint32_t aEvent) {
+ // The OnContentBlockingEvent only happenes in the parent process. It should
+ // not be seen in the content process.
+ MOZ_DIAGNOSTIC_ASSERT(
+ false, "OnContentBlockingEvent should not be seen in content process.");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP BrowserChild::OnProgressChange64(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest,
+ int64_t aCurSelfProgress,
+ int64_t aMaxSelfProgress,
+ int64_t aCurTotalProgress,
+ int64_t aMaxTotalProgress) {
+ // All the events we receive are filtered through an nsBrowserStatusFilter,
+ // which accepts ProgressChange64 events, but truncates the progress values to
+ // uint32_t and calls OnProgressChange.
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP BrowserChild::OnRefreshAttempted(nsIWebProgress* aWebProgress,
+ nsIURI* aRefreshURI,
+ uint32_t aMillis, bool aSameURI,
+ bool* aOut) {
+ NS_ENSURE_ARG_POINTER(aOut);
+ *aOut = true;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP BrowserChild::NotifyNavigationFinished() {
+ Unused << SendNavigationFinished();
+ return NS_OK;
+}
+
+nsresult BrowserChild::PrepareRequestData(nsIRequest* aRequest,
+ RequestData& aRequestData) {
+ nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
+ if (!channel) {
+ aRequestData.requestURI() = nullptr;
+ return NS_OK;
+ }
+
+ nsresult rv = channel->GetURI(getter_AddRefs(aRequestData.requestURI()));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = channel->GetOriginalURI(
+ getter_AddRefs(aRequestData.originalRequestURI()));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsIClassifiedChannel> classifiedChannel = do_QueryInterface(channel);
+ if (classifiedChannel) {
+ rv = classifiedChannel->GetMatchedList(aRequestData.matchedList());
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ return NS_OK;
+}
+
+nsresult BrowserChild::PrepareProgressListenerData(
+ nsIWebProgress* aWebProgress, nsIRequest* aRequest,
+ WebProgressData& aWebProgressData, RequestData& aRequestData) {
+ nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(aWebProgress);
+ if (!docShell) {
+ MOZ_ASSERT_UNREACHABLE("aWebProgress is null or not a nsIDocShell?");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ aWebProgressData.browsingContext() = docShell->GetBrowsingContext();
+ nsresult rv = aWebProgress->GetLoadType(&aWebProgressData.loadType());
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return PrepareRequestData(aRequest, aRequestData);
+}
+
+void BrowserChild::UpdateSessionStore() {
+ if (mSessionStoreChild) {
+ mSessionStoreChild->UpdateSessionStore();
+ }
+}
+
+#ifdef XP_WIN
+RefPtr<PBrowserChild::IsWindowSupportingProtectedMediaPromise>
+BrowserChild::DoesWindowSupportProtectedMedia() {
+ MOZ_ASSERT(
+ NS_IsMainThread(),
+ "Protected media support check should be done on main thread only.");
+ if (mWindowSupportsProtectedMedia) {
+ // If we've already checked and have a cached result, resolve with that.
+ return IsWindowSupportingProtectedMediaPromise::CreateAndResolve(
+ mWindowSupportsProtectedMedia.value(), __func__);
+ }
+ RefPtr<BrowserChild> self = this;
+ // We chain off the promise rather than passing it directly so we can cache
+ // the result and use that for future calls.
+ return SendIsWindowSupportingProtectedMedia(ChromeOuterWindowID())
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [self](bool isSupported) {
+ // If a result was cached while this check was inflight, ensure the
+ // results match.
+ MOZ_ASSERT_IF(
+ self->mWindowSupportsProtectedMedia,
+ self->mWindowSupportsProtectedMedia.value() == isSupported);
+ // Cache the response as it will not change during the lifetime
+ // of this object.
+ self->mWindowSupportsProtectedMedia = Some(isSupported);
+ return IsWindowSupportingProtectedMediaPromise::CreateAndResolve(
+ self->mWindowSupportsProtectedMedia.value(), __func__);
+ },
+ [](ResponseRejectReason reason) {
+ return IsWindowSupportingProtectedMediaPromise::CreateAndReject(
+ reason, __func__);
+ });
+}
+#endif
+
+void BrowserChild::NotifyContentBlockingEvent(
+ uint32_t aEvent, nsIChannel* aChannel, bool aBlocked,
+ const nsACString& aTrackingOrigin,
+ const nsTArray<nsCString>& aTrackingFullHashes,
+ const Maybe<
+ mozilla::ContentBlockingNotifier::StorageAccessPermissionGrantedReason>&
+ aReason,
+ const Maybe<ContentBlockingNotifier::CanvasFingerprinter>&
+ aCanvasFingerprinter,
+ const Maybe<bool> aCanvasFingerprinterKnownText) {
+ if (!IPCOpen()) {
+ return;
+ }
+
+ RequestData requestData;
+ if (NS_SUCCEEDED(PrepareRequestData(aChannel, requestData))) {
+ Unused << SendNotifyContentBlockingEvent(
+ aEvent, requestData, aBlocked, PromiseFlatCString(aTrackingOrigin),
+ aTrackingFullHashes, aReason, aCanvasFingerprinter,
+ aCanvasFingerprinterKnownText);
+ }
+}
+
+NS_IMETHODIMP
+BrowserChild::ContentTransformsReceived(JSContext* aCx,
+ dom::Promise** aPromise) {
+ auto* globalObject = xpc::CurrentNativeGlobal(aCx);
+ ErrorResult rv;
+ if (mChildToParentConversionMatrix) {
+ // Already received content transforms
+ RefPtr<Promise> promise =
+ Promise::CreateResolvedWithUndefined(globalObject, rv);
+ promise.forget(aPromise);
+ return rv.StealNSResult();
+ }
+
+ if (!mContentTransformPromise) {
+ mContentTransformPromise = Promise::Create(globalObject, rv);
+ }
+
+ MOZ_ASSERT(globalObject == mContentTransformPromise->GetGlobalObject());
+ NS_IF_ADDREF(*aPromise = mContentTransformPromise);
+ return rv.StealNSResult();
+}
+
+BrowserChildMessageManager::BrowserChildMessageManager(
+ BrowserChild* aBrowserChild)
+ : ContentFrameMessageManager(new nsFrameMessageManager(aBrowserChild)),
+ mBrowserChild(aBrowserChild) {}
+
+BrowserChildMessageManager::~BrowserChildMessageManager() = default;
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(BrowserChildMessageManager)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(BrowserChildMessageManager,
+ DOMEventTargetHelper)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageManager);
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowserChild);
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_REFERENCE
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(BrowserChildMessageManager,
+ DOMEventTargetHelper)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageManager)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowserChild)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BrowserChildMessageManager)
+ NS_INTERFACE_MAP_ENTRY(nsIMessageSender)
+ NS_INTERFACE_MAP_ENTRY_CONCRETE(ContentFrameMessageManager)
+ NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
+
+NS_IMPL_ADDREF_INHERITED(BrowserChildMessageManager, DOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(BrowserChildMessageManager, DOMEventTargetHelper)
+
+JSObject* BrowserChildMessageManager::WrapObject(
+ JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
+ return ContentFrameMessageManager_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+void BrowserChildMessageManager::MarkForCC() {
+ if (mBrowserChild) {
+ mBrowserChild->MarkScopesForCC();
+ }
+ EventListenerManager* elm = GetExistingListenerManager();
+ if (elm) {
+ elm->MarkForCC();
+ }
+ MessageManagerGlobal::MarkForCC();
+}
+
+Nullable<WindowProxyHolder> BrowserChildMessageManager::GetContent(
+ ErrorResult& aError) {
+ nsCOMPtr<nsIDocShell> docShell = GetDocShell(aError);
+ if (!docShell) {
+ return nullptr;
+ }
+ return WindowProxyHolder(docShell->GetBrowsingContext());
+}
+
+already_AddRefed<nsIDocShell> BrowserChildMessageManager::GetDocShell(
+ ErrorResult& aError) {
+ if (!mBrowserChild) {
+ aError.Throw(NS_ERROR_NULL_POINTER);
+ return nullptr;
+ }
+ nsCOMPtr<nsIDocShell> window =
+ do_GetInterface(mBrowserChild->WebNavigation());
+ return window.forget();
+}
+
+already_AddRefed<nsIEventTarget>
+BrowserChildMessageManager::GetTabEventTarget() {
+ return do_AddRef(GetMainThreadSerialEventTarget());
+}
+
+nsresult BrowserChildMessageManager::Dispatch(
+ already_AddRefed<nsIRunnable>&& aRunnable) const {
+ return SchedulerGroup::Dispatch(std::move(aRunnable));
+}
diff --git a/dom/ipc/BrowserChild.h b/dom/ipc/BrowserChild.h
new file mode 100644
index 0000000000..ddbf8f0b74
--- /dev/null
+++ b/dom/ipc/BrowserChild.h
@@ -0,0 +1,850 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_BrowserChild_h
+#define mozilla_dom_BrowserChild_h
+
+#include "mozilla/dom/ContentFrameMessageManager.h"
+#include "mozilla/dom/PBrowserChild.h"
+#include "nsIWebNavigation.h"
+#include "nsCOMPtr.h"
+#include "nsIWebBrowserChrome.h"
+#include "nsIWebBrowserChromeFocus.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIWindowProvider.h"
+#include "nsIDocShell.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsWeakReference.h"
+#include "nsIBrowserChild.h"
+#include "nsITooltipListener.h"
+#include "nsIWebProgressListener.h"
+#include "nsIWebProgressListener2.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/dom/TabContext.h"
+#include "mozilla/dom/CoalescedMouseData.h"
+#include "mozilla/dom/CoalescedTouchData.h"
+#include "mozilla/dom/CoalescedWheelData.h"
+#include "mozilla/dom/MessageManagerCallback.h"
+#include "mozilla/dom/VsyncMainChild.h"
+#include "mozilla/DOMEventTargetHelper.h"
+#include "mozilla/EventDispatcher.h"
+#include "mozilla/EventForwards.h"
+#include "mozilla/layers/APZCCallbackHelper.h"
+#include "mozilla/layers/CompositorOptions.h"
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/GeckoContentControllerTypes.h"
+#include "mozilla/dom/ipc/IdType.h"
+#include "PuppetWidget.h"
+#include "nsDeque.h"
+#include "nsIRemoteTab.h"
+
+class nsBrowserStatusFilter;
+class nsIDOMWindow;
+class nsIHttpChannel;
+class nsIRequest;
+class nsISerialEventTarget;
+class nsIWebProgress;
+class nsWebBrowser;
+class nsDocShellLoadState;
+
+template <typename T>
+class nsTHashtable;
+template <typename T>
+class nsPtrHashKey;
+
+namespace mozilla {
+enum class NativeKeyBindingsType : uint8_t;
+
+class AbstractThread;
+class PresShell;
+
+namespace layers {
+class APZChild;
+class APZEventState;
+class AsyncDragMetrics;
+class IAPZCTreeManager;
+class ImageCompositeNotification;
+class PCompositorBridgeChild;
+} // namespace layers
+
+namespace widget {
+struct AutoCacheNativeKeyCommands;
+} // namespace widget
+
+namespace dom {
+
+class BrowserChild;
+class BrowsingContext;
+class TabGroup;
+class ClonedMessageData;
+class CoalescedMouseData;
+class CoalescedWheelData;
+class SessionStoreChild;
+class RequestData;
+class WebProgressData;
+
+class BrowserChildMessageManager : public ContentFrameMessageManager,
+ public nsIMessageSender,
+ public nsSupportsWeakReference {
+ public:
+ explicit BrowserChildMessageManager(BrowserChild* aBrowserChild);
+
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(BrowserChildMessageManager,
+ DOMEventTargetHelper)
+
+ void MarkForCC();
+
+ JSObject* WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) override;
+
+ Nullable<WindowProxyHolder> GetContent(ErrorResult& aError) override;
+ already_AddRefed<nsIDocShell> GetDocShell(ErrorResult& aError) override;
+ already_AddRefed<nsIEventTarget> GetTabEventTarget() override;
+
+ NS_FORWARD_SAFE_NSIMESSAGESENDER(mMessageManager)
+
+ void GetEventTargetParent(EventChainPreVisitor& aVisitor) override {
+ aVisitor.mForceContentDispatch = true;
+ }
+
+ // Dispatch a runnable related to the global.
+ nsresult Dispatch(already_AddRefed<nsIRunnable>&& aRunnable) const;
+
+ RefPtr<BrowserChild> mBrowserChild;
+
+ protected:
+ ~BrowserChildMessageManager();
+};
+
+/**
+ * BrowserChild implements the child actor part of the PBrowser protocol. See
+ * PBrowser for more information.
+ */
+class BrowserChild final : public nsMessageManagerScriptExecutor,
+ public ipc::MessageManagerCallback,
+ public PBrowserChild,
+ public nsIWebBrowserChrome,
+ public nsIWebBrowserChromeFocus,
+ public nsIInterfaceRequestor,
+ public nsIWindowProvider,
+ public nsSupportsWeakReference,
+ public nsIBrowserChild,
+ public nsIObserver,
+ public nsIWebProgressListener2,
+ public TabContext,
+ public nsITooltipListener,
+ public mozilla::ipc::IShmemAllocator {
+ using PuppetWidget = mozilla::widget::PuppetWidget;
+ using ClonedMessageData = mozilla::dom::ClonedMessageData;
+ using CoalescedMouseData = mozilla::dom::CoalescedMouseData;
+ using CoalescedWheelData = mozilla::dom::CoalescedWheelData;
+ using APZEventState = mozilla::layers::APZEventState;
+ using TouchBehaviorFlags = mozilla::layers::TouchBehaviorFlags;
+
+ friend class PBrowserChild;
+
+ public:
+ /**
+ * Find BrowserChild of aTabId in the same content process of the
+ * caller.
+ */
+ static already_AddRefed<BrowserChild> FindBrowserChild(const TabId& aTabId);
+
+ // Return a list of all active BrowserChildren.
+ static nsTArray<RefPtr<BrowserChild>> GetAll();
+
+ public:
+ /**
+ * Create a new BrowserChild object.
+ */
+ BrowserChild(ContentChild* aManager, const TabId& aTabId,
+ const TabContext& aContext,
+ dom::BrowsingContext* aBrowsingContext, uint32_t aChromeFlags,
+ bool aIsTopLevel);
+
+ MOZ_CAN_RUN_SCRIPT nsresult Init(mozIDOMWindowProxy* aParent,
+ WindowGlobalChild* aInitialWindowChild);
+
+ /** Return a BrowserChild with the given attributes. */
+ static already_AddRefed<BrowserChild> Create(
+ ContentChild* aManager, const TabId& aTabId, const TabContext& aContext,
+ BrowsingContext* aBrowsingContext, uint32_t aChromeFlags,
+ bool aIsTopLevel);
+
+ // Let managees query if it is safe to send messages.
+ bool IsDestroyed() const { return mDestroyed; }
+
+ TabId GetTabId() const {
+ MOZ_ASSERT(mUniqueId != 0);
+ return mUniqueId;
+ }
+
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_NSIWEBBROWSERCHROME
+ NS_DECL_NSIWEBBROWSERCHROMEFOCUS
+ NS_DECL_NSIINTERFACEREQUESTOR
+ NS_DECL_NSIWINDOWPROVIDER
+ NS_DECL_NSIBROWSERCHILD
+ NS_DECL_NSIOBSERVER
+ NS_DECL_NSIWEBPROGRESSLISTENER
+ NS_DECL_NSIWEBPROGRESSLISTENER2
+ NS_DECL_NSITOOLTIPLISTENER
+
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(BrowserChild,
+ nsIBrowserChild)
+
+ FORWARD_SHMEM_ALLOCATOR_TO(PBrowserChild)
+
+ JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
+ return mBrowserChildMessageManager->WrapObject(aCx, aGivenProto);
+ }
+
+ // Get the Document for the top-level window in this tab.
+ already_AddRefed<Document> GetTopLevelDocument() const;
+
+ // Get the pres-shell of the document for the top-level window in this tab.
+ PresShell* GetTopLevelPresShell() const;
+
+ BrowserChildMessageManager* GetMessageManager() {
+ return mBrowserChildMessageManager;
+ }
+
+ bool IsTopLevel() const { return mIsTopLevel; }
+
+ bool ShouldSendWebProgressEventsToParent() const {
+ return mShouldSendWebProgressEventsToParent;
+ }
+
+ /**
+ * MessageManagerCallback methods that we override.
+ */
+ virtual bool DoSendBlockingMessage(
+ const nsAString& aMessage, StructuredCloneData& aData,
+ nsTArray<StructuredCloneData>* aRetVal) override;
+
+ virtual nsresult DoSendAsyncMessage(const nsAString& aMessage,
+ StructuredCloneData& aData) override;
+
+ bool DoUpdateZoomConstraints(const uint32_t& aPresShellId,
+ const ViewID& aViewId,
+ const Maybe<ZoomConstraints>& aConstraints);
+
+ mozilla::ipc::IPCResult RecvLoadURL(nsDocShellLoadState* aLoadState,
+ const ParentShowInfo& aInfo);
+
+ mozilla::ipc::IPCResult RecvCreateAboutBlankDocumentViewer(
+ nsIPrincipal* aPrincipal, nsIPrincipal* aPartitionedPrincipal);
+
+ mozilla::ipc::IPCResult RecvResumeLoad(const uint64_t& aPendingSwitchID,
+ const ParentShowInfo&);
+
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY mozilla::ipc::IPCResult
+ RecvCloneDocumentTreeIntoSelf(
+ const MaybeDiscarded<BrowsingContext>& aSourceBC,
+ const embedding::PrintData& aPrintData,
+ CloneDocumentTreeIntoSelfResolver&& aResolve);
+
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ mozilla::ipc::IPCResult RecvUpdateRemotePrintSettings(
+ const embedding::PrintData& aPrintData);
+
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ mozilla::ipc::IPCResult RecvShow(const ParentShowInfo&, const OwnerShowInfo&);
+
+ mozilla::ipc::IPCResult RecvInitRendering(
+ const TextureFactoryIdentifier& aTextureFactoryIdentifier,
+ const layers::LayersId& aLayersId,
+ const mozilla::layers::CompositorOptions& aCompositorOptions,
+ const bool& aLayersConnected);
+
+ mozilla::ipc::IPCResult RecvCompositorOptionsChanged(
+ const mozilla::layers::CompositorOptions& aNewOptions);
+
+ mozilla::ipc::IPCResult RecvUpdateDimensions(
+ const mozilla::dom::DimensionInfo& aDimensionInfo);
+ mozilla::ipc::IPCResult RecvSizeModeChanged(const nsSizeMode& aSizeMode);
+
+ mozilla::ipc::IPCResult RecvChildToParentMatrix(
+ const mozilla::Maybe<mozilla::gfx::Matrix4x4>& aMatrix,
+ const mozilla::ScreenRect& aTopLevelViewportVisibleRectInBrowserCoords);
+
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ mozilla::ipc::IPCResult RecvDynamicToolbarMaxHeightChanged(
+ const mozilla::ScreenIntCoord& aHeight);
+
+ mozilla::ipc::IPCResult RecvDynamicToolbarOffsetChanged(
+ const mozilla::ScreenIntCoord& aOffset);
+
+ mozilla::ipc::IPCResult RecvActivate(uint64_t aActionId);
+
+ mozilla::ipc::IPCResult RecvDeactivate(uint64_t aActionId);
+
+ mozilla::ipc::IPCResult RecvRealMouseMoveEvent(
+ const mozilla::WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId);
+ mozilla::ipc::IPCResult RecvNormalPriorityRealMouseMoveEvent(
+ const mozilla::WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId);
+ mozilla::ipc::IPCResult RecvRealMouseMoveEventForTests(
+ const mozilla::WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId);
+ mozilla::ipc::IPCResult RecvNormalPriorityRealMouseMoveEventForTests(
+ const mozilla::WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId);
+
+ mozilla::ipc::IPCResult RecvSynthMouseMoveEvent(
+ const mozilla::WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId);
+ mozilla::ipc::IPCResult RecvNormalPrioritySynthMouseMoveEvent(
+ const mozilla::WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId);
+
+ mozilla::ipc::IPCResult RecvRealMouseButtonEvent(
+ const mozilla::WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId);
+ mozilla::ipc::IPCResult RecvNormalPriorityRealMouseButtonEvent(
+ const mozilla::WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId);
+
+ mozilla::ipc::IPCResult RecvRealMouseEnterExitWidgetEvent(
+ const mozilla::WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId);
+ mozilla::ipc::IPCResult RecvNormalPriorityRealMouseEnterExitWidgetEvent(
+ const mozilla::WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId);
+
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ mozilla::ipc::IPCResult RecvRealDragEvent(const WidgetDragEvent& aEvent,
+ const uint32_t& aDragAction,
+ const uint32_t& aDropEffect,
+ nsIPrincipal* aPrincipal,
+ nsIContentSecurityPolicy* aCsp);
+
+ mozilla::ipc::IPCResult RecvRealKeyEvent(
+ const mozilla::WidgetKeyboardEvent& aEvent, const nsID& aUUID);
+
+ mozilla::ipc::IPCResult RecvNormalPriorityRealKeyEvent(
+ const mozilla::WidgetKeyboardEvent& aEvent, const nsID& aUUID);
+
+ mozilla::ipc::IPCResult RecvMouseWheelEvent(
+ const mozilla::WidgetWheelEvent& aEvent, const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId);
+
+ mozilla::ipc::IPCResult RecvNormalPriorityMouseWheelEvent(
+ const mozilla::WidgetWheelEvent& aEvent, const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId);
+
+ mozilla::ipc::IPCResult RecvRealTouchEvent(const WidgetTouchEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId,
+ const nsEventStatus& aApzResponse);
+
+ mozilla::ipc::IPCResult RecvNormalPriorityRealTouchEvent(
+ const WidgetTouchEvent& aEvent, const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId, const nsEventStatus& aApzResponse);
+
+ mozilla::ipc::IPCResult RecvRealTouchMoveEvent(
+ const WidgetTouchEvent& aEvent, const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId, const nsEventStatus& aApzResponse);
+
+ mozilla::ipc::IPCResult RecvNormalPriorityRealTouchMoveEvent(
+ const WidgetTouchEvent& aEvent, const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId, const nsEventStatus& aApzResponse);
+
+ mozilla::ipc::IPCResult RecvRealTouchMoveEvent2(
+ const WidgetTouchEvent& aEvent, const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId, const nsEventStatus& aApzResponse) {
+ return RecvRealTouchMoveEvent(aEvent, aGuid, aInputBlockId, aApzResponse);
+ }
+
+ mozilla::ipc::IPCResult RecvNormalPriorityRealTouchMoveEvent2(
+ const WidgetTouchEvent& aEvent, const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId, const nsEventStatus& aApzResponse) {
+ return RecvNormalPriorityRealTouchMoveEvent(aEvent, aGuid, aInputBlockId,
+ aApzResponse);
+ }
+
+ mozilla::ipc::IPCResult RecvUpdateSHistory();
+
+ mozilla::ipc::IPCResult RecvNativeSynthesisResponse(
+ const uint64_t& aObserverId, const nsCString& aResponse);
+
+ mozilla::ipc::IPCResult RecvCompositionEvent(
+ const mozilla::WidgetCompositionEvent& aEvent);
+
+ mozilla::ipc::IPCResult RecvNormalPriorityCompositionEvent(
+ const mozilla::WidgetCompositionEvent& aEvent);
+
+ mozilla::ipc::IPCResult RecvSelectionEvent(
+ const mozilla::WidgetSelectionEvent& aEvent);
+
+ mozilla::ipc::IPCResult RecvNormalPrioritySelectionEvent(
+ const mozilla::WidgetSelectionEvent& aEvent);
+
+ mozilla::ipc::IPCResult RecvInsertText(const nsAString& aStringToInsert);
+
+ mozilla::ipc::IPCResult RecvUpdateRemoteStyle(
+ const StyleImageRendering& aImageRendering);
+
+ mozilla::ipc::IPCResult RecvNormalPriorityInsertText(
+ const nsAString& aStringToInsert);
+
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ mozilla::ipc::IPCResult RecvPasteTransferable(
+ const IPCTransferable& aTransferable);
+
+ mozilla::ipc::IPCResult RecvLoadRemoteScript(const nsAString& aURL,
+ const bool& aRunInGlobalScope);
+
+ mozilla::ipc::IPCResult RecvAsyncMessage(const nsAString& aMessage,
+ const ClonedMessageData& aData);
+ mozilla::ipc::IPCResult RecvSwappedWithOtherRemoteLoader(
+ const IPCTabContext& aContext);
+
+ mozilla::ipc::IPCResult RecvSafeAreaInsetsChanged(
+ const mozilla::ScreenIntMargin& aSafeAreaInsets);
+
+#ifdef ACCESSIBILITY
+ PDocAccessibleChild* AllocPDocAccessibleChild(
+ PDocAccessibleChild*, const uint64_t&,
+ const MaybeDiscardedBrowsingContext&);
+ bool DeallocPDocAccessibleChild(PDocAccessibleChild*);
+#endif
+
+ RefPtr<VsyncMainChild> GetVsyncChild();
+
+ nsIWebNavigation* WebNavigation() const { return mWebNav; }
+
+ PuppetWidget* WebWidget() { return mPuppetWidget; }
+
+ bool IsTransparent() const { return mIsTransparent; }
+
+ const EffectsInfo& GetEffectsInfo() const { return mEffectsInfo; }
+
+ void SetBackgroundColor(const nscolor& aColor);
+
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY virtual mozilla::ipc::IPCResult RecvUpdateEffects(
+ const EffectsInfo& aEffects);
+
+ void RequestEditCommands(NativeKeyBindingsType aType,
+ const WidgetKeyboardEvent& aEvent,
+ nsTArray<CommandInt>& aCommands);
+
+ bool IsVisible();
+ bool IsPreservingLayers() const { return mIsPreservingLayers; }
+
+ /**
+ * Signal to this BrowserChild that it should be made visible:
+ * activated widget, retained layer tree, etc. (Respectively,
+ * made not visible.)
+ */
+ void UpdateVisibility();
+ void MakeVisible();
+ void MakeHidden();
+ void PresShellActivenessMaybeChanged();
+
+ ContentChild* Manager() const { return mManager; }
+
+ static inline BrowserChild* GetFrom(nsIDocShell* aDocShell) {
+ if (!aDocShell) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIBrowserChild> tc = aDocShell->GetBrowserChild();
+ return static_cast<BrowserChild*>(tc.get());
+ }
+
+ static inline BrowserChild* GetFrom(mozIDOMWindow* aWindow) {
+ nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(aWindow);
+ nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(webNav);
+ return GetFrom(docShell);
+ }
+
+ static inline BrowserChild* GetFrom(mozIDOMWindowProxy* aWindow) {
+ nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(aWindow);
+ nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(webNav);
+ return GetFrom(docShell);
+ }
+
+ static BrowserChild* GetFrom(PresShell* aPresShell);
+ static BrowserChild* GetFrom(layers::LayersId aLayersId);
+
+ layers::LayersId GetLayersId() { return mLayersId; }
+ Maybe<bool> IsLayersConnected() { return mLayersConnected; }
+
+ void DidComposite(mozilla::layers::TransactionId aTransactionId,
+ const TimeStamp& aCompositeStart,
+ const TimeStamp& aCompositeEnd);
+
+ void ClearCachedResources();
+ void SchedulePaint();
+ void ReinitRendering();
+ void ReinitRenderingForDeviceReset();
+
+ void NotifyJankedAnimations(const nsTArray<uint64_t>& aJankedAnimations);
+
+ static inline BrowserChild* GetFrom(nsIDOMWindow* aWindow) {
+ nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(aWindow);
+ nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(webNav);
+ return GetFrom(docShell);
+ }
+
+ mozilla::ipc::IPCResult RecvUIResolutionChanged(const float& aDpi,
+ const int32_t& aRounding,
+ const double& aScale);
+
+ mozilla::ipc::IPCResult RecvHandleAccessKey(const WidgetKeyboardEvent& aEvent,
+ nsTArray<uint32_t>&& aCharCodes);
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ mozilla::ipc::IPCResult RecvPrintPreview(const PrintData& aPrintData,
+ const MaybeDiscardedBrowsingContext&,
+ PrintPreviewResolver&& aCallback);
+
+ mozilla::ipc::IPCResult RecvExitPrintPreview();
+
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY mozilla::ipc::IPCResult RecvPrint(
+ const MaybeDiscardedBrowsingContext&, const PrintData&);
+
+ mozilla::ipc::IPCResult RecvUpdateNativeWindowHandle(
+ const uintptr_t& aNewHandle);
+
+ mozilla::ipc::IPCResult RecvWillChangeProcess();
+
+ PPaymentRequestChild* AllocPPaymentRequestChild();
+
+ bool DeallocPPaymentRequestChild(PPaymentRequestChild* aActor);
+
+ LayoutDeviceIntPoint GetClientOffset() const { return mClientOffset; }
+ LayoutDeviceIntPoint GetChromeOffset() const { return mChromeOffset; };
+ ScreenIntCoord GetDynamicToolbarMaxHeight() const {
+ return mDynamicToolbarMaxHeight;
+ };
+
+ bool IPCOpen() const { return mIPCOpen; }
+
+ const mozilla::layers::CompositorOptions& GetCompositorOptions() const;
+ bool AsyncPanZoomEnabled() const;
+
+ ScreenIntSize GetInnerSize();
+ CSSSize GetUnscaledInnerSize() { return mUnscaledInnerSize; }
+
+ Maybe<nsRect> GetVisibleRect() const;
+
+ // Call RecvShow(nsIntSize(0, 0)) and block future calls to RecvShow().
+ void DoFakeShow(const ParentShowInfo&);
+
+ void ContentReceivedInputBlock(uint64_t aInputBlockId,
+ bool aPreventDefault) const;
+ void SetTargetAPZC(
+ uint64_t aInputBlockId,
+ const nsTArray<layers::ScrollableLayerGuid>& aTargets) const;
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ mozilla::ipc::IPCResult RecvHandleTap(
+ const layers::GeckoContentController_TapType& aType,
+ const LayoutDevicePoint& aPoint, const Modifiers& aModifiers,
+ const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId,
+ const Maybe<DoubleTapToZoomMetrics>& aDoubleTapToZoomMetrics);
+
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ mozilla::ipc::IPCResult RecvNormalPriorityHandleTap(
+ const layers::GeckoContentController_TapType& aType,
+ const LayoutDevicePoint& aPoint, const Modifiers& aModifiers,
+ const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId,
+ const Maybe<DoubleTapToZoomMetrics>& aDoubleTapToZoomMetrics);
+
+ bool UpdateFrame(const layers::RepaintRequest& aRequest);
+ void NotifyAPZStateChange(
+ const ViewID& aViewId,
+ const layers::GeckoContentController_APZStateChange& aChange,
+ const int& aArg, Maybe<uint64_t> aInputBlockId);
+ void StartScrollbarDrag(const layers::AsyncDragMetrics& aDragMetrics);
+ void ZoomToRect(const uint32_t& aPresShellId,
+ const ScrollableLayerGuid::ViewID& aViewId,
+ const CSSRect& aRect, const uint32_t& aFlags);
+
+ // Request that the docshell be marked as active.
+ void PaintWhileInterruptingJS();
+
+ void UnloadLayersWhileInterruptingJS();
+
+ nsresult CanCancelContentJS(nsIRemoteTab::NavigationType aNavigationType,
+ int32_t aNavigationIndex, nsIURI* aNavigationURI,
+ int32_t aEpoch, bool* aCanCancel);
+
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+ uintptr_t GetNativeWindowHandle() const { return mNativeWindowHandle; }
+#endif
+
+ BrowsingContext* GetBrowsingContext() const { return mBrowsingContext; }
+
+ // The transform from the coordinate space of this BrowserChild to the
+ // coordinate space of the native window its BrowserParent is in.
+ mozilla::LayoutDeviceToLayoutDeviceMatrix4x4
+ GetChildToParentConversionMatrix() const;
+
+ // Returns the portion of the visible rect of this remote document in the
+ // top browser window coordinate system. This is the result of being
+ // clipped by all ancestor viewports.
+ Maybe<ScreenRect> GetTopLevelViewportVisibleRectInBrowserCoords() const;
+
+ // Similar to above GetTopLevelViewportVisibleRectInBrowserCoords(), but
+ // in this out-of-process document's coordinate system.
+ Maybe<LayoutDeviceRect> GetTopLevelViewportVisibleRectInSelfCoords() const;
+
+ // Prepare to dispatch all coalesced mousemove events. We'll move all data
+ // in mCoalescedMouseData to a nsDeque; then we start processing them. We
+ // can't fetch the coalesced event one by one and dispatch it because we
+ // may reentry the event loop and access to the same hashtable. It's
+ // called when dispatching some mouse events other than mousemove.
+ void FlushAllCoalescedMouseData();
+ void ProcessPendingCoalescedMouseDataAndDispatchEvents();
+
+ void ProcessPendingCoalescedTouchData();
+
+ void HandleRealMouseButtonEvent(const WidgetMouseEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId);
+
+ void SetCancelContentJSEpoch(int32_t aEpoch) {
+ mCancelContentJSEpoch = aEpoch;
+ }
+
+ void UpdateSessionStore();
+
+ mozilla::dom::SessionStoreChild* GetSessionStoreChild() {
+ return mSessionStoreChild;
+ }
+
+#ifdef XP_WIN
+ // Check if the window this BrowserChild is associated with supports
+ // protected media (EME) or not.
+ // Returns a promise the will resolve true if the window supports
+ // protected media or false if it does not. The promise will be rejected
+ // with an ResponseRejectReason if the IPC needed to do the check fails.
+ // Callers should treat the reject case as if the window does not support
+ // protected media to ensure robust handling.
+ RefPtr<IsWindowSupportingProtectedMediaPromise>
+ DoesWindowSupportProtectedMedia();
+#endif
+
+ // Notify the content blocking event in the parent process. This sends an
+ // IPC message to the BrowserParent in the parent. The BrowserParent will
+ // find the top-level WindowGlobalParent and notify the event from it.
+ void NotifyContentBlockingEvent(
+ uint32_t aEvent, nsIChannel* aChannel, bool aBlocked,
+ const nsACString& aTrackingOrigin,
+ const nsTArray<nsCString>& aTrackingFullHashes,
+ const Maybe<
+ ContentBlockingNotifier::StorageAccessPermissionGrantedReason>&
+ aReason,
+ const Maybe<ContentBlockingNotifier::CanvasFingerprinter>&
+ aCanvasFingerprinter,
+ const Maybe<bool> aCanvasFingerprinterKnownText);
+
+ protected:
+ virtual ~BrowserChild();
+
+ mozilla::ipc::IPCResult RecvDestroy();
+
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ mozilla::ipc::IPCResult RecvRenderLayers(const bool& aEnabled);
+
+ mozilla::ipc::IPCResult RecvPreserveLayers(bool);
+
+ mozilla::ipc::IPCResult RecvNavigateByKey(const bool& aForward,
+ const bool& aForDocumentNavigation);
+
+ mozilla::ipc::IPCResult RecvSuppressDisplayport(const bool& aEnabled);
+
+ mozilla::ipc::IPCResult RecvScrollbarPreferenceChanged(ScrollbarPreference);
+
+ mozilla::ipc::IPCResult RecvStopIMEStateManagement();
+
+ mozilla::ipc::IPCResult RecvAllowScriptsToClose();
+
+ mozilla::ipc::IPCResult RecvReleaseAllPointerCapture();
+
+ private:
+ void HandleDoubleTap(const CSSPoint& aPoint, const Modifiers& aModifiers,
+ const ScrollableLayerGuid& aGuid,
+ const DoubleTapToZoomMetrics& aMetrics);
+
+ void ActorDestroy(ActorDestroyReason why) override;
+
+ bool InitBrowserChildMessageManager();
+
+ void InitRenderingState(
+ const TextureFactoryIdentifier& aTextureFactoryIdentifier,
+ const layers::LayersId& aLayersId,
+ const mozilla::layers::CompositorOptions& aCompositorOptions);
+ void InitAPZState();
+
+ void DestroyWindow();
+
+ void ApplyParentShowInfo(const ParentShowInfo&);
+
+ bool HasValidInnerSize();
+
+ ScreenIntRect GetOuterRect();
+
+ void SetUnscaledInnerSize(const CSSSize& aSize) {
+ mUnscaledInnerSize = aSize;
+ }
+
+ bool SkipRepeatedKeyEvent(const WidgetKeyboardEvent& aEvent);
+
+ void UpdateRepeatedKeyEventEndTime(const WidgetKeyboardEvent& aEvent);
+
+ void DispatchCoalescedWheelEvent();
+
+ /**
+ * Dispatch aEvent on aEvent.mWidget.
+ */
+ nsEventStatus DispatchWidgetEventViaAPZ(WidgetGUIEvent& aEvent);
+
+ void DispatchWheelEvent(const WidgetWheelEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId);
+
+ void InternalSetDocShellIsActive(bool aIsActive);
+
+ bool CreateRemoteLayerManager(
+ mozilla::layers::PCompositorBridgeChild* aCompositorChild);
+
+ nsresult PrepareRequestData(nsIRequest* aRequest, RequestData& aRequestData);
+ nsresult PrepareProgressListenerData(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest,
+ WebProgressData& aWebProgressData,
+ RequestData& aRequestData);
+
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ nsresult UpdateRemotePrintSettings(const embedding::PrintData& aPrintData);
+
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ nsresult CloneDocumentTreeIntoSelf(
+ const MaybeDiscarded<BrowsingContext>& aSourceBC,
+ const embedding::PrintData& aPrintData);
+
+ class DelayedDeleteRunnable;
+
+ RefPtr<BrowserChildMessageManager> mBrowserChildMessageManager;
+ TextureFactoryIdentifier mTextureFactoryIdentifier;
+ RefPtr<nsWebBrowser> mWebBrowser;
+ nsCOMPtr<nsIWebNavigation> mWebNav;
+ RefPtr<PuppetWidget> mPuppetWidget;
+ nsCOMPtr<nsIURI> mLastURI;
+ RefPtr<ContentChild> mManager;
+ RefPtr<BrowsingContext> mBrowsingContext;
+ RefPtr<nsBrowserStatusFilter> mStatusFilter;
+ uint32_t mChromeFlags;
+ uint32_t mMaxTouchPoints;
+ layers::LayersId mLayersId;
+ CSSRect mUnscaledOuterRect;
+ Maybe<bool> mLayersConnected;
+ Maybe<bool> mLayersConnectRequested;
+ EffectsInfo mEffectsInfo;
+
+ RefPtr<VsyncMainChild> mVsyncChild;
+
+ RefPtr<APZEventState> mAPZEventState;
+
+ // Position of client area relative to the outer window
+ LayoutDeviceIntPoint mClientOffset;
+ // Position of tab, relative to parent widget (typically the window)
+ // NOTE: This value is valuable only for the top level browser.
+ LayoutDeviceIntPoint mChromeOffset;
+ ScreenIntCoord mDynamicToolbarMaxHeight;
+ TabId mUniqueId;
+
+ bool mDidFakeShow : 1;
+ bool mTriedBrowserInit : 1;
+ bool mIgnoreKeyPressEvent : 1;
+ bool mHasValidInnerSize : 1;
+ bool mDestroyed : 1;
+
+ // Whether or not this browser is the child part of the top level PBrowser
+ // actor in a remote browser.
+ bool mIsTopLevel : 1;
+
+ bool mIsTransparent : 1;
+ bool mIPCOpen : 1;
+
+ bool mDidSetRealShowInfo : 1;
+ bool mDidLoadURLInit : 1;
+
+ bool mSkipKeyPress : 1;
+
+ bool mCoalesceMouseMoveEvents : 1;
+
+ bool mShouldSendWebProgressEventsToParent : 1;
+
+ // Whether we are rendering to the compositor or not.
+ bool mRenderLayers : 1;
+
+ // Whether we're artificially preserving layers.
+ bool mIsPreservingLayers : 1;
+
+ // Holds the compositor options for the compositor rendering this tab,
+ // once we find out which compositor that is.
+ Maybe<mozilla::layers::CompositorOptions> mCompositorOptions;
+
+ friend class ContentChild;
+
+ CSSSize mUnscaledInnerSize;
+
+ // Store the end time of the handling of the last repeated
+ // keydown/keypress event so that in case event handling takes time, some
+ // repeated events can be skipped to not flood child process.
+ mozilla::TimeStamp mRepeatedKeyEventTime;
+
+ // Similar to mRepeatedKeyEventTime, store the end time (from parent
+ // process) of handling the last repeated wheel event so that in case
+ // event handling takes time, some repeated events can be skipped to not
+ // flood child process.
+ mozilla::TimeStamp mLastWheelProcessedTimeFromParent;
+ mozilla::TimeDuration mLastWheelProcessingDuration;
+
+ // Hash table to track coalesced mousemove events for different pointers.
+ nsClassHashtable<nsUint32HashKey, CoalescedMouseData> mCoalescedMouseData;
+
+ nsDeque<CoalescedMouseData> mToBeDispatchedMouseData;
+
+ CoalescedWheelData mCoalescedWheelData;
+ CoalescedTouchData mCoalescedTouchData;
+
+ RefPtr<CoalescedMouseMoveFlusher> mCoalescedMouseEventFlusher;
+ RefPtr<CoalescedTouchMoveFlusher> mCoalescedTouchMoveEventFlusher;
+
+ RefPtr<layers::IAPZCTreeManager> mApzcTreeManager;
+ RefPtr<SessionStoreChild> mSessionStoreChild;
+
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+ // The handle associated with the native window that contains this tab
+ uintptr_t mNativeWindowHandle;
+#endif // defined(XP_WIN)
+
+ int32_t mCancelContentJSEpoch;
+
+ Maybe<LayoutDeviceToLayoutDeviceMatrix4x4> mChildToParentConversionMatrix;
+ // When mChildToParentConversionMatrix is Nothing() this value is invalid.
+ ScreenRect mTopLevelViewportVisibleRectInBrowserCoords;
+
+#ifdef XP_WIN
+ // Should only be accessed on main thread.
+ Maybe<bool> mWindowSupportsProtectedMedia;
+#endif
+
+ // If set, resolve when we receive ChildToParentMatrix.
+ RefPtr<dom::Promise> mContentTransformPromise;
+
+ DISALLOW_EVIL_CONSTRUCTORS(BrowserChild);
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_BrowserChild_h
diff --git a/dom/ipc/BrowserHost.cpp b/dom/ipc/BrowserHost.cpp
new file mode 100644
index 0000000000..489f07a612
--- /dev/null
+++ b/dom/ipc/BrowserHost.cpp
@@ -0,0 +1,287 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/BrowserHost.h"
+
+#include "mozilla/Unused.h"
+#include "mozilla/dom/CancelContentJSOptionsBinding.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/WindowGlobalParent.h"
+#include "mozilla/ProcessPriorityManager.h"
+
+#include "nsIObserverService.h"
+
+namespace mozilla::dom {
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BrowserHost)
+ NS_INTERFACE_MAP_ENTRY(nsIRemoteTab)
+ NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, RemoteBrowser)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTION_WEAK(BrowserHost, mRoot)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(BrowserHost)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(BrowserHost)
+
+BrowserHost::BrowserHost(BrowserParent* aParent)
+ : mId(aParent->GetTabId()),
+ mRoot(aParent),
+ mEffectsInfo{EffectsInfo::FullyHidden()} {
+ mRoot->SetBrowserHost(this);
+}
+
+BrowserHost* BrowserHost::GetFrom(nsIRemoteTab* aRemoteTab) {
+ return static_cast<BrowserHost*>(aRemoteTab);
+}
+
+TabId BrowserHost::GetTabId() const { return mId; }
+
+mozilla::layers::LayersId BrowserHost::GetLayersId() const {
+ return mRoot->GetLayersId();
+}
+
+BrowsingContext* BrowserHost::GetBrowsingContext() const {
+ return mRoot->GetBrowsingContext();
+}
+
+nsILoadContext* BrowserHost::GetLoadContext() const {
+ RefPtr<nsILoadContext> loadContext = mRoot->GetLoadContext();
+ return loadContext;
+}
+
+bool BrowserHost::CanRecv() const { return mRoot && mRoot->CanRecv(); }
+
+a11y::DocAccessibleParent* BrowserHost::GetTopLevelDocAccessible() const {
+ return mRoot ? mRoot->GetTopLevelDocAccessible() : nullptr;
+}
+
+void BrowserHost::LoadURL(nsDocShellLoadState* aLoadState) {
+ MOZ_ASSERT(aLoadState);
+ mRoot->LoadURL(aLoadState);
+}
+
+void BrowserHost::ResumeLoad(uint64_t aPendingSwitchId) {
+ mRoot->ResumeLoad(aPendingSwitchId);
+}
+
+void BrowserHost::DestroyStart() { mRoot->Destroy(); }
+
+void BrowserHost::DestroyComplete() {
+ if (!mRoot) {
+ return;
+ }
+ mRoot->SetOwnerElement(nullptr);
+ mRoot->Destroy();
+ mRoot->SetBrowserHost(nullptr);
+ mRoot = nullptr;
+
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ if (os) {
+ os->NotifyObservers(NS_ISUPPORTS_CAST(nsIRemoteTab*, this),
+ "ipc:browser-destroyed", nullptr);
+ }
+}
+
+bool BrowserHost::Show(const OwnerShowInfo& aShowInfo) {
+ return mRoot->Show(aShowInfo);
+}
+
+void BrowserHost::UpdateDimensions(const nsIntRect& aRect,
+ const ScreenIntSize& aSize) {
+ mRoot->UpdateDimensions(aRect, aSize);
+}
+
+void BrowserHost::UpdateEffects(EffectsInfo aEffects) {
+ if (!mRoot || mEffectsInfo == aEffects) {
+ return;
+ }
+ mEffectsInfo = aEffects;
+ Unused << mRoot->SendUpdateEffects(mEffectsInfo);
+}
+
+/* attribute boolean renderLayers; */
+NS_IMETHODIMP
+BrowserHost::GetRenderLayers(bool* aRenderLayers) {
+ if (!mRoot) {
+ *aRenderLayers = false;
+ return NS_OK;
+ }
+ *aRenderLayers = mRoot->GetRenderLayers();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BrowserHost::SetRenderLayers(bool aRenderLayers) {
+ if (!mRoot) {
+ return NS_OK;
+ }
+
+ mRoot->SetRenderLayers(aRenderLayers);
+ return NS_OK;
+}
+
+/* readonly attribute boolean hasLayers; */
+NS_IMETHODIMP
+BrowserHost::GetHasLayers(bool* aHasLayers) {
+ *aHasLayers = mRoot && mRoot->GetHasLayers();
+ return NS_OK;
+}
+
+/* attribute boolean priorityHint; */
+NS_IMETHODIMP
+BrowserHost::SetPriorityHint(bool aPriorityHint) {
+ if (!mRoot) {
+ return NS_OK;
+ }
+ mRoot->SetPriorityHint(aPriorityHint);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BrowserHost::GetPriorityHint(bool* aPriorityHint) {
+ *aPriorityHint = mRoot && mRoot->GetPriorityHint();
+ return NS_OK;
+}
+
+/* void resolutionChanged (); */
+NS_IMETHODIMP
+BrowserHost::NotifyResolutionChanged() {
+ if (!mRoot) {
+ return NS_OK;
+ }
+ VisitAll([](BrowserParent* aBrowserParent) {
+ aBrowserParent->NotifyResolutionChanged();
+ });
+ return NS_OK;
+}
+
+/* void deprioritize (); */
+NS_IMETHODIMP
+BrowserHost::Deprioritize() {
+ if (!mRoot) {
+ return NS_OK;
+ }
+ auto* bc = GetBrowsingContext()->Canonical();
+ ProcessPriorityManager::BrowserPriorityChanged(bc,
+ /* aPriority = */ false);
+ return NS_OK;
+}
+
+/* void preserveLayers (in boolean aPreserveLayers); */
+NS_IMETHODIMP
+BrowserHost::PreserveLayers(bool aPreserveLayers) {
+ if (!mRoot) {
+ return NS_OK;
+ }
+ VisitAll([&](BrowserParent* aBrowserParent) {
+ aBrowserParent->PreserveLayers(aPreserveLayers);
+ });
+ return NS_OK;
+}
+
+/* readonly attribute uint64_t tabId; */
+NS_IMETHODIMP
+BrowserHost::GetTabId(uint64_t* aTabId) {
+ *aTabId = mId;
+ return NS_OK;
+}
+
+/* readonly attribute uint64_t contentProcessId; */
+NS_IMETHODIMP
+BrowserHost::GetContentProcessId(uint64_t* aContentProcessId) {
+ if (!mRoot) {
+ *aContentProcessId = 0;
+ return NS_OK;
+ }
+ *aContentProcessId = GetContentParent()->ChildID();
+ return NS_OK;
+}
+
+/* readonly attribute int32_t osPid; */
+NS_IMETHODIMP
+BrowserHost::GetOsPid(int32_t* aOsPid) {
+ if (!mRoot) {
+ *aOsPid = 0;
+ return NS_OK;
+ }
+ *aOsPid = GetContentParent()->Pid();
+ return NS_OK;
+}
+
+/* readonly attribute BrowsingContext browsingContext; */
+NS_IMETHODIMP
+BrowserHost::GetBrowsingContext(BrowsingContext** aBc) {
+ if (!mRoot) {
+ *aBc = nullptr;
+ return NS_OK;
+ }
+ RefPtr<BrowsingContext> bc = mRoot->GetBrowsingContext();
+ bc.forget(aBc);
+ return NS_OK;
+}
+
+/* readonly attribute boolean hasPresented; */
+NS_IMETHODIMP
+BrowserHost::GetHasPresented(bool* aHasPresented) {
+ if (!mRoot) {
+ *aHasPresented = false;
+ return NS_OK;
+ }
+ *aHasPresented = mRoot->GetHasPresented();
+ return NS_OK;
+}
+
+/* void transmitPermissionsForPrincipal (in nsIPrincipal aPrincipal); */
+NS_IMETHODIMP
+BrowserHost::TransmitPermissionsForPrincipal(nsIPrincipal* aPrincipal) {
+ if (!mRoot) {
+ return NS_OK;
+ }
+ return GetContentParent()->TransmitPermissionsForPrincipal(aPrincipal);
+}
+
+/* void createAboutBlankDocumentViewer(in nsIPrincipal aPrincipal, in
+ * nsIPrincipal aPartitionedPrincipal); */
+NS_IMETHODIMP
+BrowserHost::CreateAboutBlankDocumentViewer(
+ nsIPrincipal* aPrincipal, nsIPrincipal* aPartitionedPrincipal) {
+ if (!mRoot) {
+ return NS_OK;
+ }
+
+ // Ensure the content process has permisisons for the new document we're about
+ // to create in it.
+ nsresult rv = GetContentParent()->TransmitPermissionsForPrincipal(aPrincipal);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ Unused << mRoot->SendCreateAboutBlankDocumentViewer(aPrincipal,
+ aPartitionedPrincipal);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BrowserHost::MaybeCancelContentJSExecutionFromScript(
+ nsIRemoteTab::NavigationType aNavigationType,
+ JS::Handle<JS::Value> aCancelContentJSOptions, JSContext* aCx) {
+ // If we're in the process of creating a new window (via window.open), then
+ // the load that called this function isn't a "normal" load and should be
+ // ignored for the purposes of cancelling content JS.
+ if (!mRoot || mRoot->CreatingWindow()) {
+ return NS_OK;
+ }
+ dom::CancelContentJSOptions cancelContentJSOptions;
+ if (!cancelContentJSOptions.Init(aCx, aCancelContentJSOptions)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+ GetContentParent()->CancelContentJSExecutionIfRunning(mRoot, aNavigationType,
+ cancelContentJSOptions);
+ return NS_OK;
+}
+
+} // namespace mozilla::dom
diff --git a/dom/ipc/BrowserHost.h b/dom/ipc/BrowserHost.h
new file mode 100644
index 0000000000..5b651a1df0
--- /dev/null
+++ b/dom/ipc/BrowserHost.h
@@ -0,0 +1,109 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_BrowserHost_h
+#define mozilla_dom_BrowserHost_h
+
+#include "nsIRemoteTab.h"
+#include "mozilla/dom/RemoteBrowser.h"
+#include "mozilla/dom/BrowserParent.h"
+
+class nsPIDOMWindowOuter;
+
+namespace mozilla {
+
+namespace a11y {
+class DocAccessibleParent;
+} // namespace a11y
+
+namespace dom {
+
+class Element;
+
+/**
+ * BrowserHost manages a remote browser from the chrome process.
+ *
+ * It is used via the RemoteBrowser interface in nsFrameLoader and supports
+ * operations over the tree of BrowserParent/BrowserBridgeParent's.
+ *
+ * See `dom/docs/Fission-IPC-Diagram.svg` for an overview of the DOM IPC
+ * actors.
+ */
+class BrowserHost : public RemoteBrowser,
+ public nsIRemoteTab,
+ public nsSupportsWeakReference {
+ public:
+ typedef mozilla::layers::LayersId LayersId;
+
+ explicit BrowserHost(BrowserParent* aParent);
+
+ static BrowserHost* GetFrom(nsIRemoteTab* aRemoteTab);
+
+ // NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ // nsIRemoteTab
+ NS_DECL_NSIREMOTETAB
+
+ NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(BrowserHost, RemoteBrowser)
+
+ // Get the IPDL actor for the root BrowserParent. This method should be
+ // avoided and consumers migrated to use this class.
+ BrowserParent* GetActor() { return mRoot; }
+ ContentParent* GetContentParent() const {
+ return mRoot ? mRoot->Manager() : nullptr;
+ }
+
+ BrowserHost* AsBrowserHost() override { return this; }
+ BrowserBridgeHost* AsBrowserBridgeHost() override { return nullptr; }
+
+ TabId GetTabId() const override;
+ LayersId GetLayersId() const override;
+ BrowsingContext* GetBrowsingContext() const override;
+ nsILoadContext* GetLoadContext() const override;
+ bool CanRecv() const override;
+
+ Element* GetOwnerElement() const { return mRoot->GetOwnerElement(); }
+ already_AddRefed<nsPIDOMWindowOuter> GetParentWindowOuter() const {
+ return mRoot->GetParentWindowOuter();
+ }
+ a11y::DocAccessibleParent* GetTopLevelDocAccessible() const;
+
+ // Visit each BrowserParent in the tree formed by PBrowser and
+ // PBrowserBridge that is anchored by `mRoot`.
+ template <typename Callback>
+ void VisitAll(Callback aCallback) {
+ if (!mRoot) {
+ return;
+ }
+ mRoot->VisitAll(aCallback);
+ }
+
+ void LoadURL(nsDocShellLoadState* aLoadState) override;
+ void ResumeLoad(uint64_t aPendingSwitchId) override;
+ void DestroyStart() override;
+ void DestroyComplete() override;
+
+ bool Show(const OwnerShowInfo&) override;
+ void UpdateDimensions(const nsIntRect& aRect,
+ const ScreenIntSize& aSize) override;
+
+ void UpdateEffects(EffectsInfo aInfo) override;
+
+ private:
+ virtual ~BrowserHost() = default;
+
+ // The TabID for the root BrowserParent, we cache this so that we can access
+ // it after the remote browser has been destroyed
+ TabId mId;
+ // The root BrowserParent of this remote browser
+ RefPtr<BrowserParent> mRoot;
+ EffectsInfo mEffectsInfo;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_BrowserHost_h
diff --git a/dom/ipc/BrowserParent.cpp b/dom/ipc/BrowserParent.cpp
new file mode 100644
index 0000000000..d369556588
--- /dev/null
+++ b/dom/ipc/BrowserParent.cpp
@@ -0,0 +1,4118 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "base/basictypes.h"
+
+#include "BrowserParent.h"
+#include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/EventForwards.h"
+
+#ifdef ACCESSIBILITY
+# include "mozilla/a11y/DocAccessibleParent.h"
+# include "mozilla/a11y/Platform.h"
+# include "nsAccessibilityService.h"
+#endif
+#include "mozilla/Components.h"
+#include "mozilla/dom/BrowserHost.h"
+#include "mozilla/dom/BrowserSessionStore.h"
+#include "mozilla/dom/BrowsingContextGroup.h"
+#include "mozilla/dom/CancelContentJSOptionsBinding.h"
+#include "mozilla/dom/ChromeMessageSender.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/ContentProcessManager.h"
+#include "mozilla/dom/DataTransfer.h"
+#include "mozilla/dom/DataTransferItemList.h"
+#include "mozilla/dom/DocumentInlines.h"
+#include "mozilla/dom/Event.h"
+#include "mozilla/dom/indexedDB/ActorsParent.h"
+#include "mozilla/dom/PaymentRequestParent.h"
+#include "mozilla/dom/PointerEventHandler.h"
+#include "mozilla/dom/BrowserBridgeParent.h"
+#include "mozilla/dom/RemoteDragStartData.h"
+#include "mozilla/dom/RemoteWebProgressRequest.h"
+#include "mozilla/dom/SessionHistoryEntry.h"
+#include "mozilla/dom/SessionStoreParent.h"
+#include "mozilla/dom/UserActivation.h"
+#include "mozilla/EventStateManager.h"
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/DataSurfaceHelpers.h"
+#include "mozilla/gfx/GPUProcessManager.h"
+#include "mozilla/IMEStateManager.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/layers/AsyncDragMetrics.h"
+#include "mozilla/layers/InputAPZContext.h"
+#include "mozilla/layout/RemoteLayerTreeOwner.h"
+#include "mozilla/LookAndFeel.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/MiscEvents.h"
+#include "mozilla/MouseEvents.h"
+#include "mozilla/NativeKeyBindingsType.h"
+#include "mozilla/net/NeckoChild.h"
+#include "mozilla/net/CookieJarSettings.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/PresShell.h"
+#include "mozilla/ProcessHangMonitor.h"
+#include "mozilla/RecursiveMutex.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/StaticPrefs_dom.h"
+#include "mozilla/TextEventDispatcher.h"
+#include "mozilla/TextEvents.h"
+#include "mozilla/TouchEvents.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Unused.h"
+#include "nsCOMPtr.h"
+#include "nsContentUtils.h"
+#include "nsDebug.h"
+#include "nsFocusManager.h"
+#include "nsFrameLoader.h"
+#include "nsFrameLoaderOwner.h"
+#include "nsFrameManager.h"
+#include "nsIBaseWindow.h"
+#include "nsIBrowser.h"
+#include "nsIBrowserController.h"
+#include "nsIContent.h"
+#include "nsICookieJarSettings.h"
+#include "nsIDocShell.h"
+#include "nsIDocShellTreeOwner.h"
+#include "nsImportModule.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsILoadInfo.h"
+#include "nsIPromptFactory.h"
+#include "nsIURI.h"
+#include "nsIWebBrowserChrome.h"
+#include "nsIWebProtocolHandlerRegistrar.h"
+#include "nsIWindowWatcher.h"
+#include "nsIXPConnect.h"
+#include "nsIXULBrowserWindow.h"
+#include "nsIAppWindow.h"
+#include "nsLayoutUtils.h"
+#include "nsQueryActor.h"
+#include "nsSHistory.h"
+#include "nsViewManager.h"
+#include "nsVariant.h"
+#include "nsIWidget.h"
+#include "nsNetUtil.h"
+#ifndef XP_WIN
+# include "nsJARProtocolHandler.h"
+#endif
+#include "nsPIDOMWindow.h"
+#include "nsPrintfCString.h"
+#include "nsQueryObject.h"
+#include "nsServiceManagerUtils.h"
+#include "nsThreadUtils.h"
+#include "PermissionMessageUtils.h"
+#include "StructuredCloneData.h"
+#include "ColorPickerParent.h"
+#include "FilePickerParent.h"
+#include "BrowserChild.h"
+#include "nsNetCID.h"
+#include "nsIAuthInformation.h"
+#include "nsIAuthPromptCallback.h"
+#include "nsAuthInformationHolder.h"
+#include "nsICancelable.h"
+#include "gfxUtils.h"
+#include "nsILoginManagerAuthPrompter.h"
+#include "nsPIWindowRoot.h"
+#include "nsReadableUtils.h"
+#include "nsIAuthPrompt2.h"
+#include "gfxDrawable.h"
+#include "ImageOps.h"
+#include "UnitTransforms.h"
+#include <algorithm>
+#include "mozilla/NullPrincipal.h"
+#include "mozilla/WebBrowserPersistDocumentParent.h"
+#include "ProcessPriorityManager.h"
+#include "nsString.h"
+#include "IHistory.h"
+#include "mozilla/dom/WindowGlobalParent.h"
+#include "mozilla/dom/CanonicalBrowsingContext.h"
+#include "mozilla/ProfilerLabels.h"
+#include "MMPrinter.h"
+#include "mozilla/dom/CrashReport.h"
+#include "nsISecureBrowserUI.h"
+#include "nsIXULRuntime.h"
+#include "VsyncSource.h"
+#include "nsSubDocumentFrame.h"
+
+#ifdef XP_WIN
+# include "FxRWindowManager.h"
+#endif
+
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+# include "mozilla/a11y/AccessibleWrap.h"
+# include "mozilla/a11y/Compatibility.h"
+# include "mozilla/a11y/nsWinUtils.h"
+#endif
+
+#ifdef MOZ_ANDROID_HISTORY
+# include "GeckoViewHistory.h"
+#endif
+
+#if defined(MOZ_WIDGET_ANDROID)
+# include "mozilla/widget/nsWindow.h"
+#endif // defined(MOZ_WIDGET_ANDROID)
+
+using namespace mozilla::dom;
+using namespace mozilla::ipc;
+using namespace mozilla::layers;
+using namespace mozilla::layout;
+using namespace mozilla::services;
+using namespace mozilla::widget;
+using namespace mozilla::gfx;
+
+using mozilla::LazyLogModule;
+
+extern mozilla::LazyLogModule gSHIPBFCacheLog;
+
+LazyLogModule gBrowserFocusLog("BrowserFocus");
+
+#define LOGBROWSERFOCUS(args) \
+ MOZ_LOG(gBrowserFocusLog, mozilla::LogLevel::Debug, args)
+
+/* static */
+BrowserParent* BrowserParent::sFocus = nullptr;
+/* static */
+BrowserParent* BrowserParent::sTopLevelWebFocus = nullptr;
+/* static */
+BrowserParent* BrowserParent::sLastMouseRemoteTarget = nullptr;
+
+// The flags passed by the webProgress notifications are 16 bits shifted
+// from the ones registered by webProgressListeners.
+#define NOTIFY_FLAG_SHIFT 16
+
+namespace mozilla {
+
+/**
+ * Store data of a keypress event which is requesting to handled it in a remote
+ * process or some remote processes.
+ */
+class RequestingAccessKeyEventData {
+ public:
+ RequestingAccessKeyEventData() = delete;
+
+ static void OnBrowserParentCreated() {
+ MOZ_ASSERT(sBrowserParentCount <= INT32_MAX);
+ sBrowserParentCount++;
+ }
+ static void OnBrowserParentDestroyed() {
+ MOZ_ASSERT(sBrowserParentCount > 0);
+ sBrowserParentCount--;
+ // To avoid memory leak, we need to reset sData when the last BrowserParent
+ // is destroyed.
+ if (!sBrowserParentCount) {
+ Clear();
+ }
+ }
+
+ static void Set(const WidgetKeyboardEvent& aKeyPressEvent) {
+ MOZ_ASSERT(aKeyPressEvent.mMessage == eKeyPress);
+ MOZ_ASSERT(sBrowserParentCount > 0);
+ sData =
+ Some(Data{aKeyPressEvent.mAlternativeCharCodes, aKeyPressEvent.mKeyCode,
+ aKeyPressEvent.mCharCode, aKeyPressEvent.mKeyNameIndex,
+ aKeyPressEvent.mCodeNameIndex, aKeyPressEvent.mKeyValue,
+ aKeyPressEvent.mModifiers});
+ }
+
+ static void Clear() { sData.reset(); }
+
+ [[nodiscard]] static bool Equals(const WidgetKeyboardEvent& aKeyPressEvent) {
+ MOZ_ASSERT(sBrowserParentCount > 0);
+ return sData.isSome() && sData->Equals(aKeyPressEvent);
+ }
+
+ [[nodiscard]] static bool IsSet() {
+ MOZ_ASSERT(sBrowserParentCount > 0);
+ return sData.isSome();
+ }
+
+ private:
+ struct Data {
+ [[nodiscard]] bool Equals(const WidgetKeyboardEvent& aKeyPressEvent) {
+ return mKeyCode == aKeyPressEvent.mKeyCode &&
+ mCharCode == aKeyPressEvent.mCharCode &&
+ mKeyNameIndex == aKeyPressEvent.mKeyNameIndex &&
+ mCodeNameIndex == aKeyPressEvent.mCodeNameIndex &&
+ mKeyValue == aKeyPressEvent.mKeyValue &&
+ mModifiers == aKeyPressEvent.mModifiers &&
+ mAlternativeCharCodes == aKeyPressEvent.mAlternativeCharCodes;
+ }
+
+ CopyableTArray<AlternativeCharCode> mAlternativeCharCodes;
+ uint32_t mKeyCode;
+ uint32_t mCharCode;
+ KeyNameIndex mKeyNameIndex;
+ CodeNameIndex mCodeNameIndex;
+ nsString mKeyValue;
+ Modifiers mModifiers;
+ };
+ static Maybe<Data> sData;
+ static int32_t sBrowserParentCount;
+};
+int32_t RequestingAccessKeyEventData::sBrowserParentCount = 0;
+Maybe<RequestingAccessKeyEventData::Data> RequestingAccessKeyEventData::sData;
+
+namespace dom {
+
+BrowserParent::LayerToBrowserParentTable*
+ BrowserParent::sLayerToBrowserParentTable = nullptr;
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BrowserParent)
+ NS_INTERFACE_MAP_ENTRY(nsIAuthPromptProvider)
+ NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMEventListener)
+NS_INTERFACE_MAP_END
+NS_IMPL_CYCLE_COLLECTION_WEAK(BrowserParent, mFrameLoader, mBrowsingContext)
+NS_IMPL_CYCLE_COLLECTING_ADDREF(BrowserParent)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(BrowserParent)
+
+BrowserParent::BrowserParent(ContentParent* aManager, const TabId& aTabId,
+ const TabContext& aContext,
+ CanonicalBrowsingContext* aBrowsingContext,
+ uint32_t aChromeFlags)
+ : TabContext(aContext),
+ mTabId(aTabId),
+ mManager(aManager),
+ mBrowsingContext(aBrowsingContext),
+ mFrameElement(nullptr),
+ mBrowserDOMWindow(nullptr),
+ mFrameLoader(nullptr),
+ mChromeFlags(aChromeFlags),
+ mBrowserBridgeParent(nullptr),
+ mBrowserHost(nullptr),
+ mContentCache(*this),
+ mRect(0, 0, 0, 0),
+ mDimensions(0, 0),
+ mDPI(0),
+ mRounding(0),
+ mDefaultScale(0),
+ mUpdatedDimensions(false),
+ mSizeMode(nsSizeMode_Normal),
+ mCreatingWindow(false),
+ mVsyncParent(nullptr),
+ mMarkedDestroying(false),
+ mIsDestroyed(false),
+ mRemoteTargetSetsCursor(false),
+ mIsPreservingLayers(false),
+ mRenderLayers(true),
+ mPriorityHint(false),
+ mHasLayers(false),
+ mHasPresented(false),
+ mIsReadyToHandleInputEvents(false),
+ mIsMouseEnterIntoWidgetEventSuppressed(false),
+ mLockedNativePointer(false),
+ mShowingTooltip(false) {
+ MOZ_ASSERT(aManager);
+
+ RequestingAccessKeyEventData::OnBrowserParentCreated();
+
+ // When the input event queue is disabled, we don't need to handle the case
+ // that some input events are dispatched before PBrowserConstructor.
+ mIsReadyToHandleInputEvents = !ContentParent::IsInputEventQueueSupported();
+
+ // Make sure to compute our process priority if needed before the block of
+ // code below. This makes sure the block below prioritizes our process if
+ // needed.
+ if (aBrowsingContext->IsTop()) {
+ RecomputeProcessPriority();
+ }
+
+ // If we're in a BC tree that is active with respect to the priority manager,
+ // ensure that this new BrowserParent is marked as active. This ensures that
+ // the process will be prioritized in a cross-site iframe navigation in an
+ // active tab, and also that the process is correctly prioritized if we got
+ // created for a browsing context which was already active.
+ if (aBrowsingContext->Top()->IsPriorityActive()) {
+ ProcessPriorityManager::BrowserPriorityChanged(this, true);
+ }
+}
+
+BrowserParent::~BrowserParent() {
+ RequestingAccessKeyEventData::OnBrowserParentDestroyed();
+}
+
+/* static */
+BrowserParent* BrowserParent::GetFocused() { return sFocus; }
+
+/* static */
+BrowserParent* BrowserParent::GetLastMouseRemoteTarget() {
+ return sLastMouseRemoteTarget;
+}
+
+/*static*/
+BrowserParent* BrowserParent::GetFrom(nsFrameLoader* aFrameLoader) {
+ if (!aFrameLoader) {
+ return nullptr;
+ }
+ return aFrameLoader->GetBrowserParent();
+}
+
+/*static*/
+BrowserParent* BrowserParent::GetFrom(PBrowserParent* aBrowserParent) {
+ return static_cast<BrowserParent*>(aBrowserParent);
+}
+
+/*static*/
+BrowserParent* BrowserParent::GetFrom(nsIContent* aContent) {
+ RefPtr<nsFrameLoaderOwner> loaderOwner = do_QueryObject(aContent);
+ if (!loaderOwner) {
+ return nullptr;
+ }
+ RefPtr<nsFrameLoader> frameLoader = loaderOwner->GetFrameLoader();
+ return GetFrom(frameLoader);
+}
+
+/* static */
+BrowserParent* BrowserParent::GetBrowserParentFromLayersId(
+ layers::LayersId aLayersId) {
+ if (!sLayerToBrowserParentTable) {
+ return nullptr;
+ }
+ return sLayerToBrowserParentTable->Get(uint64_t(aLayersId));
+}
+
+/*static*/
+TabId BrowserParent::GetTabIdFrom(nsIDocShell* docShell) {
+ nsCOMPtr<nsIBrowserChild> browserChild(BrowserChild::GetFrom(docShell));
+ if (browserChild) {
+ return static_cast<BrowserChild*>(browserChild.get())->GetTabId();
+ }
+ return TabId(0);
+}
+
+void BrowserParent::AddBrowserParentToTable(layers::LayersId aLayersId,
+ BrowserParent* aBrowserParent) {
+ if (!sLayerToBrowserParentTable) {
+ sLayerToBrowserParentTable = new LayerToBrowserParentTable();
+ }
+ sLayerToBrowserParentTable->InsertOrUpdate(uint64_t(aLayersId),
+ aBrowserParent);
+}
+
+void BrowserParent::RemoveBrowserParentFromTable(layers::LayersId aLayersId) {
+ if (!sLayerToBrowserParentTable) {
+ return;
+ }
+ sLayerToBrowserParentTable->Remove(uint64_t(aLayersId));
+ if (sLayerToBrowserParentTable->Count() == 0) {
+ delete sLayerToBrowserParentTable;
+ sLayerToBrowserParentTable = nullptr;
+ }
+}
+
+already_AddRefed<nsILoadContext> BrowserParent::GetLoadContext() {
+ return do_AddRef(mBrowsingContext);
+}
+
+/**
+ * Will return nullptr if there is no outer window available for the
+ * document hosting the owner element of this BrowserParent. Also will return
+ * nullptr if that outer window is in the process of closing.
+ */
+already_AddRefed<nsPIDOMWindowOuter> BrowserParent::GetParentWindowOuter() {
+ nsCOMPtr<nsIContent> frame = GetOwnerElement();
+ if (!frame) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> parent = frame->OwnerDoc()->GetWindow();
+ if (!parent || parent->Closed()) {
+ return nullptr;
+ }
+
+ return parent.forget();
+}
+
+already_AddRefed<nsIWidget> BrowserParent::GetTopLevelWidget() {
+ if (RefPtr<Element> element = mFrameElement) {
+ if (PresShell* presShell = element->OwnerDoc()->GetPresShell()) {
+ return do_AddRef(presShell->GetViewManager()->GetRootWidget());
+ }
+ }
+ return nullptr;
+}
+
+already_AddRefed<nsIWidget> BrowserParent::GetTextInputHandlingWidget() const {
+ if (!mFrameElement) {
+ return nullptr;
+ }
+ PresShell* presShell = mFrameElement->OwnerDoc()->GetPresShell();
+ if (!presShell) {
+ return nullptr;
+ }
+ nsPresContext* presContext = presShell->GetPresContext();
+ if (!presContext) {
+ return nullptr;
+ }
+ nsCOMPtr<nsIWidget> widget = presContext->GetTextInputHandlingWidget();
+ return widget.forget();
+}
+
+already_AddRefed<nsIWidget> BrowserParent::GetWidget() const {
+ if (!mFrameElement) {
+ return nullptr;
+ }
+ nsCOMPtr<nsIWidget> widget = nsContentUtils::WidgetForContent(mFrameElement);
+ if (!widget) {
+ widget = nsContentUtils::WidgetForDocument(mFrameElement->OwnerDoc());
+ }
+ return widget.forget();
+}
+
+already_AddRefed<nsIWidget> BrowserParent::GetDocWidget() const {
+ if (!mFrameElement) {
+ return nullptr;
+ }
+ return do_AddRef(
+ nsContentUtils::WidgetForDocument(mFrameElement->OwnerDoc()));
+}
+
+nsIXULBrowserWindow* BrowserParent::GetXULBrowserWindow() {
+ if (!mFrameElement) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIDocShell> docShell = mFrameElement->OwnerDoc()->GetDocShell();
+ if (!docShell) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
+ docShell->GetTreeOwner(getter_AddRefs(treeOwner));
+ if (!treeOwner) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIAppWindow> window = do_GetInterface(treeOwner);
+ if (!window) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIXULBrowserWindow> xulBrowserWindow;
+ window->GetXULBrowserWindow(getter_AddRefs(xulBrowserWindow));
+ return xulBrowserWindow;
+}
+
+uint32_t BrowserParent::GetMaxTouchPoints(Element* aElement) {
+ if (!aElement) {
+ return 0;
+ }
+
+ if (StaticPrefs::dom_maxtouchpoints_testing_value() >= 0) {
+ return StaticPrefs::dom_maxtouchpoints_testing_value();
+ }
+
+ nsIWidget* widget = nsContentUtils::WidgetForDocument(aElement->OwnerDoc());
+ return widget ? widget->GetMaxTouchPoints() : 0;
+}
+
+a11y::DocAccessibleParent* BrowserParent::GetTopLevelDocAccessible() const {
+#ifdef ACCESSIBILITY
+ // XXX Consider managing non top level PDocAccessibles with their parent
+ // document accessible.
+ const ManagedContainer<PDocAccessibleParent>& docs =
+ ManagedPDocAccessibleParent();
+ for (auto* key : docs) {
+ auto* doc = static_cast<a11y::DocAccessibleParent*>(key);
+ // We want the document for this BrowserParent even if it's for an
+ // embedded out-of-process iframe. Therefore, we use
+ // IsTopLevelInContentProcess. In contrast, using IsToplevel would only
+ // include documents that aren't embedded; e.g. tab documents.
+ if (doc->IsTopLevelInContentProcess() && !doc->IsShutdown()) {
+ return doc;
+ }
+ }
+#endif
+ return nullptr;
+}
+
+LayersId BrowserParent::GetLayersId() const {
+ if (!mRemoteLayerTreeOwner.IsInitialized()) {
+ return LayersId{};
+ }
+ return mRemoteLayerTreeOwner.GetLayersId();
+}
+
+BrowserBridgeParent* BrowserParent::GetBrowserBridgeParent() const {
+ return mBrowserBridgeParent;
+}
+
+BrowserHost* BrowserParent::GetBrowserHost() const { return mBrowserHost; }
+
+ParentShowInfo BrowserParent::GetShowInfo() {
+ TryCacheDPIAndScale();
+ if (mFrameElement) {
+ nsAutoString name;
+ mFrameElement->GetAttr(nsGkAtoms::name, name);
+ bool isTransparent =
+ nsContentUtils::IsChromeDoc(mFrameElement->OwnerDoc()) &&
+ mFrameElement->HasAttr(nsGkAtoms::transparent);
+ return ParentShowInfo(name, false, isTransparent, mDPI, mRounding,
+ mDefaultScale.scale);
+ }
+
+ return ParentShowInfo(u""_ns, false, false, mDPI, mRounding,
+ mDefaultScale.scale);
+}
+
+already_AddRefed<nsIPrincipal> BrowserParent::GetContentPrincipal() const {
+ nsCOMPtr<nsIBrowser> browser =
+ mFrameElement ? mFrameElement->AsBrowser() : nullptr;
+ NS_ENSURE_TRUE(browser, nullptr);
+
+ RefPtr<nsIPrincipal> principal;
+
+ nsresult rv;
+ rv = browser->GetContentPrincipal(getter_AddRefs(principal));
+ NS_ENSURE_SUCCESS(rv, nullptr);
+
+ return principal.forget();
+}
+
+void BrowserParent::SetOwnerElement(Element* aElement) {
+ // If we held previous content then unregister for its events.
+ RemoveWindowListeners();
+
+ // If we change top-level documents then we need to change our
+ // registration with them.
+ RefPtr<nsPIWindowRoot> curTopLevelWin, newTopLevelWin;
+ if (mFrameElement) {
+ curTopLevelWin = nsContentUtils::GetWindowRoot(mFrameElement->OwnerDoc());
+ }
+ if (aElement) {
+ newTopLevelWin = nsContentUtils::GetWindowRoot(aElement->OwnerDoc());
+ }
+ bool isSameTopLevelWin = curTopLevelWin == newTopLevelWin;
+ if (mBrowserHost && curTopLevelWin && !isSameTopLevelWin) {
+ curTopLevelWin->RemoveBrowser(mBrowserHost);
+ }
+
+ // Update to the new content, and register to listen for events from it.
+ mFrameElement = aElement;
+
+ if (mBrowserHost && newTopLevelWin && !isSameTopLevelWin) {
+ newTopLevelWin->AddBrowser(mBrowserHost);
+ }
+
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+ if (!mIsDestroyed) {
+ uintptr_t newWindowHandle = 0;
+ if (nsCOMPtr<nsIWidget> widget = GetWidget()) {
+ newWindowHandle =
+ reinterpret_cast<uintptr_t>(widget->GetNativeData(NS_NATIVE_WINDOW));
+ }
+ Unused << SendUpdateNativeWindowHandle(newWindowHandle);
+ a11y::DocAccessibleParent* doc = GetTopLevelDocAccessible();
+ if (doc) {
+ HWND hWnd = reinterpret_cast<HWND>(doc->GetEmulatedWindowHandle());
+ if (hWnd) {
+ HWND parentHwnd = reinterpret_cast<HWND>(newWindowHandle);
+ if (parentHwnd != ::GetParent(hWnd)) {
+ ::SetParent(hWnd, parentHwnd);
+ }
+ }
+ }
+ }
+#endif
+
+ AddWindowListeners();
+
+ // The DPI depends on our frame element's widget, so invalidate now in case
+ // we've tried to cache it already.
+ mDPI = -1;
+ TryCacheDPIAndScale();
+
+ if (mRemoteLayerTreeOwner.IsInitialized()) {
+ mRemoteLayerTreeOwner.OwnerContentChanged();
+ }
+
+ // Set our BrowsingContext's embedder if we're not embedded within a
+ // BrowserBridgeParent.
+ if (!GetBrowserBridgeParent() && mBrowsingContext && mFrameElement) {
+ mBrowsingContext->SetEmbedderElement(mFrameElement);
+ }
+
+ UpdateVsyncParentVsyncDispatcher();
+
+ VisitChildren([aElement](BrowserBridgeParent* aBrowser) {
+ if (auto* browserParent = aBrowser->GetBrowserParent()) {
+ browserParent->SetOwnerElement(aElement);
+ }
+ });
+}
+
+void BrowserParent::CacheFrameLoader(nsFrameLoader* aFrameLoader) {
+ mFrameLoader = aFrameLoader;
+}
+
+void BrowserParent::AddWindowListeners() {
+ if (mFrameElement) {
+ if (nsCOMPtr<nsPIDOMWindowOuter> window =
+ mFrameElement->OwnerDoc()->GetWindow()) {
+ nsCOMPtr<EventTarget> eventTarget = window->GetTopWindowRoot();
+ if (eventTarget) {
+ eventTarget->AddEventListener(u"MozUpdateWindowPos"_ns, this, false,
+ false);
+ eventTarget->AddEventListener(u"fullscreenchange"_ns, this, false,
+ false);
+ }
+ }
+ }
+}
+
+void BrowserParent::RemoveWindowListeners() {
+ if (mFrameElement && mFrameElement->OwnerDoc()->GetWindow()) {
+ nsCOMPtr<nsPIDOMWindowOuter> window =
+ mFrameElement->OwnerDoc()->GetWindow();
+ nsCOMPtr<EventTarget> eventTarget = window->GetTopWindowRoot();
+ if (eventTarget) {
+ eventTarget->RemoveEventListener(u"MozUpdateWindowPos"_ns, this, false);
+ eventTarget->RemoveEventListener(u"fullscreenchange"_ns, this, false);
+ }
+ }
+}
+
+void BrowserParent::Deactivated() {
+ if (mShowingTooltip) {
+ // Reuse the normal tooltip hiding method.
+ mozilla::Unused << RecvHideTooltip();
+ }
+ UnlockNativePointer();
+ UnsetTopLevelWebFocus(this);
+ UnsetLastMouseRemoteTarget(this);
+ PointerLockManager::ReleaseLockedRemoteTarget(this);
+ PointerEventHandler::ReleasePointerCaptureRemoteTarget(this);
+ PresShell::ReleaseCapturingRemoteTarget(this);
+ ProcessPriorityManager::BrowserPriorityChanged(this, /* aPriority = */ false);
+}
+
+void BrowserParent::Destroy() {
+ // Aggressively release the window to avoid leaking the world in shutdown
+ // corner cases.
+ mBrowserDOMWindow = nullptr;
+
+ if (mIsDestroyed) {
+ return;
+ }
+
+ Deactivated();
+
+ RemoveWindowListeners();
+
+#ifdef ACCESSIBILITY
+ if (a11y::DocAccessibleParent* tabDoc = GetTopLevelDocAccessible()) {
+# if defined(ANDROID)
+ MonitorAutoLock mal(nsAccessibilityService::GetAndroidMonitor());
+# endif
+ tabDoc->Destroy();
+ }
+#endif
+
+ {
+ // The following sequence assumes that the keepalive state does not change
+ // between the calls, but our ThreadsafeHandle might be accessed from other
+ // threads in the meantime.
+ RecursiveMutexAutoLock lock(Manager()->ThreadsafeHandleMutex());
+
+ // If we are shutting down everything or we know to be the last
+ // BrowserParent, signal the impending shutdown early to the content process
+ // to avoid to run the SendDestroy before we know we are ExpectingShutdown.
+ Manager()->NotifyTabWillDestroy();
+
+ // If this fails, it's most likely due to a content-process crash, and
+ // auto-cleanup will kick in. Otherwise, the child side will destroy itself
+ // and send back __delete__().
+ (void)SendDestroy();
+ mIsDestroyed = true;
+
+ Manager()->NotifyTabDestroying();
+ }
+
+ // This `AddKeepAlive` will be cleared if `mMarkedDestroying` is set in
+ // `ActorDestroy`. Out of caution, we don't add the `KeepAlive` if our IPC
+ // actor has somehow already been destroyed, as that would mean `ActorDestroy`
+ // won't be called.
+ if (CanRecv()) {
+ mBrowsingContext->Group()->AddKeepAlive();
+ }
+
+ mMarkedDestroying = true;
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvDidUnsuppressPainting() {
+ if (!mFrameElement) {
+ return IPC_OK();
+ }
+ nsSubDocumentFrame* subdocFrame =
+ do_QueryFrame(mFrameElement->GetPrimaryFrame());
+ if (subdocFrame && subdocFrame->HasRetainedPaintData()) {
+ subdocFrame->ClearRetainedPaintData();
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvEnsureLayersConnected(
+ CompositorOptions* aCompositorOptions) {
+ if (mRemoteLayerTreeOwner.IsInitialized()) {
+ mRemoteLayerTreeOwner.EnsureLayersConnected(aCompositorOptions);
+ }
+ return IPC_OK();
+}
+
+void BrowserParent::ActorDestroy(ActorDestroyReason why) {
+ Manager()->NotifyTabDestroyed(mTabId, mMarkedDestroying);
+
+ ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+ if (cpm) {
+ cpm->UnregisterRemoteFrame(mTabId);
+ }
+
+ if (mRemoteLayerTreeOwner.IsInitialized()) {
+ auto layersId = mRemoteLayerTreeOwner.GetLayersId();
+ if (mFrameElement) {
+ nsSubDocumentFrame* f = do_QueryFrame(mFrameElement->GetPrimaryFrame());
+ if (f && f->HasRetainedPaintData() &&
+ f->GetRemotePaintData().mLayersId == layersId) {
+ f->ClearRetainedPaintData();
+ }
+ }
+
+ // It's important to unmap layers after the remote browser has been
+ // destroyed, otherwise it may still send messages to the compositor which
+ // will reject them, causing assertions.
+ RemoveBrowserParentFromTable(layersId);
+ mRemoteLayerTreeOwner.Destroy();
+ }
+
+ // Even though BrowserParent::Destroy calls this, we need to do it here too in
+ // case of a crash.
+ Deactivated();
+
+ if (why == AbnormalShutdown) {
+ // dom_reporting_header must also be enabled for the report to be sent.
+ if (StaticPrefs::dom_reporting_crash_enabled()) {
+ nsCOMPtr<nsIPrincipal> principal = GetContentPrincipal();
+
+ if (principal) {
+ nsAutoCString crash_reason;
+ CrashReporter::GetAnnotation(OtherPid(),
+ CrashReporter::Annotation::MozCrashReason,
+ crash_reason);
+ // FIXME(arenevier): Find a less fragile way to identify that a crash
+ // was caused by OOM
+ bool is_oom = false;
+ if (crash_reason == "OOM" || crash_reason == "OOM!" ||
+ StringBeginsWith(crash_reason, "[unhandlable oom]"_ns) ||
+ StringBeginsWith(crash_reason, "Unhandlable OOM"_ns)) {
+ is_oom = true;
+ }
+
+ CrashReport::Deliver(principal, is_oom);
+ }
+ }
+ }
+
+ // If we were shutting down normally, we held a reference to our
+ // BrowsingContextGroup in `BrowserParent::Destroy`. Clear that reference
+ // here.
+ if (mMarkedDestroying) {
+ mBrowsingContext->Group()->RemoveKeepAlive();
+ }
+
+ // Tell our embedder that the tab is now going away unless we're an
+ // out-of-process iframe.
+ RefPtr<nsFrameLoader> frameLoader = GetFrameLoader(true);
+ if (frameLoader) {
+ ReceiveMessage(CHILD_PROCESS_SHUTDOWN_MESSAGE, false, nullptr);
+
+ if (mBrowsingContext->IsTop()) {
+ // If this is a top-level BrowsingContext, tell the frameloader it's time
+ // to go away. Otherwise, this is a subframe crash, and we can keep the
+ // frameloader around.
+ frameLoader->DestroyComplete();
+ }
+
+ // If this was a crash, tell our nsFrameLoader to fire crash events.
+ if (why == AbnormalShutdown) {
+ frameLoader->MaybeNotifyCrashed(mBrowsingContext, Manager()->ChildID(),
+ GetIPCChannel());
+ } else if (why == ManagedEndpointDropped) {
+ // If we instead failed due to a constructor error, don't include process
+ // information, as the process did not crash.
+ frameLoader->MaybeNotifyCrashed(mBrowsingContext, ContentParentId{},
+ nullptr);
+ }
+ }
+
+ mFrameLoader = nullptr;
+
+ // If we were destroyed due to our ManagedEndpoints being dropped, make a
+ // point of showing the subframe crashed UI. We don't fire the full
+ // `MaybeNotifyCrashed` codepath, as the entire process hasn't crashed on us,
+ // and it may confuse the frontend.
+ mBrowsingContext->BrowserParentDestroyed(
+ this, why == AbnormalShutdown || why == ManagedEndpointDropped);
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvMoveFocus(
+ const bool& aForward, const bool& aForDocumentNavigation) {
+ LOGBROWSERFOCUS(("RecvMoveFocus %p, aForward: %d, aForDocumentNavigation: %d",
+ this, aForward, aForDocumentNavigation));
+ BrowserBridgeParent* bridgeParent = GetBrowserBridgeParent();
+ if (bridgeParent) {
+ mozilla::Unused << bridgeParent->SendMoveFocus(aForward,
+ aForDocumentNavigation);
+ return IPC_OK();
+ }
+
+ RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager();
+ if (fm) {
+ RefPtr<Element> dummy;
+
+ uint32_t type =
+ aForward
+ ? (aForDocumentNavigation
+ ? static_cast<uint32_t>(
+ nsIFocusManager::MOVEFOCUS_FORWARDDOC)
+ : static_cast<uint32_t>(nsIFocusManager::MOVEFOCUS_FORWARD))
+ : (aForDocumentNavigation
+ ? static_cast<uint32_t>(
+ nsIFocusManager::MOVEFOCUS_BACKWARDDOC)
+ : static_cast<uint32_t>(
+ nsIFocusManager::MOVEFOCUS_BACKWARD));
+ fm->MoveFocus(nullptr, mFrameElement, type, nsIFocusManager::FLAG_BYKEY,
+ getter_AddRefs(dummy));
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvDropLinks(
+ nsTArray<nsString>&& aLinks) {
+ nsCOMPtr<nsIBrowser> browser =
+ mFrameElement ? mFrameElement->AsBrowser() : nullptr;
+ if (browser) {
+ // Verify that links have not been modified by the child. If links have
+ // not been modified then it's safe to load those links using the
+ // SystemPrincipal. If they have been modified by web content, then
+ // we use a NullPrincipal which still allows to load web links.
+ bool loadUsingSystemPrincipal = true;
+ if (aLinks.Length() != mVerifyDropLinks.Length()) {
+ loadUsingSystemPrincipal = false;
+ }
+ for (uint32_t i = 0; i < aLinks.Length(); i++) {
+ if (loadUsingSystemPrincipal) {
+ if (!aLinks[i].Equals(mVerifyDropLinks[i])) {
+ loadUsingSystemPrincipal = false;
+ }
+ }
+ }
+ mVerifyDropLinks.Clear();
+ nsCOMPtr<nsIPrincipal> triggeringPrincipal;
+ if (loadUsingSystemPrincipal) {
+ triggeringPrincipal = nsContentUtils::GetSystemPrincipal();
+ } else {
+ triggeringPrincipal = NullPrincipal::CreateWithoutOriginAttributes();
+ }
+ browser->DropLinks(aLinks, triggeringPrincipal);
+ }
+ return IPC_OK();
+}
+
+bool BrowserParent::SendLoadRemoteScript(const nsAString& aURL,
+ const bool& aRunInGlobalScope) {
+ if (mCreatingWindow) {
+ mDelayedFrameScripts.AppendElement(
+ FrameScriptInfo(nsString(aURL), aRunInGlobalScope));
+ return true;
+ }
+
+ MOZ_ASSERT(mDelayedFrameScripts.IsEmpty());
+ return PBrowserParent::SendLoadRemoteScript(aURL, aRunInGlobalScope);
+}
+
+void BrowserParent::LoadURL(nsDocShellLoadState* aLoadState) {
+ MOZ_ASSERT(aLoadState);
+ MOZ_ASSERT(aLoadState->URI());
+ if (mIsDestroyed) {
+ return;
+ }
+
+ if (mCreatingWindow) {
+ // Don't send the message if the child wants to load its own URL.
+ return;
+ }
+
+ Unused << SendLoadURL(WrapNotNull(aLoadState), GetShowInfo());
+}
+
+void BrowserParent::ResumeLoad(uint64_t aPendingSwitchID) {
+ MOZ_ASSERT(aPendingSwitchID != 0);
+
+ if (NS_WARN_IF(mIsDestroyed)) {
+ return;
+ }
+
+ Unused << SendResumeLoad(aPendingSwitchID, GetShowInfo());
+}
+
+void BrowserParent::InitRendering() {
+ if (mRemoteLayerTreeOwner.IsInitialized()) {
+ return;
+ }
+ mRemoteLayerTreeOwner.Initialize(this);
+
+ layers::LayersId layersId = mRemoteLayerTreeOwner.GetLayersId();
+ AddBrowserParentToTable(layersId, this);
+
+ RefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
+ if (frameLoader) {
+ nsIFrame* frame = frameLoader->GetPrimaryFrameOfOwningContent();
+ if (frame) {
+ frame->InvalidateFrame();
+ }
+ }
+
+ TextureFactoryIdentifier textureFactoryIdentifier;
+ mRemoteLayerTreeOwner.GetTextureFactoryIdentifier(&textureFactoryIdentifier);
+ Unused << SendInitRendering(textureFactoryIdentifier, layersId,
+ mRemoteLayerTreeOwner.GetCompositorOptions(),
+ mRemoteLayerTreeOwner.IsLayersConnected());
+
+ RefPtr<nsIWidget> widget = GetTopLevelWidget();
+ if (widget) {
+ ScreenIntMargin safeAreaInsets = widget->GetSafeAreaInsets();
+ Unused << SendSafeAreaInsetsChanged(safeAreaInsets);
+ }
+
+#if defined(MOZ_WIDGET_ANDROID)
+ MOZ_ASSERT(widget);
+
+ if (GetBrowsingContext()->IsTopContent()) {
+ Unused << SendDynamicToolbarMaxHeightChanged(
+ widget->GetDynamicToolbarMaxHeight());
+ }
+#endif
+}
+
+bool BrowserParent::AttachWindowRenderer() {
+ return mRemoteLayerTreeOwner.AttachWindowRenderer();
+}
+
+void BrowserParent::MaybeShowFrame() {
+ RefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
+ if (!frameLoader) {
+ return;
+ }
+ frameLoader->MaybeShowFrame();
+}
+
+bool BrowserParent::Show(const OwnerShowInfo& aOwnerInfo) {
+ mDimensions = aOwnerInfo.size();
+ if (mIsDestroyed) {
+ return false;
+ }
+
+ MOZ_ASSERT(mRemoteLayerTreeOwner.IsInitialized());
+ if (!mRemoteLayerTreeOwner.AttachWindowRenderer()) {
+ return false;
+ }
+
+ mSizeMode = aOwnerInfo.sizeMode();
+ Unused << SendShow(GetShowInfo(), aOwnerInfo);
+ return true;
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvSetDimensions(
+ mozilla::DimensionRequest aRequest, const double& aScale) {
+ NS_ENSURE_TRUE(mFrameElement, IPC_OK());
+ nsCOMPtr<nsIDocShell> docShell = mFrameElement->OwnerDoc()->GetDocShell();
+ NS_ENSURE_TRUE(docShell, IPC_OK());
+ nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
+ docShell->GetTreeOwner(getter_AddRefs(treeOwner));
+ nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = do_QueryInterface(treeOwner);
+ NS_ENSURE_TRUE(treeOwnerAsWin, IPC_OK());
+
+ // `BrowserChild` only sends the values to actually be changed, see more
+ // details in `BrowserChild::SetDimensions()`.
+ // Note that `BrowserChild::SetDimensions()` may be called before receiving
+ // our `SendUIResolutionChanged()` call. Therefore, if given each coordinate
+ // shouldn't be ignored, we need to recompute it if DPI has been changed.
+ // And also note that don't use `mDefaultScale.scale` here since it may be
+ // different from the result of `GetWidgetCSSToDeviceScale()`.
+ // NOTE(emilio): We use GetWidgetCSSToDeviceScale() because the old scale is a
+ // widget scale, and we only use the current scale to scale up/down the
+ // relevant values.
+
+ CSSToLayoutDeviceScale oldScale((float)aScale);
+ CSSToLayoutDeviceScale currentScale(
+ (float)treeOwnerAsWin->GetWidgetCSSToDeviceScale());
+
+ if (oldScale != currentScale) {
+ auto rescaleFunc = [&oldScale, &currentScale](LayoutDeviceIntCoord& aVal) {
+ aVal = (LayoutDeviceCoord(aVal) / oldScale * currentScale).Rounded();
+ };
+ aRequest.mX.apply(rescaleFunc);
+ aRequest.mY.apply(rescaleFunc);
+ aRequest.mWidth.apply(rescaleFunc);
+ aRequest.mHeight.apply(rescaleFunc);
+ }
+
+ // treeOwner is the chrome tree owner, but we wan't the content tree owner.
+ nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = do_GetInterface(treeOwner);
+ NS_ENSURE_TRUE(webBrowserChrome, IPC_OK());
+ webBrowserChrome->SetDimensions(std::move(aRequest));
+ return IPC_OK();
+}
+
+nsresult BrowserParent::UpdatePosition() {
+ RefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
+ if (!frameLoader) {
+ return NS_OK;
+ }
+ nsIntRect windowDims;
+ NS_ENSURE_SUCCESS(frameLoader->GetWindowDimensions(windowDims),
+ NS_ERROR_FAILURE);
+ // Avoid updating sizes here.
+ windowDims.SizeTo(mRect.Size());
+ UpdateDimensions(windowDims, mDimensions);
+ return NS_OK;
+}
+
+void BrowserParent::NotifyPositionUpdatedForContentsInPopup() {
+ if (CanonicalBrowsingContext* bc = GetBrowsingContext()) {
+ bc->PreOrderWalk([](BrowsingContext* aContext) {
+ if (WindowGlobalParent* windowGlobalParent =
+ aContext->Canonical()->GetCurrentWindowGlobal()) {
+ if (RefPtr<BrowserParent> browserParent =
+ windowGlobalParent->GetBrowserParent()) {
+ browserParent->UpdatePosition();
+ }
+ }
+ });
+ }
+}
+
+void BrowserParent::UpdateDimensions(const nsIntRect& rect,
+ const ScreenIntSize& size) {
+ if (mIsDestroyed) {
+ return;
+ }
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (!widget) {
+ NS_WARNING("No widget found in BrowserParent::UpdateDimensions");
+ return;
+ }
+
+ LayoutDeviceIntPoint clientOffset = GetClientOffset();
+ LayoutDeviceIntPoint chromeOffset = !GetBrowserBridgeParent()
+ ? -GetChildProcessOffset()
+ : LayoutDeviceIntPoint();
+
+ if (!mUpdatedDimensions || mDimensions != size || !mRect.IsEqualEdges(rect) ||
+ clientOffset != mClientOffset || chromeOffset != mChromeOffset) {
+ mUpdatedDimensions = true;
+ mRect = rect;
+ mDimensions = size;
+ mClientOffset = clientOffset;
+ mChromeOffset = chromeOffset;
+
+ Unused << SendUpdateDimensions(GetDimensionInfo());
+ UpdateNativePointerLockCenter(widget);
+ }
+}
+
+DimensionInfo BrowserParent::GetDimensionInfo() {
+ LayoutDeviceIntRect devicePixelRect = ViewAs<LayoutDevicePixel>(
+ mRect, PixelCastJustification::LayoutDeviceIsScreenForTabDims);
+ LayoutDeviceIntSize devicePixelSize = ViewAs<LayoutDevicePixel>(
+ mDimensions, PixelCastJustification::LayoutDeviceIsScreenForTabDims);
+
+ CSSRect unscaledRect = devicePixelRect / mDefaultScale;
+ CSSSize unscaledSize = devicePixelSize / mDefaultScale;
+ DimensionInfo di(unscaledRect, unscaledSize, mClientOffset, mChromeOffset);
+ return di;
+}
+
+void BrowserParent::UpdateNativePointerLockCenter(nsIWidget* aWidget) {
+ if (!mLockedNativePointer) {
+ return;
+ }
+ LayoutDeviceIntRect dims(
+ {0, 0},
+ ViewAs<LayoutDevicePixel>(
+ mDimensions, PixelCastJustification::LayoutDeviceIsScreenForTabDims));
+ aWidget->SetNativePointerLockCenter((dims + mChromeOffset).Center());
+}
+
+void BrowserParent::SizeModeChanged(const nsSizeMode& aSizeMode) {
+ if (!mIsDestroyed && aSizeMode != mSizeMode) {
+ mSizeMode = aSizeMode;
+ Unused << SendSizeModeChanged(aSizeMode);
+ }
+}
+
+#if defined(MOZ_WIDGET_ANDROID)
+void BrowserParent::DynamicToolbarMaxHeightChanged(ScreenIntCoord aHeight) {
+ if (!mIsDestroyed) {
+ Unused << SendDynamicToolbarMaxHeightChanged(aHeight);
+ }
+}
+
+void BrowserParent::DynamicToolbarOffsetChanged(ScreenIntCoord aOffset) {
+ if (!mIsDestroyed) {
+ Unused << SendDynamicToolbarOffsetChanged(aOffset);
+ }
+}
+#endif
+
+void BrowserParent::HandleAccessKey(const WidgetKeyboardEvent& aEvent,
+ nsTArray<uint32_t>& aCharCodes) {
+ if (!mIsDestroyed) {
+ // Note that we don't need to mark aEvent is posted to a remote process
+ // because the event may be dispatched to it as normal keyboard event.
+ // Therefore, we should use local copy to send it.
+ WidgetKeyboardEvent localEvent(aEvent);
+ RequestingAccessKeyEventData::Set(localEvent);
+ Unused << SendHandleAccessKey(localEvent, aCharCodes);
+ }
+}
+
+void BrowserParent::Activate(uint64_t aActionId) {
+ LOGBROWSERFOCUS(("Activate %p actionid: %" PRIu64, this, aActionId));
+ if (!mIsDestroyed) {
+ SetTopLevelWebFocus(this); // Intentionally inside "if"
+ Unused << SendActivate(aActionId);
+ }
+}
+
+void BrowserParent::Deactivate(bool aWindowLowering, uint64_t aActionId) {
+ LOGBROWSERFOCUS(("Deactivate %p actionid: %" PRIu64, this, aActionId));
+ if (!aWindowLowering) {
+ UnsetTopLevelWebFocus(this); // Intentionally outside the next "if"
+ }
+ if (!mIsDestroyed) {
+ Unused << SendDeactivate(aActionId);
+ }
+}
+
+#ifdef ACCESSIBILITY
+a11y::PDocAccessibleParent* BrowserParent::AllocPDocAccessibleParent(
+ PDocAccessibleParent* aParent, const uint64_t&,
+ const MaybeDiscardedBrowsingContext&) {
+ // Reference freed in DeallocPDocAccessibleParent.
+ return a11y::DocAccessibleParent::New().take();
+}
+
+bool BrowserParent::DeallocPDocAccessibleParent(PDocAccessibleParent* aParent) {
+ // Free reference from AllocPDocAccessibleParent.
+ static_cast<a11y::DocAccessibleParent*>(aParent)->Release();
+ return true;
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvPDocAccessibleConstructor(
+ PDocAccessibleParent* aDoc, PDocAccessibleParent* aParentDoc,
+ const uint64_t& aParentID,
+ const MaybeDiscardedBrowsingContext& aBrowsingContext) {
+# if defined(ANDROID)
+ MonitorAutoLock mal(nsAccessibilityService::GetAndroidMonitor());
+# endif
+ auto doc = static_cast<a11y::DocAccessibleParent*>(aDoc);
+
+ // If this tab is already shutting down just mark the new actor as shutdown
+ // and ignore it. When the tab actor is destroyed it will be too.
+ if (mIsDestroyed) {
+ doc->MarkAsShutdown();
+ return IPC_OK();
+ }
+
+ if (aParentDoc) {
+ // Iframe document rendered in the same process as its embedder.
+ // A document should never directly be the parent of another document.
+ // There should always be an outer doc accessible child of the outer
+ // document containing the child.
+ MOZ_ASSERT(aParentID);
+ if (!aParentID) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ auto parentDoc = static_cast<a11y::DocAccessibleParent*>(aParentDoc);
+ if (parentDoc->IsShutdown()) {
+ // This can happen if parentDoc is an OOP iframe, but its embedder has
+ // been destroyed. (DocAccessibleParent::Destroy destroys any child
+ // documents.) The OOP iframe (and anything it embeds) will die soon
+ // anyway, so mark this document as shutdown and ignore it.
+ doc->MarkAsShutdown();
+ return IPC_OK();
+ }
+
+ if (aBrowsingContext) {
+ doc->SetBrowsingContext(aBrowsingContext.get_canonical());
+ }
+
+ mozilla::ipc::IPCResult added = parentDoc->AddChildDoc(doc, aParentID);
+ if (!added) {
+# ifdef DEBUG
+ return added;
+# else
+ return IPC_OK();
+# endif
+ }
+
+# ifdef XP_WIN
+ if (a11y::nsWinUtils::IsWindowEmulationStarted()) {
+ doc->SetEmulatedWindowHandle(parentDoc->GetEmulatedWindowHandle());
+ }
+# endif
+
+ return IPC_OK();
+ }
+
+ if (aBrowsingContext) {
+ doc->SetBrowsingContext(aBrowsingContext.get_canonical());
+ }
+
+ if (auto* bridge = GetBrowserBridgeParent()) {
+ // Iframe document rendered in a different process to its embedder.
+ // In this case, we don't get aParentDoc and aParentID.
+ MOZ_ASSERT(!aParentDoc && !aParentID);
+ doc->SetTopLevelInContentProcess();
+ a11y::ProxyCreated(doc);
+ // It's possible the embedder accessible hasn't been set yet; e.g.
+ // a hidden iframe. In that case, embedderDoc will be null and this will
+ // be handled when the embedder is set.
+ if (a11y::DocAccessibleParent* embedderDoc =
+ bridge->GetEmbedderAccessibleDoc()) {
+ mozilla::ipc::IPCResult added = embedderDoc->AddChildDoc(bridge);
+ if (!added) {
+# ifdef DEBUG
+ return added;
+# else
+ return IPC_OK();
+# endif
+ }
+ }
+ return IPC_OK();
+ } else {
+ // null aParentDoc means this document is at the top level in the child
+ // process. That means it makes no sense to get an id for an accessible
+ // that is its parent.
+ MOZ_ASSERT(!aParentID);
+ if (aParentID) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ if (auto* prevTopLevel = GetTopLevelDocAccessible()) {
+ // Sometimes, we can get a new top level DocAccessibleParent before the
+ // old one gets destroyed. The old one will die pretty shortly anyway,
+ // so just destroy it now. If we don't do this, GetTopLevelDocAccessible()
+ // might return the wrong document for a short while.
+ prevTopLevel->Destroy();
+ }
+ doc->SetTopLevel();
+ a11y::DocManager::RemoteDocAdded(doc);
+# ifdef XP_WIN
+ doc->MaybeInitWindowEmulation();
+# endif
+ }
+ return IPC_OK();
+}
+#endif
+
+already_AddRefed<PFilePickerParent> BrowserParent::AllocPFilePickerParent(
+ const nsString& aTitle, const nsIFilePicker::Mode& aMode) {
+ return MakeAndAddRef<FilePickerParent>(aTitle, aMode);
+}
+
+already_AddRefed<PSessionStoreParent>
+BrowserParent::AllocPSessionStoreParent() {
+ RefPtr<BrowserSessionStore> sessionStore =
+ BrowserSessionStore::GetOrCreate(mBrowsingContext->Top());
+ if (!sessionStore) {
+ return nullptr;
+ }
+
+ return do_AddRef(new SessionStoreParent(mBrowsingContext, sessionStore));
+}
+
+IPCResult BrowserParent::RecvNewWindowGlobal(
+ ManagedEndpoint<PWindowGlobalParent>&& aEndpoint,
+ const WindowGlobalInit& aInit) {
+ RefPtr<CanonicalBrowsingContext> browsingContext =
+ CanonicalBrowsingContext::Get(aInit.context().mBrowsingContextId);
+ if (!browsingContext) {
+ return IPC_FAIL(this, "Cannot create for missing BrowsingContext");
+ }
+ if (!aInit.principal()) {
+ return IPC_FAIL(this, "Cannot create without valid principal");
+ }
+
+ // Ensure we never load a document with a content principal in
+ // the wrong type of webIsolated process
+ EnumSet<ContentParent::ValidatePrincipalOptions> validationOptions = {};
+ nsCOMPtr<nsIURI> docURI = aInit.documentURI();
+ if (docURI->SchemeIs("blob") || docURI->SchemeIs("chrome")) {
+ // XXXckerschb TODO - Do not use SystemPrincipal for:
+ // Bug 1699385: Remove allowSystem for blobs
+ // Bug 1698087: chrome://devtools/content/shared/webextension-fallback.html
+ // chrome reftests, e.g.
+ // * chrome://reftest/content/writing-mode/ua-style-sheet-button-1a-ref.html
+ // * chrome://reftest/content/xul-document-load/test003.xhtml
+ // * chrome://reftest/content/forms/input/text/centering-1.xhtml
+ validationOptions = {ContentParent::ValidatePrincipalOptions::AllowSystem};
+ }
+
+ // Some reftests have frames inside their chrome URIs and those load
+ // about:blank:
+ if (xpc::IsInAutomation() && docURI->SchemeIs("about")) {
+ WindowGlobalParent* wgp = browsingContext->GetParentWindowContext();
+ nsAutoCString spec;
+ NS_ENSURE_SUCCESS(docURI->GetSpec(spec),
+ IPC_FAIL(this, "Should have spec for about: URI"));
+ if (spec.Equals("about:blank") && wgp &&
+ wgp->DocumentPrincipal()->IsSystemPrincipal()) {
+ validationOptions = {
+ ContentParent::ValidatePrincipalOptions::AllowSystem};
+ }
+ }
+
+ if (!mManager->ValidatePrincipal(aInit.principal(), validationOptions)) {
+ ContentParent::LogAndAssertFailedPrincipalValidationInfo(aInit.principal(),
+ __func__);
+ }
+
+ // Construct our new WindowGlobalParent, bind, and initialize it.
+ RefPtr<WindowGlobalParent> wgp =
+ WindowGlobalParent::CreateDisconnected(aInit);
+ BindPWindowGlobalEndpoint(std::move(aEndpoint), wgp);
+ wgp->Init();
+ return IPC_OK();
+}
+
+PVsyncParent* BrowserParent::AllocPVsyncParent() {
+ MOZ_ASSERT(!mVsyncParent);
+ mVsyncParent = new VsyncParent();
+ UpdateVsyncParentVsyncDispatcher();
+ return mVsyncParent.get();
+}
+
+bool BrowserParent::DeallocPVsyncParent(PVsyncParent* aActor) {
+ MOZ_ASSERT(aActor);
+ mVsyncParent = nullptr;
+ return true;
+}
+
+void BrowserParent::UpdateVsyncParentVsyncDispatcher() {
+ if (!mVsyncParent) {
+ return;
+ }
+
+ if (nsCOMPtr<nsIWidget> widget = GetWidget()) {
+ RefPtr<VsyncDispatcher> vsyncDispatcher = widget->GetVsyncDispatcher();
+ if (!vsyncDispatcher) {
+ vsyncDispatcher = gfxPlatform::GetPlatform()->GetGlobalVsyncDispatcher();
+ }
+ mVsyncParent->UpdateVsyncDispatcher(vsyncDispatcher);
+ }
+}
+
+void BrowserParent::MouseEnterIntoWidget() {
+ if (nsCOMPtr<nsIWidget> widget = GetWidget()) {
+ // When we mouseenter the remote target, the remote target's cursor should
+ // become the current cursor. When we mouseexit, we stop.
+ mRemoteTargetSetsCursor = true;
+ widget->SetCursor(mCursor);
+ EventStateManager::ClearCursorSettingManager();
+ }
+
+ // Mark that we have missed a mouse enter event, so that
+ // the next mouse event will create a replacement mouse
+ // enter event and send it to the child.
+ mIsMouseEnterIntoWidgetEventSuppressed = true;
+}
+
+void BrowserParent::SendRealMouseEvent(WidgetMouseEvent& aEvent) {
+ if (mIsDestroyed) {
+ return;
+ }
+
+ // XXXedgar, if the synthesized mouse events could deliver to the correct
+ // process directly (see
+ // https://bugzilla.mozilla.org/show_bug.cgi?id=1549355), we probably don't
+ // need to check mReason then.
+ if (aEvent.mReason == WidgetMouseEvent::eReal) {
+ if (aEvent.mMessage == eMouseExitFromWidget) {
+ // Since we are leaving this remote target, so don't need to update
+ // sLastMouseRemoteTarget, and if we are sLastMouseRemoteTarget, reset it
+ // to null.
+ BrowserParent::UnsetLastMouseRemoteTarget(this);
+ } else {
+ // Last remote target should not be changed without eMouseExitFromWidget.
+ MOZ_ASSERT_IF(sLastMouseRemoteTarget, sLastMouseRemoteTarget == this);
+ sLastMouseRemoteTarget = this;
+ }
+ }
+
+ aEvent.mRefPoint = TransformParentToChild(aEvent);
+
+ if (nsCOMPtr<nsIWidget> widget = GetWidget()) {
+ // When we mouseenter the remote target, the remote target's cursor should
+ // become the current cursor. When we mouseexit, we stop.
+ if (eMouseEnterIntoWidget == aEvent.mMessage) {
+ mRemoteTargetSetsCursor = true;
+ widget->SetCursor(mCursor);
+ EventStateManager::ClearCursorSettingManager();
+ } else if (eMouseExitFromWidget == aEvent.mMessage) {
+ mRemoteTargetSetsCursor = false;
+ }
+ }
+ if (!mIsReadyToHandleInputEvents) {
+ if (eMouseEnterIntoWidget == aEvent.mMessage) {
+ mIsMouseEnterIntoWidgetEventSuppressed = true;
+ } else if (eMouseExitFromWidget == aEvent.mMessage) {
+ mIsMouseEnterIntoWidgetEventSuppressed = false;
+ }
+ return;
+ }
+
+ ScrollableLayerGuid guid;
+ uint64_t blockId;
+ ApzAwareEventRoutingToChild(&guid, &blockId, nullptr);
+
+ bool isInputPriorityEventEnabled = Manager()->IsInputPriorityEventEnabled();
+
+ if (mIsMouseEnterIntoWidgetEventSuppressed) {
+ // In the case that the BrowserParent suppressed the eMouseEnterWidget event
+ // due to its corresponding BrowserChild wasn't ready to handle it, we have
+ // to resend it when the BrowserChild is ready.
+ mIsMouseEnterIntoWidgetEventSuppressed = false;
+ WidgetMouseEvent localEvent(aEvent);
+ localEvent.mMessage = eMouseEnterIntoWidget;
+ DebugOnly<bool> ret =
+ isInputPriorityEventEnabled
+ ? SendRealMouseEnterExitWidgetEvent(localEvent, guid, blockId)
+ : SendNormalPriorityRealMouseEnterExitWidgetEvent(localEvent, guid,
+ blockId);
+ NS_WARNING_ASSERTION(ret, "SendRealMouseEnterExitWidgetEvent() failed");
+ MOZ_ASSERT(!ret || localEvent.HasBeenPostedToRemoteProcess());
+ }
+
+ if (eMouseMove == aEvent.mMessage) {
+ if (aEvent.mReason == WidgetMouseEvent::eSynthesized) {
+ DebugOnly<bool> ret =
+ isInputPriorityEventEnabled
+ ? SendSynthMouseMoveEvent(aEvent, guid, blockId)
+ : SendNormalPrioritySynthMouseMoveEvent(aEvent, guid, blockId);
+ NS_WARNING_ASSERTION(ret, "SendSynthMouseMoveEvent() failed");
+ MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess());
+ return;
+ }
+
+ if (!aEvent.mFlags.mIsSynthesizedForTests) {
+ DebugOnly<bool> ret =
+ isInputPriorityEventEnabled
+ ? SendRealMouseMoveEvent(aEvent, guid, blockId)
+ : SendNormalPriorityRealMouseMoveEvent(aEvent, guid, blockId);
+ NS_WARNING_ASSERTION(ret, "SendRealMouseMoveEvent() failed");
+ MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess());
+ return;
+ }
+
+ DebugOnly<bool> ret =
+ isInputPriorityEventEnabled
+ ? SendRealMouseMoveEventForTests(aEvent, guid, blockId)
+ : SendNormalPriorityRealMouseMoveEventForTests(aEvent, guid,
+ blockId);
+ NS_WARNING_ASSERTION(ret, "SendRealMouseMoveEventForTests() failed");
+ MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess());
+ return;
+ }
+
+ if (eMouseEnterIntoWidget == aEvent.mMessage ||
+ eMouseExitFromWidget == aEvent.mMessage) {
+ DebugOnly<bool> ret =
+ isInputPriorityEventEnabled
+ ? SendRealMouseEnterExitWidgetEvent(aEvent, guid, blockId)
+ : SendNormalPriorityRealMouseEnterExitWidgetEvent(aEvent, guid,
+ blockId);
+ NS_WARNING_ASSERTION(ret, "SendRealMouseEnterExitWidgetEvent() failed");
+ MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess());
+ return;
+ }
+
+ DebugOnly<bool> ret =
+ isInputPriorityEventEnabled
+ ? SendRealMouseButtonEvent(aEvent, guid, blockId)
+ : SendNormalPriorityRealMouseButtonEvent(aEvent, guid, blockId);
+ NS_WARNING_ASSERTION(ret, "SendRealMouseButtonEvent() failed");
+ MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess());
+}
+
+LayoutDeviceToCSSScale BrowserParent::GetLayoutDeviceToCSSScale() {
+ Document* doc = (mFrameElement ? mFrameElement->OwnerDoc() : nullptr);
+ nsPresContext* ctx = (doc ? doc->GetPresContext() : nullptr);
+ return LayoutDeviceToCSSScale(
+ ctx ? (float)ctx->AppUnitsPerDevPixel() / AppUnitsPerCSSPixel() : 0.0f);
+}
+
+bool BrowserParent::QueryDropLinksForVerification() {
+ // Before sending the dragEvent, we query the links being dragged and
+ // store them on the parent, to make sure the child can not modify links.
+ nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
+ if (!dragSession) {
+ NS_WARNING("No dragSession to query links for verification");
+ return false;
+ }
+
+ RefPtr<DataTransfer> initialDataTransfer = dragSession->GetDataTransfer();
+ if (!initialDataTransfer) {
+ NS_WARNING("No initialDataTransfer to query links for verification");
+ return false;
+ }
+
+ nsCOMPtr<nsIDroppedLinkHandler> dropHandler =
+ do_GetService("@mozilla.org/content/dropped-link-handler;1");
+ if (!dropHandler) {
+ NS_WARNING("No dropHandler to query links for verification");
+ return false;
+ }
+
+ // No more than one drop event can happen simultaneously; reset the link
+ // verification array and store all links that are being dragged.
+ mVerifyDropLinks.Clear();
+
+ nsTArray<RefPtr<nsIDroppedLinkItem>> droppedLinkItems;
+ dropHandler->QueryLinks(initialDataTransfer, droppedLinkItems);
+
+ // Since the entire event is cancelled if one of the links is invalid,
+ // we can store all links on the parent side without any prior
+ // validation checks.
+ nsresult rv = NS_OK;
+ for (nsIDroppedLinkItem* item : droppedLinkItems) {
+ nsString tmp;
+ rv = item->GetUrl(tmp);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Failed to query url for verification");
+ break;
+ }
+ mVerifyDropLinks.AppendElement(tmp);
+
+ rv = item->GetName(tmp);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Failed to query name for verification");
+ break;
+ }
+ mVerifyDropLinks.AppendElement(tmp);
+
+ rv = item->GetType(tmp);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Failed to query type for verification");
+ break;
+ }
+ mVerifyDropLinks.AppendElement(tmp);
+ }
+ if (NS_FAILED(rv)) {
+ mVerifyDropLinks.Clear();
+ return false;
+ }
+ return true;
+}
+
+void BrowserParent::SendRealDragEvent(WidgetDragEvent& aEvent,
+ uint32_t aDragAction,
+ uint32_t aDropEffect,
+ nsIPrincipal* aPrincipal,
+ nsIContentSecurityPolicy* aCsp) {
+ if (mIsDestroyed || !mIsReadyToHandleInputEvents) {
+ return;
+ }
+ MOZ_ASSERT(!Manager()->IsInputPriorityEventEnabled());
+ aEvent.mRefPoint = TransformParentToChild(aEvent.mRefPoint);
+ if (aEvent.mMessage == eDrop) {
+ if (!QueryDropLinksForVerification()) {
+ return;
+ }
+ }
+ DebugOnly<bool> ret = PBrowserParent::SendRealDragEvent(
+ aEvent, aDragAction, aDropEffect, aPrincipal, aCsp);
+ NS_WARNING_ASSERTION(ret, "PBrowserParent::SendRealDragEvent() failed");
+ MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess());
+}
+
+void BrowserParent::SendMouseWheelEvent(WidgetWheelEvent& aEvent) {
+ if (mIsDestroyed || !mIsReadyToHandleInputEvents) {
+ return;
+ }
+
+ ScrollableLayerGuid guid;
+ uint64_t blockId;
+ ApzAwareEventRoutingToChild(&guid, &blockId, nullptr);
+ aEvent.mRefPoint = TransformParentToChild(aEvent.mRefPoint);
+ DebugOnly<bool> ret =
+ Manager()->IsInputPriorityEventEnabled()
+ ? PBrowserParent::SendMouseWheelEvent(aEvent, guid, blockId)
+ : PBrowserParent::SendNormalPriorityMouseWheelEvent(aEvent, guid,
+ blockId);
+
+ NS_WARNING_ASSERTION(ret, "PBrowserParent::SendMouseWheelEvent() failed");
+ MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess());
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvDispatchWheelEvent(
+ const mozilla::WidgetWheelEvent& aEvent) {
+ NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event"));
+
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (!widget) {
+ return IPC_OK();
+ }
+
+ WidgetWheelEvent localEvent(aEvent);
+ localEvent.mWidget = widget;
+ localEvent.mRefPoint = TransformChildToParent(localEvent.mRefPoint);
+
+ widget->DispatchInputEvent(&localEvent);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvDispatchMouseEvent(
+ const mozilla::WidgetMouseEvent& aEvent) {
+ NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event"));
+
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (!widget) {
+ return IPC_OK();
+ }
+
+ WidgetMouseEvent localEvent(aEvent);
+ localEvent.mWidget = widget;
+ localEvent.mRefPoint = TransformChildToParent(localEvent.mRefPoint);
+
+ widget->DispatchInputEvent(&localEvent);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvDispatchKeyboardEvent(
+ const mozilla::WidgetKeyboardEvent& aEvent) {
+ NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event"));
+
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (!widget) {
+ return IPC_OK();
+ }
+
+ WidgetKeyboardEvent localEvent(aEvent);
+ localEvent.mWidget = widget;
+ localEvent.mRefPoint = TransformChildToParent(localEvent.mRefPoint);
+
+ widget->DispatchInputEvent(&localEvent);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvDispatchTouchEvent(
+ const mozilla::WidgetTouchEvent& aEvent) {
+ NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event"));
+
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (!widget) {
+ return IPC_OK();
+ }
+
+ WidgetTouchEvent localEvent(aEvent);
+ localEvent.mWidget = widget;
+
+ for (uint32_t i = 0; i < localEvent.mTouches.Length(); i++) {
+ localEvent.mTouches[i]->mRefPoint =
+ TransformChildToParent(localEvent.mTouches[i]->mRefPoint);
+ }
+
+ widget->DispatchInputEvent(&localEvent);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvRequestNativeKeyBindings(
+ const uint32_t& aType, const WidgetKeyboardEvent& aEvent,
+ nsTArray<CommandInt>* aCommands) {
+ MOZ_ASSERT(aCommands);
+ MOZ_ASSERT(aCommands->IsEmpty());
+
+ NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event"));
+
+ NativeKeyBindingsType keyBindingsType =
+ static_cast<NativeKeyBindingsType>(aType);
+ switch (keyBindingsType) {
+ case NativeKeyBindingsType::SingleLineEditor:
+ case NativeKeyBindingsType::MultiLineEditor:
+ case NativeKeyBindingsType::RichTextEditor:
+ break;
+ default:
+ return IPC_FAIL(this, "Invalid aType value");
+ }
+
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (!widget) {
+ return IPC_OK();
+ }
+
+ WidgetKeyboardEvent localEvent(aEvent);
+ localEvent.mWidget = widget;
+
+ if (NS_FAILED(widget->AttachNativeKeyEvent(localEvent))) {
+ return IPC_OK();
+ }
+
+ Maybe<WritingMode> writingMode;
+ if (RefPtr<widget::TextEventDispatcher> dispatcher =
+ widget->GetTextEventDispatcher()) {
+ writingMode = dispatcher->MaybeQueryWritingModeAtSelection();
+ }
+ if (localEvent.InitEditCommandsFor(keyBindingsType, writingMode)) {
+ *aCommands = localEvent.EditCommandsConstRef(keyBindingsType).Clone();
+ }
+
+ return IPC_OK();
+}
+
+class SynthesizedEventObserver : public nsIObserver {
+ NS_DECL_ISUPPORTS
+
+ public:
+ SynthesizedEventObserver(BrowserParent* aBrowserParent,
+ const uint64_t& aObserverId)
+ : mBrowserParent(aBrowserParent), mObserverId(aObserverId) {
+ MOZ_ASSERT(mBrowserParent);
+ }
+
+ NS_IMETHOD Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData) override {
+ if (!mBrowserParent || !mObserverId) {
+ // We already sent the notification, or we don't actually need to
+ // send any notification at all.
+ return NS_OK;
+ }
+
+ if (mBrowserParent->IsDestroyed()) {
+ // If this happens it's probably a bug in the test that's triggering this.
+ NS_WARNING(
+ "BrowserParent was unexpectedly destroyed during event "
+ "synthesization!");
+ } else if (!mBrowserParent->SendNativeSynthesisResponse(
+ mObserverId, nsCString(aTopic))) {
+ NS_WARNING("Unable to send native event synthesization response!");
+ }
+ // Null out browserParent to indicate we already sent the response
+ mBrowserParent = nullptr;
+ return NS_OK;
+ }
+
+ private:
+ virtual ~SynthesizedEventObserver() = default;
+
+ RefPtr<BrowserParent> mBrowserParent;
+ uint64_t mObserverId;
+};
+
+NS_IMPL_ISUPPORTS(SynthesizedEventObserver, nsIObserver)
+
+class MOZ_STACK_CLASS AutoSynthesizedEventResponder {
+ public:
+ AutoSynthesizedEventResponder(BrowserParent* aBrowserParent,
+ const uint64_t& aObserverId, const char* aTopic)
+ : mObserver(new SynthesizedEventObserver(aBrowserParent, aObserverId)),
+ mTopic(aTopic) {}
+
+ ~AutoSynthesizedEventResponder() {
+ // This may be a no-op if the observer already sent a response.
+ mObserver->Observe(nullptr, mTopic, nullptr);
+ }
+
+ nsIObserver* GetObserver() { return mObserver; }
+
+ private:
+ nsCOMPtr<nsIObserver> mObserver;
+ const char* mTopic;
+};
+
+mozilla::ipc::IPCResult BrowserParent::RecvSynthesizeNativeKeyEvent(
+ const int32_t& aNativeKeyboardLayout, const int32_t& aNativeKeyCode,
+ const uint32_t& aModifierFlags, const nsString& aCharacters,
+ const nsString& aUnmodifiedCharacters, const uint64_t& aObserverId) {
+ NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event"));
+
+ AutoSynthesizedEventResponder responder(this, aObserverId, "keyevent");
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (widget) {
+ widget->SynthesizeNativeKeyEvent(
+ aNativeKeyboardLayout, aNativeKeyCode, aModifierFlags, aCharacters,
+ aUnmodifiedCharacters, responder.GetObserver());
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvSynthesizeNativeMouseEvent(
+ const LayoutDeviceIntPoint& aPoint, const uint32_t& aNativeMessage,
+ const int16_t& aButton, const uint32_t& aModifierFlags,
+ const uint64_t& aObserverId) {
+ NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event"));
+
+ const uint32_t last =
+ static_cast<uint32_t>(nsIWidget::NativeMouseMessage::LeaveWindow);
+ NS_ENSURE_TRUE(aNativeMessage <= last, IPC_FAIL(this, "Bogus message"));
+ AutoSynthesizedEventResponder responder(this, aObserverId, "mouseevent");
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (widget) {
+ widget->SynthesizeNativeMouseEvent(
+ aPoint, static_cast<nsIWidget::NativeMouseMessage>(aNativeMessage),
+ static_cast<mozilla::MouseButton>(aButton),
+ static_cast<nsIWidget::Modifiers>(aModifierFlags),
+ responder.GetObserver());
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvSynthesizeNativeMouseMove(
+ const LayoutDeviceIntPoint& aPoint, const uint64_t& aObserverId) {
+ // This is used by pointer lock API. So, even if it's not in the automation
+ // mode, we need to accept the request.
+ AutoSynthesizedEventResponder responder(this, aObserverId, "mousemove");
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (widget) {
+ widget->SynthesizeNativeMouseMove(aPoint, responder.GetObserver());
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvSynthesizeNativeMouseScrollEvent(
+ const LayoutDeviceIntPoint& aPoint, const uint32_t& aNativeMessage,
+ const double& aDeltaX, const double& aDeltaY, const double& aDeltaZ,
+ const uint32_t& aModifierFlags, const uint32_t& aAdditionalFlags,
+ const uint64_t& aObserverId) {
+ NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event"));
+
+ AutoSynthesizedEventResponder responder(this, aObserverId,
+ "mousescrollevent");
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (widget) {
+ widget->SynthesizeNativeMouseScrollEvent(
+ aPoint, aNativeMessage, aDeltaX, aDeltaY, aDeltaZ, aModifierFlags,
+ aAdditionalFlags, responder.GetObserver());
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvSynthesizeNativeTouchPoint(
+ const uint32_t& aPointerId, const TouchPointerState& aPointerState,
+ const LayoutDeviceIntPoint& aPoint, const double& aPointerPressure,
+ const uint32_t& aPointerOrientation, const uint64_t& aObserverId) {
+ // This is used by DevTools to emulate touch events from mouse events in the
+ // responsive design mode. Therefore, we should accept the IPC messages even
+ // if it's not in the automation mode but the browsing context is in RDM pane.
+ // And the IPC message could be just delayed after closing the responsive
+ // design mode. Therefore, we shouldn't return IPC_FAIL since doing it makes
+ // the tab crash.
+ if (!xpc::IsInAutomation()) {
+ NS_ENSURE_TRUE(mBrowsingContext, IPC_OK());
+ NS_ENSURE_TRUE(mBrowsingContext->Top()->GetInRDMPane(), IPC_OK());
+ }
+
+ AutoSynthesizedEventResponder responder(this, aObserverId, "touchpoint");
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (widget) {
+ widget->SynthesizeNativeTouchPoint(aPointerId, aPointerState, aPoint,
+ aPointerPressure, aPointerOrientation,
+ responder.GetObserver());
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvSynthesizeNativeTouchPadPinch(
+ const TouchpadGesturePhase& aEventPhase, const float& aScale,
+ const LayoutDeviceIntPoint& aPoint, const int32_t& aModifierFlags) {
+ NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event"));
+
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (widget) {
+ widget->SynthesizeNativeTouchPadPinch(aEventPhase, aScale, aPoint,
+ aModifierFlags);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvSynthesizeNativeTouchTap(
+ const LayoutDeviceIntPoint& aPoint, const bool& aLongTap,
+ const uint64_t& aObserverId) {
+ NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event"));
+
+ AutoSynthesizedEventResponder responder(this, aObserverId, "touchtap");
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (widget) {
+ widget->SynthesizeNativeTouchTap(aPoint, aLongTap, responder.GetObserver());
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvClearNativeTouchSequence(
+ const uint64_t& aObserverId) {
+ NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event"));
+
+ AutoSynthesizedEventResponder responder(this, aObserverId, "cleartouch");
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (widget) {
+ widget->ClearNativeTouchSequence(responder.GetObserver());
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvSynthesizeNativePenInput(
+ const uint32_t& aPointerId, const TouchPointerState& aPointerState,
+ const LayoutDeviceIntPoint& aPoint, const double& aPressure,
+ const uint32_t& aRotation, const int32_t& aTiltX, const int32_t& aTiltY,
+ const int32_t& aButton, const uint64_t& aObserverId) {
+ NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event"));
+
+ AutoSynthesizedEventResponder responder(this, aObserverId, "peninput");
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (widget) {
+ widget->SynthesizeNativePenInput(aPointerId, aPointerState, aPoint,
+ aPressure, aRotation, aTiltX, aTiltY,
+ aButton, responder.GetObserver());
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvSynthesizeNativeTouchpadDoubleTap(
+ const LayoutDeviceIntPoint& aPoint, const uint32_t& aModifierFlags) {
+ NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event"));
+
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (widget) {
+ widget->SynthesizeNativeTouchpadDoubleTap(aPoint, aModifierFlags);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvSynthesizeNativeTouchpadPan(
+ const TouchpadGesturePhase& aEventPhase, const LayoutDeviceIntPoint& aPoint,
+ const double& aDeltaX, const double& aDeltaY, const int32_t& aModifierFlags,
+ const uint64_t& aObserverId) {
+ NS_ENSURE_TRUE(xpc::IsInAutomation(), IPC_FAIL(this, "Unexpected event"));
+
+ AutoSynthesizedEventResponder responder(this, aObserverId,
+ "touchpadpanevent");
+
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (widget) {
+ widget->SynthesizeNativeTouchpadPan(aEventPhase, aPoint, aDeltaX, aDeltaY,
+ aModifierFlags,
+ responder.GetObserver());
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvLockNativePointer() {
+ if (nsCOMPtr<nsIWidget> widget = GetWidget()) {
+ mLockedNativePointer = true; // do before updating the center
+ UpdateNativePointerLockCenter(widget);
+ widget->LockNativePointer();
+ }
+ return IPC_OK();
+}
+
+void BrowserParent::UnlockNativePointer() {
+ if (!mLockedNativePointer) {
+ return;
+ }
+ if (nsCOMPtr<nsIWidget> widget = GetWidget()) {
+ widget->UnlockNativePointer();
+ mLockedNativePointer = false;
+ }
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvUnlockNativePointer() {
+ UnlockNativePointer();
+ return IPC_OK();
+}
+
+void BrowserParent::SendRealKeyEvent(WidgetKeyboardEvent& aEvent) {
+ if (mIsDestroyed || !mIsReadyToHandleInputEvents) {
+ return;
+ }
+ aEvent.mRefPoint = TransformParentToChild(aEvent.mRefPoint);
+
+ // NOTE: If you call `InitAllEditCommands()` for the other messages too,
+ // you also need to update
+ // TextEventDispatcher::DispatchKeyboardEventInternal().
+ if (aEvent.mMessage == eKeyPress) {
+ // If current input context is editable, the edit commands are initialized
+ // by TextEventDispatcher::DispatchKeyboardEventInternal(). Otherwise,
+ // we need to do it here (they are not necessary for the parent process,
+ // therefore, we need to do it here for saving the runtime cost).
+ if (!aEvent.AreAllEditCommandsInitialized()) {
+ // XXX Is it good thing that the keypress event will be handled in an
+ // editor even though the user pressed the key combination before the
+ // focus change has not been completed in the parent process yet or
+ // focus change will happen? If no, we can stop doing this.
+ Maybe<WritingMode> writingMode;
+ if (aEvent.mWidget) {
+ if (RefPtr<widget::TextEventDispatcher> dispatcher =
+ aEvent.mWidget->GetTextEventDispatcher()) {
+ writingMode = dispatcher->MaybeQueryWritingModeAtSelection();
+ }
+ }
+ aEvent.InitAllEditCommands(writingMode);
+ }
+ } else {
+ aEvent.PreventNativeKeyBindings();
+ }
+ SentKeyEventData sendKeyEventData{
+ aEvent.mKeyCode, aEvent.mCharCode, aEvent.mPseudoCharCode,
+ aEvent.mKeyNameIndex, aEvent.mCodeNameIndex, aEvent.mModifiers,
+ nsID::GenerateUUID()};
+ const bool ok =
+ Manager()->IsInputPriorityEventEnabled()
+ ? PBrowserParent::SendRealKeyEvent(aEvent, sendKeyEventData.mUUID)
+ : PBrowserParent::SendNormalPriorityRealKeyEvent(
+ aEvent, sendKeyEventData.mUUID);
+
+ NS_WARNING_ASSERTION(ok, "PBrowserParent::SendRealKeyEvent() failed");
+ MOZ_ASSERT(!ok || aEvent.HasBeenPostedToRemoteProcess());
+ if (ok && aEvent.IsWaitingReplyFromRemoteProcess()) {
+ mWaitingReplyKeyboardEvents.AppendElement(sendKeyEventData);
+ }
+}
+
+void BrowserParent::SendRealTouchEvent(WidgetTouchEvent& aEvent) {
+ if (mIsDestroyed || !mIsReadyToHandleInputEvents) {
+ return;
+ }
+
+ // PresShell::HandleEventInternal adds touches on touch end/cancel. This
+ // confuses remote content and the panning and zooming logic into thinking
+ // that the added touches are part of the touchend/cancel, when actually
+ // they're not.
+ if (aEvent.mMessage == eTouchEnd || aEvent.mMessage == eTouchCancel) {
+ aEvent.mTouches.RemoveElementsBy(
+ [](const auto& touch) { return !touch->mChanged; });
+ }
+
+ APZData apzData;
+ ApzAwareEventRoutingToChild(&apzData.guid, &apzData.blockId,
+ &apzData.apzResponse);
+
+ if (mIsDestroyed) {
+ return;
+ }
+
+ for (uint32_t i = 0; i < aEvent.mTouches.Length(); i++) {
+ aEvent.mTouches[i]->mRefPoint =
+ TransformParentToChild(aEvent.mTouches[i]->mRefPoint);
+ }
+
+ static uint32_t sConsecutiveTouchMoveCount = 0;
+ if (aEvent.mMessage == eTouchMove) {
+ ++sConsecutiveTouchMoveCount;
+ SendRealTouchMoveEvent(aEvent, apzData, sConsecutiveTouchMoveCount);
+ return;
+ }
+
+ sConsecutiveTouchMoveCount = 0;
+ DebugOnly<bool> ret =
+ Manager()->IsInputPriorityEventEnabled()
+ ? PBrowserParent::SendRealTouchEvent(
+ aEvent, apzData.guid, apzData.blockId, apzData.apzResponse)
+ : PBrowserParent::SendNormalPriorityRealTouchEvent(
+ aEvent, apzData.guid, apzData.blockId, apzData.apzResponse);
+
+ NS_WARNING_ASSERTION(ret, "PBrowserParent::SendRealTouchEvent() failed");
+ MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess());
+}
+
+void BrowserParent::SendRealTouchMoveEvent(
+ WidgetTouchEvent& aEvent, APZData& aAPZData,
+ uint32_t aConsecutiveTouchMoveCount) {
+ // Touchmove handling is complicated, since IPC compression should be used
+ // only when there are consecutive touch objects for the same touch on the
+ // same BrowserParent. IPC compression can be disabled by switching to
+ // different IPC message.
+ static bool sIPCMessageType1 = true;
+ static TabId sLastTargetBrowserParent(0);
+ static Maybe<APZData> sPreviousAPZData;
+ // Artificially limit max touch points to 10. That should be in practise
+ // more than enough.
+ const uint32_t kMaxTouchMoveIdentifiers = 10;
+ static Maybe<int32_t> sLastTouchMoveIdentifiers[kMaxTouchMoveIdentifiers];
+
+ // Returns true if aIdentifiers contains all the touches in
+ // sLastTouchMoveIdentifiers.
+ auto LastTouchMoveIdentifiersContainedIn =
+ [&](const nsTArray<int32_t>& aIdentifiers) -> bool {
+ for (Maybe<int32_t>& entry : sLastTouchMoveIdentifiers) {
+ if (entry.isSome() && !aIdentifiers.Contains(entry.value())) {
+ return false;
+ }
+ }
+ return true;
+ };
+
+ // Cache touch identifiers in sLastTouchMoveIdentifiers array to be used
+ // when checking whether compression can be done for the next touchmove.
+ auto SetLastTouchMoveIdentifiers =
+ [&](const nsTArray<int32_t>& aIdentifiers) {
+ for (Maybe<int32_t>& entry : sLastTouchMoveIdentifiers) {
+ entry.reset();
+ }
+
+ MOZ_ASSERT(aIdentifiers.Length() <= kMaxTouchMoveIdentifiers);
+ for (uint32_t j = 0; j < aIdentifiers.Length(); ++j) {
+ sLastTouchMoveIdentifiers[j].emplace(aIdentifiers[j]);
+ }
+ };
+
+ AutoTArray<int32_t, kMaxTouchMoveIdentifiers> changedTouches;
+ bool preventCompression = !StaticPrefs::dom_events_compress_touchmove() ||
+ // Ensure the very first touchmove isn't overridden
+ // by the second one, so that web pages can get
+ // accurate coordinates for the first touchmove.
+ aConsecutiveTouchMoveCount < 3 ||
+ sPreviousAPZData.isNothing() ||
+ sPreviousAPZData.value() != aAPZData ||
+ sLastTargetBrowserParent != GetTabId() ||
+ aEvent.mTouches.Length() > kMaxTouchMoveIdentifiers;
+
+ if (!preventCompression) {
+ for (RefPtr<Touch>& touch : aEvent.mTouches) {
+ if (touch->mChanged) {
+ changedTouches.AppendElement(touch->mIdentifier);
+ }
+ }
+
+ // Prevent compression if the new event has fewer or different touches
+ // than the old one.
+ preventCompression = !LastTouchMoveIdentifiersContainedIn(changedTouches);
+ }
+
+ if (preventCompression) {
+ sIPCMessageType1 = !sIPCMessageType1;
+ }
+
+ // Update the last touch move identifiers always, so that when the next
+ // event comes in, the new identifiers can be compared to the old ones.
+ // If the pref is disabled, this just does a quick small loop.
+ SetLastTouchMoveIdentifiers(changedTouches);
+ sPreviousAPZData.reset();
+ sPreviousAPZData.emplace(aAPZData);
+ sLastTargetBrowserParent = GetTabId();
+
+ DebugOnly<bool> ret = true;
+ if (sIPCMessageType1) {
+ ret =
+ Manager()->IsInputPriorityEventEnabled()
+ ? PBrowserParent::SendRealTouchMoveEvent(
+ aEvent, aAPZData.guid, aAPZData.blockId, aAPZData.apzResponse)
+ : PBrowserParent::SendNormalPriorityRealTouchMoveEvent(
+ aEvent, aAPZData.guid, aAPZData.blockId,
+ aAPZData.apzResponse);
+ } else {
+ ret =
+ Manager()->IsInputPriorityEventEnabled()
+ ? PBrowserParent::SendRealTouchMoveEvent2(
+ aEvent, aAPZData.guid, aAPZData.blockId, aAPZData.apzResponse)
+ : PBrowserParent::SendNormalPriorityRealTouchMoveEvent2(
+ aEvent, aAPZData.guid, aAPZData.blockId,
+ aAPZData.apzResponse);
+ }
+
+ NS_WARNING_ASSERTION(ret, "PBrowserParent::SendRealTouchMoveEvent() failed");
+ MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess());
+}
+
+bool BrowserParent::SendHandleTap(
+ TapType aType, const LayoutDevicePoint& aPoint, Modifiers aModifiers,
+ const ScrollableLayerGuid& aGuid, uint64_t aInputBlockId,
+ const Maybe<DoubleTapToZoomMetrics>& aDoubleTapToZoomMetrics) {
+ if (mIsDestroyed || !mIsReadyToHandleInputEvents) {
+ return false;
+ }
+ if ((aType == TapType::eSingleTap || aType == TapType::eSecondTap)) {
+ if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
+ if (RefPtr<nsFrameLoader> frameLoader = GetFrameLoader()) {
+ if (RefPtr<Element> element = frameLoader->GetOwnerContent()) {
+ fm->SetFocus(element, nsIFocusManager::FLAG_BYMOUSE |
+ nsIFocusManager::FLAG_BYTOUCH |
+ nsIFocusManager::FLAG_NOSCROLL);
+ }
+ }
+ }
+ }
+ return Manager()->IsInputPriorityEventEnabled()
+ ? PBrowserParent::SendHandleTap(
+ aType, TransformParentToChild(aPoint), aModifiers, aGuid,
+ aInputBlockId, aDoubleTapToZoomMetrics)
+ : PBrowserParent::SendNormalPriorityHandleTap(
+ aType, TransformParentToChild(aPoint), aModifiers, aGuid,
+ aInputBlockId, aDoubleTapToZoomMetrics);
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvSyncMessage(
+ const nsString& aMessage, const ClonedMessageData& aData,
+ nsTArray<StructuredCloneData>* aRetVal) {
+ AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING("BrowserParent::RecvSyncMessage",
+ OTHER, aMessage);
+ MMPrinter::Print("BrowserParent::RecvSyncMessage", aMessage, aData);
+
+ StructuredCloneData data;
+ ipc::UnpackClonedMessageData(aData, data);
+
+ if (!ReceiveMessage(aMessage, true, &data, aRetVal)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvAsyncMessage(
+ const nsString& aMessage, const ClonedMessageData& aData) {
+ AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING("BrowserParent::RecvAsyncMessage",
+ OTHER, aMessage);
+ MMPrinter::Print("BrowserParent::RecvAsyncMessage", aMessage, aData);
+
+ StructuredCloneData data;
+ ipc::UnpackClonedMessageData(aData, data);
+
+ if (!ReceiveMessage(aMessage, false, &data, nullptr)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvSetCursor(
+ const nsCursor& aCursor, const bool& aHasCustomCursor,
+ Maybe<BigBuffer>&& aCursorData, const uint32_t& aWidth,
+ const uint32_t& aHeight, const float& aResolutionX,
+ const float& aResolutionY, const uint32_t& aStride,
+ const gfx::SurfaceFormat& aFormat, const uint32_t& aHotspotX,
+ const uint32_t& aHotspotY, const bool& aForce) {
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (!widget) {
+ return IPC_OK();
+ }
+
+ if (aForce) {
+ widget->ClearCachedCursor();
+ }
+
+ nsCOMPtr<imgIContainer> cursorImage;
+ if (aHasCustomCursor) {
+ const bool cursorDataValid = [&] {
+ if (!aCursorData) {
+ return false;
+ }
+ auto expectedSize = CheckedInt<uint32_t>(aHeight) * aStride;
+ if (!expectedSize.isValid() ||
+ expectedSize.value() != aCursorData->Size()) {
+ return false;
+ }
+ auto minStride =
+ CheckedInt<uint32_t>(aWidth) * gfx::BytesPerPixel(aFormat);
+ if (!minStride.isValid() || aStride < minStride.value()) {
+ return false;
+ }
+ return true;
+ }();
+ if (!cursorDataValid) {
+ return IPC_FAIL(this, "Invalid custom cursor data");
+ }
+ const gfx::IntSize size(aWidth, aHeight);
+ RefPtr<gfx::DataSourceSurface> customCursor =
+ gfx::CreateDataSourceSurfaceFromData(size, aFormat, aCursorData->Data(),
+ aStride);
+
+ RefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(customCursor, size);
+ cursorImage = image::ImageOps::CreateFromDrawable(drawable);
+ }
+
+ mCursor = nsIWidget::Cursor{aCursor,
+ std::move(cursorImage),
+ aHotspotX,
+ aHotspotY,
+ {aResolutionX, aResolutionY}};
+ if (!mRemoteTargetSetsCursor) {
+ return IPC_OK();
+ }
+
+ widget->SetCursor(mCursor);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvSetLinkStatus(
+ const nsString& aStatus) {
+ nsCOMPtr<nsIXULBrowserWindow> xulBrowserWindow = GetXULBrowserWindow();
+ if (!xulBrowserWindow) {
+ return IPC_OK();
+ }
+
+ xulBrowserWindow->SetOverLink(aStatus);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvShowTooltip(
+ const uint32_t& aX, const uint32_t& aY, const nsString& aTooltip,
+ const nsString& aDirection) {
+ nsCOMPtr<nsIXULBrowserWindow> xulBrowserWindow = GetXULBrowserWindow();
+ if (!xulBrowserWindow) {
+ return IPC_OK();
+ }
+
+ // ShowTooltip will end up accessing XULElement properties in JS (specifically
+ // BoxObject). However, to get it to JS, we need to make sure we're a
+ // nsFrameLoaderOwner, which implies we're a XULFrameElement. We can then
+ // safely pass Element into JS.
+ RefPtr<nsFrameLoaderOwner> flo = do_QueryObject(mFrameElement);
+ if (!flo) return IPC_OK();
+
+ nsCOMPtr<Element> el = do_QueryInterface(flo);
+ if (!el) return IPC_OK();
+
+ if (NS_SUCCEEDED(
+ xulBrowserWindow->ShowTooltip(aX, aY, aTooltip, aDirection, el))) {
+ mShowingTooltip = true;
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvHideTooltip() {
+ mShowingTooltip = false;
+
+ nsCOMPtr<nsIXULBrowserWindow> xulBrowserWindow = GetXULBrowserWindow();
+ if (!xulBrowserWindow) {
+ return IPC_OK();
+ }
+
+ xulBrowserWindow->HideTooltip();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvNotifyIMEFocus(
+ const ContentCache& aContentCache, const IMENotification& aIMENotification,
+ NotifyIMEFocusResolver&& aResolve) {
+ if (mIsDestroyed) {
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsIWidget> widget = GetTextInputHandlingWidget();
+ if (!widget) {
+ aResolve(IMENotificationRequests());
+ return IPC_OK();
+ }
+ if (NS_WARN_IF(!aContentCache.IsValid())) {
+ return IPC_FAIL(this, "Invalid content cache data");
+ }
+ mContentCache.AssignContent(aContentCache, widget, &aIMENotification);
+ IMEStateManager::NotifyIME(aIMENotification, widget, this);
+
+ IMENotificationRequests requests;
+ if (aIMENotification.mMessage == NOTIFY_IME_OF_FOCUS) {
+ requests = widget->IMENotificationRequestsRef();
+ }
+ aResolve(requests);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvNotifyIMETextChange(
+ const ContentCache& aContentCache,
+ const IMENotification& aIMENotification) {
+ nsCOMPtr<nsIWidget> widget = GetTextInputHandlingWidget();
+ if (!widget || !IMEStateManager::DoesBrowserParentHaveIMEFocus(this)) {
+ return IPC_OK();
+ }
+ if (NS_WARN_IF(!aContentCache.IsValid())) {
+ return IPC_FAIL(this, "Invalid content cache data");
+ }
+ mContentCache.AssignContent(aContentCache, widget, &aIMENotification);
+ mContentCache.MaybeNotifyIME(widget, aIMENotification);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvNotifyIMECompositionUpdate(
+ const ContentCache& aContentCache,
+ const IMENotification& aIMENotification) {
+ nsCOMPtr<nsIWidget> widget = GetTextInputHandlingWidget();
+ if (!widget || !IMEStateManager::DoesBrowserParentHaveIMEFocus(this)) {
+ return IPC_OK();
+ }
+ if (NS_WARN_IF(!aContentCache.IsValid())) {
+ return IPC_FAIL(this, "Invalid content cache data");
+ }
+ mContentCache.AssignContent(aContentCache, widget, &aIMENotification);
+ mContentCache.MaybeNotifyIME(widget, aIMENotification);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvNotifyIMESelection(
+ const ContentCache& aContentCache,
+ const IMENotification& aIMENotification) {
+ nsCOMPtr<nsIWidget> widget = GetTextInputHandlingWidget();
+ if (!widget || !IMEStateManager::DoesBrowserParentHaveIMEFocus(this)) {
+ return IPC_OK();
+ }
+ if (NS_WARN_IF(!aContentCache.IsValid())) {
+ return IPC_FAIL(this, "Invalid content cache data");
+ }
+ mContentCache.AssignContent(aContentCache, widget, &aIMENotification);
+ mContentCache.MaybeNotifyIME(widget, aIMENotification);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvUpdateContentCache(
+ const ContentCache& aContentCache) {
+ nsCOMPtr<nsIWidget> widget = GetTextInputHandlingWidget();
+ if (!widget || !IMEStateManager::DoesBrowserParentHaveIMEFocus(this)) {
+ return IPC_OK();
+ }
+ if (NS_WARN_IF(!aContentCache.IsValid())) {
+ return IPC_FAIL(this, "Invalid content cache data");
+ }
+ mContentCache.AssignContent(aContentCache, widget);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvNotifyIMEMouseButtonEvent(
+ const IMENotification& aIMENotification, bool* aConsumedByIME) {
+ nsCOMPtr<nsIWidget> widget = GetTextInputHandlingWidget();
+ if (!widget || !IMEStateManager::DoesBrowserParentHaveIMEFocus(this)) {
+ *aConsumedByIME = false;
+ return IPC_OK();
+ }
+ nsresult rv = IMEStateManager::NotifyIME(aIMENotification, widget, this);
+ *aConsumedByIME = rv == NS_SUCCESS_EVENT_CONSUMED;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvNotifyIMEPositionChange(
+ const ContentCache& aContentCache,
+ const IMENotification& aIMENotification) {
+ nsCOMPtr<nsIWidget> widget = GetTextInputHandlingWidget();
+ if (!widget || !IMEStateManager::DoesBrowserParentHaveIMEFocus(this)) {
+ return IPC_OK();
+ }
+ if (NS_WARN_IF(!aContentCache.IsValid())) {
+ return IPC_FAIL(this, "Invalid content cache data");
+ }
+ mContentCache.AssignContent(aContentCache, widget, &aIMENotification);
+ mContentCache.MaybeNotifyIME(widget, aIMENotification);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvOnEventNeedingAckHandled(
+ const EventMessage& aMessage, const uint32_t& aCompositionId) {
+ // This is called when the child process receives WidgetCompositionEvent or
+ // WidgetSelectionEvent.
+ // FYI: Don't check if widget is nullptr here because it's more important to
+ // notify mContentCahce of this than handling something in it.
+ nsCOMPtr<nsIWidget> widget = GetTextInputHandlingWidget();
+
+ // While calling OnEventNeedingAckHandled(), BrowserParent *might* be
+ // destroyed since it may send notifications to IME.
+ RefPtr<BrowserParent> kungFuDeathGrip(this);
+ mContentCache.OnEventNeedingAckHandled(widget, aMessage, aCompositionId);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvRequestFocus(
+ const bool& aCanRaise, const CallerType aCallerType) {
+ LOGBROWSERFOCUS(("RecvRequestFocus %p, aCanRaise: %d", this, aCanRaise));
+ if (BrowserBridgeParent* bridgeParent = GetBrowserBridgeParent()) {
+ mozilla::Unused << bridgeParent->SendRequestFocus(aCanRaise, aCallerType);
+ return IPC_OK();
+ }
+
+ if (!mFrameElement) {
+ return IPC_OK();
+ }
+
+ nsContentUtils::RequestFrameFocus(*mFrameElement, aCanRaise, aCallerType);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvWheelZoomChange(bool aIncrease) {
+ RefPtr<BrowsingContext> bc = GetBrowsingContext();
+ if (!bc) {
+ return IPC_OK();
+ }
+
+ bc->Canonical()->DispatchWheelZoomChange(aIncrease);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvEnableDisableCommands(
+ const MaybeDiscarded<BrowsingContext>& aContext, const nsString& aAction,
+ nsTArray<nsCString>&& aEnabledCommands,
+ nsTArray<nsCString>&& aDisabledCommands) {
+ if (aContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsIBrowserController> browserController = do_QueryActor(
+ "Controllers", aContext.get_canonical()->GetCurrentWindowGlobal());
+ if (browserController) {
+ browserController->EnableDisableCommands(aAction, aEnabledCommands,
+ aDisabledCommands);
+ }
+
+ return IPC_OK();
+}
+
+LayoutDeviceIntPoint BrowserParent::TransformPoint(
+ const LayoutDeviceIntPoint& aPoint,
+ const LayoutDeviceToLayoutDeviceMatrix4x4& aMatrix) {
+ LayoutDevicePoint floatPoint(aPoint);
+ LayoutDevicePoint floatTransformed = TransformPoint(floatPoint, aMatrix);
+ // The next line loses precision if an out-of-process iframe
+ // has been scaled or rotated.
+ return RoundedToInt(floatTransformed);
+}
+
+LayoutDevicePoint BrowserParent::TransformPoint(
+ const LayoutDevicePoint& aPoint,
+ const LayoutDeviceToLayoutDeviceMatrix4x4& aMatrix) {
+ return aMatrix.TransformPoint(aPoint);
+}
+
+LayoutDeviceIntPoint BrowserParent::TransformParentToChild(
+ const WidgetMouseEvent& aEvent) {
+ MOZ_ASSERT(aEvent.mWidget);
+
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (widget && widget != aEvent.mWidget) {
+ return TransformParentToChild(
+ aEvent.mRefPoint +
+ nsLayoutUtils::WidgetToWidgetOffset(aEvent.mWidget, widget));
+ }
+ return TransformParentToChild(aEvent.mRefPoint);
+}
+
+LayoutDeviceIntPoint BrowserParent::TransformParentToChild(
+ const LayoutDeviceIntPoint& aPoint) {
+ LayoutDeviceToLayoutDeviceMatrix4x4 matrix =
+ GetChildToParentConversionMatrix();
+ if (!matrix.Invert()) {
+ return LayoutDeviceIntPoint();
+ }
+ auto transformed = UntransformBy(matrix, aPoint);
+ if (!transformed) {
+ return LayoutDeviceIntPoint();
+ }
+ return transformed.ref();
+}
+
+LayoutDevicePoint BrowserParent::TransformParentToChild(
+ const LayoutDevicePoint& aPoint) {
+ LayoutDeviceToLayoutDeviceMatrix4x4 matrix =
+ GetChildToParentConversionMatrix();
+ if (!matrix.Invert()) {
+ return LayoutDevicePoint();
+ }
+ auto transformed = UntransformBy(matrix, aPoint);
+ if (!transformed) {
+ return LayoutDeviceIntPoint();
+ }
+ return transformed.ref();
+}
+
+LayoutDeviceIntPoint BrowserParent::TransformChildToParent(
+ const LayoutDeviceIntPoint& aPoint) {
+ return TransformPoint(aPoint, GetChildToParentConversionMatrix());
+}
+
+LayoutDevicePoint BrowserParent::TransformChildToParent(
+ const LayoutDevicePoint& aPoint) {
+ return TransformPoint(aPoint, GetChildToParentConversionMatrix());
+}
+
+LayoutDeviceIntRect BrowserParent::TransformChildToParent(
+ const LayoutDeviceIntRect& aRect) {
+ LayoutDeviceToLayoutDeviceMatrix4x4 matrix =
+ GetChildToParentConversionMatrix();
+ LayoutDeviceRect floatRect(aRect);
+ // The outcome is not ideal if an out-of-process iframe has been rotated
+ LayoutDeviceRect floatTransformed = matrix.TransformBounds(floatRect);
+ // The next line loses precision if an out-of-process iframe
+ // has been scaled or rotated.
+ return RoundedToInt(floatTransformed);
+}
+
+LayoutDeviceToLayoutDeviceMatrix4x4
+BrowserParent::GetChildToParentConversionMatrix() {
+ if (mChildToParentConversionMatrix) {
+ return *mChildToParentConversionMatrix;
+ }
+ LayoutDevicePoint offset(-GetChildProcessOffset());
+ return LayoutDeviceToLayoutDeviceMatrix4x4::Translation(offset);
+}
+
+void BrowserParent::SetChildToParentConversionMatrix(
+ const Maybe<LayoutDeviceToLayoutDeviceMatrix4x4>& aMatrix,
+ const ScreenRect& aRemoteDocumentRect) {
+ if (mChildToParentConversionMatrix == aMatrix &&
+ mRemoteDocumentRect.isSome() &&
+ mRemoteDocumentRect.value() == aRemoteDocumentRect) {
+ return;
+ }
+
+ mChildToParentConversionMatrix = aMatrix;
+ mRemoteDocumentRect = Some(aRemoteDocumentRect);
+ if (mIsDestroyed) {
+ return;
+ }
+ mozilla::Unused << SendChildToParentMatrix(ToUnknownMatrix(aMatrix),
+ aRemoteDocumentRect);
+}
+
+LayoutDeviceIntPoint BrowserParent::GetChildProcessOffset() {
+ // The "toplevel widget" in child processes is always at position
+ // 0,0. Map the event coordinates to match that.
+
+ LayoutDeviceIntPoint offset(0, 0);
+ RefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
+ if (!frameLoader) {
+ return offset;
+ }
+ nsIFrame* targetFrame = frameLoader->GetPrimaryFrameOfOwningContent();
+ if (!targetFrame) {
+ return offset;
+ }
+
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (!widget) {
+ return offset;
+ }
+
+ nsPresContext* presContext = targetFrame->PresContext();
+ nsIFrame* rootFrame = presContext->PresShell()->GetRootFrame();
+ nsView* rootView = rootFrame ? rootFrame->GetView() : nullptr;
+ if (!rootView) {
+ return offset;
+ }
+
+ // Note that we don't want to take into account transforms here:
+#if 0
+ nsPoint pt(0, 0);
+ nsLayoutUtils::TransformPoint(targetFrame, rootFrame, pt);
+#endif
+ // In practice, when transforms are applied to this frameLoader, we currently
+ // get the wrong results whether we take transforms into account here or not.
+ // But applying transforms here gives us the wrong results in all
+ // circumstances when transforms are applied, unless they're purely
+ // translational. It also gives us the wrong results whenever CSS transitions
+ // are used to apply transforms, since the offeets aren't updated as the
+ // transition is animated.
+ //
+ // What we actually need to do is apply the transforms to the coordinates of
+ // any events we send to the child, and reverse them for any screen
+ // coordinates that we retrieve from the child.
+
+ // TODO: Once we take into account transforms here, set viewportType
+ // correctly. For now we use Visual as this means we don't apply
+ // the layout-to-visual transform in TranslateViewToWidget().
+ ViewportType viewportType = ViewportType::Visual;
+
+ nsPoint pt = targetFrame->GetOffsetTo(rootFrame);
+ return -nsLayoutUtils::TranslateViewToWidget(presContext, rootView, pt,
+ viewportType, widget);
+}
+
+LayoutDeviceIntPoint BrowserParent::GetClientOffset() {
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ nsCOMPtr<nsIWidget> docWidget = GetDocWidget();
+
+ if (widget == docWidget) {
+ return widget->GetClientOffset();
+ }
+
+ return (docWidget->GetClientOffset() +
+ nsLayoutUtils::WidgetToWidgetOffset(widget, docWidget));
+}
+
+void BrowserParent::StopIMEStateManagement() {
+ if (mIsDestroyed) {
+ return;
+ }
+ Unused << SendStopIMEStateManagement();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvReplyKeyEvent(
+ const WidgetKeyboardEvent& aEvent, const nsID& aUUID) {
+ NS_ENSURE_TRUE(mFrameElement, IPC_OK());
+
+ // First, verify aEvent is what we've sent to a remote process.
+ Maybe<size_t> index = [&]() -> Maybe<size_t> {
+ for (const size_t i : IntegerRange(mWaitingReplyKeyboardEvents.Length())) {
+ const SentKeyEventData& data = mWaitingReplyKeyboardEvents[i];
+ if (data.mUUID.Equals(aUUID)) {
+ if (NS_WARN_IF(data.mKeyCode != aEvent.mKeyCode) ||
+ NS_WARN_IF(data.mCharCode != aEvent.mCharCode) ||
+ NS_WARN_IF(data.mPseudoCharCode != aEvent.mPseudoCharCode) ||
+ NS_WARN_IF(data.mKeyNameIndex != aEvent.mKeyNameIndex) ||
+ NS_WARN_IF(data.mCodeNameIndex != aEvent.mCodeNameIndex) ||
+ NS_WARN_IF(data.mModifiers != aEvent.mModifiers)) {
+ // Got different event data from what we stored before dispatching an
+ // event with the ID.
+ return Nothing();
+ }
+ return Some(i);
+ }
+ }
+ // No entry found.
+ return Nothing();
+ }();
+ if (MOZ_UNLIKELY(index.isNothing())) {
+ return IPC_FAIL(this, "Bogus reply keyboard event");
+ }
+ // Don't discard the older keyboard events because the order may be changed if
+ // the remote process has a event listener which takes too long time and while
+ // the freezing, user may switch the tab, or if the remote process sends
+ // synchronous XMLHttpRequest.
+ mWaitingReplyKeyboardEvents.RemoveElementAt(*index);
+
+ // If the event propagation was stopped by the child, it means that the event
+ // was ignored in the child. In the case, we should ignore it too because the
+ // focused web app didn't have a chance to prevent its default.
+ if (aEvent.PropagationStopped()) {
+ return IPC_OK();
+ }
+
+ WidgetKeyboardEvent localEvent(aEvent);
+ localEvent.MarkAsHandledInRemoteProcess();
+
+ // Here we convert the WidgetEvent that we received to an Event
+ // to be able to dispatch it to the <browser> element as the target element.
+ RefPtr<nsPresContext> presContext =
+ mFrameElement->OwnerDoc()->GetPresContext();
+ NS_ENSURE_TRUE(presContext, IPC_OK());
+
+ AutoHandlingUserInputStatePusher userInpStatePusher(localEvent.IsTrusted(),
+ &localEvent);
+
+ nsEventStatus status = nsEventStatus_eIgnore;
+
+ // Handle access key in this process before dispatching reply event because
+ // ESM handles it before dispatching the event to the DOM tree.
+ if (localEvent.mMessage == eKeyPress &&
+ (localEvent.ModifiersMatchWithAccessKey(AccessKeyType::eChrome) ||
+ localEvent.ModifiersMatchWithAccessKey(AccessKeyType::eContent))) {
+ RefPtr<EventStateManager> esm = presContext->EventStateManager();
+ AutoTArray<uint32_t, 10> accessCharCodes;
+ localEvent.GetAccessKeyCandidates(accessCharCodes);
+ if (esm->HandleAccessKey(&localEvent, presContext, accessCharCodes)) {
+ status = nsEventStatus_eConsumeNoDefault;
+ }
+ }
+
+ RefPtr<Element> frameElement = mFrameElement;
+ EventDispatcher::Dispatch(frameElement, presContext, &localEvent, nullptr,
+ &status);
+
+ if (!localEvent.DefaultPrevented() &&
+ !localEvent.mFlags.mIsSynthesizedForTests) {
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (widget) {
+ widget->PostHandleKeyEvent(&localEvent);
+ localEvent.StopPropagation();
+ }
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvAccessKeyNotHandled(
+ const WidgetKeyboardEvent& aEvent) {
+ NS_ENSURE_TRUE(mFrameElement, IPC_OK());
+
+ // This is called only when this process had focus and HandleAccessKey
+ // message was posted to all remote process and each remote process didn't
+ // execute any content access keys.
+
+ if (MOZ_UNLIKELY(aEvent.mMessage != eKeyPress || !aEvent.IsTrusted())) {
+ return IPC_FAIL(this, "Called with unexpected event");
+ }
+
+ // If there is no requesting event, the event may have already been handled
+ // when it's returned from another remote process.
+ if (MOZ_UNLIKELY(!RequestingAccessKeyEventData::IsSet())) {
+ return IPC_OK();
+ }
+
+ // If the event does not match with the one which we requested a remote
+ // process to handle access key of (that means that we has already requested
+ // for another key press), we should ignore this call because user focuses
+ // to the last key press.
+ if (MOZ_UNLIKELY(!RequestingAccessKeyEventData::Equals(aEvent))) {
+ return IPC_OK();
+ }
+
+ RequestingAccessKeyEventData::Clear();
+
+ WidgetKeyboardEvent localEvent(aEvent);
+ localEvent.MarkAsHandledInRemoteProcess();
+ localEvent.mMessage = eAccessKeyNotFound;
+
+ // Here we convert the WidgetEvent that we received to an Event
+ // to be able to dispatch it to the <browser> element as the target element.
+ Document* doc = mFrameElement->OwnerDoc();
+ PresShell* presShell = doc->GetPresShell();
+ NS_ENSURE_TRUE(presShell, IPC_OK());
+
+ if (presShell->CanDispatchEvent()) {
+ RefPtr<nsPresContext> presContext = presShell->GetPresContext();
+ NS_ENSURE_TRUE(presContext, IPC_OK());
+
+ RefPtr<Element> frameElement = mFrameElement;
+ EventDispatcher::Dispatch(frameElement, presContext, &localEvent);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvRegisterProtocolHandler(
+ const nsString& aScheme, nsIURI* aHandlerURI, const nsString& aTitle,
+ nsIURI* aDocURI) {
+ nsCOMPtr<nsIWebProtocolHandlerRegistrar> registrar =
+ do_GetService(NS_WEBPROTOCOLHANDLERREGISTRAR_CONTRACTID);
+ if (registrar) {
+ registrar->RegisterProtocolHandler(aScheme, aHandlerURI, aTitle, aDocURI,
+ mFrameElement);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvOnStateChange(
+ const WebProgressData& aWebProgressData, const RequestData& aRequestData,
+ const uint32_t aStateFlags, const nsresult aStatus,
+ const Maybe<WebProgressStateChangeData>& aStateChangeData) {
+ RefPtr<CanonicalBrowsingContext> browsingContext =
+ BrowsingContextForWebProgress(aWebProgressData);
+ if (!browsingContext) {
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsIRequest> request;
+ if (aRequestData.requestURI()) {
+ request = MakeAndAddRef<RemoteWebProgressRequest>(
+ aRequestData.requestURI(), aRequestData.originalRequestURI(),
+ aRequestData.matchedList());
+ }
+
+ if (aStateChangeData.isSome()) {
+ if (!browsingContext->IsTopContent()) {
+ return IPC_FAIL(
+ this,
+ "Unexpected WebProgressStateChangeData for non toplevel webProgress");
+ }
+
+ if (nsCOMPtr<nsIBrowser> browser = GetBrowser()) {
+ Unused << browser->SetIsNavigating(aStateChangeData->isNavigating());
+ Unused << browser->SetMayEnableCharacterEncodingMenu(
+ aStateChangeData->mayEnableCharacterEncodingMenu());
+ Unused << browser->UpdateForStateChange(aStateChangeData->charset(),
+ aStateChangeData->documentURI(),
+ aStateChangeData->contentType());
+ }
+ }
+
+ if (auto* listener = browsingContext->GetWebProgress()) {
+ listener->OnStateChange(listener, request, aStateFlags, aStatus);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvOnProgressChange(
+ const int32_t aCurTotalProgress, const int32_t aMaxTotalProgress) {
+ // We only collect progress change notifications for the toplevel
+ // BrowserParent.
+ // FIXME: In the future, consider merging in progress change information from
+ // oop subframes.
+ if (!GetBrowsingContext()->IsTopContent() ||
+ !GetBrowsingContext()->GetWebProgress()) {
+ return IPC_OK();
+ }
+
+ GetBrowsingContext()->GetWebProgress()->OnProgressChange(
+ nullptr, nullptr, 0, 0, aCurTotalProgress, aMaxTotalProgress);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvOnLocationChange(
+ const WebProgressData& aWebProgressData, const RequestData& aRequestData,
+ nsIURI* aLocation, const uint32_t aFlags, const bool aCanGoBack,
+ const bool aCanGoForward,
+ const Maybe<WebProgressLocationChangeData>& aLocationChangeData) {
+ RefPtr<CanonicalBrowsingContext> browsingContext =
+ BrowsingContextForWebProgress(aWebProgressData);
+ if (!browsingContext) {
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsIRequest> request;
+ if (aRequestData.requestURI()) {
+ request = MakeAndAddRef<RemoteWebProgressRequest>(
+ aRequestData.requestURI(), aRequestData.originalRequestURI(),
+ aRequestData.matchedList());
+ }
+
+ browsingContext->SetCurrentRemoteURI(aLocation);
+
+ nsCOMPtr<nsIBrowser> browser = GetBrowser();
+ if (!mozilla::SessionHistoryInParent() && browser) {
+ Unused << browser->UpdateWebNavigationForLocationChange(aCanGoBack,
+ aCanGoForward);
+ }
+
+ if (aLocationChangeData.isSome()) {
+ if (!browsingContext->IsTopContent()) {
+ return IPC_FAIL(this,
+ "Unexpected WebProgressLocationChangeData for non "
+ "toplevel webProgress");
+ }
+
+ if (browser) {
+ Unused << browser->SetIsNavigating(aLocationChangeData->isNavigating());
+ Unused << browser->UpdateForLocationChange(
+ aLocation, aLocationChangeData->charset(),
+ aLocationChangeData->mayEnableCharacterEncodingMenu(),
+ aLocationChangeData->documentURI(), aLocationChangeData->title(),
+ aLocationChangeData->contentPrincipal(),
+ aLocationChangeData->contentPartitionedPrincipal(),
+ aLocationChangeData->csp(), aLocationChangeData->referrerInfo(),
+ aLocationChangeData->isSyntheticDocument(),
+ aLocationChangeData->requestContextID().isSome(),
+ aLocationChangeData->requestContextID().valueOr(0),
+ aLocationChangeData->contentType());
+ }
+ }
+
+ if (auto* listener = browsingContext->GetWebProgress()) {
+ listener->OnLocationChange(listener, request, aLocation, aFlags);
+ }
+
+ // Since we've now changed Documents, notify the BrowsingContext that we've
+ // changed. Ideally we'd just let the BrowsingContext do this when it changes
+ // the current window global, but that happens before this and we have a lot
+ // of tests that depend on the specific ordering of messages.
+ if (browsingContext->IsTopContent() &&
+ !(aFlags & nsIWebProgressListener::LOCATION_CHANGE_SAME_DOCUMENT)) {
+ browsingContext->UpdateSecurityState();
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvOnStatusChange(
+ const nsString& aMessage) {
+ // We only collect status change notifications for the toplevel
+ // BrowserParent.
+ // FIXME: In the future, consider merging in status change information from
+ // oop subframes.
+ if (!GetBrowsingContext()->IsTopContent() ||
+ !GetBrowsingContext()->GetWebProgress()) {
+ return IPC_OK();
+ }
+
+ GetBrowsingContext()->GetWebProgress()->OnStatusChange(nullptr, nullptr,
+ NS_OK, aMessage.get());
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvNavigationFinished() {
+ nsCOMPtr<nsIBrowser> browser =
+ mFrameElement ? mFrameElement->AsBrowser() : nullptr;
+
+ if (browser) {
+ browser->SetIsNavigating(false);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvNotifyContentBlockingEvent(
+ const uint32_t& aEvent, const RequestData& aRequestData,
+ const bool aBlocked, const nsACString& aTrackingOrigin,
+ nsTArray<nsCString>&& aTrackingFullHashes,
+ const Maybe<
+ mozilla::ContentBlockingNotifier::StorageAccessPermissionGrantedReason>&
+ aReason,
+ const Maybe<mozilla::ContentBlockingNotifier::CanvasFingerprinter>&
+ aCanvasFingerprinter,
+ const Maybe<bool>& aCanvasFingerprinterKnownText) {
+ RefPtr<BrowsingContext> bc = GetBrowsingContext();
+
+ if (!bc || bc->IsDiscarded()) {
+ return IPC_OK();
+ }
+
+ // Get the top-level browsing context.
+ bc = bc->Top();
+ RefPtr<dom::WindowGlobalParent> wgp =
+ bc->Canonical()->GetCurrentWindowGlobal();
+
+ // The WindowGlobalParent would be null while running the test
+ // browser_339445.js. This is unexpected and we will address this in a
+ // following bug. For now, we first workaround this issue.
+ if (!wgp) {
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsIRequest> request = MakeAndAddRef<RemoteWebProgressRequest>(
+ aRequestData.requestURI(), aRequestData.originalRequestURI(),
+ aRequestData.matchedList());
+
+ wgp->NotifyContentBlockingEvent(
+ aEvent, request, aBlocked, aTrackingOrigin, aTrackingFullHashes, aReason,
+ aCanvasFingerprinter, aCanvasFingerprinterKnownText);
+
+ return IPC_OK();
+}
+
+already_AddRefed<nsIBrowser> BrowserParent::GetBrowser() {
+ nsCOMPtr<nsIBrowser> browser;
+ RefPtr<Element> currentElement = mFrameElement;
+
+ // In Responsive Design Mode, mFrameElement will be the <iframe mozbrowser>,
+ // but we want the <xul:browser> that it is embedded in.
+ while (currentElement) {
+ browser = currentElement->AsBrowser();
+ if (browser) {
+ break;
+ }
+
+ BrowsingContext* browsingContext =
+ currentElement->OwnerDoc()->GetBrowsingContext();
+ currentElement =
+ browsingContext ? browsingContext->GetEmbedderElement() : nullptr;
+ }
+
+ return browser.forget();
+}
+
+already_AddRefed<CanonicalBrowsingContext>
+BrowserParent::BrowsingContextForWebProgress(
+ const WebProgressData& aWebProgressData) {
+ // Look up the BrowsingContext which this notification was fired for.
+ if (aWebProgressData.browsingContext().IsNullOrDiscarded()) {
+ NS_WARNING("WebProgress Ignored: BrowsingContext is null or discarded");
+ return nullptr;
+ }
+ RefPtr<CanonicalBrowsingContext> browsingContext =
+ aWebProgressData.browsingContext().get_canonical();
+
+ // Double-check that we actually manage this BrowsingContext, and are not
+ // receiving a malformed or out-of-date request. browsingContext should either
+ // be the toplevel one managed by this BrowserParent, or embedded within a
+ // WindowGlobalParent managed by this BrowserParent.
+ if (browsingContext != mBrowsingContext) {
+ WindowGlobalParent* embedder = browsingContext->GetParentWindowContext();
+ if (!embedder || embedder->GetBrowserParent() != this) {
+ NS_WARNING("WebProgress Ignored: wrong embedder process");
+ return nullptr;
+ }
+ }
+
+ // The current process for this BrowsingContext may have changed since the
+ // notification was fired. Don't fire events for it anymore, as ownership of
+ // the BrowsingContext has been moved elsewhere.
+ if (RefPtr<WindowGlobalParent> current =
+ browsingContext->GetCurrentWindowGlobal();
+ current && current->GetBrowserParent() != this) {
+ NS_WARNING("WebProgress Ignored: no longer current window global");
+ return nullptr;
+ }
+
+ if (RefPtr<BrowsingContextWebProgress> progress =
+ browsingContext->GetWebProgress()) {
+ progress->SetLoadType(aWebProgressData.loadType());
+ }
+
+ return browsingContext.forget();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvIntrinsicSizeOrRatioChanged(
+ const Maybe<IntrinsicSize>& aIntrinsicSize,
+ const Maybe<AspectRatio>& aIntrinsicRatio) {
+ BrowserBridgeParent* bridge = GetBrowserBridgeParent();
+ if (!bridge || !bridge->CanSend()) {
+ return IPC_OK();
+ }
+
+ Unused << bridge->SendIntrinsicSizeOrRatioChanged(aIntrinsicSize,
+ aIntrinsicRatio);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvImageLoadComplete(
+ const nsresult& aResult) {
+ BrowserBridgeParent* bridge = GetBrowserBridgeParent();
+ if (!bridge || !bridge->CanSend()) {
+ return IPC_OK();
+ }
+
+ Unused << bridge->SendImageLoadComplete(aResult);
+
+ return IPC_OK();
+}
+
+bool BrowserParent::HandleQueryContentEvent(WidgetQueryContentEvent& aEvent) {
+ nsCOMPtr<nsIWidget> textInputHandlingWidget = GetTextInputHandlingWidget();
+ if (!textInputHandlingWidget) {
+ return true;
+ }
+ if (!mContentCache.HandleQueryContentEvent(aEvent, textInputHandlingWidget) ||
+ NS_WARN_IF(aEvent.Failed())) {
+ return true;
+ }
+ switch (aEvent.mMessage) {
+ case eQueryTextRect:
+ case eQueryCaretRect:
+ case eQueryEditorRect: {
+ nsCOMPtr<nsIWidget> browserWidget = GetWidget();
+ if (browserWidget != textInputHandlingWidget) {
+ aEvent.mReply->mRect += nsLayoutUtils::WidgetToWidgetOffset(
+ browserWidget, textInputHandlingWidget);
+ }
+ aEvent.mReply->mRect = TransformChildToParent(aEvent.mReply->mRect);
+ break;
+ }
+ default:
+ break;
+ }
+ return true;
+}
+
+bool BrowserParent::SendCompositionEvent(WidgetCompositionEvent& aEvent,
+ uint32_t aCompositionId) {
+ if (mIsDestroyed) {
+ return false;
+ }
+
+ // When the composition is handled in a remote process, we need to handle
+ // commit/cancel result for composition with the composition ID to avoid
+ // to abort newer composition. Therefore, we need to let the remote process
+ // know the composition ID.
+ MOZ_ASSERT(aCompositionId != 0);
+ aEvent.mCompositionId = aCompositionId;
+
+ if (!mContentCache.OnCompositionEvent(aEvent)) {
+ return true;
+ }
+
+ bool ret = Manager()->IsInputPriorityEventEnabled()
+ ? PBrowserParent::SendCompositionEvent(aEvent)
+ : PBrowserParent::SendNormalPriorityCompositionEvent(aEvent);
+ if (NS_WARN_IF(!ret)) {
+ return false;
+ }
+ MOZ_ASSERT(aEvent.HasBeenPostedToRemoteProcess());
+ return true;
+}
+
+bool BrowserParent::SendSelectionEvent(WidgetSelectionEvent& aEvent) {
+ if (mIsDestroyed) {
+ return false;
+ }
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (!widget) {
+ return true;
+ }
+ mContentCache.OnSelectionEvent(aEvent);
+ bool ret = Manager()->IsInputPriorityEventEnabled()
+ ? PBrowserParent::SendSelectionEvent(aEvent)
+ : PBrowserParent::SendNormalPrioritySelectionEvent(aEvent);
+ if (NS_WARN_IF(!ret)) {
+ return false;
+ }
+ MOZ_ASSERT(aEvent.HasBeenPostedToRemoteProcess());
+ aEvent.mSucceeded = true;
+ return true;
+}
+
+bool BrowserParent::SendInsertText(const nsString& aStringToInsert) {
+ if (mIsDestroyed) {
+ return false;
+ }
+ return Manager()->IsInputPriorityEventEnabled()
+ ? PBrowserParent::SendInsertText(aStringToInsert)
+ : PBrowserParent::SendNormalPriorityInsertText(aStringToInsert);
+}
+
+bool BrowserParent::SendPasteTransferable(IPCTransferable&& aTransferable) {
+ return PBrowserParent::SendPasteTransferable(std::move(aTransferable));
+}
+
+/* static */
+void BrowserParent::SetTopLevelWebFocus(BrowserParent* aBrowserParent) {
+ BrowserParent* old = GetFocused();
+ if (aBrowserParent && !aBrowserParent->GetBrowserBridgeParent()) {
+ // top-level Web content
+ sTopLevelWebFocus = aBrowserParent;
+ BrowserParent* bp = UpdateFocus();
+ if (old != bp) {
+ LOGBROWSERFOCUS(
+ ("SetTopLevelWebFocus updated focus; old: %p, new: %p", old, bp));
+ IMEStateManager::OnFocusMovedBetweenBrowsers(old, bp);
+ }
+ }
+}
+
+/* static */
+void BrowserParent::UnsetTopLevelWebFocus(BrowserParent* aBrowserParent) {
+ BrowserParent* old = GetFocused();
+ if (sTopLevelWebFocus == aBrowserParent) {
+ // top-level Web content
+ sTopLevelWebFocus = nullptr;
+ sFocus = nullptr;
+ if (old) {
+ LOGBROWSERFOCUS(
+ ("UnsetTopLevelWebFocus moved focus to chrome; old: %p", old));
+ IMEStateManager::OnFocusMovedBetweenBrowsers(old, nullptr);
+ }
+ }
+}
+
+/* static */
+void BrowserParent::UpdateFocusFromBrowsingContext() {
+ BrowserParent* old = GetFocused();
+ BrowserParent* bp = UpdateFocus();
+ if (old != bp) {
+ LOGBROWSERFOCUS(
+ ("UpdateFocusFromBrowsingContext updated focus; old: %p, new: %p", old,
+ bp));
+ IMEStateManager::OnFocusMovedBetweenBrowsers(old, bp);
+ }
+}
+
+/* static */
+BrowserParent* BrowserParent::UpdateFocus() {
+ if (!sTopLevelWebFocus) {
+ sFocus = nullptr;
+ return nullptr;
+ }
+ nsFocusManager* fm = nsFocusManager::GetFocusManager();
+ if (fm) {
+ BrowsingContext* bc = fm->GetFocusedBrowsingContextInChrome();
+ if (bc) {
+ BrowsingContext* top = bc->Top();
+ MOZ_ASSERT(top, "Should always have a top BrowsingContext.");
+ CanonicalBrowsingContext* canonicalTop = top->Canonical();
+ MOZ_ASSERT(canonicalTop,
+ "Casting to canonical should always be possible in the parent "
+ "process (top case).");
+ WindowGlobalParent* globalTop = canonicalTop->GetCurrentWindowGlobal();
+ if (globalTop) {
+ RefPtr<BrowserParent> globalTopParent = globalTop->GetBrowserParent();
+ if (sTopLevelWebFocus == globalTopParent) {
+ CanonicalBrowsingContext* canonical = bc->Canonical();
+ MOZ_ASSERT(
+ canonical,
+ "Casting to canonical should always be possible in the parent "
+ "process.");
+ WindowGlobalParent* global = canonical->GetCurrentWindowGlobal();
+ if (global) {
+ RefPtr<BrowserParent> parent = global->GetBrowserParent();
+ sFocus = parent;
+ return sFocus;
+ }
+ LOGBROWSERFOCUS(
+ ("Focused BrowsingContext did not have WindowGlobalParent."));
+ }
+ } else {
+ LOGBROWSERFOCUS(
+ ("Top-level BrowsingContext did not have WindowGlobalParent."));
+ }
+ }
+ }
+ sFocus = sTopLevelWebFocus;
+ return sFocus;
+}
+
+/* static */
+void BrowserParent::UnsetTopLevelWebFocusAll() {
+ if (sTopLevelWebFocus) {
+ UnsetTopLevelWebFocus(sTopLevelWebFocus);
+ }
+}
+
+/* static */
+void BrowserParent::UnsetLastMouseRemoteTarget(BrowserParent* aBrowserParent) {
+ if (sLastMouseRemoteTarget == aBrowserParent) {
+ sLastMouseRemoteTarget = nullptr;
+ }
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvRequestIMEToCommitComposition(
+ const bool& aCancel, const uint32_t& aCompositionId, bool* aIsCommitted,
+ nsString* aCommittedString) {
+ nsCOMPtr<nsIWidget> widget = GetTextInputHandlingWidget();
+ if (!widget) {
+ *aIsCommitted = false;
+ return IPC_OK();
+ }
+
+ *aIsCommitted = mContentCache.RequestIMEToCommitComposition(
+ widget, aCancel, aCompositionId, *aCommittedString);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvGetInputContext(
+ widget::IMEState* aState) {
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (!widget) {
+ *aState = widget::IMEState(IMEEnabled::Disabled,
+ IMEState::OPEN_STATE_NOT_SUPPORTED);
+ return IPC_OK();
+ }
+
+ *aState = widget->GetInputContext().mIMEState;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvSetInputContext(
+ const InputContext& aContext, const InputContextAction& aAction) {
+ IMEStateManager::SetInputContextForChildProcess(this, aContext, aAction);
+ return IPC_OK();
+}
+
+bool BrowserParent::ReceiveMessage(const nsString& aMessage, bool aSync,
+ StructuredCloneData* aData,
+ nsTArray<StructuredCloneData>* aRetVal) {
+ // If we're for an oop iframe, don't deliver messages to the wrong place.
+ if (mBrowserBridgeParent) {
+ return true;
+ }
+
+ RefPtr<nsFrameLoader> frameLoader = GetFrameLoader(true);
+ if (frameLoader && frameLoader->GetFrameMessageManager()) {
+ RefPtr<nsFrameMessageManager> manager =
+ frameLoader->GetFrameMessageManager();
+
+ manager->ReceiveMessage(mFrameElement, frameLoader, aMessage, aSync, aData,
+ aRetVal, IgnoreErrors());
+ }
+ return true;
+}
+
+// nsIAuthPromptProvider
+
+// This method is largely copied from nsDocShell::GetAuthPrompt
+NS_IMETHODIMP
+BrowserParent::GetAuthPrompt(uint32_t aPromptReason, const nsIID& iid,
+ void** aResult) {
+ // we're either allowing auth, or it's a proxy request
+ nsresult rv;
+ nsCOMPtr<nsIPromptFactory> wwatch =
+ do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsPIDOMWindowOuter> window;
+ RefPtr<Element> frame = mFrameElement;
+ if (frame) window = frame->OwnerDoc()->GetWindow();
+
+ // Get an auth prompter for our window so that the parenting
+ // of the dialogs works as it should when using tabs.
+ nsCOMPtr<nsISupports> prompt;
+ rv = wwatch->GetPrompt(window, iid, getter_AddRefs(prompt));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsILoginManagerAuthPrompter> prompter = do_QueryInterface(prompt);
+ if (prompter) {
+ prompter->SetBrowser(mFrameElement);
+ }
+
+ *aResult = prompt.forget().take();
+ return NS_OK;
+}
+
+already_AddRefed<PColorPickerParent> BrowserParent::AllocPColorPickerParent(
+ const nsString& aTitle, const nsString& aInitialColor,
+ const nsTArray<nsString>& aDefaultColors) {
+ return MakeAndAddRef<ColorPickerParent>(aTitle, aInitialColor,
+ aDefaultColors);
+}
+
+already_AddRefed<nsFrameLoader> BrowserParent::GetFrameLoader(
+ bool aUseCachedFrameLoaderAfterDestroy) const {
+ if (mIsDestroyed && !aUseCachedFrameLoaderAfterDestroy) {
+ return nullptr;
+ }
+
+ if (mFrameLoader) {
+ RefPtr<nsFrameLoader> fl = mFrameLoader;
+ return fl.forget();
+ }
+ RefPtr<Element> frameElement(mFrameElement);
+ RefPtr<nsFrameLoaderOwner> frameLoaderOwner = do_QueryObject(frameElement);
+ return frameLoaderOwner ? frameLoaderOwner->GetFrameLoader() : nullptr;
+}
+
+void BrowserParent::TryCacheDPIAndScale() {
+ if (mDPI > 0) {
+ return;
+ }
+
+ const auto oldDefaultScale = mDefaultScale;
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ mDPI = widget ? widget->GetDPI() : nsIWidget::GetFallbackDPI();
+ mRounding = widget ? widget->RoundsWidgetCoordinatesTo() : 1;
+ mDefaultScale =
+ widget ? widget->GetDefaultScale() : nsIWidget::GetFallbackDefaultScale();
+
+ if (mDefaultScale != oldDefaultScale) {
+ // The change of the default scale factor will affect the child dimensions
+ // so we need to invalidate it.
+ mUpdatedDimensions = false;
+ }
+}
+
+void BrowserParent::ApzAwareEventRoutingToChild(
+ ScrollableLayerGuid* aOutTargetGuid, uint64_t* aOutInputBlockId,
+ nsEventStatus* aOutApzResponse) {
+ // Let the widget know that the event will be sent to the child process,
+ // which will (hopefully) send a confirmation notice back to APZ.
+ // Do this even if APZ is off since we need it for swipe gesture support on
+ // OS X without APZ.
+ InputAPZContext::SetRoutedToChildProcess();
+
+ if (AsyncPanZoomEnabled()) {
+ if (aOutTargetGuid) {
+ *aOutTargetGuid = InputAPZContext::GetTargetLayerGuid();
+
+ // There may be cases where the APZ hit-testing code came to a different
+ // conclusion than the main-thread hit-testing code as to where the event
+ // is destined. In such cases the layersId of the APZ result may not match
+ // the layersId of this RemoteLayerTreeOwner. In such cases the
+ // main-thread hit- testing code "wins" so we need to update the guid to
+ // reflect this.
+ if (mRemoteLayerTreeOwner.IsInitialized()) {
+ if (aOutTargetGuid->mLayersId != mRemoteLayerTreeOwner.GetLayersId()) {
+ *aOutTargetGuid =
+ ScrollableLayerGuid(mRemoteLayerTreeOwner.GetLayersId(), 0,
+ ScrollableLayerGuid::NULL_SCROLL_ID);
+ }
+ }
+ }
+ if (aOutInputBlockId) {
+ *aOutInputBlockId = InputAPZContext::GetInputBlockId();
+ }
+ if (aOutApzResponse) {
+ *aOutApzResponse = InputAPZContext::GetApzResponse();
+
+ // We can get here without there being an InputAPZContext on the stack
+ // if a non-native event synthesization function (such as
+ // nsIDOMWindowUtils.sendTouchEvent()) was used in the parent process to
+ // synthesize an event that's targeting a content process. Such events do
+ // not go through APZ. Without an InputAPZContext on the stack we pick up
+ // the default value "eSentinel" which cannot be sent over IPC, so replace
+ // it with "eIgnore" instead, which what APZ uses when it ignores an
+ // event. If a caller needs the ability to synthesize a event with a
+ // different APZ response, a native event synthesization function (such as
+ // sendNativeTouchPoint()) can be used.
+ if (*aOutApzResponse == nsEventStatus_eSentinel) {
+ *aOutApzResponse = nsEventStatus_eIgnore;
+ }
+ }
+ } else {
+ if (aOutInputBlockId) {
+ *aOutInputBlockId = 0;
+ }
+ if (aOutApzResponse) {
+ *aOutApzResponse = nsEventStatus_eIgnore;
+ }
+ }
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvRespondStartSwipeEvent(
+ const uint64_t& aInputBlockId, const bool& aStartSwipe) {
+ if (nsCOMPtr<nsIWidget> widget = GetWidget()) {
+ widget->ReportSwipeStarted(aInputBlockId, aStartSwipe);
+ }
+ return IPC_OK();
+}
+
+bool BrowserParent::GetDocShellIsActive() {
+ return mBrowsingContext && mBrowsingContext->IsActive();
+}
+
+bool BrowserParent::GetHasPresented() { return mHasPresented; }
+
+bool BrowserParent::GetHasLayers() { return mHasLayers; }
+
+bool BrowserParent::GetRenderLayers() { return mRenderLayers; }
+
+void BrowserParent::SetRenderLayers(bool aEnabled) {
+ if (aEnabled == mRenderLayers) {
+ return;
+ }
+
+ // Preserve layers means that attempts to stop rendering layers
+ // will be ignored.
+ if (!aEnabled && mIsPreservingLayers) {
+ return;
+ }
+
+ mRenderLayers = aEnabled;
+
+ SetRenderLayersInternal(aEnabled);
+}
+
+void BrowserParent::SetRenderLayersInternal(bool aEnabled) {
+ Unused << SendRenderLayers(aEnabled);
+
+ // Ask the child to repaint/unload layers using the PHangMonitor
+ // channel/thread (which may be less congested).
+ if (aEnabled) {
+ Manager()->PaintTabWhileInterruptingJS(this);
+ } else {
+ Manager()->UnloadLayersWhileInterruptingJS(this);
+ }
+}
+
+bool BrowserParent::GetPriorityHint() { return mPriorityHint; }
+
+void BrowserParent::SetPriorityHint(bool aPriorityHint) {
+ mPriorityHint = aPriorityHint;
+ RecomputeProcessPriority();
+}
+
+void BrowserParent::RecomputeProcessPriority() {
+ auto* bc = GetBrowsingContext();
+ ProcessPriorityManager::BrowserPriorityChanged(
+ bc, bc->IsActive() || mPriorityHint);
+}
+
+void BrowserParent::PreserveLayers(bool aPreserveLayers) {
+ if (mIsPreservingLayers == aPreserveLayers) {
+ return;
+ }
+ mIsPreservingLayers = aPreserveLayers;
+ Unused << SendPreserveLayers(aPreserveLayers);
+}
+
+void BrowserParent::NotifyResolutionChanged() {
+ if (mIsDestroyed) {
+ return;
+ }
+ // TryCacheDPIAndScale()'s cache is keyed off of
+ // mDPI being greater than 0, so this invalidates it.
+ mDPI = -1;
+ TryCacheDPIAndScale();
+ // If mDPI was set to -1 to invalidate it and then TryCacheDPIAndScale
+ // fails to cache the values, then mDefaultScale.scale might be invalid.
+ // We don't want to send that value to content. Just send -1 for it too in
+ // that case.
+ Unused << SendUIResolutionChanged(mDPI, mRounding,
+ mDPI < 0 ? -1.0 : mDefaultScale.scale);
+}
+
+bool BrowserParent::CanCancelContentJS(
+ nsIRemoteTab::NavigationType aNavigationType, int32_t aNavigationIndex,
+ nsIURI* aNavigationURI) const {
+ // Pre-checking if we can cancel content js in the parent is only
+ // supported when session history in the parent is enabled.
+ if (!mozilla::SessionHistoryInParent()) {
+ // If session history in the parent isn't enabled, this check will
+ // be fully done in BrowserChild::CanCancelContentJS
+ return true;
+ }
+
+ nsCOMPtr<nsISHistory> history = mBrowsingContext->GetSessionHistory();
+
+ if (!history) {
+ // If there is no history we can't possibly know if it's ok to
+ // cancel content js.
+ return false;
+ }
+
+ int32_t current;
+ NS_ENSURE_SUCCESS(history->GetIndex(&current), false);
+
+ if (current == -1) {
+ // This tab has no history! Just return.
+ return false;
+ }
+
+ nsCOMPtr<nsISHEntry> entry;
+ NS_ENSURE_SUCCESS(history->GetEntryAtIndex(current, getter_AddRefs(entry)),
+ false);
+
+ nsCOMPtr<nsIURI> currentURI = entry->GetURI();
+ if (!currentURI->SchemeIs("http") && !currentURI->SchemeIs("https") &&
+ !currentURI->SchemeIs("file")) {
+ // Only cancel content JS for http(s) and file URIs. Other URIs are probably
+ // internal and we should just let them run to completion.
+ return false;
+ }
+
+ if (aNavigationType == nsIRemoteTab::NAVIGATE_BACK) {
+ aNavigationIndex = current - 1;
+ } else if (aNavigationType == nsIRemoteTab::NAVIGATE_FORWARD) {
+ aNavigationIndex = current + 1;
+ } else if (aNavigationType == nsIRemoteTab::NAVIGATE_URL) {
+ if (!aNavigationURI) {
+ return false;
+ }
+
+ if (aNavigationURI->SchemeIs("javascript")) {
+ // "javascript:" URIs don't (necessarily) trigger navigation to a
+ // different page, so don't allow the current page's JS to terminate.
+ return false;
+ }
+
+ // If navigating directly to a URL (e.g. via hitting Enter in the location
+ // bar), then we can cancel anytime the next URL is different from the
+ // current, *excluding* the ref ("#").
+ bool equals;
+ NS_ENSURE_SUCCESS(currentURI->EqualsExceptRef(aNavigationURI, &equals),
+ false);
+ return !equals;
+ }
+ // Note: aNavigationType may also be NAVIGATE_INDEX, in which case we don't
+ // need to do anything special.
+
+ int32_t delta = aNavigationIndex > current ? 1 : -1;
+ for (int32_t i = current + delta; i != aNavigationIndex + delta; i += delta) {
+ nsCOMPtr<nsISHEntry> nextEntry;
+ // If `i` happens to be negative, this call will fail (which is what we
+ // would want to happen).
+ NS_ENSURE_SUCCESS(history->GetEntryAtIndex(i, getter_AddRefs(nextEntry)),
+ false);
+
+ nsCOMPtr<nsISHEntry> laterEntry = delta == 1 ? nextEntry : entry;
+ nsCOMPtr<nsIURI> thisURI = entry->GetURI();
+ nsCOMPtr<nsIURI> nextURI = nextEntry->GetURI();
+
+ // If we changed origin and the load wasn't in a subframe, we know it was
+ // a full document load, so we can cancel the content JS safely.
+ if (!laterEntry->GetIsSubFrame()) {
+ nsAutoCString thisHost;
+ NS_ENSURE_SUCCESS(thisURI->GetPrePath(thisHost), false);
+
+ nsAutoCString nextHost;
+ NS_ENSURE_SUCCESS(nextURI->GetPrePath(nextHost), false);
+
+ if (!thisHost.Equals(nextHost)) {
+ return true;
+ }
+ }
+
+ entry = nextEntry;
+ }
+
+ return false;
+}
+
+void BrowserParent::SuppressDisplayport(bool aEnabled) {
+ if (IsDestroyed()) {
+ return;
+ }
+
+#ifdef DEBUG
+ if (aEnabled) {
+ mActiveSupressDisplayportCount++;
+ } else {
+ mActiveSupressDisplayportCount--;
+ }
+ MOZ_ASSERT(mActiveSupressDisplayportCount >= 0);
+#endif
+
+ Unused << SendSuppressDisplayport(aEnabled);
+}
+
+void BrowserParent::NavigateByKey(bool aForward, bool aForDocumentNavigation) {
+ Unused << SendNavigateByKey(aForward, aForDocumentNavigation);
+}
+
+void BrowserParent::LayerTreeUpdate(bool aActive) {
+ if (NS_WARN_IF(mHasLayers == aActive)) {
+ return;
+ }
+ mHasPresented |= aActive;
+ mHasLayers = aActive;
+ if (GetBrowserBridgeParent()) {
+ // Ignore updates if we're an out-of-process iframe. For oop iframes, our
+ // |mFrameElement| is that of the top-level document, and so
+ // AsyncTabSwitcher will treat MozLayerTreeReady / MozLayerTreeCleared
+ // events as if they came from the top-level tab, which is wrong.
+ return;
+ }
+
+ if (mIsDestroyed) {
+ return;
+ }
+
+ RefPtr<Element> frameElement = mFrameElement;
+ if (NS_WARN_IF(!frameElement)) {
+ return;
+ }
+
+ RefPtr<Event> event = NS_NewDOMEvent(frameElement, nullptr, nullptr);
+ if (aActive) {
+ event->InitEvent(u"MozLayerTreeReady"_ns, true, false);
+ } else {
+ event->InitEvent(u"MozLayerTreeCleared"_ns, true, false);
+ }
+ event->SetTrusted(true);
+ event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
+ frameElement->DispatchEvent(*event);
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvRemoteIsReadyToHandleInputEvents() {
+ // 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 the parent that BrowserChild is created and ready to handle input
+ // events.
+ SetReadyToHandleInputEvents();
+ return IPC_OK();
+}
+
+PPaymentRequestParent* BrowserParent::AllocPPaymentRequestParent() {
+ RefPtr<PaymentRequestParent> actor = new PaymentRequestParent();
+ return actor.forget().take();
+}
+
+bool BrowserParent::DeallocPPaymentRequestParent(
+ PPaymentRequestParent* aActor) {
+ RefPtr<PaymentRequestParent> actor =
+ dont_AddRef(static_cast<PaymentRequestParent*>(aActor));
+ return true;
+}
+
+nsresult BrowserParent::HandleEvent(Event* aEvent) {
+ if (mIsDestroyed) {
+ return NS_OK;
+ }
+
+ nsAutoString eventType;
+ aEvent->GetType(eventType);
+ if (eventType.EqualsLiteral("MozUpdateWindowPos") ||
+ eventType.EqualsLiteral("fullscreenchange")) {
+ // Events that signify the window moving are used to update the position
+ // and notify the BrowserChild.
+ return UpdatePosition();
+ }
+ return NS_OK;
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvInvokeDragSession(
+ nsTArray<IPCTransferableData>&& aTransferables, const uint32_t& aAction,
+ Maybe<BigBuffer>&& aVisualDnDData, const uint32_t& aStride,
+ const gfx::SurfaceFormat& aFormat, const LayoutDeviceIntRect& aDragRect,
+ nsIPrincipal* aPrincipal, nsIContentSecurityPolicy* aCsp,
+ const CookieJarSettingsArgs& aCookieJarSettingsArgs,
+ const MaybeDiscarded<WindowContext>& aSourceWindowContext,
+ const MaybeDiscarded<WindowContext>& aSourceTopWindowContext) {
+ PresShell* presShell = mFrameElement->OwnerDoc()->GetPresShell();
+ if (!presShell) {
+ Unused << Manager()->SendEndDragSession(
+ true, true, LayoutDeviceIntPoint(), 0,
+ nsIDragService::DRAGDROP_ACTION_NONE);
+ // Continue sending input events with input priority when stopping the dnd
+ // session.
+ Manager()->SetInputPriorityEventEnabled(true);
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
+ net::CookieJarSettings::Deserialize(aCookieJarSettingsArgs,
+ getter_AddRefs(cookieJarSettings));
+
+ RefPtr<RemoteDragStartData> dragStartData = new RemoteDragStartData(
+ this, std::move(aTransferables), aDragRect, aPrincipal, aCsp,
+ cookieJarSettings, aSourceWindowContext.GetMaybeDiscarded(),
+ aSourceTopWindowContext.GetMaybeDiscarded());
+
+ if (aVisualDnDData) {
+ const auto checkedSize = CheckedInt<size_t>(aDragRect.height) * aStride;
+ if (checkedSize.isValid() &&
+ aVisualDnDData->Size() >= checkedSize.value()) {
+ dragStartData->SetVisualization(gfx::CreateDataSourceSurfaceFromData(
+ gfx::IntSize(aDragRect.width, aDragRect.height), aFormat,
+ aVisualDnDData->Data(), aStride));
+ }
+ }
+
+ nsCOMPtr<nsIDragService> dragService =
+ do_GetService("@mozilla.org/widget/dragservice;1");
+ if (dragService) {
+ dragService->MaybeAddChildProcess(Manager());
+ }
+
+ presShell->GetPresContext()
+ ->EventStateManager()
+ ->BeginTrackingRemoteDragGesture(mFrameElement, dragStartData);
+
+ return IPC_OK();
+}
+
+bool BrowserParent::AsyncPanZoomEnabled() const {
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ return widget && widget->AsyncPanZoomEnabled();
+}
+
+void BrowserParent::StartPersistence(
+ CanonicalBrowsingContext* aContext,
+ nsIWebBrowserPersistDocumentReceiver* aRecv, ErrorResult& aRv) {
+ RefPtr<WebBrowserPersistDocumentParent> actor =
+ new WebBrowserPersistDocumentParent();
+ actor->SetOnReady(aRecv);
+ bool ok = Manager()->SendPWebBrowserPersistDocumentConstructor(actor, this,
+ aContext);
+ if (!ok) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ }
+ // (The actor will be destroyed on constructor failure.)
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvLookUpDictionary(
+ const nsString& aText, nsTArray<FontRange>&& aFontRangeArray,
+ const bool& aIsVertical, const LayoutDeviceIntPoint& aPoint) {
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (!widget) {
+ return IPC_OK();
+ }
+
+ widget->LookUpDictionary(aText, aFontRangeArray, aIsVertical,
+ TransformChildToParent(aPoint));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvShowCanvasPermissionPrompt(
+ const nsCString& aOrigin, const bool& aHideDoorHanger) {
+ nsCOMPtr<nsIBrowser> browser =
+ mFrameElement ? mFrameElement->AsBrowser() : nullptr;
+ if (!browser) {
+ // If the tab is being closed, the browser may not be available.
+ // In this case we can ignore the request.
+ return IPC_OK();
+ }
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ if (!os) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ nsresult rv = os->NotifyObservers(
+ browser,
+ aHideDoorHanger ? "canvas-permissions-prompt-hide-doorhanger"
+ : "canvas-permissions-prompt",
+ NS_ConvertUTF8toUTF16(aOrigin).get());
+ if (NS_FAILED(rv)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvVisitURI(
+ nsIURI* aURI, nsIURI* aLastVisitedURI, const uint32_t& aFlags,
+ const uint64_t& aBrowserId) {
+ if (!aURI) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ RefPtr<nsIWidget> widget = GetWidget();
+ if (NS_WARN_IF(!widget)) {
+ return IPC_OK();
+ }
+ nsCOMPtr<IHistory> history = components::History::Service();
+ if (history) {
+ Unused << history->VisitURI(widget, aURI, aLastVisitedURI, aFlags,
+ aBrowserId);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvQueryVisitedState(
+ nsTArray<RefPtr<nsIURI>>&& aURIs) {
+#ifdef MOZ_ANDROID_HISTORY
+ nsCOMPtr<IHistory> history = components::History::Service();
+ if (NS_WARN_IF(!history)) {
+ return IPC_OK();
+ }
+ RefPtr<nsIWidget> widget = GetWidget();
+ if (NS_WARN_IF(!widget)) {
+ return IPC_OK();
+ }
+
+ // FIXME(emilio): Is this check really needed?
+ for (nsIURI* uri : aURIs) {
+ if (!uri) {
+ return IPC_FAIL(this, "Received null URI");
+ }
+ }
+
+ auto* gvHistory = static_cast<GeckoViewHistory*>(history.get());
+ gvHistory->QueryVisitedState(widget, mManager, std::move(aURIs));
+ return IPC_OK();
+#else
+ return IPC_FAIL(this, "QueryVisitedState is Android-only");
+#endif
+}
+
+void BrowserParent::LiveResizeStarted() { SuppressDisplayport(true); }
+
+void BrowserParent::LiveResizeStopped() { SuppressDisplayport(false); }
+
+void BrowserParent::SetBrowserBridgeParent(BrowserBridgeParent* aBrowser) {
+ // We should either be clearing out our reference to a browser bridge, or not
+ // have either a browser bridge, browser host, or owner content yet.
+ MOZ_ASSERT(!aBrowser ||
+ (!mBrowserBridgeParent && !mBrowserHost && !mFrameElement));
+ mBrowserBridgeParent = aBrowser;
+}
+
+void BrowserParent::SetBrowserHost(BrowserHost* aBrowser) {
+ // We should either be clearing out our reference to a browser host, or not
+ // have either a browser bridge, browser host, or owner content yet.
+ MOZ_ASSERT(!aBrowser ||
+ (!mBrowserBridgeParent && !mBrowserHost && !mFrameElement));
+ mBrowserHost = aBrowser;
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvSetSystemFont(
+ const nsCString& aFontName) {
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (widget) {
+ widget->SetSystemFont(aFontName);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvGetSystemFont(nsCString* aFontName) {
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (widget) {
+ widget->GetSystemFont(*aFontName);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvMaybeFireEmbedderLoadEvents(
+ EmbedderElementEventType aFireEventAtEmbeddingElement) {
+ BrowserBridgeParent* bridge = GetBrowserBridgeParent();
+ if (!bridge) {
+ NS_WARNING("Received `load` event on unbridged BrowserParent!");
+ return IPC_OK();
+ }
+
+ Unused << bridge->SendMaybeFireEmbedderLoadEvents(
+ aFireEventAtEmbeddingElement);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvScrollRectIntoView(
+ const nsRect& aRect, const ScrollAxis& aVertical,
+ const ScrollAxis& aHorizontal, const ScrollFlags& aScrollFlags,
+ const int32_t& aAppUnitsPerDevPixel) {
+ BrowserBridgeParent* bridge = GetBrowserBridgeParent();
+ if (!bridge || !bridge->CanSend()) {
+ return IPC_OK();
+ }
+
+ Unused << bridge->SendScrollRectIntoView(aRect, aVertical, aHorizontal,
+ aScrollFlags, aAppUnitsPerDevPixel);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvIsWindowSupportingProtectedMedia(
+ const uint64_t& aOuterWindowID,
+ IsWindowSupportingProtectedMediaResolver&& aResolve) {
+#ifdef XP_WIN
+ bool isFxrWindow =
+ FxRWindowManager::GetInstance()->IsFxRWindow(aOuterWindowID);
+ aResolve(!isFxrWindow);
+#else
+# ifdef FUZZING_SNAPSHOT
+ return IPC_FAIL(this, "Should only be called on Windows");
+# endif
+ MOZ_CRASH("Should only be called on Windows");
+#endif
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvIsWindowSupportingWebVR(
+ const uint64_t& aOuterWindowID,
+ IsWindowSupportingWebVRResolver&& aResolve) {
+#ifdef XP_WIN
+ bool isFxrWindow =
+ FxRWindowManager::GetInstance()->IsFxRWindow(aOuterWindowID);
+ aResolve(!isFxrWindow);
+#else
+ aResolve(true);
+#endif
+
+ return IPC_OK();
+}
+
+static BrowserParent* GetTopLevelBrowserParent(BrowserParent* aBrowserParent) {
+ MOZ_ASSERT(aBrowserParent);
+ BrowserParent* parent = aBrowserParent;
+ while (BrowserBridgeParent* bridge = parent->GetBrowserBridgeParent()) {
+ parent = bridge->Manager();
+ }
+ return parent;
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvRequestPointerLock(
+ RequestPointerLockResolver&& aResolve) {
+ nsCString error;
+ if (sTopLevelWebFocus != GetTopLevelBrowserParent(this)) {
+ error = "PointerLockDeniedNotFocused";
+ } else if (!PointerLockManager::SetLockedRemoteTarget(this)) {
+ error = "PointerLockDeniedInUse";
+ } else {
+ PointerEventHandler::ReleaseAllPointerCaptureRemoteTarget();
+ }
+ aResolve(error);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvReleasePointerLock() {
+ MOZ_ASSERT_IF(PointerLockManager::GetLockedRemoteTarget(),
+ PointerLockManager::GetLockedRemoteTarget() == this);
+ PointerLockManager::ReleaseLockedRemoteTarget(this);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvRequestPointerCapture(
+ const uint32_t& aPointerId, RequestPointerCaptureResolver&& aResolve) {
+ aResolve(
+ PointerEventHandler::SetPointerCaptureRemoteTarget(aPointerId, this));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvReleasePointerCapture(
+ const uint32_t& aPointerId) {
+ PointerEventHandler::ReleasePointerCaptureRemoteTarget(aPointerId);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvShowDynamicToolbar() {
+#if defined(MOZ_WIDGET_ANDROID)
+ nsCOMPtr<nsIWidget> widget = GetTopLevelWidget();
+ if (!widget) {
+ return IPC_OK();
+ }
+
+ RefPtr<nsWindow> window = nsWindow::From(widget);
+ if (!window) {
+ return IPC_OK();
+ }
+
+ window->ShowDynamicToolbar();
+#endif // defined(MOZ_WIDGET_ANDROID)
+ return IPC_OK();
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/BrowserParent.h b/dom/ipc/BrowserParent.h
new file mode 100644
index 0000000000..4d4ae287b4
--- /dev/null
+++ b/dom/ipc/BrowserParent.h
@@ -0,0 +1,1013 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_BrowserParent_h
+#define mozilla_dom_BrowserParent_h
+
+#include <utility>
+
+#include "LiveResizeListener.h"
+#include "Units.h"
+#include "js/TypeDecls.h"
+#include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/ContentCache.h"
+#include "mozilla/EventForwards.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/dom/BrowserBridgeParent.h"
+#include "mozilla/dom/PBrowserParent.h"
+#include "mozilla/dom/TabContext.h"
+#include "mozilla/dom/VsyncParent.h"
+#include "mozilla/dom/ipc/IdType.h"
+#include "mozilla/layout/RemoteLayerTreeOwner.h"
+#include "nsCOMPtr.h"
+#include "nsIAuthPromptProvider.h"
+#include "nsIBrowserDOMWindow.h"
+#include "nsIDOMEventListener.h"
+#include "nsIFilePicker.h"
+#include "nsIRemoteTab.h"
+#include "nsIWidget.h"
+#include "nsTArray.h"
+#include "nsWeakReference.h"
+
+class imgIContainer;
+class nsCycleCollectionTraversalCallback;
+class nsDocShellLoadState;
+class nsFrameLoader;
+class nsIBrowser;
+class nsIContent;
+class nsIContentSecurityPolicy;
+class nsIDocShell;
+class nsILoadContext;
+class nsIPrincipal;
+class nsIRequest;
+class nsIURI;
+class nsIWebBrowserPersistDocumentReceiver;
+class nsIWebProgress;
+class nsIXULBrowserWindow;
+class nsPIDOMWindowOuter;
+
+namespace mozilla {
+
+namespace a11y {
+class DocAccessibleParent;
+}
+
+namespace widget {
+struct IMENotification;
+} // namespace widget
+
+namespace gfx {
+class SourceSurface;
+} // namespace gfx
+
+namespace dom {
+
+class CanonicalBrowsingContext;
+class ClonedMessageData;
+class ContentParent;
+class Element;
+class DataTransfer;
+class BrowserHost;
+class BrowserBridgeParent;
+
+namespace ipc {
+class StructuredCloneData;
+} // namespace ipc
+
+/**
+ * BrowserParent implements the parent actor part of the PBrowser protocol. See
+ * PBrowser for more information.
+ */
+class BrowserParent final : public PBrowserParent,
+ public nsIDOMEventListener,
+ public nsIAuthPromptProvider,
+ public nsSupportsWeakReference,
+ public TabContext,
+ public LiveResizeListener {
+ typedef mozilla::dom::ClonedMessageData ClonedMessageData;
+ using TapType = GeckoContentController_TapType;
+
+ friend class PBrowserParent;
+
+ virtual ~BrowserParent();
+
+ public:
+ // Helper class for ContentParent::RecvCreateWindow.
+ struct AutoUseNewTab;
+
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_NSIAUTHPROMPTPROVIDER
+ // nsIDOMEventListener interfaces
+ NS_DECL_NSIDOMEVENTLISTENER
+
+ NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(BrowserParent, nsIDOMEventListener)
+
+ BrowserParent(ContentParent* aManager, const TabId& aTabId,
+ const TabContext& aContext,
+ CanonicalBrowsingContext* aBrowsingContext,
+ uint32_t aChromeFlags);
+
+ /**
+ * Returns the focused BrowserParent or nullptr if chrome or another app
+ * is focused.
+ */
+ static BrowserParent* GetFocused();
+
+ static BrowserParent* GetLastMouseRemoteTarget();
+
+ static BrowserParent* GetFrom(nsFrameLoader* aFrameLoader);
+
+ static BrowserParent* GetFrom(PBrowserParent* aBrowserParent);
+
+ static BrowserParent* GetFrom(nsIContent* aContent);
+
+ static BrowserParent* GetBrowserParentFromLayersId(
+ layers::LayersId aLayersId);
+
+ static TabId GetTabIdFrom(nsIDocShell* docshell);
+
+ const TabId GetTabId() const { return mTabId; }
+
+ ContentParent* Manager() const { return mManager; }
+
+ CanonicalBrowsingContext* GetBrowsingContext() { return mBrowsingContext; }
+
+ void RecomputeProcessPriority();
+
+ already_AddRefed<nsILoadContext> GetLoadContext();
+
+ Element* GetOwnerElement() const { return mFrameElement; }
+
+ nsIBrowserDOMWindow* GetBrowserDOMWindow() const { return mBrowserDOMWindow; }
+
+ already_AddRefed<nsPIDOMWindowOuter> GetParentWindowOuter();
+
+ already_AddRefed<nsIWidget> GetTopLevelWidget();
+
+ // Returns the closest widget for our frameloader's content.
+ already_AddRefed<nsIWidget> GetWidget() const;
+
+ // Returns the top-level widget for our frameloader's document.
+ already_AddRefed<nsIWidget> GetDocWidget() const;
+
+ /**
+ * Returns the widget which may have native focus and handles text input
+ * like keyboard input, IME, etc.
+ */
+ already_AddRefed<nsIWidget> GetTextInputHandlingWidget() const;
+
+ nsIXULBrowserWindow* GetXULBrowserWindow();
+
+ static uint32_t GetMaxTouchPoints(Element* aElement);
+ uint32_t GetMaxTouchPoints() { return GetMaxTouchPoints(mFrameElement); }
+
+ /**
+ * Return the top level DocAccessibleParent for this BrowserParent.
+ * Note that in the case of an out-of-process iframe, the returned actor
+ * might not be at the top level of the DocAccessibleParent tree; i.e. it
+ * might have a parent. However, it will be at the top level in its content
+ * process. That is, doc->IsTopLevelInContentProcess() will always be true,
+ * but doc->IsTopLevel() might not.
+ */
+ a11y::DocAccessibleParent* GetTopLevelDocAccessible() const;
+
+ LayersId GetLayersId() const;
+
+ // Returns the BrowserBridgeParent if this BrowserParent is for an
+ // out-of-process iframe and nullptr otherwise.
+ BrowserBridgeParent* GetBrowserBridgeParent() const;
+
+ // Returns the BrowserHost if this BrowserParent is for a top-level browser
+ // and nullptr otherwise.
+ BrowserHost* GetBrowserHost() const;
+
+ ParentShowInfo GetShowInfo();
+
+ // Get the content principal from the owner element.
+ already_AddRefed<nsIPrincipal> GetContentPrincipal() const;
+
+ /**
+ * Let managees query if Destroy() is already called so they don't send out
+ * messages when the PBrowser actor is being destroyed.
+ */
+ bool IsDestroyed() const { return mIsDestroyed; }
+
+ /**
+ * Returns whether we're in the process of creating a new window (from
+ * window.open). If so, LoadURL calls are being skipped until everything is
+ * set up. For further details, see `mCreatingWindow` below.
+ */
+ bool CreatingWindow() const { return mCreatingWindow; }
+
+ /*
+ * Visit each BrowserParent in the tree formed by PBrowser and
+ * PBrowserBridge, including `this`.
+ */
+ template <typename Callback>
+ void VisitAll(Callback aCallback) {
+ aCallback(this);
+ VisitAllDescendants(aCallback);
+ }
+
+ /*
+ * Visit each BrowserParent in the tree formed by PBrowser and
+ * PBrowserBridge, excluding `this`.
+ */
+ template <typename Callback>
+ void VisitAllDescendants(Callback aCallback) {
+ const auto& browserBridges = ManagedPBrowserBridgeParent();
+ for (const auto& key : browserBridges) {
+ BrowserBridgeParent* browserBridge =
+ static_cast<BrowserBridgeParent*>(key);
+ BrowserParent* browserParent = browserBridge->GetBrowserParent();
+
+ aCallback(browserParent);
+ browserParent->VisitAllDescendants(aCallback);
+ }
+ }
+
+ /*
+ * Visit each BrowserBridgeParent that is a child of this BrowserParent.
+ */
+ template <typename Callback>
+ void VisitChildren(Callback aCallback) {
+ const auto& browserBridges = ManagedPBrowserBridgeParent();
+ for (const auto& key : browserBridges) {
+ BrowserBridgeParent* browserBridge =
+ static_cast<BrowserBridgeParent*>(key);
+ aCallback(browserBridge);
+ }
+ }
+
+ void SetOwnerElement(Element* aElement);
+
+ void SetBrowserDOMWindow(nsIBrowserDOMWindow* aBrowserDOMWindow) {
+ mBrowserDOMWindow = aBrowserDOMWindow;
+ }
+
+ void SwapFrameScriptsFrom(nsTArray<FrameScriptInfo>& aFrameScripts) {
+ aFrameScripts.SwapElements(mDelayedFrameScripts);
+ }
+
+ void CacheFrameLoader(nsFrameLoader* aFrameLoader);
+
+ void Destroy();
+
+ void RemoveWindowListeners();
+
+ void AddWindowListeners();
+
+ mozilla::ipc::IPCResult RecvDidUnsuppressPainting();
+ mozilla::ipc::IPCResult RecvDidUnsuppressPaintingNormalPriority() {
+ return RecvDidUnsuppressPainting();
+ }
+ mozilla::ipc::IPCResult RecvMoveFocus(const bool& aForward,
+ const bool& aForDocumentNavigation);
+
+ mozilla::ipc::IPCResult RecvDropLinks(nsTArray<nsString>&& aLinks);
+
+ // TODO: Use MOZ_CAN_RUN_SCRIPT when it gains IPDL support (bug 1539864)
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY mozilla::ipc::IPCResult RecvReplyKeyEvent(
+ const WidgetKeyboardEvent& aEvent, const nsID& aUUID);
+
+ // TODO: Use MOZ_CAN_RUN_SCRIPT when it gains IPDL support (bug 1539864)
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY mozilla::ipc::IPCResult RecvAccessKeyNotHandled(
+ const WidgetKeyboardEvent& aEvent);
+
+ mozilla::ipc::IPCResult RecvRegisterProtocolHandler(const nsString& aScheme,
+ nsIURI* aHandlerURI,
+ const nsString& aTitle,
+ nsIURI* aDocURI);
+
+ mozilla::ipc::IPCResult RecvOnStateChange(
+ const WebProgressData& aWebProgressData, const RequestData& aRequestData,
+ const uint32_t aStateFlags, const nsresult aStatus,
+ const Maybe<WebProgressStateChangeData>& aStateChangeData);
+
+ mozilla::ipc::IPCResult RecvOnProgressChange(const int32_t aCurTotalProgres,
+ const int32_t aMaxTotalProgress);
+
+ mozilla::ipc::IPCResult RecvOnLocationChange(
+ const WebProgressData& aWebProgressData, const RequestData& aRequestData,
+ nsIURI* aLocation, const uint32_t aFlags, const bool aCanGoBack,
+ const bool aCanGoForward,
+ const Maybe<WebProgressLocationChangeData>& aLocationChangeData);
+
+ mozilla::ipc::IPCResult RecvOnStatusChange(const nsString& aMessage);
+
+ mozilla::ipc::IPCResult RecvNotifyContentBlockingEvent(
+ const uint32_t& aEvent, const RequestData& aRequestData,
+ const bool aBlocked, const nsACString& aTrackingOrigin,
+ nsTArray<nsCString>&& aTrackingFullHashes,
+ const Maybe<mozilla::ContentBlockingNotifier::
+ StorageAccessPermissionGrantedReason>& aReason,
+ const Maybe<mozilla::ContentBlockingNotifier::CanvasFingerprinter>&
+ aCanvasFingerprinter,
+ const Maybe<bool>& aCanvasFingerprinterKnownText);
+
+ mozilla::ipc::IPCResult RecvNavigationFinished();
+
+ already_AddRefed<nsIBrowser> GetBrowser();
+
+ already_AddRefed<CanonicalBrowsingContext> BrowsingContextForWebProgress(
+ const WebProgressData& aWebProgressData);
+
+ mozilla::ipc::IPCResult RecvIntrinsicSizeOrRatioChanged(
+ const Maybe<IntrinsicSize>& aIntrinsicSize,
+ const Maybe<AspectRatio>& aIntrinsicRatio);
+
+ mozilla::ipc::IPCResult RecvImageLoadComplete(const nsresult& aResult);
+
+ mozilla::ipc::IPCResult RecvSyncMessage(
+ const nsString& aMessage, const ClonedMessageData& aData,
+ nsTArray<ipc::StructuredCloneData>* aRetVal);
+
+ mozilla::ipc::IPCResult RecvAsyncMessage(const nsString& aMessage,
+ const ClonedMessageData& aData);
+
+ mozilla::ipc::IPCResult RecvNotifyIMEFocus(
+ const ContentCache& aContentCache,
+ const widget::IMENotification& aEventMessage,
+ NotifyIMEFocusResolver&& aResolve);
+
+ mozilla::ipc::IPCResult RecvNotifyIMETextChange(
+ const ContentCache& aContentCache,
+ const widget::IMENotification& aEventMessage);
+
+ mozilla::ipc::IPCResult RecvNotifyIMECompositionUpdate(
+ const ContentCache& aContentCache,
+ const widget::IMENotification& aEventMessage);
+
+ mozilla::ipc::IPCResult RecvNotifyIMESelection(
+ const ContentCache& aContentCache,
+ const widget::IMENotification& aEventMessage);
+
+ mozilla::ipc::IPCResult RecvUpdateContentCache(
+ const ContentCache& aContentCache);
+
+ mozilla::ipc::IPCResult RecvNotifyIMEMouseButtonEvent(
+ const widget::IMENotification& aEventMessage, bool* aConsumedByIME);
+
+ mozilla::ipc::IPCResult RecvNotifyIMEPositionChange(
+ const ContentCache& aContentCache,
+ const widget::IMENotification& aEventMessage);
+
+ mozilla::ipc::IPCResult RecvOnEventNeedingAckHandled(
+ const EventMessage& aMessage, const uint32_t& aCompositionId);
+
+ mozilla::ipc::IPCResult RecvRequestIMEToCommitComposition(
+ const bool& aCancel, const uint32_t& aCompositionId, bool* aIsCommitted,
+ nsString* aCommittedString);
+
+ mozilla::ipc::IPCResult RecvGetInputContext(widget::IMEState* aIMEState);
+
+ mozilla::ipc::IPCResult RecvSetInputContext(
+ const widget::InputContext& aContext,
+ const widget::InputContextAction& aAction);
+
+ mozilla::ipc::IPCResult RecvRequestFocus(const bool& aCanRaise,
+ const CallerType aCallerType);
+
+ mozilla::ipc::IPCResult RecvWheelZoomChange(bool aIncrease);
+
+ mozilla::ipc::IPCResult RecvLookUpDictionary(
+ const nsString& aText, nsTArray<mozilla::FontRange>&& aFontRangeArray,
+ const bool& aIsVertical, const LayoutDeviceIntPoint& aPoint);
+
+ mozilla::ipc::IPCResult RecvEnableDisableCommands(
+ const MaybeDiscarded<BrowsingContext>& aContext, const nsString& aAction,
+ nsTArray<nsCString>&& aEnabledCommands,
+ nsTArray<nsCString>&& aDisabledCommands);
+
+ mozilla::ipc::IPCResult RecvSetCursor(
+ const nsCursor& aValue, const bool& aHasCustomCursor,
+ Maybe<BigBuffer>&& aCursorData, const uint32_t& aWidth,
+ const uint32_t& aHeight, const float& aResolutionX,
+ const float& aResolutionY, const uint32_t& aStride,
+ const gfx::SurfaceFormat& aFormat, const uint32_t& aHotspotX,
+ const uint32_t& aHotspotY, const bool& aForce);
+
+ mozilla::ipc::IPCResult RecvSetLinkStatus(const nsString& aStatus);
+
+ mozilla::ipc::IPCResult RecvShowTooltip(const uint32_t& aX,
+ const uint32_t& aY,
+ const nsString& aTooltip,
+ const nsString& aDirection);
+
+ mozilla::ipc::IPCResult RecvHideTooltip();
+
+ mozilla::ipc::IPCResult RecvRespondStartSwipeEvent(
+ const uint64_t& aInputBlockId, const bool& aStartSwipe);
+
+ mozilla::ipc::IPCResult RecvDispatchWheelEvent(
+ const mozilla::WidgetWheelEvent& aEvent);
+
+ mozilla::ipc::IPCResult RecvDispatchMouseEvent(
+ const mozilla::WidgetMouseEvent& aEvent);
+
+ mozilla::ipc::IPCResult RecvDispatchKeyboardEvent(
+ const mozilla::WidgetKeyboardEvent& aEvent);
+
+ mozilla::ipc::IPCResult RecvDispatchTouchEvent(
+ const mozilla::WidgetTouchEvent& aEvent);
+
+ mozilla::ipc::IPCResult RecvScrollRectIntoView(
+ const nsRect& aRect, const ScrollAxis& aVertical,
+ const ScrollAxis& aHorizontal, const ScrollFlags& aScrollFlags,
+ const int32_t& aAppUnitsPerDevPixel);
+
+ already_AddRefed<PColorPickerParent> AllocPColorPickerParent(
+ const nsString& aTitle, const nsString& aInitialColor,
+ const nsTArray<nsString>& aDefaultColors);
+
+ PVsyncParent* AllocPVsyncParent();
+
+ bool DeallocPVsyncParent(PVsyncParent* aActor);
+
+#ifdef ACCESSIBILITY
+ PDocAccessibleParent* AllocPDocAccessibleParent(
+ PDocAccessibleParent*, const uint64_t&,
+ const MaybeDiscardedBrowsingContext&);
+ bool DeallocPDocAccessibleParent(PDocAccessibleParent*);
+ virtual mozilla::ipc::IPCResult RecvPDocAccessibleConstructor(
+ PDocAccessibleParent* aDoc, PDocAccessibleParent* aParentDoc,
+ const uint64_t& aParentID,
+ const MaybeDiscardedBrowsingContext& aBrowsingContext) override;
+#endif
+
+ already_AddRefed<PSessionStoreParent> AllocPSessionStoreParent();
+
+ mozilla::ipc::IPCResult RecvNewWindowGlobal(
+ ManagedEndpoint<PWindowGlobalParent>&& aEndpoint,
+ const WindowGlobalInit& aInit);
+
+ mozilla::ipc::IPCResult RecvIsWindowSupportingProtectedMedia(
+ const uint64_t& aOuterWindowID,
+ IsWindowSupportingProtectedMediaResolver&& aResolve);
+
+ mozilla::ipc::IPCResult RecvIsWindowSupportingWebVR(
+ const uint64_t& aOuterWindowID,
+ IsWindowSupportingWebVRResolver&& aResolve);
+
+ void LoadURL(nsDocShellLoadState* aLoadState);
+
+ void ResumeLoad(uint64_t aPendingSwitchID);
+
+ void InitRendering();
+ bool AttachWindowRenderer();
+ void MaybeShowFrame();
+
+ bool Show(const OwnerShowInfo&);
+
+ void UpdateDimensions(const nsIntRect& aRect, const ScreenIntSize& aSize);
+
+ DimensionInfo GetDimensionInfo();
+
+ nsresult UpdatePosition();
+
+ // Notify position update to all descendant documents in this browser parent.
+ // NOTE: This should use only for browsers in popup windows attached to the
+ // main browser window.
+ void NotifyPositionUpdatedForContentsInPopup();
+
+ void SizeModeChanged(const nsSizeMode& aSizeMode);
+
+ void HandleAccessKey(const WidgetKeyboardEvent& aEvent,
+ nsTArray<uint32_t>& aCharCodes);
+
+#if defined(MOZ_WIDGET_ANDROID)
+ void DynamicToolbarMaxHeightChanged(ScreenIntCoord aHeight);
+ void DynamicToolbarOffsetChanged(ScreenIntCoord aOffset);
+#endif
+
+ void Activate(uint64_t aActionId);
+
+ void Deactivate(bool aWindowLowering, uint64_t aActionId);
+
+ void MouseEnterIntoWidget();
+
+ bool MapEventCoordinatesForChildProcess(mozilla::WidgetEvent* aEvent);
+
+ void MapEventCoordinatesForChildProcess(const LayoutDeviceIntPoint& aOffset,
+ mozilla::WidgetEvent* aEvent);
+
+ LayoutDeviceToCSSScale GetLayoutDeviceToCSSScale();
+
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY mozilla::ipc::IPCResult
+ RecvRequestNativeKeyBindings(const uint32_t& aType,
+ const mozilla::WidgetKeyboardEvent& aEvent,
+ nsTArray<mozilla::CommandInt>* aCommands);
+
+ mozilla::ipc::IPCResult RecvSynthesizeNativeKeyEvent(
+ const int32_t& aNativeKeyboardLayout, const int32_t& aNativeKeyCode,
+ const uint32_t& aModifierFlags, const nsString& aCharacters,
+ const nsString& aUnmodifiedCharacters, const uint64_t& aObserverId);
+
+ mozilla::ipc::IPCResult RecvSynthesizeNativeMouseEvent(
+ const LayoutDeviceIntPoint& aPoint, const uint32_t& aNativeMessage,
+ const int16_t& aButton, const uint32_t& aModifierFlags,
+ const uint64_t& aObserverId);
+
+ mozilla::ipc::IPCResult RecvSynthesizeNativeMouseMove(
+ const LayoutDeviceIntPoint& aPoint, const uint64_t& aObserverId);
+
+ mozilla::ipc::IPCResult RecvSynthesizeNativeMouseScrollEvent(
+ const LayoutDeviceIntPoint& aPoint, const uint32_t& aNativeMessage,
+ const double& aDeltaX, const double& aDeltaY, const double& aDeltaZ,
+ const uint32_t& aModifierFlags, const uint32_t& aAdditionalFlags,
+ const uint64_t& aObserverId);
+
+ mozilla::ipc::IPCResult RecvSynthesizeNativeTouchPoint(
+ const uint32_t& aPointerId, const TouchPointerState& aPointerState,
+ const LayoutDeviceIntPoint& aPoint, const double& aPointerPressure,
+ const uint32_t& aPointerOrientation, const uint64_t& aObserverId);
+
+ mozilla::ipc::IPCResult RecvSynthesizeNativeTouchPadPinch(
+ const TouchpadGesturePhase& aEventPhase, const float& aScale,
+ const LayoutDeviceIntPoint& aPoint, const int32_t& aModifierFlags);
+
+ mozilla::ipc::IPCResult RecvSynthesizeNativeTouchTap(
+ const LayoutDeviceIntPoint& aPoint, const bool& aLongTap,
+ const uint64_t& aObserverId);
+
+ mozilla::ipc::IPCResult RecvClearNativeTouchSequence(
+ const uint64_t& aObserverId);
+
+ mozilla::ipc::IPCResult RecvSynthesizeNativePenInput(
+ const uint32_t& aPointerId, const TouchPointerState& aPointerState,
+ const LayoutDeviceIntPoint& aPoint, const double& aPressure,
+ const uint32_t& aRotation, const int32_t& aTiltX, const int32_t& aTiltY,
+ const int32_t& aButton, const uint64_t& aObserverId);
+
+ mozilla::ipc::IPCResult RecvSynthesizeNativeTouchpadDoubleTap(
+ const LayoutDeviceIntPoint& aPoint, const uint32_t& aModifierFlags);
+
+ mozilla::ipc::IPCResult RecvSynthesizeNativeTouchpadPan(
+ const TouchpadGesturePhase& aEventPhase,
+ const LayoutDeviceIntPoint& aPoint, const double& aDeltaX,
+ const double& aDeltaY, const int32_t& aModifierFlags,
+ const uint64_t& aObserverId);
+
+ mozilla::ipc::IPCResult RecvLockNativePointer();
+
+ mozilla::ipc::IPCResult RecvUnlockNativePointer();
+
+ /**
+ * The following Send*Event() marks aEvent as posted to remote process if
+ * it succeeded. So, you can check the result with
+ * aEvent.HasBeenPostedToRemoteProcess().
+ */
+ void SendRealMouseEvent(WidgetMouseEvent& aEvent);
+
+ void SendRealDragEvent(WidgetDragEvent& aEvent, uint32_t aDragAction,
+ uint32_t aDropEffect, nsIPrincipal* aPrincipal,
+ nsIContentSecurityPolicy* aCsp);
+
+ void SendMouseWheelEvent(WidgetWheelEvent& aEvent);
+
+ /**
+ * Only when the event is synthesized, retrieving writing mode may flush
+ * the layout.
+ */
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY void SendRealKeyEvent(
+ WidgetKeyboardEvent& aEvent);
+
+ void SendRealTouchEvent(WidgetTouchEvent& aEvent);
+
+ /**
+ * Different from above Send*Event(), these methods return true if the
+ * event has been posted to the remote process or failed to do that but
+ * shouldn't be handled by following event listeners.
+ * If you need to check if it's actually posted to the remote process,
+ * you can refer aEvent.HasBeenPostedToRemoteProcess().
+ */
+ bool SendCompositionEvent(mozilla::WidgetCompositionEvent& aEvent,
+ uint32_t aCompositionId);
+
+ bool SendSelectionEvent(mozilla::WidgetSelectionEvent& aEvent);
+
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY bool SendHandleTap(
+ TapType aType, const LayoutDevicePoint& aPoint, Modifiers aModifiers,
+ const ScrollableLayerGuid& aGuid, uint64_t aInputBlockId,
+ const Maybe<DoubleTapToZoomMetrics>& aDoubleTapToZoomMetrics);
+
+ already_AddRefed<PFilePickerParent> AllocPFilePickerParent(
+ const nsString& aTitle, const nsIFilePicker::Mode& aMode);
+
+ bool GetGlobalJSObject(JSContext* cx, JSObject** globalp);
+
+ void StartPersistence(CanonicalBrowsingContext* aContext,
+ nsIWebBrowserPersistDocumentReceiver* aRecv,
+ ErrorResult& aRv);
+
+ bool HandleQueryContentEvent(mozilla::WidgetQueryContentEvent& aEvent);
+
+ bool SendInsertText(const nsString& aStringToInsert);
+
+ bool SendPasteTransferable(IPCTransferable&& aTransferable);
+
+ // Helper for transforming a point
+ LayoutDeviceIntPoint TransformPoint(
+ const LayoutDeviceIntPoint& aPoint,
+ const LayoutDeviceToLayoutDeviceMatrix4x4& aMatrix);
+ LayoutDevicePoint TransformPoint(
+ const LayoutDevicePoint& aPoint,
+ const LayoutDeviceToLayoutDeviceMatrix4x4& aMatrix);
+
+ // Transform a coordinate from the parent process coordinate space to the
+ // child process coordinate space.
+ LayoutDeviceIntPoint TransformParentToChild(const WidgetMouseEvent& aEvent);
+ LayoutDeviceIntPoint TransformParentToChild(
+ const LayoutDeviceIntPoint& aPoint);
+ LayoutDevicePoint TransformParentToChild(const LayoutDevicePoint& aPoint);
+
+ // Transform a coordinate from the child process coordinate space to the
+ // parent process coordinate space.
+ LayoutDeviceIntPoint TransformChildToParent(
+ const LayoutDeviceIntPoint& aPoint);
+ LayoutDevicePoint TransformChildToParent(const LayoutDevicePoint& aPoint);
+ LayoutDeviceIntRect TransformChildToParent(const LayoutDeviceIntRect& aRect);
+
+ // Returns the matrix that transforms event coordinates from the coordinate
+ // space of the child process to the coordinate space of the parent process.
+ LayoutDeviceToLayoutDeviceMatrix4x4 GetChildToParentConversionMatrix();
+
+ void SetChildToParentConversionMatrix(
+ const Maybe<LayoutDeviceToLayoutDeviceMatrix4x4>& aMatrix,
+ const ScreenRect& aRemoteDocumentRect);
+
+ // Returns the offset from the origin of our frameloader's nearest widget to
+ // the origin of its layout frame. This offset is used to translate event
+ // coordinates relative to the PuppetWidget origin in the child process.
+ //
+ // GOING AWAY. PLEASE AVOID ADDING CALLERS. Use the above tranformation
+ // methods instead.
+ LayoutDeviceIntPoint GetChildProcessOffset();
+
+ // Returns the offset from the on-screen origin of our top-level window's
+ // widget (including window decorations) to the origin of our frameloader's
+ // nearest widget. This offset is used to translate coordinates from the
+ // PuppetWidget's origin to absolute screen coordinates in the child.
+ LayoutDeviceIntPoint GetClientOffset();
+
+ void StopIMEStateManagement();
+
+ PPaymentRequestParent* AllocPPaymentRequestParent();
+
+ bool DeallocPPaymentRequestParent(PPaymentRequestParent* aActor);
+
+ bool SendLoadRemoteScript(const nsAString& aURL,
+ const bool& aRunInGlobalScope);
+
+ void LayerTreeUpdate(bool aActive);
+
+ mozilla::ipc::IPCResult RecvInvokeDragSession(
+ nsTArray<IPCTransferableData>&& aTransferables, const uint32_t& aAction,
+ Maybe<BigBuffer>&& aVisualDnDData, const uint32_t& aStride,
+ const gfx::SurfaceFormat& aFormat, const LayoutDeviceIntRect& aDragRect,
+ nsIPrincipal* aPrincipal, nsIContentSecurityPolicy* aCsp,
+ const CookieJarSettingsArgs& aCookieJarSettingsArgs,
+ const MaybeDiscarded<WindowContext>& aSourceWindowContext,
+ const MaybeDiscarded<WindowContext>& aSourceTopWindowContext);
+
+ void AddInitialDnDDataTo(IPCTransferableData* aTransferableData,
+ nsIPrincipal** aPrincipal);
+
+ bool TakeDragVisualization(RefPtr<mozilla::gfx::SourceSurface>& aSurface,
+ LayoutDeviceIntRect* aDragRect);
+
+ mozilla::ipc::IPCResult RecvEnsureLayersConnected(
+ CompositorOptions* aCompositorOptions);
+
+ // LiveResizeListener implementation
+ void LiveResizeStarted() override;
+ void LiveResizeStopped() override;
+
+ void SetReadyToHandleInputEvents() { mIsReadyToHandleInputEvents = true; }
+ bool IsReadyToHandleInputEvents() { return mIsReadyToHandleInputEvents; }
+
+ void NavigateByKey(bool aForward, bool aForDocumentNavigation);
+
+ bool GetDocShellIsActive();
+ void SetDocShellIsActive(bool aDocShellIsActive);
+
+ bool GetHasPresented();
+ bool GetHasLayers();
+ bool GetRenderLayers();
+ void SetRenderLayers(bool aRenderLayers);
+ bool GetPriorityHint();
+ void SetPriorityHint(bool aPriorityHint);
+ void PreserveLayers(bool aPreserveLayers);
+ void NotifyResolutionChanged();
+
+ bool CanCancelContentJS(nsIRemoteTab::NavigationType aNavigationType,
+ int32_t aNavigationIndex,
+ nsIURI* aNavigationURI) const;
+
+ // Called when the BrowserParent is being destroyed or entering bfcache.
+ void Deactivated();
+
+ protected:
+ friend BrowserBridgeParent;
+ friend BrowserHost;
+
+ void SetBrowserBridgeParent(BrowserBridgeParent* aBrowser);
+ void SetBrowserHost(BrowserHost* aBrowser);
+
+ bool ReceiveMessage(
+ const nsString& aMessage, bool aSync, ipc::StructuredCloneData* aData,
+ nsTArray<ipc::StructuredCloneData>* aJSONRetVal = nullptr);
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ mozilla::ipc::IPCResult RecvRemoteIsReadyToHandleInputEvents();
+
+ mozilla::ipc::IPCResult RecvSetDimensions(mozilla::DimensionRequest aRequest,
+ const double& aScale);
+
+ mozilla::ipc::IPCResult RecvShowCanvasPermissionPrompt(
+ const nsCString& aOrigin, const bool& aHideDoorHanger);
+
+ mozilla::ipc::IPCResult RecvSetSystemFont(const nsCString& aFontName);
+ mozilla::ipc::IPCResult RecvGetSystemFont(nsCString* aFontName);
+
+ mozilla::ipc::IPCResult RecvVisitURI(nsIURI* aURI, nsIURI* aLastVisitedURI,
+ const uint32_t& aFlags,
+ const uint64_t& aBrowserId);
+
+ mozilla::ipc::IPCResult RecvQueryVisitedState(
+ nsTArray<RefPtr<nsIURI>>&& aURIs);
+
+ mozilla::ipc::IPCResult RecvMaybeFireEmbedderLoadEvents(
+ EmbedderElementEventType aFireEventAtEmbeddingElement);
+
+ mozilla::ipc::IPCResult RecvRequestPointerLock(
+ RequestPointerLockResolver&& aResolve);
+ mozilla::ipc::IPCResult RecvReleasePointerLock();
+
+ mozilla::ipc::IPCResult RecvRequestPointerCapture(
+ const uint32_t& aPointerId, RequestPointerCaptureResolver&& aResolve);
+ mozilla::ipc::IPCResult RecvReleasePointerCapture(const uint32_t& aPointerId);
+
+ mozilla::ipc::IPCResult RecvShowDynamicToolbar();
+
+ private:
+ void SuppressDisplayport(bool aEnabled);
+
+ void SetRenderLayersInternal(bool aEnabled);
+
+ already_AddRefed<nsFrameLoader> GetFrameLoader(
+ bool aUseCachedFrameLoaderAfterDestroy = false) const;
+
+ void TryCacheDPIAndScale();
+
+ bool AsyncPanZoomEnabled() const;
+
+ // Update state prior to routing an APZ-aware event to the child process.
+ // |aOutTargetGuid| will contain the identifier
+ // of the APZC instance that handled the event. aOutTargetGuid may be null.
+ // |aOutInputBlockId| will contain the identifier of the input block
+ // that this event was added to, if there was one. aOutInputBlockId may be
+ // null. |aOutApzResponse| will contain the response that the APZ gave when
+ // processing the input block; this is used for generating appropriate
+ // pointercancel events.
+ void ApzAwareEventRoutingToChild(ScrollableLayerGuid* aOutTargetGuid,
+ uint64_t* aOutInputBlockId,
+ nsEventStatus* aOutApzResponse);
+
+ // When dropping links we perform a roundtrip from
+ // Parent (SendRealDragEvent) -> Child -> Parent (RecvDropLinks)
+ // and have to ensure that the child did not modify links to be loaded.
+ bool QueryDropLinksForVerification();
+
+ void UnlockNativePointer();
+
+ void UpdateNativePointerLockCenter(nsIWidget* aWidget);
+
+ private:
+ // This is used when APZ needs to find the BrowserParent associated with a
+ // layer to dispatch events.
+ typedef nsTHashMap<nsUint64HashKey, BrowserParent*> LayerToBrowserParentTable;
+ static LayerToBrowserParentTable* sLayerToBrowserParentTable;
+
+ static void AddBrowserParentToTable(layers::LayersId aLayersId,
+ BrowserParent* aBrowserParent);
+
+ static void RemoveBrowserParentFromTable(layers::LayersId aLayersId);
+
+ // Keeps track of which BrowserParent has keyboard focus.
+ // If nullptr, the parent process has focus.
+ // Use UpdateFocus() to manage.
+ static BrowserParent* sFocus;
+
+ // Keeps track of which top-level BrowserParent the keyboard focus is under.
+ // If nullptr, the parent process has focus.
+ // Use SetTopLevelWebFocus and UnsetTopLevelWebFocus to manage.
+ static BrowserParent* sTopLevelWebFocus;
+
+ // Setter for sTopLevelWebFocus
+ static void SetTopLevelWebFocus(BrowserParent* aBrowserParent);
+
+ // Unsetter for sTopLevelWebFocus; only unsets if argument matches
+ // current sTopLevelWebFocus. Use UnsetTopLevelWebFocusAll() to
+ // unset regardless of current value.
+ static void UnsetTopLevelWebFocus(BrowserParent* aBrowserParent);
+
+ // Recomputes sFocus and returns it.
+ static BrowserParent* UpdateFocus();
+
+ // Keeps track of which BrowserParent the real mouse event is sent to.
+ static BrowserParent* sLastMouseRemoteTarget;
+
+ // Unsetter for LastMouseRemoteTarget; only unsets if argument matches
+ // current sLastMouseRemoteTarget.
+ static void UnsetLastMouseRemoteTarget(BrowserParent* aBrowserParent);
+
+ struct APZData {
+ bool operator==(const APZData& aOther) const {
+ return aOther.guid == guid && aOther.blockId == blockId &&
+ aOther.apzResponse == apzResponse;
+ }
+
+ bool operator!=(const APZData& aOther) const { return !(*this == aOther); }
+
+ ScrollableLayerGuid guid;
+ uint64_t blockId;
+ nsEventStatus apzResponse;
+ };
+ void SendRealTouchMoveEvent(WidgetTouchEvent& aEvent, APZData& aAPZData,
+ uint32_t aConsecutiveTouchMoveCount);
+
+ void UpdateVsyncParentVsyncDispatcher();
+
+ public:
+ // Unsets sTopLevelWebFocus regardless of its current value.
+ static void UnsetTopLevelWebFocusAll();
+
+ // Recomputes focus when the BrowsingContext tree changes in a
+ // way that potentially invalidates the sFocus.
+ static void UpdateFocusFromBrowsingContext();
+
+ private:
+ TabId mTabId;
+
+ RefPtr<ContentParent> mManager;
+ // The root browsing context loaded in this BrowserParent.
+ RefPtr<CanonicalBrowsingContext> mBrowsingContext;
+ nsCOMPtr<nsILoadContext> mLoadContext;
+ RefPtr<Element> mFrameElement;
+ nsCOMPtr<nsIBrowserDOMWindow> mBrowserDOMWindow;
+ // We keep a strong reference to the frameloader after we've sent the
+ // Destroy message and before we've received __delete__. This allows us to
+ // dispatch message manager messages during this time.
+ RefPtr<nsFrameLoader> mFrameLoader;
+ uint32_t mChromeFlags;
+
+ // Pointer back to BrowserBridgeParent if there is one associated with
+ // this BrowserParent. This is non-owning to avoid cycles and is managed
+ // by the BrowserBridgeParent instance, which has the strong reference
+ // to this BrowserParent.
+ BrowserBridgeParent* mBrowserBridgeParent;
+ // Pointer to the BrowserHost that owns us, if any. This is mutually
+ // exclusive with mBrowserBridgeParent, and one is guaranteed to be
+ // non-null.
+ BrowserHost* mBrowserHost;
+
+ ContentCacheInParent mContentCache;
+
+ layout::RemoteLayerTreeOwner mRemoteLayerTreeOwner;
+
+ Maybe<LayoutDeviceToLayoutDeviceMatrix4x4> mChildToParentConversionMatrix;
+ Maybe<ScreenRect> mRemoteDocumentRect;
+
+ // mWaitingReplyKeyboardEvents stores keyboard events which are sent from
+ // SendRealKeyEvent and the event will be back as a reply event. They are
+ // removed when RecvReplyKeyEvent receives corresponding event or newer event.
+ // Note that reply event will be used for handling non-reserved shortcut keys.
+ // Therefore, we need to store only important data for GlobalKeyHandler.
+ struct SentKeyEventData {
+ uint32_t mKeyCode;
+ uint32_t mCharCode;
+ uint32_t mPseudoCharCode;
+ KeyNameIndex mKeyNameIndex;
+ CodeNameIndex mCodeNameIndex;
+ Modifiers mModifiers;
+ nsID mUUID;
+ };
+ nsTArray<SentKeyEventData> mWaitingReplyKeyboardEvents;
+
+ nsIntRect mRect;
+ ScreenIntSize mDimensions;
+ float mDPI;
+ int32_t mRounding;
+ CSSToLayoutDeviceScale mDefaultScale;
+ bool mUpdatedDimensions;
+ nsSizeMode mSizeMode;
+ LayoutDeviceIntPoint mClientOffset;
+ LayoutDeviceIntPoint mChromeOffset;
+
+ // When loading a new tab or window via window.open, the child is
+ // responsible for loading the URL it wants into the new BrowserChild. When
+ // the parent receives the CreateWindow message, though, it sends a LoadURL
+ // message, usually for about:blank. It's important for the about:blank load
+ // to get processed because the Firefox frontend expects every new window to
+ // immediately start loading something (see bug 1123090). However, we want
+ // the child to process the LoadURL message before it returns from
+ // ProvideWindow so that the URL sent from the parent doesn't override the
+ // child's URL. This is not possible using our IPC mechanisms. To solve the
+ // problem, we skip sending the LoadURL message in the parent and instead
+ // return the URL as a result from CreateWindow. The child simulates
+ // receiving a LoadURL message before returning from ProvideWindow.
+ //
+ // The mCreatingWindow flag is set while dispatching CreateWindow. During
+ // that time, any LoadURL calls are skipped.
+ bool mCreatingWindow;
+
+ // When loading a new tab or window via window.open, we want to ensure that
+ // frame scripts for that tab are loaded before any scripts start to run in
+ // the window. We can't load the frame scripts the normal way, using
+ // separate IPC messages, since they won't be processed by the child until
+ // returning to the event loop, which is too late. Instead, we queue up
+ // frame scripts that we intend to load and send them as part of the
+ // CreateWindow response. Then BrowserChild loads them immediately.
+ nsTArray<FrameScriptInfo> mDelayedFrameScripts;
+
+ // Cached cursor setting from BrowserChild. When the cursor is over the
+ // tab, it should take this appearance.
+ nsIWidget::Cursor mCursor;
+
+ nsTArray<nsString> mVerifyDropLinks;
+
+ RefPtr<VsyncParent> mVsyncParent;
+
+#ifdef DEBUG
+ int32_t mActiveSupressDisplayportCount = 0;
+#endif
+
+ // When true, we've initiated normal shutdown and notified our managing
+ // PContent.
+ bool mMarkedDestroying : 1;
+ // When true, the BrowserParent is invalid and we should not send IPC
+ // messages anymore.
+ bool mIsDestroyed : 1;
+ // True if the cursor changes from the BrowserChild should change the widget
+ // cursor. This happens whenever the cursor is in the remote target's
+ // region.
+ bool mRemoteTargetSetsCursor : 1;
+
+ // If this flag is set, then the tab's layers will be preserved even when
+ // the tab's docshell is inactive.
+ bool mIsPreservingLayers : 1;
+
+ // Holds the most recent value passed to the RenderLayers function. This
+ // does not necessarily mean that the layers have finished rendering
+ // and have uploaded - for that, use mHasLayers.
+ bool mRenderLayers : 1;
+
+ // True if process should be set to a higher priority.
+ bool mPriorityHint : 1;
+
+ // True if the compositor has reported that the BrowserChild has uploaded
+ // layers.
+ bool mHasLayers : 1;
+
+ // True if this BrowserParent has had its layer tree sent to the compositor
+ // at least once.
+ bool mHasPresented : 1;
+
+ // True when the remote browser is created and ready to handle input events.
+ bool mIsReadyToHandleInputEvents : 1;
+
+ // True if we suppress the eMouseEnterIntoWidget event due to the
+ // BrowserChild was not ready to handle it. We will resend it when the next
+ // time we fire a mouse event and the BrowserChild is ready.
+ bool mIsMouseEnterIntoWidgetEventSuppressed : 1;
+
+ // True after RecvLockNativePointer has been called and until
+ // UnlockNativePointer has been called.
+ bool mLockedNativePointer : 1;
+
+ // True between ShowTooltip and HideTooltip messages.
+ bool mShowingTooltip : 1;
+};
+
+struct MOZ_STACK_CLASS BrowserParent::AutoUseNewTab final {
+ public:
+ explicit AutoUseNewTab(BrowserParent* aNewTab) : mNewTab(aNewTab) {
+ MOZ_ASSERT(!aNewTab->mCreatingWindow);
+ aNewTab->mCreatingWindow = true;
+ }
+
+ ~AutoUseNewTab() { mNewTab->mCreatingWindow = false; }
+
+ private:
+ RefPtr<BrowserParent> mNewTab;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_BrowserParent_h
diff --git a/dom/ipc/CSPMessageUtils.cpp b/dom/ipc/CSPMessageUtils.cpp
new file mode 100644
index 0000000000..b952d01b6f
--- /dev/null
+++ b/dom/ipc/CSPMessageUtils.cpp
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/CSPMessageUtils.h"
+#include "mozilla/ipc/PBackgroundSharedTypes.h"
+#include "nsSerializationHelper.h"
+#include "mozilla/ipc/BackgroundUtils.h"
+
+namespace IPC {
+
+using namespace mozilla::ipc;
+
+void ParamTraits<nsIContentSecurityPolicy*>::Write(
+ MessageWriter* aWriter, nsIContentSecurityPolicy* aParam) {
+ bool isNull = !aParam;
+ WriteParam(aWriter, isNull);
+ if (isNull) {
+ return;
+ }
+
+ CSPInfo csp;
+ mozilla::Unused << NS_WARN_IF(NS_FAILED(CSPToCSPInfo(aParam, &csp)));
+ WriteParam(aWriter, csp);
+}
+
+bool ParamTraits<nsIContentSecurityPolicy*>::Read(
+ MessageReader* aReader, RefPtr<nsIContentSecurityPolicy>* aResult) {
+ bool isNull;
+ if (!ReadParam(aReader, &isNull)) {
+ return false;
+ }
+
+ if (isNull) {
+ *aResult = nullptr;
+ return true;
+ }
+
+ CSPInfo csp;
+ if (!ReadParam(aReader, &csp)) {
+ return false;
+ }
+
+ *aResult = CSPInfoToCSP(csp, nullptr, nullptr);
+ return *aResult;
+}
+
+} // namespace IPC
diff --git a/dom/ipc/CSPMessageUtils.h b/dom/ipc/CSPMessageUtils.h
new file mode 100644
index 0000000000..286a3a9778
--- /dev/null
+++ b/dom/ipc/CSPMessageUtils.h
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_csp_message_utils_h__
+#define mozilla_dom_csp_message_utils_h__
+
+#include "ipc/IPCMessageUtils.h"
+#include "nsCOMPtr.h"
+#include "nsIContentSecurityPolicy.h"
+
+namespace IPC {
+
+template <>
+struct ParamTraits<nsIContentSecurityPolicy*> {
+ static void Write(MessageWriter* aWriter, nsIContentSecurityPolicy* aParam);
+ static bool Read(MessageReader* aReader,
+ RefPtr<nsIContentSecurityPolicy>* aResult);
+};
+
+} // namespace IPC
+
+#endif // mozilla_dom_csp_message_utils_h__
diff --git a/dom/ipc/ClonedErrorHolder.cpp b/dom/ipc/ClonedErrorHolder.cpp
new file mode 100644
index 0000000000..8db9bd064e
--- /dev/null
+++ b/dom/ipc/ClonedErrorHolder.cpp
@@ -0,0 +1,373 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/ClonedErrorHolder.h"
+
+#include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/ClonedErrorHolderBinding.h"
+#include "mozilla/dom/DOMException.h"
+#include "mozilla/dom/DOMExceptionBinding.h"
+#include "mozilla/dom/Exceptions.h"
+#include "mozilla/dom/StructuredCloneTags.h"
+#include "mozilla/dom/ToJSValue.h"
+#include "jsapi.h"
+#include "jsfriendapi.h"
+#include "js/StructuredClone.h"
+#include "nsReadableUtils.h"
+#include "xpcpublic.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+// static
+UniquePtr<ClonedErrorHolder> ClonedErrorHolder::Constructor(
+ const GlobalObject& aGlobal, JS::Handle<JSObject*> aError,
+ ErrorResult& aRv) {
+ return Create(aGlobal.Context(), aError, aRv);
+}
+
+// static
+UniquePtr<ClonedErrorHolder> ClonedErrorHolder::Create(
+ JSContext* aCx, JS::Handle<JSObject*> aError, ErrorResult& aRv) {
+ UniquePtr<ClonedErrorHolder> ceh(new ClonedErrorHolder());
+ ceh->Init(aCx, aError, aRv);
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+ return ceh;
+}
+
+ClonedErrorHolder::ClonedErrorHolder()
+ : mName(VoidCString()),
+ mMessage(VoidCString()),
+ mFilename(VoidCString()),
+ mSourceLine(VoidCString()) {}
+
+void ClonedErrorHolder::Init(JSContext* aCx, JS::Handle<JSObject*> aError,
+ ErrorResult& aRv) {
+ JS::Rooted<JSObject*> stack(aCx);
+
+ if (JSErrorReport* err = JS_ErrorFromException(aCx, aError)) {
+ mType = Type::JSError;
+ if (err->message()) {
+ mMessage = err->message().c_str();
+ }
+ if (err->filename) {
+ mFilename = err->filename.c_str();
+ }
+ if (err->linebuf()) {
+ AppendUTF16toUTF8(
+ nsDependentSubstring(err->linebuf(), err->linebufLength()),
+ mSourceLine);
+ mTokenOffset = err->tokenOffset();
+ }
+ mLineNumber = err->lineno;
+ mColumn = err->column;
+ mErrorNumber = err->errorNumber;
+ mExnType = JSExnType(err->exnType);
+
+ // Note: We don't save the souce ID here, since this object is cross-process
+ // clonable, and the source ID won't be valid in other processes.
+ // We don't store the source notes either, though for no other reason that
+ // it isn't clear that it's worth the complexity.
+
+ stack = JS::ExceptionStackOrNull(aError);
+ } else {
+ RefPtr<DOMException> domExn;
+ RefPtr<Exception> exn;
+ if (NS_SUCCEEDED(UNWRAP_OBJECT(DOMException, aError, domExn))) {
+ mType = Type::DOMException;
+ mCode = domExn->Code();
+ exn = std::move(domExn);
+ } else if (NS_SUCCEEDED(UNWRAP_OBJECT(Exception, aError, exn))) {
+ mType = Type::Exception;
+ } else {
+ aRv.ThrowNotSupportedError(
+ "We can only clone DOM Exceptions and native JS Error objects");
+ return;
+ }
+
+ nsAutoString str;
+
+ exn->GetName(str);
+ CopyUTF16toUTF8(str, mName);
+
+ exn->GetMessageMoz(str);
+ CopyUTF16toUTF8(str, mMessage);
+
+ // Note: In DOM exceptions, filename, line number, and column number come
+ // from the stack frame, and don't need to be stored separately. mFilename,
+ // mLineNumber, and mColumn are only used for JS exceptions.
+ //
+ // We also don't serialized Exception's mThrownJSVal or mData fields, since
+ // they generally won't be serializable.
+
+ mResult = nsresult(exn->Result());
+
+ if (nsCOMPtr<nsIStackFrame> frame = exn->GetLocation()) {
+ JS::Rooted<JS::Value> value(aCx);
+ frame->GetNativeSavedFrame(&value);
+ if (value.isObject()) {
+ stack = &value.toObject();
+ }
+ }
+ }
+
+ Maybe<JSAutoRealm> ar;
+ if (stack) {
+ ar.emplace(aCx, stack);
+ }
+ JS::Rooted<JS::Value> stackValue(aCx, JS::ObjectOrNullValue(stack));
+ mStack.Write(aCx, stackValue, aRv);
+}
+
+bool ClonedErrorHolder::WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto,
+ JS::MutableHandle<JSObject*> aReflector) {
+ return ClonedErrorHolder_Binding::Wrap(aCx, this, aGivenProto, aReflector);
+}
+
+static constexpr uint32_t kVoidStringLength = ~0;
+
+static bool WriteStringPair(JSStructuredCloneWriter* aWriter,
+ const nsACString& aString1,
+ const nsACString& aString2) {
+ auto StringLength = [](const nsACString& aStr) -> uint32_t {
+ auto length = uint32_t(aStr.Length());
+ MOZ_DIAGNOSTIC_ASSERT(length != kVoidStringLength,
+ "We should not be serializing a 4GiB string");
+ if (aStr.IsVoid()) {
+ return kVoidStringLength;
+ }
+ return length;
+ };
+
+ return JS_WriteUint32Pair(aWriter, StringLength(aString1),
+ StringLength(aString2)) &&
+ JS_WriteBytes(aWriter, aString1.BeginReading(), aString1.Length()) &&
+ JS_WriteBytes(aWriter, aString2.BeginReading(), aString2.Length());
+}
+
+static bool ReadStringPair(JSStructuredCloneReader* aReader,
+ nsACString& aString1, nsACString& aString2) {
+ auto ReadString = [&](nsACString& aStr, uint32_t aLength) {
+ if (aLength == kVoidStringLength) {
+ aStr.SetIsVoid(true);
+ return true;
+ }
+ char* data = nullptr;
+ return aLength == 0 || (aStr.GetMutableData(&data, aLength, fallible) &&
+ JS_ReadBytes(aReader, data, aLength));
+ };
+
+ aString1.Truncate(0);
+ aString2.Truncate(0);
+
+ uint32_t length1, length2;
+ return JS_ReadUint32Pair(aReader, &length1, &length2) &&
+ ReadString(aString1, length1) && ReadString(aString2, length2);
+}
+
+bool ClonedErrorHolder::WriteStructuredClone(JSContext* aCx,
+ JSStructuredCloneWriter* aWriter,
+ StructuredCloneHolder* aHolder) {
+ auto& data = mStack.BufferData();
+ return JS_WriteUint32Pair(aWriter, SCTAG_DOM_CLONED_ERROR_OBJECT, 0) &&
+ WriteStringPair(aWriter, mName, mMessage) &&
+ WriteStringPair(aWriter, mFilename, mSourceLine) &&
+ JS_WriteUint32Pair(aWriter, mLineNumber,
+ *mColumn.addressOfValueForTranscode()) &&
+ JS_WriteUint32Pair(aWriter, mTokenOffset, mErrorNumber) &&
+ JS_WriteUint32Pair(aWriter, uint32_t(mType), uint32_t(mExnType)) &&
+ JS_WriteUint32Pair(aWriter, mCode, uint32_t(mResult)) &&
+ JS_WriteUint32Pair(aWriter, data.Size(),
+ JS_STRUCTURED_CLONE_VERSION) &&
+ data.ForEachDataChunk([&](const char* aData, size_t aSize) {
+ return JS_WriteBytes(aWriter, aData, aSize);
+ });
+}
+
+bool ClonedErrorHolder::Init(JSContext* aCx, JSStructuredCloneReader* aReader) {
+ uint32_t type, exnType, result, code;
+ if (!(ReadStringPair(aReader, mName, mMessage) &&
+ ReadStringPair(aReader, mFilename, mSourceLine) &&
+ JS_ReadUint32Pair(aReader, &mLineNumber,
+ mColumn.addressOfValueForTranscode()) &&
+ JS_ReadUint32Pair(aReader, &mTokenOffset, &mErrorNumber) &&
+ JS_ReadUint32Pair(aReader, &type, &exnType) &&
+ JS_ReadUint32Pair(aReader, &code, &result) &&
+ mStack.ReadStructuredCloneInternal(aCx, aReader))) {
+ return false;
+ }
+
+ if (type == uint32_t(Type::Uninitialized) || type >= uint32_t(Type::Max_) ||
+ exnType >= uint32_t(JSEXN_ERROR_LIMIT)) {
+ return false;
+ }
+
+ mType = Type(type);
+ mExnType = JSExnType(exnType);
+ mResult = nsresult(result);
+ mCode = code;
+
+ return true;
+}
+
+/* static */
+JSObject* ClonedErrorHolder::ReadStructuredClone(
+ JSContext* aCx, JSStructuredCloneReader* aReader,
+ StructuredCloneHolder* aHolder) {
+ // Keep the result object rooted across the call to ClonedErrorHolder::Release
+ // to avoid a potential rooting hazard.
+ JS::Rooted<JS::Value> errorVal(aCx);
+ {
+ UniquePtr<ClonedErrorHolder> ceh(new ClonedErrorHolder());
+ if (!ceh->Init(aCx, aReader) || !ceh->ToErrorValue(aCx, &errorVal)) {
+ return nullptr;
+ }
+ }
+ return &errorVal.toObject();
+}
+
+static JS::UniqueTwoByteChars ToNullTerminatedJSStringBuffer(
+ JSContext* aCx, const nsString& aStr) {
+ // Since nsString is null terminated, we can simply copy + 1 characters.
+ size_t nbytes = (aStr.Length() + 1) * sizeof(char16_t);
+ JS::UniqueTwoByteChars buffer(static_cast<char16_t*>(JS_malloc(aCx, nbytes)));
+ if (buffer) {
+ memcpy(buffer.get(), aStr.get(), nbytes);
+ }
+ return buffer;
+}
+
+static bool ToJSString(JSContext* aCx, const nsACString& aStr,
+ JS::MutableHandle<JSString*> aJSString) {
+ if (aStr.IsVoid()) {
+ aJSString.set(nullptr);
+ return true;
+ }
+ JS::Rooted<JS::Value> res(aCx);
+ if (xpc::NonVoidStringToJsval(aCx, NS_ConvertUTF8toUTF16(aStr), &res)) {
+ aJSString.set(res.toString());
+ return true;
+ }
+ return false;
+}
+
+bool ClonedErrorHolder::ToErrorValue(JSContext* aCx,
+ JS::MutableHandle<JS::Value> aResult) {
+ JS::Rooted<JS::Value> stackVal(aCx);
+ JS::Rooted<JSObject*> stack(aCx);
+
+ IgnoredErrorResult rv;
+ mStack.Read(xpc::CurrentNativeGlobal(aCx), aCx, &stackVal, rv);
+ // Note: We continue even if reading the stack fails, since we can still
+ // produce a useful error object even without a stack. That said, if decoding
+ // the stack fails, there's a pretty good chance that the rest of the message
+ // is corrupt, and there's no telling how useful the final result will
+ // actually be.
+ if (!rv.Failed() && stackVal.isObject()) {
+ stack = &stackVal.toObject();
+ // Make sure that this is really a saved frame. This mainly ensures that
+ // malicious code on the child side can't trigger a memory exploit by
+ // sending an incompatible data type, but also protects against potential
+ // issues like a cross-compartment wrapper being unexpectedly cut.
+ if (!js::IsSavedFrame(stack)) {
+ stack = nullptr;
+ }
+ }
+
+ if (mType == Type::JSError) {
+ JS::Rooted<JSString*> filename(aCx);
+ JS::Rooted<JSString*> message(aCx);
+
+ // For some unknown reason, we can end up with a void string in mFilename,
+ // which will cause filename to be null, which causes JS::CreateError() to
+ // crash. Make this code against robust against this by treating void
+ // strings as the empty string.
+ if (mFilename.IsVoid()) {
+ mFilename.Assign(""_ns);
+ }
+
+ // When fuzzing, we can also end up with the message to be null,
+ // so we should handle that case as well.
+ if (mMessage.IsVoid()) {
+ mMessage.Assign(""_ns);
+ }
+
+ if (!ToJSString(aCx, mFilename, &filename) ||
+ !ToJSString(aCx, mMessage, &message)) {
+ return false;
+ }
+ if (!JS::CreateError(aCx, mExnType, stack, filename, mLineNumber, mColumn,
+ nullptr, message, JS::NothingHandleValue, aResult)) {
+ return false;
+ }
+
+ if (!mSourceLine.IsVoid()) {
+ JS::Rooted<JSObject*> errObj(aCx, &aResult.toObject());
+ if (JSErrorReport* err = JS_ErrorFromException(aCx, errObj)) {
+ NS_ConvertUTF8toUTF16 sourceLine(mSourceLine);
+ // Because this string ends up being consumed as an nsDependentString
+ // in nsXPCComponents_Utils::ReportError, this needs to be a null
+ // terminated string.
+ //
+ // See Bug 1699569.
+ if (mTokenOffset >= sourceLine.Length()) {
+ // Corrupt data, leave linebuf unset.
+ } else if (JS::UniqueTwoByteChars buffer =
+ ToNullTerminatedJSStringBuffer(aCx, sourceLine)) {
+ err->initOwnedLinebuf(buffer.release(), sourceLine.Length(),
+ mTokenOffset);
+ } else {
+ // Just ignore OOM and continue if the string copy failed.
+ JS_ClearPendingException(aCx);
+ }
+ }
+ }
+
+ return true;
+ }
+
+ nsCOMPtr<nsIStackFrame> frame(exceptions::CreateStack(aCx, stack));
+
+ RefPtr<Exception> exn;
+ if (mType == Type::Exception) {
+ exn = new Exception(mMessage, mResult, mName, frame, nullptr);
+ } else {
+ MOZ_ASSERT(mType == Type::DOMException);
+ exn = new DOMException(mResult, mMessage, mName, mCode, frame);
+ }
+
+ return ToJSValue(aCx, exn, aResult);
+}
+
+bool ClonedErrorHolder::Holder::ReadStructuredCloneInternal(
+ JSContext* aCx, JSStructuredCloneReader* aReader) {
+ uint32_t length;
+ uint32_t version;
+ if (!JS_ReadUint32Pair(aReader, &length, &version)) {
+ return false;
+ }
+ if (length % 8 != 0) {
+ return false;
+ }
+
+ JSStructuredCloneData data(mStructuredCloneScope);
+ while (length) {
+ size_t size;
+ char* buffer = data.AllocateBytes(length, &size);
+ if (!buffer || !JS_ReadBytes(aReader, buffer, size)) {
+ return false;
+ }
+ length -= size;
+ }
+
+ mBuffer = MakeUnique<JSAutoStructuredCloneBuffer>(
+ mStructuredCloneScope, &StructuredCloneHolder::sCallbacks, this);
+ mBuffer->adopt(std::move(data), version, &StructuredCloneHolder::sCallbacks);
+ return true;
+}
diff --git a/dom/ipc/ClonedErrorHolder.h b/dom/ipc/ClonedErrorHolder.h
new file mode 100644
index 0000000000..862b43c78b
--- /dev/null
+++ b/dom/ipc/ClonedErrorHolder.h
@@ -0,0 +1,104 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/. */
+
+#ifndef mozilla_dom_ClonedErrorHolder_h
+#define mozilla_dom_ClonedErrorHolder_h
+
+#include "mozilla/dom/NonRefcountedDOMObject.h"
+#include "nsISupportsImpl.h"
+#include "js/ColumnNumber.h" // JS::ColumnNumberOneOrigin
+#include "js/ErrorReport.h"
+#include "js/TypeDecls.h"
+#include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/StructuredCloneHolder.h"
+#include "mozilla/Attributes.h"
+
+class nsIGlobalObject;
+class nsQueryActorChild;
+
+namespace mozilla {
+class ErrorResult;
+
+namespace dom {
+
+class ClonedErrorHolder final : public NonRefcountedDOMObject {
+ public:
+ static UniquePtr<ClonedErrorHolder> Constructor(const GlobalObject& aGlobal,
+ JS::Handle<JSObject*> aError,
+ ErrorResult& aRv);
+
+ static UniquePtr<ClonedErrorHolder> Create(JSContext* aCx,
+ JS::Handle<JSObject*> aError,
+ ErrorResult& aRv);
+
+ enum class Type : uint8_t {
+ Uninitialized,
+ JSError,
+ Exception,
+ DOMException,
+ Max_,
+ };
+
+ bool WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto,
+ JS::MutableHandle<JSObject*> aReflector);
+
+ bool WriteStructuredClone(JSContext* aCx, JSStructuredCloneWriter* aWriter,
+ StructuredCloneHolder* aHolder);
+
+ // Reads the structured clone data for the ClonedErrorHolder and returns the
+ // wrapped object (either a JS Error or an Exception/DOMException object)
+ // directly. Never returns an actual ClonedErrorHolder object.
+ static JSObject* ReadStructuredClone(JSContext* aCx,
+ JSStructuredCloneReader* aReader,
+ StructuredCloneHolder* aHolder);
+
+ private:
+ ClonedErrorHolder();
+
+ void Init(JSContext* aCx, JS::Handle<JSObject*> aError, ErrorResult& aRv);
+
+ bool Init(JSContext* aCx, JSStructuredCloneReader* aReader);
+
+ // Creates a new JS Error or Exception/DOMException object based on the
+ // values stored in the holder. Returns false and sets an exception on aCx
+ // if it fails.
+ bool ToErrorValue(JSContext* aCx, JS::MutableHandle<JS::Value> aResult);
+
+ class Holder final : public StructuredCloneHolder {
+ public:
+ using StructuredCloneHolder::StructuredCloneHolder;
+
+ bool ReadStructuredCloneInternal(JSContext* aCx,
+ JSStructuredCloneReader* aReader);
+ };
+
+ // Only a subset of the following fields are used, depending on the mType of
+ // the error stored:
+ nsCString mName; // Exception, DOMException
+ nsCString mMessage; // JSError, Exception, DOMException
+ nsCString mFilename; // JSError only
+ nsCString mSourceLine; // JSError only
+
+ uint32_t mLineNumber = 0; // JSError only
+ JS::ColumnNumberOneOrigin mColumn; // JSError only
+ uint32_t mTokenOffset = 0; // JSError only
+ uint32_t mErrorNumber = 0; // JSError only
+
+ Type mType = Type::Uninitialized;
+
+ uint16_t mCode = 0; // DOMException only
+ JSExnType mExnType = JSExnType(0); // JSError only
+ nsresult mResult = NS_OK; // Exception, DOMException
+
+ // JSError, Exception, DOMException
+ Holder mStack{Holder::CloningSupported, Holder::TransferringNotSupported,
+ Holder::StructuredCloneScope::DifferentProcess};
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // !defined(mozilla_dom_ClonedErrorHolder_h)
diff --git a/dom/ipc/CoalescedInputData.cpp b/dom/ipc/CoalescedInputData.cpp
new file mode 100644
index 0000000000..6336f86928
--- /dev/null
+++ b/dom/ipc/CoalescedInputData.cpp
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsRefreshDriver.h"
+#include "BrowserChild.h"
+#include "CoalescedInputData.h"
+#include "mozilla/PresShell.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+void CoalescedInputFlusher::StartObserver() {
+ nsRefreshDriver* refreshDriver = GetRefreshDriver();
+ if (mRefreshDriver && mRefreshDriver == refreshDriver) {
+ // Nothing to do if we already added an observer and it's same refresh
+ // driver.
+ return;
+ }
+ RemoveObserver();
+ if (refreshDriver) {
+ mRefreshDriver = refreshDriver;
+ mRefreshDriver->AddRefreshObserver(this, FlushType::Event,
+ "Coalesced input move flusher");
+ }
+}
+
+CoalescedInputFlusher::CoalescedInputFlusher(BrowserChild* aBrowserChild)
+ : mBrowserChild(aBrowserChild) {}
+
+void CoalescedInputFlusher::RemoveObserver() {
+ if (mRefreshDriver) {
+ mRefreshDriver->RemoveRefreshObserver(this, FlushType::Event);
+ mRefreshDriver = nullptr;
+ }
+}
+
+CoalescedInputFlusher::~CoalescedInputFlusher() = default;
+
+nsRefreshDriver* CoalescedInputFlusher::GetRefreshDriver() {
+ if (PresShell* presShell = mBrowserChild->GetTopLevelPresShell()) {
+ return presShell->GetRefreshDriver();
+ }
+ return nullptr;
+}
diff --git a/dom/ipc/CoalescedInputData.h b/dom/ipc/CoalescedInputData.h
new file mode 100644
index 0000000000..aeac9d99a0
--- /dev/null
+++ b/dom/ipc/CoalescedInputData.h
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_CoalescedInputData_h
+#define mozilla_dom_CoalescedInputData_h
+
+#include "mozilla/UniquePtr.h"
+#include "mozilla/layers/ScrollableLayerGuid.h"
+#include "nsRefreshObservers.h"
+
+class nsRefreshDriver;
+
+namespace mozilla::dom {
+
+class BrowserChild;
+
+template <class InputEventType>
+class CoalescedInputData {
+ protected:
+ using ScrollableLayerGuid = mozilla::layers::ScrollableLayerGuid;
+
+ UniquePtr<InputEventType> mCoalescedInputEvent;
+ ScrollableLayerGuid mGuid;
+ uint64_t mInputBlockId;
+
+ public:
+ CoalescedInputData() : mInputBlockId(0) {}
+
+ void RetrieveDataFrom(CoalescedInputData& aSource) {
+ mCoalescedInputEvent = std::move(aSource.mCoalescedInputEvent);
+ mGuid = aSource.mGuid;
+ mInputBlockId = aSource.mInputBlockId;
+ }
+
+ bool IsEmpty() { return !mCoalescedInputEvent; }
+
+ bool CanCoalesce(const InputEventType& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId);
+
+ UniquePtr<InputEventType> TakeCoalescedEvent() {
+ return std::move(mCoalescedInputEvent);
+ }
+
+ ScrollableLayerGuid GetScrollableLayerGuid() { return mGuid; }
+
+ uint64_t GetInputBlockId() { return mInputBlockId; }
+};
+
+class CoalescedInputFlusher : public nsARefreshObserver {
+ public:
+ explicit CoalescedInputFlusher(BrowserChild* aBrowserChild);
+
+ virtual void WillRefresh(mozilla::TimeStamp aTime) override = 0;
+
+ NS_INLINE_DECL_REFCOUNTING(CoalescedInputFlusher, override)
+
+ void StartObserver();
+ void RemoveObserver();
+
+ protected:
+ virtual ~CoalescedInputFlusher();
+
+ nsRefreshDriver* GetRefreshDriver();
+
+ BrowserChild* mBrowserChild;
+ RefPtr<nsRefreshDriver> mRefreshDriver;
+};
+} // namespace mozilla::dom
+
+#endif // mozilla_dom_CoalescedInputData_h
diff --git a/dom/ipc/CoalescedMouseData.cpp b/dom/ipc/CoalescedMouseData.cpp
new file mode 100644
index 0000000000..5aa445d2e0
--- /dev/null
+++ b/dom/ipc/CoalescedMouseData.cpp
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "base/basictypes.h"
+
+#include "CoalescedMouseData.h"
+#include "BrowserChild.h"
+
+#include "mozilla/PresShell.h"
+#include "mozilla/StaticPrefs_dom.h"
+#include "nsRefreshDriver.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+void CoalescedMouseData::Coalesce(const WidgetMouseEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId) {
+ if (IsEmpty()) {
+ mCoalescedInputEvent = MakeUnique<WidgetMouseEvent>(aEvent);
+ mGuid = aGuid;
+ mInputBlockId = aInputBlockId;
+ MOZ_ASSERT(!mCoalescedInputEvent->mCoalescedWidgetEvents);
+ } else {
+ MOZ_ASSERT(mGuid == aGuid);
+ MOZ_ASSERT(mInputBlockId == aInputBlockId);
+ MOZ_ASSERT(mCoalescedInputEvent->mModifiers == aEvent.mModifiers);
+ MOZ_ASSERT(mCoalescedInputEvent->mReason == aEvent.mReason);
+ MOZ_ASSERT(mCoalescedInputEvent->mInputSource == aEvent.mInputSource);
+ MOZ_ASSERT(mCoalescedInputEvent->mButton == aEvent.mButton);
+ MOZ_ASSERT(mCoalescedInputEvent->mButtons == aEvent.mButtons);
+ mCoalescedInputEvent->mTimeStamp = aEvent.mTimeStamp;
+ mCoalescedInputEvent->mRefPoint = aEvent.mRefPoint;
+ mCoalescedInputEvent->mPressure = aEvent.mPressure;
+ mCoalescedInputEvent->AssignPointerHelperData(aEvent);
+ }
+
+ if (aEvent.mMessage == eMouseMove) {
+ // PointerEvent::getCoalescedEvents is only applied to pointermove events.
+ if (!mCoalescedInputEvent->mCoalescedWidgetEvents) {
+ mCoalescedInputEvent->mCoalescedWidgetEvents =
+ new WidgetPointerEventHolder();
+ }
+ // Append current event in mCoalescedWidgetEvents. We use them to generate
+ // DOM events when content calls PointerEvent::getCoalescedEvents.
+ WidgetPointerEvent* event =
+ mCoalescedInputEvent->mCoalescedWidgetEvents->mEvents.AppendElement(
+ aEvent);
+
+ event->mMessage = ePointerMove;
+ event->mFlags.mBubbles = false;
+ event->mFlags.mCancelable = false;
+ }
+}
+
+bool CoalescedMouseData::CanCoalesce(const WidgetMouseEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId) {
+ MOZ_ASSERT(aEvent.mMessage == eMouseMove);
+ return !mCoalescedInputEvent ||
+ (!mCoalescedInputEvent->mFlags.mIsSynthesizedForTests &&
+ !aEvent.mFlags.mIsSynthesizedForTests &&
+ mCoalescedInputEvent->mModifiers == aEvent.mModifiers &&
+ mCoalescedInputEvent->mInputSource == aEvent.mInputSource &&
+ mCoalescedInputEvent->pointerId == aEvent.pointerId &&
+ mCoalescedInputEvent->mButton == aEvent.mButton &&
+ mCoalescedInputEvent->mButtons == aEvent.mButtons && mGuid == aGuid &&
+ mInputBlockId == aInputBlockId);
+}
+
+CoalescedMouseMoveFlusher::CoalescedMouseMoveFlusher(
+ BrowserChild* aBrowserChild)
+ : CoalescedInputFlusher(aBrowserChild) {}
+
+void CoalescedMouseMoveFlusher::WillRefresh(mozilla::TimeStamp aTime) {
+ MOZ_ASSERT(mRefreshDriver);
+ mBrowserChild->FlushAllCoalescedMouseData();
+ mBrowserChild->ProcessPendingCoalescedMouseDataAndDispatchEvents();
+}
+
+CoalescedMouseMoveFlusher::~CoalescedMouseMoveFlusher() { RemoveObserver(); }
diff --git a/dom/ipc/CoalescedMouseData.h b/dom/ipc/CoalescedMouseData.h
new file mode 100644
index 0000000000..4d09783ec4
--- /dev/null
+++ b/dom/ipc/CoalescedMouseData.h
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_CoalescedMouseData_h
+#define mozilla_dom_CoalescedMouseData_h
+
+#include "CoalescedInputData.h"
+#include "mozilla/MouseEvents.h"
+#include "nsRefreshObservers.h"
+
+class nsRefreshDriver;
+
+namespace mozilla::dom {
+
+class CoalescedMouseData final : public CoalescedInputData<WidgetMouseEvent> {
+ public:
+ CoalescedMouseData() { MOZ_COUNT_CTOR(mozilla::dom::CoalescedMouseData); }
+
+ ~CoalescedMouseData() { MOZ_COUNT_DTOR(mozilla::dom::CoalescedMouseData); }
+
+ void Coalesce(const WidgetMouseEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId);
+
+ bool CanCoalesce(const WidgetMouseEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId);
+};
+
+class CoalescedMouseMoveFlusher final : public CoalescedInputFlusher {
+ public:
+ explicit CoalescedMouseMoveFlusher(BrowserChild* aBrowserChild);
+
+ void WillRefresh(mozilla::TimeStamp aTime) override;
+
+ private:
+ ~CoalescedMouseMoveFlusher() override;
+};
+
+} // namespace mozilla::dom
+
+#endif // mozilla_dom_CoalescedMouseData_h
diff --git a/dom/ipc/CoalescedTouchData.cpp b/dom/ipc/CoalescedTouchData.cpp
new file mode 100644
index 0000000000..3c3bc71a0c
--- /dev/null
+++ b/dom/ipc/CoalescedTouchData.cpp
@@ -0,0 +1,132 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CoalescedTouchData.h"
+#include "BrowserChild.h"
+#include "mozilla/dom/PointerEventHandler.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+static const uint32_t sMaxTouchMoveIdentifiers = 10;
+
+void CoalescedTouchData::CreateCoalescedTouchEvent(
+ const WidgetTouchEvent& aEvent) {
+ MOZ_ASSERT(IsEmpty());
+ mCoalescedInputEvent = MakeUnique<WidgetTouchEvent>(aEvent);
+ for (size_t i = 0; i < mCoalescedInputEvent->mTouches.Length(); i++) {
+ const RefPtr<Touch>& touch = mCoalescedInputEvent->mTouches[i];
+ touch->mCoalescedWidgetEvents = MakeAndAddRef<WidgetPointerEventHolder>();
+ // Add an initial event into coalesced events, so
+ // the relevant pointer event would at least contain one coalesced event.
+ WidgetPointerEvent* event =
+ touch->mCoalescedWidgetEvents->mEvents.AppendElement(WidgetPointerEvent(
+ aEvent.IsTrusted(), ePointerMove, aEvent.mWidget));
+ PointerEventHandler::InitPointerEventFromTouch(*event, aEvent, *touch,
+ i == 0);
+ event->mFlags.mBubbles = false;
+ event->mFlags.mCancelable = false;
+ }
+}
+
+void CoalescedTouchData::Coalesce(const WidgetTouchEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId,
+ const nsEventStatus& aApzResponse) {
+ MOZ_ASSERT(aEvent.mMessage == eTouchMove);
+ if (IsEmpty()) {
+ CreateCoalescedTouchEvent(aEvent);
+ mGuid = aGuid;
+ mInputBlockId = aInputBlockId;
+ mApzResponse = aApzResponse;
+ } else {
+ MOZ_ASSERT(mGuid == aGuid);
+ MOZ_ASSERT(mInputBlockId == aInputBlockId);
+ MOZ_ASSERT(mCoalescedInputEvent->mModifiers == aEvent.mModifiers);
+ MOZ_ASSERT(mCoalescedInputEvent->mInputSource == aEvent.mInputSource);
+
+ for (size_t i = 0; i < aEvent.mTouches.Length(); i++) {
+ const RefPtr<Touch>& touch = aEvent.mTouches[i];
+ // Get the same touch in the original event
+ RefPtr<Touch> sameTouch = GetTouch(touch->Identifier());
+ // The checks in CoalescedTouchData::CanCoalesce ensure it should never
+ // be null.
+ MOZ_ASSERT(sameTouch);
+ MOZ_ASSERT(sameTouch->mCoalescedWidgetEvents);
+ MOZ_ASSERT(!sameTouch->mCoalescedWidgetEvents->mEvents.IsEmpty());
+ if (!sameTouch->Equals(touch)) {
+ sameTouch->SetSameAs(touch);
+ WidgetPointerEvent* event =
+ sameTouch->mCoalescedWidgetEvents->mEvents.AppendElement(
+ WidgetPointerEvent(aEvent.IsTrusted(), ePointerMove,
+ aEvent.mWidget));
+ PointerEventHandler::InitPointerEventFromTouch(*event, aEvent, *touch,
+ i == 0);
+ event->mFlags.mBubbles = false;
+ event->mFlags.mCancelable = false;
+ }
+ }
+
+ mCoalescedInputEvent->mTimeStamp = aEvent.mTimeStamp;
+ }
+}
+
+bool CoalescedTouchData::CanCoalesce(const WidgetTouchEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId,
+ const nsEventStatus& aApzResponse) {
+ MOZ_ASSERT(!IsEmpty());
+ if (mGuid != aGuid || mInputBlockId != aInputBlockId ||
+ mCoalescedInputEvent->mModifiers != aEvent.mModifiers ||
+ mCoalescedInputEvent->mInputSource != aEvent.mInputSource ||
+ aEvent.mTouches.Length() > sMaxTouchMoveIdentifiers) {
+ return false;
+ }
+
+ // Ensures both touchmove events have the same touches
+ if (aEvent.mTouches.Length() != mCoalescedInputEvent->mTouches.Length()) {
+ return false;
+ }
+ for (const RefPtr<Touch>& touch : aEvent.mTouches) {
+ if (!GetTouch(touch->Identifier())) {
+ return false;
+ }
+ }
+
+ // If one of them is eIgnore and the other one is eConsumeDoDefault,
+ // we always coalesce them to eConsumeDoDefault.
+ if (mApzResponse != aApzResponse) {
+ if (mApzResponse == nsEventStatus::nsEventStatus_eIgnore &&
+ aApzResponse == nsEventStatus::nsEventStatus_eConsumeDoDefault) {
+ mApzResponse = aApzResponse;
+ } else if (mApzResponse != nsEventStatus::nsEventStatus_eConsumeDoDefault ||
+ aApzResponse != nsEventStatus::nsEventStatus_eIgnore) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+Touch* CoalescedTouchData::GetTouch(int32_t aIdentifier) {
+ for (const RefPtr<Touch>& touch : mCoalescedInputEvent->mTouches) {
+ if (touch->Identifier() == aIdentifier) {
+ return touch;
+ }
+ }
+ return nullptr;
+}
+
+void CoalescedTouchMoveFlusher::WillRefresh(mozilla::TimeStamp aTime) {
+ MOZ_ASSERT(mRefreshDriver);
+ mBrowserChild->ProcessPendingCoalescedTouchData();
+}
+
+CoalescedTouchMoveFlusher::CoalescedTouchMoveFlusher(
+ BrowserChild* aBrowserChild)
+ : CoalescedInputFlusher(aBrowserChild) {}
+
+CoalescedTouchMoveFlusher::~CoalescedTouchMoveFlusher() { RemoveObserver(); }
diff --git a/dom/ipc/CoalescedTouchData.h b/dom/ipc/CoalescedTouchData.h
new file mode 100644
index 0000000000..1fc0efa262
--- /dev/null
+++ b/dom/ipc/CoalescedTouchData.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom__CoalescedTouchData_h
+#define mozilla_dom__CoalescedTouchData_h
+
+#include "CoalescedInputData.h"
+#include "mozilla/TouchEvents.h"
+
+namespace mozilla::dom {
+
+class CoalescedTouchData final : public CoalescedInputData<WidgetTouchEvent> {
+ public:
+ void CreateCoalescedTouchEvent(const WidgetTouchEvent& aEvent);
+
+ void Coalesce(const WidgetTouchEvent& aEvent,
+ const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId,
+ const nsEventStatus& aApzResponse);
+
+ bool CanCoalesce(const WidgetTouchEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId,
+ const nsEventStatus& aApzResponse);
+
+ nsEventStatus GetApzResponse() { return mApzResponse; }
+
+ private:
+ Touch* GetTouch(int32_t aIdentifier);
+
+ nsEventStatus mApzResponse = nsEventStatus_eIgnore;
+};
+
+class CoalescedTouchMoveFlusher final : public CoalescedInputFlusher {
+ public:
+ explicit CoalescedTouchMoveFlusher(BrowserChild* aBrowserChild);
+ void WillRefresh(mozilla::TimeStamp aTime) override;
+
+ private:
+ ~CoalescedTouchMoveFlusher() override;
+};
+}; // namespace mozilla::dom
+
+#endif // mozilla_dom_CoalescedTouchData_h
diff --git a/dom/ipc/CoalescedWheelData.cpp b/dom/ipc/CoalescedWheelData.cpp
new file mode 100644
index 0000000000..c8e23ac86d
--- /dev/null
+++ b/dom/ipc/CoalescedWheelData.cpp
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+#include "base/basictypes.h"
+
+#include "CoalescedWheelData.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+void CoalescedWheelData::Coalesce(const WidgetWheelEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId) {
+ if (IsEmpty()) {
+ mCoalescedInputEvent = MakeUnique<WidgetWheelEvent>(aEvent);
+ mGuid = aGuid;
+ mInputBlockId = aInputBlockId;
+ } else {
+ MOZ_ASSERT(mGuid == aGuid);
+ MOZ_ASSERT(mInputBlockId == aInputBlockId);
+ MOZ_ASSERT(mCoalescedInputEvent->mModifiers == aEvent.mModifiers);
+ MOZ_ASSERT(mCoalescedInputEvent->mDeltaMode == aEvent.mDeltaMode);
+ MOZ_ASSERT(mCoalescedInputEvent->mCanTriggerSwipe ==
+ aEvent.mCanTriggerSwipe);
+ mCoalescedInputEvent->mDeltaX += aEvent.mDeltaX;
+ mCoalescedInputEvent->mDeltaY += aEvent.mDeltaY;
+ mCoalescedInputEvent->mDeltaZ += aEvent.mDeltaZ;
+ mCoalescedInputEvent->mLineOrPageDeltaX += aEvent.mLineOrPageDeltaX;
+ mCoalescedInputEvent->mLineOrPageDeltaY += aEvent.mLineOrPageDeltaY;
+ mCoalescedInputEvent->mTimeStamp = aEvent.mTimeStamp;
+ }
+}
+
+bool CoalescedWheelData::CanCoalesce(const WidgetWheelEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId) {
+ MOZ_ASSERT(!IsEmpty());
+ return !mCoalescedInputEvent ||
+ (mCoalescedInputEvent->mRefPoint == aEvent.mRefPoint &&
+ mCoalescedInputEvent->mModifiers == aEvent.mModifiers &&
+ mCoalescedInputEvent->mDeltaMode == aEvent.mDeltaMode &&
+ mCoalescedInputEvent->mCanTriggerSwipe == aEvent.mCanTriggerSwipe &&
+ mGuid == aGuid && mInputBlockId == aInputBlockId);
+}
diff --git a/dom/ipc/CoalescedWheelData.h b/dom/ipc/CoalescedWheelData.h
new file mode 100644
index 0000000000..cd322d8874
--- /dev/null
+++ b/dom/ipc/CoalescedWheelData.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_CoalescedWheelData_h
+#define mozilla_dom_CoalescedWheelData_h
+
+#include "CoalescedInputData.h"
+#include "mozilla/MouseEvents.h"
+
+namespace mozilla::dom {
+
+class CoalescedWheelData final : public CoalescedInputData<WidgetWheelEvent> {
+ public:
+ void Coalesce(const WidgetWheelEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId);
+
+ bool CanCoalesce(const WidgetWheelEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId);
+};
+
+} // namespace mozilla::dom
+
+#endif // mozilla_dom_CoalescedWheelData_h
diff --git a/dom/ipc/ColorPickerParent.cpp b/dom/ipc/ColorPickerParent.cpp
new file mode 100644
index 0000000000..18cf197330
--- /dev/null
+++ b/dom/ipc/ColorPickerParent.cpp
@@ -0,0 +1,77 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ColorPickerParent.h"
+#include "nsComponentManagerUtils.h"
+#include "mozilla/dom/Document.h"
+#include "mozilla/Unused.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/BrowserParent.h"
+
+using mozilla::Unused;
+using namespace mozilla::dom;
+
+NS_IMPL_ISUPPORTS(ColorPickerParent::ColorPickerShownCallback,
+ nsIColorPickerShownCallback);
+
+NS_IMETHODIMP
+ColorPickerParent::ColorPickerShownCallback::Update(const nsAString& aColor) {
+ if (mColorPickerParent) {
+ Unused << mColorPickerParent->SendUpdate(aColor);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ColorPickerParent::ColorPickerShownCallback::Done(const nsAString& aColor) {
+ if (mColorPickerParent) {
+ Unused << ColorPickerParent::Send__delete__(mColorPickerParent, aColor);
+ }
+ return NS_OK;
+}
+
+void ColorPickerParent::ColorPickerShownCallback::Destroy() {
+ mColorPickerParent = nullptr;
+}
+
+bool ColorPickerParent::CreateColorPicker() {
+ mPicker = do_CreateInstance("@mozilla.org/colorpicker;1");
+ if (!mPicker) {
+ return false;
+ }
+
+ Element* ownerElement = BrowserParent::GetFrom(Manager())->GetOwnerElement();
+ if (!ownerElement) {
+ return false;
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> window = ownerElement->OwnerDoc()->GetWindow();
+ if (!window) {
+ return false;
+ }
+
+ return NS_SUCCEEDED(
+ mPicker->Init(window, mTitle, mInitialColor, mDefaultColors));
+}
+
+mozilla::ipc::IPCResult ColorPickerParent::RecvOpen() {
+ if (!CreateColorPicker()) {
+ Unused << Send__delete__(this, mInitialColor);
+ return IPC_OK();
+ }
+
+ MOZ_ASSERT(!mCallback);
+ mCallback = new ColorPickerShownCallback(this);
+
+ mPicker->Open(mCallback);
+ return IPC_OK();
+};
+
+void ColorPickerParent::ActorDestroy(ActorDestroyReason aWhy) {
+ if (mCallback) {
+ mCallback->Destroy();
+ }
+}
diff --git a/dom/ipc/ColorPickerParent.h b/dom/ipc/ColorPickerParent.h
new file mode 100644
index 0000000000..00b0828034
--- /dev/null
+++ b/dom/ipc/ColorPickerParent.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_ColorPickerParent_h
+#define mozilla_dom_ColorPickerParent_h
+
+#include "mozilla/dom/PColorPickerParent.h"
+#include "nsIColorPicker.h"
+
+namespace mozilla::dom {
+
+class ColorPickerParent : public PColorPickerParent {
+ public:
+ ColorPickerParent(const nsString& aTitle, const nsString& aInitialColor,
+ const nsTArray<nsString>& aDefaultColors)
+ : mTitle(aTitle),
+ mInitialColor(aInitialColor),
+ mDefaultColors(aDefaultColors.Clone()) {}
+
+ NS_INLINE_DECL_REFCOUNTING(ColorPickerParent, final)
+
+ virtual mozilla::ipc::IPCResult RecvOpen() override;
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ class ColorPickerShownCallback final : public nsIColorPickerShownCallback {
+ public:
+ explicit ColorPickerShownCallback(ColorPickerParent* aColorPickerParnet)
+ : mColorPickerParent(aColorPickerParnet) {}
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSICOLORPICKERSHOWNCALLBACK
+
+ void Destroy();
+
+ private:
+ ~ColorPickerShownCallback() = default;
+ RefPtr<ColorPickerParent> mColorPickerParent;
+ };
+
+ private:
+ virtual ~ColorPickerParent() = default;
+
+ bool CreateColorPicker();
+
+ RefPtr<ColorPickerShownCallback> mCallback;
+ nsCOMPtr<nsIColorPicker> mPicker;
+
+ nsString mTitle;
+ nsString mInitialColor;
+ nsTArray<nsString> mDefaultColors;
+};
+
+} // namespace mozilla::dom
+
+#endif // mozilla_dom_ColorPickerParent_h
diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp
new file mode 100644
index 0000000000..e3b0ebd795
--- /dev/null
+++ b/dom/ipc/ContentChild.cpp
@@ -0,0 +1,5081 @@
+/* -*- 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 "BrowserChild.h"
+#include "nsNSSComponent.h"
+#include "ContentChild.h"
+#include "GeckoProfiler.h"
+#include "HandlerServiceChild.h"
+#include "nsXPLookAndFeel.h"
+#include "mozilla/AppShutdown.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/BackgroundHangMonitor.h"
+#include "mozilla/BenchmarkStorageChild.h"
+#include "mozilla/FOGIPC.h"
+#include "GMPServiceChild.h"
+#include "Geolocation.h"
+#include "imgLoader.h"
+#include "ScrollingMetrics.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/ClipboardReadRequestChild.h"
+#include "mozilla/Components.h"
+#include "mozilla/HangDetails.h"
+#include "mozilla/LoadInfo.h"
+#include "mozilla/Logging.h"
+#include "mozilla/LookAndFeel.h"
+#include "mozilla/MemoryTelemetry.h"
+#include "mozilla/NullPrincipal.h"
+#include "mozilla/PerfStats.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/ProcessHangMonitorIPC.h"
+#include "mozilla/RemoteDecoderManagerChild.h"
+#include "mozilla/RemoteLazyInputStreamChild.h"
+#include "mozilla/SchedulerGroup.h"
+#include "mozilla/ScopeExit.h"
+#include "mozilla/SharedStyleSheetCache.h"
+#include "mozilla/SimpleEnumerator.h"
+#include "mozilla/SpinEventLoopUntil.h"
+#include "mozilla/StaticPrefs_browser.h"
+#include "mozilla/StaticPrefs_dom.h"
+#include "mozilla/StaticPrefs_fission.h"
+#include "mozilla/StaticPrefs_javascript.h"
+#include "mozilla/StaticPrefs_media.h"
+#include "mozilla/StaticPrefs_threads.h"
+#include "mozilla/StorageAccessAPIHelper.h"
+#include "mozilla/TelemetryIPC.h"
+#include "mozilla/Unused.h"
+#include "mozilla/WebBrowserPersistDocumentChild.h"
+#include "mozilla/devtools/HeapSnapshotTempFileHelperChild.h"
+#include "mozilla/dom/AutoSuppressEventHandlingAndSuspend.h"
+#include "mozilla/dom/BlobImpl.h"
+#include "mozilla/dom/BrowserBridgeHost.h"
+#include "mozilla/dom/BrowsingContext.h"
+#include "mozilla/dom/BrowsingContextGroup.h"
+#include "mozilla/dom/ChildProcessChannelListener.h"
+#include "mozilla/dom/ChildProcessMessageManager.h"
+#include "mozilla/dom/ClientManager.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/ContentProcessManager.h"
+#include "mozilla/dom/ContentPlaybackController.h"
+#include "mozilla/dom/ContentProcessMessageManager.h"
+#include "mozilla/dom/DataTransfer.h"
+#include "mozilla/dom/DocGroup.h"
+#include "mozilla/dom/ExternalHelperAppChild.h"
+#include "mozilla/dom/GetFilesHelper.h"
+#include "mozilla/dom/IPCBlobUtils.h"
+#include "mozilla/dom/InProcessChild.h"
+#include "mozilla/dom/JSActorService.h"
+#include "mozilla/dom/JSProcessActorBinding.h"
+#include "mozilla/dom/JSProcessActorChild.h"
+#include "mozilla/dom/LSObject.h"
+#include "mozilla/dom/MemoryReportRequest.h"
+#include "mozilla/dom/PSessionStorageObserverChild.h"
+#include "mozilla/dom/PostMessageEvent.h"
+#include "mozilla/dom/PushNotifier.h"
+#include "mozilla/dom/RemoteWorkerService.h"
+#include "mozilla/dom/ScreenOrientation.h"
+#include "mozilla/dom/ServiceWorkerManager.h"
+#include "mozilla/dom/SessionStorageManager.h"
+#include "mozilla/dom/URLClassifierChild.h"
+#include "mozilla/dom/UserActivation.h"
+#include "mozilla/dom/WindowGlobalChild.h"
+#include "mozilla/dom/WorkerDebugger.h"
+#include "mozilla/dom/WorkerDebuggerManager.h"
+#include "mozilla/dom/ipc/SharedMap.h"
+#include "mozilla/extensions/ExtensionsChild.h"
+#include "mozilla/extensions/StreamFilterParent.h"
+#include "mozilla/gfx/Logging.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/hal_sandbox/PHalChild.h"
+#include "mozilla/intl/L10nRegistry.h"
+#include "mozilla/intl/LocaleService.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/ipc/FileDescriptorUtils.h"
+#include "mozilla/ipc/GeckoChildProcessHost.h"
+#include "mozilla/ipc/ProcessChild.h"
+#include "mozilla/ipc/TestShellChild.h"
+#include "mozilla/layers/APZChild.h"
+#include "mozilla/layers/CompositorManagerChild.h"
+#include "mozilla/layers/ContentProcessController.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+#ifdef NS_PRINTING
+# include "mozilla/layout/RemotePrintJobChild.h"
+#endif
+#include "mozilla/loader/ScriptCacheActors.h"
+#include "mozilla/media/MediaChild.h"
+#include "mozilla/net/CaptivePortalService.h"
+#include "mozilla/net/ChildDNSService.h"
+#include "mozilla/net/CookieServiceChild.h"
+#include "mozilla/net/DocumentChannelChild.h"
+#include "mozilla/net/HttpChannelChild.h"
+#include "mozilla/widget/RemoteLookAndFeel.h"
+#include "mozilla/widget/ScreenManager.h"
+#include "mozilla/widget/WidgetMessageUtils.h"
+#include "nsBaseDragService.h"
+#include "nsDocShellLoadTypes.h"
+#include "nsFocusManager.h"
+#include "nsHttpHandler.h"
+#include "nsIConsoleService.h"
+#include "nsIInputStreamChannel.h"
+#include "nsILayoutHistoryState.h"
+#include "nsILoadGroup.h"
+#include "nsIOpenWindowInfo.h"
+#include "nsISimpleEnumerator.h"
+#include "nsIStringBundle.h"
+#include "nsIURIMutator.h"
+#include "nsQueryObject.h"
+#include "nsRefreshDriver.h"
+#include "nsSandboxFlags.h"
+#include "mozmemory.h"
+
+#include "ChildProfilerController.h"
+
+#if defined(MOZ_SANDBOX)
+# include "mozilla/SandboxSettings.h"
+# if defined(XP_WIN)
+# include "mozilla/sandboxTarget.h"
+# elif defined(XP_LINUX)
+# include "CubebUtils.h"
+# include "mozilla/Sandbox.h"
+# include "mozilla/SandboxInfo.h"
+# elif defined(XP_MACOSX)
+# include <CoreGraphics/CGError.h>
+# include "mozilla/Sandbox.h"
+# elif defined(__OpenBSD__)
+# include <err.h>
+# include <sys/stat.h>
+# include <unistd.h>
+
+# include <fstream>
+
+# include "BinaryPath.h"
+# include "SpecialSystemDirectory.h"
+# include "nsILineInputStream.h"
+# include "mozilla/ipc/UtilityProcessSandboxing.h"
+# endif
+# if defined(MOZ_DEBUG) && defined(ENABLE_TESTS)
+# include "mozilla/SandboxTestingChild.h"
+# endif
+#endif
+
+#include "SandboxHal.h"
+#include "mozInlineSpellChecker.h"
+#include "mozilla/GlobalStyleSheetCache.h"
+#include "nsAnonymousTemporaryFile.h"
+#include "nsCategoryManagerUtils.h"
+#include "nsClipboardProxy.h"
+#include "nsContentPermissionHelper.h"
+#include "nsDebugImpl.h"
+#include "nsDirectoryService.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsDocShell.h"
+#include "nsDocShellLoadState.h"
+#include "nsHashPropertyBag.h"
+#include "nsIConsoleListener.h"
+#include "nsICycleCollectorListener.h"
+#include "nsIDocShellTreeOwner.h"
+#include "nsIDocumentViewer.h"
+#include "nsIDragService.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIMemoryInfoDumper.h"
+#include "nsIMemoryReporter.h"
+#include "nsIObserverService.h"
+#include "nsIOService.h"
+#include "nsIScriptError.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsJSEnvironment.h"
+#include "nsJSUtils.h"
+#include "nsMemoryInfoDumper.h"
+#include "nsServiceManagerUtils.h"
+#include "nsStyleSheetService.h"
+#include "nsThreadManager.h"
+#include "nsVariant.h"
+#include "nsXULAppAPI.h"
+#include "IHistory.h"
+#include "ReferrerInfo.h"
+#include "base/message_loop.h"
+#include "base/process_util.h"
+#include "base/task.h"
+#include "mozilla/dom/BlobURLProtocolHandler.h"
+#include "mozilla/dom/PCycleCollectWithLogsChild.h"
+#include "mozilla/dom/PerformanceStorage.h"
+#include "nsChromeRegistryContent.h"
+#include "nsFrameMessageManager.h"
+#include "nsNetUtil.h"
+#include "nsWindowMemoryReporter.h"
+
+#ifdef MOZ_WEBRTC
+# include "jsapi/WebrtcGlobalChild.h"
+#endif
+
+#include "PermissionMessageUtils.h"
+#include "mozilla/Permission.h"
+#include "mozilla/PermissionManager.h"
+
+#if defined(MOZ_WIDGET_ANDROID)
+# include "APKOpen.h"
+# include <sched.h>
+#endif
+
+#ifdef XP_WIN
+# include <process.h>
+# define getpid _getpid
+# include "mozilla/WinDllServices.h"
+#endif
+
+#if defined(XP_MACOSX)
+# include "nsMacUtilsImpl.h"
+# include <sys/qos.h>
+#endif /* XP_MACOSX */
+
+#ifdef MOZ_X11
+# include "mozilla/X11Util.h"
+#endif
+
+#ifdef ACCESSIBILITY
+# include "nsAccessibilityService.h"
+# ifdef XP_WIN
+# include "mozilla/a11y/AccessibleWrap.h"
+# endif
+# include "mozilla/a11y/DocAccessible.h"
+# include "mozilla/a11y/DocManager.h"
+# include "mozilla/a11y/OuterDocAccessible.h"
+#endif
+
+#include "mozilla/dom/File.h"
+#include "mozilla/dom/MediaControllerBinding.h"
+
+#ifdef MOZ_WEBSPEECH
+# include "mozilla/dom/PSpeechSynthesisChild.h"
+#endif
+
+#include "ClearOnShutdown.h"
+#include "DomainPolicy.h"
+#include "GfxInfoBase.h"
+#include "MMPrinter.h"
+#include "mozilla/ipc/ProcessUtils.h"
+#include "mozilla/ipc/URIUtils.h"
+#include "VRManagerChild.h"
+#include "gfxPlatform.h"
+#include "gfxPlatformFontList.h"
+#include "mozilla/RemoteSpellCheckEngineChild.h"
+#include "mozilla/dom/TabContext.h"
+#include "mozilla/dom/ipc/StructuredCloneData.h"
+#include "mozilla/ipc/CrashReporterClient.h"
+#include "mozilla/net/NeckoMessageUtils.h"
+#include "mozilla/widget/PuppetBidiKeyboard.h"
+#include "nsContentUtils.h"
+#include "nsIPrincipal.h"
+#include "nsString.h"
+#include "nscore.h" // for NS_FREE_PERMANENT_DATA
+#include "private/pprio.h"
+
+#ifdef MOZ_WIDGET_GTK
+# include "mozilla/WidgetUtilsGtk.h"
+# include "nsAppRunner.h"
+# include <gtk/gtk.h>
+#endif
+
+#ifdef MOZ_CODE_COVERAGE
+# include "mozilla/CodeCoverageHandler.h"
+#endif
+
+extern mozilla::LazyLogModule gSHIPBFCacheLog;
+
+using namespace mozilla;
+using namespace mozilla::dom::ipc;
+using namespace mozilla::media;
+using namespace mozilla::embedding;
+using namespace mozilla::gmp;
+using namespace mozilla::hal_sandbox;
+using namespace mozilla::ipc;
+using namespace mozilla::intl;
+using namespace mozilla::layers;
+using namespace mozilla::layout;
+using namespace mozilla::net;
+using namespace mozilla::widget;
+using mozilla::loader::PScriptCacheChild;
+
+namespace geckoprofiler::markers {
+struct ProcessPriorityChange {
+ static constexpr Span<const char> MarkerTypeName() {
+ return MakeStringSpan("ProcessPriorityChange");
+ }
+ static void StreamJSONMarkerData(baseprofiler::SpliceableJSONWriter& aWriter,
+ const ProfilerString8View& aPreviousPriority,
+ const ProfilerString8View& aNewPriority) {
+ aWriter.StringProperty("Before", aPreviousPriority);
+ aWriter.StringProperty("After", aNewPriority);
+ }
+ static MarkerSchema MarkerTypeDisplay() {
+ using MS = MarkerSchema;
+ MS schema{MS::Location::MarkerChart, MS::Location::MarkerTable};
+ schema.AddKeyFormat("Before", MS::Format::String);
+ schema.AddKeyFormat("After", MS::Format::String);
+ schema.AddStaticLabelValue("Note",
+ "This is a notification of the priority change "
+ "that was done by the parent process");
+ schema.SetAllLabels(
+ "priority: {marker.data.Before} -> {marker.data.After}");
+ return schema;
+ }
+};
+
+struct ProcessPriority {
+ static constexpr Span<const char> MarkerTypeName() {
+ return MakeStringSpan("ProcessPriority");
+ }
+ static void StreamJSONMarkerData(baseprofiler::SpliceableJSONWriter& aWriter,
+ const ProfilerString8View& aPriority,
+ const ProfilingState& aProfilingState) {
+ aWriter.StringProperty("Priority", aPriority);
+ aWriter.StringProperty("Marker cause",
+ ProfilerString8View::WrapNullTerminatedString(
+ ProfilingStateToString(aProfilingState)));
+ }
+ static MarkerSchema MarkerTypeDisplay() {
+ using MS = MarkerSchema;
+ MS schema{MS::Location::MarkerChart, MS::Location::MarkerTable};
+ schema.AddKeyFormat("Priority", MS::Format::String);
+ schema.AddKeyFormat("Marker cause", MS::Format::String);
+ schema.SetAllLabels("priority: {marker.data.Priority}");
+ return schema;
+ }
+};
+} // namespace geckoprofiler::markers
+
+namespace mozilla {
+namespace dom {
+
+// IPC sender for remote GC/CC logging.
+class CycleCollectWithLogsChild final : public PCycleCollectWithLogsChild {
+ public:
+ NS_INLINE_DECL_REFCOUNTING(CycleCollectWithLogsChild)
+
+ class Sink final : public nsICycleCollectorLogSink {
+ NS_DECL_ISUPPORTS
+
+ Sink(CycleCollectWithLogsChild* aActor, const FileDescriptor& aGCLog,
+ const FileDescriptor& aCCLog) {
+ mActor = aActor;
+ mGCLog = FileDescriptorToFILE(aGCLog, "w");
+ mCCLog = FileDescriptorToFILE(aCCLog, "w");
+ }
+
+ NS_IMETHOD Open(FILE** aGCLog, FILE** aCCLog) override {
+ if (NS_WARN_IF(!mGCLog) || NS_WARN_IF(!mCCLog)) {
+ return NS_ERROR_FAILURE;
+ }
+ *aGCLog = mGCLog;
+ *aCCLog = mCCLog;
+ return NS_OK;
+ }
+
+ NS_IMETHOD CloseGCLog() override {
+ MOZ_ASSERT(mGCLog);
+ fclose(mGCLog);
+ mGCLog = nullptr;
+ mActor->SendCloseGCLog();
+ return NS_OK;
+ }
+
+ NS_IMETHOD CloseCCLog() override {
+ MOZ_ASSERT(mCCLog);
+ fclose(mCCLog);
+ mCCLog = nullptr;
+ mActor->SendCloseCCLog();
+ return NS_OK;
+ }
+
+ NS_IMETHOD GetFilenameIdentifier(nsAString& aIdentifier) override {
+ return UnimplementedProperty();
+ }
+
+ NS_IMETHOD SetFilenameIdentifier(const nsAString& aIdentifier) override {
+ return UnimplementedProperty();
+ }
+
+ NS_IMETHOD GetProcessIdentifier(int32_t* aIdentifier) override {
+ return UnimplementedProperty();
+ }
+
+ NS_IMETHOD SetProcessIdentifier(int32_t aIdentifier) override {
+ return UnimplementedProperty();
+ }
+
+ NS_IMETHOD GetGcLog(nsIFile** aPath) override {
+ return UnimplementedProperty();
+ }
+
+ NS_IMETHOD GetCcLog(nsIFile** aPath) override {
+ return UnimplementedProperty();
+ }
+
+ private:
+ ~Sink() {
+ if (mGCLog) {
+ fclose(mGCLog);
+ mGCLog = nullptr;
+ }
+ if (mCCLog) {
+ fclose(mCCLog);
+ mCCLog = nullptr;
+ }
+ // The XPCOM refcount drives the IPC lifecycle;
+ Unused << mActor->Send__delete__(mActor);
+ }
+
+ nsresult UnimplementedProperty() {
+ MOZ_ASSERT(false,
+ "This object is a remote GC/CC logger;"
+ " this property isn't meaningful.");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ RefPtr<CycleCollectWithLogsChild> mActor;
+ FILE* mGCLog;
+ FILE* mCCLog;
+ };
+
+ private:
+ ~CycleCollectWithLogsChild() = default;
+};
+
+NS_IMPL_ISUPPORTS(CycleCollectWithLogsChild::Sink, nsICycleCollectorLogSink);
+
+class AlertObserver {
+ public:
+ AlertObserver(nsIObserver* aObserver, const nsString& aData)
+ : mObserver(aObserver), mData(aData) {}
+
+ ~AlertObserver() = default;
+
+ nsCOMPtr<nsIObserver> mObserver;
+ nsString mData;
+};
+
+class ConsoleListener final : public nsIConsoleListener {
+ public:
+ explicit ConsoleListener(ContentChild* aChild) : mChild(aChild) {}
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSICONSOLELISTENER
+
+ private:
+ ~ConsoleListener() = default;
+
+ ContentChild* mChild;
+ friend class ContentChild;
+};
+
+NS_IMPL_ISUPPORTS(ConsoleListener, nsIConsoleListener)
+
+// Before we send the error to the parent process (which
+// involves copying the memory), truncate any long lines. CSS
+// errors in particular share the memory for long lines with
+// repeated errors, but the IPC communication we're about to do
+// will break that sharing, so we better truncate now.
+static void TruncateString(nsAString& aString) {
+ if (aString.Length() > 1000) {
+ aString.Truncate(1000);
+ }
+}
+
+NS_IMETHODIMP
+ConsoleListener::Observe(nsIConsoleMessage* aMessage) {
+ if (!mChild) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIScriptError> scriptError = do_QueryInterface(aMessage);
+ if (scriptError) {
+ nsAutoString msg, sourceName, sourceLine;
+ nsCString category;
+ uint32_t lineNum, colNum, flags;
+ bool fromPrivateWindow, fromChromeContext;
+
+ nsresult rv = scriptError->GetErrorMessage(msg);
+ NS_ENSURE_SUCCESS(rv, rv);
+ TruncateString(msg);
+ rv = scriptError->GetSourceName(sourceName);
+ NS_ENSURE_SUCCESS(rv, rv);
+ TruncateString(sourceName);
+ rv = scriptError->GetSourceLine(sourceLine);
+ NS_ENSURE_SUCCESS(rv, rv);
+ TruncateString(sourceLine);
+
+ rv = scriptError->GetCategory(getter_Copies(category));
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = scriptError->GetLineNumber(&lineNum);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = scriptError->GetColumnNumber(&colNum);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = scriptError->GetFlags(&flags);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = scriptError->GetIsFromPrivateWindow(&fromPrivateWindow);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = scriptError->GetIsFromChromeContext(&fromChromeContext);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ {
+ AutoJSAPI jsapi;
+ jsapi.Init();
+ JSContext* cx = jsapi.cx();
+
+ JS::Rooted<JS::Value> stack(cx);
+ rv = scriptError->GetStack(&stack);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (stack.isObject()) {
+ // Because |stack| might be a cross-compartment wrapper, we can't use it
+ // with JSAutoRealm. Use the stackGlobal for that.
+ JS::Rooted<JS::Value> stackGlobal(cx);
+ rv = scriptError->GetStackGlobal(&stackGlobal);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ JSAutoRealm ar(cx, &stackGlobal.toObject());
+
+ StructuredCloneData data;
+ ErrorResult err;
+ data.Write(cx, stack, err);
+ if (err.Failed()) {
+ return err.StealNSResult();
+ }
+
+ ClonedMessageData cloned;
+ if (!data.BuildClonedMessageData(cloned)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mChild->SendScriptErrorWithStack(
+ msg, sourceName, sourceLine, lineNum, colNum, flags, category,
+ fromPrivateWindow, fromChromeContext, cloned);
+ return NS_OK;
+ }
+ }
+
+ mChild->SendScriptError(msg, sourceName, sourceLine, lineNum, colNum, flags,
+ category, fromPrivateWindow, 0, fromChromeContext);
+ return NS_OK;
+ }
+
+ nsString msg;
+ nsresult rv = aMessage->GetMessageMoz(msg);
+ NS_ENSURE_SUCCESS(rv, rv);
+ mChild->SendConsoleMessage(msg);
+ return NS_OK;
+}
+
+#ifdef NIGHTLY_BUILD
+/**
+ * The singleton of this class is registered with the BackgroundHangMonitor as
+ * an annotator, so that the hang monitor can record whether or not there were
+ * pending input events when the thread hung.
+ */
+class PendingInputEventHangAnnotator final : public BackgroundHangAnnotator {
+ public:
+ virtual void AnnotateHang(BackgroundHangAnnotations& aAnnotations) override {
+ int32_t pending = ContentChild::GetSingleton()->GetPendingInputEvents();
+ if (pending > 0) {
+ aAnnotations.AddAnnotation(u"PendingInput"_ns, pending);
+ }
+ }
+
+ static PendingInputEventHangAnnotator sSingleton;
+};
+PendingInputEventHangAnnotator PendingInputEventHangAnnotator::sSingleton;
+#endif
+
+class ContentChild::ShutdownCanary final {};
+
+ContentChild* ContentChild::sSingleton;
+StaticAutoPtr<ContentChild::ShutdownCanary> ContentChild::sShutdownCanary;
+
+ContentChild::ContentChild()
+ : mID(uint64_t(-1)),
+ mIsForBrowser(false),
+ mIsAlive(true),
+ mShuttingDown(false) {
+ // This process is a content process, so it's clearly running in
+ // multiprocess mode!
+ nsDebugImpl::SetMultiprocessMode("Child");
+
+ // Our static analysis doesn't allow capturing ref-counted pointers in
+ // lambdas, so we need to hide it in a uintptr_t. This is safe because this
+ // lambda will be destroyed in ~ContentChild().
+ uintptr_t self = reinterpret_cast<uintptr_t>(this);
+ profiler_add_state_change_callback(
+ AllProfilingStates(),
+ [self](ProfilingState aProfilingState) {
+ const ContentChild* selfPtr =
+ reinterpret_cast<const ContentChild*>(self);
+ PROFILER_MARKER("Process Priority", OTHER,
+ mozilla::MarkerThreadId::MainThread(), ProcessPriority,
+ ProfilerString8View::WrapNullTerminatedString(
+ ProcessPriorityToString(selfPtr->mProcessPriority)),
+ aProfilingState);
+ },
+ self);
+
+ // When ContentChild is created, the observer service does not even exist.
+ // When ContentChild::RecvSetXPCOMProcessAttributes is called (the first
+ // IPDL call made on this object), shutdown may have already happened. Thus
+ // we create a canary here that relies upon getting cleared if shutdown
+ // happens without requiring the observer service at this time.
+ if (!sShutdownCanary) {
+ sShutdownCanary = new ShutdownCanary();
+ ClearOnShutdown(&sShutdownCanary, ShutdownPhase::XPCOMShutdown);
+ }
+}
+
+#ifdef _MSC_VER
+# pragma warning(push)
+# pragma warning( \
+ disable : 4722) /* Silence "destructor never returns" warning \
+ */
+#endif
+
+ContentChild::~ContentChild() {
+ profiler_remove_state_change_callback(reinterpret_cast<uintptr_t>(this));
+
+#ifndef NS_FREE_PERMANENT_DATA
+ MOZ_CRASH("Content Child shouldn't be destroyed.");
+#endif
+}
+
+#ifdef _MSC_VER
+# pragma warning(pop)
+#endif
+
+NS_INTERFACE_MAP_BEGIN(ContentChild)
+ NS_INTERFACE_MAP_ENTRY(nsIDOMProcessChild)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMProcessChild)
+NS_INTERFACE_MAP_END
+
+mozilla::ipc::IPCResult ContentChild::RecvSetXPCOMProcessAttributes(
+ XPCOMInitData&& aXPCOMInit, const StructuredCloneData& aInitialData,
+ FullLookAndFeel&& aLookAndFeelData, dom::SystemFontList&& aFontList,
+ Maybe<SharedMemoryHandle>&& aSharedUASheetHandle,
+ const uintptr_t& aSharedUASheetAddress,
+ nsTArray<SharedMemoryHandle>&& aSharedFontListBlocks,
+ const bool& aIsReadyForBackgroundProcessing) {
+ if (!sShutdownCanary) {
+ return IPC_OK();
+ }
+
+ mLookAndFeelData = std::move(aLookAndFeelData);
+ mFontList = std::move(aFontList);
+ mSharedFontListBlocks = std::move(aSharedFontListBlocks);
+
+ gfx::gfxVars::SetValuesForInitialize(aXPCOMInit.gfxNonDefaultVarUpdates());
+ PerfStats::SetCollectionMask(aXPCOMInit.perfStatsMask());
+ InitSharedUASheets(std::move(aSharedUASheetHandle), aSharedUASheetAddress);
+ InitXPCOM(std::move(aXPCOMInit), aInitialData,
+ aIsReadyForBackgroundProcessing);
+ InitGraphicsDeviceData(aXPCOMInit.contentDeviceData());
+ RefPtr<net::ChildDNSService> dnsServiceChild =
+ dont_AddRef(net::ChildDNSService::GetSingleton());
+ if (dnsServiceChild) {
+ dnsServiceChild->SetTRRDomain(aXPCOMInit.trrDomain());
+ dnsServiceChild->SetTRRModeInChild(aXPCOMInit.trrMode(),
+ aXPCOMInit.trrModeFromPref());
+ }
+ return IPC_OK();
+}
+
+class nsGtkNativeInitRunnable : public Runnable {
+ public:
+ nsGtkNativeInitRunnable() : Runnable("nsGtkNativeInitRunnable") {}
+
+ NS_IMETHOD Run() override {
+ LookAndFeel::NativeInit();
+ return NS_OK;
+ }
+};
+
+void ContentChild::Init(mozilla::ipc::UntypedEndpoint&& aEndpoint,
+ const char* aParentBuildID, uint64_t aChildID,
+ bool aIsForBrowser) {
+#ifdef MOZ_WIDGET_GTK
+ // When running X11 only build we need to pass a display down
+ // to gtk_init because it's not going to use the one from the environment
+ // on its own when deciding which backend to use, and when starting under
+ // XWayland, it may choose to start with the wayland backend
+ // instead of the x11 backend.
+ // The DISPLAY environment variable is normally set by the parent process.
+ // The MOZ_GDK_DISPLAY environment variable is set from nsAppRunner.cpp
+ // when --display is set by the command line.
+ if (!gfxPlatform::IsHeadless()) {
+ const char* display_name = PR_GetEnv("MOZ_GDK_DISPLAY");
+ if (!display_name) {
+ bool waylandEnabled = false;
+# ifdef MOZ_WAYLAND
+ waylandEnabled = IsWaylandEnabled();
+# endif
+ if (!waylandEnabled) {
+ display_name = PR_GetEnv("DISPLAY");
+ }
+ }
+ if (display_name) {
+ int argc = 3;
+ char option_name[] = "--display";
+ char* argv[] = {
+ // argv0 is unused because g_set_prgname() was called in
+ // XRE_InitChildProcess().
+ nullptr, option_name, const_cast<char*>(display_name), nullptr};
+ char** argvp = argv;
+ gtk_init(&argc, &argvp);
+ } else {
+ gtk_init(nullptr, nullptr);
+ }
+ }
+#endif
+
+#ifdef MOZ_X11
+ if (!gfxPlatform::IsHeadless()) {
+ // Do this after initializing GDK, or GDK will install its own handler.
+ XRE_InstallX11ErrorHandler();
+ }
+#endif
+
+ MOZ_ASSERT(!sSingleton, "only one ContentChild per child");
+
+ // Once we start sending IPC messages, we need the thread manager to be
+ // initialized so we can deal with the responses. Do that here before we
+ // try to construct the crash reporter.
+ nsresult rv = nsThreadManager::get().Init();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ MOZ_CRASH("Failed to initialize the thread manager in ContentChild::Init");
+ }
+
+ if (!aEndpoint.Bind(this)) {
+ MOZ_CRASH("Bind failed in ContentChild::Init");
+ }
+ sSingleton = this;
+
+ // If communications with the parent have broken down, take the process
+ // down so it's not hanging around.
+ GetIPCChannel()->SetAbortOnError(true);
+
+ // This must be checked before any IPDL message, which may hit sentinel
+ // errors due to parent and content processes having different
+ // versions.
+ MessageChannel* channel = GetIPCChannel();
+ if (channel && !channel->SendBuildIDsMatchMessage(aParentBuildID)) {
+ // We need to quit this process if the buildID doesn't match the parent's.
+ // This can occur when an update occurred in the background.
+ ProcessChild::QuickExit();
+ }
+
+#if defined(__OpenBSD__) && defined(MOZ_SANDBOX)
+ StartOpenBSDSandbox(GeckoProcessType_Content);
+#endif
+
+#ifdef MOZ_X11
+# ifdef MOZ_WIDGET_GTK
+ if (GdkIsX11Display() && !gfxPlatform::IsHeadless()) {
+ // Send the parent our X socket to act as a proxy reference for our X
+ // resources.
+ int xSocketFd = ConnectionNumber(DefaultXDisplay());
+ SendBackUpXResources(FileDescriptor(xSocketFd));
+ }
+# endif
+#endif
+
+ CrashReporterClient::InitSingleton(this);
+
+ mID = aChildID;
+ mIsForBrowser = aIsForBrowser;
+
+ SetProcessName("Web Content"_ns);
+
+#ifdef NIGHTLY_BUILD
+ // NOTE: We have to register the annotator on the main thread, as annotators
+ // only affect a single thread.
+ SchedulerGroup::Dispatch(
+ NS_NewRunnableFunction("RegisterPendingInputEventHangAnnotator", [] {
+ BackgroundHangMonitor::RegisterAnnotator(
+ PendingInputEventHangAnnotator::sSingleton);
+ }));
+#endif
+}
+
+void ContentChild::AddProfileToProcessName(const nsACString& aProfile) {
+ nsCOMPtr<nsIPrincipal> isolationPrincipal =
+ ContentParent::CreateRemoteTypeIsolationPrincipal(mRemoteType);
+ if (isolationPrincipal) {
+ // DEFAULT_PRIVATE_BROWSING_ID is the value when it's not private
+ if (isolationPrincipal->OriginAttributesRef().mPrivateBrowsingId !=
+ nsIScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID) {
+ return;
+ }
+ }
+
+ mProcessName = aProfile + ":"_ns + mProcessName; //<profile_name>:example.com
+}
+
+void ContentChild::SetProcessName(const nsACString& aName,
+ const nsACString* aSite,
+ const nsACString* aCurrentProfile) {
+ char* name;
+ if ((name = PR_GetEnv("MOZ_DEBUG_APP_PROCESS")) && aName.EqualsASCII(name)) {
+#ifdef XP_UNIX
+ printf_stderr("\n\nCHILDCHILDCHILDCHILD\n [%s] debug me @%d\n\n", name,
+ getpid());
+ sleep(30);
+#elif defined(XP_WIN)
+ // Windows has a decent JIT debugging story, so NS_DebugBreak does the
+ // right thing.
+ NS_DebugBreak(NS_DEBUG_BREAK,
+ "Invoking NS_DebugBreak() to debug child process", nullptr,
+ __FILE__, __LINE__);
+#endif
+ }
+
+ if (aSite) {
+ profiler_set_process_name(aName, aSite);
+ } else {
+ profiler_set_process_name(aName);
+ }
+
+ mProcessName = aName;
+
+ // Requires pref flip
+ if (aSite && StaticPrefs::fission_processSiteNames()) {
+ nsCOMPtr<nsIPrincipal> isolationPrincipal =
+ ContentParent::CreateRemoteTypeIsolationPrincipal(mRemoteType);
+ if (isolationPrincipal) {
+ // DEFAULT_PRIVATE_BROWSING_ID is the value when it's not private
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("private = %d, pref = %d",
+ isolationPrincipal->OriginAttributesRef().mPrivateBrowsingId !=
+ nsIScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID,
+ StaticPrefs::fission_processPrivateWindowSiteNames()));
+ if (isolationPrincipal->OriginAttributesRef().mPrivateBrowsingId ==
+ nsIScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID
+#ifdef NIGHTLY_BUILD
+ // Nightly can show site names for private windows, with a second pref
+ || StaticPrefs::fission_processPrivateWindowSiteNames()
+#endif
+ ) {
+#if !defined(XP_MACOSX)
+ // Mac doesn't have the 15-character limit Linux does
+ // Sets profiler process name
+ if (isolationPrincipal->SchemeIs("https")) {
+ nsAutoCString schemeless;
+ isolationPrincipal->GetHostPort(schemeless);
+ nsAutoCString originSuffix;
+ isolationPrincipal->GetOriginSuffix(originSuffix);
+ schemeless.Append(originSuffix);
+ mProcessName = schemeless;
+ } else
+#endif
+ {
+ mProcessName = *aSite;
+ }
+ }
+ }
+ }
+
+ if (StaticPrefs::fission_processProfileName() && aCurrentProfile &&
+ !aCurrentProfile->IsEmpty()) {
+ AddProfileToProcessName(*aCurrentProfile);
+ }
+
+ // else private window, don't change process name, or the pref isn't set
+ // mProcessName is always flat (mProcessName == aName)
+
+ mozilla::ipc::SetThisProcessName(mProcessName.get());
+
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("Changed name of process %d to %s", getpid(),
+ PromiseFlatCString(mProcessName).get()));
+}
+
+static nsresult GetCreateWindowParams(nsIOpenWindowInfo* aOpenWindowInfo,
+ nsDocShellLoadState* aLoadState,
+ bool aForceNoReferrer,
+ nsIReferrerInfo** aReferrerInfo,
+ nsIPrincipal** aTriggeringPrincipal,
+ nsIContentSecurityPolicy** aCsp) {
+ if (!aTriggeringPrincipal || !aCsp) {
+ NS_ERROR("aTriggeringPrincipal || aCsp is null");
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!aReferrerInfo) {
+ NS_ERROR("aReferrerInfo is null");
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIReferrerInfo> referrerInfo;
+ if (aForceNoReferrer) {
+ referrerInfo = new ReferrerInfo(nullptr, ReferrerPolicy::_empty, false);
+ }
+ if (aLoadState && !referrerInfo) {
+ referrerInfo = aLoadState->GetReferrerInfo();
+ }
+
+ RefPtr<BrowsingContext> parent = aOpenWindowInfo->GetParent();
+ nsCOMPtr<nsPIDOMWindowOuter> opener =
+ parent ? parent->GetDOMWindow() : nullptr;
+ if (!opener) {
+ nsCOMPtr<nsIPrincipal> nullPrincipal =
+ NullPrincipal::Create(aOpenWindowInfo->GetOriginAttributes());
+ if (!referrerInfo) {
+ referrerInfo = new ReferrerInfo(nullptr, ReferrerPolicy::_empty);
+ }
+
+ referrerInfo.swap(*aReferrerInfo);
+ NS_ADDREF(*aTriggeringPrincipal = nullPrincipal);
+ return NS_OK;
+ }
+
+ nsCOMPtr<Document> doc = opener->GetDoc();
+ NS_ADDREF(*aTriggeringPrincipal = doc->NodePrincipal());
+
+ nsCOMPtr<nsIContentSecurityPolicy> csp = doc->GetCsp();
+ if (csp) {
+ csp.forget(aCsp);
+ }
+
+ nsCOMPtr<nsIURI> baseURI = doc->GetDocBaseURI();
+ if (!baseURI) {
+ NS_ERROR("Document didn't return a base URI");
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!referrerInfo) {
+ referrerInfo = new ReferrerInfo(*doc);
+ }
+
+ referrerInfo.swap(*aReferrerInfo);
+ return NS_OK;
+}
+
+nsresult ContentChild::ProvideWindowCommon(
+ NotNull<BrowserChild*> aTabOpener, nsIOpenWindowInfo* aOpenWindowInfo,
+ uint32_t aChromeFlags, bool aCalledFromJS, nsIURI* aURI,
+ const nsAString& aName, const nsACString& aFeatures,
+ const UserActivation::Modifiers& aModifiers, bool aForceNoOpener,
+ bool aForceNoReferrer, bool aIsPopupRequested,
+ nsDocShellLoadState* aLoadState, bool* aWindowIsNew,
+ BrowsingContext** aReturn) {
+ *aReturn = nullptr;
+
+ nsAutoCString features(aFeatures);
+ nsAutoString name(aName);
+
+ nsresult rv;
+
+ RefPtr<BrowsingContext> parent = aOpenWindowInfo->GetParent();
+ MOZ_DIAGNOSTIC_ASSERT(parent, "We must have a parent BC");
+
+ // Block the attempt to open a new window if the opening BrowsingContext is
+ // not marked to use remote tabs. This ensures that the newly opened window is
+ // correctly remote.
+ if (NS_WARN_IF(!parent->UseRemoteTabs())) {
+ return NS_ERROR_ABORT;
+ }
+
+ bool useRemoteSubframes =
+ aChromeFlags & nsIWebBrowserChrome::CHROME_FISSION_WINDOW;
+
+ uint32_t parentSandboxFlags = parent->SandboxFlags();
+ if (Document* doc = parent->GetDocument()) {
+ parentSandboxFlags = doc->GetSandboxFlags();
+ }
+
+ // Certain conditions complicate the process of creating the new
+ // BrowsingContext, and prevent us from using the
+ // "CreateWindowInDifferentProcess" codepath.
+ // * With Fission enabled, process selection will happen during the load, so
+ // switching processes eagerly will not provide a benefit.
+ // * Windows created for printing must be created within the current process
+ // so that a static clone of the source document can be created.
+ // * Sandboxed popups require the full window creation codepath.
+ // * Loads with form or POST data require the full window creation codepath.
+ bool cannotLoadInDifferentProcess =
+ useRemoteSubframes || aOpenWindowInfo->GetIsForPrinting() ||
+ (parentSandboxFlags &
+ SANDBOX_PROPAGATES_TO_AUXILIARY_BROWSING_CONTEXTS) ||
+ (aLoadState &&
+ (aLoadState->IsFormSubmission() || aLoadState->PostDataStream()));
+ if (!cannotLoadInDifferentProcess) {
+ // If we're in a content process and we have noopener set, there's no reason
+ // to load in our process, so let's load it elsewhere!
+ bool loadInDifferentProcess =
+ aForceNoOpener && StaticPrefs::dom_noopener_newprocess_enabled();
+ if (loadInDifferentProcess) {
+ nsCOMPtr<nsIPrincipal> triggeringPrincipal;
+ nsCOMPtr<nsIContentSecurityPolicy> csp;
+ nsCOMPtr<nsIReferrerInfo> referrerInfo;
+ rv = GetCreateWindowParams(aOpenWindowInfo, aLoadState, aForceNoReferrer,
+ getter_AddRefs(referrerInfo),
+ getter_AddRefs(triggeringPrincipal),
+ getter_AddRefs(csp));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (name.LowerCaseEqualsLiteral("_blank")) {
+ name.Truncate();
+ }
+
+ MOZ_DIAGNOSTIC_ASSERT(!nsContentUtils::IsSpecialName(name));
+
+ Unused << SendCreateWindowInDifferentProcess(
+ aTabOpener, parent, aChromeFlags, aCalledFromJS, aURI, features,
+ aModifiers, name, triggeringPrincipal, csp, referrerInfo,
+ aOpenWindowInfo->GetOriginAttributes());
+
+ // We return NS_ERROR_ABORT, so that the caller knows that we've abandoned
+ // the window open as far as it is concerned.
+ return NS_ERROR_ABORT;
+ }
+ }
+
+ TabId tabId(nsContentUtils::GenerateTabId());
+
+ // We need to assign a TabGroup to the PBrowser actor before we send it to the
+ // parent. Otherwise, the parent could send messages to us before we have a
+ // proper TabGroup for that actor.
+ RefPtr<BrowsingContext> openerBC;
+ if (!aForceNoOpener) {
+ openerBC = parent;
+ }
+
+ RefPtr<BrowsingContext> browsingContext = BrowsingContext::CreateDetached(
+ nullptr, openerBC, nullptr, aName, BrowsingContext::Type::Content,
+ aIsPopupRequested);
+ MOZ_ALWAYS_SUCCEEDS(browsingContext->SetRemoteTabs(true));
+ MOZ_ALWAYS_SUCCEEDS(browsingContext->SetRemoteSubframes(useRemoteSubframes));
+ MOZ_ALWAYS_SUCCEEDS(browsingContext->SetOriginAttributes(
+ aOpenWindowInfo->GetOriginAttributes()));
+
+ browsingContext->InitPendingInitialization(true);
+ auto unsetPending = MakeScopeExit([browsingContext]() {
+ Unused << browsingContext->SetPendingInitialization(false);
+ });
+
+ browsingContext->EnsureAttached();
+
+ // The initial about:blank document we generate within the nsDocShell will
+ // almost certainly be replaced at some point. Unfortunately, getting the
+ // principal right here causes bugs due to frame scripts not getting events
+ // they expect, due to the real initial about:blank not being created yet.
+ //
+ // For this reason, we intentionally mispredict the initial principal here, so
+ // that we can act the same as we did before when not predicting a result
+ // principal. This `PWindowGlobal` will almost immediately be destroyed.
+ nsCOMPtr<nsIPrincipal> initialPrincipal =
+ NullPrincipal::Create(browsingContext->OriginAttributesRef());
+ WindowGlobalInit windowInit = WindowGlobalActor::AboutBlankInitializer(
+ browsingContext, initialPrincipal);
+
+ RefPtr<WindowGlobalChild> windowChild =
+ WindowGlobalChild::CreateDisconnected(windowInit);
+ if (NS_WARN_IF(!windowChild)) {
+ return NS_ERROR_ABORT;
+ }
+
+ auto newChild = MakeNotNull<RefPtr<BrowserChild>>(
+ this, tabId, *aTabOpener, browsingContext, aChromeFlags,
+ /* aIsTopLevel */ true);
+
+ if (IsShuttingDown()) {
+ return NS_ERROR_ABORT;
+ }
+
+ // Open a remote endpoint for our PBrowser actor.
+ ManagedEndpoint<PBrowserParent> parentEp = OpenPBrowserEndpoint(newChild);
+ if (NS_WARN_IF(!parentEp.IsValid())) {
+ return NS_ERROR_ABORT;
+ }
+
+ // Open a remote endpoint for our PWindowGlobal actor.
+ ManagedEndpoint<PWindowGlobalParent> windowParentEp =
+ newChild->OpenPWindowGlobalEndpoint(windowChild);
+ if (NS_WARN_IF(!windowParentEp.IsValid())) {
+ return NS_ERROR_ABORT;
+ }
+
+ // Tell the parent process to set up its PBrowserParent.
+ PopupIPCTabContext ipcContext(aTabOpener, 0);
+ if (NS_WARN_IF(!SendConstructPopupBrowser(
+ std::move(parentEp), std::move(windowParentEp), tabId, ipcContext,
+ windowInit, aChromeFlags))) {
+ return NS_ERROR_ABORT;
+ }
+
+ windowChild->Init();
+ auto guardNullWindowGlobal = MakeScopeExit([&] {
+ if (!windowChild->GetWindowGlobal()) {
+ windowChild->Destroy();
+ }
+ });
+
+ // Now that |newChild| has had its IPC link established, call |Init| to set it
+ // up.
+ // XXX: This MOZ_KnownLive is only necessary because the static analysis can't
+ // tell that NotNull<RefPtr<BrowserChild>> is a strong pointer.
+ RefPtr<nsPIDOMWindowOuter> parentWindow =
+ parent ? parent->GetDOMWindow() : nullptr;
+ if (NS_FAILED(MOZ_KnownLive(newChild)->Init(parentWindow, windowChild))) {
+ return NS_ERROR_ABORT;
+ }
+
+ // Set to true when we're ready to return from this function.
+ bool ready = false;
+
+ // NOTE: Capturing by reference here is safe, as this function won't return
+ // until one of these callbacks is called.
+ auto resolve = [&](CreatedWindowInfo&& info) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ rv = info.rv();
+ *aWindowIsNew = info.windowOpened();
+ nsTArray<FrameScriptInfo> frameScripts(std::move(info.frameScripts()));
+ uint32_t maxTouchPoints = info.maxTouchPoints();
+ DimensionInfo dimensionInfo = std::move(info.dimensions());
+
+ // Once this function exits, we should try to exit the nested event loop.
+ ready = true;
+
+ // NOTE: We have to handle this immediately in the resolve callback in order
+ // to make sure that we don't process any more IPC messages before returning
+ // from ProvideWindowCommon.
+
+ // Handle the error which we got back from the parent process, if we got
+ // one.
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ if (!*aWindowIsNew) {
+ rv = NS_ERROR_ABORT;
+ return;
+ }
+
+ // If the BrowserChild has been torn down, we don't need to do this anymore.
+ if (NS_WARN_IF(!newChild->IPCOpen() || newChild->IsDestroyed())) {
+ rv = NS_ERROR_ABORT;
+ return;
+ }
+
+ ParentShowInfo showInfo(u""_ns, /* fakeShowInfo = */ true,
+ /* isTransparent = */ false,
+ newChild->WebWidget()->GetDPI(),
+ newChild->WebWidget()->RoundsWidgetCoordinatesTo(),
+ newChild->WebWidget()->GetDefaultScale().scale);
+
+ newChild->SetMaxTouchPoints(maxTouchPoints);
+
+ if (aForceNoOpener || !parent) {
+ MOZ_DIAGNOSTIC_ASSERT(!browsingContext->HadOriginalOpener());
+ MOZ_DIAGNOSTIC_ASSERT(browsingContext->GetOpenerId() == 0);
+ } else {
+ MOZ_DIAGNOSTIC_ASSERT(browsingContext->HadOriginalOpener());
+ MOZ_DIAGNOSTIC_ASSERT(browsingContext->GetOpenerId() == parent->Id());
+ }
+
+ // Unfortunately we don't get a window unless we've shown the frame. That's
+ // pretty bogus; see bug 763602.
+ newChild->DoFakeShow(showInfo);
+
+ newChild->RecvUpdateDimensions(dimensionInfo);
+
+ for (size_t i = 0; i < frameScripts.Length(); i++) {
+ FrameScriptInfo& info = frameScripts[i];
+ if (!newChild->RecvLoadRemoteScript(info.url(),
+ info.runInGlobalScope())) {
+ MOZ_CRASH();
+ }
+ }
+
+ if (xpc::IsInAutomation()) {
+ if (nsCOMPtr<nsPIDOMWindowOuter> outer =
+ do_GetInterface(newChild->WebNavigation())) {
+ nsCOMPtr<nsIObserverService> obs(services::GetObserverService());
+ obs->NotifyObservers(
+ outer, "dangerous:test-only:new-browser-child-ready", nullptr);
+ }
+ }
+
+ browsingContext.forget(aReturn);
+ };
+
+ // NOTE: Capturing by reference here is safe, as this function won't return
+ // until one of these callbacks is called.
+ auto reject = [&](ResponseRejectReason) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ NS_WARNING("windowCreated promise rejected");
+ rv = NS_ERROR_NOT_AVAILABLE;
+ ready = true;
+ };
+
+ // Send down the request to open the window.
+ nsCOMPtr<nsIPrincipal> triggeringPrincipal;
+ nsCOMPtr<nsIContentSecurityPolicy> csp;
+ nsCOMPtr<nsIReferrerInfo> referrerInfo;
+ rv = GetCreateWindowParams(aOpenWindowInfo, aLoadState, aForceNoReferrer,
+ getter_AddRefs(referrerInfo),
+ getter_AddRefs(triggeringPrincipal),
+ getter_AddRefs(csp));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ SendCreateWindow(aTabOpener, parent, newChild, aChromeFlags, aCalledFromJS,
+ aOpenWindowInfo->GetIsForPrinting(),
+ aOpenWindowInfo->GetIsForWindowDotPrint(), aURI, features,
+ aModifiers, triggeringPrincipal, csp, referrerInfo,
+ aOpenWindowInfo->GetOriginAttributes(), std::move(resolve),
+ std::move(reject));
+
+ // =======================
+ // Begin Nested Event Loop
+ // =======================
+
+ // We have to wait for a response from SendCreateWindow or with information
+ // we're going to need to return from this function, So we spin a nested event
+ // loop until they get back to us.
+
+ {
+ // Suppress event handling for all contexts in our BrowsingContextGroup so
+ // that event handlers cannot target our new window while it's still being
+ // opened. Note that pending events that were suppressed while our blocker
+ // was active will be dispatched asynchronously from a runnable dispatched
+ // to the main event loop after this function returns, not immediately when
+ // we leave this scope.
+ AutoSuppressEventHandlingAndSuspend seh(browsingContext->Group());
+
+ AutoNoJSAPI nojsapi;
+
+ // Spin the event loop until we get a response. Callers of this function
+ // already have to guard against an inner event loop spinning in the
+ // non-e10s case because of the need to spin one to create a new chrome
+ // window.
+ SpinEventLoopUntil("ContentChild::ProvideWindowCommon"_ns,
+ [&]() { return ready; });
+ MOZ_RELEASE_ASSERT(ready,
+ "We are on the main thread, so we should not exit this "
+ "loop without ready being true.");
+ }
+
+ // =====================
+ // End Nested Event Loop
+ // =====================
+
+ // It's possible for our new BrowsingContext to become discarded during the
+ // nested event loop, in which case we shouldn't return it, since our callers
+ // will generally not be prepared to deal with that.
+ if (*aReturn && (*aReturn)->IsDiscarded()) {
+ NS_RELEASE(*aReturn);
+ return NS_ERROR_ABORT;
+ }
+
+ // We should have the results already set by the callbacks.
+ MOZ_ASSERT_IF(NS_SUCCEEDED(rv), *aReturn);
+ return rv;
+}
+
+bool ContentChild::IsAlive() const { return mIsAlive; }
+
+bool ContentChild::IsShuttingDown() const { return mShuttingDown; }
+
+void ContentChild::GetProcessName(nsACString& aName) const {
+ aName = mProcessName;
+}
+
+/* static */
+void ContentChild::AppendProcessId(nsACString& aName) {
+ if (!aName.IsEmpty()) {
+ aName.Append(' ');
+ }
+ unsigned pid = getpid();
+ aName.Append(nsPrintfCString("(pid %u)", pid));
+}
+
+void ContentChild::InitGraphicsDeviceData(const ContentDeviceData& aData) {
+ gfxPlatform::InitChild(aData);
+}
+
+void ContentChild::InitSharedUASheets(Maybe<SharedMemoryHandle>&& aHandle,
+ uintptr_t aAddress) {
+ MOZ_ASSERT_IF(!aHandle, !aAddress);
+
+ if (!aAddress) {
+ return;
+ }
+
+ // Map the shared memory storing the user agent style sheets. Do this as
+ // early as possible to maximize the chance of being able to map at the
+ // address we want.
+ GlobalStyleSheetCache::SetSharedMemory(std::move(*aHandle), aAddress);
+}
+
+void ContentChild::InitXPCOM(
+ XPCOMInitData&& aXPCOMInit,
+ const mozilla::dom::ipc::StructuredCloneData& aInitialData,
+ bool aIsReadyForBackgroundProcessing) {
+#ifdef MOZ_WIDGET_GTK
+ // LookAndFeel::NativeInit takes a long time to run on Linux, here we schedule
+ // it as soon as possible after BackgroundChild::Startup to give
+ // it chance to run ahead of ConstructBrowser
+ nsCOMPtr<nsIRunnable> event = new nsGtkNativeInitRunnable();
+ NS_DispatchToMainThreadQueue(event.forget(), EventQueuePriority::Idle);
+#endif
+
+#if defined(XP_WIN)
+ // DLL services untrusted modules processing depends on
+ // BackgroundChild::Startup having been called
+ RefPtr<DllServices> dllSvc(DllServices::Get());
+ dllSvc->StartUntrustedModulesProcessor(aIsReadyForBackgroundProcessing);
+#endif // defined(XP_WIN)
+
+ PBackgroundChild* actorChild = BackgroundChild::GetOrCreateForCurrentThread();
+ if (NS_WARN_IF(!actorChild)) {
+ MOZ_ASSERT_UNREACHABLE("PBackground init can't fail at this point");
+ return;
+ }
+
+ ClientManager::Startup();
+
+ // RemoteWorkerService will be initialized in RecvRemoteType, to avoid to
+ // register it to the RemoteWorkerManager while it is still a prealloc
+ // remoteType and defer it to the point the child process is assigned a.
+ // actual remoteType.
+
+ nsCOMPtr<nsIConsoleService> svc(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
+ if (!svc) {
+ NS_WARNING("Couldn't acquire console service");
+ return;
+ }
+
+ mConsoleListener = new ConsoleListener(this);
+ if (NS_FAILED(svc->RegisterListener(mConsoleListener)))
+ NS_WARNING("Couldn't register console listener for child process");
+
+ mAvailableDictionaries = std::move(aXPCOMInit.dictionaries());
+
+ RecvSetOffline(aXPCOMInit.isOffline());
+ RecvSetConnectivity(aXPCOMInit.isConnected());
+
+ LocaleService::GetInstance()->AssignAppLocales(aXPCOMInit.appLocales());
+ LocaleService::GetInstance()->AssignRequestedLocales(
+ aXPCOMInit.requestedLocales());
+
+ L10nRegistry::RegisterFileSourcesFromParentProcess(
+ aXPCOMInit.l10nFileSources());
+
+ RecvSetCaptivePortalState(aXPCOMInit.captivePortalState());
+ RecvBidiKeyboardNotify(aXPCOMInit.isLangRTL(),
+ aXPCOMInit.haveBidiKeyboards());
+
+ if (aXPCOMInit.domainPolicy().active()) {
+ nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
+ MOZ_ASSERT(ssm);
+ ssm->ActivateDomainPolicyInternal(getter_AddRefs(mPolicy));
+ if (!mPolicy) {
+ MOZ_CRASH("Failed to activate domain policy.");
+ }
+ mPolicy->ApplyClone(&aXPCOMInit.domainPolicy());
+ }
+
+ nsCOMPtr<nsIClipboard> clipboard(
+ do_GetService("@mozilla.org/widget/clipboard;1"));
+ if (nsCOMPtr<nsIClipboardProxy> clipboardProxy =
+ do_QueryInterface(clipboard)) {
+ clipboardProxy->SetCapabilities(aXPCOMInit.clipboardCaps());
+ }
+
+ {
+ AutoJSAPI jsapi;
+ if (NS_WARN_IF(!jsapi.Init(xpc::PrivilegedJunkScope()))) {
+ MOZ_CRASH();
+ }
+ ErrorResult rv;
+ JS::Rooted<JS::Value> data(jsapi.cx());
+ mozilla::dom::ipc::StructuredCloneData id;
+ id.Copy(aInitialData);
+ id.Read(jsapi.cx(), &data, rv);
+ if (NS_WARN_IF(rv.Failed())) {
+ MOZ_CRASH();
+ }
+ auto* global = ContentProcessMessageManager::Get();
+ global->SetInitialProcessData(data);
+ }
+
+ // The stylesheet cache is not ready yet. Store this URL for future use.
+ nsCOMPtr<nsIURI> ucsURL = std::move(aXPCOMInit.userContentSheetURL());
+ GlobalStyleSheetCache::SetUserContentCSSURL(ucsURL);
+
+ GfxInfoBase::SetFeatureStatus(std::move(aXPCOMInit.gfxFeatureStatus()));
+
+ // Initialize the RemoteDecoderManager thread and its associated PBackground
+ // channel.
+ RemoteDecoderManagerChild::Init();
+
+ Preferences::RegisterCallbackAndCall(&OnFissionBlocklistPrefChange,
+ kFissionEnforceBlockList);
+ Preferences::RegisterCallbackAndCall(&OnFissionBlocklistPrefChange,
+ kFissionOmitBlockListValues);
+
+ // Set the dynamic scalar definitions for this process.
+ TelemetryIPC::AddDynamicScalarDefinitions(aXPCOMInit.dynamicScalarDefs());
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvRequestMemoryReport(
+ const uint32_t& aGeneration, const bool& aAnonymize,
+ const bool& aMinimizeMemoryUsage,
+ const Maybe<mozilla::ipc::FileDescriptor>& aDMDFile,
+ const RequestMemoryReportResolver& aResolver) {
+ nsCString process;
+ if (aAnonymize || mRemoteType.IsEmpty()) {
+ GetProcessName(process);
+ } else {
+ process = mRemoteType;
+ }
+ AppendProcessId(process);
+ MOZ_ASSERT(!process.IsEmpty());
+
+ MemoryReportRequestClient::Start(
+ aGeneration, aAnonymize, aMinimizeMemoryUsage, aDMDFile, process,
+ [&](const MemoryReport& aReport) {
+ Unused << GetSingleton()->SendAddMemoryReport(aReport);
+ },
+ aResolver);
+ return IPC_OK();
+}
+
+#if defined(XP_WIN)
+mozilla::ipc::IPCResult ContentChild::RecvGetUntrustedModulesData(
+ GetUntrustedModulesDataResolver&& aResolver) {
+ RefPtr<DllServices> dllSvc(DllServices::Get());
+ dllSvc->GetUntrustedModulesData()->Then(
+ GetMainThreadSerialEventTarget(), __func__,
+ [aResolver](Maybe<UntrustedModulesData>&& aData) {
+ aResolver(std::move(aData));
+ },
+ [aResolver](nsresult aReason) { aResolver(Nothing()); });
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvUnblockUntrustedModulesThread() {
+ if (nsCOMPtr<nsIObserverService> obs =
+ mozilla::services::GetObserverService()) {
+ obs->NotifyObservers(nullptr, "unblock-untrusted-modules-thread", nullptr);
+ }
+ return IPC_OK();
+}
+#endif // defined(XP_WIN)
+
+PCycleCollectWithLogsChild* ContentChild::AllocPCycleCollectWithLogsChild(
+ const bool& aDumpAllTraces, const FileDescriptor& aGCLog,
+ const FileDescriptor& aCCLog) {
+ return do_AddRef(new CycleCollectWithLogsChild()).take();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvPCycleCollectWithLogsConstructor(
+ PCycleCollectWithLogsChild* aActor, const bool& aDumpAllTraces,
+ const FileDescriptor& aGCLog, const FileDescriptor& aCCLog) {
+ // The sink's destructor is called when the last reference goes away, which
+ // will cause the actor to be closed down.
+ auto* actor = static_cast<CycleCollectWithLogsChild*>(aActor);
+ RefPtr<CycleCollectWithLogsChild::Sink> sink =
+ new CycleCollectWithLogsChild::Sink(actor, aGCLog, aCCLog);
+
+ // Invoke the dumper, which will take a reference to the sink.
+ nsCOMPtr<nsIMemoryInfoDumper> dumper =
+ do_GetService("@mozilla.org/memory-info-dumper;1");
+ dumper->DumpGCAndCCLogsToSink(aDumpAllTraces, sink);
+ return IPC_OK();
+}
+
+bool ContentChild::DeallocPCycleCollectWithLogsChild(
+ PCycleCollectWithLogsChild* aActor) {
+ RefPtr<CycleCollectWithLogsChild> actor =
+ dont_AddRef(static_cast<CycleCollectWithLogsChild*>(aActor));
+ return true;
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvInitGMPService(
+ Endpoint<PGMPServiceChild>&& aGMPService) {
+ if (!GMPServiceChild::Create(std::move(aGMPService))) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvInitProfiler(
+ Endpoint<PProfilerChild>&& aEndpoint) {
+ mProfilerController = ChildProfilerController::Create(std::move(aEndpoint));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvGMPsChanged(
+ nsTArray<GMPCapabilityData>&& capabilities) {
+ GeckoMediaPluginServiceChild::UpdateGMPCapabilities(std::move(capabilities));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvInitProcessHangMonitor(
+ Endpoint<PProcessHangMonitorChild>&& aHangMonitor) {
+ CreateHangMonitorChild(std::move(aHangMonitor));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::GetResultForRenderingInitFailure(
+ base::ProcessId aOtherPid) {
+ if (aOtherPid == base::GetCurrentProcId() || aOtherPid == OtherPid()) {
+ // If we are talking to ourselves, or the UI process, then that is a fatal
+ // protocol error.
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ // If we are talking to the GPU process, then we should recover from this on
+ // the next ContentChild::RecvReinitRendering call.
+ gfxCriticalNote << "Could not initialize rendering with GPU process";
+ return IPC_OK();
+}
+
+#if defined(XP_MACOSX)
+extern "C" {
+void CGSShutdownServerConnections();
+};
+#endif
+
+mozilla::ipc::IPCResult ContentChild::RecvInitRendering(
+ Endpoint<PCompositorManagerChild>&& aCompositor,
+ Endpoint<PImageBridgeChild>&& aImageBridge,
+ Endpoint<PVRManagerChild>&& aVRBridge,
+ Endpoint<PRemoteDecoderManagerChild>&& aVideoManager,
+ nsTArray<uint32_t>&& namespaces) {
+ MOZ_ASSERT(namespaces.Length() == 3);
+
+ // Note that for all of the methods below, if it can fail, it should only
+ // return false if the failure is an IPDL error. In such situations,
+ // ContentChild can reason about whether or not to wait for
+ // RecvReinitRendering (because we surmised the GPU process crashed), or if it
+ // should crash itself (because we are actually talking to the UI process). If
+ // there are localized failures (e.g. failed to spawn a thread), then it
+ // should MOZ_RELEASE_ASSERT or MOZ_CRASH as necessary instead.
+ if (!CompositorManagerChild::Init(std::move(aCompositor), namespaces[0])) {
+ return GetResultForRenderingInitFailure(aCompositor.OtherPid());
+ }
+ if (!CompositorManagerChild::CreateContentCompositorBridge(namespaces[1])) {
+ return GetResultForRenderingInitFailure(aCompositor.OtherPid());
+ }
+ if (!ImageBridgeChild::InitForContent(std::move(aImageBridge),
+ namespaces[2])) {
+ return GetResultForRenderingInitFailure(aImageBridge.OtherPid());
+ }
+ if (!gfx::VRManagerChild::InitForContent(std::move(aVRBridge))) {
+ return GetResultForRenderingInitFailure(aVRBridge.OtherPid());
+ }
+ RemoteDecoderManagerChild::InitForGPUProcess(std::move(aVideoManager));
+
+#if defined(XP_MACOSX) && !defined(MOZ_SANDBOX)
+ // Close all current connections to the WindowServer. This ensures that the
+ // Activity Monitor will not label the content process as "Not responding"
+ // because it's not running a native event loop. See bug 1384336. When the
+ // build is configured with sandbox support, this is called during sandbox
+ // setup.
+ CGSShutdownServerConnections();
+#endif
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvReinitRendering(
+ Endpoint<PCompositorManagerChild>&& aCompositor,
+ Endpoint<PImageBridgeChild>&& aImageBridge,
+ Endpoint<PVRManagerChild>&& aVRBridge,
+ Endpoint<PRemoteDecoderManagerChild>&& aVideoManager,
+ nsTArray<uint32_t>&& namespaces) {
+ MOZ_ASSERT(namespaces.Length() == 3);
+ nsTArray<RefPtr<BrowserChild>> tabs = BrowserChild::GetAll();
+
+ // Re-establish singleton bridges to the compositor.
+ if (!CompositorManagerChild::Init(std::move(aCompositor), namespaces[0])) {
+ return GetResultForRenderingInitFailure(aCompositor.OtherPid());
+ }
+ if (!CompositorManagerChild::CreateContentCompositorBridge(namespaces[1])) {
+ return GetResultForRenderingInitFailure(aCompositor.OtherPid());
+ }
+ if (!ImageBridgeChild::ReinitForContent(std::move(aImageBridge),
+ namespaces[2])) {
+ return GetResultForRenderingInitFailure(aImageBridge.OtherPid());
+ }
+ if (!gfx::VRManagerChild::InitForContent(std::move(aVRBridge))) {
+ return GetResultForRenderingInitFailure(aVRBridge.OtherPid());
+ }
+ gfxPlatform::GetPlatform()->CompositorUpdated();
+
+ // Establish new PLayerTransactions.
+ for (const auto& browserChild : tabs) {
+ if (browserChild->GetLayersId().IsValid()) {
+ browserChild->ReinitRendering();
+ }
+ }
+
+ // Notify any observers that the compositor has been reinitialized,
+ // eg the ZoomConstraintsClients for documents in this process.
+ // This must occur after the ReinitRendering call above so that the
+ // APZCTreeManagers have been connected.
+ nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
+ if (observerService) {
+ observerService->NotifyObservers(nullptr, "compositor-reinitialized",
+ nullptr);
+ }
+
+ RemoteDecoderManagerChild::InitForGPUProcess(std::move(aVideoManager));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvReinitRenderingForDeviceReset() {
+ gfxPlatform::GetPlatform()->CompositorUpdated();
+
+ nsTArray<RefPtr<BrowserChild>> tabs = BrowserChild::GetAll();
+ for (const auto& browserChild : tabs) {
+ if (browserChild->GetLayersId().IsValid()) {
+ browserChild->ReinitRenderingForDeviceReset();
+ }
+ }
+ return IPC_OK();
+}
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+extern "C" {
+CGError CGSSetDenyWindowServerConnections(bool);
+};
+
+static void DisconnectWindowServer(bool aIsSandboxEnabled) {
+ // Close all current connections to the WindowServer. This ensures that the
+ // Activity Monitor will not label the content process as "Not responding"
+ // because it's not running a native event loop. See bug 1384336.
+ // This is required with or without the sandbox enabled. Until the
+ // window server is blocked as the policy level, this should be called
+ // just before CGSSetDenyWindowServerConnections() so there are no
+ // windowserver connections active when CGSSetDenyWindowServerConnections()
+ // is called.
+ CGSShutdownServerConnections();
+
+ // Actual security benefits are only achieved when we additionally deny
+ // future connections using the sandbox policy. WebGL must be remoted if
+ // the windowserver connections are blocked. WebGL remoting is disabled
+ // for some tests.
+ if (aIsSandboxEnabled &&
+ Preferences::GetBool(
+ "security.sandbox.content.mac.disconnect-windowserver") &&
+ Preferences::GetBool("webgl.out-of-process")) {
+ CGError result = CGSSetDenyWindowServerConnections(true);
+ MOZ_DIAGNOSTIC_ASSERT(result == kCGErrorSuccess);
+# if !MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ Unused << result;
+# endif
+ }
+}
+#endif
+
+mozilla::ipc::IPCResult ContentChild::RecvSetProcessSandbox(
+ const Maybe<mozilla::ipc::FileDescriptor>& aBroker) {
+ // We may want to move the sandbox initialization somewhere else
+ // at some point; see bug 880808.
+#if defined(MOZ_SANDBOX)
+
+ bool sandboxEnabled = true;
+# if defined(XP_LINUX)
+ // On Linux, we have to support systems that can't use any sandboxing.
+ sandboxEnabled = SandboxInfo::Get().CanSandboxContent();
+
+ if (sandboxEnabled && !StaticPrefs::media_cubeb_sandbox()) {
+ // Pre-start audio before sandboxing; see bug 1443612.
+ Unused << CubebUtils::GetCubeb();
+ }
+
+ if (sandboxEnabled) {
+ sandboxEnabled = SetContentProcessSandbox(
+ ContentProcessSandboxParams::ForThisProcess(aBroker));
+ }
+# elif defined(XP_WIN)
+ mozilla::SandboxTarget::Instance()->StartSandbox();
+# elif defined(XP_MACOSX)
+ sandboxEnabled = (GetEffectiveContentSandboxLevel() >= 1);
+ DisconnectWindowServer(sandboxEnabled);
+# endif
+
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::ContentSandboxEnabled, sandboxEnabled);
+# if defined(XP_LINUX) && !defined(ANDROID)
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::ContentSandboxCapabilities,
+ static_cast<int>(SandboxInfo::Get().AsInteger()));
+# endif /* XP_LINUX && !ANDROID */
+#endif /* MOZ_SANDBOX */
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvBidiKeyboardNotify(
+ const bool& aIsLangRTL, const bool& aHaveBidiKeyboards) {
+ // bidi is always of type PuppetBidiKeyboard* (because in the child, the only
+ // possible implementation of nsIBidiKeyboard is PuppetBidiKeyboard).
+ PuppetBidiKeyboard* bidi =
+ static_cast<PuppetBidiKeyboard*>(nsContentUtils::GetBidiKeyboard());
+ if (bidi) {
+ bidi->SetBidiKeyboardInfo(aIsLangRTL, aHaveBidiKeyboards);
+ }
+ return IPC_OK();
+}
+
+static StaticRefPtr<CancelableRunnable> gFirstIdleTask;
+
+static void FirstIdle(void) {
+ MOZ_ASSERT(gFirstIdleTask);
+ gFirstIdleTask = nullptr;
+
+ ContentChild::GetSingleton()->SendFirstIdle();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvConstructBrowser(
+ ManagedEndpoint<PBrowserChild>&& aBrowserEp,
+ ManagedEndpoint<PWindowGlobalChild>&& aWindowEp, const TabId& aTabId,
+ const IPCTabContext& aContext, const WindowGlobalInit& aWindowInit,
+ const uint32_t& aChromeFlags, const ContentParentId& aCpID,
+ const bool& aIsForBrowser, const bool& aIsTopLevel) {
+ MOZ_DIAGNOSTIC_ASSERT(!IsShuttingDown());
+
+ static bool hasRunOnce = false;
+ if (!hasRunOnce) {
+ hasRunOnce = true;
+ MOZ_ASSERT(!gFirstIdleTask);
+ RefPtr<CancelableRunnable> firstIdleTask =
+ NewCancelableRunnableFunction("FirstIdleRunnable", FirstIdle);
+ gFirstIdleTask = firstIdleTask;
+ if (NS_FAILED(NS_DispatchToCurrentThreadQueue(firstIdleTask.forget(),
+ EventQueuePriority::Idle))) {
+ gFirstIdleTask = nullptr;
+ hasRunOnce = false;
+ }
+ }
+
+ RefPtr<BrowsingContext> browsingContext =
+ BrowsingContext::Get(aWindowInit.context().mBrowsingContextId);
+ if (!browsingContext || browsingContext->IsDiscarded()) {
+ nsPrintfCString reason("%s initial %s BrowsingContext",
+ browsingContext ? "discarded" : "missing",
+ aIsTopLevel ? "top" : "frame");
+ MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Warning, ("%s", reason.get()));
+ if (!aIsTopLevel) {
+ // Recover if the BrowsingContext is missing for a new subframe. The
+ // `ManagedEndpoint` instances will be automatically destroyed.
+ NS_WARNING(reason.get());
+ return IPC_OK();
+ }
+
+ // (these are the only possible values of `reason` at this point)
+ return browsingContext
+ ? IPC_FAIL(this, "discarded initial top BrowsingContext")
+ : IPC_FAIL(this, "missing initial top BrowsingContext");
+ }
+
+ if (xpc::IsInAutomation() &&
+ StaticPrefs::
+ browser_tabs_remote_testOnly_failPBrowserCreation_enabled()) {
+ nsAutoCString idString;
+ if (NS_SUCCEEDED(Preferences::GetCString(
+ "browser.tabs.remote.testOnly.failPBrowserCreation.browsingContext",
+ idString))) {
+ nsresult rv = NS_OK;
+ uint64_t bcid = idString.ToInteger64(&rv);
+ if (NS_SUCCEEDED(rv) && bcid == browsingContext->Id()) {
+ NS_WARNING("Injecting artificial PBrowser creation failure");
+ return IPC_OK();
+ }
+ }
+ }
+
+ if (!aWindowInit.isInitialDocument() ||
+ !NS_IsAboutBlank(aWindowInit.documentURI())) {
+ return IPC_FAIL(this,
+ "Logic in CreateDocumentViewerForActor currently requires "
+ "actors to be initial about:blank documents");
+ }
+
+ // We'll happily accept any kind of IPCTabContext here; we don't need to
+ // check that it's of a certain type for security purposes, because we
+ // believe whatever the parent process tells us.
+ MaybeInvalidTabContext tc(aContext);
+ if (!tc.IsValid()) {
+ NS_ERROR(nsPrintfCString("Received an invalid TabContext from "
+ "the parent process. (%s) Crashing...",
+ tc.GetInvalidReason())
+ .get());
+ MOZ_CRASH("Invalid TabContext received from the parent process.");
+ }
+
+ RefPtr<WindowGlobalChild> windowChild =
+ WindowGlobalChild::CreateDisconnected(aWindowInit);
+ if (!windowChild) {
+ return IPC_FAIL(this, "Failed to create initial WindowGlobalChild");
+ }
+
+ RefPtr<BrowserChild> browserChild =
+ BrowserChild::Create(this, aTabId, tc.GetTabContext(), browsingContext,
+ aChromeFlags, aIsTopLevel);
+
+ // Bind the created BrowserChild to IPC to actually link the actor.
+ if (NS_WARN_IF(!BindPBrowserEndpoint(std::move(aBrowserEp), browserChild))) {
+ return IPC_FAIL(this, "BindPBrowserEndpoint failed");
+ }
+
+ if (NS_WARN_IF(!browserChild->BindPWindowGlobalEndpoint(std::move(aWindowEp),
+ windowChild))) {
+ return IPC_FAIL(this, "BindPWindowGlobalEndpoint failed");
+ }
+ windowChild->Init();
+ auto guardNullWindowGlobal = MakeScopeExit([&] {
+ if (!windowChild->GetWindowGlobal()) {
+ windowChild->Destroy();
+ }
+ });
+
+ // Ensure that a BrowsingContext is set for our BrowserChild before
+ // running `Init`.
+ MOZ_RELEASE_ASSERT(browserChild->mBrowsingContext->Id() ==
+ aWindowInit.context().mBrowsingContextId);
+
+ if (NS_WARN_IF(
+ NS_FAILED(browserChild->Init(/* aOpener */ nullptr, windowChild)))) {
+ return IPC_FAIL(browserChild, "BrowserChild::Init failed");
+ }
+
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ if (os) {
+ os->NotifyObservers(static_cast<nsIBrowserChild*>(browserChild),
+ "tab-child-created", nullptr);
+ }
+ // Notify parent that we are ready to handle input events.
+ browserChild->SendRemoteIsReadyToHandleInputEvents();
+ return IPC_OK();
+}
+
+void ContentChild::GetAvailableDictionaries(
+ nsTArray<nsCString>& aDictionaries) {
+ aDictionaries = mAvailableDictionaries.Clone();
+}
+
+mozilla::PRemoteSpellcheckEngineChild*
+ContentChild::AllocPRemoteSpellcheckEngineChild() {
+ MOZ_CRASH(
+ "Default Constructor for PRemoteSpellcheckEngineChild should never be "
+ "called");
+ return nullptr;
+}
+
+bool ContentChild::DeallocPRemoteSpellcheckEngineChild(
+ PRemoteSpellcheckEngineChild* child) {
+ delete child;
+ return true;
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvNotifyEmptyHTTPCache() {
+ MOZ_ASSERT(NS_IsMainThread());
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ obs->NotifyObservers(nullptr, "cacheservice:empty-cache", nullptr);
+ return IPC_OK();
+}
+
+PHalChild* ContentChild::AllocPHalChild() { return CreateHalChild(); }
+
+bool ContentChild::DeallocPHalChild(PHalChild* aHal) {
+ delete aHal;
+ return true;
+}
+
+devtools::PHeapSnapshotTempFileHelperChild*
+ContentChild::AllocPHeapSnapshotTempFileHelperChild() {
+ return devtools::HeapSnapshotTempFileHelperChild::Create();
+}
+
+bool ContentChild::DeallocPHeapSnapshotTempFileHelperChild(
+ devtools::PHeapSnapshotTempFileHelperChild* aHeapSnapshotHelper) {
+ delete aHeapSnapshotHelper;
+ return true;
+}
+
+already_AddRefed<PTestShellChild> ContentChild::AllocPTestShellChild() {
+ return MakeAndAddRef<TestShellChild>();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvPTestShellConstructor(
+ PTestShellChild* actor) {
+ return IPC_OK();
+}
+
+RefPtr<GenericPromise> ContentChild::UpdateCookieStatus(nsIChannel* aChannel) {
+ RefPtr<CookieServiceChild> csChild = CookieServiceChild::GetSingleton();
+ NS_ASSERTION(csChild, "Couldn't get CookieServiceChild");
+
+ return csChild->TrackCookieLoad(aChannel);
+}
+
+PScriptCacheChild* ContentChild::AllocPScriptCacheChild(
+ const FileDescOrError& cacheFile, const bool& wantCacheData) {
+ return new loader::ScriptCacheChild();
+}
+
+bool ContentChild::DeallocPScriptCacheChild(PScriptCacheChild* cache) {
+ delete static_cast<loader::ScriptCacheChild*>(cache);
+ return true;
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvPScriptCacheConstructor(
+ PScriptCacheChild* actor, const FileDescOrError& cacheFile,
+ const bool& wantCacheData) {
+ Maybe<FileDescriptor> fd;
+ if (cacheFile.type() == cacheFile.TFileDescriptor) {
+ fd.emplace(cacheFile.get_FileDescriptor());
+ }
+
+ static_cast<loader::ScriptCacheChild*>(actor)->Init(fd, wantCacheData);
+
+ // Some scripts listen for "app-startup" to start. However, in the content
+ // process, this category runs before the ScriptPreloader is initialized so
+ // these scripts wouldn't be added to the cache. Instead, if a script needs to
+ // run on start up in the content process, it should listen for this category.
+ NS_CreateServicesFromCategory("content-process-ready-for-script", nullptr,
+ "content-process-ready-for-script", nullptr);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvNetworkLinkTypeChange(
+ const uint32_t& aType) {
+ mNetworkLinkType = aType;
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ obs->NotifyObservers(nullptr, "contentchild:network-link-type-changed",
+ nullptr);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvSocketProcessCrashed() {
+ nsIOService::IncreaseSocketProcessCrashCount();
+ return IPC_OK();
+}
+
+PRemotePrintJobChild* ContentChild::AllocPRemotePrintJobChild() {
+#ifdef NS_PRINTING
+ return new RemotePrintJobChild();
+#else
+ return nullptr;
+#endif
+}
+
+already_AddRefed<PClipboardReadRequestChild>
+ContentChild::AllocPClipboardReadRequestChild(
+ const nsTArray<nsCString>& aTypes) {
+ return MakeAndAddRef<ClipboardReadRequestChild>(aTypes);
+}
+
+media::PMediaChild* ContentChild::AllocPMediaChild() {
+ return media::AllocPMediaChild();
+}
+
+bool ContentChild::DeallocPMediaChild(media::PMediaChild* aActor) {
+ return media::DeallocPMediaChild(aActor);
+}
+
+PBenchmarkStorageChild* ContentChild::AllocPBenchmarkStorageChild() {
+ return BenchmarkStorageChild::Instance();
+}
+
+bool ContentChild::DeallocPBenchmarkStorageChild(
+ PBenchmarkStorageChild* aActor) {
+ delete aActor;
+ return true;
+}
+
+#ifdef MOZ_WEBRTC
+PWebrtcGlobalChild* ContentChild::AllocPWebrtcGlobalChild() {
+ auto* child = new WebrtcGlobalChild();
+ return child;
+}
+
+bool ContentChild::DeallocPWebrtcGlobalChild(PWebrtcGlobalChild* aActor) {
+ delete static_cast<WebrtcGlobalChild*>(aActor);
+ return true;
+}
+#endif
+
+mozilla::ipc::IPCResult ContentChild::RecvRegisterChrome(
+ nsTArray<ChromePackage>&& packages,
+ nsTArray<SubstitutionMapping>&& resources,
+ nsTArray<OverrideMapping>&& overrides, const nsCString& locale,
+ const bool& reset) {
+ nsCOMPtr<nsIChromeRegistry> registrySvc = nsChromeRegistry::GetService();
+ nsChromeRegistryContent* chromeRegistry =
+ static_cast<nsChromeRegistryContent*>(registrySvc.get());
+ if (!chromeRegistry) {
+ return IPC_FAIL(this, "ChromeRegistryContent is null!");
+ }
+ chromeRegistry->RegisterRemoteChrome(packages, resources, overrides, locale,
+ reset);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvRegisterChromeItem(
+ const ChromeRegistryItem& item) {
+ nsCOMPtr<nsIChromeRegistry> registrySvc = nsChromeRegistry::GetService();
+ nsChromeRegistryContent* chromeRegistry =
+ static_cast<nsChromeRegistryContent*>(registrySvc.get());
+ if (!chromeRegistry) {
+ return IPC_FAIL(this, "ChromeRegistryContent is null!");
+ }
+ switch (item.type()) {
+ case ChromeRegistryItem::TChromePackage:
+ chromeRegistry->RegisterPackage(item.get_ChromePackage());
+ break;
+
+ case ChromeRegistryItem::TOverrideMapping:
+ chromeRegistry->RegisterOverride(item.get_OverrideMapping());
+ break;
+
+ case ChromeRegistryItem::TSubstitutionMapping:
+ chromeRegistry->RegisterSubstitution(item.get_SubstitutionMapping());
+ break;
+
+ default:
+ MOZ_ASSERT(false, "bad chrome item");
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ return IPC_OK();
+}
+mozilla::ipc::IPCResult ContentChild::RecvClearStyleSheetCache(
+ const Maybe<RefPtr<nsIPrincipal>>& aForPrincipal,
+ const Maybe<nsCString>& aBaseDomain) {
+ nsIPrincipal* principal =
+ aForPrincipal ? aForPrincipal.value().get() : nullptr;
+ const nsCString* baseDomain = aBaseDomain ? aBaseDomain.ptr() : nullptr;
+ SharedStyleSheetCache::Clear(principal, baseDomain);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvClearImageCacheFromPrincipal(
+ nsIPrincipal* aPrincipal) {
+ imgLoader* loader;
+ if (aPrincipal->OriginAttributesRef().mPrivateBrowsingId ==
+ nsIScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID) {
+ loader = imgLoader::NormalLoader();
+ } else {
+ loader = imgLoader::PrivateBrowsingLoader();
+ }
+
+ loader->RemoveEntriesInternal(aPrincipal, nullptr);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvClearImageCacheFromBaseDomain(
+ const nsCString& aBaseDomain) {
+ imgLoader::NormalLoader()->RemoveEntriesInternal(nullptr, &aBaseDomain);
+ imgLoader::PrivateBrowsingLoader()->RemoveEntriesInternal(nullptr,
+ &aBaseDomain);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvClearImageCache(
+ const bool& privateLoader, const bool& chrome) {
+ imgLoader* loader = privateLoader ? imgLoader::PrivateBrowsingLoader()
+ : imgLoader::NormalLoader();
+
+ loader->ClearCache(chrome);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvSetOffline(const bool& offline) {
+ nsCOMPtr<nsIIOService> io(do_GetIOService());
+ NS_ASSERTION(io, "IO Service can not be null");
+
+ io->SetOffline(offline);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvSetConnectivity(
+ const bool& connectivity) {
+ nsCOMPtr<nsIIOService> io(do_GetIOService());
+ nsCOMPtr<nsIIOServiceInternal> ioInternal(do_QueryInterface(io));
+ NS_ASSERTION(ioInternal, "IO Service can not be null");
+
+ ioInternal->SetConnectivity(connectivity);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvSetCaptivePortalState(
+ const int32_t& aState) {
+ nsCOMPtr<nsICaptivePortalService> cps = do_GetService(NS_CAPTIVEPORTAL_CID);
+ if (!cps) {
+ return IPC_OK();
+ }
+
+ mozilla::net::CaptivePortalService* portal =
+ static_cast<mozilla::net::CaptivePortalService*>(cps.get());
+ portal->SetStateInChild(aState);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvSetTRRMode(
+ const nsIDNSService::ResolverMode& mode,
+ const nsIDNSService::ResolverMode& modeFromPref) {
+ RefPtr<net::ChildDNSService> dnsServiceChild =
+ dont_AddRef(net::ChildDNSService::GetSingleton());
+ if (dnsServiceChild) {
+ dnsServiceChild->SetTRRModeInChild(mode, modeFromPref);
+ }
+ return IPC_OK();
+}
+
+void ContentChild::ActorDestroy(ActorDestroyReason why) {
+ if (mForceKillTimer) {
+ mForceKillTimer->Cancel();
+ mForceKillTimer = nullptr;
+ }
+
+ if (AbnormalShutdown == why) {
+ NS_WARNING("shutting down early because of crash!");
+ ProcessChild::QuickExit();
+ }
+
+#ifndef NS_FREE_PERMANENT_DATA
+ // In release builds, there's no point in the content process
+ // going through the full XPCOM shutdown path, because it doesn't
+ // keep persistent state.
+ ProcessChild::QuickExit();
+#else
+ // Destroy our JSProcessActors, and reject any pending queries.
+ JSActorDidDestroy();
+
+# if defined(XP_WIN)
+ RefPtr<DllServices> dllSvc(DllServices::Get());
+ dllSvc->DisableFull();
+# endif // defined(XP_WIN)
+
+ if (gFirstIdleTask) {
+ gFirstIdleTask->Cancel();
+ gFirstIdleTask = nullptr;
+ }
+
+ BlobURLProtocolHandler::RemoveDataEntries();
+
+ mSharedData = nullptr;
+
+ mAlertObservers.Clear();
+
+ mIdleObservers.Clear();
+
+ if (mConsoleListener) {
+ nsCOMPtr<nsIConsoleService> svc(
+ do_GetService(NS_CONSOLESERVICE_CONTRACTID));
+ if (svc) {
+ svc->UnregisterListener(mConsoleListener);
+ mConsoleListener->mChild = nullptr;
+ }
+ }
+ mIsAlive = false;
+
+ CrashReporterClient::DestroySingleton();
+
+ XRE_ShutdownChildProcess();
+#endif // NS_FREE_PERMANENT_DATA
+}
+
+void ContentChild::ProcessingError(Result aCode, const char* aReason) {
+ switch (aCode) {
+ case MsgDropped:
+ NS_WARNING("MsgDropped in ContentChild");
+ return;
+
+ case MsgNotKnown:
+ case MsgNotAllowed:
+ case MsgPayloadError:
+ case MsgProcessingError:
+ case MsgRouteError:
+ case MsgValueError:
+ break;
+
+ default:
+ MOZ_CRASH("not reached");
+ }
+
+ nsDependentCString reason(aReason);
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::ipc_channel_error, reason);
+
+ MOZ_CRASH("Content child abort due to IPC error");
+}
+
+nsresult ContentChild::AddRemoteAlertObserver(const nsString& aData,
+ nsIObserver* aObserver) {
+ NS_ASSERTION(aObserver, "Adding a null observer?");
+ mAlertObservers.AppendElement(new AlertObserver(aObserver, aData));
+ return NS_OK;
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvPreferenceUpdate(const Pref& aPref) {
+ Preferences::SetPreference(aPref);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvVarUpdate(const GfxVarUpdate& aVar) {
+ gfx::gfxVars::ApplyUpdate(aVar);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvUpdatePerfStatsCollectionMask(
+ const uint64_t& aMask) {
+ PerfStats::SetCollectionMask(static_cast<PerfStats::MetricMask>(aMask));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvCollectPerfStatsJSON(
+ CollectPerfStatsJSONResolver&& aResolver) {
+ aResolver(PerfStats::CollectLocalPerfStatsJSON());
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvCollectScrollingMetrics(
+ CollectScrollingMetricsResolver&& aResolver) {
+ auto metrics = ScrollingMetrics::CollectLocalScrollingMetrics();
+ using ResolverArgs = std::tuple<const uint32_t&, const uint32_t&>;
+ aResolver(ResolverArgs(std::get<0>(metrics), std::get<1>(metrics)));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvNotifyAlertsObserver(
+ const nsCString& aType, const nsString& aData) {
+ nsTArray<nsCOMPtr<nsIObserver>> observersToNotify;
+
+ mAlertObservers.RemoveElementsBy([&](UniquePtr<AlertObserver>& observer) {
+ if (!observer->mData.Equals(aData)) {
+ return false;
+ }
+
+ // aType == alertfinished, this alert is done and we can remove the
+ // observer.
+ observersToNotify.AppendElement(observer->mObserver);
+ return aType.EqualsLiteral("alertfinished");
+ });
+
+ for (auto& observer : observersToNotify) {
+ observer->Observe(nullptr, aType.get(), aData.get());
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvNotifyVisited(
+ nsTArray<VisitedQueryResult>&& aURIs) {
+ nsCOMPtr<IHistory> history = components::History::Service();
+ if (!history) {
+ return IPC_OK();
+ }
+ for (const VisitedQueryResult& result : aURIs) {
+ nsCOMPtr<nsIURI> newURI = result.uri();
+ if (!newURI) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ auto status = result.visited() ? IHistory::VisitedStatus::Visited
+ : IHistory::VisitedStatus::Unvisited;
+ history->NotifyVisited(newURI, status);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvThemeChanged(
+ FullLookAndFeel&& aLookAndFeelData, widget::ThemeChangeKind aKind) {
+ LookAndFeel::SetData(std::move(aLookAndFeelData));
+ LookAndFeel::NotifyChangedAllWindows(aKind);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvLoadProcessScript(
+ const nsString& aURL) {
+ auto* global = ContentProcessMessageManager::Get();
+ global->LoadScript(aURL);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvAsyncMessage(
+ const nsString& aMsg, const ClonedMessageData& aData) {
+ AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING("ContentChild::RecvAsyncMessage",
+ OTHER, aMsg);
+ MMPrinter::Print("ContentChild::RecvAsyncMessage", aMsg, aData);
+
+ RefPtr<nsFrameMessageManager> cpm =
+ nsFrameMessageManager::GetChildProcessManager();
+ if (cpm) {
+ StructuredCloneData data;
+ ipc::UnpackClonedMessageData(aData, data);
+ cpm->ReceiveMessage(cpm, nullptr, aMsg, false, &data, nullptr,
+ IgnoreErrors());
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvRegisterStringBundles(
+ nsTArray<mozilla::dom::StringBundleDescriptor>&& aDescriptors) {
+ nsCOMPtr<nsIStringBundleService> stringBundleService =
+ components::StringBundle::Service();
+
+ for (auto& descriptor : aDescriptors) {
+ stringBundleService->RegisterContentBundle(
+ descriptor.bundleURL(), descriptor.mapFile(), descriptor.mapSize());
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvUpdateL10nFileSources(
+ nsTArray<mozilla::dom::L10nFileSourceDescriptor>&& aDescriptors) {
+ L10nRegistry::RegisterFileSourcesFromParentProcess(aDescriptors);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvUpdateSharedData(
+ const FileDescriptor& aMapFile, const uint32_t& aMapSize,
+ nsTArray<IPCBlob>&& aBlobs, nsTArray<nsCString>&& aChangedKeys) {
+ nsTArray<RefPtr<BlobImpl>> blobImpls(aBlobs.Length());
+ for (auto& ipcBlob : aBlobs) {
+ blobImpls.AppendElement(IPCBlobUtils::Deserialize(ipcBlob));
+ }
+
+ if (mSharedData) {
+ mSharedData->Update(aMapFile, aMapSize, std::move(blobImpls),
+ std::move(aChangedKeys));
+ } else {
+ mSharedData =
+ new SharedMap(ContentProcessMessageManager::Get()->GetParentObject(),
+ aMapFile, aMapSize, std::move(blobImpls));
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvFontListChanged() {
+ gfxPlatformFontList::PlatformFontList()->FontListChanged();
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvForceGlobalReflow(
+ bool aNeedsReframe) {
+ gfxPlatform::ForceGlobalReflow(aNeedsReframe ? gfxPlatform::NeedsReframe::Yes
+ : gfxPlatform::NeedsReframe::No);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvGeolocationUpdate(
+ nsIDOMGeoPosition* aPosition) {
+ RefPtr<nsGeolocationService> gs =
+ nsGeolocationService::GetGeolocationService();
+ if (!gs) {
+ return IPC_OK();
+ }
+ gs->Update(aPosition);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvGeolocationError(
+ const uint16_t& errorCode) {
+ RefPtr<nsGeolocationService> gs =
+ nsGeolocationService::GetGeolocationService();
+ if (!gs) {
+ return IPC_OK();
+ }
+ gs->NotifyError(errorCode);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvUpdateDictionaryList(
+ nsTArray<nsCString>&& aDictionaries) {
+ mAvailableDictionaries = std::move(aDictionaries);
+ mozInlineSpellChecker::UpdateCanEnableInlineSpellChecking();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvUpdateFontList(
+ dom::SystemFontList&& aFontList) {
+ mFontList = std::move(aFontList);
+ if (gfxPlatform::Initialized()) {
+ gfxPlatform::GetPlatform()->UpdateFontList(true);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvRebuildFontList(
+ const bool& aFullRebuild) {
+ if (gfxPlatform::Initialized()) {
+ gfxPlatform::GetPlatform()->UpdateFontList(aFullRebuild);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvFontListShmBlockAdded(
+ const uint32_t& aGeneration, const uint32_t& aIndex,
+ base::SharedMemoryHandle&& aHandle) {
+ if (gfxPlatform::Initialized()) {
+ gfxPlatformFontList::PlatformFontList()->ShmBlockAdded(aGeneration, aIndex,
+ std::move(aHandle));
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvUpdateAppLocales(
+ nsTArray<nsCString>&& aAppLocales) {
+ LocaleService::GetInstance()->AssignAppLocales(aAppLocales);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvUpdateRequestedLocales(
+ nsTArray<nsCString>&& aRequestedLocales) {
+ LocaleService::GetInstance()->AssignRequestedLocales(aRequestedLocales);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvSystemTimezoneChanged() {
+ nsJSUtils::ResetTimeZone();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvAddPermission(
+ const IPC::Permission& permission) {
+ nsCOMPtr<nsIPermissionManager> permissionManagerIface =
+ components::PermissionManager::Service();
+ PermissionManager* permissionManager =
+ static_cast<PermissionManager*>(permissionManagerIface.get());
+ MOZ_ASSERT(permissionManager,
+ "We have no permissionManager in the Content process !");
+
+ // note we do not need to force mUserContextId to the default here because
+ // the permission manager does that internally.
+ nsAutoCString originNoSuffix;
+ OriginAttributes attrs;
+ bool success = attrs.PopulateFromOrigin(permission.origin, originNoSuffix);
+ NS_ENSURE_TRUE(success, IPC_FAIL_NO_REASON(this));
+
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = NS_NewURI(getter_AddRefs(uri), originNoSuffix);
+ NS_ENSURE_SUCCESS(rv, IPC_OK());
+
+ nsCOMPtr<nsIPrincipal> principal =
+ mozilla::BasePrincipal::CreateContentPrincipal(uri, attrs);
+
+ // child processes don't care about modification time.
+ int64_t modificationTime = 0;
+
+ permissionManager->AddInternal(
+ principal, nsCString(permission.type), permission.capability, 0,
+ permission.expireType, permission.expireTime, modificationTime,
+ PermissionManager::eNotify, PermissionManager::eNoDBOperation);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvRemoveAllPermissions() {
+ nsCOMPtr<nsIPermissionManager> permissionManagerIface =
+ components::PermissionManager::Service();
+ PermissionManager* permissionManager =
+ static_cast<PermissionManager*>(permissionManagerIface.get());
+ MOZ_ASSERT(permissionManager,
+ "We have no permissionManager in the Content process !");
+
+ permissionManager->RemoveAllFromIPC();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvFlushMemory(const nsString& reason) {
+ nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+ if (!mShuttingDown && os) {
+ os->NotifyObservers(nullptr, "memory-pressure", reason.get());
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvActivateA11y() {
+#ifdef ACCESSIBILITY
+ // Start accessibility in content process if it's running in chrome
+ // process.
+ GetOrCreateAccService(nsAccessibilityService::eMainProcess);
+#endif // ACCESSIBILITY
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvShutdownA11y() {
+#ifdef ACCESSIBILITY
+ // Try to shutdown accessibility in content process if it's shutting down in
+ // chrome process.
+ MaybeShutdownAccService(nsAccessibilityService::eMainProcess);
+#endif
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvApplicationForeground() {
+ // Rebroadcast the "application-foreground"
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ obs->NotifyObservers(nullptr, "application-foreground", nullptr);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvApplicationBackground() {
+ // Rebroadcast the "application-background"
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ obs->NotifyObservers(nullptr, "application-background", nullptr);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvGarbageCollect() {
+ // Rebroadcast the "child-gc-request" so that workers will GC.
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ obs->NotifyObservers(nullptr, "child-gc-request", nullptr);
+ }
+ nsJSContext::GarbageCollectNow(JS::GCReason::DOM_IPC);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvCycleCollect() {
+ // Rebroadcast the "child-cc-request" so that workers will CC.
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ obs->NotifyObservers(nullptr, "child-cc-request", nullptr);
+ }
+ nsJSContext::CycleCollectNow(CCReason::IPC_MESSAGE);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvUnlinkGhosts() {
+#ifdef DEBUG
+ nsWindowMemoryReporter::UnlinkGhostWindows();
+#endif
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvAppInfo(
+ const nsCString& version, const nsCString& buildID, const nsCString& name,
+ const nsCString& UAName, const nsCString& ID, const nsCString& vendor,
+ const nsCString& sourceURL, const nsCString& updateURL) {
+ mAppInfo.version.Assign(version);
+ mAppInfo.buildID.Assign(buildID);
+ mAppInfo.name.Assign(name);
+ mAppInfo.UAName.Assign(UAName);
+ mAppInfo.ID.Assign(ID);
+ mAppInfo.vendor.Assign(vendor);
+ mAppInfo.sourceURL.Assign(sourceURL);
+ mAppInfo.updateURL.Assign(updateURL);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvRemoteType(
+ const nsCString& aRemoteType, const nsCString& aProfile) {
+ if (aRemoteType == mRemoteType) {
+ // Allocation of preallocated processes that are still launching can
+ // cause this
+ return IPC_OK();
+ }
+
+ if (!mRemoteType.IsVoid()) {
+ // Preallocated processes are type PREALLOC_REMOTE_TYPE; they may not
+ // become a File: process, or Privileged About Content Process
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("Changing remoteType of process %d from %s to %s", getpid(),
+ mRemoteType.get(), aRemoteType.get()));
+ // prealloc->anything (but file) or web->web allowed, and no-change
+ MOZ_RELEASE_ASSERT(mRemoteType == PREALLOC_REMOTE_TYPE &&
+ aRemoteType != FILE_REMOTE_TYPE &&
+ aRemoteType != PRIVILEGEDABOUT_REMOTE_TYPE);
+ } else {
+ // Initial setting of remote type. Either to 'prealloc' or the actual
+ // final type (if we didn't use a preallocated process)
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("Setting remoteType of process %d to %s", getpid(),
+ aRemoteType.get()));
+
+ if (aRemoteType == PREALLOC_REMOTE_TYPE) {
+ PreallocInit();
+ }
+ }
+
+ auto remoteTypePrefix = RemoteTypePrefix(aRemoteType);
+
+ // Must do before SetProcessName
+ mRemoteType.Assign(aRemoteType);
+
+ // Update the process name so about:memory's process names are more obvious.
+ if (aRemoteType == FILE_REMOTE_TYPE) {
+ SetProcessName("file:// Content"_ns, nullptr, &aProfile);
+ } else if (aRemoteType == EXTENSION_REMOTE_TYPE) {
+ SetProcessName("WebExtensions"_ns, nullptr, &aProfile);
+ } else if (aRemoteType == PRIVILEGEDABOUT_REMOTE_TYPE) {
+ SetProcessName("Privileged Content"_ns, nullptr, &aProfile);
+ } else if (aRemoteType == PRIVILEGEDMOZILLA_REMOTE_TYPE) {
+ SetProcessName("Privileged Mozilla"_ns, nullptr, &aProfile);
+ } else if (remoteTypePrefix == WITH_COOP_COEP_REMOTE_TYPE) {
+ // The profiler can sanitize out the eTLD+1
+ nsDependentCSubstring etld =
+ Substring(aRemoteType, WITH_COOP_COEP_REMOTE_TYPE.Length() + 1);
+#ifdef NIGHTLY_BUILD
+ SetProcessName("WebCOOP+COEP Content"_ns, &etld, &aProfile);
+#else
+ SetProcessName("Isolated Web Content"_ns, &etld,
+ &aProfile); // to avoid confusing people
+#endif
+ } else if (remoteTypePrefix == FISSION_WEB_REMOTE_TYPE) {
+ // The profiler can sanitize out the eTLD+1
+ nsDependentCSubstring etld =
+ Substring(aRemoteType, FISSION_WEB_REMOTE_TYPE.Length() + 1);
+ SetProcessName("Isolated Web Content"_ns, &etld, &aProfile);
+ } else if (remoteTypePrefix == SERVICEWORKER_REMOTE_TYPE) {
+ // The profiler can sanitize out the eTLD+1
+ nsDependentCSubstring etld =
+ Substring(aRemoteType, SERVICEWORKER_REMOTE_TYPE.Length() + 1);
+ SetProcessName("Isolated Service Worker"_ns, &etld, &aProfile);
+ } else {
+ // else "prealloc" or "web" type -> "Web Content"
+ SetProcessName("Web Content"_ns, nullptr, &aProfile);
+ }
+
+ // Turn off Spectre mitigations in isolated web content processes.
+ if (StaticPrefs::javascript_options_spectre_disable_for_isolated_content() &&
+ (remoteTypePrefix == FISSION_WEB_REMOTE_TYPE ||
+ remoteTypePrefix == SERVICEWORKER_REMOTE_TYPE ||
+ remoteTypePrefix == WITH_COOP_COEP_REMOTE_TYPE ||
+ aRemoteType == PRIVILEGEDABOUT_REMOTE_TYPE ||
+ aRemoteType == PRIVILEGEDMOZILLA_REMOTE_TYPE)) {
+ JS::DisableSpectreMitigationsAfterInit();
+ }
+
+ // Use the prefix to avoid URIs from Fission isolated processes.
+ CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::RemoteType,
+ remoteTypePrefix);
+
+ // Defer RemoteWorkerService initialization until the child process does
+ // receive its specific remoteType and can become actionable for the
+ // RemoteWorkerManager in the parent process.
+ if (mRemoteType != PREALLOC_REMOTE_TYPE) {
+ RemoteWorkerService::Initialize();
+ }
+
+ return IPC_OK();
+}
+
+// A method to initialize anything we need during the preallocation phase
+void ContentChild::PreallocInit() {
+ EnsureNSSInitializedChromeOrContent();
+
+ // SetAcceptLanguages() needs to read localized strings (file access),
+ // which is slow, so do this in prealloc
+ nsHttpHandler::PresetAcceptLanguages();
+}
+
+// Call RemoteTypePrefix() on the result to remove URIs if you want to use this
+// for telemetry.
+const nsACString& ContentChild::GetRemoteType() const { return mRemoteType; }
+
+mozilla::ipc::IPCResult ContentChild::RecvInitBlobURLs(
+ nsTArray<BlobURLRegistrationData>&& aRegistrations) {
+ for (uint32_t i = 0; i < aRegistrations.Length(); ++i) {
+ BlobURLRegistrationData& registration = aRegistrations[i];
+ RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(registration.blob());
+ MOZ_ASSERT(blobImpl);
+
+ BlobURLProtocolHandler::AddDataEntry(registration.url(),
+ registration.principal(),
+ registration.partitionKey(), blobImpl);
+ // If we have received an already-revoked blobURL, we have to keep it alive
+ // for a while (see BlobURLProtocolHandler) in order to support pending
+ // operations such as navigation, download and so on.
+ if (registration.revoked()) {
+ BlobURLProtocolHandler::RemoveDataEntry(registration.url(), false);
+ }
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvInitJSActorInfos(
+ nsTArray<JSProcessActorInfo>&& aContentInfos,
+ nsTArray<JSWindowActorInfo>&& aWindowInfos) {
+ RefPtr<JSActorService> actSvc = JSActorService::GetSingleton();
+ actSvc->LoadJSActorInfos(aContentInfos, aWindowInfos);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvUnregisterJSWindowActor(
+ const nsCString& aName) {
+ RefPtr<JSActorService> actSvc = JSActorService::GetSingleton();
+ actSvc->UnregisterWindowActor(aName);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvUnregisterJSProcessActor(
+ const nsCString& aName) {
+ RefPtr<JSActorService> actSvc = JSActorService::GetSingleton();
+ actSvc->UnregisterProcessActor(aName);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvLastPrivateDocShellDestroyed() {
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ obs->NotifyObservers(nullptr, "last-pb-context-exited", nullptr);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvNotifyProcessPriorityChanged(
+ const hal::ProcessPriority& aPriority) {
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ NS_ENSURE_TRUE(os, IPC_OK());
+
+ RefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();
+ props->SetPropertyAsInt32(u"priority"_ns, static_cast<int32_t>(aPriority));
+
+ PROFILER_MARKER("Process Priority", OTHER,
+ mozilla::MarkerThreadId::MainThread(), ProcessPriorityChange,
+ ProfilerString8View::WrapNullTerminatedString(
+ ProcessPriorityToString(mProcessPriority)),
+ ProfilerString8View::WrapNullTerminatedString(
+ ProcessPriorityToString(aPriority)));
+
+ // Record FOG data before the priority change.
+ // Ignore the change if it's the first time we set the process priority.
+ if (mProcessPriority != hal::PROCESS_PRIORITY_UNKNOWN) {
+ glean::RecordPowerMetrics();
+ }
+
+ ConfigureThreadPerformanceHints(aPriority);
+
+ mProcessPriority = aPriority;
+
+ os->NotifyObservers(static_cast<nsIPropertyBag2*>(props),
+ "ipc:process-priority-changed", nullptr);
+ if (StaticPrefs::
+ dom_memory_foreground_content_processes_have_larger_page_cache()) {
+ if (mProcessPriority >= hal::PROCESS_PRIORITY_FOREGROUND) {
+ // Note: keep this in sync with the JS shell (js/src/shell/js.cpp).
+ moz_set_max_dirty_page_modifier(4);
+ } else if (mProcessPriority == hal::PROCESS_PRIORITY_BACKGROUND) {
+ moz_set_max_dirty_page_modifier(-2);
+ } else {
+ moz_set_max_dirty_page_modifier(0);
+ }
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvMinimizeMemoryUsage() {
+ nsCOMPtr<nsIMemoryReporterManager> mgr =
+ do_GetService("@mozilla.org/memory-reporter-manager;1");
+ NS_ENSURE_TRUE(mgr, IPC_OK());
+
+ Unused << mgr->MinimizeMemoryUsage(/* callback = */ nullptr);
+ return IPC_OK();
+}
+
+void ContentChild::AddIdleObserver(nsIObserver* aObserver,
+ uint32_t aIdleTimeInS) {
+ MOZ_ASSERT(aObserver, "null idle observer");
+ // Make sure aObserver isn't released while we wait for the parent
+ aObserver->AddRef();
+ SendAddIdleObserver(reinterpret_cast<uint64_t>(aObserver), aIdleTimeInS);
+ mIdleObservers.Insert(aObserver);
+}
+
+void ContentChild::RemoveIdleObserver(nsIObserver* aObserver,
+ uint32_t aIdleTimeInS) {
+ MOZ_ASSERT(aObserver, "null idle observer");
+ SendRemoveIdleObserver(reinterpret_cast<uint64_t>(aObserver), aIdleTimeInS);
+ aObserver->Release();
+ mIdleObservers.Remove(aObserver);
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvNotifyIdleObserver(
+ const uint64_t& aObserver, const nsCString& aTopic,
+ const nsString& aTimeStr) {
+ nsIObserver* observer = reinterpret_cast<nsIObserver*>(aObserver);
+ if (mIdleObservers.Contains(observer)) {
+ observer->Observe(nullptr, aTopic.get(), aTimeStr.get());
+ } else {
+ NS_WARNING("Received notification for an idle observer that was removed.");
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvLoadAndRegisterSheet(
+ nsIURI* aURI, const uint32_t& aType) {
+ if (!aURI) {
+ return IPC_OK();
+ }
+
+ nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance();
+ if (sheetService) {
+ sheetService->LoadAndRegisterSheet(aURI, aType);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvUnregisterSheet(
+ nsIURI* aURI, const uint32_t& aType) {
+ if (!aURI) {
+ return IPC_OK();
+ }
+
+ nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance();
+ if (sheetService) {
+ sheetService->UnregisterSheet(aURI, aType);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvDomainSetChanged(
+ const uint32_t& aSetType, const uint32_t& aChangeType, nsIURI* aDomain) {
+ if (aChangeType == ACTIVATE_POLICY) {
+ if (mPolicy) {
+ return IPC_OK();
+ }
+ nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
+ MOZ_ASSERT(ssm);
+ ssm->ActivateDomainPolicyInternal(getter_AddRefs(mPolicy));
+ if (!mPolicy) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ return IPC_OK();
+ }
+ if (!mPolicy) {
+ MOZ_ASSERT_UNREACHABLE(
+ "If the domain policy is not active yet,"
+ " the first message should be ACTIVATE_POLICY");
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ NS_ENSURE_TRUE(mPolicy, IPC_FAIL_NO_REASON(this));
+
+ if (aChangeType == DEACTIVATE_POLICY) {
+ mPolicy->Deactivate();
+ mPolicy = nullptr;
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsIDomainSet> set;
+ switch (aSetType) {
+ case BLOCKLIST:
+ mPolicy->GetBlocklist(getter_AddRefs(set));
+ break;
+ case SUPER_BLOCKLIST:
+ mPolicy->GetSuperBlocklist(getter_AddRefs(set));
+ break;
+ case ALLOWLIST:
+ mPolicy->GetAllowlist(getter_AddRefs(set));
+ break;
+ case SUPER_ALLOWLIST:
+ mPolicy->GetSuperAllowlist(getter_AddRefs(set));
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unexpected setType");
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ MOZ_ASSERT(set);
+
+ switch (aChangeType) {
+ case ADD_DOMAIN:
+ NS_ENSURE_TRUE(aDomain, IPC_FAIL_NO_REASON(this));
+ set->Add(aDomain);
+ break;
+ case REMOVE_DOMAIN:
+ NS_ENSURE_TRUE(aDomain, IPC_FAIL_NO_REASON(this));
+ set->Remove(aDomain);
+ break;
+ case CLEAR_DOMAINS:
+ set->Clear();
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unexpected changeType");
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ return IPC_OK();
+}
+
+void ContentChild::StartForceKillTimer() {
+ if (mForceKillTimer) {
+ return;
+ }
+
+ int32_t timeoutSecs = StaticPrefs::dom_ipc_tabs_shutdownTimeoutSecs();
+ if (timeoutSecs > 0) {
+ NS_NewTimerWithFuncCallback(getter_AddRefs(mForceKillTimer),
+ ContentChild::ForceKillTimerCallback, this,
+ timeoutSecs * 1000, nsITimer::TYPE_ONE_SHOT,
+ "dom::ContentChild::StartForceKillTimer");
+ MOZ_ASSERT(mForceKillTimer);
+ }
+}
+
+/* static */
+void ContentChild::ForceKillTimerCallback(nsITimer* aTimer, void* aClosure) {
+ ProcessChild::QuickExit();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvShutdownConfirmedHP() {
+ CrashReporter::AppendToCrashReportAnnotation(
+ CrashReporter::Annotation::IPCShutdownState,
+ "RecvShutdownConfirmedHP entry"_ns);
+
+ // Bug 1755376: If we see "RecvShutdownConfirmedHP entry" often in
+ // bug IPCError_ShutDownKill we might want to anticipate
+ // ShutdownPhase::AppShutdownConfirmed to start here.
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvShutdown() {
+ CrashReporter::AppendToCrashReportAnnotation(
+ CrashReporter::Annotation::IPCShutdownState, "RecvShutdown entry"_ns);
+
+ // Signal the ongoing shutdown to AppShutdown, this
+ // will make abort nested SpinEventLoopUntilOrQuit loops
+ AppShutdown::AdvanceShutdownPhaseWithoutNotify(
+ ShutdownPhase::AppShutdownConfirmed);
+
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ if (os) {
+ CrashReporter::AppendToCrashReportAnnotation(
+ CrashReporter::Annotation::IPCShutdownState,
+ "content-child-will-shutdown started"_ns);
+
+ os->NotifyObservers(ToSupports(this), "content-child-will-shutdown",
+ nullptr);
+ }
+
+ ShutdownInternal();
+ return IPC_OK();
+}
+
+void ContentChild::ShutdownInternal() {
+ CrashReporter::AppendToCrashReportAnnotation(
+ CrashReporter::Annotation::IPCShutdownState, "ShutdownInternal entry"_ns);
+
+ // If we receive the shutdown message from within a nested event loop, we want
+ // to wait for that event loop to finish. Otherwise we could prematurely
+ // terminate an "unload" or "pagehide" event handler (which might be doing a
+ // sync XHR, for example).
+
+ MOZ_ASSERT(NS_IsMainThread());
+ RefPtr<nsThread> mainThread = nsThreadManager::get().GetCurrentThread();
+ // Note that we only have to check the recursion count for the current
+ // cooperative thread. Since the Shutdown message is not labeled with a
+ // SchedulerGroup, there can be no other cooperative threads doing work while
+ // we're running.
+ if (mainThread && mainThread->RecursionDepth() > 1) {
+ // We're in a nested event loop. Let's delay for an arbitrary period of
+ // time (100ms) in the hopes that the event loop will have finished by
+ // then.
+ GetCurrentSerialEventTarget()->DelayedDispatch(
+ NewRunnableMethod("dom::ContentChild::RecvShutdown", this,
+ &ContentChild::ShutdownInternal),
+ 100);
+ return;
+ }
+
+ mShuttingDown = true;
+
+#ifdef NIGHTLY_BUILD
+ BackgroundHangMonitor::UnregisterAnnotator(
+ PendingInputEventHangAnnotator::sSingleton);
+#endif
+
+ if (mPolicy) {
+ mPolicy->Deactivate();
+ mPolicy = nullptr;
+ }
+
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ if (os) {
+ CrashReporter::AppendToCrashReportAnnotation(
+ CrashReporter::Annotation::IPCShutdownState,
+ "content-child-shutdown started"_ns);
+ os->NotifyObservers(ToSupports(this), "content-child-shutdown", nullptr);
+ }
+
+ GetIPCChannel()->SetAbortOnError(false);
+
+ if (mProfilerController) {
+ const bool isProfiling = profiler_is_active();
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::ProfilerChildShutdownPhase,
+ isProfiling ? "Profiling - GrabShutdownProfileAndShutdown"_ns
+ : "Not profiling - GrabShutdownProfileAndShutdown"_ns);
+ ProfileAndAdditionalInformation shutdownProfileAndAdditionalInformation =
+ mProfilerController->GrabShutdownProfileAndShutdown();
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::ProfilerChildShutdownPhase,
+ isProfiling ? "Profiling - Destroying ChildProfilerController"_ns
+ : "Not profiling - Destroying ChildProfilerController"_ns);
+ mProfilerController = nullptr;
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::ProfilerChildShutdownPhase,
+ isProfiling ? "Profiling - SendShutdownProfile (sending)"_ns
+ : "Not profiling - SendShutdownProfile (sending)"_ns);
+ if (const size_t len = shutdownProfileAndAdditionalInformation.SizeOf();
+ len >= size_t(IPC::Channel::kMaximumMessageSize)) {
+ shutdownProfileAndAdditionalInformation.mProfile = nsPrintfCString(
+ "*Profile from pid %u bigger (%zu) than IPC max (%zu)",
+ unsigned(profiler_current_process_id().ToNumber()), len,
+ size_t(IPC::Channel::kMaximumMessageSize));
+ }
+ // Send the shutdown profile to the parent process through our own
+ // message channel, which we know will survive for long enough.
+ bool sent =
+ SendShutdownProfile(shutdownProfileAndAdditionalInformation.mProfile);
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::ProfilerChildShutdownPhase,
+ sent ? (isProfiling ? "Profiling - SendShutdownProfile (sent)"_ns
+ : "Not profiling - SendShutdownProfile (sent)"_ns)
+ : (isProfiling
+ ? "Profiling - SendShutdownProfile (failed)"_ns
+ : "Not profiling - SendShutdownProfile (failed)"_ns));
+ }
+
+ if (PerfStats::GetCollectionMask() != 0) {
+ SendShutdownPerfStats(PerfStats::CollectLocalPerfStatsJSON());
+ }
+
+ // Start a timer that will ensure we quickly exit after a reasonable period
+ // of time. Prevents shutdown hangs after our connection to the parent
+ // closes or when the parent is too busy to ever kill us.
+ CrashReporter::AppendToCrashReportAnnotation(
+ CrashReporter::Annotation::IPCShutdownState, "StartForceKillTimer"_ns);
+ StartForceKillTimer();
+
+ CrashReporter::AppendToCrashReportAnnotation(
+ CrashReporter::Annotation::IPCShutdownState,
+ "SendFinishShutdown (sending)"_ns);
+
+ // Notify the parent that we are done with shutdown. This is sent with high
+ // priority and will just flag we are done.
+ Unused << SendNotifyShutdownSuccess();
+
+ // Now tell the parent to actually destroy our channel which will make end
+ // our process. This is expected to be the last event the parent will
+ // ever process for this ContentChild.
+ bool sent = SendFinishShutdown();
+
+ CrashReporter::AppendToCrashReportAnnotation(
+ CrashReporter::Annotation::IPCShutdownState,
+ sent ? "SendFinishShutdown (sent)"_ns : "SendFinishShutdown (failed)"_ns);
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvUpdateWindow(
+ const uintptr_t& aChildId) {
+ MOZ_ASSERT(
+ false,
+ "ContentChild::RecvUpdateWindow calls unexpected on this platform.");
+ return IPC_FAIL_NO_REASON(this);
+}
+
+PContentPermissionRequestChild*
+ContentChild::AllocPContentPermissionRequestChild(
+ Span<const PermissionRequest> aRequests, nsIPrincipal* aPrincipal,
+ nsIPrincipal* aTopLevelPrincipal, const bool& aIsHandlingUserInput,
+ const bool& aMaybeUnsafePermissionDelegate, const TabId& aTabId) {
+ MOZ_CRASH("unused");
+ return nullptr;
+}
+
+bool ContentChild::DeallocPContentPermissionRequestChild(
+ PContentPermissionRequestChild* actor) {
+ nsContentPermissionUtils::NotifyRemoveContentPermissionRequestChild(actor);
+ auto child = static_cast<RemotePermissionRequest*>(actor);
+ child->IPDLRelease();
+ return true;
+}
+
+already_AddRefed<PWebBrowserPersistDocumentChild>
+ContentChild::AllocPWebBrowserPersistDocumentChild(
+ PBrowserChild* aBrowser, const MaybeDiscarded<BrowsingContext>& aContext) {
+ return MakeAndAddRef<WebBrowserPersistDocumentChild>();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvPWebBrowserPersistDocumentConstructor(
+ PWebBrowserPersistDocumentChild* aActor, PBrowserChild* aBrowser,
+ const MaybeDiscarded<BrowsingContext>& aContext) {
+ if (NS_WARN_IF(!aBrowser)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ if (aContext.IsNullOrDiscarded()) {
+ aActor->SendInitFailure(NS_ERROR_NO_CONTENT);
+ return IPC_OK();
+ }
+
+ nsCOMPtr<Document> foundDoc = aContext.get()->GetDocument();
+
+ if (!foundDoc) {
+ aActor->SendInitFailure(NS_ERROR_NO_CONTENT);
+ } else {
+ static_cast<WebBrowserPersistDocumentChild*>(aActor)->Start(foundDoc);
+ }
+ return IPC_OK();
+}
+
+static already_AddRefed<DataTransfer> ConvertToDataTransfer(
+ nsTArray<IPCTransferableData>&& aTransferables, EventMessage aMessage) {
+ // Check if we are receiving any file objects. If we are we will want
+ // to hide any of the other objects coming in from content.
+ bool hasFiles = false;
+ for (uint32_t i = 0; i < aTransferables.Length() && !hasFiles; ++i) {
+ auto& items = aTransferables[i].items();
+ for (uint32_t j = 0; j < items.Length() && !hasFiles; ++j) {
+ if (items[j].data().type() ==
+ IPCTransferableDataType::TIPCTransferableDataBlob) {
+ hasFiles = true;
+ }
+ }
+ }
+ // Add the entries from the IPC to the new DataTransfer
+ RefPtr<DataTransfer> dataTransfer =
+ new DataTransfer(nullptr, aMessage, false, -1);
+ for (uint32_t i = 0; i < aTransferables.Length(); ++i) {
+ auto& items = aTransferables[i].items();
+ for (uint32_t j = 0; j < items.Length(); ++j) {
+ const IPCTransferableDataItem& item = items[j];
+ RefPtr<nsVariantCC> variant = new nsVariantCC();
+ nsresult rv =
+ nsContentUtils::IPCTransferableDataItemToVariant(item, variant);
+ if (NS_FAILED(rv)) {
+ continue;
+ }
+
+ // We should hide this data from content if we have a file, and we
+ // aren't a file.
+ bool hidden =
+ hasFiles && item.data().type() !=
+ IPCTransferableDataType::TIPCTransferableDataBlob;
+ dataTransfer->SetDataWithPrincipalFromOtherProcess(
+ NS_ConvertUTF8toUTF16(item.flavor()), variant, i,
+ nsContentUtils::GetSystemPrincipal(), hidden);
+ }
+ }
+ return dataTransfer.forget();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvInvokeDragSession(
+ const MaybeDiscarded<WindowContext>& aSourceWindowContext,
+ const MaybeDiscarded<WindowContext>& aSourceTopWindowContext,
+ nsTArray<IPCTransferableData>&& aTransferables, const uint32_t& aAction) {
+ if (nsCOMPtr<nsIDragService> dragService =
+ do_GetService("@mozilla.org/widget/dragservice;1")) {
+ dragService->StartDragSession();
+ nsCOMPtr<nsIDragSession> session;
+ dragService->GetCurrentSession(getter_AddRefs(session));
+ if (session) {
+ session->SetSourceWindowContext(aSourceWindowContext.GetMaybeDiscarded());
+ session->SetSourceTopWindowContext(
+ aSourceTopWindowContext.GetMaybeDiscarded());
+ session->SetDragAction(aAction);
+
+ RefPtr<DataTransfer> dataTransfer =
+ ConvertToDataTransfer(std::move(aTransferables), eDragStart);
+ session->SetDataTransfer(dataTransfer);
+ }
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvUpdateDragSession(
+ nsTArray<IPCTransferableData>&& aTransferables,
+ EventMessage aEventMessage) {
+ if (nsCOMPtr<nsIDragService> dragService =
+ do_GetService("@mozilla.org/widget/dragservice;1")) {
+ nsCOMPtr<nsIDragSession> session;
+ dragService->GetCurrentSession(getter_AddRefs(session));
+ if (session) {
+ nsCOMPtr<DataTransfer> dataTransfer =
+ ConvertToDataTransfer(std::move(aTransferables), aEventMessage);
+ session->SetDataTransfer(dataTransfer);
+ }
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvEndDragSession(
+ const bool& aDoneDrag, const bool& aUserCancelled,
+ const LayoutDeviceIntPoint& aDragEndPoint, const uint32_t& aKeyModifiers,
+ const uint32_t& aDropEffect) {
+ nsCOMPtr<nsIDragService> dragService =
+ do_GetService("@mozilla.org/widget/dragservice;1");
+ if (dragService) {
+ nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
+ if (dragSession) {
+ if (aUserCancelled) {
+ dragSession->UserCancelled();
+ }
+
+ RefPtr<DataTransfer> dataTransfer = dragSession->GetDataTransfer();
+ if (dataTransfer) {
+ dataTransfer->SetDropEffectInt(aDropEffect);
+ }
+ }
+
+ static_cast<nsBaseDragService*>(dragService.get())
+ ->SetDragEndPoint(aDragEndPoint);
+ dragService->EndDragSession(aDoneDrag, aKeyModifiers);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvPush(const nsCString& aScope,
+ nsIPrincipal* aPrincipal,
+ const nsString& aMessageId) {
+ PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId, Nothing());
+ Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers()));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvPushWithData(
+ const nsCString& aScope, nsIPrincipal* aPrincipal,
+ const nsString& aMessageId, nsTArray<uint8_t>&& aData) {
+ PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId,
+ Some(std::move(aData)));
+ Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers()));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvPushSubscriptionChange(
+ const nsCString& aScope, nsIPrincipal* aPrincipal) {
+ PushSubscriptionChangeDispatcher dispatcher(aScope, aPrincipal);
+ Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers()));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvPushError(const nsCString& aScope,
+ nsIPrincipal* aPrincipal,
+ const nsString& aMessage,
+ const uint32_t& aFlags) {
+ PushErrorDispatcher dispatcher(aScope, aPrincipal, aMessage, aFlags);
+ Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers()));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+ContentChild::RecvNotifyPushSubscriptionModifiedObservers(
+ const nsCString& aScope, nsIPrincipal* aPrincipal) {
+ PushSubscriptionModifiedDispatcher dispatcher(aScope, aPrincipal);
+ Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObservers()));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvBlobURLRegistration(
+ const nsCString& aURI, const IPCBlob& aBlob, nsIPrincipal* aPrincipal,
+ const nsCString& aPartitionKey) {
+ RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(aBlob);
+ MOZ_ASSERT(blobImpl);
+
+ BlobURLProtocolHandler::AddDataEntry(aURI, aPrincipal, aPartitionKey,
+ blobImpl);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvBlobURLUnregistration(
+ const nsCString& aURI) {
+ BlobURLProtocolHandler::RemoveDataEntry(
+ aURI,
+ /* aBroadcastToOtherProcesses = */ false);
+ return IPC_OK();
+}
+
+void ContentChild::CreateGetFilesRequest(const nsAString& aDirectoryPath,
+ bool aRecursiveFlag, nsID& aUUID,
+ GetFilesHelperChild* aChild) {
+ MOZ_ASSERT(aChild);
+ MOZ_ASSERT(!mGetFilesPendingRequests.Contains(aUUID));
+
+ Unused << SendGetFilesRequest(aUUID, aDirectoryPath, aRecursiveFlag);
+ mGetFilesPendingRequests.InsertOrUpdate(aUUID, RefPtr{aChild});
+}
+
+void ContentChild::DeleteGetFilesRequest(nsID& aUUID,
+ GetFilesHelperChild* aChild) {
+ MOZ_ASSERT(aChild);
+ MOZ_ASSERT(mGetFilesPendingRequests.Contains(aUUID));
+
+ Unused << SendDeleteGetFilesRequest(aUUID);
+ mGetFilesPendingRequests.Remove(aUUID);
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvGetFilesResponse(
+ const nsID& aUUID, const GetFilesResponseResult& aResult) {
+ RefPtr<GetFilesHelperChild> child;
+
+ // This object can already been deleted in case DeleteGetFilesRequest has
+ // been called when the response was sending by the parent.
+ if (!mGetFilesPendingRequests.Remove(aUUID, getter_AddRefs(child))) {
+ return IPC_OK();
+ }
+
+ if (aResult.type() == GetFilesResponseResult::TGetFilesResponseFailure) {
+ child->Finished(aResult.get_GetFilesResponseFailure().errorCode());
+ } else {
+ MOZ_ASSERT(aResult.type() ==
+ GetFilesResponseResult::TGetFilesResponseSuccess);
+
+ const nsTArray<IPCBlob>& ipcBlobs =
+ aResult.get_GetFilesResponseSuccess().blobs();
+
+ bool succeeded = true;
+ for (uint32_t i = 0; succeeded && i < ipcBlobs.Length(); ++i) {
+ RefPtr<BlobImpl> impl = IPCBlobUtils::Deserialize(ipcBlobs[i]);
+ succeeded = child->AppendBlobImpl(impl);
+ }
+
+ child->Finished(succeeded ? NS_OK : NS_ERROR_OUT_OF_MEMORY);
+ }
+ return IPC_OK();
+}
+
+/* static */
+void ContentChild::FatalErrorIfNotUsingGPUProcess(const char* const aErrorMsg,
+ base::ProcessId aOtherPid) {
+ // If we're communicating with the same process or the UI process then we
+ // want to crash normally. Otherwise we want to just warn as the other end
+ // must be the GPU process and it crashing shouldn't be fatal for us.
+ if (aOtherPid == base::GetCurrentProcId() ||
+ (GetSingleton() && GetSingleton()->OtherPid() == aOtherPid)) {
+ mozilla::ipc::FatalError(aErrorMsg, false);
+ } else {
+ nsAutoCString formattedMessage("IPDL error: \"");
+ formattedMessage.AppendASCII(aErrorMsg);
+ formattedMessage.AppendLiteral(R"(".)");
+ NS_WARNING(formattedMessage.get());
+ }
+}
+
+PURLClassifierChild* ContentChild::AllocPURLClassifierChild(
+ nsIPrincipal* aPrincipal, bool* aSuccess) {
+ *aSuccess = true;
+ return new URLClassifierChild();
+}
+
+bool ContentChild::DeallocPURLClassifierChild(PURLClassifierChild* aActor) {
+ MOZ_ASSERT(aActor);
+ delete aActor;
+ return true;
+}
+
+PURLClassifierLocalChild* ContentChild::AllocPURLClassifierLocalChild(
+ nsIURI* aUri, Span<const IPCURLClassifierFeature> aFeatures) {
+ return new URLClassifierLocalChild();
+}
+
+bool ContentChild::DeallocPURLClassifierLocalChild(
+ PURLClassifierLocalChild* aActor) {
+ MOZ_ASSERT(aActor);
+ delete aActor;
+ return true;
+}
+
+PSessionStorageObserverChild*
+ContentChild::AllocPSessionStorageObserverChild() {
+ MOZ_CRASH(
+ "PSessionStorageObserverChild actors should be manually constructed!");
+}
+
+bool ContentChild::DeallocPSessionStorageObserverChild(
+ PSessionStorageObserverChild* aActor) {
+ MOZ_ASSERT(aActor);
+
+ delete aActor;
+ return true;
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvProvideAnonymousTemporaryFile(
+ const uint64_t& aID, const FileDescOrError& aFDOrError) {
+ mozilla::UniquePtr<AnonymousTemporaryFileCallback> callback;
+ mPendingAnonymousTemporaryFiles.Remove(aID, &callback);
+ MOZ_ASSERT(callback);
+
+ PRFileDesc* prfile = nullptr;
+ if (aFDOrError.type() == FileDescOrError::Tnsresult) {
+ DebugOnly<nsresult> rv = aFDOrError.get_nsresult();
+ MOZ_ASSERT(NS_FAILED(rv));
+ } else {
+ auto rawFD = aFDOrError.get_FileDescriptor().ClonePlatformHandle();
+ prfile = PR_ImportFile(PROsfd(rawFD.release()));
+ }
+ (*callback)(prfile);
+ return IPC_OK();
+}
+
+nsresult ContentChild::AsyncOpenAnonymousTemporaryFile(
+ const AnonymousTemporaryFileCallback& aCallback) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ static uint64_t id = 0;
+ auto newID = id++;
+ if (!SendRequestAnonymousTemporaryFile(newID)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Remember the association with the callback.
+ MOZ_ASSERT(!mPendingAnonymousTemporaryFiles.Get(newID));
+ mPendingAnonymousTemporaryFiles.GetOrInsertNew(newID, aCallback);
+ return NS_OK;
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvSetPermissionsWithKey(
+ const nsCString& aPermissionKey, nsTArray<IPC::Permission>&& aPerms) {
+ RefPtr<PermissionManager> permManager = PermissionManager::GetInstance();
+ if (permManager) {
+ permManager->SetPermissionsWithKey(aPermissionKey, aPerms);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvRefreshScreens(
+ nsTArray<ScreenDetails>&& aScreens) {
+ ScreenManager& screenManager = ScreenManager::GetSingleton();
+ screenManager.Refresh(std::move(aScreens));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvShareCodeCoverageMutex(
+ CrossProcessMutexHandle aHandle) {
+#ifdef MOZ_CODE_COVERAGE
+ CodeCoverageHandler::Init(std::move(aHandle));
+ return IPC_OK();
+#else
+ MOZ_CRASH("Shouldn't receive this message in non-code coverage builds!");
+#endif
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvFlushCodeCoverageCounters(
+ FlushCodeCoverageCountersResolver&& aResolver) {
+#ifdef MOZ_CODE_COVERAGE
+ CodeCoverageHandler::FlushCounters();
+ aResolver(/* unused */ true);
+ return IPC_OK();
+#else
+ MOZ_CRASH("Shouldn't receive this message in non-code coverage builds!");
+#endif
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvSetInputEventQueueEnabled() {
+ nsThreadManager::get().EnableMainThreadEventPrioritization();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvFlushInputEventQueue() {
+ nsThreadManager::get().FlushInputEventPrioritization();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvSuspendInputEventQueue() {
+ nsThreadManager::get().SuspendInputEventPrioritization();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvResumeInputEventQueue() {
+ nsThreadManager::get().ResumeInputEventPrioritization();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvAddDynamicScalars(
+ nsTArray<DynamicScalarDefinition>&& aDefs) {
+ TelemetryIPC::AddDynamicScalarDefinitions(aDefs);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvCrossProcessRedirect(
+ RedirectToRealChannelArgs&& aArgs,
+ nsTArray<Endpoint<extensions::PStreamFilterParent>>&& aEndpoints,
+ CrossProcessRedirectResolver&& aResolve) {
+ nsCOMPtr<nsILoadInfo> loadInfo;
+ nsresult rv = mozilla::ipc::LoadInfoArgsToLoadInfo(
+ aArgs.loadInfo(), NOT_REMOTE_TYPE, getter_AddRefs(loadInfo));
+ if (NS_FAILED(rv)) {
+ MOZ_DIAGNOSTIC_ASSERT(false, "LoadInfoArgsToLoadInfo failed");
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsIChannel> newChannel;
+ MOZ_ASSERT((aArgs.loadStateInternalLoadFlags() &
+ nsDocShell::InternalLoad::INTERNAL_LOAD_FLAGS_IS_SRCDOC) ||
+ aArgs.srcdocData().IsVoid());
+ rv = nsDocShell::CreateRealChannelForDocument(
+ getter_AddRefs(newChannel), aArgs.uri(), loadInfo, nullptr,
+ aArgs.newLoadFlags(), aArgs.srcdocData(), aArgs.baseUri());
+
+ if (RefPtr<HttpBaseChannel> httpChannel = do_QueryObject(newChannel)) {
+ httpChannel->SetEarlyHints(std::move(aArgs.earlyHints()));
+ httpChannel->SetEarlyHintLinkType(aArgs.earlyHintLinkType());
+ }
+
+ // This is used to report any errors back to the parent by calling
+ // CrossProcessRedirectFinished.
+ RefPtr<HttpChannelChild> httpChild = do_QueryObject(newChannel);
+ auto resolve = [=](const nsresult& aRv) {
+ nsresult rv = aRv;
+ if (httpChild) {
+ rv = httpChild->CrossProcessRedirectFinished(rv);
+ }
+ aResolve(rv);
+ };
+ auto scopeExit = MakeScopeExit([&]() { resolve(rv); });
+
+ if (NS_FAILED(rv)) {
+ return IPC_OK();
+ }
+
+ if (nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(newChannel)) {
+ rv = httpChannel->SetChannelId(aArgs.channelId());
+ }
+ if (NS_FAILED(rv)) {
+ return IPC_OK();
+ }
+
+ rv = newChannel->SetOriginalURI(aArgs.originalURI());
+ if (NS_FAILED(rv)) {
+ return IPC_OK();
+ }
+
+ if (nsCOMPtr<nsIHttpChannelInternal> httpChannelInternal =
+ do_QueryInterface(newChannel)) {
+ rv = httpChannelInternal->SetRedirectMode(aArgs.redirectMode());
+ }
+ if (NS_FAILED(rv)) {
+ return IPC_OK();
+ }
+
+ if (aArgs.init()) {
+ HttpBaseChannel::ReplacementChannelConfig config(std::move(*aArgs.init()));
+ HttpBaseChannel::ConfigureReplacementChannel(
+ newChannel, config,
+ HttpBaseChannel::ReplacementReason::DocumentChannel);
+ }
+
+ if (aArgs.contentDisposition()) {
+ newChannel->SetContentDisposition(*aArgs.contentDisposition());
+ }
+
+ if (aArgs.contentDispositionFilename()) {
+ newChannel->SetContentDispositionFilename(
+ *aArgs.contentDispositionFilename());
+ }
+
+ if (nsCOMPtr<nsIChildChannel> childChannel = do_QueryInterface(newChannel)) {
+ // Connect to the parent if this is a remote channel. If it's entirely
+ // handled locally, then we'll call AsyncOpen from the docshell when
+ // we complete the setup
+ rv = childChannel->ConnectParent(
+ aArgs.registrarId()); // creates parent channel
+ if (NS_FAILED(rv)) {
+ return IPC_OK();
+ }
+ }
+
+ // We need to copy the property bag before signaling that the channel
+ // is ready so that the nsDocShell can retrieve the history data when called.
+ if (nsCOMPtr<nsIWritablePropertyBag> bag = do_QueryInterface(newChannel)) {
+ nsHashPropertyBag::CopyFrom(bag, aArgs.properties());
+ }
+
+ RefPtr<nsDocShellLoadState> loadState;
+ rv = nsDocShellLoadState::CreateFromPendingChannel(
+ newChannel, aArgs.loadIdentifier(), aArgs.registrarId(),
+ getter_AddRefs(loadState));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return IPC_OK();
+ }
+ loadState->SetLoadFlags(aArgs.loadStateExternalLoadFlags());
+ loadState->SetInternalLoadFlags(aArgs.loadStateInternalLoadFlags());
+ if (IsValidLoadType(aArgs.loadStateLoadType())) {
+ loadState->SetLoadType(aArgs.loadStateLoadType());
+ }
+
+ if (aArgs.loadingSessionHistoryInfo().isSome()) {
+ loadState->SetLoadingSessionHistoryInfo(
+ aArgs.loadingSessionHistoryInfo().ref());
+ }
+ if (aArgs.originalUriString().isSome()) {
+ loadState->SetOriginalURIString(aArgs.originalUriString().ref());
+ }
+
+ RefPtr<ChildProcessChannelListener> processListener =
+ ChildProcessChannelListener::GetSingleton();
+ // The listener will call completeRedirectSetup or asyncOpen on the channel.
+ processListener->OnChannelReady(loadState, aArgs.loadIdentifier(),
+ std::move(aEndpoints), aArgs.timing(),
+ std::move(resolve));
+ scopeExit.release();
+
+ // scopeExit will call CrossProcessRedirectFinished(rv) here
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvStartDelayedAutoplayMediaComponents(
+ const MaybeDiscarded<BrowsingContext>& aContext) {
+ if (NS_WARN_IF(aContext.IsNullOrDiscarded())) {
+ return IPC_OK();
+ }
+
+ aContext.get()->StartDelayedAutoplayMediaComponents();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvUpdateMediaControlAction(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const MediaControlAction& aAction) {
+ if (NS_WARN_IF(aContext.IsNullOrDiscarded())) {
+ return IPC_OK();
+ }
+
+ ContentMediaControlKeyHandler::HandleMediaControlAction(aContext.get(),
+ aAction);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvOnAllowAccessFor(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const nsCString& aTrackingOrigin, uint32_t aCookieBehavior,
+ const ContentBlockingNotifier::StorageAccessPermissionGrantedReason&
+ aReason) {
+ MOZ_ASSERT(!aContext.IsNull(), "Browsing context cannot be null");
+
+ StorageAccessAPIHelper::OnAllowAccessFor(
+ aContext.GetMaybeDiscarded(), aTrackingOrigin, aCookieBehavior, aReason);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvOnContentBlockingDecision(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const ContentBlockingNotifier::BlockingDecision& aDecision,
+ uint32_t aRejectedReason) {
+ MOZ_ASSERT(!aContext.IsNull(), "Browsing context cannot be null");
+
+ nsCOMPtr<nsPIDOMWindowOuter> outer = aContext.get()->GetDOMWindow();
+ if (!outer) {
+ MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ChildIPC: Trying to send a message to a context without a outer "
+ "window"));
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsPIDOMWindowInner> inner = outer->GetCurrentInnerWindow();
+ if (!inner) {
+ MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ChildIPC: Trying to send a message to a context without a inner "
+ "window"));
+ return IPC_OK();
+ }
+
+ ContentBlockingNotifier::OnDecision(inner, aDecision, aRejectedReason);
+ return IPC_OK();
+}
+
+#ifdef NIGHTLY_BUILD
+void ContentChild::OnChannelReceivedMessage(const Message& aMsg) {
+ if (nsContentUtils::IsMessageInputEvent(aMsg)) {
+ mPendingInputEvents++;
+ }
+}
+
+PContentChild::Result ContentChild::OnMessageReceived(const Message& aMsg) {
+ if (nsContentUtils::IsMessageInputEvent(aMsg)) {
+ DebugOnly<uint32_t> prevEvts = mPendingInputEvents--;
+ MOZ_ASSERT(prevEvts > 0);
+ }
+
+ return PContentChild::OnMessageReceived(aMsg);
+}
+
+PContentChild::Result ContentChild::OnMessageReceived(
+ const Message& aMsg, UniquePtr<Message>& aReply) {
+ return PContentChild::OnMessageReceived(aMsg, aReply);
+}
+#endif
+
+mozilla::ipc::IPCResult ContentChild::RecvCreateBrowsingContext(
+ uint64_t aGroupId, BrowsingContext::IPCInitializer&& aInit) {
+ // We can't already have a BrowsingContext with this ID.
+ if (RefPtr<BrowsingContext> existing = BrowsingContext::Get(aInit.mId)) {
+ return IPC_FAIL(this, "Browsing context already exists");
+ }
+
+ RefPtr<WindowContext> parent = WindowContext::GetById(aInit.mParentId);
+ if (!parent && aInit.mParentId != 0) {
+ // Handle this case by ignoring the request, as parent must be in the
+ // process of being discarded.
+ // In the future it would be nice to avoid sending this message to the child
+ // at all.
+ NS_WARNING("Attempt to attach BrowsingContext to discarded parent");
+ return IPC_OK();
+ }
+
+ RefPtr<BrowsingContextGroup> group =
+ BrowsingContextGroup::GetOrCreate(aGroupId);
+ return BrowsingContext::CreateFromIPC(std::move(aInit), group, nullptr);
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvDiscardBrowsingContext(
+ const MaybeDiscarded<BrowsingContext>& aContext, bool aDoDiscard,
+ DiscardBrowsingContextResolver&& aResolve) {
+ if (BrowsingContext* context = aContext.GetMaybeDiscarded()) {
+ if (aDoDiscard && !context->IsDiscarded()) {
+ context->Detach(/* aFromIPC */ true);
+ }
+ context->AddDiscardListener(aResolve);
+ return IPC_OK();
+ }
+
+ // Immediately resolve the promise, as we've received the message. This will
+ // allow the parent process to discard references to this BC.
+ aResolve(true);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvRegisterBrowsingContextGroup(
+ uint64_t aGroupId, nsTArray<SyncedContextInitializer>&& aInits) {
+ RefPtr<BrowsingContextGroup> group =
+ BrowsingContextGroup::GetOrCreate(aGroupId);
+
+ // Each of the initializers in aInits is sorted in pre-order, so our parent
+ // should always be available before the element itself.
+ for (auto& initUnion : aInits) {
+ switch (initUnion.type()) {
+ case SyncedContextInitializer::TBrowsingContextInitializer: {
+ auto& init = initUnion.get_BrowsingContextInitializer();
+#ifdef DEBUG
+ RefPtr<BrowsingContext> existing = BrowsingContext::Get(init.mId);
+ MOZ_ASSERT(!existing, "BrowsingContext must not exist yet!");
+
+ RefPtr<WindowContext> parent = init.GetParent();
+ MOZ_ASSERT_IF(parent, parent->Group() == group);
+#endif
+
+ BrowsingContext::CreateFromIPC(std::move(init), group, nullptr);
+ break;
+ }
+ case SyncedContextInitializer::TWindowContextInitializer: {
+ auto& init = initUnion.get_WindowContextInitializer();
+#ifdef DEBUG
+ RefPtr<WindowContext> existing =
+ WindowContext::GetById(init.mInnerWindowId);
+ MOZ_ASSERT(!existing, "WindowContext must not exist yet!");
+ RefPtr<BrowsingContext> parent =
+ BrowsingContext::Get(init.mBrowsingContextId);
+ MOZ_ASSERT(parent && parent->Group() == group);
+#endif
+
+ WindowContext::CreateFromIPC(std::move(init));
+ break;
+ };
+ default:
+ MOZ_ASSERT_UNREACHABLE();
+ }
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvDestroyBrowsingContextGroup(
+ uint64_t aGroupId) {
+ if (RefPtr<BrowsingContextGroup> group =
+ BrowsingContextGroup::GetExisting(aGroupId)) {
+ group->ChildDestroy();
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvWindowClose(
+ const MaybeDiscarded<BrowsingContext>& aContext, bool aTrustedCaller) {
+ if (aContext.IsNullOrDiscarded()) {
+ MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ChildIPC: Trying to send a message to dead or detached context"));
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> window = aContext.get()->GetDOMWindow();
+ if (!window) {
+ MOZ_LOG(
+ BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ChildIPC: Trying to send a message to a context without a window"));
+ return IPC_OK();
+ }
+
+ // Call `GetDocument()` to force the document and its inner window to be
+ // created, as it would be forced to be created if this call was being
+ // performed in-process.
+ if (NS_WARN_IF(!aContext.get()->GetDocument())) {
+ MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ChildIPC: Trying to send a message to a context but document "
+ "creation failed"));
+ return IPC_OK();
+ }
+
+ nsGlobalWindowOuter::Cast(window)->CloseOuter(aTrustedCaller);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvWindowFocus(
+ const MaybeDiscarded<BrowsingContext>& aContext, CallerType aCallerType,
+ uint64_t aActionId) {
+ if (aContext.IsNullOrDiscarded()) {
+ MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ChildIPC: Trying to send a message to dead or detached context"));
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> window = aContext.get()->GetDOMWindow();
+ if (!window) {
+ MOZ_LOG(
+ BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ChildIPC: Trying to send a message to a context without a window"));
+ return IPC_OK();
+ }
+
+ // Call `GetDocument()` to force the document and its inner window to be
+ // created, as it would be forced to be created if this call was being
+ // performed in-process.
+ if (NS_WARN_IF(!aContext.get()->GetDocument())) {
+ MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ChildIPC: Trying to send a message to a context but document "
+ "creation failed"));
+ return IPC_OK();
+ }
+
+ nsGlobalWindowOuter::Cast(window)->FocusOuter(
+ aCallerType, /* aFromOtherProcess */ true, aActionId);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvWindowBlur(
+ const MaybeDiscarded<BrowsingContext>& aContext, CallerType aCallerType) {
+ if (aContext.IsNullOrDiscarded()) {
+ MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ChildIPC: Trying to send a message to dead or detached context"));
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> window = aContext.get()->GetDOMWindow();
+ if (!window) {
+ MOZ_LOG(
+ BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ChildIPC: Trying to send a message to a context without a window"));
+ return IPC_OK();
+ }
+
+ // Call `GetDocument()` to force the document and its inner window to be
+ // created, as it would be forced to be created if this call was being
+ // performed in-process.
+ if (NS_WARN_IF(!aContext.get()->GetDocument())) {
+ MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ChildIPC: Trying to send a message to a context but document "
+ "creation failed"));
+ return IPC_OK();
+ }
+
+ nsGlobalWindowOuter::Cast(window)->BlurOuter(aCallerType);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvRaiseWindow(
+ const MaybeDiscarded<BrowsingContext>& aContext, CallerType aCallerType,
+ uint64_t aActionId) {
+ if (aContext.IsNullOrDiscarded()) {
+ MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ChildIPC: Trying to send a message to dead or detached context"));
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> window = aContext.get()->GetDOMWindow();
+ if (!window) {
+ MOZ_LOG(
+ BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ChildIPC: Trying to send a message to a context without a window"));
+ return IPC_OK();
+ }
+
+ if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
+ fm->RaiseWindow(window, aCallerType, aActionId);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvAdjustWindowFocus(
+ const MaybeDiscarded<BrowsingContext>& aContext, bool aIsVisible,
+ uint64_t aActionId) {
+ if (aContext.IsNullOrDiscarded()) {
+ MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ChildIPC: Trying to send a message to dead or detached context"));
+ return IPC_OK();
+ }
+
+ if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
+ RefPtr<BrowsingContext> bc = aContext.get();
+ fm->AdjustInProcessWindowFocus(bc, false, aIsVisible, aActionId);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvClearFocus(
+ const MaybeDiscarded<BrowsingContext>& aContext) {
+ if (aContext.IsNullOrDiscarded()) {
+ MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ChildIPC: Trying to send a message to dead or detached context"));
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> window = aContext.get()->GetDOMWindow();
+ if (!window) {
+ MOZ_LOG(
+ BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ChildIPC: Trying to send a message to a context without a window"));
+ return IPC_OK();
+ }
+
+ if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
+ fm->ClearFocus(window);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvSetFocusedBrowsingContext(
+ const MaybeDiscarded<BrowsingContext>& aContext, uint64_t aActionId) {
+ if (aContext.IsNullOrDiscarded()) {
+ MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ChildIPC: Trying to send a message to dead or detached context"));
+ return IPC_OK();
+ }
+
+ nsFocusManager* fm = nsFocusManager::GetFocusManager();
+ if (fm) {
+ fm->SetFocusedBrowsingContextFromOtherProcess(aContext.get(), aActionId);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvSetActiveBrowsingContext(
+ const MaybeDiscarded<BrowsingContext>& aContext, uint64_t aActionId) {
+ if (aContext.IsNullOrDiscarded()) {
+ MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ChildIPC: Trying to send a message to dead or detached context"));
+ return IPC_OK();
+ }
+
+ nsFocusManager* fm = nsFocusManager::GetFocusManager();
+ if (fm) {
+ fm->SetActiveBrowsingContextFromOtherProcess(aContext.get(), aActionId);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvAbortOrientationPendingPromises(
+ const MaybeDiscarded<BrowsingContext>& aContext) {
+ if (aContext.IsNullOrDiscarded()) {
+ MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ChildIPC: Trying to send a message to dead or detached context"));
+ return IPC_OK();
+ }
+
+ dom::ScreenOrientation::AbortInProcessOrientationPromises(aContext.get());
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvUnsetActiveBrowsingContext(
+ const MaybeDiscarded<BrowsingContext>& aContext, uint64_t aActionId) {
+ if (aContext.IsNullOrDiscarded()) {
+ MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ChildIPC: Trying to send a message to dead or detached context"));
+ return IPC_OK();
+ }
+
+ nsFocusManager* fm = nsFocusManager::GetFocusManager();
+ if (fm) {
+ fm->UnsetActiveBrowsingContextFromOtherProcess(aContext.get(), aActionId);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvSetFocusedElement(
+ const MaybeDiscarded<BrowsingContext>& aContext, bool aNeedsFocus) {
+ if (aContext.IsNullOrDiscarded()) {
+ MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ChildIPC: Trying to send a message to dead or detached context"));
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> window = aContext.get()->GetDOMWindow();
+ if (!window) {
+ MOZ_LOG(
+ BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ChildIPC: Trying to send a message to a context without a window"));
+ return IPC_OK();
+ }
+
+ window->SetFocusedElement(nullptr, 0, aNeedsFocus);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvFinalizeFocusOuter(
+ const MaybeDiscarded<BrowsingContext>& aContext, bool aCanFocus,
+ CallerType aCallerType) {
+ if (aContext.IsNullOrDiscarded()) {
+ MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ChildIPC: Trying to send a message to dead or detached context"));
+ return IPC_OK();
+ }
+
+ if (Element* frame = aContext.get()->GetEmbedderElement()) {
+ nsContentUtils::RequestFrameFocus(*frame, aCanFocus, aCallerType);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvBlurToChild(
+ const MaybeDiscarded<BrowsingContext>& aFocusedBrowsingContext,
+ const MaybeDiscarded<BrowsingContext>& aBrowsingContextToClear,
+ const MaybeDiscarded<BrowsingContext>& aAncestorBrowsingContextToFocus,
+ bool aIsLeavingDocument, bool aAdjustWidget, uint64_t aActionId) {
+ if (aFocusedBrowsingContext.IsNullOrDiscarded()) {
+ MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ChildIPC: Trying to send a message to dead or detached context"));
+ return IPC_OK();
+ }
+
+ RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager();
+ if (MOZ_UNLIKELY(!fm)) {
+ return IPC_OK();
+ }
+
+ RefPtr<BrowsingContext> toClear = aBrowsingContextToClear.IsDiscarded()
+ ? nullptr
+ : aBrowsingContextToClear.get();
+ RefPtr<BrowsingContext> toFocus =
+ aAncestorBrowsingContextToFocus.IsDiscarded()
+ ? nullptr
+ : aAncestorBrowsingContextToFocus.get();
+
+ RefPtr<BrowsingContext> focusedBrowsingContext =
+ aFocusedBrowsingContext.get();
+
+ fm->BlurFromOtherProcess(focusedBrowsingContext, toClear, toFocus,
+ aIsLeavingDocument, aAdjustWidget, aActionId);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvSetupFocusedAndActive(
+ const MaybeDiscarded<BrowsingContext>& aFocusedBrowsingContext,
+ uint64_t aActionIdForFocused,
+ const MaybeDiscarded<BrowsingContext>& aActiveBrowsingContext,
+ uint64_t aActionIdForActive) {
+ nsFocusManager* fm = nsFocusManager::GetFocusManager();
+ if (fm) {
+ if (!aActiveBrowsingContext.IsNullOrDiscarded()) {
+ fm->SetActiveBrowsingContextFromOtherProcess(aActiveBrowsingContext.get(),
+ aActionIdForActive);
+ }
+ if (!aFocusedBrowsingContext.IsNullOrDiscarded()) {
+ fm->SetFocusedBrowsingContextFromOtherProcess(
+ aFocusedBrowsingContext.get(), aActionIdForFocused);
+ }
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvReviseActiveBrowsingContext(
+ uint64_t aOldActionId,
+ const MaybeDiscarded<BrowsingContext>& aActiveBrowsingContext,
+ uint64_t aNewActionId) {
+ nsFocusManager* fm = nsFocusManager::GetFocusManager();
+ if (fm && !aActiveBrowsingContext.IsNullOrDiscarded()) {
+ fm->ReviseActiveBrowsingContext(aOldActionId, aActiveBrowsingContext.get(),
+ aNewActionId);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvReviseFocusedBrowsingContext(
+ uint64_t aOldActionId,
+ const MaybeDiscarded<BrowsingContext>& aFocusedBrowsingContext,
+ uint64_t aNewActionId) {
+ nsFocusManager* fm = nsFocusManager::GetFocusManager();
+ if (fm && !aFocusedBrowsingContext.IsNullOrDiscarded()) {
+ fm->ReviseFocusedBrowsingContext(
+ aOldActionId, aFocusedBrowsingContext.get(), aNewActionId);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvMaybeExitFullscreen(
+ const MaybeDiscarded<BrowsingContext>& aContext) {
+ if (aContext.IsNullOrDiscarded()) {
+ MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ChildIPC: Trying to send a message to dead or detached context"));
+ return IPC_OK();
+ }
+
+ nsIDocShell* shell = aContext.get()->GetDocShell();
+ if (!shell) {
+ return IPC_OK();
+ }
+
+ Document* doc = shell->GetDocument();
+ if (doc && doc->GetFullscreenElement()) {
+ Document::AsyncExitFullscreen(doc);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvWindowPostMessage(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const ClonedOrErrorMessageData& aMessage, const PostMessageData& aData) {
+ if (aContext.IsNullOrDiscarded()) {
+ MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ChildIPC: Trying to send a message to dead or detached context"));
+ return IPC_OK();
+ }
+
+ RefPtr<nsGlobalWindowOuter> window =
+ nsGlobalWindowOuter::Cast(aContext.get()->GetDOMWindow());
+ if (!window) {
+ MOZ_LOG(
+ BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ChildIPC: Trying to send a message to a context without a window"));
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsIPrincipal> providedPrincipal;
+ if (!window->GetPrincipalForPostMessage(
+ aData.targetOrigin(), aData.targetOriginURI(),
+ aData.callerPrincipal(), *aData.subjectPrincipal(),
+ getter_AddRefs(providedPrincipal))) {
+ return IPC_OK();
+ }
+
+ // Call `GetDocument()` to force the document and its inner window to be
+ // created, as it would be forced to be created if this call was being
+ // performed in-process.
+ if (NS_WARN_IF(!aContext.get()->GetDocument())) {
+ MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ChildIPC: Trying to send a message to a context but document "
+ "creation failed"));
+ return IPC_OK();
+ }
+
+ // It's OK if `sourceBc` has already been discarded, so long as we can
+ // continue to wrap it.
+ RefPtr<BrowsingContext> sourceBc = aData.source().GetMaybeDiscarded();
+
+ // Create and asynchronously dispatch a runnable which will handle actual DOM
+ // event creation and dispatch.
+ RefPtr<PostMessageEvent> event =
+ new PostMessageEvent(sourceBc, aData.origin(), window, providedPrincipal,
+ aData.innerWindowId(), aData.callerURI(),
+ aData.scriptLocation(), aData.isFromPrivateWindow());
+ event->UnpackFrom(aMessage);
+
+ event->DispatchToTargetThread(IgnoredErrorResult());
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvCommitBrowsingContextTransaction(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ BrowsingContext::BaseTransaction&& aTransaction, uint64_t aEpoch) {
+ return aTransaction.CommitFromIPC(aContext, aEpoch, this);
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvCommitWindowContextTransaction(
+ const MaybeDiscarded<WindowContext>& aContext,
+ WindowContext::BaseTransaction&& aTransaction, uint64_t aEpoch) {
+ return aTransaction.CommitFromIPC(aContext, aEpoch, this);
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvCreateWindowContext(
+ WindowContext::IPCInitializer&& aInit) {
+ RefPtr<BrowsingContext> bc = BrowsingContext::Get(aInit.mBrowsingContextId);
+ if (!bc) {
+ // Handle this case by ignoring the request, as bc must be in the process of
+ // being discarded.
+ // In the future it would be nice to avoid sending this message to the child
+ // at all.
+ NS_WARNING("Attempt to attach WindowContext to discarded parent");
+ return IPC_OK();
+ }
+
+ WindowContext::CreateFromIPC(std::move(aInit));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvDiscardWindowContext(
+ uint64_t aContextId, DiscardWindowContextResolver&& aResolve) {
+ // Resolve immediately to acknowledge call
+ aResolve(true);
+
+ RefPtr<WindowContext> window = WindowContext::GetById(aContextId);
+ if (NS_WARN_IF(!window) || NS_WARN_IF(window->IsDiscarded())) {
+ return IPC_OK();
+ }
+
+ window->Discard();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvScriptError(
+ const nsString& aMessage, const nsString& aSourceName,
+ const nsString& aSourceLine, const uint32_t& aLineNumber,
+ const uint32_t& aColNumber, const uint32_t& aFlags,
+ const nsCString& aCategory, const bool& aFromPrivateWindow,
+ const uint64_t& aInnerWindowId, const bool& aFromChromeContext) {
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIConsoleService> consoleService =
+ do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, IPC_FAIL(this, "Failed to get console service"));
+
+ nsCOMPtr<nsIScriptError> scriptError(
+ do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
+ NS_ENSURE_TRUE(scriptError,
+ IPC_FAIL(this, "Failed to construct nsIScriptError"));
+
+ scriptError->InitWithWindowID(aMessage, aSourceName, aSourceLine, aLineNumber,
+ aColNumber, aFlags, aCategory, aInnerWindowId,
+ aFromChromeContext);
+ rv = consoleService->LogMessage(scriptError);
+ NS_ENSURE_SUCCESS(rv, IPC_FAIL(this, "Failed to log script error"));
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvReportFrameTimingData(
+ const LoadInfoArgs& loadInfoArgs, const nsString& entryName,
+ const nsString& initiatorType, UniquePtr<PerformanceTimingData>&& aData) {
+ if (!aData) {
+ return IPC_FAIL(this, "aData should not be null");
+ }
+
+ nsCOMPtr<nsILoadInfo> loadInfo;
+ nsresult rv = mozilla::ipc::LoadInfoArgsToLoadInfo(
+ loadInfoArgs, NOT_REMOTE_TYPE, getter_AddRefs(loadInfo));
+ if (NS_FAILED(rv)) {
+ MOZ_DIAGNOSTIC_ASSERT(false, "LoadInfoArgsToLoadInfo failed");
+ return IPC_OK();
+ }
+
+ // It is important to call LoadInfo::GetPerformanceStorage instead of simply
+ // getting the performance object via the innerWindowID in order to perform
+ // necessary cross origin checks.
+ if (PerformanceStorage* storage = loadInfo->GetPerformanceStorage()) {
+ storage->AddEntry(entryName, initiatorType, std::move(aData));
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvLoadURI(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ nsDocShellLoadState* aLoadState, bool aSetNavigating,
+ LoadURIResolver&& aResolve) {
+ auto resolveOnExit = MakeScopeExit([&] { aResolve(true); });
+
+ if (aContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+ RefPtr<BrowsingContext> context = aContext.get();
+ if (!context->IsInProcess()) {
+ // The DocShell has been torn down or the BrowsingContext has changed
+ // process in the middle of the load request. There's not much we can do at
+ // this point, so just give up.
+ return IPC_OK();
+ }
+
+ context->LoadURI(aLoadState, aSetNavigating);
+
+ nsCOMPtr<nsPIDOMWindowOuter> window = context->GetDOMWindow();
+ BrowserChild* bc = BrowserChild::GetFrom(window);
+ if (bc) {
+ bc->NotifyNavigationFinished();
+ }
+
+#ifdef MOZ_CRASHREPORTER
+ if (CrashReporter::GetEnabled()) {
+ nsCOMPtr<nsIURI> annotationURI;
+
+ nsresult rv = NS_MutateURI(aLoadState->URI())
+ .SetUserPass(""_ns)
+ .Finalize(annotationURI);
+
+ if (NS_FAILED(rv)) {
+ // Ignore failures on about: URIs.
+ annotationURI = aLoadState->URI();
+ }
+
+ CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::URL,
+ annotationURI->GetSpecOrDefault());
+ }
+#endif
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvInternalLoad(
+ nsDocShellLoadState* aLoadState) {
+ if (!aLoadState->Target().IsEmpty() ||
+ aLoadState->TargetBrowsingContext().IsNull()) {
+ return IPC_FAIL(this, "must already be retargeted");
+ }
+ if (aLoadState->TargetBrowsingContext().IsDiscarded()) {
+ return IPC_OK();
+ }
+ RefPtr<BrowsingContext> context = aLoadState->TargetBrowsingContext().get();
+
+ context->InternalLoad(aLoadState);
+
+#ifdef MOZ_CRASHREPORTER
+ if (CrashReporter::GetEnabled()) {
+ nsCOMPtr<nsIURI> annotationURI;
+
+ nsresult rv = NS_MutateURI(aLoadState->URI())
+ .SetUserPass(""_ns)
+ .Finalize(annotationURI);
+
+ if (NS_FAILED(rv)) {
+ // Ignore failures on about: URIs.
+ annotationURI = aLoadState->URI();
+ }
+
+ CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::URL,
+ annotationURI->GetSpecOrDefault());
+ }
+#endif
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvDisplayLoadError(
+ const MaybeDiscarded<BrowsingContext>& aContext, const nsAString& aURI) {
+ if (aContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+ RefPtr<BrowsingContext> context = aContext.get();
+
+ context->DisplayLoadError(aURI);
+
+ nsCOMPtr<nsPIDOMWindowOuter> window = context->GetDOMWindow();
+ BrowserChild* bc = BrowserChild::GetFrom(window);
+ if (bc) {
+ bc->NotifyNavigationFinished();
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvHistoryCommitIndexAndLength(
+ const MaybeDiscarded<BrowsingContext>& aContext, const uint32_t& aIndex,
+ const uint32_t& aLength, const nsID& aChangeID) {
+ if (!aContext.IsNullOrDiscarded()) {
+ ChildSHistory* shistory = aContext.get()->GetChildSessionHistory();
+ if (shistory) {
+ shistory->SetIndexAndLength(aIndex, aLength, aChangeID);
+ }
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvGetLayoutHistoryState(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ GetLayoutHistoryStateResolver&& aResolver) {
+ nsCOMPtr<nsILayoutHistoryState> state;
+ nsIDocShell* docShell;
+ mozilla::Maybe<mozilla::dom::Wireframe> wireframe;
+ if (!aContext.IsNullOrDiscarded() &&
+ (docShell = aContext.get()->GetDocShell())) {
+ docShell->PersistLayoutHistoryState();
+ docShell->GetLayoutHistoryState(getter_AddRefs(state));
+ wireframe = static_cast<nsDocShell*>(docShell)->GetWireframe();
+ }
+ aResolver(
+ std::tuple<nsILayoutHistoryState*, const mozilla::Maybe<Wireframe>&>(
+ state, wireframe));
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvDispatchLocationChangeEvent(
+ const MaybeDiscarded<BrowsingContext>& aContext) {
+ if (!aContext.IsNullOrDiscarded() && aContext.get()->GetDocShell()) {
+ aContext.get()->GetDocShell()->DispatchLocationChangeEvent();
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvDispatchBeforeUnloadToSubtree(
+ const MaybeDiscarded<BrowsingContext>& aStartingAt,
+ DispatchBeforeUnloadToSubtreeResolver&& aResolver) {
+ if (aStartingAt.IsNullOrDiscarded()) {
+ aResolver(nsIDocumentViewer::eAllowNavigation);
+ } else {
+ DispatchBeforeUnloadToSubtree(aStartingAt.get(), std::move(aResolver));
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvDecoderSupportedMimeTypes(
+ nsTArray<nsCString>&& aSupportedTypes) {
+#ifdef MOZ_WIDGET_ANDROID
+ AndroidDecoderModule::SetSupportedMimeTypes(std::move(aSupportedTypes));
+#endif
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvInitNextGenLocalStorageEnabled(
+ const bool& aEnabled) {
+ mozilla::dom::RecvInitNextGenLocalStorageEnabled(aEnabled);
+
+ return IPC_OK();
+}
+
+/* static */ void ContentChild::DispatchBeforeUnloadToSubtree(
+ BrowsingContext* aStartingAt,
+ const DispatchBeforeUnloadToSubtreeResolver& aResolver) {
+ bool resolved = false;
+
+ aStartingAt->PreOrderWalk([&](dom::BrowsingContext* aBC) {
+ if (aBC->GetDocShell()) {
+ nsCOMPtr<nsIDocumentViewer> viewer;
+ aBC->GetDocShell()->GetDocViewer(getter_AddRefs(viewer));
+ if (viewer &&
+ viewer->DispatchBeforeUnload() ==
+ nsIDocumentViewer::eRequestBlockNavigation &&
+ !resolved) {
+ // Send our response as soon as we find any blocker, so that we can show
+ // the permit unload prompt as soon as possible, without giving
+ // subsequent handlers a chance to delay it.
+ aResolver(nsIDocumentViewer::eRequestBlockNavigation);
+ resolved = true;
+ }
+ }
+ });
+
+ if (!resolved) {
+ aResolver(nsIDocumentViewer::eAllowNavigation);
+ }
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvGoBack(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const Maybe<int32_t>& aCancelContentJSEpoch, bool aRequireUserInteraction,
+ bool aUserActivation) {
+ if (aContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+ BrowsingContext* bc = aContext.get();
+
+ if (RefPtr<nsDocShell> docShell = nsDocShell::Cast(bc->GetDocShell())) {
+ if (aCancelContentJSEpoch) {
+ docShell->SetCancelContentJSEpoch(*aCancelContentJSEpoch);
+ }
+ docShell->GoBack(aRequireUserInteraction, aUserActivation);
+
+ if (BrowserChild* browserChild = BrowserChild::GetFrom(docShell)) {
+ browserChild->NotifyNavigationFinished();
+ }
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvGoForward(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const Maybe<int32_t>& aCancelContentJSEpoch, bool aRequireUserInteraction,
+ bool aUserActivation) {
+ if (aContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+ BrowsingContext* bc = aContext.get();
+
+ if (RefPtr<nsDocShell> docShell = nsDocShell::Cast(bc->GetDocShell())) {
+ if (aCancelContentJSEpoch) {
+ docShell->SetCancelContentJSEpoch(*aCancelContentJSEpoch);
+ }
+ docShell->GoForward(aRequireUserInteraction, aUserActivation);
+
+ if (BrowserChild* browserChild = BrowserChild::GetFrom(docShell)) {
+ browserChild->NotifyNavigationFinished();
+ }
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvGoToIndex(
+ const MaybeDiscarded<BrowsingContext>& aContext, const int32_t& aIndex,
+ const Maybe<int32_t>& aCancelContentJSEpoch, bool aUserActivation) {
+ if (aContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+ BrowsingContext* bc = aContext.get();
+
+ if (RefPtr<nsDocShell> docShell = nsDocShell::Cast(bc->GetDocShell())) {
+ if (aCancelContentJSEpoch) {
+ docShell->SetCancelContentJSEpoch(*aCancelContentJSEpoch);
+ }
+ docShell->GotoIndex(aIndex, aUserActivation);
+
+ if (BrowserChild* browserChild = BrowserChild::GetFrom(docShell)) {
+ browserChild->NotifyNavigationFinished();
+ }
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvReload(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const uint32_t aReloadFlags) {
+ if (aContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+ BrowsingContext* bc = aContext.get();
+
+ if (RefPtr<nsDocShell> docShell = nsDocShell::Cast(bc->GetDocShell())) {
+ docShell->Reload(aReloadFlags);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvStopLoad(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const uint32_t aStopFlags) {
+ if (aContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+ BrowsingContext* bc = aContext.get();
+
+ if (auto* docShell = nsDocShell::Cast(bc->GetDocShell())) {
+ docShell->Stop(aStopFlags);
+ }
+
+ return IPC_OK();
+}
+
+#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS)
+mozilla::ipc::IPCResult ContentChild::RecvInitSandboxTesting(
+ Endpoint<PSandboxTestingChild>&& aEndpoint) {
+ if (!SandboxTestingChild::Initialize(std::move(aEndpoint))) {
+ return IPC_FAIL(
+ this, "InitSandboxTesting failed to initialise the child process.");
+ }
+ return IPC_OK();
+}
+#endif
+
+NS_IMETHODIMP ContentChild::GetChildID(uint64_t* aOut) {
+ *aOut = mID;
+ return NS_OK;
+}
+
+NS_IMETHODIMP ContentChild::GetActor(const nsACString& aName, JSContext* aCx,
+ JSProcessActorChild** retval) {
+ ErrorResult error;
+ RefPtr<JSProcessActorChild> actor =
+ JSActorManager::GetActor(aCx, aName, error)
+ .downcast<JSProcessActorChild>();
+ if (error.MaybeSetPendingException(aCx)) {
+ return NS_ERROR_FAILURE;
+ }
+ actor.forget(retval);
+ return NS_OK;
+}
+
+NS_IMETHODIMP ContentChild::GetExistingActor(const nsACString& aName,
+ JSProcessActorChild** retval) {
+ RefPtr<JSProcessActorChild> actor =
+ JSActorManager::GetExistingActor(aName).downcast<JSProcessActorChild>();
+ actor.forget(retval);
+ return NS_OK;
+}
+
+already_AddRefed<JSActor> ContentChild::InitJSActor(
+ JS::Handle<JSObject*> aMaybeActor, const nsACString& aName,
+ ErrorResult& aRv) {
+ RefPtr<JSProcessActorChild> actor;
+ if (aMaybeActor.get()) {
+ aRv = UNWRAP_OBJECT(JSProcessActorChild, aMaybeActor.get(), actor);
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+ } else {
+ actor = new JSProcessActorChild();
+ }
+
+ MOZ_RELEASE_ASSERT(!actor->Manager(),
+ "mManager was already initialized once!");
+ actor->Init(aName, this);
+ return actor.forget();
+}
+
+IPCResult ContentChild::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 ContentChild::GetCanSend(bool* aCanSend) {
+ *aCanSend = CanSend();
+ return NS_OK;
+}
+
+ContentChild* ContentChild::AsContentChild() { return this; }
+
+JSActorManager* ContentChild::AsJSActorManager() { return this; }
+
+IPCResult ContentChild::RecvFlushFOGData(FlushFOGDataResolver&& aResolver) {
+ glean::FlushFOGData(std::move(aResolver));
+ return IPC_OK();
+}
+
+IPCResult ContentChild::RecvUpdateMediaCodecsSupported(
+ RemoteDecodeIn aLocation, const media::MediaCodecsSupported& aSupported) {
+ RemoteDecoderManagerChild::SetSupported(aLocation, aSupported);
+
+ return IPC_OK();
+}
+
+void ContentChild::ConfigureThreadPerformanceHints(
+ const hal::ProcessPriority& aPriority) {
+ if (aPriority >= hal::PROCESS_PRIORITY_FOREGROUND) {
+ static bool canUsePerformanceHintSession = true;
+ if (!mPerformanceHintSession && canUsePerformanceHintSession) {
+ nsTArray<PlatformThreadHandle> threads;
+ Servo_ThreadPool_GetThreadHandles(&threads);
+#ifdef XP_WIN
+ threads.AppendElement(GetCurrentThread());
+#else
+ threads.AppendElement(pthread_self());
+#endif
+
+ mPerformanceHintSession = hal::CreatePerformanceHintSession(
+ threads, GetPerformanceHintTarget(TimeDuration::FromMilliseconds(
+ nsRefreshDriver::DefaultInterval())));
+
+ // Avoid repeatedly attempting to create a session if it is not
+ // supported.
+ canUsePerformanceHintSession = mPerformanceHintSession != nullptr;
+ }
+
+#ifdef MOZ_WIDGET_ANDROID
+ // On Android if we are unable to use PerformanceHintManager then fall back
+ // to setting the stylo threads' affinities to the performant cores. Android
+ // automatically sets each thread's affinity to all cores when a process is
+ // foregrounded, and to a subset of cores when the process is backgrounded.
+ // We must therefore override this each time the process is foregrounded,
+ // but we don't have to do anything when backgrounded.
+ if (!mPerformanceHintSession) {
+ if (const auto& cpuInfo = hal::GetHeterogeneousCpuInfo()) {
+ // If CPUs are homogeneous there is no point setting affinity.
+ if (cpuInfo->mBigCpus.Count() != cpuInfo->mTotalNumCpus) {
+ BitSet<hal::HeterogeneousCpuInfo::MAX_CPUS> cpus = cpuInfo->mBigCpus;
+ if (cpus.Count() < 2) {
+ // From testing on a variety of devices it appears using only the
+ // big cores gives best performance when there are 2 or more big
+ // cores. If there are fewer than 2 big cores then additionally
+ // using the medium cores performs better.
+ cpus |= cpuInfo->mMediumCpus;
+ }
+
+ static_assert(
+ hal::HeterogeneousCpuInfo::MAX_CPUS <= CPU_SETSIZE,
+ "HeterogeneousCpuInfo::MAX_CPUS is too large for CPU_SETSIZE");
+
+ cpu_set_t cpuset;
+ CPU_ZERO(&cpuset);
+ for (size_t i = 0; i < cpuInfo->mTotalNumCpus; i++) {
+ if (cpus.Test(i)) {
+ CPU_SET(i, &cpuset);
+ }
+ }
+
+ // Only set the affinity for the stylo threads, not the main thread.
+ // Testing showed no difference in performance between the two
+ // options, and as newly spawned threads automatically inherit their
+ // parent's affinity mask there may be unintended consequences to
+ // setting the main thread affinity.
+ nsTArray<PlatformThreadHandle> threads;
+ Servo_ThreadPool_GetThreadHandles(&threads);
+ for (pthread_t thread : threads) {
+ int ret = sched_setaffinity(pthread_gettid_np(thread),
+ sizeof(cpu_set_t), &cpuset);
+ // Occasionally sched_setaffinity fails, presumably due to a race
+ // between us receiving the process foreground signal and whatever
+ // is responsible for restricting which processes can use certain
+ // cores. Trying again in a runnable seems to do the trick, but if
+ // that still fails it's not the end of the world.
+ if (ret < 0) {
+ NS_DispatchToCurrentThread(NS_NewRunnableFunction(
+ "ContentChild::ConfigureThreadPerformanceHints",
+ [threads = std::move(threads), cpuset] {
+ for (pthread_t thread : threads) {
+ sched_setaffinity(pthread_gettid_np(thread),
+ sizeof(cpu_set_t), &cpuset);
+ }
+ }));
+ break;
+ }
+ }
+ }
+ }
+ }
+#endif
+ } else {
+ mPerformanceHintSession = nullptr;
+ }
+}
+
+} // namespace dom
+
+#if defined(__OpenBSD__) && defined(MOZ_SANDBOX)
+
+static LazyLogModule sPledgeLog("OpenBSDSandbox");
+
+NS_IMETHODIMP
+OpenBSDFindPledgeUnveilFilePath(const char* file, nsACString& result) {
+ struct stat st;
+
+ // Allow overriding files in /etc/$MOZ_APP_NAME
+ result.Assign(nsPrintfCString("/etc/%s/%s", MOZ_APP_NAME, file));
+ if (stat(PromiseFlatCString(result).get(), &st) == 0) {
+ return NS_OK;
+ }
+
+ // Or look in the system default directory
+ result.Assign(nsPrintfCString(
+ "/usr/local/lib/%s/browser/defaults/preferences/%s", MOZ_APP_NAME, file));
+ if (stat(PromiseFlatCString(result).get(), &st) == 0) {
+ return NS_OK;
+ }
+
+ errx(1, "can't locate %s", file);
+}
+
+NS_IMETHODIMP
+OpenBSDPledgePromises(const nsACString& aPath) {
+ // Using NS_LOCAL_FILE_CONTRACTID/NS_LOCALFILEINPUTSTREAM_CONTRACTID requires
+ // a lot of setup before they are supported and we want to pledge early on
+ // before all of that, so read the file directly
+ std::ifstream input(PromiseFlatCString(aPath).get());
+
+ // Build up one line of pledge promises without comments
+ nsAutoCString promises;
+ bool disabled = false;
+ int linenum = 0;
+ for (std::string tLine; std::getline(input, tLine);) {
+ nsAutoCString line(tLine.c_str());
+ linenum++;
+
+ // Cut off any comments at the end of the line, also catches lines
+ // that are entirely a comment
+ int32_t hash = line.FindChar('#');
+ if (hash >= 0) {
+ line = Substring(line, 0, hash);
+ }
+ line.CompressWhitespace(true, true);
+ if (line.IsEmpty()) {
+ continue;
+ }
+
+ if (linenum == 1 && line.EqualsLiteral("disable")) {
+ disabled = true;
+ break;
+ }
+
+ if (!promises.IsEmpty()) {
+ promises.Append(" ");
+ }
+ promises.Append(line);
+ }
+ input.close();
+
+ if (disabled) {
+ warnx("%s: disabled", PromiseFlatCString(aPath).get());
+ } else {
+ MOZ_LOG(
+ sPledgeLog, LogLevel::Debug,
+ ("%s: pledge(%s)\n", PromiseFlatCString(aPath).get(), promises.get()));
+ if (pledge(promises.get(), nullptr) != 0) {
+ err(1, "%s: pledge(%s) failed", PromiseFlatCString(aPath).get(),
+ promises.get());
+ }
+ }
+
+ return NS_OK;
+}
+
+void ExpandUnveilPath(nsAutoCString& path) {
+ // Expand $XDG_RUNTIME_DIR to the environment variable, or ~/.cache
+ nsCString xdgRuntimeDir(PR_GetEnv("XDG_RUNTIME_DIR"));
+ if (xdgRuntimeDir.IsEmpty()) {
+ xdgRuntimeDir = "~/.cache";
+ }
+ path.ReplaceSubstring("$XDG_RUNTIME_DIR", xdgRuntimeDir.get());
+
+ // Expand $XDG_CONFIG_HOME to the environment variable, or ~/.config
+ nsCString xdgConfigHome(PR_GetEnv("XDG_CONFIG_HOME"));
+ if (xdgConfigHome.IsEmpty()) {
+ xdgConfigHome = "~/.config";
+ }
+ path.ReplaceSubstring("$XDG_CONFIG_HOME", xdgConfigHome.get());
+
+ // Expand $XDG_CACHE_HOME to the environment variable, or ~/.cache
+ nsCString xdgCacheHome(PR_GetEnv("XDG_CACHE_HOME"));
+ if (xdgCacheHome.IsEmpty()) {
+ xdgCacheHome = "~/.cache";
+ }
+ path.ReplaceSubstring("$XDG_CACHE_HOME", xdgCacheHome.get());
+
+ // Expand $XDG_DATA_HOME to the environment variable, or ~/.local/share
+ nsCString xdgDataHome(PR_GetEnv("XDG_DATA_HOME"));
+ if (xdgDataHome.IsEmpty()) {
+ xdgDataHome = "~/.local/share";
+ }
+ path.ReplaceSubstring("$XDG_DATA_HOME", xdgDataHome.get());
+
+ // Expand leading ~ to the user's home directory
+ nsCOMPtr<nsIFile> homeDir;
+ nsresult rv =
+ GetSpecialSystemDirectory(Unix_HomeDirectory, getter_AddRefs(homeDir));
+ if (NS_FAILED(rv)) {
+ errx(1, "failed getting home directory");
+ }
+ if (path.FindChar('~') == 0) {
+ nsCString tHome(homeDir->NativePath());
+ tHome.Append(Substring(path, 1, path.Length() - 1));
+ path = tHome.get();
+ }
+}
+
+void MkdirP(nsAutoCString& path) {
+ // nsLocalFile::CreateAllAncestors would be nice to use
+
+ nsAutoCString tPath("");
+ for (const nsACString& dir : path.Split('/')) {
+ struct stat st;
+
+ if (dir.IsEmpty()) {
+ continue;
+ }
+
+ tPath.Append("/");
+ tPath.Append(dir);
+
+ if (stat(tPath.get(), &st) == -1) {
+ if (mkdir(tPath.get(), 0700) == -1) {
+ err(1, "failed mkdir(%s) while MkdirP(%s)",
+ PromiseFlatCString(tPath).get(), PromiseFlatCString(path).get());
+ }
+ }
+ }
+}
+
+NS_IMETHODIMP
+OpenBSDUnveilPaths(const nsACString& uPath, const nsACString& pledgePath) {
+ // Using NS_LOCAL_FILE_CONTRACTID/NS_LOCALFILEINPUTSTREAM_CONTRACTID requires
+ // a lot of setup before they are allowed/supported and we want to pledge and
+ // unveil early on before all of that is setup
+ std::ifstream input(PromiseFlatCString(uPath).get());
+
+ bool disabled = false;
+ int linenum = 0;
+ for (std::string tLine; std::getline(input, tLine);) {
+ nsAutoCString line(tLine.c_str());
+ linenum++;
+
+ // Cut off any comments at the end of the line, also catches lines
+ // that are entirely a comment
+ int32_t hash = line.FindChar('#');
+ if (hash >= 0) {
+ line = Substring(line, 0, hash);
+ }
+ line.CompressWhitespace(true, true);
+ if (line.IsEmpty()) {
+ continue;
+ }
+
+ if (linenum == 1 && line.EqualsLiteral("disable")) {
+ disabled = true;
+ break;
+ }
+
+ int32_t space = line.FindChar(' ');
+ if (space <= 0) {
+ errx(1, "%s: line %d: invalid format", PromiseFlatCString(uPath).get(),
+ linenum);
+ }
+
+ nsAutoCString uPath(Substring(line, 0, space));
+ ExpandUnveilPath(uPath);
+
+ nsAutoCString perms(Substring(line, space + 1, line.Length() - space - 1));
+
+ MOZ_LOG(sPledgeLog, LogLevel::Debug,
+ ("%s: unveil(%s, %s)\n", PromiseFlatCString(uPath).get(),
+ uPath.get(), perms.get()));
+ if (unveil(uPath.get(), perms.get()) == -1 && errno != ENOENT) {
+ err(1, "%s: unveil(%s, %s) failed", PromiseFlatCString(uPath).get(),
+ uPath.get(), perms.get());
+ }
+ }
+ input.close();
+
+ if (disabled) {
+ warnx("%s: disabled", PromiseFlatCString(uPath).get());
+ } else {
+ struct stat st;
+
+ // Only unveil the pledgePath file if it's not already unveiled, otherwise
+ // some containing directory will lose visibility.
+ if (stat(PromiseFlatCString(pledgePath).get(), &st) == -1) {
+ if (errno == ENOENT) {
+ if (unveil(PromiseFlatCString(pledgePath).get(), "r") == -1) {
+ err(1, "unveil(%s, r) failed", PromiseFlatCString(pledgePath).get());
+ }
+ } else {
+ err(1, "stat(%s)", PromiseFlatCString(pledgePath).get());
+ }
+ }
+ }
+
+ return NS_OK;
+}
+
+bool StartOpenBSDSandbox(GeckoProcessType type, ipc::SandboxingKind kind) {
+ nsAutoCString pledgeFile;
+ nsAutoCString unveilFile;
+ char binaryPath[MAXPATHLEN];
+
+ switch (type) {
+ case GeckoProcessType_Default: {
+ OpenBSDFindPledgeUnveilFilePath("pledge.main", pledgeFile);
+ OpenBSDFindPledgeUnveilFilePath("unveil.main", unveilFile);
+
+ // Ensure dconf dir exists before we veil the filesystem
+ nsAutoCString dConf("$XDG_RUNTIME_DIR/dconf");
+ ExpandUnveilPath(dConf);
+ MkdirP(dConf);
+ break;
+ }
+
+ case GeckoProcessType_Content:
+ OpenBSDFindPledgeUnveilFilePath("pledge.content", pledgeFile);
+ OpenBSDFindPledgeUnveilFilePath("unveil.content", unveilFile);
+ break;
+
+ case GeckoProcessType_GPU:
+ OpenBSDFindPledgeUnveilFilePath("pledge.gpu", pledgeFile);
+ OpenBSDFindPledgeUnveilFilePath("unveil.gpu", unveilFile);
+ break;
+
+ case GeckoProcessType_Socket:
+ OpenBSDFindPledgeUnveilFilePath("pledge.socket", pledgeFile);
+ OpenBSDFindPledgeUnveilFilePath("unveil.socket", unveilFile);
+ break;
+
+ case GeckoProcessType_RDD:
+ OpenBSDFindPledgeUnveilFilePath("pledge.rdd", pledgeFile);
+ OpenBSDFindPledgeUnveilFilePath("unveil.rdd", unveilFile);
+ break;
+
+ case GeckoProcessType_Utility: {
+ MOZ_RELEASE_ASSERT(kind <= SandboxingKind::COUNT,
+ "Should define a sandbox");
+ switch (kind) {
+ case ipc::SandboxingKind::GENERIC_UTILITY:
+ default:
+ OpenBSDFindPledgeUnveilFilePath("pledge.utility", pledgeFile);
+ OpenBSDFindPledgeUnveilFilePath("unveil.utility", unveilFile);
+ break;
+ }
+ } break;
+
+ default:
+ MOZ_ASSERT(false, "unknown process type");
+ return false;
+ }
+
+ nsresult rv = mozilla::BinaryPath::Get(binaryPath);
+ if (NS_FAILED(rv)) {
+ errx(1, "failed to cache binary path ?");
+ }
+
+ if (NS_WARN_IF(NS_FAILED(OpenBSDUnveilPaths(unveilFile, pledgeFile)))) {
+ errx(1, "failed reading/parsing %s", unveilFile.get());
+ }
+
+ if (NS_WARN_IF(NS_FAILED(OpenBSDPledgePromises(pledgeFile)))) {
+ errx(1, "failed reading/parsing %s", pledgeFile.get());
+ }
+
+ // Don't overwrite an existing session dbus address, but ensure it is set
+ if (!PR_GetEnv("DBUS_SESSION_BUS_ADDRESS")) {
+ PR_SetEnv("DBUS_SESSION_BUS_ADDRESS=");
+ }
+
+ return true;
+}
+#endif
+
+} // namespace mozilla
+
+/* static */
+nsIDOMProcessChild* nsIDOMProcessChild::GetSingleton() {
+ if (XRE_IsContentProcess()) {
+ return mozilla::dom::ContentChild::GetSingleton();
+ }
+ return mozilla::dom::InProcessChild::Singleton();
+}
diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h
new file mode 100644
index 0000000000..4cd1e0771c
--- /dev/null
+++ b/dom/ipc/ContentChild.h
@@ -0,0 +1,911 @@
+/* -*- 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/. */
+
+#ifndef mozilla_dom_ContentChild_h
+#define mozilla_dom_ContentChild_h
+
+#include "mozilla/Atomics.h"
+#include "mozilla/dom/BlobImpl.h"
+#include "mozilla/dom/GetFilesHelper.h"
+#include "mozilla/dom/UserActivation.h"
+#include "mozilla/dom/PContentChild.h"
+#include "mozilla/dom/ProcessActor.h"
+#include "mozilla/dom/RemoteType.h"
+#include "mozilla/Hal.h"
+#include "mozilla/ipc/InputStreamUtils.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "nsClassHashtable.h"
+#include "nscore.h"
+#include "nsHashKeys.h"
+#include "nsIDOMProcessChild.h"
+#include "nsRefPtrHashtable.h"
+#include "nsString.h"
+#include "nsTArrayForwardDeclare.h"
+#include "nsTHashSet.h"
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+# include "nsIFile.h"
+#endif
+
+#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS)
+# include "mozilla/PSandboxTestingChild.h"
+#endif
+
+struct ChromePackage;
+class nsIObserver;
+struct SubstitutionMapping;
+struct OverrideMapping;
+class nsIDomainPolicy;
+class nsIURIClassifierCallback;
+class nsDocShellLoadState;
+class nsFrameLoader;
+class nsIOpenWindowInfo;
+
+namespace mozilla {
+class RemoteSpellcheckEngineChild;
+class ChildProfilerController;
+class BenchmarkStorageChild;
+
+namespace ipc {
+class UntypedEndpoint;
+}
+
+namespace loader {
+class PScriptCacheChild;
+}
+
+namespace widget {
+enum class ThemeChangeKind : uint8_t;
+}
+namespace dom {
+
+namespace ipc {
+class SharedMap;
+}
+
+class AlertObserver;
+class ConsoleListener;
+class ClonedMessageData;
+class BrowserChild;
+class TabContext;
+enum class CallerType : uint32_t;
+
+class ContentChild final : public PContentChild,
+ public nsIDOMProcessChild,
+ public mozilla::ipc::IShmemAllocator,
+ public ProcessActor {
+ using ClonedMessageData = mozilla::dom::ClonedMessageData;
+ using FileDescriptor = mozilla::ipc::FileDescriptor;
+
+ friend class PContentChild;
+
+ public:
+ NS_DECL_NSIDOMPROCESSCHILD
+
+ ContentChild();
+ virtual ~ContentChild();
+ NS_IMETHOD QueryInterface(REFNSIID aIID, void** aInstancePtr) override;
+ NS_IMETHOD_(MozExternalRefCountType) AddRef(void) override { return 1; }
+ NS_IMETHOD_(MozExternalRefCountType) Release(void) override { return 1; }
+
+ struct AppInfo {
+ nsCString version;
+ nsCString buildID;
+ nsCString name;
+ nsCString UAName;
+ nsCString ID;
+ nsCString vendor;
+ nsCString sourceURL;
+ nsCString updateURL;
+ };
+
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult ProvideWindowCommon(
+ NotNull<BrowserChild*> aTabOpener, nsIOpenWindowInfo* aOpenWindowInfo,
+ uint32_t aChromeFlags, bool aCalledFromJS, nsIURI* aURI,
+ const nsAString& aName, const nsACString& aFeatures,
+ const UserActivation::Modifiers& aModifiers, bool aForceNoOpener,
+ bool aForceNoReferrer, bool aIsPopupRequested,
+ nsDocShellLoadState* aLoadState, bool* aWindowIsNew,
+ BrowsingContext** aReturn);
+
+ void Init(mozilla::ipc::UntypedEndpoint&& aEndpoint,
+ const char* aParentBuildID, uint64_t aChildID, bool aIsForBrowser);
+
+ void InitXPCOM(XPCOMInitData&& aXPCOMInit,
+ const mozilla::dom::ipc::StructuredCloneData& aInitialData,
+ bool aIsReadyForBackgroundProcessing);
+
+ void InitSharedUASheets(Maybe<base::SharedMemoryHandle>&& aHandle,
+ uintptr_t aAddress);
+
+ void InitGraphicsDeviceData(const ContentDeviceData& aData);
+
+ static ContentChild* GetSingleton() { return sSingleton; }
+
+ const AppInfo& GetAppInfo() { return mAppInfo; }
+
+ void SetProcessName(const nsACString& aName,
+ const nsACString* aETLDplus1 = nullptr,
+ const nsACString* aCurrentProfile = nullptr);
+
+ void GetProcessName(nsACString& aName) const;
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+ void GetProfileDir(nsIFile** aProfileDir) const {
+ *aProfileDir = mProfileDir;
+ NS_IF_ADDREF(*aProfileDir);
+ }
+
+ void SetProfileDir(nsIFile* aProfileDir) { mProfileDir = aProfileDir; }
+#endif
+
+ bool IsAlive() const;
+
+ bool IsShuttingDown() const;
+
+ ipc::SharedMap* SharedData() { return mSharedData; };
+
+ static void AppendProcessId(nsACString& aName);
+
+ static RefPtr<GenericPromise> UpdateCookieStatus(nsIChannel* aChannel);
+
+ mozilla::ipc::IPCResult RecvInitGMPService(
+ Endpoint<PGMPServiceChild>&& aGMPService);
+
+ mozilla::ipc::IPCResult RecvInitProfiler(
+ Endpoint<PProfilerChild>&& aEndpoint);
+
+ mozilla::ipc::IPCResult RecvGMPsChanged(
+ nsTArray<GMPCapabilityData>&& capabilities);
+
+ mozilla::ipc::IPCResult RecvInitProcessHangMonitor(
+ Endpoint<PProcessHangMonitorChild>&& aHangMonitor);
+
+ mozilla::ipc::IPCResult RecvInitRendering(
+ Endpoint<PCompositorManagerChild>&& aCompositor,
+ Endpoint<PImageBridgeChild>&& aImageBridge,
+ Endpoint<PVRManagerChild>&& aVRBridge,
+ Endpoint<PRemoteDecoderManagerChild>&& aVideoManager,
+ nsTArray<uint32_t>&& namespaces);
+
+ mozilla::ipc::IPCResult RecvReinitRendering(
+ Endpoint<PCompositorManagerChild>&& aCompositor,
+ Endpoint<PImageBridgeChild>&& aImageBridge,
+ Endpoint<PVRManagerChild>&& aVRBridge,
+ Endpoint<PRemoteDecoderManagerChild>&& aVideoManager,
+ nsTArray<uint32_t>&& namespaces);
+
+ mozilla::ipc::IPCResult RecvReinitRenderingForDeviceReset();
+
+ mozilla::ipc::IPCResult RecvSetProcessSandbox(
+ const Maybe<FileDescriptor>& aBroker);
+
+ PHalChild* AllocPHalChild();
+ bool DeallocPHalChild(PHalChild*);
+
+ PHeapSnapshotTempFileHelperChild* AllocPHeapSnapshotTempFileHelperChild();
+
+ bool DeallocPHeapSnapshotTempFileHelperChild(
+ PHeapSnapshotTempFileHelperChild*);
+
+ PCycleCollectWithLogsChild* AllocPCycleCollectWithLogsChild(
+ const bool& aDumpAllTraces, const FileDescriptor& aGCLog,
+ const FileDescriptor& aCCLog);
+
+ bool DeallocPCycleCollectWithLogsChild(PCycleCollectWithLogsChild* aActor);
+
+ virtual mozilla::ipc::IPCResult RecvPCycleCollectWithLogsConstructor(
+ PCycleCollectWithLogsChild* aChild, const bool& aDumpAllTraces,
+ const FileDescriptor& aGCLog, const FileDescriptor& aCCLog) override;
+
+ already_AddRefed<PWebBrowserPersistDocumentChild>
+ AllocPWebBrowserPersistDocumentChild(
+ PBrowserChild* aBrowser, const MaybeDiscarded<BrowsingContext>& aContext);
+
+ virtual mozilla::ipc::IPCResult RecvPWebBrowserPersistDocumentConstructor(
+ PWebBrowserPersistDocumentChild* aActor, PBrowserChild* aBrowser,
+ const MaybeDiscarded<BrowsingContext>& aContext) override;
+
+ already_AddRefed<PTestShellChild> AllocPTestShellChild();
+
+ virtual mozilla::ipc::IPCResult RecvPTestShellConstructor(
+ PTestShellChild*) override;
+
+ PScriptCacheChild* AllocPScriptCacheChild(const FileDescOrError& cacheFile,
+ const bool& wantCacheData);
+
+ bool DeallocPScriptCacheChild(PScriptCacheChild*);
+
+ virtual mozilla::ipc::IPCResult RecvPScriptCacheConstructor(
+ PScriptCacheChild*, const FileDescOrError& cacheFile,
+ const bool& wantCacheData) override;
+
+ PRemotePrintJobChild* AllocPRemotePrintJobChild();
+
+ already_AddRefed<PClipboardReadRequestChild> AllocPClipboardReadRequestChild(
+ const nsTArray<nsCString>& aTypes);
+
+ PMediaChild* AllocPMediaChild();
+
+ bool DeallocPMediaChild(PMediaChild* aActor);
+
+ PBenchmarkStorageChild* AllocPBenchmarkStorageChild();
+
+ bool DeallocPBenchmarkStorageChild(PBenchmarkStorageChild* aActor);
+
+ mozilla::ipc::IPCResult RecvNotifyEmptyHTTPCache();
+
+ mozilla::ipc::IPCResult RecvRegisterChrome(
+ nsTArray<ChromePackage>&& packages,
+ nsTArray<SubstitutionMapping>&& resources,
+ nsTArray<OverrideMapping>&& overrides, const nsCString& locale,
+ const bool& reset);
+ mozilla::ipc::IPCResult RecvRegisterChromeItem(
+ const ChromeRegistryItem& item);
+
+ mozilla::ipc::IPCResult RecvClearStyleSheetCache(
+ const Maybe<RefPtr<nsIPrincipal>>& aForPrincipal,
+ const Maybe<nsCString>& aBaseDomain);
+
+ mozilla::ipc::IPCResult RecvClearImageCacheFromPrincipal(
+ nsIPrincipal* aPrincipal);
+ mozilla::ipc::IPCResult RecvClearImageCacheFromBaseDomain(
+ const nsCString& aBaseDomain);
+ mozilla::ipc::IPCResult RecvClearImageCache(const bool& privateLoader,
+ const bool& chrome);
+
+ PRemoteSpellcheckEngineChild* AllocPRemoteSpellcheckEngineChild();
+
+ bool DeallocPRemoteSpellcheckEngineChild(PRemoteSpellcheckEngineChild*);
+
+ mozilla::ipc::IPCResult RecvSetOffline(const bool& offline);
+
+ mozilla::ipc::IPCResult RecvSetConnectivity(const bool& connectivity);
+ mozilla::ipc::IPCResult RecvSetCaptivePortalState(const int32_t& state);
+ mozilla::ipc::IPCResult RecvSetTRRMode(
+ const nsIDNSService::ResolverMode& mode,
+ const nsIDNSService::ResolverMode& modeFromPref);
+
+ mozilla::ipc::IPCResult RecvBidiKeyboardNotify(const bool& isLangRTL,
+ const bool& haveBidiKeyboards);
+
+ mozilla::ipc::IPCResult RecvNotifyVisited(nsTArray<VisitedQueryResult>&&);
+
+ mozilla::ipc::IPCResult RecvThemeChanged(FullLookAndFeel&&,
+ widget::ThemeChangeKind);
+
+ // auto remove when alertfinished is received.
+ nsresult AddRemoteAlertObserver(const nsString& aData,
+ nsIObserver* aObserver);
+
+ mozilla::ipc::IPCResult RecvPreferenceUpdate(const Pref& aPref);
+ mozilla::ipc::IPCResult RecvVarUpdate(const GfxVarUpdate& pref);
+
+ mozilla::ipc::IPCResult RecvUpdatePerfStatsCollectionMask(
+ const uint64_t& aMask);
+
+ mozilla::ipc::IPCResult RecvCollectPerfStatsJSON(
+ CollectPerfStatsJSONResolver&& aResolver);
+
+ mozilla::ipc::IPCResult RecvCollectScrollingMetrics(
+ CollectScrollingMetricsResolver&& aResolver);
+
+ mozilla::ipc::IPCResult RecvNotifyAlertsObserver(const nsCString& aType,
+ const nsString& aData);
+
+ mozilla::ipc::IPCResult RecvLoadProcessScript(const nsString& aURL);
+
+ mozilla::ipc::IPCResult RecvAsyncMessage(const nsString& aMsg,
+ const ClonedMessageData& aData);
+
+ mozilla::ipc::IPCResult RecvRegisterStringBundles(
+ nsTArray<StringBundleDescriptor>&& stringBundles);
+
+ mozilla::ipc::IPCResult RecvUpdateL10nFileSources(
+ nsTArray<L10nFileSourceDescriptor>&& aDescriptors);
+
+ mozilla::ipc::IPCResult RecvUpdateSharedData(
+ const FileDescriptor& aMapFile, const uint32_t& aMapSize,
+ nsTArray<IPCBlob>&& aBlobs, nsTArray<nsCString>&& aChangedKeys);
+
+ mozilla::ipc::IPCResult RecvFontListChanged();
+ mozilla::ipc::IPCResult RecvForceGlobalReflow(bool aNeedsReframe);
+
+ mozilla::ipc::IPCResult RecvGeolocationUpdate(nsIDOMGeoPosition* aPosition);
+
+ // MOZ_CAN_RUN_SCRIPT_BOUNDARY because we don't have MOZ_CAN_RUN_SCRIPT bits
+ // in IPC code yet.
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ mozilla::ipc::IPCResult RecvGeolocationError(const uint16_t& errorCode);
+
+ mozilla::ipc::IPCResult RecvUpdateDictionaryList(
+ nsTArray<nsCString>&& aDictionaries);
+
+ mozilla::ipc::IPCResult RecvUpdateFontList(SystemFontList&&);
+ mozilla::ipc::IPCResult RecvRebuildFontList(const bool& aFullRebuild);
+ mozilla::ipc::IPCResult RecvFontListShmBlockAdded(
+ const uint32_t& aGeneration, const uint32_t& aIndex,
+ base::SharedMemoryHandle&& aHandle);
+
+ mozilla::ipc::IPCResult RecvUpdateAppLocales(
+ nsTArray<nsCString>&& aAppLocales);
+ mozilla::ipc::IPCResult RecvUpdateRequestedLocales(
+ nsTArray<nsCString>&& aRequestedLocales);
+
+ mozilla::ipc::IPCResult RecvSystemTimezoneChanged();
+
+ mozilla::ipc::IPCResult RecvAddPermission(const IPC::Permission& permission);
+
+ mozilla::ipc::IPCResult RecvRemoveAllPermissions();
+
+ mozilla::ipc::IPCResult RecvFlushMemory(const nsString& reason);
+
+ mozilla::ipc::IPCResult RecvActivateA11y();
+ mozilla::ipc::IPCResult RecvShutdownA11y();
+
+ mozilla::ipc::IPCResult RecvApplicationForeground();
+ mozilla::ipc::IPCResult RecvApplicationBackground();
+ mozilla::ipc::IPCResult RecvGarbageCollect();
+ mozilla::ipc::IPCResult RecvCycleCollect();
+ mozilla::ipc::IPCResult RecvUnlinkGhosts();
+
+ mozilla::ipc::IPCResult RecvAppInfo(
+ const nsCString& version, const nsCString& buildID, const nsCString& name,
+ const nsCString& UAName, const nsCString& ID, const nsCString& vendor,
+ const nsCString& sourceURL, const nsCString& updateURL);
+
+ mozilla::ipc::IPCResult RecvRemoteType(const nsCString& aRemoteType,
+ const nsCString& aProfile);
+
+ void PreallocInit();
+
+ // Call RemoteTypePrefix() on the result to remove URIs if you want to use
+ // this for telemetry.
+ const nsACString& GetRemoteType() const override;
+
+ mozilla::ipc::IPCResult RecvInitBlobURLs(
+ nsTArray<BlobURLRegistrationData>&& aRegistations);
+
+ mozilla::ipc::IPCResult RecvInitJSActorInfos(
+ nsTArray<JSProcessActorInfo>&& aContentInfos,
+ nsTArray<JSWindowActorInfo>&& aWindowInfos);
+
+ mozilla::ipc::IPCResult RecvUnregisterJSWindowActor(const nsCString& aName);
+
+ mozilla::ipc::IPCResult RecvUnregisterJSProcessActor(const nsCString& aName);
+
+ mozilla::ipc::IPCResult RecvLastPrivateDocShellDestroyed();
+
+ mozilla::ipc::IPCResult RecvNotifyProcessPriorityChanged(
+ const hal::ProcessPriority& aPriority);
+
+ mozilla::ipc::IPCResult RecvMinimizeMemoryUsage();
+
+ mozilla::ipc::IPCResult RecvLoadAndRegisterSheet(nsIURI* aURI,
+ const uint32_t& aType);
+
+ mozilla::ipc::IPCResult RecvUnregisterSheet(nsIURI* aURI,
+ const uint32_t& aType);
+
+ void AddIdleObserver(nsIObserver* aObserver, uint32_t aIdleTimeInS);
+
+ void RemoveIdleObserver(nsIObserver* aObserver, uint32_t aIdleTimeInS);
+
+ mozilla::ipc::IPCResult RecvNotifyIdleObserver(const uint64_t& aObserver,
+ const nsCString& aTopic,
+ const nsString& aData);
+
+ mozilla::ipc::IPCResult RecvUpdateWindow(const uintptr_t& aChildId);
+
+ mozilla::ipc::IPCResult RecvDomainSetChanged(const uint32_t& aSetType,
+ const uint32_t& aChangeType,
+ nsIURI* aDomain);
+
+ mozilla::ipc::IPCResult RecvShutdownConfirmedHP();
+
+ mozilla::ipc::IPCResult RecvShutdown();
+
+ mozilla::ipc::IPCResult RecvInvokeDragSession(
+ const MaybeDiscarded<WindowContext>& aSourceWindowContext,
+ const MaybeDiscarded<WindowContext>& aSourceTopWindowContext,
+ nsTArray<IPCTransferableData>&& aTransferables, const uint32_t& aAction);
+
+ mozilla::ipc::IPCResult RecvUpdateDragSession(
+ nsTArray<IPCTransferableData>&& aTransferables,
+ EventMessage aEventMessage);
+
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ mozilla::ipc::IPCResult RecvEndDragSession(
+ const bool& aDoneDrag, const bool& aUserCancelled,
+ const mozilla::LayoutDeviceIntPoint& aEndDragPoint,
+ const uint32_t& aKeyModifiers, const uint32_t& aDropEffect);
+
+ mozilla::ipc::IPCResult RecvPush(const nsCString& aScope,
+ nsIPrincipal* aPrincipal,
+ const nsString& aMessageId);
+
+ mozilla::ipc::IPCResult RecvPushWithData(const nsCString& aScope,
+ nsIPrincipal* aPrincipal,
+ const nsString& aMessageId,
+ nsTArray<uint8_t>&& aData);
+
+ mozilla::ipc::IPCResult RecvPushSubscriptionChange(const nsCString& aScope,
+ nsIPrincipal* aPrincipal);
+
+ mozilla::ipc::IPCResult RecvPushError(const nsCString& aScope,
+ nsIPrincipal* aPrincipal,
+ const nsString& aMessage,
+ const uint32_t& aFlags);
+
+ mozilla::ipc::IPCResult RecvNotifyPushSubscriptionModifiedObservers(
+ const nsCString& aScope, nsIPrincipal* aPrincipal);
+
+ mozilla::ipc::IPCResult RecvRefreshScreens(
+ nsTArray<ScreenDetails>&& aScreens);
+
+ mozilla::ipc::IPCResult RecvNetworkLinkTypeChange(const uint32_t& aType);
+ uint32_t NetworkLinkType() const { return mNetworkLinkType; }
+
+ mozilla::ipc::IPCResult RecvSocketProcessCrashed();
+
+ // Get the directory for IndexedDB files. We query the parent for this and
+ // cache the value
+ nsString& GetIndexedDBPath();
+
+ ContentParentId GetID() const { return mID; }
+
+ bool IsForBrowser() const { return mIsForBrowser; }
+
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY mozilla::ipc::IPCResult RecvConstructBrowser(
+ ManagedEndpoint<PBrowserChild>&& aBrowserEp,
+ ManagedEndpoint<PWindowGlobalChild>&& aWindowEp, const TabId& aTabId,
+ const IPCTabContext& aContext, const WindowGlobalInit& aWindowInit,
+ const uint32_t& aChromeFlags, const ContentParentId& aCpID,
+ const bool& aIsForBrowser, const bool& aIsTopLevel);
+
+ FORWARD_SHMEM_ALLOCATOR_TO(PContentChild)
+
+ void GetAvailableDictionaries(nsTArray<nsCString>& aDictionaries);
+
+#ifdef MOZ_WEBRTC
+ PWebrtcGlobalChild* AllocPWebrtcGlobalChild();
+ bool DeallocPWebrtcGlobalChild(PWebrtcGlobalChild* aActor);
+#endif
+
+ PContentPermissionRequestChild* AllocPContentPermissionRequestChild(
+ Span<const PermissionRequest> aRequests, nsIPrincipal* aPrincipal,
+ nsIPrincipal* aTopLevelPrincipal, const bool& aIsHandlingUserInput,
+ const bool& aMaybeUnsafePermissionDelegate, const TabId& aTabId);
+ bool DeallocPContentPermissionRequestChild(
+ PContentPermissionRequestChild* actor);
+
+ // GetFiles for WebKit/Blink FileSystem API and Directory API must run on the
+ // parent process.
+ void CreateGetFilesRequest(const nsAString& aDirectoryPath,
+ bool aRecursiveFlag, nsID& aUUID,
+ GetFilesHelperChild* aChild);
+
+ void DeleteGetFilesRequest(nsID& aUUID, GetFilesHelperChild* aChild);
+
+ mozilla::ipc::IPCResult RecvGetFilesResponse(
+ const nsID& aUUID, const GetFilesResponseResult& aResult);
+
+ mozilla::ipc::IPCResult RecvBlobURLRegistration(
+ const nsCString& aURI, const IPCBlob& aBlob, nsIPrincipal* aPrincipal,
+ const nsCString& aPartitionKey);
+
+ mozilla::ipc::IPCResult RecvBlobURLUnregistration(const nsCString& aURI);
+
+ mozilla::ipc::IPCResult RecvRequestMemoryReport(
+ const uint32_t& generation, const bool& anonymize,
+ const bool& minimizeMemoryUsage, const Maybe<FileDescriptor>& DMDFile,
+ const RequestMemoryReportResolver& aResolver);
+
+#if defined(XP_WIN)
+ mozilla::ipc::IPCResult RecvGetUntrustedModulesData(
+ GetUntrustedModulesDataResolver&& aResolver);
+ mozilla::ipc::IPCResult RecvUnblockUntrustedModulesThread();
+#endif // defined(XP_WIN)
+
+ mozilla::ipc::IPCResult RecvSetXPCOMProcessAttributes(
+ XPCOMInitData&& aXPCOMInit, const StructuredCloneData& aInitialData,
+ FullLookAndFeel&& aLookAndFeelData, SystemFontList&& aFontList,
+ Maybe<base::SharedMemoryHandle>&& aSharedUASheetHandle,
+ const uintptr_t& aSharedUASheetAddress,
+ nsTArray<base::SharedMemoryHandle>&& aSharedFontListBlocks,
+ const bool& aIsReadyForBackgroundProcessing);
+
+ mozilla::ipc::IPCResult RecvProvideAnonymousTemporaryFile(
+ const uint64_t& aID, const FileDescOrError& aFD);
+
+ mozilla::ipc::IPCResult RecvSetPermissionsWithKey(
+ const nsCString& aPermissionKey, nsTArray<IPC::Permission>&& aPerms);
+
+ mozilla::ipc::IPCResult RecvShareCodeCoverageMutex(
+ CrossProcessMutexHandle aHandle);
+
+ mozilla::ipc::IPCResult RecvFlushCodeCoverageCounters(
+ FlushCodeCoverageCountersResolver&& aResolver);
+
+ mozilla::ipc::IPCResult RecvSetInputEventQueueEnabled();
+
+ mozilla::ipc::IPCResult RecvFlushInputEventQueue();
+
+ mozilla::ipc::IPCResult RecvSuspendInputEventQueue();
+
+ mozilla::ipc::IPCResult RecvResumeInputEventQueue();
+
+ mozilla::ipc::IPCResult RecvAddDynamicScalars(
+ nsTArray<DynamicScalarDefinition>&& aDefs);
+
+ // Get a reference to the font list passed from the chrome process,
+ // for use during gfx initialization.
+ SystemFontList& SystemFontList() { return mFontList; }
+
+ nsTArray<base::SharedMemoryHandle>& SharedFontListBlocks() {
+ return mSharedFontListBlocks;
+ }
+
+ // PURLClassifierChild
+ PURLClassifierChild* AllocPURLClassifierChild(nsIPrincipal* aPrincipal,
+ bool* aSuccess);
+ bool DeallocPURLClassifierChild(PURLClassifierChild* aActor);
+
+ // PURLClassifierLocalChild
+ PURLClassifierLocalChild* AllocPURLClassifierLocalChild(
+ nsIURI* aUri, Span<const IPCURLClassifierFeature> aFeatures);
+ bool DeallocPURLClassifierLocalChild(PURLClassifierLocalChild* aActor);
+
+ PSessionStorageObserverChild* AllocPSessionStorageObserverChild();
+
+ bool DeallocPSessionStorageObserverChild(
+ PSessionStorageObserverChild* aActor);
+
+ FullLookAndFeel& BorrowLookAndFeelData() { return mLookAndFeelData; }
+
+ /**
+ * Helper function for protocols that use the GPU process when available.
+ * Overrides FatalError to just be a warning when communicating with the
+ * GPU process since we don't want to crash the content process when the
+ * GPU process crashes.
+ */
+ static void FatalErrorIfNotUsingGPUProcess(const char* const aErrorMsg,
+ base::ProcessId aOtherPid);
+
+ using AnonymousTemporaryFileCallback = std::function<void(PRFileDesc*)>;
+ nsresult AsyncOpenAnonymousTemporaryFile(
+ const AnonymousTemporaryFileCallback& aCallback);
+
+ mozilla::ipc::IPCResult RecvSaveRecording(const FileDescriptor& aFile);
+
+ mozilla::ipc::IPCResult RecvCrossProcessRedirect(
+ RedirectToRealChannelArgs&& aArgs,
+ nsTArray<Endpoint<extensions::PStreamFilterParent>>&& aEndpoints,
+ CrossProcessRedirectResolver&& aResolve);
+
+ mozilla::ipc::IPCResult RecvStartDelayedAutoplayMediaComponents(
+ const MaybeDiscarded<BrowsingContext>& aContext);
+
+ mozilla::ipc::IPCResult RecvUpdateMediaControlAction(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const MediaControlAction& aAction);
+
+ // See `BrowsingContext::mEpochs` for an explanation of this field.
+ uint64_t GetBrowsingContextFieldEpoch() const {
+ return mBrowsingContextFieldEpoch;
+ }
+ uint64_t NextBrowsingContextFieldEpoch() {
+ mBrowsingContextFieldEpoch++;
+ return mBrowsingContextFieldEpoch;
+ }
+
+ mozilla::ipc::IPCResult RecvOnAllowAccessFor(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const nsCString& aTrackingOrigin, uint32_t aCookieBehavior,
+ const ContentBlockingNotifier::StorageAccessPermissionGrantedReason&
+ aReason);
+
+ mozilla::ipc::IPCResult RecvOnContentBlockingDecision(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const ContentBlockingNotifier::BlockingDecision& aDecision,
+ uint32_t aRejectedReason);
+
+#ifdef NIGHTLY_BUILD
+ // Fetch the current number of pending input events.
+ //
+ // NOTE: This method performs an atomic read, and is safe to call from all
+ // threads.
+ uint32_t GetPendingInputEvents() { return mPendingInputEvents; }
+#endif
+
+#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS)
+ mozilla::ipc::IPCResult RecvInitSandboxTesting(
+ Endpoint<PSandboxTestingChild>&& aEndpoint);
+#endif
+
+ private:
+ static void ForceKillTimerCallback(nsITimer* aTimer, void* aClosure);
+ void StartForceKillTimer();
+
+ void ShutdownInternal();
+
+ mozilla::ipc::IPCResult GetResultForRenderingInitFailure(
+ base::ProcessId aOtherPid);
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ virtual void ProcessingError(Result aCode, const char* aReason) override;
+
+ mozilla::ipc::IPCResult RecvCreateBrowsingContext(
+ uint64_t aGroupId, BrowsingContext::IPCInitializer&& aInit);
+
+ mozilla::ipc::IPCResult RecvDiscardBrowsingContext(
+ const MaybeDiscarded<BrowsingContext>& aContext, bool aDoDiscard,
+ DiscardBrowsingContextResolver&& aResolve);
+
+ mozilla::ipc::IPCResult RecvRegisterBrowsingContextGroup(
+ uint64_t aGroupId, nsTArray<SyncedContextInitializer>&& aInits);
+ mozilla::ipc::IPCResult RecvDestroyBrowsingContextGroup(uint64_t aGroupId);
+
+ mozilla::ipc::IPCResult RecvWindowClose(
+ const MaybeDiscarded<BrowsingContext>& aContext, bool aTrustedCaller);
+ mozilla::ipc::IPCResult RecvWindowFocus(
+ const MaybeDiscarded<BrowsingContext>& aContext, CallerType aCallerType,
+ uint64_t aActionId);
+ mozilla::ipc::IPCResult RecvWindowBlur(
+ const MaybeDiscarded<BrowsingContext>& aContext, CallerType aCallerType);
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY mozilla::ipc::IPCResult RecvRaiseWindow(
+ const MaybeDiscarded<BrowsingContext>& aContext, CallerType aCallerType,
+ uint64_t aActionId);
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY mozilla::ipc::IPCResult RecvAdjustWindowFocus(
+ const MaybeDiscarded<BrowsingContext>& aContext, bool aIsVisible,
+ uint64_t aActionId);
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY mozilla::ipc::IPCResult RecvClearFocus(
+ const MaybeDiscarded<BrowsingContext>& aContext);
+ mozilla::ipc::IPCResult RecvSetFocusedBrowsingContext(
+ const MaybeDiscarded<BrowsingContext>& aContext, uint64_t aActionId);
+ mozilla::ipc::IPCResult RecvSetActiveBrowsingContext(
+ const MaybeDiscarded<BrowsingContext>& aContext, uint64_t aActionId);
+ mozilla::ipc::IPCResult RecvAbortOrientationPendingPromises(
+ const MaybeDiscarded<BrowsingContext>& aContext);
+ mozilla::ipc::IPCResult RecvUnsetActiveBrowsingContext(
+ const MaybeDiscarded<BrowsingContext>& aContext, uint64_t aActionId);
+ mozilla::ipc::IPCResult RecvSetFocusedElement(
+ const MaybeDiscarded<BrowsingContext>& aContext, bool aNeedsFocus);
+ mozilla::ipc::IPCResult RecvFinalizeFocusOuter(
+ const MaybeDiscarded<BrowsingContext>& aContext, bool aCanFocus,
+ CallerType aCallerType);
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY mozilla::ipc::IPCResult RecvBlurToChild(
+ const MaybeDiscarded<BrowsingContext>& aFocusedBrowsingContext,
+ const MaybeDiscarded<BrowsingContext>& aBrowsingContextToClear,
+ const MaybeDiscarded<BrowsingContext>& aAncestorBrowsingContextToFocus,
+ bool aIsLeavingDocument, bool aAdjustWidget, uint64_t aActionId);
+ mozilla::ipc::IPCResult RecvSetupFocusedAndActive(
+ const MaybeDiscarded<BrowsingContext>& aFocusedBrowsingContext,
+ uint64_t aActionIdForFocused,
+ const MaybeDiscarded<BrowsingContext>& aActiveBrowsingContext,
+ uint64_t aActionIdForActive);
+ mozilla::ipc::IPCResult RecvReviseActiveBrowsingContext(
+ uint64_t aOldActionId,
+ const MaybeDiscarded<BrowsingContext>& aActiveBrowsingContext,
+ uint64_t aNewActionId);
+ mozilla::ipc::IPCResult RecvReviseFocusedBrowsingContext(
+ uint64_t aOldActionId,
+ const MaybeDiscarded<BrowsingContext>& aFocusedBrowsingContext,
+ uint64_t aNewActionId);
+ mozilla::ipc::IPCResult RecvMaybeExitFullscreen(
+ const MaybeDiscarded<BrowsingContext>& aContext);
+
+ mozilla::ipc::IPCResult RecvWindowPostMessage(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const ClonedOrErrorMessageData& aMessage, const PostMessageData& aData);
+
+ mozilla::ipc::IPCResult RecvCommitBrowsingContextTransaction(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ BrowsingContext::BaseTransaction&& aTransaction, uint64_t aEpoch);
+
+ mozilla::ipc::IPCResult RecvCommitWindowContextTransaction(
+ const MaybeDiscarded<WindowContext>& aContext,
+ WindowContext::BaseTransaction&& aTransaction, uint64_t aEpoch);
+
+ mozilla::ipc::IPCResult RecvCreateWindowContext(
+ WindowContext::IPCInitializer&& aInit);
+ mozilla::ipc::IPCResult RecvDiscardWindowContext(
+ uint64_t aContextId, DiscardWindowContextResolver&& aResolve);
+
+ mozilla::ipc::IPCResult RecvScriptError(
+ const nsString& aMessage, const nsString& aSourceName,
+ const nsString& aSourceLine, const uint32_t& aLineNumber,
+ const uint32_t& aColNumber, const uint32_t& aFlags,
+ const nsCString& aCategory, const bool& aFromPrivateWindow,
+ const uint64_t& aInnerWindowId, const bool& aFromChromeContext);
+
+ mozilla::ipc::IPCResult RecvReportFrameTimingData(
+ const LoadInfoArgs& loadInfoArgs, const nsString& entryName,
+ const nsString& initiatorType, UniquePtr<PerformanceTimingData>&& aData);
+
+ mozilla::ipc::IPCResult RecvLoadURI(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ nsDocShellLoadState* aLoadState, bool aSetNavigating,
+ LoadURIResolver&& aResolve);
+
+ mozilla::ipc::IPCResult RecvInternalLoad(nsDocShellLoadState* aLoadState);
+
+ mozilla::ipc::IPCResult RecvDisplayLoadError(
+ const MaybeDiscarded<BrowsingContext>& aContext, const nsAString& aURI);
+
+ mozilla::ipc::IPCResult RecvGoBack(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const Maybe<int32_t>& aCancelContentJSEpoch, bool aRequireUserInteraction,
+ bool aUserActivation);
+ mozilla::ipc::IPCResult RecvGoForward(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const Maybe<int32_t>& aCancelContentJSEpoch, bool aRequireUserInteraction,
+ bool aUserActivation);
+ mozilla::ipc::IPCResult RecvGoToIndex(
+ const MaybeDiscarded<BrowsingContext>& aContext, const int32_t& aIndex,
+ const Maybe<int32_t>& aCancelContentJSEpoch, bool aUserActivation);
+ mozilla::ipc::IPCResult RecvReload(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const uint32_t aReloadFlags);
+ mozilla::ipc::IPCResult RecvStopLoad(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const uint32_t aStopFlags);
+
+ mozilla::ipc::IPCResult RecvRawMessage(
+ const JSActorMessageMeta& aMeta, const Maybe<ClonedMessageData>& aData,
+ const Maybe<ClonedMessageData>& aStack);
+
+ already_AddRefed<JSActor> InitJSActor(JS::Handle<JSObject*> aMaybeActor,
+ const nsACString& aName,
+ ErrorResult& aRv) override;
+ mozilla::ipc::IProtocol* AsNativeActor() override { return this; }
+
+ mozilla::ipc::IPCResult RecvHistoryCommitIndexAndLength(
+ const MaybeDiscarded<BrowsingContext>& aContext, const uint32_t& aIndex,
+ const uint32_t& aLength, const nsID& aChangeID);
+
+ mozilla::ipc::IPCResult RecvGetLayoutHistoryState(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ GetLayoutHistoryStateResolver&& aResolver);
+
+ mozilla::ipc::IPCResult RecvDispatchLocationChangeEvent(
+ const MaybeDiscarded<BrowsingContext>& aContext);
+
+ mozilla::ipc::IPCResult RecvDispatchBeforeUnloadToSubtree(
+ const MaybeDiscarded<BrowsingContext>& aStartingAt,
+ DispatchBeforeUnloadToSubtreeResolver&& aResolver);
+
+ mozilla::ipc::IPCResult RecvDecoderSupportedMimeTypes(
+ nsTArray<nsCString>&& aSupportedTypes);
+
+ mozilla::ipc::IPCResult RecvInitNextGenLocalStorageEnabled(
+ const bool& aEnabled);
+
+ public:
+ static void DispatchBeforeUnloadToSubtree(
+ BrowsingContext* aStartingAt,
+ const DispatchBeforeUnloadToSubtreeResolver& aResolver);
+
+ hal::ProcessPriority GetProcessPriority() const { return mProcessPriority; }
+
+ hal::PerformanceHintSession* PerformanceHintSession() const {
+ return mPerformanceHintSession.get();
+ }
+
+ // Returns the target work duration for the PerformanceHintSession, based on
+ // the refresh interval. Estimate that we want the tick to complete in at most
+ // half of the refresh period. This is fairly arbitrary and can be tweaked
+ // later.
+ static TimeDuration GetPerformanceHintTarget(TimeDuration aRefreshInterval) {
+ return aRefreshInterval / int64_t(2);
+ }
+
+ private:
+ void AddProfileToProcessName(const nsACString& aProfile);
+ mozilla::ipc::IPCResult RecvFlushFOGData(FlushFOGDataResolver&& aResolver);
+
+ mozilla::ipc::IPCResult RecvUpdateMediaCodecsSupported(
+ RemoteDecodeIn aLocation, const media::MediaCodecsSupported& aSupported);
+
+#ifdef NIGHTLY_BUILD
+ virtual void OnChannelReceivedMessage(const Message& aMsg) override;
+
+ virtual PContentChild::Result OnMessageReceived(const Message& aMsg) override;
+
+ virtual PContentChild::Result OnMessageReceived(
+ const Message& aMsg, UniquePtr<Message>& aReply) override;
+#endif
+
+ void ConfigureThreadPerformanceHints(const hal::ProcessPriority& aPriority);
+
+ nsTArray<mozilla::UniquePtr<AlertObserver>> mAlertObservers;
+ RefPtr<ConsoleListener> mConsoleListener;
+
+ nsTHashSet<nsIObserver*> mIdleObservers;
+
+ nsTArray<nsCString> mAvailableDictionaries;
+
+ // Temporary storage for a list of available fonts, passed from the
+ // parent process and used to initialize gfx in the child. Currently used
+ // only on MacOSX and Linux.
+ dom::SystemFontList mFontList;
+ // Temporary storage for look and feel data.
+ FullLookAndFeel mLookAndFeelData;
+ // Temporary storage for list of shared-fontlist memory blocks.
+ nsTArray<base::SharedMemoryHandle> mSharedFontListBlocks;
+
+ /**
+ * An ID unique to the process containing our corresponding
+ * content parent.
+ *
+ * We expect our content parent to set this ID immediately after opening a
+ * channel to us.
+ */
+ ContentParentId mID;
+
+ AppInfo mAppInfo;
+
+ bool mIsForBrowser;
+ nsCString mRemoteType = NOT_REMOTE_TYPE;
+ bool mIsAlive;
+ nsCString mProcessName;
+
+ static ContentChild* sSingleton;
+
+ class ShutdownCanary;
+ static StaticAutoPtr<ShutdownCanary> sShutdownCanary;
+
+ nsCOMPtr<nsIDomainPolicy> mPolicy;
+ nsCOMPtr<nsITimer> mForceKillTimer;
+
+ RefPtr<ipc::SharedMap> mSharedData;
+
+ RefPtr<ChildProfilerController> mProfilerController;
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+ nsCOMPtr<nsIFile> mProfileDir;
+#endif
+
+ // Hashtable to keep track of the pending GetFilesHelper objects.
+ // This GetFilesHelperChild objects are removed when RecvGetFilesResponse is
+ // received.
+ nsRefPtrHashtable<nsIDHashKey, GetFilesHelperChild> mGetFilesPendingRequests;
+
+ nsClassHashtable<nsUint64HashKey, AnonymousTemporaryFileCallback>
+ mPendingAnonymousTemporaryFiles;
+
+ mozilla::Atomic<bool> mShuttingDown;
+
+#ifdef NIGHTLY_BUILD
+ // NOTE: This member is atomic because it can be accessed from
+ // off-main-thread.
+ mozilla::Atomic<uint32_t> mPendingInputEvents;
+#endif
+
+ uint32_t mNetworkLinkType = 0;
+
+ // See `BrowsingContext::mEpochs` for an explanation of this field.
+ uint64_t mBrowsingContextFieldEpoch = 0;
+
+ hal::ProcessPriority mProcessPriority = hal::PROCESS_PRIORITY_UNKNOWN;
+
+ // Session created when the process priority is FOREGROUND to ensure high
+ // priority scheduling of important threads. (Currently main thread and style
+ // threads.) The work duration is reported by the RefreshDriverTimer.
+ UniquePtr<hal::PerformanceHintSession> mPerformanceHintSession;
+};
+
+inline nsISupports* ToSupports(mozilla::dom::ContentChild* aContentChild) {
+ return static_cast<nsIDOMProcessChild*>(aContentChild);
+}
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_ContentChild_h
diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp
new file mode 100644
index 0000000000..13206216f3
--- /dev/null
+++ b/dom/ipc/ContentParent.cpp
@@ -0,0 +1,8184 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifdef MOZ_WIDGET_ANDROID
+# include "AndroidDecoderModule.h"
+#endif
+
+#include "mozilla/AppShutdown.h"
+#include "mozilla/DebugOnly.h"
+
+#include "base/basictypes.h"
+#include "base/shared_memory.h"
+
+#include "ContentParent.h"
+#include "mozilla/ipc/ProcessUtils.h"
+#include "mozilla/CmdLineAndEnvUtils.h"
+#include "BrowserParent.h"
+
+#include "chrome/common/process_watcher.h"
+#include "mozilla/Result.h"
+#include "mozilla/XREAppData.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIBrowserDOMWindow.h"
+
+#include "GMPServiceParent.h"
+#include "HandlerServiceParent.h"
+#include "IHistory.h"
+#include <map>
+#include <utility>
+
+#include "ContentProcessManager.h"
+#include "GeckoProfiler.h"
+#include "Geolocation.h"
+#include "GfxInfoBase.h"
+#include "MMPrinter.h"
+#include "PreallocatedProcessManager.h"
+#include "ProcessPriorityManager.h"
+#include "ProfilerParent.h"
+#include "SandboxHal.h"
+#include "SourceSurfaceRawData.h"
+#include "mozilla/ipc/URIUtils.h"
+#include "gfxPlatform.h"
+#include "gfxPlatformFontList.h"
+#include "nsDNSService2.h"
+#include "nsPIDNSService.h"
+#include "mozilla/AntiTrackingUtils.h"
+#include "mozilla/AppShutdown.h"
+#include "mozilla/AutoRestore.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/BenchmarkStorageParent.h"
+#include "mozilla/Casting.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/ClipboardReadRequestParent.h"
+#include "mozilla/ClipboardWriteRequestParent.h"
+#include "mozilla/ContentBlockingUserInteraction.h"
+#include "mozilla/FOGIPC.h"
+#include "mozilla/GlobalStyleSheetCache.h"
+#include "mozilla/GeckoArgs.h"
+#include "mozilla/HangDetails.h"
+#include "mozilla/LookAndFeel.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/NullPrincipal.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/PresShell.h"
+#include "mozilla/ProcessHangMonitor.h"
+#include "mozilla/ProcessHangMonitorIPC.h"
+#include "mozilla/ProfilerLabels.h"
+#include "mozilla/ProfilerMarkers.h"
+#include "mozilla/RecursiveMutex.h"
+#include "mozilla/ScopeExit.h"
+#include "mozilla/ScriptPreloader.h"
+#include "mozilla/Components.h"
+#include "mozilla/Sprintf.h"
+#include "mozilla/StaticPrefs_dom.h"
+#include "mozilla/StaticPrefs_fission.h"
+#include "mozilla/StaticPrefs_media.h"
+#include "mozilla/StaticPrefs_network.h"
+#include "mozilla/StaticPrefs_widget.h"
+#include "mozilla/StorageAccessAPIHelper.h"
+#include "mozilla/StyleSheet.h"
+#include "mozilla/StyleSheetInlines.h"
+#include "mozilla/TaskController.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/TelemetryIPC.h"
+#include "mozilla/ThreadSafety.h"
+#include "mozilla/Unused.h"
+#include "mozilla/WebBrowserPersistDocumentParent.h"
+#include "mozilla/devtools/HeapSnapshotTempFileHelperParent.h"
+#include "mozilla/dom/BlobURLProtocolHandler.h"
+#include "mozilla/dom/BrowserHost.h"
+#include "mozilla/dom/BrowsingContext.h"
+#include "mozilla/dom/BrowsingContextGroup.h"
+#include "mozilla/dom/CancelContentJSOptionsBinding.h"
+#include "mozilla/dom/CanonicalBrowsingContext.h"
+#include "mozilla/dom/ClientManager.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/DataTransfer.h"
+#include "mozilla/dom/Document.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/ExternalHelperAppParent.h"
+#include "mozilla/dom/File.h"
+#include "mozilla/dom/FileSystemSecurity.h"
+#include "mozilla/dom/GeolocationBinding.h"
+#include "mozilla/dom/GeolocationPositionError.h"
+#include "mozilla/dom/GetFilesHelper.h"
+#include "mozilla/dom/IPCBlobUtils.h"
+#include "mozilla/dom/JSActorService.h"
+#include "mozilla/dom/JSProcessActorBinding.h"
+#include "mozilla/dom/LocalStorageCommon.h"
+#include "mozilla/dom/MediaController.h"
+#include "mozilla/dom/MemoryReportRequest.h"
+#include "mozilla/dom/MediaStatusManager.h"
+#include "mozilla/dom/Notification.h"
+#include "mozilla/dom/PContentPermissionRequestParent.h"
+#include "mozilla/dom/PCycleCollectWithLogsParent.h"
+#include "mozilla/dom/ParentProcessMessageManager.h"
+#include "mozilla/dom/Permissions.h"
+#include "mozilla/dom/ProcessMessageManager.h"
+#include "mozilla/dom/PushNotifier.h"
+#include "mozilla/dom/ServiceWorkerManager.h"
+#include "mozilla/dom/ServiceWorkerRegistrar.h"
+#include "mozilla/dom/ServiceWorkerUtils.h"
+#include "mozilla/dom/SessionHistoryEntry.h"
+#include "mozilla/dom/SessionStorageManager.h"
+#include "mozilla/dom/StorageIPC.h"
+#include "mozilla/dom/UserActivation.h"
+#include "mozilla/dom/URLClassifierParent.h"
+#include "mozilla/dom/WindowGlobalParent.h"
+#include "mozilla/dom/ipc/SharedMap.h"
+#include "mozilla/dom/ipc/StructuredCloneData.h"
+#include "mozilla/dom/nsMixedContentBlocker.h"
+#include "mozilla/dom/power/PowerManagerService.h"
+#include "mozilla/dom/quota/QuotaManagerService.h"
+#include "mozilla/extensions/ExtensionsParent.h"
+#include "mozilla/extensions/StreamFilterParent.h"
+#include "mozilla/gfx/GPUProcessManager.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/glean/GleanPings.h"
+#include "mozilla/hal_sandbox/PHalParent.h"
+#include "mozilla/intl/L10nRegistry.h"
+#include "mozilla/intl/LocaleService.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/BackgroundParent.h"
+#include "mozilla/ipc/ByteBuf.h"
+#include "mozilla/ipc/CrashReporterHost.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/ipc/FileDescriptorUtils.h"
+#include "mozilla/ipc/IPCStreamUtils.h"
+#include "mozilla/ipc/TestShellParent.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/ImageBridgeParent.h"
+#include "mozilla/layers/LayerTreeOwnerTracker.h"
+#include "mozilla/layers/PAPZParent.h"
+#include "mozilla/loader/ScriptCacheActors.h"
+#include "mozilla/media/MediaParent.h"
+#include "mozilla/mozSpellChecker.h"
+#include "mozilla/net/CookieServiceParent.h"
+#include "mozilla/net/NeckoMessageUtils.h"
+#include "mozilla/net/NeckoParent.h"
+#include "mozilla/net/PCookieServiceParent.h"
+#include "mozilla/net/CookieKey.h"
+#include "mozilla/net/TRRService.h"
+#include "mozilla/TelemetryComms.h"
+#include "mozilla/TelemetryEventEnums.h"
+#include "mozilla/RemoteLazyInputStreamParent.h"
+#include "mozilla/widget/RemoteLookAndFeel.h"
+#include "mozilla/widget/ScreenManager.h"
+#include "mozilla/widget/TextRecognition.h"
+#include "nsAnonymousTemporaryFile.h"
+#include "nsAppRunner.h"
+#include "nsCExternalHandlerService.h"
+#include "nsCOMPtr.h"
+#include "nsChromeRegistryChrome.h"
+#include "nsConsoleMessage.h"
+#include "nsConsoleService.h"
+#include "nsContentPermissionHelper.h"
+#include "nsContentUtils.h"
+#include "nsCRT.h"
+#include "nsDebugImpl.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsDocShell.h"
+#include "nsEmbedCID.h"
+#include "nsFocusManager.h"
+#include "nsFrameLoader.h"
+#include "nsFrameMessageManager.h"
+#include "nsGlobalWindowOuter.h"
+#include "nsHashPropertyBag.h"
+#include "nsHyphenationManager.h"
+#include "nsIAlertsService.h"
+#include "nsIAppShell.h"
+#include "nsIAppWindow.h"
+#include "nsIAsyncInputStream.h"
+#include "nsIBidiKeyboard.h"
+#include "nsICaptivePortalService.h"
+#include "nsICertOverrideService.h"
+#include "nsIClipboard.h"
+#include "nsIContentAnalysis.h"
+#include "nsIContentProcess.h"
+#include "nsIContentSecurityPolicy.h"
+#include "nsICookie.h"
+#include "nsICrashService.h"
+#include "nsICycleCollectorListener.h"
+#include "nsIDocShell.h"
+#include "nsIDocShellTreeOwner.h"
+#include "nsIDragService.h"
+#include "nsIExternalProtocolService.h"
+#include "nsIGfxInfo.h"
+#include "nsIUserIdleService.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsILocalStorageManager.h"
+#include "nsIMemoryInfoDumper.h"
+#include "nsIMemoryReporter.h"
+#include "nsIMozBrowserFrame.h"
+#include "nsINetworkLinkService.h"
+#include "nsIObserverService.h"
+#include "nsIParentChannel.h"
+#include "nsIScriptError.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsIServiceWorkerManager.h"
+#include "nsISiteSecurityService.h"
+#include "nsIStringBundle.h"
+#include "nsITimer.h"
+#include "nsIURL.h"
+#include "nsIWebBrowserChrome.h"
+#include "nsIX509Cert.h"
+#include "nsIXULRuntime.h"
+#include "nsICookieNotification.h"
+#if defined(MOZ_WIDGET_GTK) || defined(XP_WIN)
+# include "nsIconChannel.h"
+#endif
+#include "nsMemoryInfoDumper.h"
+#include "nsMemoryReporterManager.h"
+#include "nsOpenURIInFrameParams.h"
+#include "nsPIWindowWatcher.h"
+#include "nsQueryObject.h"
+#include "nsReadableUtils.h"
+#include "nsSHistory.h"
+#include "nsScriptError.h"
+#include "nsServiceManagerUtils.h"
+#include "nsStreamUtils.h"
+#include "nsStyleSheetService.h"
+#include "nsThread.h"
+#include "nsThreadUtils.h"
+#include "nsWidgetsCID.h"
+#include "nsWindowWatcher.h"
+#include "prenv.h"
+#include "prio.h"
+#include "private/pprio.h"
+#include "xpcpublic.h"
+#include "nsOpenWindowInfo.h"
+#include "nsFrameLoaderOwner.h"
+
+#ifdef MOZ_WEBRTC
+# include "jsapi/WebrtcGlobalParent.h"
+#endif
+
+#if defined(XP_MACOSX)
+# include "nsMacUtilsImpl.h"
+# include "mozilla/AvailableMemoryWatcher.h"
+#endif
+
+#if defined(ANDROID) || defined(LINUX)
+# include "nsSystemInfo.h"
+#endif
+
+#if defined(XP_LINUX)
+# include "mozilla/Hal.h"
+#endif
+
+#ifdef ANDROID
+# include "gfxAndroidPlatform.h"
+#endif
+
+#include "mozilla/PermissionManager.h"
+
+#ifdef MOZ_WIDGET_ANDROID
+# include "AndroidBridge.h"
+# include "mozilla/java/GeckoProcessManagerWrappers.h"
+# include "mozilla/java/GeckoProcessTypeWrappers.h"
+#endif
+
+#ifdef MOZ_WIDGET_GTK
+# include <gdk/gdk.h>
+# include "mozilla/WidgetUtilsGtk.h"
+#endif
+
+#include "mozilla/RemoteSpellCheckEngineParent.h"
+
+#include "Crypto.h"
+
+#ifdef MOZ_WEBSPEECH
+# include "mozilla/dom/SpeechSynthesisParent.h"
+#endif
+
+#if defined(MOZ_SANDBOX)
+# include "mozilla/SandboxSettings.h"
+# if defined(XP_LINUX)
+# include "mozilla/SandboxInfo.h"
+# include "mozilla/SandboxBroker.h"
+# include "mozilla/SandboxBrokerPolicyFactory.h"
+# endif
+# if defined(XP_MACOSX)
+# include "mozilla/Sandbox.h"
+# endif
+#endif
+
+#ifdef XP_WIN
+# include "mozilla/widget/AudioSession.h"
+# include "mozilla/WinDllServices.h"
+#endif
+
+#ifdef MOZ_CODE_COVERAGE
+# include "mozilla/CodeCoverageHandler.h"
+#endif
+
+#ifdef FUZZING_SNAPSHOT
+# include "mozilla/fuzzing/IPCFuzzController.h"
+#endif
+
+// For VP9Benchmark::sBenchmarkFpsPref
+#include "Benchmark.h"
+
+#include "mozilla/RemoteDecodeUtils.h"
+#include "nsIToolkitProfileService.h"
+#include "nsIToolkitProfile.h"
+
+static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
+
+using base::KillProcess;
+
+using namespace CrashReporter;
+using namespace mozilla::dom::power;
+using namespace mozilla::media;
+using namespace mozilla::embedding;
+using namespace mozilla::gfx;
+using namespace mozilla::gmp;
+using namespace mozilla::hal;
+using namespace mozilla::ipc;
+using namespace mozilla::intl;
+using namespace mozilla::layers;
+using namespace mozilla::layout;
+using namespace mozilla::net;
+using namespace mozilla::psm;
+using namespace mozilla::widget;
+using namespace mozilla::Telemetry;
+using mozilla::loader::PScriptCacheParent;
+using mozilla::Telemetry::ProcessID;
+
+extern mozilla::LazyLogModule gFocusLog;
+
+#define LOGFOCUS(args) MOZ_LOG(gFocusLog, mozilla::LogLevel::Debug, args)
+
+extern mozilla::LazyLogModule sPDMLog;
+#define LOGPDM(...) MOZ_LOG(sPDMLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
+
+namespace mozilla {
+namespace CubebUtils {
+extern FileDescriptor CreateAudioIPCConnection();
+}
+
+namespace dom {
+
+LazyLogModule gProcessLog("Process");
+
+static std::map<RemoteDecodeIn, media::MediaCodecsSupported> sCodecsSupported;
+
+/* static */
+uint32_t ContentParent::sMaxContentProcesses = 0;
+
+/* static */
+LogModule* ContentParent::GetLog() { return gProcessLog; }
+
+/* static */
+uint32_t ContentParent::sPageLoadEventCounter = 0;
+
+#define NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC "ipc:network:set-offline"
+#define NS_IPC_IOSERVICE_SET_CONNECTIVITY_TOPIC "ipc:network:set-connectivity"
+
+// IPC receiver for remote GC/CC logging.
+class CycleCollectWithLogsParent final : public PCycleCollectWithLogsParent {
+ public:
+ MOZ_COUNTED_DTOR(CycleCollectWithLogsParent)
+
+ static bool AllocAndSendConstructor(ContentParent* aManager,
+ bool aDumpAllTraces,
+ nsICycleCollectorLogSink* aSink,
+ nsIDumpGCAndCCLogsCallback* aCallback) {
+ CycleCollectWithLogsParent* actor;
+ FILE* gcLog;
+ FILE* ccLog;
+ nsresult rv;
+
+ actor = new CycleCollectWithLogsParent(aSink, aCallback);
+ rv = actor->mSink->Open(&gcLog, &ccLog);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ delete actor;
+ return false;
+ }
+
+ return aManager->SendPCycleCollectWithLogsConstructor(
+ actor, aDumpAllTraces, FILEToFileDescriptor(gcLog),
+ FILEToFileDescriptor(ccLog));
+ }
+
+ private:
+ virtual mozilla::ipc::IPCResult RecvCloseGCLog() override {
+ Unused << mSink->CloseGCLog();
+ return IPC_OK();
+ }
+
+ virtual mozilla::ipc::IPCResult RecvCloseCCLog() override {
+ Unused << mSink->CloseCCLog();
+ return IPC_OK();
+ }
+
+ virtual mozilla::ipc::IPCResult Recv__delete__() override {
+ // Report completion to mCallback only on successful
+ // completion of the protocol.
+ nsCOMPtr<nsIFile> gcLog, ccLog;
+ mSink->GetGcLog(getter_AddRefs(gcLog));
+ mSink->GetCcLog(getter_AddRefs(ccLog));
+ Unused << mCallback->OnDump(gcLog, ccLog, /* parent = */ false);
+ return IPC_OK();
+ }
+
+ virtual void ActorDestroy(ActorDestroyReason aReason) override {
+ // If the actor is unexpectedly destroyed, we deliberately
+ // don't call Close[GC]CLog on the sink, because the logs may
+ // be incomplete. See also the nsCycleCollectorLogSinkToFile
+ // implementaiton of those methods, and its destructor.
+ }
+
+ CycleCollectWithLogsParent(nsICycleCollectorLogSink* aSink,
+ nsIDumpGCAndCCLogsCallback* aCallback)
+ : mSink(aSink), mCallback(aCallback) {
+ MOZ_COUNT_CTOR(CycleCollectWithLogsParent);
+ }
+
+ nsCOMPtr<nsICycleCollectorLogSink> mSink;
+ nsCOMPtr<nsIDumpGCAndCCLogsCallback> mCallback;
+};
+
+// A memory reporter for ContentParent objects themselves.
+class ContentParentsMemoryReporter final : public nsIMemoryReporter {
+ ~ContentParentsMemoryReporter() = default;
+
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMEMORYREPORTER
+};
+
+NS_IMPL_ISUPPORTS(ContentParentsMemoryReporter, nsIMemoryReporter)
+
+NS_IMETHODIMP
+ContentParentsMemoryReporter::CollectReports(
+ nsIHandleReportCallback* aHandleReport, nsISupports* aData,
+ bool aAnonymize) {
+ AutoTArray<ContentParent*, 16> cps;
+ ContentParent::GetAllEvenIfDead(cps);
+
+ for (uint32_t i = 0; i < cps.Length(); i++) {
+ ContentParent* cp = cps[i];
+ MessageChannel* channel = cp->GetIPCChannel();
+
+ nsString friendlyName;
+ cp->FriendlyName(friendlyName, aAnonymize);
+
+ cp->AddRef();
+ nsrefcnt refcnt = cp->Release();
+
+ const char* channelStr = "no channel";
+ uint32_t numQueuedMessages = 0;
+ if (channel) {
+ if (channel->IsClosed()) {
+ channelStr = "closed channel";
+ } else {
+ channelStr = "open channel";
+ }
+ numQueuedMessages =
+ 0; // XXX was channel->Unsound_NumQueuedMessages(); Bug 1754876
+ }
+
+ nsPrintfCString path(
+ "queued-ipc-messages/content-parent"
+ "(%s, pid=%d, %s, 0x%p, refcnt=%" PRIuPTR ")",
+ NS_ConvertUTF16toUTF8(friendlyName).get(), cp->Pid(), channelStr,
+ static_cast<nsIObserver*>(cp), refcnt);
+
+ constexpr auto desc =
+ "The number of unset IPC messages held in this ContentParent's "
+ "channel. A large value here might indicate that we're leaking "
+ "messages. Similarly, a ContentParent object for a process that's no "
+ "longer running could indicate that we're leaking ContentParents."_ns;
+
+ aHandleReport->Callback(/* process */ ""_ns, path, KIND_OTHER, UNITS_COUNT,
+ numQueuedMessages, desc, aData);
+ }
+
+ return NS_OK;
+}
+
+// A hashtable (by type) of processes/ContentParents. This includes
+// processes that are in the Preallocator cache (which would be type
+// 'prealloc'), and recycled processes ('web' and in the future
+// eTLD+1-locked) processes).
+nsClassHashtable<nsCStringHashKey, nsTArray<ContentParent*>>*
+ ContentParent::sBrowserContentParents;
+
+namespace {
+
+uint64_t ComputeLoadedOriginHash(nsIPrincipal* aPrincipal) {
+ uint32_t originNoSuffix =
+ BasePrincipal::Cast(aPrincipal)->GetOriginNoSuffixHash();
+ uint32_t originSuffix =
+ BasePrincipal::Cast(aPrincipal)->GetOriginSuffixHash();
+
+ return ((uint64_t)originNoSuffix) << 32 | originSuffix;
+}
+
+class ScriptableCPInfo final : public nsIContentProcessInfo {
+ public:
+ explicit ScriptableCPInfo(ContentParent* aParent) : mContentParent(aParent) {
+ MOZ_ASSERT(mContentParent);
+ }
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSICONTENTPROCESSINFO
+
+ void ProcessDied() { mContentParent = nullptr; }
+
+ private:
+ ~ScriptableCPInfo() { MOZ_ASSERT(!mContentParent, "must call ProcessDied"); }
+
+ ContentParent* mContentParent;
+};
+
+NS_IMPL_ISUPPORTS(ScriptableCPInfo, nsIContentProcessInfo)
+
+NS_IMETHODIMP
+ScriptableCPInfo::GetIsAlive(bool* aIsAlive) {
+ *aIsAlive = mContentParent != nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ScriptableCPInfo::GetProcessId(int32_t* aPID) {
+ if (!mContentParent) {
+ *aPID = -1;
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ *aPID = mContentParent->Pid();
+ if (*aPID == -1) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ScriptableCPInfo::GetTabCount(int32_t* aTabCount) {
+ if (!mContentParent) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+ *aTabCount = cpm->GetBrowserParentCountByProcessId(mContentParent->ChildID());
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ScriptableCPInfo::GetMessageManager(nsISupports** aMessenger) {
+ *aMessenger = nullptr;
+ if (!mContentParent) {
+ return NS_ERROR_NOT_INITIALIZED;
+ }
+
+ RefPtr<ProcessMessageManager> manager = mContentParent->GetMessageManager();
+ manager.forget(aMessenger);
+ return NS_OK;
+}
+
+ProcessID GetTelemetryProcessID(const nsACString& remoteType) {
+ // OOP WebExtensions run in a content process.
+ // For Telemetry though we want to break out collected data from the
+ // WebExtensions process into a separate bucket, to make sure we can analyze
+ // it separately and avoid skewing normal content process metrics.
+ return remoteType == EXTENSION_REMOTE_TYPE ? ProcessID::Extension
+ : ProcessID::Content;
+}
+
+} // anonymous namespace
+
+StaticAutoPtr<LinkedList<ContentParent>> ContentParent::sContentParents;
+StaticRefPtr<ContentParent> ContentParent::sRecycledE10SProcess;
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+StaticAutoPtr<SandboxBrokerPolicyFactory>
+ ContentParent::sSandboxBrokerPolicyFactory;
+#endif
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+StaticAutoPtr<std::vector<std::string>> ContentParent::sMacSandboxParams;
+#endif
+
+// Set to true when the first content process gets created.
+static bool sCreatedFirstContentProcess = false;
+
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+// True when we're running the process selection code, and do not expect to
+// enter code paths where processes may die.
+static bool sInProcessSelector = false;
+#endif
+
+// The first content child has ID 1, so the chrome process can have ID 0.
+static uint64_t gContentChildID = 1;
+
+static const char* sObserverTopics[] = {
+ NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC,
+ NS_IPC_IOSERVICE_SET_CONNECTIVITY_TOPIC,
+ NS_IPC_CAPTIVE_PORTAL_SET_STATE,
+ "application-background",
+ "application-foreground",
+ "memory-pressure",
+ "child-gc-request",
+ "child-cc-request",
+ "child-mmu-request",
+ "child-ghost-request",
+ "last-pb-context-exited",
+ "file-watcher-update",
+#ifdef ACCESSIBILITY
+ "a11y-init-or-shutdown",
+#endif
+ "cacheservice:empty-cache",
+ "intl:app-locales-changed",
+ "intl:requested-locales-changed",
+ "cookie-changed",
+ "private-cookie-changed",
+ NS_NETWORK_LINK_TYPE_TOPIC,
+ NS_NETWORK_TRR_MODE_CHANGED_TOPIC,
+ "network:socket-process-crashed",
+ DEFAULT_TIMEZONE_CHANGED_OBSERVER_TOPIC,
+};
+
+// PreallocateProcess is called by the PreallocatedProcessManager.
+// ContentParent then takes this process back within GetNewOrUsedBrowserProcess.
+/*static*/ already_AddRefed<ContentParent>
+ContentParent::MakePreallocProcess() {
+ RefPtr<ContentParent> process = new ContentParent(PREALLOC_REMOTE_TYPE);
+ return process.forget();
+}
+
+/*static*/
+void ContentParent::StartUp() {
+ // FIXME Bug 1023701 - Stop using ContentParent static methods in
+ // child process
+ if (!XRE_IsParentProcess()) {
+ return;
+ }
+
+ // From this point on, NS_WARNING, NS_ASSERTION, etc. should print out the
+ // PID along with the warning.
+ nsDebugImpl::SetMultiprocessMode("Parent");
+
+ // Note: This reporter measures all ContentParents.
+ RegisterStrongMemoryReporter(new ContentParentsMemoryReporter());
+
+ BackgroundChild::Startup();
+ ClientManager::Startup();
+
+ Preferences::RegisterCallbackAndCall(&OnFissionBlocklistPrefChange,
+ kFissionEnforceBlockList);
+ Preferences::RegisterCallbackAndCall(&OnFissionBlocklistPrefChange,
+ kFissionOmitBlockListValues);
+
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+ sSandboxBrokerPolicyFactory = new SandboxBrokerPolicyFactory();
+#endif
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+ sMacSandboxParams = new std::vector<std::string>();
+#endif
+}
+
+/*static*/
+void ContentParent::ShutDown() {
+ // For the most, we rely on normal process shutdown and
+ // ClearOnShutdown() to clean up our state.
+
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+ sSandboxBrokerPolicyFactory = nullptr;
+#endif
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+ sMacSandboxParams = nullptr;
+#endif
+}
+
+/*static*/
+uint32_t ContentParent::GetPoolSize(const nsACString& aContentProcessType) {
+ if (!sBrowserContentParents) {
+ return 0;
+ }
+
+ nsTArray<ContentParent*>* parents =
+ sBrowserContentParents->Get(aContentProcessType);
+
+ return parents ? parents->Length() : 0;
+}
+
+/*static*/ nsTArray<ContentParent*>& ContentParent::GetOrCreatePool(
+ const nsACString& aContentProcessType) {
+ if (!sBrowserContentParents) {
+ sBrowserContentParents =
+ new nsClassHashtable<nsCStringHashKey, nsTArray<ContentParent*>>;
+ }
+
+ return *sBrowserContentParents->GetOrInsertNew(aContentProcessType);
+}
+
+const nsDependentCSubstring RemoteTypePrefix(
+ const nsACString& aContentProcessType) {
+ // The suffix after a `=` in a remoteType is dynamic, and used to control the
+ // process pool to use.
+ int32_t equalIdx = aContentProcessType.FindChar(L'=');
+ if (equalIdx == kNotFound) {
+ equalIdx = aContentProcessType.Length();
+ }
+ return StringHead(aContentProcessType, equalIdx);
+}
+
+bool IsWebRemoteType(const nsACString& aContentProcessType) {
+ // Note: matches webIsolated, web, and webCOOP+COEP types.
+ return StringBeginsWith(aContentProcessType, DEFAULT_REMOTE_TYPE);
+}
+
+bool IsWebCoopCoepRemoteType(const nsACString& aContentProcessType) {
+ return StringBeginsWith(aContentProcessType,
+ WITH_COOP_COEP_REMOTE_TYPE_PREFIX);
+}
+
+bool IsExtensionRemoteType(const nsACString& aContentProcessType) {
+ return aContentProcessType == EXTENSION_REMOTE_TYPE;
+}
+
+/*static*/
+uint32_t ContentParent::GetMaxProcessCount(
+ const nsACString& aContentProcessType) {
+ // Max process count is based only on the prefix.
+ const nsDependentCSubstring processTypePrefix =
+ RemoteTypePrefix(aContentProcessType);
+
+ // Check for the default remote type of "web", as it uses different prefs.
+ if (processTypePrefix == DEFAULT_REMOTE_TYPE) {
+ return GetMaxWebProcessCount();
+ }
+
+ // Read the pref controling this remote type. `dom.ipc.processCount` is not
+ // used as a fallback, as it is intended to control the number of "web"
+ // content processes, checked in `mozilla::GetMaxWebProcessCount()`.
+ nsAutoCString processCountPref("dom.ipc.processCount.");
+ processCountPref.Append(processTypePrefix);
+
+ int32_t maxContentParents = Preferences::GetInt(processCountPref.get(), 1);
+ if (maxContentParents < 1) {
+ maxContentParents = 1;
+ }
+
+ return static_cast<uint32_t>(maxContentParents);
+}
+
+/*static*/
+bool ContentParent::IsMaxProcessCountReached(
+ const nsACString& aContentProcessType) {
+ return GetPoolSize(aContentProcessType) >=
+ GetMaxProcessCount(aContentProcessType);
+}
+
+// Really more ReleaseUnneededProcesses()
+/*static*/
+void ContentParent::ReleaseCachedProcesses() {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("ReleaseCachedProcesses:"));
+ if (!sBrowserContentParents) {
+ return;
+ }
+
+#ifdef DEBUG
+ for (const auto& cps : *sBrowserContentParents) {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("%s: %zu processes", PromiseFlatCString(cps.GetKey()).get(),
+ cps.GetData()->Length()));
+ }
+#endif
+
+ // First let's collect all processes and keep a grip.
+ AutoTArray<RefPtr<ContentParent>, 32> fixArray;
+ for (const auto& contentParents : sBrowserContentParents->Values()) {
+ for (auto* cp : *contentParents) {
+ fixArray.AppendElement(cp);
+ }
+ }
+
+ for (const auto& cp : fixArray) {
+ // Ensure the process cannot be claimed between check and MarkAsDead.
+ RecursiveMutexAutoLock lock(cp->ThreadsafeHandleMutex());
+
+ if (cp->ManagedPBrowserParent().Count() == 0 && !cp->HasActiveWorker() &&
+ cp->mRemoteType == DEFAULT_REMOTE_TYPE) {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ (" Shutdown %p (%s)", cp.get(), cp->mRemoteType.get()));
+
+ PreallocatedProcessManager::Erase(cp);
+ // Make sure we don't select this process for new tabs or workers.
+ cp->MarkAsDead();
+ // Start a soft shutdown.
+ cp->ShutDownProcess(SEND_SHUTDOWN_MESSAGE);
+ // Make sure that this process is no longer accessible from JS by its
+ // message manager.
+ cp->ShutDownMessageManager();
+ } else {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ (" Skipping %p (%s), count %d, HasActiveWorker %d", cp.get(),
+ cp->mRemoteType.get(), cp->ManagedPBrowserParent().Count(),
+ cp->HasActiveWorker()));
+ }
+ }
+}
+
+/*static*/
+already_AddRefed<ContentParent> ContentParent::MinTabSelect(
+ const nsTArray<ContentParent*>& aContentParents,
+ int32_t aMaxContentParents) {
+ uint32_t maxSelectable =
+ std::min(static_cast<uint32_t>(aContentParents.Length()),
+ static_cast<uint32_t>(aMaxContentParents));
+ uint32_t min = INT_MAX;
+ RefPtr<ContentParent> candidate;
+ ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+
+ for (uint32_t i = 0; i < maxSelectable; i++) {
+ ContentParent* p = aContentParents[i];
+ MOZ_DIAGNOSTIC_ASSERT(!p->IsDead());
+
+ // Ignore processes that were slated for removal but not yet removed from
+ // the pool (see also GetUsedBrowserProcess and BlockShutdown).
+ if (!p->IsShuttingDown()) {
+ uint32_t tabCount = cpm->GetBrowserParentCountByProcessId(p->ChildID());
+ if (tabCount < min) {
+ candidate = p;
+ min = tabCount;
+ }
+ }
+ }
+
+ // If all current processes have at least one tab and we have not yet reached
+ // the maximum, use a new process.
+ if (min > 0 &&
+ aContentParents.Length() < static_cast<uint32_t>(aMaxContentParents)) {
+ return nullptr;
+ }
+
+ // Otherwise we return candidate.
+ return candidate.forget();
+}
+
+/* static */
+already_AddRefed<nsIPrincipal>
+ContentParent::CreateRemoteTypeIsolationPrincipal(
+ const nsACString& aRemoteType) {
+ if ((RemoteTypePrefix(aRemoteType) != FISSION_WEB_REMOTE_TYPE) &&
+ !StringBeginsWith(aRemoteType, WITH_COOP_COEP_REMOTE_TYPE_PREFIX)) {
+ return nullptr;
+ }
+
+ int32_t offset = aRemoteType.FindChar('=') + 1;
+ MOZ_ASSERT(offset > 1, "can not extract origin from that remote type");
+ nsAutoCString origin(
+ Substring(aRemoteType, offset, aRemoteType.Length() - offset));
+
+ nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
+ nsCOMPtr<nsIPrincipal> principal;
+ ssm->CreateContentPrincipalFromOrigin(origin, getter_AddRefs(principal));
+ return principal.forget();
+}
+
+/*static*/
+already_AddRefed<ContentParent> ContentParent::GetUsedBrowserProcess(
+ const nsACString& aRemoteType, nsTArray<ContentParent*>& aContentParents,
+ uint32_t aMaxContentParents, bool aPreferUsed, ProcessPriority aPriority) {
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ AutoRestore ar(sInProcessSelector);
+ sInProcessSelector = true;
+#endif
+
+ uint32_t numberOfParents = aContentParents.Length();
+ nsTArray<RefPtr<nsIContentProcessInfo>> infos(numberOfParents);
+ for (auto* cp : aContentParents) {
+ infos.AppendElement(cp->mScriptableHelper);
+ }
+
+ if (aPreferUsed && numberOfParents) {
+ // If we prefer re-using existing content processes, we don't want to create
+ // a new process, and instead re-use an existing one, so pretend the process
+ // limit is at the current number of processes.
+ aMaxContentParents = numberOfParents;
+ }
+
+ nsCOMPtr<nsIContentProcessProvider> cpp =
+ do_GetService("@mozilla.org/ipc/processselector;1");
+ int32_t index;
+ if (cpp && NS_SUCCEEDED(cpp->ProvideProcess(aRemoteType, infos,
+ aMaxContentParents, &index))) {
+ // If the provider returned an existing ContentParent, use that one.
+ if (0 <= index && static_cast<uint32_t>(index) <= aMaxContentParents) {
+ RefPtr<ContentParent> retval = aContentParents[index];
+ // Ignore processes that were slated for removal but not yet removed from
+ // the pool.
+ if (!retval->IsShuttingDown()) {
+ if (profiler_thread_is_being_profiled_for_markers()) {
+ nsPrintfCString marker("Reused process %u",
+ (unsigned int)retval->ChildID());
+ PROFILER_MARKER_TEXT("Process", DOM, {}, marker);
+ }
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("GetUsedProcess: Reused process %p (%u) for %s", retval.get(),
+ (unsigned int)retval->ChildID(),
+ PromiseFlatCString(aRemoteType).get()));
+ retval->AssertAlive();
+ retval->StopRecyclingE10SOnly(true);
+ return retval.forget();
+ }
+ }
+ } else {
+ // If there was a problem with the JS chooser, fall back to a random
+ // selection.
+ NS_WARNING("nsIContentProcessProvider failed to return a process");
+ RefPtr<ContentParent> random;
+ if ((random = MinTabSelect(aContentParents, aMaxContentParents))) {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("GetUsedProcess: Reused random process %p (%d) for %s",
+ random.get(), (unsigned int)random->ChildID(),
+ PromiseFlatCString(aRemoteType).get()));
+ random->AssertAlive();
+ random->StopRecyclingE10SOnly(true);
+ return random.forget();
+ }
+ }
+
+ // If we are loading into the "web" remote type, are choosing to launch a new
+ // tab, and have a recycled E10S process, we should launch into that process.
+ if (aRemoteType == DEFAULT_REMOTE_TYPE && sRecycledE10SProcess) {
+ RefPtr<ContentParent> recycled = sRecycledE10SProcess;
+ MOZ_DIAGNOSTIC_ASSERT(recycled->GetRemoteType() == DEFAULT_REMOTE_TYPE);
+ recycled->AssertAlive();
+ recycled->StopRecyclingE10SOnly(true);
+ if (profiler_thread_is_being_profiled_for_markers()) {
+ nsPrintfCString marker("Recycled process %u (%p)",
+ (unsigned int)recycled->ChildID(), recycled.get());
+ PROFILER_MARKER_TEXT("Process", DOM, {}, marker);
+ }
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("Recycled process %p", recycled.get()));
+
+ return recycled.forget();
+ }
+
+ // Try to take a preallocated process except for certain remote types.
+ // Note: this process may not have finished launching yet
+ RefPtr<ContentParent> preallocated;
+ if (aRemoteType != FILE_REMOTE_TYPE &&
+ aRemoteType != PRIVILEGEDABOUT_REMOTE_TYPE &&
+ aRemoteType != EXTENSION_REMOTE_TYPE && // Bug 1638119
+ (preallocated = PreallocatedProcessManager::Take(aRemoteType))) {
+ MOZ_DIAGNOSTIC_ASSERT(preallocated->GetRemoteType() ==
+ PREALLOC_REMOTE_TYPE);
+ MOZ_DIAGNOSTIC_ASSERT(sRecycledE10SProcess != preallocated);
+ preallocated->AssertAlive();
+
+ if (profiler_thread_is_being_profiled_for_markers()) {
+ nsPrintfCString marker(
+ "Assigned preallocated process %u%s",
+ (unsigned int)preallocated->ChildID(),
+ preallocated->IsLaunching() ? " (still launching)" : "");
+ PROFILER_MARKER_TEXT("Process", DOM, {}, marker);
+ }
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("Adopted preallocated process %p for type %s%s",
+ preallocated.get(), PromiseFlatCString(aRemoteType).get(),
+ preallocated->IsLaunching() ? " (still launching)" : ""));
+
+ // This ensures that the preallocator won't shut down the process once
+ // it finishes starting
+ preallocated->mRemoteType.Assign(aRemoteType);
+ {
+ RecursiveMutexAutoLock lock(preallocated->mThreadsafeHandle->mMutex);
+ preallocated->mThreadsafeHandle->mRemoteType = preallocated->mRemoteType;
+ }
+ preallocated->mRemoteTypeIsolationPrincipal =
+ CreateRemoteTypeIsolationPrincipal(aRemoteType);
+ preallocated->mActivateTS = TimeStamp::Now();
+ preallocated->AddToPool(aContentParents);
+
+ // rare, but will happen
+ if (!preallocated->IsLaunching()) {
+ // Specialize this process for the appropriate remote type, and activate
+ // it.
+
+ Unused << preallocated->SendRemoteType(preallocated->mRemoteType,
+ preallocated->mProfile);
+
+ nsCOMPtr<nsIObserverService> obs =
+ mozilla::services::GetObserverService();
+ if (obs) {
+ nsAutoString cpId;
+ cpId.AppendInt(static_cast<uint64_t>(preallocated->ChildID()));
+ obs->NotifyObservers(static_cast<nsIObserver*>(preallocated),
+ "process-type-set", cpId.get());
+ preallocated->AssertAlive();
+ }
+ }
+ return preallocated.forget();
+ }
+
+ return nullptr;
+}
+
+/*static*/
+already_AddRefed<ContentParent>
+ContentParent::GetNewOrUsedLaunchingBrowserProcess(
+ const nsACString& aRemoteType, BrowsingContextGroup* aGroup,
+ ProcessPriority aPriority, bool aPreferUsed) {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("GetNewOrUsedProcess for type %s",
+ PromiseFlatCString(aRemoteType).get()));
+
+ // Fallback check (we really want our callers to avoid this).
+ if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
+ MOZ_DIAGNOSTIC_ASSERT(
+ false, "Late attempt to GetNewOrUsedLaunchingBrowserProcess!");
+ return nullptr;
+ }
+
+ // If we have an existing host process attached to this BrowsingContextGroup,
+ // always return it, as we can never have multiple host processes within a
+ // single BrowsingContextGroup.
+ RefPtr<ContentParent> contentParent;
+ if (aGroup) {
+ contentParent = aGroup->GetHostProcess(aRemoteType);
+ Unused << NS_WARN_IF(contentParent && contentParent->IsShuttingDown());
+ if (contentParent && !contentParent->IsShuttingDown()) {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("GetNewOrUsedProcess: Existing host process %p (launching %d)",
+ contentParent.get(), contentParent->IsLaunching()));
+ contentParent->AssertAlive();
+ contentParent->StopRecyclingE10SOnly(true);
+ return contentParent.forget();
+ }
+ }
+
+ nsTArray<ContentParent*>& contentParents = GetOrCreatePool(aRemoteType);
+ uint32_t maxContentParents = GetMaxProcessCount(aRemoteType);
+
+ // Let's try and reuse an existing process.
+ contentParent = GetUsedBrowserProcess(
+ aRemoteType, contentParents, maxContentParents, aPreferUsed, aPriority);
+
+ if (!contentParent) {
+ // No reusable process. Let's create and launch one.
+ // The life cycle will be set to `LifecycleState::LAUNCHING`.
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("Launching new process immediately for type %s",
+ PromiseFlatCString(aRemoteType).get()));
+
+ contentParent = new ContentParent(aRemoteType);
+ if (NS_WARN_IF(!contentParent->BeginSubprocessLaunch(aPriority))) {
+ // Launch aborted because of shutdown. Bailout.
+ contentParent->LaunchSubprocessReject();
+ return nullptr;
+ }
+ // Until the new process is ready let's not allow to start up any
+ // preallocated processes. The blocker will be removed once we receive
+ // the first idle message.
+ contentParent->mIsAPreallocBlocker = true;
+ PreallocatedProcessManager::AddBlocker(aRemoteType, contentParent);
+
+ // Store this process for future reuse.
+ contentParent->AddToPool(contentParents);
+
+ MOZ_LOG(
+ ContentParent::GetLog(), LogLevel::Debug,
+ ("GetNewOrUsedProcess: new immediate process %p", contentParent.get()));
+ }
+ // else we have an existing or preallocated process (which may be
+ // still launching)
+
+ contentParent->AssertAlive();
+ contentParent->StopRecyclingE10SOnly(true);
+ if (aGroup) {
+ aGroup->EnsureHostProcess(contentParent);
+ }
+ return contentParent.forget();
+}
+
+/*static*/
+RefPtr<ContentParent::LaunchPromise>
+ContentParent::GetNewOrUsedBrowserProcessAsync(const nsACString& aRemoteType,
+ BrowsingContextGroup* aGroup,
+ ProcessPriority aPriority,
+ bool aPreferUsed) {
+ // Obtain a `ContentParent` launched asynchronously.
+ RefPtr<ContentParent> contentParent = GetNewOrUsedLaunchingBrowserProcess(
+ aRemoteType, aGroup, aPriority, aPreferUsed);
+ if (!contentParent) {
+ // In case of launch error, stop here.
+ return LaunchPromise::CreateAndReject(NS_ERROR_ILLEGAL_DURING_SHUTDOWN,
+ __func__);
+ }
+ return contentParent->WaitForLaunchAsync(aPriority);
+}
+
+/*static*/
+already_AddRefed<ContentParent> ContentParent::GetNewOrUsedBrowserProcess(
+ const nsACString& aRemoteType, BrowsingContextGroup* aGroup,
+ ProcessPriority aPriority, bool aPreferUsed) {
+ RefPtr<ContentParent> contentParent = GetNewOrUsedLaunchingBrowserProcess(
+ aRemoteType, aGroup, aPriority, aPreferUsed);
+ if (!contentParent || !contentParent->WaitForLaunchSync(aPriority)) {
+ // In case of launch error, stop here.
+ return nullptr;
+ }
+ return contentParent.forget();
+}
+
+RefPtr<ContentParent::LaunchPromise> ContentParent::WaitForLaunchAsync(
+ ProcessPriority aPriority) {
+ MOZ_DIAGNOSTIC_ASSERT(!IsDead());
+ if (!IsLaunching()) {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("WaitForLaunchAsync: launched"));
+ return LaunchPromise::CreateAndResolve(this, __func__);
+ }
+
+ // We've started an async content process launch.
+ Telemetry::Accumulate(Telemetry::CONTENT_PROCESS_LAUNCH_IS_SYNC, 0);
+
+ // We have located a process that hasn't finished initializing, then attempt
+ // to finish initializing. Both `LaunchSubprocessResolve` and
+ // `LaunchSubprocessReject` are safe to call multiple times if we race with
+ // other `WaitForLaunchAsync` callbacks.
+ return mSubprocess->WhenProcessHandleReady()->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [self = RefPtr{this}, aPriority]() {
+ if (self->LaunchSubprocessResolve(/* aIsSync = */ false, aPriority)) {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("WaitForLaunchAsync: async, now launched"));
+ self->mActivateTS = TimeStamp::Now();
+ return LaunchPromise::CreateAndResolve(self, __func__);
+ }
+
+ self->LaunchSubprocessReject();
+ return LaunchPromise::CreateAndReject(NS_ERROR_INVALID_ARG, __func__);
+ },
+ [self = RefPtr{this}]() {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("WaitForLaunchAsync: async, rejected"));
+ self->LaunchSubprocessReject();
+ return LaunchPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
+ });
+}
+
+bool ContentParent::WaitForLaunchSync(ProcessPriority aPriority) {
+ MOZ_DIAGNOSTIC_ASSERT(!IsDead());
+ if (!IsLaunching()) {
+ return true;
+ }
+
+ // We've started a sync content process launch.
+ Telemetry::Accumulate(Telemetry::CONTENT_PROCESS_LAUNCH_IS_SYNC, 1);
+
+ // We're a process which hasn't finished initializing. We may be racing
+ // against whoever launched it (and whoever else is already racing). Since
+ // we're sync, we win the race and finish the initialization.
+ bool launchSuccess = mSubprocess->WaitForProcessHandle();
+ if (launchSuccess &&
+ LaunchSubprocessResolve(/* aIsSync = */ true, aPriority)) {
+ mActivateTS = TimeStamp::Now();
+ return true;
+ }
+ // In case of failure.
+ LaunchSubprocessReject();
+ return false;
+}
+
+static nsIDocShell* GetOpenerDocShellHelper(Element* aFrameElement) {
+ // Propagate the private-browsing status of the element's parent
+ // docshell to the remote docshell, via the chrome flags.
+ MOZ_ASSERT(aFrameElement);
+ nsPIDOMWindowOuter* win = aFrameElement->OwnerDoc()->GetWindow();
+ if (!win) {
+ NS_WARNING("Remote frame has no window");
+ return nullptr;
+ }
+ nsIDocShell* docShell = win->GetDocShell();
+ if (!docShell) {
+ NS_WARNING("Remote frame has no docshell");
+ return nullptr;
+ }
+
+ return docShell;
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvCreateGMPService() {
+ Endpoint<PGMPServiceParent> parent;
+ Endpoint<PGMPServiceChild> child;
+
+ if (mGMPCreated) {
+ return IPC_FAIL(this, "GMP Service already created");
+ }
+
+ nsresult rv;
+ rv = PGMPService::CreateEndpoints(base::GetCurrentProcId(), OtherPid(),
+ &parent, &child);
+ if (NS_FAILED(rv)) {
+ return IPC_FAIL(this, "CreateEndpoints failed");
+ }
+
+ if (!GMPServiceParent::Create(std::move(parent))) {
+ return IPC_FAIL(this, "GMPServiceParent::Create failed");
+ }
+
+ if (!SendInitGMPService(std::move(child))) {
+ return IPC_FAIL(this, "SendInitGMPService failed");
+ }
+
+ mGMPCreated = true;
+
+ return IPC_OK();
+}
+
+Atomic<bool, mozilla::Relaxed> sContentParentTelemetryEventEnabled(false);
+
+/*static*/
+void ContentParent::LogAndAssertFailedPrincipalValidationInfo(
+ nsIPrincipal* aPrincipal, const char* aMethod) {
+ // nsContentSecurityManager may also enable this same event, but that's okay
+ if (!sContentParentTelemetryEventEnabled.exchange(true)) {
+ sContentParentTelemetryEventEnabled = true;
+ Telemetry::SetEventRecordingEnabled("security"_ns, true);
+ }
+
+ // Send Telemetry
+ nsAutoCString principalScheme, principalType, spec;
+ CopyableTArray<EventExtraEntry> extra(2);
+
+ if (!aPrincipal) {
+ principalType.AssignLiteral("NullPtr");
+ } else if (aPrincipal->IsSystemPrincipal()) {
+ principalType.AssignLiteral("SystemPrincipal");
+ } else if (aPrincipal->GetIsExpandedPrincipal()) {
+ principalType.AssignLiteral("ExpandedPrincipal");
+ } else if (aPrincipal->GetIsContentPrincipal()) {
+ principalType.AssignLiteral("ContentPrincipal");
+ aPrincipal->GetSpec(spec);
+ aPrincipal->GetScheme(principalScheme);
+
+ extra.AppendElement(EventExtraEntry{"scheme"_ns, principalScheme});
+ } else {
+ principalType.AssignLiteral("Unknown");
+ }
+
+ extra.AppendElement(EventExtraEntry{"principalType"_ns, principalType});
+
+ // Do not send telemetry when chrome-debugging is enabled
+ bool isChromeDebuggingEnabled =
+ Preferences::GetBool("devtools.chrome.enabled", false);
+ if (!isChromeDebuggingEnabled) {
+ Telemetry::EventID eventType =
+ Telemetry::EventID::Security_Fissionprincipals_Contentparent;
+ Telemetry::RecordEvent(eventType, mozilla::Some(aMethod),
+ mozilla::Some(extra));
+ }
+
+ // And log it
+ MOZ_LOG(
+ ContentParent::GetLog(), LogLevel::Error,
+ (" Receiving unexpected Principal (%s) within %s",
+ aPrincipal && aPrincipal->GetIsContentPrincipal() ? spec.get()
+ : principalType.get(),
+ aMethod));
+
+#ifdef DEBUG
+ // Not only log but also ensure we do not receive an unexpected
+ // principal when running in debug mode.
+ MOZ_ASSERT(false, "Receiving unexpected Principal");
+#endif
+}
+
+bool ContentParent::ValidatePrincipal(
+ nsIPrincipal* aPrincipal,
+ const EnumSet<ValidatePrincipalOptions>& aOptions) {
+ // If the pref says we should not validate, then there is nothing to do
+ if (!StaticPrefs::dom_security_enforceIPCBasedPrincipalVetting()) {
+ return true;
+ }
+
+ // If there is no principal, then there is nothing to validate!
+ if (!aPrincipal) {
+ return aOptions.contains(ValidatePrincipalOptions::AllowNullPtr);
+ }
+
+ // We currently do not track relationships between specific null principals
+ // and content processes, so we can not validate much here - just allow all
+ // null principals we see because they are generally safe anyway!
+ if (aPrincipal->GetIsNullPrincipal()) {
+ return true;
+ }
+
+ // Only allow the system principal if the passed in options flags
+ // request permitting the system principal.
+ if (aPrincipal->IsSystemPrincipal()) {
+ return aOptions.contains(ValidatePrincipalOptions::AllowSystem);
+ }
+
+ // XXXckerschb: we should eliminate the resource carve-out here and always
+ // validate the Principal, see Bug 1686200: Investigate Principal for pdf.js
+ if (aPrincipal->SchemeIs("resource")) {
+ return true;
+ }
+
+ // Validate each inner principal individually, allowing us to catch expanded
+ // principals containing the system principal, etc.
+ if (aPrincipal->GetIsExpandedPrincipal()) {
+ if (!aOptions.contains(ValidatePrincipalOptions::AllowExpanded)) {
+ return false;
+ }
+ // FIXME: There are more constraints on expanded principals in-practice,
+ // such as the structure of extension expanded principals. This may need
+ // to be investigated more in the future.
+ nsCOMPtr<nsIExpandedPrincipal> expandedPrincipal =
+ do_QueryInterface(aPrincipal);
+ const auto& allowList = expandedPrincipal->AllowList();
+ for (const auto& innerPrincipal : allowList) {
+ if (!ValidatePrincipal(innerPrincipal, aOptions)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // A URI with a file:// scheme can never load in a non-file content process
+ // due to sandboxing.
+ if (aPrincipal->SchemeIs("file")) {
+ // If we don't support a separate 'file' process, then we can return here.
+ if (!StaticPrefs::browser_tabs_remote_separateFileUriProcess()) {
+ return true;
+ }
+ return mRemoteType == FILE_REMOTE_TYPE;
+ }
+
+ if (aPrincipal->SchemeIs("about")) {
+ uint32_t flags = 0;
+ if (NS_FAILED(aPrincipal->GetAboutModuleFlags(&flags))) {
+ return false;
+ }
+
+ // Block principals for about: URIs which can't load in this process.
+ if (!(flags & (nsIAboutModule::URI_CAN_LOAD_IN_CHILD |
+ nsIAboutModule::URI_MUST_LOAD_IN_CHILD))) {
+ return false;
+ }
+ if (flags & nsIAboutModule::URI_MUST_LOAD_IN_EXTENSION_PROCESS) {
+ return mRemoteType == EXTENSION_REMOTE_TYPE;
+ }
+ return true;
+ }
+
+ if (!mRemoteTypeIsolationPrincipal ||
+ RemoteTypePrefix(mRemoteType) != FISSION_WEB_REMOTE_TYPE) {
+ return true;
+ }
+
+ // Web content can contain extension content frames, so a content process may
+ // send us an extension's principal.
+ auto* addonPolicy = BasePrincipal::Cast(aPrincipal)->AddonPolicy();
+ if (addonPolicy) {
+ return true;
+ }
+
+ // Ensure that the expected site-origin matches the one specified by our
+ // mRemoteTypeIsolationPrincipal.
+ nsAutoCString siteOriginNoSuffix;
+ if (NS_FAILED(aPrincipal->GetSiteOriginNoSuffix(siteOriginNoSuffix))) {
+ return false;
+ }
+ nsAutoCString remoteTypeSiteOriginNoSuffix;
+ if (NS_FAILED(mRemoteTypeIsolationPrincipal->GetSiteOriginNoSuffix(
+ remoteTypeSiteOriginNoSuffix))) {
+ return false;
+ }
+
+ return remoteTypeSiteOriginNoSuffix.Equals(siteOriginNoSuffix);
+}
+
+/*static*/
+already_AddRefed<RemoteBrowser> ContentParent::CreateBrowser(
+ const TabContext& aContext, Element* aFrameElement,
+ const nsACString& aRemoteType, BrowsingContext* aBrowsingContext,
+ ContentParent* aOpenerContentParent) {
+ AUTO_PROFILER_LABEL("ContentParent::CreateBrowser", OTHER);
+
+ MOZ_DIAGNOSTIC_ASSERT(
+ !aBrowsingContext->Canonical()->GetBrowserParent(),
+ "BrowsingContext must not have BrowserParent, or have previous "
+ "BrowserParent cleared");
+
+ // Don't bother creating new content browsers after entering shutdown. This
+ // could lead to starting a new content process, which may significantly delay
+ // shutdown, and the content is unlikely to be displayed.
+ if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
+ NS_WARNING("Ignoring remote browser creation request during shutdown");
+ return nullptr;
+ }
+
+ nsAutoCString remoteType(aRemoteType);
+ if (remoteType.IsEmpty()) {
+ remoteType = DEFAULT_REMOTE_TYPE;
+ }
+
+ TabId tabId(nsContentUtils::GenerateTabId());
+
+ nsIDocShell* docShell = GetOpenerDocShellHelper(aFrameElement);
+ TabId openerTabId;
+ if (docShell) {
+ openerTabId = BrowserParent::GetTabIdFrom(docShell);
+ }
+
+ RefPtr<ContentParent> constructorSender;
+ MOZ_RELEASE_ASSERT(XRE_IsParentProcess(),
+ "Cannot allocate BrowserParent in content process");
+ if (aOpenerContentParent && !aOpenerContentParent->IsShuttingDown()) {
+ constructorSender = aOpenerContentParent;
+ } else {
+ constructorSender = GetNewOrUsedBrowserProcess(
+ remoteType, aBrowsingContext->Group(), PROCESS_PRIORITY_FOREGROUND);
+ if (!constructorSender) {
+ return nullptr;
+ }
+ }
+
+ aBrowsingContext->SetEmbedderElement(aFrameElement);
+
+ // Ensure that the process which we're using to launch is set as the host
+ // process for this BrowsingContextGroup.
+ aBrowsingContext->Group()->EnsureHostProcess(constructorSender);
+
+ nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
+ docShell->GetTreeOwner(getter_AddRefs(treeOwner));
+ if (!treeOwner) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIWebBrowserChrome> wbc = do_GetInterface(treeOwner);
+ if (!wbc) {
+ return nullptr;
+ }
+ uint32_t chromeFlags = 0;
+ wbc->GetChromeFlags(&chromeFlags);
+
+ nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(docShell);
+ if (loadContext && loadContext->UsePrivateBrowsing()) {
+ chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW;
+ }
+ if (loadContext && loadContext->UseRemoteTabs()) {
+ chromeFlags |= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW;
+ }
+ if (loadContext && loadContext->UseRemoteSubframes()) {
+ chromeFlags |= nsIWebBrowserChrome::CHROME_FISSION_WINDOW;
+ }
+
+ if (tabId == 0) {
+ return nullptr;
+ }
+
+ aBrowsingContext->Canonical()->SetOwnerProcessId(
+ constructorSender->ChildID());
+
+ RefPtr<BrowserParent> browserParent =
+ new BrowserParent(constructorSender, tabId, aContext,
+ aBrowsingContext->Canonical(), chromeFlags);
+
+ // Open a remote endpoint for our PBrowser actor.
+ ManagedEndpoint<PBrowserChild> childEp =
+ constructorSender->OpenPBrowserEndpoint(browserParent);
+ if (NS_WARN_IF(!childEp.IsValid())) {
+ return nullptr;
+ }
+
+ ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+ if (NS_WARN_IF(!cpm)) {
+ return nullptr;
+ }
+ cpm->RegisterRemoteFrame(browserParent);
+
+ nsCOMPtr<nsIPrincipal> initialPrincipal =
+ NullPrincipal::Create(aBrowsingContext->OriginAttributesRef());
+ WindowGlobalInit windowInit = WindowGlobalActor::AboutBlankInitializer(
+ aBrowsingContext, initialPrincipal);
+
+ RefPtr<WindowGlobalParent> windowParent =
+ WindowGlobalParent::CreateDisconnected(windowInit);
+ if (NS_WARN_IF(!windowParent)) {
+ return nullptr;
+ }
+
+ // Open a remote endpoint for the initial PWindowGlobal actor.
+ ManagedEndpoint<PWindowGlobalChild> windowEp =
+ browserParent->OpenPWindowGlobalEndpoint(windowParent);
+ if (NS_WARN_IF(!windowEp.IsValid())) {
+ return nullptr;
+ }
+
+ // Tell the content process to set up its PBrowserChild.
+ bool ok = constructorSender->SendConstructBrowser(
+ std::move(childEp), std::move(windowEp), tabId,
+ aContext.AsIPCTabContext(), windowInit, chromeFlags,
+ constructorSender->ChildID(), constructorSender->IsForBrowser(),
+ /* aIsTopLevel */ true);
+ if (NS_WARN_IF(!ok)) {
+ return nullptr;
+ }
+
+ // Ensure that we're marked as the current BrowserParent on our
+ // CanonicalBrowsingContext.
+ aBrowsingContext->Canonical()->SetCurrentBrowserParent(browserParent);
+
+ windowParent->Init();
+
+ RefPtr<BrowserHost> browserHost = new BrowserHost(browserParent);
+ browserParent->SetOwnerElement(aFrameElement);
+ return browserHost.forget();
+}
+
+void ContentParent::GetAll(nsTArray<ContentParent*>& aArray) {
+ aArray.Clear();
+
+ for (auto* cp : AllProcesses(eLive)) {
+ aArray.AppendElement(cp);
+ }
+}
+
+void ContentParent::GetAllEvenIfDead(nsTArray<ContentParent*>& aArray) {
+ aArray.Clear();
+
+ for (auto* cp : AllProcesses(eAll)) {
+ aArray.AppendElement(cp);
+ }
+}
+
+void ContentParent::BroadcastStringBundle(
+ const StringBundleDescriptor& aBundle) {
+ AutoTArray<StringBundleDescriptor, 1> array;
+ array.AppendElement(aBundle);
+
+ for (auto* cp : AllProcesses(eLive)) {
+ Unused << cp->SendRegisterStringBundles(array);
+ }
+}
+
+void ContentParent::BroadcastFontListChanged() {
+ for (auto* cp : AllProcesses(eLive)) {
+ Unused << cp->SendFontListChanged();
+ }
+}
+
+void ContentParent::BroadcastShmBlockAdded(uint32_t aGeneration,
+ uint32_t aIndex) {
+ auto* pfl = gfxPlatformFontList::PlatformFontList();
+ for (auto* cp : AllProcesses(eLive)) {
+ base::SharedMemoryHandle handle =
+ pfl->ShareShmBlockToProcess(aIndex, cp->Pid());
+ if (handle == base::SharedMemory::NULLHandle()) {
+ // If something went wrong here, we just skip it; the child will need to
+ // request the block as needed, at some performance cost.
+ continue;
+ }
+ Unused << cp->SendFontListShmBlockAdded(aGeneration, aIndex,
+ std::move(handle));
+ }
+}
+
+void ContentParent::BroadcastThemeUpdate(widget::ThemeChangeKind aKind) {
+ const FullLookAndFeel& lnf = *RemoteLookAndFeel::ExtractData();
+ for (auto* cp : AllProcesses(eLive)) {
+ Unused << cp->SendThemeChanged(lnf, aKind);
+ }
+}
+
+/*static */
+void ContentParent::BroadcastMediaCodecsSupportedUpdate(
+ RemoteDecodeIn aLocation, const media::MediaCodecsSupported& aSupported) {
+ // Merge incoming codec support with existing support list
+ media::MCSInfo::AddSupport(aSupported);
+ auto support = media::MCSInfo::GetSupport();
+
+ // Update processes
+ sCodecsSupported[aLocation] = support;
+ for (auto* cp : AllProcesses(eAll)) {
+ Unused << cp->SendUpdateMediaCodecsSupported(aLocation, support);
+ }
+
+ // Generate + save support string for display in about:support
+ nsCString supportString;
+ media::MCSInfo::GetMediaCodecsSupportedString(supportString, support);
+ gfx::gfxVars::SetCodecSupportInfo(supportString);
+
+ // Print the support info only from the given location for debug purpose.
+ supportString.Truncate();
+ media::MCSInfo::GetMediaCodecsSupportedString(supportString, aSupported);
+ supportString.ReplaceSubstring("\n"_ns, ", "_ns);
+ LOGPDM("Broadcast support from '%s', support=%s",
+ RemoteDecodeInToStr(aLocation), supportString.get());
+}
+
+const nsACString& ContentParent::GetRemoteType() const { return mRemoteType; }
+
+static StaticRefPtr<nsIAsyncShutdownClient> sXPCOMShutdownClient;
+static StaticRefPtr<nsIAsyncShutdownClient> sProfileBeforeChangeClient;
+static StaticRefPtr<nsIAsyncShutdownClient> sQuitApplicationGrantedClient;
+
+void ContentParent::Init() {
+ MOZ_ASSERT(sXPCOMShutdownClient);
+
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ size_t length = ArrayLength(sObserverTopics);
+ for (size_t i = 0; i < length; ++i) {
+ obs->AddObserver(this, sObserverTopics[i], false);
+ }
+ }
+
+ if (obs) {
+ nsAutoString cpId;
+ cpId.AppendInt(static_cast<uint64_t>(this->ChildID()));
+ obs->NotifyObservers(static_cast<nsIObserver*>(this), "ipc:content-created",
+ cpId.get());
+ }
+
+#ifdef ACCESSIBILITY
+ // If accessibility is running in chrome process then start it in content
+ // process.
+ if (GetAccService()) {
+ Unused << SendActivateA11y();
+ }
+#endif // #ifdef ACCESSIBILITY
+
+ Unused << SendInitProfiler(ProfilerParent::CreateForProcess(OtherPid()));
+
+ RefPtr<GeckoMediaPluginServiceParent> gmps(
+ GeckoMediaPluginServiceParent::GetSingleton());
+ if (gmps) {
+ gmps->UpdateContentProcessGMPCapabilities(this);
+ }
+
+ // Flush any pref updates that happened during launch and weren't
+ // included in the blobs set up in BeginSubprocessLaunch.
+ for (const Pref& pref : mQueuedPrefs) {
+ Unused << NS_WARN_IF(!SendPreferenceUpdate(pref));
+ }
+ mQueuedPrefs.Clear();
+
+ Unused << SendInitNextGenLocalStorageEnabled(NextGenLocalStorageEnabled());
+}
+
+// Note that for E10S we can get a false here that will be overruled by
+// TryToRecycleE10SOnly as late as MaybeBeginShutdown. We cannot really
+// foresee its result here.
+bool ContentParent::CheckTabDestroyWillKeepAlive(
+ uint32_t aExpectedBrowserCount) {
+ return ManagedPBrowserParent().Count() != aExpectedBrowserCount ||
+ ShouldKeepProcessAlive();
+}
+
+RecursiveMutex& ContentParent::ThreadsafeHandleMutex() {
+ return mThreadsafeHandle->mMutex;
+}
+
+void ContentParent::NotifyTabWillDestroy() {
+ if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)
+#if !defined(MOZ_WIDGET_ANDROID)
+ /* on Android we keep processes alive more agressively, see
+ NotifyTabDestroying where we omit MaybeBeginShutdown */
+ || (/* we cannot trust CheckTabDestroyWillKeepAlive in E10S mode */
+ mozilla::FissionAutostart() &&
+ !CheckTabDestroyWillKeepAlive(mNumDestroyingTabs + 1))
+#endif
+ ) {
+ // Once we notify the impending shutdown, the content process will stop
+ // to process content JS on interrupt (among other things), so we need to
+ // be sure that the process will not be re-used after this point.
+ // The inverse is harmless, that is if we decide later to shut it down
+ // but did not notify here, it will be just notified later (but in rare
+ // cases too late to avoid a hang).
+ NotifyImpendingShutdown();
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ mNotifiedImpendingShutdownOnTabWillDestroy = true;
+#endif
+ }
+}
+
+void ContentParent::MaybeBeginShutDown(uint32_t aExpectedBrowserCount,
+ bool aSendShutDown) {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
+ ("MaybeBeginShutdown %p, %u vs %u", this,
+ ManagedPBrowserParent().Count(), aExpectedBrowserCount));
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // We need to lock our mutex here to ensure the state does not change
+ // between the check and the MarkAsDead.
+ // Note that if we come through BrowserParent::Destroy our mutex is
+ // already locked.
+ // TODO: We want to get rid of the ThreadsafeHandle, see bug 1683595.
+ RecursiveMutexAutoLock lock(mThreadsafeHandle->mMutex);
+
+ // Both CheckTabDestroyWillKeepAlive and TryToRecycleE10SOnly will return
+ // false if IsInOrBeyond(AppShutdownConfirmed), so if the parent shuts
+ // down we will always shutdown the child.
+ if (CheckTabDestroyWillKeepAlive(aExpectedBrowserCount) ||
+ TryToRecycleE10SOnly()) {
+ return;
+ }
+
+ MOZ_LOG(
+ ContentParent::GetLog(), LogLevel::Debug,
+ ("Beginning ContentParent Shutdown %p (%s)", this, mRemoteType.get()));
+
+ // We're dying now, prevent anything from re-using this process.
+ MarkAsDead();
+ SignalImpendingShutdownToContentJS();
+
+ if (aSendShutDown) {
+ AsyncSendShutDownMessage();
+ } else {
+ // aSendShutDown is false only when we get called from
+ // NotifyTabDestroying where we expect a subsequent call from
+ // NotifyTabDestroyed triggered by a Browser actor destroy
+ // roundtrip through the content process that might never arrive.
+ StartSendShutdownTimer();
+ }
+}
+
+void ContentParent::AsyncSendShutDownMessage() {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
+ ("AsyncSendShutDownMessage %p", this));
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(sRecycledE10SProcess != this);
+
+ // In the case of normal shutdown, send a shutdown message to child to
+ // allow it to perform shutdown tasks.
+ GetCurrentSerialEventTarget()->Dispatch(NewRunnableMethod<ShutDownMethod>(
+ "dom::ContentParent::ShutDownProcess", this,
+ &ContentParent::ShutDownProcess, SEND_SHUTDOWN_MESSAGE));
+}
+
+void MaybeLogBlockShutdownDiagnostics(ContentParent* aSelf, const char* aMsg,
+ const char* aFile, int32_t aLine) {
+#if defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
+ if (aSelf->IsBlockingShutdown()) {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Info,
+ ("ContentParent: id=%p pid=%d - %s at %s(%d)", aSelf, aSelf->Pid(),
+ aMsg, aFile, aLine));
+ }
+#else
+ Unused << aSelf;
+ Unused << aMsg;
+ Unused << aFile;
+ Unused << aLine;
+#endif
+}
+
+bool ContentParent::ShutDownProcess(ShutDownMethod aMethod) {
+ bool result = false;
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("ShutDownProcess: %p", this));
+ // NB: must MarkAsDead() here so that this isn't accidentally
+ // returned from Get*() while in the midst of shutdown.
+ MarkAsDead();
+
+ // Shutting down by sending a shutdown message works differently than the
+ // other methods. We first call Shutdown() in the child. After the child is
+ // ready, it calls FinishShutdown() on us. Then we close the channel.
+ if (aMethod == SEND_SHUTDOWN_MESSAGE) {
+ if (!mShutdownPending) {
+ if (CanSend()) {
+ // Stop sending input events with input priority when shutting down.
+ SetInputPriorityEventEnabled(false);
+ // If we did not earlier, let's signal the shutdown to JS now.
+ SignalImpendingShutdownToContentJS();
+ // Send a high priority announcement first. If this fails, SendShutdown
+ // will also fail.
+ Unused << SendShutdownConfirmedHP();
+ // Send the definite message with normal priority.
+ if (SendShutdown()) {
+ MaybeLogBlockShutdownDiagnostics(
+ this, "ShutDownProcess: Sent shutdown message.", __FILE__,
+ __LINE__);
+ mShutdownPending = true;
+ // We start the kill timer only after we asked our process to
+ // shutdown.
+ StartForceKillTimer();
+ result = true;
+ } else {
+ MaybeLogBlockShutdownDiagnostics(
+ this, "ShutDownProcess: !!! Send shutdown message failed! !!!",
+ __FILE__, __LINE__);
+ }
+ } else {
+ MaybeLogBlockShutdownDiagnostics(
+ this, "ShutDownProcess: !!! !CanSend !!!", __FILE__, __LINE__);
+ }
+ } else {
+ MaybeLogBlockShutdownDiagnostics(
+ this, "ShutDownProcess: Shutdown already pending.", __FILE__,
+ __LINE__);
+
+ result = true;
+ }
+ // If call was not successful, the channel must have been broken
+ // somehow, and we will clean up the error in ActorDestroy.
+ return result;
+ }
+
+ using mozilla::dom::quota::QuotaManagerService;
+
+ if (QuotaManagerService* qms = QuotaManagerService::GetOrCreate()) {
+ qms->AbortOperationsForProcess(mChildID);
+ }
+
+ if (aMethod == CLOSE_CHANNEL) {
+ if (!mCalledClose) {
+ MaybeLogBlockShutdownDiagnostics(
+ this, "ShutDownProcess: Closing channel.", __FILE__, __LINE__);
+ // Close() can only be called once: It kicks off the destruction sequence.
+ mCalledClose = true;
+ Close();
+ }
+ result = true;
+ }
+
+ // A ContentParent object might not get freed until after XPCOM shutdown has
+ // shut down the cycle collector. But by then it's too late to release any
+ // CC'ed objects, so we need to null them out here, while we still can. See
+ // bug 899761.
+ ShutDownMessageManager();
+ return result;
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvNotifyShutdownSuccess() {
+ if (!mShutdownPending) {
+ return IPC_FAIL(this, "RecvNotifyShutdownSuccess without mShutdownPending");
+ }
+
+ mIsNotifiedShutdownSuccess = true;
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvFinishShutdown() {
+ if (!mShutdownPending) {
+ return IPC_FAIL(this, "RecvFinishShutdown without mShutdownPending");
+ }
+
+ // At this point, we already called ShutDownProcess once with
+ // SEND_SHUTDOWN_MESSAGE. To actually close the channel, we call
+ // ShutDownProcess again with CLOSE_CHANNEL.
+ if (mCalledClose) {
+ MaybeLogBlockShutdownDiagnostics(
+ this, "RecvFinishShutdown: Channel already closed.", __FILE__,
+ __LINE__);
+ }
+
+ ShutDownProcess(CLOSE_CHANNEL);
+ return IPC_OK();
+}
+
+void ContentParent::ShutDownMessageManager() {
+ if (!mMessageManager) {
+ return;
+ }
+
+ mMessageManager->ReceiveMessage(mMessageManager, nullptr,
+ CHILD_PROCESS_SHUTDOWN_MESSAGE, false,
+ nullptr, nullptr, IgnoreErrors());
+
+ mMessageManager->SetOsPid(-1);
+ mMessageManager->Disconnect();
+ mMessageManager = nullptr;
+}
+
+void ContentParent::AddToPool(nsTArray<ContentParent*>& aPool) {
+ MOZ_DIAGNOSTIC_ASSERT(!mIsInPool);
+ AssertAlive();
+ MOZ_DIAGNOSTIC_ASSERT(!mCalledKillHard);
+ aPool.AppendElement(this);
+ mIsInPool = true;
+}
+
+void ContentParent::RemoveFromPool(nsTArray<ContentParent*>& aPool) {
+ MOZ_DIAGNOSTIC_ASSERT(mIsInPool);
+ aPool.RemoveElement(this);
+ mIsInPool = false;
+}
+
+void ContentParent::AssertNotInPool() {
+ MOZ_RELEASE_ASSERT(!mIsInPool);
+
+ MOZ_RELEASE_ASSERT(sRecycledE10SProcess != this);
+ 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 (!mIsInPool) {
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ AssertNotInPool();
+#endif
+ return;
+ }
+
+ // Ensure that this BrowsingContextGroup is no longer used to host new
+ // documents from any associated BrowsingContextGroups. It may become a host
+ // again in the future, if it is restored to the pool.
+ for (const auto& group : mGroups) {
+ group->RemoveHostProcess(this);
+ }
+
+ StopRecyclingE10SOnly(/* aForeground */ false);
+
+ if (sBrowserContentParents) {
+ if (auto entry = sBrowserContentParents->Lookup(mRemoteType)) {
+ const auto& contentParents = entry.Data();
+ RemoveFromPool(*contentParents);
+ if (contentParents->IsEmpty()) {
+ entry.Remove();
+ }
+ }
+ if (sBrowserContentParents->IsEmpty()) {
+ delete sBrowserContentParents;
+ sBrowserContentParents = nullptr;
+ }
+ }
+}
+
+void ContentParent::MarkAsDead() {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
+ ("Marking ContentProcess %p as dead", this));
+ MOZ_DIAGNOSTIC_ASSERT(!sInProcessSelector);
+ RemoveFromList();
+
+ // Flag shutdown has started for us to our threadsafe handle.
+ {
+ // Depending on how we get here, the lock might or might not be set.
+ RecursiveMutexAutoLock lock(mThreadsafeHandle->mMutex);
+
+ mThreadsafeHandle->mShutdownStarted = true;
+ }
+
+ // Prevent this process from being re-used.
+ PreallocatedProcessManager::Erase(this);
+ StopRecyclingE10SOnly(false);
+
+#if defined(MOZ_WIDGET_ANDROID) && !defined(MOZ_PROFILE_GENERATE)
+ if (IsAlive()) {
+ // We're intentionally killing the content process at this point to ensure
+ // that we never have a "dead" content process sitting around and occupying
+ // an Android Service.
+ //
+ // The exception is in MOZ_PROFILE_GENERATE builds where we must allow the
+ // process to shutdown cleanly so that profile data can be dumped. This is
+ // okay as we will not reach our process limit during the profile run.
+ nsCOMPtr<nsIEventTarget> launcherThread(GetIPCLauncher());
+ MOZ_ASSERT(launcherThread);
+
+ auto procType = java::GeckoProcessType::CONTENT();
+ auto selector =
+ java::GeckoProcessManager::Selector::New(procType, OtherPid());
+
+ launcherThread->Dispatch(NS_NewRunnableFunction(
+ "ContentParent::MarkAsDead",
+ [selector =
+ java::GeckoProcessManager::Selector::GlobalRef(selector)]() {
+ java::GeckoProcessManager::ShutdownProcess(selector);
+ }));
+ }
+#endif
+
+ if (mScriptableHelper) {
+ static_cast<ScriptableCPInfo*>(mScriptableHelper.get())->ProcessDied();
+ mScriptableHelper = nullptr;
+ }
+
+ mLifecycleState = LifecycleState::DEAD;
+}
+
+void ContentParent::OnChannelError() {
+ RefPtr<ContentParent> kungFuDeathGrip(this);
+ PContentParent::OnChannelError();
+}
+
+void ContentParent::ProcessingError(Result aCode, const char* aReason) {
+ if (MsgDropped == aCode) {
+ return;
+ }
+ // Other errors are big deals.
+#ifndef FUZZING
+ KillHard(aReason);
+#endif
+ if (CanSend()) {
+ GetIPCChannel()->InduceConnectionError();
+ }
+}
+
+void ContentParent::ActorDestroy(ActorDestroyReason why) {
+#ifdef FUZZING_SNAPSHOT
+ MOZ_FUZZING_IPC_DROP_PEER("ContentParent::ActorDestroy");
+#endif
+
+ if (mSendShutdownTimer) {
+ mSendShutdownTimer->Cancel();
+ mSendShutdownTimer = nullptr;
+ }
+ if (mForceKillTimer) {
+ mForceKillTimer->Cancel();
+ mForceKillTimer = nullptr;
+ }
+
+ // Signal shutdown completion regardless of error state, so we can
+ // finish waiting in the xpcom-shutdown/profile-before-change observer.
+ RemoveShutdownBlockers();
+
+ if (mHangMonitorActor) {
+ ProcessHangMonitor::RemoveProcess(mHangMonitorActor);
+ mHangMonitorActor = nullptr;
+ }
+
+ RefPtr<FileSystemSecurity> fss = FileSystemSecurity::Get();
+ if (fss) {
+ fss->Forget(ChildID());
+ }
+
+ if (why == NormalShutdown && !mCalledClose) {
+ // If we shut down normally but haven't called Close, assume somebody
+ // else called Close on us. In that case, we still need to call
+ // ShutDownProcess below to perform other necessary clean up.
+ mCalledClose = true;
+ }
+
+ // Make sure we always clean up.
+ ShutDownProcess(CLOSE_CHANNEL);
+
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ size_t length = ArrayLength(sObserverTopics);
+ for (size_t i = 0; i < length; ++i) {
+ obs->RemoveObserver(static_cast<nsIObserver*>(this), sObserverTopics[i]);
+ }
+ }
+
+ // remove the global remote preferences observers
+ Preferences::RemoveObserver(this, "");
+ gfxVars::RemoveReceiver(this);
+
+ if (GPUProcessManager* gpu = GPUProcessManager::Get()) {
+ // Note: the manager could have shutdown already.
+ gpu->RemoveListener(this);
+ }
+
+ RecvRemoveGeolocationListener();
+
+ // Destroy our JSProcessActors, and reject any pending queries.
+ JSActorDidDestroy();
+
+ if (obs) {
+ RefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();
+
+ props->SetPropertyAsUint64(u"childID"_ns, mChildID);
+
+ if (AbnormalShutdown == why) {
+ Telemetry::Accumulate(Telemetry::SUBPROCESS_ABNORMAL_ABORT, "content"_ns,
+ 1);
+
+ props->SetPropertyAsBool(u"abnormal"_ns, true);
+
+ nsAutoString dumpID;
+ // There's a window in which child processes can crash
+ // after IPC is established, but before a crash reporter
+ // is created.
+ if (mCrashReporter) {
+ // if mCreatedPairedMinidumps is true, we've already generated
+ // parent/child dumps for desktop crashes.
+ if (!mCreatedPairedMinidumps) {
+#if defined(XP_MACOSX)
+ RefPtr<nsAvailableMemoryWatcherBase> memWatcher;
+ memWatcher = nsAvailableMemoryWatcherBase::GetSingleton();
+ memWatcher->AddChildAnnotations(mCrashReporter);
+#endif
+
+ mCrashReporter->GenerateCrashReport(OtherPid());
+ }
+
+ if (mCrashReporter->HasMinidump()) {
+ dumpID = mCrashReporter->MinidumpID();
+ }
+ } else {
+ HandleOrphanedMinidump(&dumpID);
+ }
+
+ if (!dumpID.IsEmpty()) {
+ props->SetPropertyAsAString(u"dumpID"_ns, dumpID);
+ }
+ }
+ nsAutoString cpId;
+ cpId.AppendInt(static_cast<uint64_t>(this->ChildID()));
+ obs->NotifyObservers((nsIPropertyBag2*)props, "ipc:content-shutdown",
+ cpId.get());
+ }
+
+ // Remove any and all idle listeners.
+ if (mIdleListeners.Length() > 0) {
+ nsCOMPtr<nsIUserIdleService> idleService =
+ do_GetService("@mozilla.org/widget/useridleservice;1");
+ if (idleService) {
+ RefPtr<ParentIdleListener> listener;
+ for (const auto& lentry : mIdleListeners) {
+ listener = static_cast<ParentIdleListener*>(lentry.get());
+ idleService->RemoveIdleObserver(listener, listener->mTime);
+ }
+ }
+ mIdleListeners.Clear();
+ }
+
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
+ ("destroying Subprocess in ActorDestroy: ContentParent %p "
+ "mSubprocess %p handle %" PRIuPTR,
+ this, mSubprocess,
+ mSubprocess ? (uintptr_t)mSubprocess->GetChildProcessHandle() : -1));
+ // FIXME (bug 1520997): does this really need an additional dispatch?
+ if (GetCurrentSerialEventTarget()) {
+ GetCurrentSerialEventTarget()->Dispatch(NS_NewRunnableFunction(
+ "DelayedDeleteSubprocessRunnable", [subprocess = mSubprocess] {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("destroyed Subprocess in ActorDestroy: Subprocess %p handle "
+ "%" PRIuPTR,
+ subprocess,
+ subprocess ? (uintptr_t)subprocess->GetChildProcessHandle()
+ : -1));
+ subprocess->Destroy();
+ }));
+ }
+ mSubprocess = nullptr;
+
+ ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+ if (cpm) {
+ cpm->RemoveContentProcess(this->ChildID());
+ }
+
+ if (mDriverCrashGuard) {
+ mDriverCrashGuard->NotifyCrashed();
+ }
+
+ // Unregister all the BlobURLs registered by the ContentChild.
+ for (uint32_t i = 0; i < mBlobURLs.Length(); ++i) {
+ BlobURLProtocolHandler::RemoveDataEntry(mBlobURLs[i]);
+ }
+
+ mBlobURLs.Clear();
+
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ AssertNotInPool();
+#endif
+
+ // As this process is going away, ensure that every BrowsingContext hosted by
+ // it has been detached, and every BrowsingContextGroup has been fully
+ // unsubscribed.
+ BrowsingContext::DiscardFromContentParent(this);
+
+ const nsTHashSet<RefPtr<BrowsingContextGroup>> groups = std::move(mGroups);
+ for (const auto& group : groups) {
+ group->Unsubscribe(this);
+ }
+ MOZ_DIAGNOSTIC_ASSERT(mGroups.IsEmpty());
+
+ mPendingLoadStates.Clear();
+}
+
+bool ContentParent::TryToRecycleE10SOnly() {
+ // Only try to recycle "web" content processes, as other remote types are
+ // generally more unique, and cannot be effectively re-used. This is disabled
+ // with Fission, as "web" content processes are no longer frequently used.
+ //
+ // Disabling the process pre-allocator will also disable process recycling,
+ // allowing for more consistent process counts under testing.
+ if (mRemoteType != DEFAULT_REMOTE_TYPE || mozilla::FissionAutostart() ||
+ !PreallocatedProcessManager::Enabled()) {
+ return false;
+ }
+
+ // This life time check should be replaced by a memory health check (memory
+ // usage + fragmentation).
+
+ // Note that this is specifically to help with edge cases that rapidly
+ // create-and-destroy processes
+ const double kMaxLifeSpan = 5;
+ MOZ_LOG(
+ ContentParent::GetLog(), LogLevel::Debug,
+ ("TryToRecycle ContentProcess %p (%u) with lifespan %f seconds", this,
+ (unsigned int)ChildID(), (TimeStamp::Now() - mActivateTS).ToSeconds()));
+
+ if (mCalledKillHard || !IsAlive() ||
+ (TimeStamp::Now() - mActivateTS).ToSeconds() > kMaxLifeSpan) {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("TryToRecycle did not recycle %p", this));
+
+ // It's possible that the process was already cached, and we're being called
+ // from a different path, and we're now past kMaxLifeSpan (or some other).
+ // Ensure that if we're going to kill this process we don't recycle it.
+ StopRecyclingE10SOnly(/* aForeground */ false);
+ return false;
+ }
+
+ if (!sRecycledE10SProcess) {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("TryToRecycle began recycling %p", this));
+ sRecycledE10SProcess = this;
+
+ ProcessPriorityManager::SetProcessPriority(this,
+ PROCESS_PRIORITY_BACKGROUND);
+ return true;
+ }
+
+ if (sRecycledE10SProcess == this) {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("TryToRecycle continue recycling %p", this));
+ return true;
+ }
+
+ // Some other process is already being recycled, just shut this one down.
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("TryToRecycle did not recycle %p (already recycling %p)", this,
+ sRecycledE10SProcess.get()));
+ return false;
+}
+
+void ContentParent::StopRecyclingE10SOnly(bool aForeground) {
+ if (sRecycledE10SProcess != this) {
+ return;
+ }
+
+ sRecycledE10SProcess = nullptr;
+ if (aForeground) {
+ ProcessPriorityManager::SetProcessPriority(this,
+ PROCESS_PRIORITY_FOREGROUND);
+ }
+}
+
+bool ContentParent::HasActiveWorker() {
+ // If we have active workers, we need to stay alive.
+ {
+ // Most of the times we'll get here with the mutex acquired, but still.
+ RecursiveMutexAutoLock lock(mThreadsafeHandle->mMutex);
+ if (mThreadsafeHandle->mRemoteWorkerActorCount) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ContentParent::ShouldKeepProcessAlive() {
+ if (HasActiveWorker()) {
+ return true;
+ }
+
+ if (mNumKeepaliveCalls > 0) {
+ return true;
+ }
+
+ if (IsLaunching()) {
+ return true;
+ }
+
+ // If we have already been marked as dead, don't prevent shutdown.
+ if (IsDead()) {
+ return false;
+ }
+
+ // If everything is going down, there is no need to keep us alive, neither.
+ if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
+ return false;
+ }
+
+ if (!sBrowserContentParents) {
+ return false;
+ }
+
+ auto contentParents = sBrowserContentParents->Get(mRemoteType);
+ if (!contentParents) {
+ return false;
+ }
+
+ // We might want to keep some content processes alive for performance reasons.
+ // e.g. test runs and privileged content process for some about: pages.
+ // We don't want to alter behavior if the pref is not set, so default to 0.
+ int32_t processesToKeepAlive = 0;
+
+ nsAutoCString keepAlivePref("dom.ipc.keepProcessesAlive.");
+
+ if (StringBeginsWith(mRemoteType, FISSION_WEB_REMOTE_TYPE) &&
+ xpc::IsInAutomation()) {
+ keepAlivePref.Append(FISSION_WEB_REMOTE_TYPE);
+ keepAlivePref.AppendLiteral(".perOrigin");
+ } else {
+ keepAlivePref.Append(mRemoteType);
+ }
+ if (NS_FAILED(
+ Preferences::GetInt(keepAlivePref.get(), &processesToKeepAlive))) {
+ return false;
+ }
+
+ int32_t numberOfAliveProcesses = contentParents->Length();
+
+ return numberOfAliveProcesses <= processesToKeepAlive;
+}
+
+void ContentParent::NotifyTabDestroying() {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("NotifyTabDestroying %p:", this));
+ // There can be more than one PBrowser for a given app process
+ // because of popup windows. PBrowsers can also destroy
+ // concurrently. When all the PBrowsers are destroying, kick off
+ // another task to ensure the child process *really* shuts down,
+ // even if the PBrowsers themselves never finish destroying.
+ ++mNumDestroyingTabs;
+
+ /**
+ * We intentionally skip this code on Android:
+ * 1. Android has a fixed upper bound on the number of content processes, so
+ * we prefer to re-use them whenever possible (as opposed to letting an
+ * old process wind down while we launch a new one).
+ * 2. GeckoView always hard-kills content processes (and if it does not,
+ * Android itself will), so we don't concern ourselves with the ForceKill
+ * timer either.
+ */
+#if !defined(MOZ_WIDGET_ANDROID)
+ MaybeBeginShutDown(/* aExpectedBrowserCount */ mNumDestroyingTabs,
+ /* aSendShutDown */ false);
+#endif // !defined(MOZ_WIDGET_ANDROID)
+}
+
+void ContentParent::AddKeepAlive() {
+ AssertAlive();
+ // Something wants to keep this content process alive.
+ ++mNumKeepaliveCalls;
+}
+
+void ContentParent::RemoveKeepAlive() {
+ MOZ_DIAGNOSTIC_ASSERT(mNumKeepaliveCalls > 0);
+ --mNumKeepaliveCalls;
+
+ MaybeBeginShutDown();
+}
+
+void ContentParent::StartSendShutdownTimer() {
+ if (mSendShutdownTimer || !CanSend()) {
+ return;
+ }
+
+ uint32_t timeoutSecs = StaticPrefs::dom_ipc_tabs_shutdownTimeoutSecs();
+ if (timeoutSecs > 0) {
+ NS_NewTimerWithFuncCallback(getter_AddRefs(mSendShutdownTimer),
+ ContentParent::SendShutdownTimerCallback, this,
+ timeoutSecs * 1000, nsITimer::TYPE_ONE_SHOT,
+ "dom::ContentParent::StartSendShutdownTimer");
+ MOZ_ASSERT(mSendShutdownTimer);
+ }
+}
+
+void ContentParent::StartForceKillTimer() {
+ if (mForceKillTimer || !CanSend()) {
+ return;
+ }
+
+ uint32_t timeoutSecs = StaticPrefs::dom_ipc_tabs_shutdownTimeoutSecs();
+ if (timeoutSecs > 0) {
+ NS_NewTimerWithFuncCallback(getter_AddRefs(mForceKillTimer),
+ ContentParent::ForceKillTimerCallback, this,
+ timeoutSecs * 1000, nsITimer::TYPE_ONE_SHOT,
+ "dom::ContentParent::StartForceKillTimer");
+ MOZ_ASSERT(mForceKillTimer);
+ }
+}
+
+void ContentParent::NotifyTabDestroyed(const TabId& aTabId,
+ bool aNotifiedDestroying) {
+ if (aNotifiedDestroying) {
+ --mNumDestroyingTabs;
+ }
+
+ nsTArray<PContentPermissionRequestParent*> parentArray =
+ nsContentPermissionUtils::GetContentPermissionRequestParentById(aTabId);
+
+ // Need to close undeleted ContentPermissionRequestParents before tab is
+ // closed.
+ for (auto& permissionRequestParent : parentArray) {
+ Unused << PContentPermissionRequestParent::Send__delete__(
+ permissionRequestParent);
+ }
+
+ // There can be more than one PBrowser for a given app process
+ // because of popup windows. When the last one closes, shut
+ // us down.
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
+ ("NotifyTabDestroyed %p", this));
+
+ MaybeBeginShutDown(/* aExpectedBrowserCount */ 1);
+}
+
+TestShellParent* ContentParent::CreateTestShell() {
+ RefPtr<TestShellParent> actor = new TestShellParent();
+ if (!SendPTestShellConstructor(actor)) {
+ return nullptr;
+ }
+ return actor;
+}
+
+bool ContentParent::DestroyTestShell(TestShellParent* aTestShell) {
+ return PTestShellParent::Send__delete__(aTestShell);
+}
+
+TestShellParent* ContentParent::GetTestShellSingleton() {
+ PTestShellParent* p = LoneManagedOrNullAsserts(ManagedPTestShellParent());
+ return static_cast<TestShellParent*>(p);
+}
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+// Append the sandbox command line parameters that are not static. i.e.,
+// parameters that can be different for different child processes.
+void ContentParent::AppendDynamicSandboxParams(
+ std::vector<std::string>& aArgs) {
+ // For file content processes
+ if (GetRemoteType() == FILE_REMOTE_TYPE) {
+ MacSandboxInfo::AppendFileAccessParam(aArgs, true);
+ }
+}
+
+// Generate the static sandbox command line parameters and store
+// them in the provided params vector to be used each time a new
+// content process is launched.
+static void CacheSandboxParams(std::vector<std::string>& aCachedParams) {
+ // This must only be called once and we should
+ // be starting with an empty list of parameters.
+ MOZ_ASSERT(aCachedParams.empty());
+
+ MacSandboxInfo info;
+ info.type = MacSandboxType_Content;
+ info.level = GetEffectiveContentSandboxLevel();
+
+ // Sandbox logging
+ if (Preferences::GetBool("security.sandbox.logging.enabled") ||
+ PR_GetEnv("MOZ_SANDBOX_LOGGING")) {
+ info.shouldLog = true;
+ }
+
+ // Audio access
+ if (!StaticPrefs::media_cubeb_sandbox()) {
+ info.hasAudio = true;
+ }
+
+ // Window server access. If the disconnect-windowserver pref is not
+ // "true" or out-of-process WebGL is not enabled, allow window server
+ // access in the sandbox policy.
+ if (!Preferences::GetBool(
+ "security.sandbox.content.mac.disconnect-windowserver") ||
+ !Preferences::GetBool("webgl.out-of-process")) {
+ info.hasWindowServer = true;
+ }
+
+ // .app path (normalized)
+ nsAutoCString appPath;
+ if (!nsMacUtilsImpl::GetAppPath(appPath)) {
+ MOZ_CRASH("Failed to get app dir paths");
+ }
+ info.appPath = appPath.get();
+
+ // TESTING_READ_PATH1
+ nsAutoCString testingReadPath1;
+ Preferences::GetCString("security.sandbox.content.mac.testing_read_path1",
+ testingReadPath1);
+ if (!testingReadPath1.IsEmpty()) {
+ info.testingReadPath1 = testingReadPath1.get();
+ }
+
+ // TESTING_READ_PATH2
+ nsAutoCString testingReadPath2;
+ Preferences::GetCString("security.sandbox.content.mac.testing_read_path2",
+ testingReadPath2);
+ if (!testingReadPath2.IsEmpty()) {
+ info.testingReadPath2 = testingReadPath2.get();
+ }
+
+ // TESTING_READ_PATH3, TESTING_READ_PATH4. In non-packaged builds,
+ // these are used to whitelist the repo dir and object dir respectively.
+ nsresult rv;
+ if (!mozilla::IsPackagedBuild()) {
+ // Repo dir
+ nsCOMPtr<nsIFile> repoDir;
+ rv = nsMacUtilsImpl::GetRepoDir(getter_AddRefs(repoDir));
+ if (NS_FAILED(rv)) {
+ MOZ_CRASH("Failed to get path to repo dir");
+ }
+ nsCString repoDirPath;
+ Unused << repoDir->GetNativePath(repoDirPath);
+ info.testingReadPath3 = repoDirPath.get();
+
+ // Object dir
+ nsCOMPtr<nsIFile> objDir;
+ rv = nsMacUtilsImpl::GetObjDir(getter_AddRefs(objDir));
+ if (NS_FAILED(rv)) {
+ MOZ_CRASH("Failed to get path to build object dir");
+ }
+ nsCString objDirPath;
+ Unused << objDir->GetNativePath(objDirPath);
+ info.testingReadPath4 = objDirPath.get();
+ }
+
+ // DEBUG_WRITE_DIR
+# ifdef DEBUG
+ // For bloat/leak logging or when a content process dies intentionally
+ // (|NoteIntentionalCrash|) for tests, it wants to log that it did this.
+ // Allow writing to this location.
+ nsAutoCString bloatLogDirPath;
+ if (NS_SUCCEEDED(nsMacUtilsImpl::GetBloatLogDir(bloatLogDirPath))) {
+ info.debugWriteDir = bloatLogDirPath.get();
+ }
+# endif // DEBUG
+
+ info.AppendAsParams(aCachedParams);
+}
+
+// Append sandboxing command line parameters.
+void ContentParent::AppendSandboxParams(std::vector<std::string>& aArgs) {
+ MOZ_ASSERT(sMacSandboxParams != nullptr);
+
+ // An empty sMacSandboxParams indicates this is the
+ // first invocation and we don't have cached params yet.
+ if (sMacSandboxParams->empty()) {
+ CacheSandboxParams(*sMacSandboxParams);
+ MOZ_ASSERT(!sMacSandboxParams->empty());
+ }
+
+ // Append cached arguments.
+ aArgs.insert(aArgs.end(), sMacSandboxParams->begin(),
+ sMacSandboxParams->end());
+
+ // Append remaining arguments.
+ AppendDynamicSandboxParams(aArgs);
+}
+#endif // XP_MACOSX && MOZ_SANDBOX
+
+bool ContentParent::BeginSubprocessLaunch(ProcessPriority aPriority) {
+ AUTO_PROFILER_LABEL("ContentParent::LaunchSubprocess", OTHER);
+
+ // Ensure we will not rush through our shutdown phases while launching.
+ // LaunchSubprocessReject will remove them in case of failure,
+ // otherwise ActorDestroy will take care.
+ AddShutdownBlockers();
+
+ if (!ContentProcessManager::GetSingleton()) {
+ MOZ_ASSERT(false, "Unable to acquire ContentProcessManager singleton!");
+ return false;
+ }
+
+ std::vector<std::string> extraArgs;
+ geckoargs::sChildID.Put(mChildID, extraArgs);
+ geckoargs::sIsForBrowser.Put(IsForBrowser(), extraArgs);
+ geckoargs::sNotForBrowser.Put(!IsForBrowser(), extraArgs);
+
+ // Prefs information is passed via anonymous shared memory to avoid bloating
+ // the command line.
+
+ // Instantiate the pref serializer. It will be cleaned up in
+ // `LaunchSubprocessReject`/`LaunchSubprocessResolve`.
+ mPrefSerializer = MakeUnique<mozilla::ipc::SharedPreferenceSerializer>();
+ if (!mPrefSerializer->SerializeToSharedMemory(GeckoProcessType_Content,
+ GetRemoteType())) {
+ NS_WARNING("SharedPreferenceSerializer::SerializeToSharedMemory failed");
+ MarkAsDead();
+ return false;
+ }
+ mPrefSerializer->AddSharedPrefCmdLineArgs(*mSubprocess, extraArgs);
+
+ // The JS engine does some computation during the initialization which can be
+ // shared across processes. We add command line arguments to pass a file
+ // handle and its content length, to minimize the startup time of content
+ // processes.
+ ::mozilla::ipc::ExportSharedJSInit(*mSubprocess, extraArgs);
+
+ // Register ContentParent as an observer for changes to any pref
+ // whose prefix matches the empty string, i.e. all of them. The
+ // observation starts here in order to capture pref updates that
+ // happen during async launch.
+ Preferences::AddStrongObserver(this, "");
+
+ if (gSafeMode) {
+ geckoargs::sSafeMode.Put(extraArgs);
+ }
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+ if (IsContentSandboxEnabled()) {
+ AppendSandboxParams(extraArgs);
+ mSubprocess->DisableOSActivityMode();
+ }
+#endif
+
+ nsCString parentBuildID(mozilla::PlatformBuildID());
+ geckoargs::sParentBuildID.Put(parentBuildID.get(), extraArgs);
+
+#ifdef MOZ_WIDGET_GTK
+ // This is X11-only pending a solution for WebGL in Wayland mode.
+ if (StaticPrefs::dom_ipc_avoid_gtk() &&
+ StaticPrefs::widget_non_native_theme_enabled() &&
+ widget::GdkIsX11Display()) {
+ mSubprocess->SetEnv("MOZ_HEADLESS", "1");
+ }
+#endif
+
+ mLaunchYieldTS = TimeStamp::Now();
+ return mSubprocess->AsyncLaunch(std::move(extraArgs));
+}
+
+void ContentParent::LaunchSubprocessReject() {
+ NS_WARNING("failed to launch child in the parent");
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
+ ("failed to launch child in the parent"));
+ // Now that communication with the child is complete, we can cleanup
+ // the preference serializer.
+ mPrefSerializer = nullptr;
+ if (mIsAPreallocBlocker) {
+ PreallocatedProcessManager::RemoveBlocker(mRemoteType, this);
+ mIsAPreallocBlocker = false;
+ }
+ MarkAsDead();
+ RemoveShutdownBlockers();
+}
+
+bool ContentParent::LaunchSubprocessResolve(bool aIsSync,
+ ProcessPriority aPriority) {
+ AUTO_PROFILER_LABEL("ContentParent::LaunchSubprocess::resolve", OTHER);
+
+ if (mLaunchResolved) {
+ // We've already been called, return.
+ MOZ_ASSERT(sCreatedFirstContentProcess);
+ MOZ_ASSERT(!mPrefSerializer);
+ MOZ_ASSERT(mLifecycleState != LifecycleState::LAUNCHING);
+ return mLaunchResolvedOk;
+ }
+ mLaunchResolved = true;
+
+ // Now that communication with the child is complete, we can cleanup
+ // the preference serializer.
+ mPrefSerializer = nullptr;
+
+ const auto launchResumeTS = TimeStamp::Now();
+ if (profiler_thread_is_being_profiled_for_markers()) {
+ nsPrintfCString marker("Process start%s for %u",
+ mIsAPreallocBlocker ? " (immediate)" : "",
+ (unsigned int)ChildID());
+ PROFILER_MARKER_TEXT(
+ mIsAPreallocBlocker ? ProfilerString8View("Process Immediate Launch")
+ : ProfilerString8View("Process Launch"),
+ DOM, MarkerTiming::Interval(mLaunchTS, launchResumeTS), marker);
+ }
+
+ if (!sCreatedFirstContentProcess) {
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ obs->NotifyObservers(nullptr, "ipc:first-content-process-created", nullptr);
+ sCreatedFirstContentProcess = true;
+ }
+
+ mSubprocess->TakeInitialEndpoint().Bind(this);
+
+ ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+ if (!cpm) {
+ NS_WARNING("immediately shutting-down caused by our shutdown");
+ ShutDownProcess(SEND_SHUTDOWN_MESSAGE);
+ return false;
+ }
+ cpm->AddContentProcess(this);
+
+#ifdef MOZ_CODE_COVERAGE
+ Unused << SendShareCodeCoverageMutex(
+ CodeCoverageHandler::Get()->GetMutexHandle());
+#endif
+
+ // We must be in the LAUNCHING state still. If we've somehow already been
+ // marked as DEAD, fail the process launch, and immediately begin tearing down
+ // the content process.
+ if (IsDead()) {
+ NS_WARNING("immediately shutting-down already-dead process");
+ ShutDownProcess(SEND_SHUTDOWN_MESSAGE);
+ return false;
+ }
+ MOZ_ASSERT(mLifecycleState == LifecycleState::LAUNCHING);
+ mLifecycleState = LifecycleState::ALIVE;
+
+ if (!InitInternal(aPriority)) {
+ NS_WARNING("failed to initialize child in the parent");
+ // We've already called Open() by this point, so we need to close the
+ // channel to avoid leaking the process.
+ ShutDownProcess(SEND_SHUTDOWN_MESSAGE);
+ return false;
+ }
+
+ mHangMonitorActor = ProcessHangMonitor::AddProcess(this);
+
+ // Set a reply timeout for CPOWs.
+ SetReplyTimeoutMs(StaticPrefs::dom_ipc_cpow_timeout());
+
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ nsAutoString cpId;
+ cpId.AppendInt(static_cast<uint64_t>(this->ChildID()));
+ obs->NotifyObservers(static_cast<nsIObserver*>(this),
+ "ipc:content-initializing", cpId.get());
+ }
+
+ Init();
+
+ mLifecycleState = LifecycleState::INITIALIZED;
+
+ if (aIsSync) {
+ Telemetry::AccumulateTimeDelta(Telemetry::CONTENT_PROCESS_SYNC_LAUNCH_MS,
+ mLaunchTS);
+ } else {
+ Telemetry::AccumulateTimeDelta(Telemetry::CONTENT_PROCESS_LAUNCH_TOTAL_MS,
+ mLaunchTS);
+
+ Telemetry::Accumulate(
+ Telemetry::CONTENT_PROCESS_LAUNCH_MAINTHREAD_MS,
+ static_cast<uint32_t>(
+ ((mLaunchYieldTS - mLaunchTS) + (TimeStamp::Now() - launchResumeTS))
+ .ToMilliseconds()));
+ }
+
+ mLaunchResolvedOk = true;
+ return true;
+}
+
+bool ContentParent::LaunchSubprocessSync(
+ hal::ProcessPriority aInitialPriority) {
+ // We've started a sync content process launch.
+ Telemetry::Accumulate(Telemetry::CONTENT_PROCESS_LAUNCH_IS_SYNC, 1);
+
+ if (BeginSubprocessLaunch(aInitialPriority)) {
+ const bool ok = mSubprocess->WaitForProcessHandle();
+ if (ok && LaunchSubprocessResolve(/* aIsSync = */ true, aInitialPriority)) {
+ return true;
+ }
+ }
+ LaunchSubprocessReject();
+ return false;
+}
+
+RefPtr<ContentParent::LaunchPromise> ContentParent::LaunchSubprocessAsync(
+ hal::ProcessPriority aInitialPriority) {
+ // We've started an async content process launch.
+ Telemetry::Accumulate(Telemetry::CONTENT_PROCESS_LAUNCH_IS_SYNC, 0);
+
+ if (!BeginSubprocessLaunch(aInitialPriority)) {
+ // Launch aborted because of shutdown. Bailout.
+ LaunchSubprocessReject();
+ return LaunchPromise::CreateAndReject(NS_ERROR_ILLEGAL_DURING_SHUTDOWN,
+ __func__);
+ }
+
+ // Otherwise, wait until the process is ready.
+ RefPtr<ProcessHandlePromise> ready = mSubprocess->WhenProcessHandleReady();
+ RefPtr<ContentParent> self = this;
+ mLaunchYieldTS = TimeStamp::Now();
+
+ return ready->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [self, aInitialPriority](
+ const ProcessHandlePromise::ResolveOrRejectValue& aValue) {
+ if (aValue.IsResolve() &&
+ self->LaunchSubprocessResolve(/* aIsSync = */ false,
+ aInitialPriority)) {
+ return LaunchPromise::CreateAndResolve(self, __func__);
+ }
+ self->LaunchSubprocessReject();
+ return LaunchPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
+ });
+}
+
+ContentParent::ContentParent(const nsACString& aRemoteType)
+ : mSubprocess(nullptr),
+ mLaunchTS(TimeStamp::Now()),
+ mLaunchYieldTS(mLaunchTS),
+ mActivateTS(mLaunchTS),
+ mIsAPreallocBlocker(false),
+ mRemoteType(aRemoteType),
+ mChildID(gContentChildID++),
+ mGeolocationWatchID(-1),
+ 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) {
+ mRemoteTypeIsolationPrincipal =
+ CreateRemoteTypeIsolationPrincipal(aRemoteType);
+
+ // Insert ourselves into the global linked list of ContentParent objects.
+ if (!sContentParents) {
+ sContentParents = new LinkedList<ContentParent>();
+ }
+ sContentParents->insertBack(this);
+
+ mMessageManager = nsFrameMessageManager::NewProcessMessageManager(true);
+
+#if defined(XP_WIN)
+ // Request Windows message deferral behavior on our side of the PContent
+ // channel. Generally only applies to the situation where we get caught in
+ // a deadlock with the plugin process when sending CPOWs.
+ GetIPCChannel()->SetChannelFlags(
+ MessageChannel::REQUIRE_DEFERRED_MESSAGE_PROTECTION);
+#endif
+
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+ bool isFile = mRemoteType == FILE_REMOTE_TYPE;
+ mSubprocess = new GeckoChildProcessHost(GeckoProcessType_Content, isFile);
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
+ ("CreateSubprocess: ContentParent %p mSubprocess %p handle %" PRIuPTR,
+ this, mSubprocess,
+ mSubprocess ? (uintptr_t)mSubprocess->GetChildProcessHandle() : -1));
+
+ // This is safe to do in the constructor, as it doesn't take a strong
+ // reference.
+ mScriptableHelper = new ScriptableCPInfo(this);
+}
+
+ContentParent::~ContentParent() {
+ if (mSendShutdownTimer) {
+ mSendShutdownTimer->Cancel();
+ }
+ if (mForceKillTimer) {
+ mForceKillTimer->Cancel();
+ }
+
+ AssertIsOnMainThread();
+
+ // Clear the weak reference from the threadsafe handle back to this actor.
+ mThreadsafeHandle->mWeakActor = nullptr;
+
+ if (mIsAPreallocBlocker) {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("Removing blocker on ContentProcess destruction"));
+ PreallocatedProcessManager::RemoveBlocker(mRemoteType, this);
+ mIsAPreallocBlocker = false;
+ }
+
+ // We should be removed from all these lists in ActorDestroy.
+ AssertNotInPool();
+
+ // Normally mSubprocess is destroyed in ActorDestroy, but that won't
+ // happen if the process wasn't launched or if it failed to launch.
+ if (mSubprocess) {
+ MOZ_LOG(
+ ContentParent::GetLog(), LogLevel::Verbose,
+ ("DestroySubprocess: ContentParent %p mSubprocess %p handle %" PRIuPTR,
+ this, mSubprocess,
+ mSubprocess ? (uintptr_t)mSubprocess->GetChildProcessHandle() : -1));
+ mSubprocess->Destroy();
+ }
+
+ // Make sure to clear the connection from `mScriptableHelper` if it hasn't
+ // been cleared yet.
+ if (mScriptableHelper) {
+ static_cast<ScriptableCPInfo*>(mScriptableHelper.get())->ProcessDied();
+ mScriptableHelper = nullptr;
+ }
+}
+
+bool ContentParent::InitInternal(ProcessPriority aInitialPriority) {
+ // We can't access the locale service after shutdown has started. Since we
+ // can't init the process without it, and since we're going to be canceling
+ // whatever load attempt that initiated this process creation anyway, just
+ // bail out now if shutdown has already started.
+ if (PastShutdownPhase(ShutdownPhase::XPCOMShutdown)) {
+ return false;
+ }
+
+ XPCOMInitData xpcomInit;
+
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("ContentParent::InitInternal: %p", (void*)this));
+ nsCOMPtr<nsIIOService> io(do_GetIOService());
+ MOZ_ASSERT(io, "No IO service?");
+ DebugOnly<nsresult> rv = io->GetOffline(&xpcomInit.isOffline());
+ MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed getting offline?");
+
+ rv = io->GetConnectivity(&xpcomInit.isConnected());
+ MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed getting connectivity?");
+
+ xpcomInit.captivePortalState() = nsICaptivePortalService::UNKNOWN;
+ nsCOMPtr<nsICaptivePortalService> cps =
+ do_GetService(NS_CAPTIVEPORTAL_CONTRACTID);
+ if (cps) {
+ cps->GetState(&xpcomInit.captivePortalState());
+ }
+
+ if (StaticPrefs::fission_processProfileName()) {
+ nsCOMPtr<nsIToolkitProfileService> profileSvc =
+ do_GetService(NS_PROFILESERVICE_CONTRACTID);
+ if (profileSvc) {
+ nsCOMPtr<nsIToolkitProfile> currentProfile;
+ nsresult rv =
+ profileSvc->GetCurrentProfile(getter_AddRefs(currentProfile));
+ if (NS_SUCCEEDED(rv) && currentProfile) {
+ currentProfile->GetName(mProfile);
+ }
+ }
+ }
+
+ nsIBidiKeyboard* bidi = nsContentUtils::GetBidiKeyboard();
+
+ xpcomInit.isLangRTL() = false;
+ xpcomInit.haveBidiKeyboards() = false;
+ if (bidi) {
+ bidi->IsLangRTL(&xpcomInit.isLangRTL());
+ bidi->GetHaveBidiKeyboards(&xpcomInit.haveBidiKeyboards());
+ }
+
+ RefPtr<mozSpellChecker> spellChecker(mozSpellChecker::Create());
+ MOZ_ASSERT(spellChecker, "No spell checker?");
+
+ spellChecker->GetDictionaryList(&xpcomInit.dictionaries());
+
+ LocaleService::GetInstance()->GetAppLocalesAsBCP47(xpcomInit.appLocales());
+ LocaleService::GetInstance()->GetRequestedLocales(
+ xpcomInit.requestedLocales());
+
+ L10nRegistry::GetParentProcessFileSourceDescriptors(
+ xpcomInit.l10nFileSources());
+
+ nsCOMPtr<nsIClipboard> clipboard(
+ do_GetService("@mozilla.org/widget/clipboard;1"));
+ MOZ_ASSERT(clipboard, "No clipboard?");
+ MOZ_ASSERT(
+ clipboard->IsClipboardTypeSupported(nsIClipboard::kGlobalClipboard),
+ "We should always support the global clipboard.");
+
+ xpcomInit.clipboardCaps().supportsSelectionClipboard() =
+ clipboard->IsClipboardTypeSupported(nsIClipboard::kSelectionClipboard);
+
+ xpcomInit.clipboardCaps().supportsFindClipboard() =
+ clipboard->IsClipboardTypeSupported(nsIClipboard::kFindClipboard);
+
+ xpcomInit.clipboardCaps().supportsSelectionCache() =
+ clipboard->IsClipboardTypeSupported(nsIClipboard::kSelectionCache);
+
+ // Let's copy the domain policy from the parent to the child (if it's active).
+ StructuredCloneData initialData;
+ nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
+ if (ssm) {
+ ssm->CloneDomainPolicy(&xpcomInit.domainPolicy());
+
+ if (ParentProcessMessageManager* mm =
+ nsFrameMessageManager::sParentProcessManager) {
+ AutoJSAPI jsapi;
+ if (NS_WARN_IF(!jsapi.Init(xpc::PrivilegedJunkScope()))) {
+ MOZ_CRASH();
+ }
+ JS::Rooted<JS::Value> init(jsapi.cx());
+ // We'll crash on failure, so use a IgnoredErrorResult (which also
+ // auto-suppresses exceptions).
+ IgnoredErrorResult rv;
+ mm->GetInitialProcessData(jsapi.cx(), &init, rv);
+ if (NS_WARN_IF(rv.Failed())) {
+ MOZ_CRASH();
+ }
+
+ initialData.Write(jsapi.cx(), init, rv);
+ if (NS_WARN_IF(rv.Failed())) {
+ MOZ_CRASH();
+ }
+ }
+ }
+ // This is only implemented (returns a non-empty list) by MacOSX and Linux
+ // at present.
+ SystemFontList fontList;
+ gfxPlatform::GetPlatform()->ReadSystemFontList(&fontList);
+
+ const FullLookAndFeel& lnf = *RemoteLookAndFeel::ExtractData();
+
+ // If the shared fontlist is in use, collect its shmem block handles to pass
+ // to the child.
+ nsTArray<SharedMemoryHandle> sharedFontListBlocks;
+ gfxPlatformFontList::PlatformFontList()->ShareFontListToProcess(
+ &sharedFontListBlocks, OtherPid());
+
+ // Content processes have no permission to access profile directory, so we
+ // send the file URL instead.
+ auto* sheetCache = GlobalStyleSheetCache::Singleton();
+ if (StyleSheet* ucs = sheetCache->GetUserContentSheet()) {
+ xpcomInit.userContentSheetURL() = ucs->GetSheetURI();
+ } else {
+ xpcomInit.userContentSheetURL() = nullptr;
+ }
+
+ // 1. Build ContentDeviceData first, as it may affect some gfxVars.
+ gfxPlatform::GetPlatform()->BuildContentDeviceData(
+ &xpcomInit.contentDeviceData());
+ // 2. Gather non-default gfxVars.
+ xpcomInit.gfxNonDefaultVarUpdates() = gfxVars::FetchNonDefaultVars();
+ // 3. Start listening for gfxVars updates, to notify content process later on.
+ gfxVars::AddReceiver(this);
+
+ nsCOMPtr<nsIGfxInfo> gfxInfo = components::GfxInfo::Service();
+ if (gfxInfo) {
+ GfxInfoBase* gfxInfoRaw = static_cast<GfxInfoBase*>(gfxInfo.get());
+ xpcomInit.gfxFeatureStatus() = gfxInfoRaw->GetAllFeatures();
+ }
+
+ // Send the dynamic scalar definitions to the new process.
+ TelemetryIPC::GetDynamicScalarDefinitions(xpcomInit.dynamicScalarDefs());
+
+ for (auto const& [location, supported] : sCodecsSupported) {
+ Unused << SendUpdateMediaCodecsSupported(location, supported);
+ }
+
+#ifdef MOZ_WIDGET_ANDROID
+ if (!(StaticPrefs::media_utility_process_enabled() &&
+ StaticPrefs::media_utility_android_media_codec_enabled())) {
+ Unused << SendDecoderSupportedMimeTypes(
+ AndroidDecoderModule::GetSupportedMimeTypesPrefixed());
+ }
+#endif
+
+ // Must send screen info before send initialData
+ ScreenManager& screenManager = ScreenManager::GetSingleton();
+ screenManager.CopyScreensToRemote(this);
+
+ // Send the UA sheet shared memory buffer and the address it is mapped at.
+ Maybe<SharedMemoryHandle> sharedUASheetHandle;
+ uintptr_t sharedUASheetAddress = sheetCache->GetSharedMemoryAddress();
+
+ if (SharedMemoryHandle handle = sheetCache->CloneHandle()) {
+ sharedUASheetHandle.emplace(std::move(handle));
+ } else {
+ sharedUASheetAddress = 0;
+ }
+
+ bool isReadyForBackgroundProcessing = false;
+#if defined(XP_WIN)
+ RefPtr<DllServices> dllSvc(DllServices::Get());
+ isReadyForBackgroundProcessing = dllSvc->IsReadyForBackgroundProcessing();
+#endif
+
+ xpcomInit.perfStatsMask() = PerfStats::GetCollectionMask();
+
+ nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
+ dns->GetTrrDomain(xpcomInit.trrDomain());
+
+ nsIDNSService::ResolverMode mode;
+ dns->GetCurrentTrrMode(&mode);
+ xpcomInit.trrMode() = mode;
+ xpcomInit.trrModeFromPref() =
+ static_cast<nsIDNSService::ResolverMode>(StaticPrefs::network_trr_mode());
+
+ Unused << SendSetXPCOMProcessAttributes(
+ xpcomInit, initialData, lnf, fontList, std::move(sharedUASheetHandle),
+ sharedUASheetAddress, std::move(sharedFontListBlocks),
+ isReadyForBackgroundProcessing);
+
+ ipc::WritableSharedMap* sharedData =
+ nsFrameMessageManager::sParentProcessManager->SharedData();
+ sharedData->Flush();
+ sharedData->SendTo(this);
+
+ nsCOMPtr<nsIChromeRegistry> registrySvc = nsChromeRegistry::GetService();
+ nsChromeRegistryChrome* chromeRegistry =
+ static_cast<nsChromeRegistryChrome*>(registrySvc.get());
+ chromeRegistry->SendRegisteredChrome(this);
+
+ nsCOMPtr<nsIStringBundleService> stringBundleService =
+ components::StringBundle::Service();
+ stringBundleService->SendContentBundles(this);
+
+ if (gAppData) {
+ nsCString version(gAppData->version);
+ nsCString buildID(gAppData->buildID);
+ nsCString name(gAppData->name);
+ nsCString UAName(gAppData->UAName);
+ nsCString ID(gAppData->ID);
+ nsCString vendor(gAppData->vendor);
+ nsCString sourceURL(gAppData->sourceURL);
+ nsCString updateURL(gAppData->updateURL);
+
+ // Sending all information to content process.
+ Unused << SendAppInfo(version, buildID, name, UAName, ID, vendor, sourceURL,
+ updateURL);
+ }
+
+ // Send the child its remote type. On Mac, this needs to be sent prior
+ // to the message we send to enable the Sandbox (SendStartProcessSandbox)
+ // because different remote types require different sandbox privileges.
+
+ Unused << SendRemoteType(mRemoteType, mProfile);
+
+ ScriptPreloader::InitContentChild(*this);
+
+ // Initialize the message manager (and load delayed scripts) now that we
+ // have established communications with the child.
+ mMessageManager->InitWithCallback(this);
+ mMessageManager->SetOsPid(Pid());
+
+ // Set the subprocess's priority. We do this early on because we're likely
+ // /lowering/ the process's CPU and memory priority, which it has inherited
+ // from this process.
+ //
+ // This call can cause us to send IPC messages to the child process, so it
+ // must come after the Open() call above.
+ ProcessPriorityManager::SetProcessPriority(this, aInitialPriority);
+
+ // NB: internally, this will send an IPC message to the child
+ // process to get it to create the CompositorBridgeChild. This
+ // message goes through the regular IPC queue for this
+ // channel, so delivery will happen-before any other messages
+ // we send. The CompositorBridgeChild must be created before any
+ // PBrowsers are created, because they rely on the Compositor
+ // already being around. (Creation is async, so can't happen
+ // on demand.)
+ GPUProcessManager* gpm = GPUProcessManager::Get();
+
+ Endpoint<PCompositorManagerChild> compositor;
+ Endpoint<PImageBridgeChild> imageBridge;
+ Endpoint<PVRManagerChild> vrBridge;
+ Endpoint<PRemoteDecoderManagerChild> videoManager;
+ AutoTArray<uint32_t, 3> namespaces;
+
+ if (!gpm->CreateContentBridges(OtherPid(), &compositor, &imageBridge,
+ &vrBridge, &videoManager, mChildID,
+ &namespaces)) {
+ // This can fail if we've already started shutting down the compositor
+ // thread. See Bug 1562763 comment 8.
+ MOZ_ASSERT(AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdown));
+ return false;
+ }
+
+ Unused << SendInitRendering(std::move(compositor), std::move(imageBridge),
+ std::move(vrBridge), std::move(videoManager),
+ namespaces);
+
+ gpm->AddListener(this);
+
+ nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance();
+ if (sheetService) {
+ // This looks like a lot of work, but in a normal browser session we just
+ // send two loads.
+ //
+ // The URIs of the Gecko and Servo sheets should be the same, so it
+ // shouldn't matter which we look at.
+
+ for (StyleSheet* sheet : *sheetService->AgentStyleSheets()) {
+ Unused << SendLoadAndRegisterSheet(sheet->GetSheetURI(),
+ nsIStyleSheetService::AGENT_SHEET);
+ }
+
+ for (StyleSheet* sheet : *sheetService->UserStyleSheets()) {
+ Unused << SendLoadAndRegisterSheet(sheet->GetSheetURI(),
+ nsIStyleSheetService::USER_SHEET);
+ }
+
+ for (StyleSheet* sheet : *sheetService->AuthorStyleSheets()) {
+ Unused << SendLoadAndRegisterSheet(sheet->GetSheetURI(),
+ nsIStyleSheetService::AUTHOR_SHEET);
+ }
+ }
+
+#ifdef MOZ_SANDBOX
+ bool shouldSandbox = true;
+ Maybe<FileDescriptor> brokerFd;
+ // XXX: Checking the pref here makes it possible to enable/disable sandboxing
+ // during an active session. Currently the pref is only used for testing
+ // purpose. If the decision is made to permanently rely on the pref, this
+ // should be changed so that it is required to restart firefox for the change
+ // of value to take effect. Always send SetProcessSandbox message on macOS.
+# if !defined(XP_MACOSX)
+ shouldSandbox = IsContentSandboxEnabled();
+# endif
+
+# ifdef XP_LINUX
+ if (shouldSandbox) {
+ MOZ_ASSERT(!mSandboxBroker);
+ bool isFileProcess = mRemoteType == FILE_REMOTE_TYPE;
+ UniquePtr<SandboxBroker::Policy> policy =
+ sSandboxBrokerPolicyFactory->GetContentPolicy(Pid(), isFileProcess);
+ if (policy) {
+ brokerFd = Some(FileDescriptor());
+ mSandboxBroker =
+ SandboxBroker::Create(std::move(policy), Pid(), brokerFd.ref());
+ if (!mSandboxBroker) {
+ KillHard("SandboxBroker::Create failed");
+ return false;
+ }
+ MOZ_ASSERT(brokerFd.ref().IsValid());
+ }
+ }
+# endif
+ if (shouldSandbox && !SendSetProcessSandbox(brokerFd)) {
+ KillHard("SandboxInitFailed");
+ }
+#endif
+
+ // Ensure that the default set of permissions are avaliable in the content
+ // process before we try to load any URIs in it.
+ //
+ // NOTE: All default permissions has to be transmitted to the child process
+ // before the blob urls in the for loop below (See Bug 1738713 comment 12).
+ EnsurePermissionsByKey(""_ns, ""_ns);
+
+ {
+ nsTArray<BlobURLRegistrationData> registrations;
+ BlobURLProtocolHandler::ForEachBlobURL([&](BlobImpl* aBlobImpl,
+ nsIPrincipal* aPrincipal,
+ const nsCString& aPartitionKey,
+ const nsACString& aURI,
+ bool aRevoked) {
+ // We send all moz-extension Blob URL's to all content processes
+ // because content scripts mean that a moz-extension can live in any
+ // process. Same thing for system principal Blob URLs. Content Blob
+ // URL's are sent for content principals on-demand by
+ // AboutToLoadHttpFtpDocumentForChild and RemoteWorkerManager.
+ if (!BlobURLProtocolHandler::IsBlobURLBroadcastPrincipal(aPrincipal)) {
+ return true;
+ }
+
+ IPCBlob ipcBlob;
+ nsresult rv = IPCBlobUtils::Serialize(aBlobImpl, ipcBlob);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return false;
+ }
+
+ registrations.AppendElement(
+ BlobURLRegistrationData(nsCString(aURI), ipcBlob, aPrincipal,
+ nsCString(aPartitionKey), aRevoked));
+
+ rv = TransmitPermissionsForPrincipal(aPrincipal);
+ Unused << NS_WARN_IF(NS_FAILED(rv));
+ return true;
+ });
+
+ if (!registrations.IsEmpty()) {
+ Unused << SendInitBlobURLs(registrations);
+ }
+ }
+
+ // Send down { Parent, Window }ActorOptions at startup to content process.
+ RefPtr<JSActorService> actorSvc = JSActorService::GetSingleton();
+ if (actorSvc) {
+ nsTArray<JSProcessActorInfo> contentInfos;
+ actorSvc->GetJSProcessActorInfos(contentInfos);
+
+ nsTArray<JSWindowActorInfo> windowInfos;
+ actorSvc->GetJSWindowActorInfos(windowInfos);
+
+ Unused << SendInitJSActorInfos(contentInfos, windowInfos);
+ }
+
+ // Begin subscribing to any BrowsingContextGroups which were hosted by this
+ // process before it finished launching.
+ for (const auto& group : mGroups) {
+ group->Subscribe(this);
+ }
+
+ MaybeEnableRemoteInputEventQueue();
+
+ return true;
+}
+
+bool ContentParent::IsAlive() const {
+ return mLifecycleState == LifecycleState::ALIVE ||
+ mLifecycleState == LifecycleState::INITIALIZED;
+}
+
+bool ContentParent::IsInitialized() const {
+ return mLifecycleState == LifecycleState::INITIALIZED;
+}
+
+int32_t ContentParent::Pid() const {
+ if (!mSubprocess) {
+ return -1;
+ }
+ auto pid = mSubprocess->GetChildProcessId();
+ if (pid == 0) {
+ return -1;
+ }
+ return ReleaseAssertedCast<int32_t>(pid);
+}
+
+void ContentParent::OnCompositorUnexpectedShutdown() {
+ GPUProcessManager* gpm = GPUProcessManager::Get();
+
+ Endpoint<PCompositorManagerChild> compositor;
+ Endpoint<PImageBridgeChild> imageBridge;
+ Endpoint<PVRManagerChild> vrBridge;
+ Endpoint<PRemoteDecoderManagerChild> videoManager;
+ AutoTArray<uint32_t, 3> namespaces;
+
+ if (!gpm->CreateContentBridges(OtherPid(), &compositor, &imageBridge,
+ &vrBridge, &videoManager, mChildID,
+ &namespaces)) {
+ MOZ_ASSERT(AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdown));
+ return;
+ }
+
+ Unused << SendReinitRendering(std::move(compositor), std::move(imageBridge),
+ std::move(vrBridge), std::move(videoManager),
+ namespaces);
+}
+
+void ContentParent::OnCompositorDeviceReset() {
+ Unused << SendReinitRenderingForDeviceReset();
+}
+
+void ContentParent::MaybeEnableRemoteInputEventQueue() {
+ MOZ_ASSERT(!mIsRemoteInputEventQueueEnabled);
+ if (!IsInputEventQueueSupported()) {
+ return;
+ }
+ mIsRemoteInputEventQueueEnabled = true;
+ Unused << SendSetInputEventQueueEnabled();
+ SetInputPriorityEventEnabled(true);
+}
+
+void ContentParent::SetInputPriorityEventEnabled(bool aEnabled) {
+ if (!IsInputEventQueueSupported() || !mIsRemoteInputEventQueueEnabled ||
+ mIsInputPriorityEventEnabled == aEnabled) {
+ return;
+ }
+ mIsInputPriorityEventEnabled = aEnabled;
+ // Send IPC messages to flush the pending events in the input event queue and
+ // the normal event queue. See PContent.ipdl for more details.
+ Unused << SendSuspendInputEventQueue();
+ Unused << SendFlushInputEventQueue();
+ Unused << SendResumeInputEventQueue();
+}
+
+/*static*/
+bool ContentParent::IsInputEventQueueSupported() {
+ static bool sSupported = false;
+ static bool sInitialized = false;
+ if (!sInitialized) {
+ MOZ_ASSERT(Preferences::IsServiceAvailable());
+ sSupported = Preferences::GetBool("input_event_queue.supported", false);
+ sInitialized = true;
+ }
+ return sSupported;
+}
+
+void ContentParent::OnVarChanged(const GfxVarUpdate& aVar) {
+ if (!CanSend()) {
+ return;
+ }
+ Unused << SendVarUpdate(aVar);
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvSetClipboard(
+ const IPCTransferable& aTransferable, const int32_t& aWhichClipboard) {
+ // aRequestingPrincipal is allowed to be nullptr here.
+
+ if (!ValidatePrincipal(aTransferable.requestingPrincipal(),
+ {ValidatePrincipalOptions::AllowNullPtr,
+ ValidatePrincipalOptions::AllowExpanded,
+ ValidatePrincipalOptions::AllowSystem})) {
+ LogAndAssertFailedPrincipalValidationInfo(
+ aTransferable.requestingPrincipal(), __func__);
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
+ NS_ENSURE_SUCCESS(rv, IPC_OK());
+
+ nsCOMPtr<nsITransferable> trans =
+ do_CreateInstance("@mozilla.org/widget/transferable;1", &rv);
+ NS_ENSURE_SUCCESS(rv, IPC_OK());
+ trans->Init(nullptr);
+
+ rv = nsContentUtils::IPCTransferableToTransferable(
+ aTransferable, true /* aAddDataFlavor */, trans,
+ true /* aFilterUnknownFlavors */);
+ NS_ENSURE_SUCCESS(rv, IPC_OK());
+
+ clipboard->SetData(trans, nullptr, aWhichClipboard);
+ return IPC_OK();
+}
+
+namespace {
+
+static Result<nsCOMPtr<nsITransferable>, nsresult> CreateTransferable(
+ const nsTArray<nsCString>& aTypes) {
+ nsresult rv;
+ nsCOMPtr<nsITransferable> trans =
+ do_CreateInstance("@mozilla.org/widget/transferable;1", &rv);
+ if (NS_FAILED(rv)) {
+ return Err(rv);
+ }
+
+ MOZ_TRY(trans->Init(nullptr));
+ // The private flag is only used to prevent the data from being cached to the
+ // disk. The flag is not exported to the IPCDataTransfer object.
+ // The flag is set because we are not sure whether the clipboard data is used
+ // in a private browsing context. The transferable is only used in this scope,
+ // so the cache would not reduce memory consumption anyway.
+ trans->SetIsPrivateData(true);
+ // Fill out flavors for transferable
+ for (uint32_t t = 0; t < aTypes.Length(); t++) {
+ MOZ_TRY(trans->AddDataFlavor(aTypes[t].get()));
+ }
+
+ return std::move(trans);
+}
+
+} // anonymous namespace
+
+mozilla::ipc::IPCResult ContentParent::RecvGetClipboard(
+ nsTArray<nsCString>&& aTypes, const int32_t& aWhichClipboard,
+ const MaybeDiscarded<WindowContext>& aRequestingWindowContext,
+ IPCTransferableData* aTransferableData) {
+ nsresult rv;
+ // We expect content processes to always pass a non-null window so Content
+ // Analysis can analyze it. (if Content Analysis is active)
+ // There may be some cases when a window is closing, etc., in
+ // which case returning no clipboard content should not be a problem.
+ if (aRequestingWindowContext.IsDiscarded()) {
+ NS_WARNING(
+ "discarded window passed to RecvGetClipboard(); returning no clipboard "
+ "content");
+ return IPC_OK();
+ }
+ if (aRequestingWindowContext.IsNull()) {
+ return IPC_FAIL(this, "passed null window to RecvGetClipboard()");
+ }
+ RefPtr<WindowGlobalParent> window = aRequestingWindowContext.get_canonical();
+ // Retrieve clipboard
+ nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
+ if (NS_FAILED(rv)) {
+ 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, window);
+
+ nsContentUtils::TransferableToIPCTransferableData(
+ trans, aTransferableData, true /* aInSyncMessage */, this);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvEmptyClipboard(
+ const int32_t& aWhichClipboard) {
+ nsresult rv;
+ nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
+ NS_ENSURE_SUCCESS(rv, IPC_OK());
+
+ clipboard->EmptyClipboard(aWhichClipboard);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvClipboardHasType(
+ nsTArray<nsCString>&& aTypes, const int32_t& aWhichClipboard,
+ bool* aHasType) {
+ nsresult rv;
+ nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
+ NS_ENSURE_SUCCESS(rv, IPC_OK());
+
+ clipboard->HasDataMatchingFlavors(aTypes, aWhichClipboard, aHasType);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvGetExternalClipboardFormats(
+ const int32_t& aWhichClipboard, const bool& aPlainTextOnly,
+ nsTArray<nsCString>* aTypes) {
+ MOZ_ASSERT(aTypes);
+ DataTransfer::GetExternalClipboardFormats(aWhichClipboard, aPlainTextOnly,
+ aTypes);
+ return IPC_OK();
+}
+
+namespace {
+
+class ClipboardGetCallback final : public nsIAsyncClipboardGetCallback {
+ public:
+ ClipboardGetCallback(ContentParent* aContentParent,
+ ContentParent::GetClipboardAsyncResolver&& aResolver)
+ : mContentParent(aContentParent), mResolver(std::move(aResolver)) {}
+
+ // This object will never be held by a cycle-collected object, so it doesn't
+ // need to be cycle-collected despite holding alive cycle-collected objects.
+ NS_DECL_ISUPPORTS
+
+ // nsIAsyncClipboardGetCallback
+ NS_IMETHOD OnSuccess(
+ nsIAsyncGetClipboardData* aAsyncGetClipboardData) override {
+ nsTArray<nsCString> flavors;
+ nsresult rv = aAsyncGetClipboardData->GetFlavorList(flavors);
+ if (NS_FAILED(rv)) {
+ return OnError(rv);
+ }
+
+ auto requestParent = MakeNotNull<RefPtr<ClipboardReadRequestParent>>(
+ mContentParent, aAsyncGetClipboardData);
+ if (!mContentParent->SendPClipboardReadRequestConstructor(
+ requestParent, std::move(flavors))) {
+ return OnError(NS_ERROR_FAILURE);
+ }
+
+ mResolver(PClipboardReadRequestOrError(requestParent));
+ return NS_OK;
+ }
+
+ NS_IMETHOD OnError(nsresult aResult) override {
+ mResolver(aResult);
+ return NS_OK;
+ }
+
+ protected:
+ ~ClipboardGetCallback() = default;
+
+ RefPtr<ContentParent> mContentParent;
+ ContentParent::GetClipboardAsyncResolver mResolver;
+};
+
+NS_IMPL_ISUPPORTS(ClipboardGetCallback, nsIAsyncClipboardGetCallback)
+
+} // namespace
+
+mozilla::ipc::IPCResult ContentParent::RecvGetClipboardAsync(
+ nsTArray<nsCString>&& aTypes, const int32_t& aWhichClipboard,
+ const MaybeDiscarded<WindowContext>& aRequestingWindowContext,
+ mozilla::NotNull<nsIPrincipal*> aRequestingPrincipal,
+ GetClipboardAsyncResolver&& aResolver) {
+ if (!ValidatePrincipal(aRequestingPrincipal,
+ {ValidatePrincipalOptions::AllowSystem,
+ ValidatePrincipalOptions::AllowExpanded})) {
+ LogAndAssertFailedPrincipalValidationInfo(aRequestingPrincipal, __func__);
+ }
+
+ // If the requesting context has been discarded, cancel the paste.
+ if (aRequestingWindowContext.IsDiscarded()) {
+ aResolver(NS_ERROR_NOT_AVAILABLE);
+ return IPC_OK();
+ }
+
+ RefPtr<WindowGlobalParent> requestingWindow =
+ aRequestingWindowContext.get_canonical();
+ if (requestingWindow && requestingWindow->GetContentParent() != this) {
+ return IPC_FAIL(
+ this, "attempt to paste into WindowContext loaded in another process");
+ }
+
+ nsresult rv;
+ // Retrieve clipboard
+ nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
+ if (NS_FAILED(rv)) {
+ aResolver(rv);
+ return IPC_OK();
+ }
+
+ auto callback = MakeRefPtr<ClipboardGetCallback>(this, std::move(aResolver));
+ rv = clipboard->AsyncGetData(aTypes, aWhichClipboard, requestingWindow,
+ aRequestingPrincipal, callback);
+ if (NS_FAILED(rv)) {
+ callback->OnError(rv);
+ return IPC_OK();
+ }
+
+ return IPC_OK();
+}
+
+already_AddRefed<PClipboardWriteRequestParent>
+ContentParent::AllocPClipboardWriteRequestParent(
+ const int32_t& aClipboardType) {
+ RefPtr<ClipboardWriteRequestParent> request =
+ MakeAndAddRef<ClipboardWriteRequestParent>(this);
+ request->Init(aClipboardType);
+ return request.forget();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvGetIconForExtension(
+ const nsACString& aFileExt, const uint32_t& aIconSize,
+ nsTArray<uint8_t>* bits) {
+#ifdef MOZ_WIDGET_ANDROID
+ NS_ASSERTION(AndroidBridge::Bridge() != nullptr,
+ "AndroidBridge is not available");
+ if (AndroidBridge::Bridge() == nullptr) {
+ // Do not fail - just no icon will be shown
+ return IPC_OK();
+ }
+
+ bits->AppendElements(aIconSize * aIconSize * 4);
+
+ AndroidBridge::Bridge()->GetIconForExtension(aFileExt, aIconSize,
+ bits->Elements());
+#endif
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvFirstIdle() {
+ // When the ContentChild goes idle, it sends us a FirstIdle message
+ // which we use as a good time to signal the PreallocatedProcessManager
+ // that it can start allocating processes from now on.
+ if (mIsAPreallocBlocker) {
+ MOZ_LOG(
+ ContentParent::GetLog(), LogLevel::Verbose,
+ ("RecvFirstIdle %p: Removing Blocker for %s", this, mRemoteType.get()));
+ PreallocatedProcessManager::RemoveBlocker(mRemoteType, this);
+ mIsAPreallocBlocker = false;
+ }
+ return IPC_OK();
+}
+
+already_AddRefed<nsDocShellLoadState> ContentParent::TakePendingLoadStateForId(
+ uint64_t aLoadIdentifier) {
+ return mPendingLoadStates.Extract(aLoadIdentifier).valueOr(nullptr).forget();
+}
+
+void ContentParent::StorePendingLoadState(nsDocShellLoadState* aLoadState) {
+ MOZ_DIAGNOSTIC_ASSERT(
+ !mPendingLoadStates.Contains(aLoadState->GetLoadIdentifier()),
+ "The same nsDocShellLoadState was sent to the same content process "
+ "twice? This will mess with cross-process tracking of loads");
+ mPendingLoadStates.InsertOrUpdate(aLoadState->GetLoadIdentifier(),
+ aLoadState);
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvCleanupPendingLoadState(
+ uint64_t aLoadIdentifier) {
+ mPendingLoadStates.Remove(aLoadIdentifier);
+ return IPC_OK();
+}
+
+// We want ContentParent to show up in CC logs for debugging purposes, but we
+// don't actually cycle collect it.
+NS_IMPL_CYCLE_COLLECTION_0(ContentParent)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(ContentParent)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(ContentParent)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ContentParent)
+ NS_INTERFACE_MAP_ENTRY_CONCRETE(ContentParent)
+ NS_INTERFACE_MAP_ENTRY(nsIDOMProcessParent)
+ NS_INTERFACE_MAP_ENTRY(nsIObserver)
+ NS_INTERFACE_MAP_ENTRY(nsIDOMGeoPositionCallback)
+ NS_INTERFACE_MAP_ENTRY(nsIDOMGeoPositionErrorCallback)
+ NS_INTERFACE_MAP_ENTRY(nsIAsyncShutdownBlocker)
+ NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMProcessParent)
+NS_INTERFACE_MAP_END
+
+class RequestContentJSInterruptRunnable final : public Runnable {
+ public:
+ explicit RequestContentJSInterruptRunnable(PProcessHangMonitorParent* aActor)
+ : Runnable("dom::RequestContentJSInterruptRunnable"),
+ mHangMonitorActor(aActor) {}
+
+ NS_IMETHOD Run() override {
+ MOZ_ASSERT(mHangMonitorActor);
+ Unused << mHangMonitorActor->SendRequestContentJSInterrupt();
+
+ return NS_OK;
+ }
+
+ private:
+ // The end-of-life of ContentParent::mHangMonitorActor is bound to
+ // ContentParent::ActorDestroy and then HangMonitorParent::Shutdown
+ // dispatches a shutdown runnable to this queue and waits for it to be
+ // executed. So the runnable needs not to care about keeping it alive,
+ // as it is surely dispatched earlier than the
+ // HangMonitorParent::ShutdownOnThread.
+ RefPtr<PProcessHangMonitorParent> mHangMonitorActor;
+};
+
+void ContentParent::SignalImpendingShutdownToContentJS() {
+ if (!mIsSignaledImpendingShutdown &&
+ !AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdown)) {
+ MaybeLogBlockShutdownDiagnostics(
+ this, "BlockShutdown: NotifyImpendingShutdown.", __FILE__, __LINE__);
+ NotifyImpendingShutdown();
+ mIsSignaledImpendingShutdown = true;
+ if (mHangMonitorActor &&
+ StaticPrefs::dom_abort_script_on_child_shutdown()) {
+ MaybeLogBlockShutdownDiagnostics(
+ this, "BlockShutdown: RequestContentJSInterrupt.", __FILE__,
+ __LINE__);
+ RefPtr<RequestContentJSInterruptRunnable> r =
+ new RequestContentJSInterruptRunnable(mHangMonitorActor);
+ ProcessHangMonitor::Get()->Dispatch(r.forget());
+ }
+ }
+}
+
+// Async shutdown blocker
+NS_IMETHODIMP
+ContentParent::BlockShutdown(nsIAsyncShutdownClient* aClient) {
+ if (!AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdown)) {
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ mBlockShutdownCalled = true;
+#endif
+ // Our real shutdown has not yet started. Just notify the impending
+ // shutdown and eventually cancel content JS.
+ SignalImpendingShutdownToContentJS();
+ // This will make our process unusable for normal content, so we need to
+ // ensure we won't get re-used by GetUsedBrowserProcess as we have not yet
+ // done MarkAsDead.
+ PreallocatedProcessManager::Erase(this);
+ StopRecyclingE10SOnly(false);
+
+ if (sQuitApplicationGrantedClient) {
+ Unused << sQuitApplicationGrantedClient->RemoveBlocker(this);
+ }
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ mBlockShutdownCalled = false;
+#endif
+ return NS_OK;
+ }
+
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ // We register two final shutdown blockers and both would call us, but if
+ // things go well we will unregister both as (delayed) reaction to the first
+ // call we get and thus never receive a second call. Thus we believe that we
+ // will get called only once except for quit-application-granted, which is
+ // handled above.
+ MOZ_ASSERT(!mBlockShutdownCalled);
+ mBlockShutdownCalled = true;
+#endif
+
+ if (CanSend()) {
+ MaybeLogBlockShutdownDiagnostics(this, "BlockShutdown: CanSend.", __FILE__,
+ __LINE__);
+
+ // Make sure that our process will get scheduled.
+ ProcessPriorityManager::SetProcessPriority(this,
+ PROCESS_PRIORITY_FOREGROUND);
+ // The normal shutdown sequence is to send a shutdown message
+ // to the child and then just wait for ActorDestroy which will
+ // cleanup everything and remove our blockers.
+ if (!ShutDownProcess(SEND_SHUTDOWN_MESSAGE)) {
+ KillHard("Failed to send Shutdown message. Destroying the process...");
+ return NS_OK;
+ }
+ } else if (IsLaunching()) {
+ MaybeLogBlockShutdownDiagnostics(
+ this, "BlockShutdown: !CanSend && IsLaunching.", __FILE__, __LINE__);
+
+ // If we get here while we are launching, we must wait for the child to
+ // be able to react on our commands. Mark this process as dead. This
+ // will make bail out LaunchSubprocessResolve and kick off the normal
+ // shutdown sequence.
+ MarkAsDead();
+ } else {
+ MOZ_ASSERT(IsDead());
+ if (!IsDead()) {
+ MaybeLogBlockShutdownDiagnostics(
+ this, "BlockShutdown: !!! !CanSend && !IsLaunching && !IsDead !!!",
+ __FILE__, __LINE__);
+ } else {
+ MaybeLogBlockShutdownDiagnostics(
+ this, "BlockShutdown: !CanSend && !IsLaunching && IsDead.", __FILE__,
+ __LINE__);
+ }
+ // Nothing left we can do. We must assume that we race with an ongoing
+ // process shutdown, such that we can expect our shutdown blockers to be
+ // removed normally.
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ContentParent::GetName(nsAString& aName) {
+ aName.AssignLiteral("ContentParent:");
+ aName.AppendPrintf(" id=%p", this);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ContentParent::GetState(nsIPropertyBag** aResult) {
+ auto props = MakeRefPtr<nsHashPropertyBag>();
+ props->SetPropertyAsACString(u"remoteTypePrefix"_ns,
+ RemoteTypePrefix(mRemoteType));
+ *aResult = props.forget().downcast<nsIWritablePropertyBag>().take();
+ return NS_OK;
+}
+
+static void InitShutdownClients() {
+ if (!sXPCOMShutdownClient) {
+ nsresult rv;
+ nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdownService();
+ if (!svc) {
+ return;
+ }
+
+ nsCOMPtr<nsIAsyncShutdownClient> client;
+ // TODO: It seems as if getPhase from AsyncShutdown.sys.mjs does not check
+ // if we are beyond our phase already. See bug 1762840.
+ if (!AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMWillShutdown)) {
+ rv = svc->GetXpcomWillShutdown(getter_AddRefs(client));
+ if (NS_SUCCEEDED(rv)) {
+ sXPCOMShutdownClient = client.forget();
+ ClearOnShutdown(&sXPCOMShutdownClient);
+ }
+ }
+ if (!AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdown)) {
+ rv = svc->GetProfileBeforeChange(getter_AddRefs(client));
+ if (NS_SUCCEEDED(rv)) {
+ sProfileBeforeChangeClient = client.forget();
+ ClearOnShutdown(&sProfileBeforeChangeClient);
+ }
+ }
+ // TODO: ShutdownPhase::AppShutdownConfirmed is not mapping to
+ // QuitApplicationGranted, see bug 1762840 comment 4.
+ if (!AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
+ rv = svc->GetQuitApplicationGranted(getter_AddRefs(client));
+ if (NS_SUCCEEDED(rv)) {
+ sQuitApplicationGrantedClient = client.forget();
+ ClearOnShutdown(&sQuitApplicationGrantedClient);
+ }
+ }
+ }
+}
+
+void ContentParent::AddShutdownBlockers() {
+ InitShutdownClients();
+ MOZ_ASSERT(sXPCOMShutdownClient);
+ MOZ_ASSERT(sProfileBeforeChangeClient);
+
+ if (sXPCOMShutdownClient) {
+ sXPCOMShutdownClient->AddBlocker(
+ this, NS_LITERAL_STRING_FROM_CSTRING(__FILE__), __LINE__, u""_ns);
+ }
+ if (sProfileBeforeChangeClient) {
+ sProfileBeforeChangeClient->AddBlocker(
+ this, NS_LITERAL_STRING_FROM_CSTRING(__FILE__), __LINE__, u""_ns);
+ }
+ if (sQuitApplicationGrantedClient) {
+ sQuitApplicationGrantedClient->AddBlocker(
+ this, NS_LITERAL_STRING_FROM_CSTRING(__FILE__), __LINE__, u""_ns);
+ }
+}
+
+void ContentParent::RemoveShutdownBlockers() {
+ MOZ_ASSERT(sXPCOMShutdownClient);
+ MOZ_ASSERT(sProfileBeforeChangeClient);
+
+ MaybeLogBlockShutdownDiagnostics(this, "RemoveShutdownBlockers", __FILE__,
+ __LINE__);
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ mBlockShutdownCalled = false;
+#endif
+
+ if (sXPCOMShutdownClient) {
+ Unused << sXPCOMShutdownClient->RemoveBlocker(this);
+ }
+ if (sProfileBeforeChangeClient) {
+ Unused << sProfileBeforeChangeClient->RemoveBlocker(this);
+ }
+ if (sQuitApplicationGrantedClient) {
+ Unused << sQuitApplicationGrantedClient->RemoveBlocker(this);
+ }
+}
+
+NS_IMETHODIMP
+ContentParent::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData) {
+ if (IsDead() || !mSubprocess) {
+ return NS_OK;
+ }
+
+ if (!strcmp(aTopic, "nsPref:changed")) {
+ // We know prefs are ASCII here.
+ NS_LossyConvertUTF16toASCII strData(aData);
+
+ Pref pref(strData, /* isLocked */ false,
+ /* isSanitized */ false, Nothing(), Nothing());
+
+ Preferences::GetPreference(&pref, GeckoProcessType_Content,
+ GetRemoteType());
+
+ // This check is a bit of a hack. We want to avoid sending excessive
+ // preference updates to subprocesses for performance reasons, but we
+ // currently don't have a great mechanism for doing so. (See Bug 1819714)
+ // We're going to hijack the sanitization mechanism to accomplish our goal
+ // but it imposes the following complications:
+ // 1) It doesn't avoid sending anything to other (non-web-content)
+ // subprocesses so we're not getting any perf gains there
+ // 2) It confuses the subprocesses w.r.t. sanitization. The point of
+ // sending a preference update of a sanitized preference is so that
+ // content process knows when it's asked to resolve a sanitized
+ // preference, and it can send telemetry and/or crash. With this
+ // change, a sanitized pref that is created during the browser session
+ // will not be sent to the content process, and therefore the content
+ // process won't know it should telemetry/crash on access - it'll just
+ // silently fail to resolve it. After browser restart, the sanitized
+ // pref will be populated into the content process via the shared pref
+ // map and _then_ if it is accessed, the content process will crash.
+ // We're seeing good telemetry/crash rates right now, so we're okay with
+ // this limitation.
+ if (pref.isSanitized()) {
+ return NS_OK;
+ }
+
+ if (IsInitialized()) {
+ MOZ_ASSERT(mQueuedPrefs.IsEmpty());
+ if (!SendPreferenceUpdate(pref)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ } else {
+ MOZ_ASSERT(!IsDead());
+ mQueuedPrefs.AppendElement(pref);
+ }
+
+ return NS_OK;
+ }
+
+ if (!IsAlive()) {
+ return NS_OK;
+ }
+
+ // listening for memory pressure event
+ if (!strcmp(aTopic, "memory-pressure")) {
+ Unused << SendFlushMemory(nsDependentString(aData));
+ } else if (!strcmp(aTopic, "application-background")) {
+ Unused << SendApplicationBackground();
+ } else if (!strcmp(aTopic, "application-foreground")) {
+ Unused << SendApplicationForeground();
+ } else if (!strcmp(aTopic, NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC)) {
+ NS_ConvertUTF16toUTF8 dataStr(aData);
+ const char* offline = dataStr.get();
+ if (!SendSetOffline(!strcmp(offline, "true"))) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ } else if (!strcmp(aTopic, NS_IPC_IOSERVICE_SET_CONNECTIVITY_TOPIC)) {
+ if (!SendSetConnectivity(u"true"_ns.Equals(aData))) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ } else if (!strcmp(aTopic, NS_IPC_CAPTIVE_PORTAL_SET_STATE)) {
+ nsCOMPtr<nsICaptivePortalService> cps = do_QueryInterface(aSubject);
+ MOZ_ASSERT(cps, "Should QI to a captive portal service");
+ if (!cps) {
+ return NS_ERROR_FAILURE;
+ }
+ int32_t state;
+ cps->GetState(&state);
+ if (!SendSetCaptivePortalState(state)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ }
+ // listening for alert notifications
+ else if (!strcmp(aTopic, "alertfinished") ||
+ !strcmp(aTopic, "alertclickcallback") ||
+ !strcmp(aTopic, "alertshow") ||
+ !strcmp(aTopic, "alertdisablecallback") ||
+ !strcmp(aTopic, "alertsettingscallback")) {
+ if (!SendNotifyAlertsObserver(nsDependentCString(aTopic),
+ nsDependentString(aData)))
+ return NS_ERROR_NOT_AVAILABLE;
+ } else if (!strcmp(aTopic, "child-gc-request")) {
+ Unused << SendGarbageCollect();
+ } else if (!strcmp(aTopic, "child-cc-request")) {
+ Unused << SendCycleCollect();
+ } else if (!strcmp(aTopic, "child-mmu-request")) {
+ Unused << SendMinimizeMemoryUsage();
+ } else if (!strcmp(aTopic, "child-ghost-request")) {
+ Unused << SendUnlinkGhosts();
+ } else if (!strcmp(aTopic, "last-pb-context-exited")) {
+ Unused << SendLastPrivateDocShellDestroyed();
+ }
+#ifdef ACCESSIBILITY
+ else if (aData && !strcmp(aTopic, "a11y-init-or-shutdown")) {
+ if (*aData == '1') {
+ // Make sure accessibility is running in content process when
+ // accessibility gets initiated in chrome process.
+ Unused << SendActivateA11y();
+ } else {
+ // If possible, shut down accessibility in content process when
+ // accessibility gets shutdown in chrome process.
+ Unused << SendShutdownA11y();
+ }
+ }
+#endif
+ else if (!strcmp(aTopic, "cacheservice:empty-cache")) {
+ Unused << SendNotifyEmptyHTTPCache();
+ } else if (!strcmp(aTopic, "intl:app-locales-changed")) {
+ nsTArray<nsCString> appLocales;
+ LocaleService::GetInstance()->GetAppLocalesAsBCP47(appLocales);
+ Unused << SendUpdateAppLocales(appLocales);
+ } else if (!strcmp(aTopic, "intl:requested-locales-changed")) {
+ nsTArray<nsCString> requestedLocales;
+ LocaleService::GetInstance()->GetRequestedLocales(requestedLocales);
+ Unused << SendUpdateRequestedLocales(requestedLocales);
+ } else if (!strcmp(aTopic, "cookie-changed") ||
+ !strcmp(aTopic, "private-cookie-changed")) {
+ MOZ_ASSERT(aSubject, "cookie changed notification must have subject.");
+ nsCOMPtr<nsICookieNotification> notification = do_QueryInterface(aSubject);
+ MOZ_ASSERT(notification,
+ "cookie changed notification must have nsICookieNotification.");
+ nsICookieNotification::Action action = notification->GetAction();
+
+ PNeckoParent* neckoParent = LoneManagedOrNullAsserts(ManagedPNeckoParent());
+ if (!neckoParent) {
+ return NS_OK;
+ }
+ PCookieServiceParent* csParent =
+ LoneManagedOrNullAsserts(neckoParent->ManagedPCookieServiceParent());
+ if (!csParent) {
+ return NS_OK;
+ }
+ auto* cs = static_cast<CookieServiceParent*>(csParent);
+ if (action == nsICookieNotification::COOKIES_BATCH_DELETED) {
+ nsCOMPtr<nsIArray> cookieList;
+ DebugOnly<nsresult> rv =
+ notification->GetBatchDeletedCookies(getter_AddRefs(cookieList));
+ NS_ASSERTION(NS_SUCCEEDED(rv) && cookieList, "couldn't get cookie list");
+ cs->RemoveBatchDeletedCookies(cookieList);
+ return NS_OK;
+ }
+
+ if (action == nsICookieNotification::ALL_COOKIES_CLEARED) {
+ cs->RemoveAll();
+ return NS_OK;
+ }
+
+ // Do not push these cookie updates to the same process they originated
+ // from.
+ if (cs->ProcessingCookie()) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsICookie> xpcCookie;
+ DebugOnly<nsresult> rv = notification->GetCookie(getter_AddRefs(xpcCookie));
+ NS_ASSERTION(NS_SUCCEEDED(rv) && xpcCookie, "couldn't get cookie");
+
+ // only broadcast the cookie change to content processes that need it
+ const Cookie& cookie = xpcCookie->AsCookie();
+
+ // do not send cookie if content process does not have similar cookie
+ if (!cs->ContentProcessHasCookie(cookie)) {
+ return NS_OK;
+ }
+
+ if (action == nsICookieNotification::COOKIE_DELETED) {
+ cs->RemoveCookie(cookie);
+ } else if (action == nsICookieNotification::COOKIE_ADDED ||
+ action == nsICookieNotification::COOKIE_CHANGED) {
+ cs->AddCookie(cookie);
+ }
+ } else if (!strcmp(aTopic, NS_NETWORK_LINK_TYPE_TOPIC)) {
+ UpdateNetworkLinkType();
+ } else if (!strcmp(aTopic, "network:socket-process-crashed")) {
+ Unused << SendSocketProcessCrashed();
+ } else if (!strcmp(aTopic, DEFAULT_TIMEZONE_CHANGED_OBSERVER_TOPIC)) {
+ Unused << SendSystemTimezoneChanged();
+ } else if (!strcmp(aTopic, NS_NETWORK_TRR_MODE_CHANGED_TOPIC)) {
+ nsCOMPtr<nsIDNSService> dns = do_GetService(NS_DNSSERVICE_CONTRACTID);
+ nsIDNSService::ResolverMode mode;
+ dns->GetCurrentTrrMode(&mode);
+ Unused << SendSetTRRMode(mode, static_cast<nsIDNSService::ResolverMode>(
+ StaticPrefs::network_trr_mode()));
+ }
+
+ return NS_OK;
+}
+
+void ContentParent::UpdateNetworkLinkType() {
+ nsresult rv;
+ nsCOMPtr<nsINetworkLinkService> nls =
+ do_GetService(NS_NETWORK_LINK_SERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ uint32_t linkType = nsINetworkLinkService::LINK_TYPE_UNKNOWN;
+ rv = nls->GetLinkType(&linkType);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+
+ Unused << SendNetworkLinkTypeChange(linkType);
+}
+
+NS_IMETHODIMP
+ContentParent::GetInterface(const nsIID& aIID, void** aResult) {
+ NS_ENSURE_ARG_POINTER(aResult);
+
+ if (aIID.Equals(NS_GET_IID(nsIMessageSender))) {
+ nsCOMPtr<nsIMessageSender> mm = GetMessageManager();
+ mm.forget(aResult);
+ return NS_OK;
+ }
+
+ return NS_NOINTERFACE;
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvInitBackground(
+ Endpoint<PBackgroundStarterParent>&& aEndpoint) {
+ if (!BackgroundParent::AllocStarter(this, std::move(aEndpoint))) {
+ NS_WARNING("BackgroundParent::Alloc failed");
+ }
+
+ return IPC_OK();
+}
+
+bool ContentParent::CanOpenBrowser(const IPCTabContext& aContext) {
+ // (PopupIPCTabContext lets the child process prove that it has access to
+ // the app it's trying to open.)
+ // On e10s we also allow UnsafeTabContext to allow service workers to open
+ // windows. This is enforced in MaybeInvalidTabContext.
+ if (aContext.type() != IPCTabContext::TPopupIPCTabContext) {
+ MOZ_CRASH_UNLESS_FUZZING(
+ "Unexpected IPCTabContext type. Aborting AllocPBrowserParent.");
+ return false;
+ }
+
+ if (aContext.type() == IPCTabContext::TPopupIPCTabContext) {
+ const PopupIPCTabContext& popupContext = aContext.get_PopupIPCTabContext();
+
+ auto opener = BrowserParent::GetFrom(popupContext.opener().AsParent());
+ if (!opener) {
+ MOZ_CRASH_UNLESS_FUZZING(
+ "Got null opener from child; aborting AllocPBrowserParent.");
+ return false;
+ }
+ }
+
+ MaybeInvalidTabContext tc(aContext);
+ if (!tc.IsValid()) {
+ NS_ERROR(nsPrintfCString("Child passed us an invalid TabContext. (%s) "
+ "Aborting AllocPBrowserParent.",
+ tc.GetInvalidReason())
+ .get());
+ return false;
+ }
+
+ return true;
+}
+
+static bool CloneIsLegal(ContentParent* aCp, CanonicalBrowsingContext& aSource,
+ CanonicalBrowsingContext& aTarget) {
+ // Source and target must be in the same BCG
+ if (NS_WARN_IF(aSource.Group() != aTarget.Group())) {
+ return false;
+ }
+ // The source and target must be in different toplevel <browser>s
+ if (NS_WARN_IF(aSource.Top() == aTarget.Top())) {
+ return false;
+ }
+
+ // Neither source nor target must be toplevel.
+ if (NS_WARN_IF(aSource.IsTop()) || NS_WARN_IF(aTarget.IsTop())) {
+ return false;
+ }
+
+ // Both should be embedded by the same process.
+ auto* sourceEmbedder = aSource.GetParentWindowContext();
+ if (NS_WARN_IF(!sourceEmbedder) ||
+ NS_WARN_IF(sourceEmbedder->GetContentParent() != aCp)) {
+ return false;
+ }
+
+ auto* targetEmbedder = aTarget.GetParentWindowContext();
+ if (NS_WARN_IF(!targetEmbedder) ||
+ NS_WARN_IF(targetEmbedder->GetContentParent() != aCp)) {
+ return false;
+ }
+
+ // All seems sane.
+ return true;
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvCloneDocumentTreeInto(
+ const MaybeDiscarded<BrowsingContext>& aSource,
+ const MaybeDiscarded<BrowsingContext>& aTarget, PrintData&& aPrintData) {
+ if (aSource.IsNullOrDiscarded() || aTarget.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+
+ if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
+ // All existing processes have potentially been slated for removal already,
+ // such that any subsequent call to GetNewOrUsedLaunchingBrowserProcess
+ // (normally supposed to find an existing process here) will try to create
+ // a new process (but fail) that nobody would ever really use. Let's avoid
+ // this together with the expensive CloneDocumentTreeInto operation.
+ return IPC_OK();
+ }
+
+ auto* source = aSource.get_canonical();
+ auto* target = aTarget.get_canonical();
+
+ if (!CloneIsLegal(this, *source, *target)) {
+ return IPC_FAIL(this, "Illegal subframe clone");
+ }
+
+ ContentParent* cp = source->GetContentParent();
+ if (NS_WARN_IF(!cp)) {
+ return IPC_OK();
+ }
+
+ if (NS_WARN_IF(cp->GetRemoteType() == GetRemoteType())) {
+ // Wanted to switch to a target browsing context that's already local again.
+ // See bug 1676996 for how this can happen.
+ //
+ // Dropping the switch on the floor seems fine for this case, though we
+ // could also try to clone the local document.
+ //
+ // If the remote type matches & it's in the same group (which was confirmed
+ // by CloneIsLegal), it must be the exact same process.
+ MOZ_DIAGNOSTIC_ASSERT(cp == this);
+ return IPC_OK();
+ }
+
+ target->CloneDocumentTreeInto(source, cp->GetRemoteType(),
+ std::move(aPrintData));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvUpdateRemotePrintSettings(
+ const MaybeDiscarded<BrowsingContext>& aTarget, PrintData&& aPrintData) {
+ if (aTarget.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+
+ auto* target = aTarget.get_canonical();
+ auto* bp = target->GetBrowserParent();
+ if (NS_WARN_IF(!bp)) {
+ return IPC_OK();
+ }
+
+ Unused << bp->SendUpdateRemotePrintSettings(std::move(aPrintData));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvConstructPopupBrowser(
+ ManagedEndpoint<PBrowserParent>&& aBrowserEp,
+ ManagedEndpoint<PWindowGlobalParent>&& aWindowEp, const TabId& aTabId,
+ const IPCTabContext& aContext, const WindowGlobalInit& aInitialWindowInit,
+ const uint32_t& aChromeFlags) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ if (!CanOpenBrowser(aContext)) {
+ return IPC_FAIL(this, "CanOpenBrowser Failed");
+ }
+
+ RefPtr<CanonicalBrowsingContext> browsingContext =
+ CanonicalBrowsingContext::Get(
+ aInitialWindowInit.context().mBrowsingContextId);
+ if (!browsingContext || browsingContext->IsDiscarded()) {
+ return IPC_FAIL(this, "Null or discarded initial BrowsingContext");
+ }
+ if (!aInitialWindowInit.principal()) {
+ return IPC_FAIL(this, "Cannot create without valid initial principal");
+ }
+
+ if (!ValidatePrincipal(aInitialWindowInit.principal())) {
+ LogAndAssertFailedPrincipalValidationInfo(aInitialWindowInit.principal(),
+ __func__);
+ }
+
+ if (browsingContext->GetBrowserParent()) {
+ return IPC_FAIL(this, "BrowsingContext already has a BrowserParent");
+ }
+
+ uint32_t chromeFlags = aChromeFlags;
+ TabId openerTabId(0);
+ ContentParentId openerCpId(0);
+ if (aContext.type() == IPCTabContext::TPopupIPCTabContext) {
+ // CanOpenBrowser has ensured that the IPCTabContext is of
+ // type PopupIPCTabContext, and that the opener BrowserParent is
+ // reachable.
+ const PopupIPCTabContext& popupContext = aContext.get_PopupIPCTabContext();
+ auto opener = BrowserParent::GetFrom(popupContext.opener().AsParent());
+ openerTabId = opener->GetTabId();
+ openerCpId = opener->Manager()->ChildID();
+
+ // We must ensure that the private browsing and remoteness flags
+ // match those of the opener.
+ nsCOMPtr<nsILoadContext> loadContext = opener->GetLoadContext();
+ if (!loadContext) {
+ return IPC_FAIL(this, "Missing Opener LoadContext");
+ }
+ if (loadContext->UsePrivateBrowsing()) {
+ chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW;
+ }
+ if (loadContext->UseRemoteSubframes()) {
+ chromeFlags |= nsIWebBrowserChrome::CHROME_FISSION_WINDOW;
+ }
+ }
+
+ // And because we're allocating a remote browser, of course the
+ // window is remote.
+ chromeFlags |= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW;
+
+ if (NS_WARN_IF(!browsingContext->IsOwnedByProcess(ChildID()))) {
+ return IPC_FAIL(this, "BrowsingContext Owned by Incorrect Process!");
+ }
+
+ MaybeInvalidTabContext tc(aContext);
+ MOZ_ASSERT(tc.IsValid());
+
+ RefPtr<WindowGlobalParent> initialWindow =
+ WindowGlobalParent::CreateDisconnected(aInitialWindowInit);
+ if (!initialWindow) {
+ return IPC_FAIL(this, "Failed to create WindowGlobalParent");
+ }
+
+ auto parent = MakeRefPtr<BrowserParent>(this, aTabId, tc.GetTabContext(),
+ browsingContext, chromeFlags);
+
+ // Bind the created BrowserParent to IPC to actually link the actor.
+ if (NS_WARN_IF(!BindPBrowserEndpoint(std::move(aBrowserEp), parent))) {
+ return IPC_FAIL(this, "BindPBrowserEndpoint failed");
+ }
+
+ // XXX: Why are we checking these requirements? It seems we should register
+ // the created frame unconditionally?
+ if (openerTabId > 0) {
+ // The creation of PBrowser was triggered from content process through
+ // window.open().
+ // We need to register remote frame with the child generated tab id.
+ auto* cpm = ContentProcessManager::GetSingleton();
+ if (!cpm || !cpm->RegisterRemoteFrame(parent)) {
+ return IPC_FAIL(this, "RegisterRemoteFrame Failed");
+ }
+ }
+
+ if (NS_WARN_IF(!parent->BindPWindowGlobalEndpoint(std::move(aWindowEp),
+ initialWindow))) {
+ return IPC_FAIL(this, "BindPWindowGlobalEndpoint failed");
+ }
+
+ browsingContext->SetCurrentBrowserParent(parent);
+
+ initialWindow->Init();
+
+ // When enabling input event prioritization, input events may preempt other
+ // normal priority IPC messages. To prevent the input events preempt
+ // PBrowserConstructor, we use an IPC 'RemoteIsReadyToHandleInputEvents' to
+ // notify parent that BrowserChild is created. In this case, PBrowser is
+ // initiated from content so that we can set BrowserParent as ready to handle
+ // input
+ parent->SetReadyToHandleInputEvents();
+ return IPC_OK();
+}
+
+mozilla::PRemoteSpellcheckEngineParent*
+ContentParent::AllocPRemoteSpellcheckEngineParent() {
+ mozilla::RemoteSpellcheckEngineParent* parent =
+ new mozilla::RemoteSpellcheckEngineParent();
+ return parent;
+}
+
+bool ContentParent::DeallocPRemoteSpellcheckEngineParent(
+ PRemoteSpellcheckEngineParent* parent) {
+ delete parent;
+ return true;
+}
+
+/* static */
+void ContentParent::SendShutdownTimerCallback(nsITimer* aTimer,
+ void* aClosure) {
+ auto* self = static_cast<ContentParent*>(aClosure);
+ self->AsyncSendShutDownMessage();
+}
+
+/* static */
+void ContentParent::ForceKillTimerCallback(nsITimer* aTimer, void* aClosure) {
+ // We don't want to time out the content process during XPCShell tests. This
+ // is the easiest way to ensure that.
+ if (PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR")) {
+ return;
+ }
+
+ auto* self = static_cast<ContentParent*>(aClosure);
+ self->KillHard("ShutDownKill");
+}
+
+void ContentParent::GeneratePairedMinidump(const char* aReason) {
+ // We're about to kill the child process associated with this content.
+ // Something has gone wrong to get us here, so we generate a minidump
+ // of the parent and child for submission to the crash server unless we're
+ // already shutting down.
+ if (mCrashReporter &&
+ !AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed) &&
+ StaticPrefs::dom_ipc_tabs_createKillHardCrashReports_AtStartup()) {
+ // GeneratePairedMinidump creates two minidumps for us - the main
+ // one is for the content process we're about to kill, and the other
+ // one is for the main browser process. That second one is the extra
+ // minidump tagging along, so we have to tell the crash reporter that
+ // it exists and is being appended.
+ nsAutoCString additionalDumps("browser");
+ mCrashReporter->AddAnnotation(
+ CrashReporter::Annotation::additional_minidumps, additionalDumps);
+ nsDependentCString reason(aReason);
+ mCrashReporter->AddAnnotation(CrashReporter::Annotation::ipc_channel_error,
+ reason);
+
+ // Generate the report and insert into the queue for submittal.
+ if (mCrashReporter->GenerateMinidumpAndPair(this, "browser"_ns)) {
+ mCrashReporter->FinalizeCrashReport();
+ mCreatedPairedMinidumps = true;
+ }
+ }
+}
+
+void ContentParent::HandleOrphanedMinidump(nsString* aDumpId) {
+ if (CrashReporter::FinalizeOrphanedMinidump(
+ OtherPid(), GeckoProcessType_Content, aDumpId)) {
+ CrashReporterHost::RecordCrash(GeckoProcessType_Content,
+ nsICrashService::CRASH_TYPE_CRASH, *aDumpId);
+ } else {
+ NS_WARNING(nsPrintfCString("content process pid = %" PRIPID
+ " crashed without leaving a minidump behind",
+ OtherPid())
+ .get());
+ }
+}
+
+// WARNING: aReason appears in telemetry, so any new value passed in requires
+// data review.
+void ContentParent::KillHard(const char* aReason) {
+ AUTO_PROFILER_LABEL("ContentParent::KillHard", OTHER);
+
+ // On Windows, calling KillHard multiple times causes problems - the
+ // process handle becomes invalid on the first call, causing a second call
+ // to crash our process - more details in bug 890840.
+ if (mCalledKillHard) {
+ return;
+ }
+ mCalledKillHard = true;
+ if (mSendShutdownTimer) {
+ mSendShutdownTimer->Cancel();
+ mSendShutdownTimer = nullptr;
+ }
+ if (mForceKillTimer) {
+ mForceKillTimer->Cancel();
+ mForceKillTimer = nullptr;
+ }
+
+ RemoveShutdownBlockers();
+ nsCString reason = nsDependentCString(aReason);
+
+ // If we find mIsNotifiedShutdownSuccess there is no reason to blame this
+ // content process, most probably our parent process is just slow in
+ // processing its own main thread queue.
+ if (!mIsNotifiedShutdownSuccess) {
+ GeneratePairedMinidump(aReason);
+ } else {
+ reason = nsDependentCString("KillHard after IsNotifiedShutdownSuccess.");
+ }
+ Telemetry::Accumulate(Telemetry::SUBPROCESS_KILL_HARD, reason, 1);
+
+ ProcessHandle otherProcessHandle;
+ if (!base::OpenProcessHandle(OtherPid(), &otherProcessHandle)) {
+ NS_ERROR("Failed to open child process when attempting kill.");
+ if (CanSend()) {
+ GetIPCChannel()->InduceConnectionError();
+ }
+ return;
+ }
+
+ if (!KillProcess(otherProcessHandle, base::PROCESS_END_KILLED_BY_USER)) {
+ if (mCrashReporter) {
+ mCrashReporter->DeleteCrashReport();
+ }
+ NS_WARNING("failed to kill subprocess!");
+ }
+
+ if (mSubprocess) {
+ MOZ_LOG(
+ ContentParent::GetLog(), LogLevel::Verbose,
+ ("KillHard Subprocess(%s): ContentParent %p mSubprocess %p handle "
+ "%" PRIuPTR,
+ aReason, this, mSubprocess,
+ mSubprocess ? (uintptr_t)mSubprocess->GetChildProcessHandle() : -1));
+ mSubprocess->SetAlreadyDead();
+ }
+
+ // After we've killed the remote process, also ensure we induce a connection
+ // error in the IPC channel to immediately stop all IPC communication on this
+ // channel.
+ if (CanSend()) {
+ GetIPCChannel()->InduceConnectionError();
+ }
+
+ // EnsureProcessTerminated has responsibilty for closing otherProcessHandle.
+ XRE_GetIOMessageLoop()->PostTask(
+ NewRunnableFunction("EnsureProcessTerminatedRunnable",
+ &ProcessWatcher::EnsureProcessTerminated,
+ otherProcessHandle, /*force=*/true));
+}
+
+void ContentParent::FriendlyName(nsAString& aName, bool aAnonymize) {
+ aName.Truncate();
+ if (mIsForBrowser) {
+ aName.AssignLiteral("Browser");
+ } else if (aAnonymize) {
+ aName.AssignLiteral("<anonymized-name>");
+ } else {
+ aName.AssignLiteral("???");
+ }
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvInitCrashReporter(
+ const NativeThreadId& aThreadId) {
+ mCrashReporter =
+ MakeUnique<CrashReporterHost>(GeckoProcessType_Content, aThreadId);
+
+ return IPC_OK();
+}
+
+hal_sandbox::PHalParent* ContentParent::AllocPHalParent() {
+ return hal_sandbox::CreateHalParent();
+}
+
+bool ContentParent::DeallocPHalParent(hal_sandbox::PHalParent* aHal) {
+ delete aHal;
+ return true;
+}
+
+devtools::PHeapSnapshotTempFileHelperParent*
+ContentParent::AllocPHeapSnapshotTempFileHelperParent() {
+ return devtools::HeapSnapshotTempFileHelperParent::Create();
+}
+
+bool ContentParent::DeallocPHeapSnapshotTempFileHelperParent(
+ devtools::PHeapSnapshotTempFileHelperParent* aHeapSnapshotHelper) {
+ delete aHeapSnapshotHelper;
+ return true;
+}
+
+bool ContentParent::SendRequestMemoryReport(
+ const uint32_t& aGeneration, const bool& aAnonymize,
+ const bool& aMinimizeMemoryUsage, const Maybe<FileDescriptor>& aDMDFile) {
+ // This automatically cancels the previous request.
+ mMemoryReportRequest = MakeUnique<MemoryReportRequestHost>(aGeneration);
+ // If we run the callback in response to a reply, then by definition |this|
+ // is still alive, so the ref pointer is redundant, but it seems easier
+ // to hold a strong reference than to worry about that.
+ RefPtr<ContentParent> self(this);
+ PContentParent::SendRequestMemoryReport(
+ aGeneration, aAnonymize, aMinimizeMemoryUsage, aDMDFile,
+ [&, self](const uint32_t& aGeneration2) {
+ if (self->mMemoryReportRequest) {
+ self->mMemoryReportRequest->Finish(aGeneration2);
+ self->mMemoryReportRequest = nullptr;
+ }
+ },
+ [&, self](mozilla::ipc::ResponseRejectReason) {
+ self->mMemoryReportRequest = nullptr;
+ });
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvAddMemoryReport(
+ const MemoryReport& aReport) {
+ if (mMemoryReportRequest) {
+ mMemoryReportRequest->RecvReport(aReport);
+ }
+ return IPC_OK();
+}
+
+PCycleCollectWithLogsParent* ContentParent::AllocPCycleCollectWithLogsParent(
+ const bool& aDumpAllTraces, const FileDescriptor& aGCLog,
+ const FileDescriptor& aCCLog) {
+ MOZ_CRASH("Don't call this; use ContentParent::CycleCollectWithLogs");
+}
+
+bool ContentParent::DeallocPCycleCollectWithLogsParent(
+ PCycleCollectWithLogsParent* aActor) {
+ delete aActor;
+ return true;
+}
+
+bool ContentParent::CycleCollectWithLogs(
+ bool aDumpAllTraces, nsICycleCollectorLogSink* aSink,
+ nsIDumpGCAndCCLogsCallback* aCallback) {
+ return CycleCollectWithLogsParent::AllocAndSendConstructor(
+ this, aDumpAllTraces, aSink, aCallback);
+}
+
+PScriptCacheParent* ContentParent::AllocPScriptCacheParent(
+ const FileDescOrError& cacheFile, const bool& wantCacheData) {
+ return new loader::ScriptCacheParent(wantCacheData);
+}
+
+bool ContentParent::DeallocPScriptCacheParent(PScriptCacheParent* cache) {
+ delete static_cast<loader::ScriptCacheParent*>(cache);
+ return true;
+}
+
+already_AddRefed<PNeckoParent> ContentParent::AllocPNeckoParent() {
+ RefPtr<NeckoParent> actor = new NeckoParent();
+ return actor.forget();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvInitStreamFilter(
+ const uint64_t& aChannelId, const nsAString& aAddonId,
+ InitStreamFilterResolver&& aResolver) {
+ extensions::StreamFilterParent::Create(this, aChannelId, aAddonId)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [aResolver](mozilla::ipc::Endpoint<PStreamFilterChild>&& aEndpoint) {
+ aResolver(std::move(aEndpoint));
+ },
+ [aResolver](bool aDummy) {
+ aResolver(mozilla::ipc::Endpoint<PStreamFilterChild>());
+ });
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvAddSecurityState(
+ const MaybeDiscarded<WindowContext>& aContext, uint32_t aStateFlags) {
+ if (aContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+
+ aContext.get()->AddSecurityState(aStateFlags);
+ return IPC_OK();
+}
+
+already_AddRefed<PExternalHelperAppParent>
+ContentParent::AllocPExternalHelperAppParent(
+ nsIURI* uri, const mozilla::net::LoadInfoArgs& aLoadInfoArgs,
+ const nsACString& aMimeContentType, const nsACString& aContentDisposition,
+ const uint32_t& aContentDispositionHint,
+ const nsAString& aContentDispositionFilename, const bool& aForceSave,
+ const int64_t& aContentLength, const bool& aWasFileChannel,
+ nsIURI* aReferrer, const MaybeDiscarded<BrowsingContext>& aContext,
+ const bool& aShouldCloseWindow) {
+ RefPtr<ExternalHelperAppParent> parent = new ExternalHelperAppParent(
+ uri, aContentLength, aWasFileChannel, aContentDisposition,
+ aContentDispositionHint, aContentDispositionFilename);
+ return parent.forget();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvPExternalHelperAppConstructor(
+ PExternalHelperAppParent* actor, nsIURI* uri,
+ const LoadInfoArgs& loadInfoArgs, const nsACString& aMimeContentType,
+ const nsACString& aContentDisposition,
+ const uint32_t& aContentDispositionHint,
+ const nsAString& aContentDispositionFilename, const bool& aForceSave,
+ const int64_t& aContentLength, const bool& aWasFileChannel,
+ nsIURI* aReferrer, const MaybeDiscarded<BrowsingContext>& aContext,
+ const bool& aShouldCloseWindow) {
+ BrowsingContext* context = aContext.IsDiscarded() ? nullptr : aContext.get();
+ if (!static_cast<ExternalHelperAppParent*>(actor)->Init(
+ loadInfoArgs, aMimeContentType, aForceSave, aReferrer, context,
+ aShouldCloseWindow)) {
+ return IPC_FAIL(this, "Init failed.");
+ }
+ return IPC_OK();
+}
+
+already_AddRefed<PHandlerServiceParent>
+ContentParent::AllocPHandlerServiceParent() {
+ RefPtr<HandlerServiceParent> actor = new HandlerServiceParent();
+ return actor.forget();
+}
+
+media::PMediaParent* ContentParent::AllocPMediaParent() {
+ return media::AllocPMediaParent();
+}
+
+bool ContentParent::DeallocPMediaParent(media::PMediaParent* aActor) {
+ return media::DeallocPMediaParent(aActor);
+}
+
+PBenchmarkStorageParent* ContentParent::AllocPBenchmarkStorageParent() {
+ return new BenchmarkStorageParent;
+}
+
+bool ContentParent::DeallocPBenchmarkStorageParent(
+ PBenchmarkStorageParent* aActor) {
+ delete aActor;
+ return true;
+}
+
+#ifdef MOZ_WEBSPEECH
+already_AddRefed<PSpeechSynthesisParent>
+ContentParent::AllocPSpeechSynthesisParent() {
+ if (!StaticPrefs::media_webspeech_synth_enabled()) {
+ return nullptr;
+ }
+ RefPtr<SpeechSynthesisParent> actor = new SpeechSynthesisParent();
+ return actor.forget();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvPSpeechSynthesisConstructor(
+ PSpeechSynthesisParent* aActor) {
+ if (!static_cast<SpeechSynthesisParent*>(aActor)->SendInit()) {
+ return IPC_FAIL(this, "SpeechSynthesisParent::SendInit failed.");
+ }
+ return IPC_OK();
+}
+#endif
+
+mozilla::ipc::IPCResult ContentParent::RecvStartVisitedQueries(
+ const nsTArray<RefPtr<nsIURI>>& aUris) {
+ nsCOMPtr<IHistory> history = components::History::Service();
+ if (!history) {
+ return IPC_OK();
+ }
+ for (const auto& uri : aUris) {
+ if (NS_WARN_IF(!uri)) {
+ continue;
+ }
+ history->ScheduleVisitedQuery(uri, this);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvSetURITitle(nsIURI* uri,
+ const nsAString& title) {
+ if (!uri) {
+ return IPC_FAIL(this, "uri must not be null.");
+ }
+ nsCOMPtr<IHistory> history = components::History::Service();
+ if (history) {
+ history->SetURITitle(uri, title);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvIsSecureURI(
+ nsIURI* aURI, const OriginAttributes& aOriginAttributes,
+ bool* aIsSecureURI) {
+ nsCOMPtr<nsISiteSecurityService> sss(do_GetService(NS_SSSERVICE_CONTRACTID));
+ if (!sss) {
+ return IPC_FAIL(this, "Failed to get nsISiteSecurityService.");
+ }
+ if (!aURI) {
+ return IPC_FAIL(this, "aURI must not be null.");
+ }
+ nsresult rv = sss->IsSecureURI(aURI, aOriginAttributes, aIsSecureURI);
+ if (NS_FAILED(rv)) {
+ return IPC_FAIL(this, "IsSecureURI failed.");
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvAccumulateMixedContentHSTS(
+ nsIURI* aURI, const bool& aActive,
+ const OriginAttributes& aOriginAttributes) {
+ if (!aURI) {
+ return IPC_FAIL(this, "aURI must not be null.");
+ }
+ nsMixedContentBlocker::AccumulateMixedContentHSTS(aURI, aActive,
+ aOriginAttributes);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvLoadURIExternal(
+ nsIURI* uri, nsIPrincipal* aTriggeringPrincipal,
+ nsIPrincipal* aRedirectPrincipal,
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ bool aWasExternallyTriggered, bool aHasValidUserGestureActivation) {
+ if (aContext.IsDiscarded()) {
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsIExternalProtocolService> extProtService(
+ do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID));
+ if (!extProtService) {
+ return IPC_OK();
+ }
+
+ if (!uri) {
+ return IPC_FAIL(this, "uri must not be null.");
+ }
+
+ BrowsingContext* bc = aContext.get();
+ extProtService->LoadURI(uri, aTriggeringPrincipal, aRedirectPrincipal, bc,
+ aWasExternallyTriggered,
+ aHasValidUserGestureActivation);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvExtProtocolChannelConnectParent(
+ const uint64_t& registrarId) {
+ nsresult rv;
+
+ // First get the real channel created before redirect on the parent.
+ nsCOMPtr<nsIChannel> channel;
+ rv = NS_LinkRedirectChannels(registrarId, nullptr, getter_AddRefs(channel));
+ NS_ENSURE_SUCCESS(rv, IPC_OK());
+
+ nsCOMPtr<nsIParentChannel> parent = do_QueryInterface(channel, &rv);
+ NS_ENSURE_SUCCESS(rv, IPC_OK());
+
+ // The channel itself is its own (faked) parent, link it.
+ rv = NS_LinkRedirectChannels(registrarId, parent, getter_AddRefs(channel));
+ NS_ENSURE_SUCCESS(rv, IPC_OK());
+
+ // Signal the parent channel that it's a redirect-to parent. This will
+ // make AsyncOpen on it do nothing (what we want).
+ // Yes, this is a bit of a hack, but I don't think it's necessary to invent
+ // a new interface just to set this flag on the channel.
+ parent->SetParentListener(nullptr);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvShowAlert(
+ nsIAlertNotification* aAlert) {
+ if (!aAlert) {
+ return IPC_FAIL(this, "aAlert must not be null.");
+ }
+ nsCOMPtr<nsIAlertsService> sysAlerts(components::Alerts::Service());
+ if (sysAlerts) {
+ sysAlerts->ShowAlert(aAlert, this);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvCloseAlert(const nsAString& aName,
+ bool aContextClosed) {
+ nsCOMPtr<nsIAlertsService> sysAlerts(components::Alerts::Service());
+ if (sysAlerts) {
+ sysAlerts->CloseAlert(aName, aContextClosed);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvDisableNotifications(
+ nsIPrincipal* aPrincipal) {
+ if (!aPrincipal) {
+ return IPC_FAIL(this, "No principal");
+ }
+
+ if (!ValidatePrincipal(aPrincipal)) {
+ LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __func__);
+ }
+ Unused << Notification::RemovePermission(aPrincipal);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvOpenNotificationSettings(
+ nsIPrincipal* aPrincipal) {
+ if (!aPrincipal) {
+ return IPC_FAIL(this, "No principal");
+ }
+
+ if (!ValidatePrincipal(aPrincipal)) {
+ LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __func__);
+ }
+ Unused << Notification::OpenSettings(aPrincipal);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvNotificationEvent(
+ const nsAString& aType, const NotificationEventData& aData) {
+ nsCOMPtr<nsIServiceWorkerManager> swm =
+ mozilla::components::ServiceWorkerManager::Service();
+ if (NS_WARN_IF(!swm)) {
+ // Probably shouldn't happen, but no need to crash the child process.
+ return IPC_OK();
+ }
+
+ if (aType.EqualsLiteral("click")) {
+ nsresult rv = swm->SendNotificationClickEvent(
+ aData.originSuffix(), aData.scope(), aData.ID(), aData.title(),
+ aData.dir(), aData.lang(), aData.body(), aData.tag(), aData.icon(),
+ aData.data(), aData.behavior());
+ Unused << NS_WARN_IF(NS_FAILED(rv));
+ } else {
+ MOZ_ASSERT(aType.EqualsLiteral("close"));
+ nsresult rv = swm->SendNotificationCloseEvent(
+ aData.originSuffix(), aData.scope(), aData.ID(), aData.title(),
+ aData.dir(), aData.lang(), aData.body(), aData.tag(), aData.icon(),
+ aData.data(), aData.behavior());
+ Unused << NS_WARN_IF(NS_FAILED(rv));
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvSyncMessage(
+ const nsAString& aMsg, const ClonedMessageData& aData,
+ nsTArray<StructuredCloneData>* aRetvals) {
+ AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING("ContentParent::RecvSyncMessage",
+ OTHER, aMsg);
+ MMPrinter::Print("ContentParent::RecvSyncMessage", aMsg, aData);
+
+ RefPtr<nsFrameMessageManager> ppm = mMessageManager;
+ if (ppm) {
+ ipc::StructuredCloneData data;
+ ipc::UnpackClonedMessageData(aData, data);
+
+ ppm->ReceiveMessage(ppm, nullptr, aMsg, true, &data, aRetvals,
+ IgnoreErrors());
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvAsyncMessage(
+ const nsAString& aMsg, const ClonedMessageData& aData) {
+ AUTO_PROFILER_LABEL_DYNAMIC_LOSSY_NSSTRING("ContentParent::RecvAsyncMessage",
+ OTHER, aMsg);
+ MMPrinter::Print("ContentParent::RecvAsyncMessage", aMsg, aData);
+
+ RefPtr<nsFrameMessageManager> ppm = mMessageManager;
+ if (ppm) {
+ ipc::StructuredCloneData data;
+ ipc::UnpackClonedMessageData(aData, data);
+
+ ppm->ReceiveMessage(ppm, nullptr, aMsg, false, &data, nullptr,
+ IgnoreErrors());
+ }
+ return IPC_OK();
+}
+
+MOZ_CAN_RUN_SCRIPT
+static int32_t AddGeolocationListener(
+ nsIDOMGeoPositionCallback* watcher,
+ nsIDOMGeoPositionErrorCallback* errorCallBack, bool highAccuracy) {
+ RefPtr<Geolocation> geo = Geolocation::NonWindowSingleton();
+
+ UniquePtr<PositionOptions> options = MakeUnique<PositionOptions>();
+ options->mTimeout = 0;
+ options->mMaximumAge = 0;
+ options->mEnableHighAccuracy = highAccuracy;
+ return geo->WatchPosition(watcher, errorCallBack, std::move(options));
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvAddGeolocationListener(
+ const bool& aHighAccuracy) {
+ // To ensure no geolocation updates are skipped, we always force the
+ // creation of a new listener.
+ RecvRemoveGeolocationListener();
+ mGeolocationWatchID = AddGeolocationListener(this, this, aHighAccuracy);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvRemoveGeolocationListener() {
+ if (mGeolocationWatchID != -1) {
+ RefPtr<Geolocation> geo = Geolocation::NonWindowSingleton();
+ if (geo) {
+ geo->ClearWatch(mGeolocationWatchID);
+ }
+ mGeolocationWatchID = -1;
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvSetGeolocationHigherAccuracy(
+ const bool& aEnable) {
+ // This should never be called without a listener already present,
+ // so this check allows us to forgo securing privileges.
+ if (mGeolocationWatchID != -1) {
+ RecvRemoveGeolocationListener();
+ mGeolocationWatchID = AddGeolocationListener(this, this, aEnable);
+ }
+ return IPC_OK();
+}
+
+NS_IMETHODIMP
+ContentParent::HandleEvent(nsIDOMGeoPosition* postion) {
+ Unused << SendGeolocationUpdate(postion);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ContentParent::HandleEvent(GeolocationPositionError* positionError) {
+ Unused << SendGeolocationError(positionError->Code());
+ return NS_OK;
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvConsoleMessage(
+ const nsAString& aMessage) {
+ nsresult rv;
+ nsCOMPtr<nsIConsoleService> consoleService =
+ do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv)) {
+ RefPtr<nsConsoleMessage> msg(new nsConsoleMessage(aMessage));
+ msg->SetIsForwardedFromContentProcess(true);
+ consoleService->LogMessageWithMode(msg, nsIConsoleService::SuppressLog);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvReportFrameTimingData(
+ const LoadInfoArgs& loadInfoArgs, const nsAString& entryName,
+ const nsAString& initiatorType, UniquePtr<PerformanceTimingData>&& aData) {
+ if (!aData) {
+ return IPC_FAIL(this, "aData should not be null");
+ }
+
+ RefPtr<WindowGlobalParent> parent =
+ WindowGlobalParent::GetByInnerWindowId(loadInfoArgs.innerWindowID());
+ if (!parent || !parent->GetContentParent()) {
+ return IPC_OK();
+ }
+
+ MOZ_ASSERT(parent->GetContentParent() != this,
+ "No need to bounce around if in the same process");
+
+ Unused << parent->GetContentParent()->SendReportFrameTimingData(
+ loadInfoArgs, entryName, initiatorType, std::move(aData));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvScriptError(
+ const nsAString& aMessage, const nsAString& aSourceName,
+ const nsAString& aSourceLine, const uint32_t& aLineNumber,
+ const uint32_t& aColNumber, const uint32_t& aFlags,
+ const nsACString& aCategory, const bool& aFromPrivateWindow,
+ const uint64_t& aInnerWindowId, const bool& aFromChromeContext) {
+ return RecvScriptErrorInternal(aMessage, aSourceName, aSourceLine,
+ aLineNumber, aColNumber, aFlags, aCategory,
+ aFromPrivateWindow, aFromChromeContext);
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvScriptErrorWithStack(
+ const nsAString& aMessage, const nsAString& aSourceName,
+ const nsAString& aSourceLine, const uint32_t& aLineNumber,
+ const uint32_t& aColNumber, const uint32_t& aFlags,
+ const nsACString& aCategory, const bool& aFromPrivateWindow,
+ const bool& aFromChromeContext, const ClonedMessageData& aFrame) {
+ return RecvScriptErrorInternal(
+ aMessage, aSourceName, aSourceLine, aLineNumber, aColNumber, aFlags,
+ aCategory, aFromPrivateWindow, aFromChromeContext, &aFrame);
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvScriptErrorInternal(
+ const nsAString& aMessage, const nsAString& aSourceName,
+ const nsAString& aSourceLine, const uint32_t& aLineNumber,
+ const uint32_t& aColNumber, const uint32_t& aFlags,
+ const nsACString& aCategory, const bool& aFromPrivateWindow,
+ const bool& aFromChromeContext, const ClonedMessageData* aStack) {
+ nsresult rv;
+ nsCOMPtr<nsIConsoleService> consoleService =
+ do_GetService(NS_CONSOLESERVICE_CONTRACTID, &rv);
+ if (NS_FAILED(rv)) {
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsIScriptError> msg;
+
+ if (aStack) {
+ StructuredCloneData data;
+ UnpackClonedMessageData(*aStack, data);
+
+ AutoJSAPI jsapi;
+ if (NS_WARN_IF(!jsapi.Init(xpc::PrivilegedJunkScope()))) {
+ MOZ_CRASH();
+ }
+ JSContext* cx = jsapi.cx();
+
+ JS::Rooted<JS::Value> stack(cx);
+ ErrorResult rv;
+ data.Read(cx, &stack, rv);
+ if (rv.Failed() || !stack.isObject()) {
+ rv.SuppressException();
+ return IPC_OK();
+ }
+
+ JS::Rooted<JSObject*> stackObj(cx, &stack.toObject());
+ if (!JS::IsUnwrappedSavedFrame(stackObj)) {
+ return IPC_FAIL(this, "Unexpected object");
+ }
+
+ JS::Rooted<JSObject*> stackGlobal(cx, JS::GetNonCCWObjectGlobal(stackObj));
+ msg = new nsScriptErrorWithStack(JS::NothingHandleValue, stackObj,
+ stackGlobal);
+ } else {
+ msg = new nsScriptError();
+ }
+
+ rv = msg->Init(aMessage, aSourceName, aSourceLine, aLineNumber, aColNumber,
+ aFlags, aCategory, aFromPrivateWindow, aFromChromeContext);
+ if (NS_FAILED(rv)) return IPC_OK();
+
+ msg->SetIsForwardedFromContentProcess(true);
+
+ consoleService->LogMessageWithMode(msg, nsIConsoleService::SuppressLog);
+ return IPC_OK();
+}
+
+bool ContentParent::DoLoadMessageManagerScript(const nsAString& aURL,
+ bool aRunInGlobalScope) {
+ MOZ_ASSERT(!aRunInGlobalScope);
+ return SendLoadProcessScript(aURL);
+}
+
+nsresult ContentParent::DoSendAsyncMessage(const nsAString& aMessage,
+ StructuredCloneData& aHelper) {
+ ClonedMessageData data;
+ if (!BuildClonedMessageData(aHelper, data)) {
+ return NS_ERROR_DOM_DATA_CLONE_ERR;
+ }
+ if (!SendAsyncMessage(aMessage, data)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ return NS_OK;
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvCopyFavicon(
+ nsIURI* aOldURI, nsIURI* aNewURI, const bool& aInPrivateBrowsing) {
+ if (!aOldURI) {
+ return IPC_FAIL(this, "aOldURI should not be null");
+ }
+ if (!aNewURI) {
+ return IPC_FAIL(this, "aNewURI should not be null");
+ }
+
+ nsDocShell::CopyFavicon(aOldURI, aNewURI, aInPrivateBrowsing);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvFindImageText(
+ IPCImage&& aImage, nsTArray<nsCString>&& aLanguages,
+ FindImageTextResolver&& aResolver) {
+ if (!TextRecognition::IsSupported() ||
+ !Preferences::GetBool("dom.text-recognition.enabled")) {
+ return IPC_FAIL(this, "Text recognition not available.");
+ }
+
+ RefPtr<DataSourceSurface> surf =
+ nsContentUtils::IPCImageToSurface(std::move(aImage));
+ if (!surf) {
+ aResolver(TextRecognitionResultOrError("Failed to read image"_ns));
+ return IPC_OK();
+ }
+ TextRecognition::FindText(*surf, aLanguages)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [resolver = std::move(aResolver)](
+ TextRecognition::NativePromise::ResolveOrRejectValue&& aValue) {
+ if (aValue.IsResolve()) {
+ resolver(TextRecognitionResultOrError(aValue.ResolveValue()));
+ } else {
+ resolver(TextRecognitionResultOrError(aValue.RejectValue()));
+ }
+ });
+ return IPC_OK();
+}
+
+bool ContentParent::ShouldContinueFromReplyTimeout() {
+ RefPtr<ProcessHangMonitor> monitor = ProcessHangMonitor::Get();
+ return !monitor || !monitor->ShouldTimeOutCPOWs();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvAddIdleObserver(
+ const uint64_t& aObserver, const uint32_t& aIdleTimeInS) {
+ nsresult rv;
+ nsCOMPtr<nsIUserIdleService> idleService =
+ do_GetService("@mozilla.org/widget/useridleservice;1", &rv);
+ NS_ENSURE_SUCCESS(rv, IPC_FAIL(this, "Failed to get UserIdleService."));
+
+ RefPtr<ParentIdleListener> listener =
+ new ParentIdleListener(this, aObserver, aIdleTimeInS);
+ rv = idleService->AddIdleObserver(listener, aIdleTimeInS);
+ NS_ENSURE_SUCCESS(rv, IPC_FAIL(this, "AddIdleObserver failed."));
+ mIdleListeners.AppendElement(listener);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvRemoveIdleObserver(
+ const uint64_t& aObserver, const uint32_t& aIdleTimeInS) {
+ RefPtr<ParentIdleListener> listener;
+ for (int32_t i = mIdleListeners.Length() - 1; i >= 0; --i) {
+ listener = static_cast<ParentIdleListener*>(mIdleListeners[i].get());
+ if (listener->mObserver == aObserver && listener->mTime == aIdleTimeInS) {
+ nsresult rv;
+ nsCOMPtr<nsIUserIdleService> idleService =
+ do_GetService("@mozilla.org/widget/useridleservice;1", &rv);
+ NS_ENSURE_SUCCESS(rv, IPC_FAIL(this, "Failed to get UserIdleService."));
+ idleService->RemoveIdleObserver(listener, aIdleTimeInS);
+ mIdleListeners.RemoveElementAt(i);
+ break;
+ }
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvBackUpXResources(
+ const FileDescriptor& aXSocketFd) {
+#ifndef MOZ_X11
+ MOZ_CRASH("This message only makes sense on X11 platforms");
+#else
+ MOZ_ASSERT(!mChildXSocketFdDup, "Already backed up X resources??");
+ if (aXSocketFd.IsValid()) {
+ mChildXSocketFdDup = aXSocketFd.ClonePlatformHandle();
+ }
+#endif
+ return IPC_OK();
+}
+
+class AnonymousTemporaryFileRequestor final : public Runnable {
+ public:
+ AnonymousTemporaryFileRequestor(ContentParent* aCP, const uint64_t& aID)
+ : Runnable("dom::AnonymousTemporaryFileRequestor"),
+ mCP(aCP),
+ mID(aID),
+ mRv(NS_OK),
+ mPRFD(nullptr) {}
+
+ NS_IMETHOD Run() override {
+ if (NS_IsMainThread()) {
+ FileDescOrError result;
+ if (NS_WARN_IF(NS_FAILED(mRv))) {
+ // Returning false will kill the child process; instead
+ // propagate the error and let the child handle it.
+ result = mRv;
+ } else {
+ result = FileDescriptor(FileDescriptor::PlatformHandleType(
+ PR_FileDesc2NativeHandle(mPRFD)));
+ // The FileDescriptor object owns a duplicate of the file handle; we
+ // must close the original (and clean up the NSPR descriptor).
+ PR_Close(mPRFD);
+ }
+ Unused << mCP->SendProvideAnonymousTemporaryFile(mID, result);
+ // It's important to release this reference while wr're on the main
+ // thread!
+ mCP = nullptr;
+ } else {
+ mRv = NS_OpenAnonymousTemporaryFile(&mPRFD);
+ NS_DispatchToMainThread(this);
+ }
+ return NS_OK;
+ }
+
+ private:
+ RefPtr<ContentParent> mCP;
+ uint64_t mID;
+ nsresult mRv;
+ PRFileDesc* mPRFD;
+};
+
+mozilla::ipc::IPCResult ContentParent::RecvRequestAnonymousTemporaryFile(
+ const uint64_t& aID) {
+ // Make sure to send a callback to the child if we bail out early.
+ nsresult rv = NS_OK;
+ RefPtr<ContentParent> self(this);
+ auto autoNotifyChildOnError = MakeScopeExit([&, self]() {
+ if (NS_FAILED(rv)) {
+ FileDescOrError result(rv);
+ Unused << self->SendProvideAnonymousTemporaryFile(aID, result);
+ }
+ });
+
+ // We use a helper runnable to open the anonymous temporary file on the IO
+ // thread. The same runnable will call us back on the main thread when the
+ // file has been opened.
+ nsCOMPtr<nsIEventTarget> target =
+ do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
+ if (!target) {
+ return IPC_OK();
+ }
+
+ rv = target->Dispatch(new AnonymousTemporaryFileRequestor(this, aID),
+ NS_DISPATCH_NORMAL);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return IPC_OK();
+ }
+
+ rv = NS_OK;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvCreateAudioIPCConnection(
+ CreateAudioIPCConnectionResolver&& aResolver) {
+ FileDescriptor fd = CubebUtils::CreateAudioIPCConnection();
+ FileDescOrError result;
+ if (fd.IsValid()) {
+ result = fd;
+ } else {
+ result = NS_ERROR_FAILURE;
+ }
+ aResolver(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::GetIPCTransferableData(
+ nsIDragSession* aSession, BrowserParent* aParent,
+ nsTArray<IPCTransferableData>& aIPCTransferables) {
+ RefPtr<DataTransfer> transfer = aSession->GetDataTransfer();
+ if (!transfer) {
+ // Pass eDrop to get DataTransfer with external
+ // drag formats cached.
+ transfer = new DataTransfer(nullptr, eDrop, true, -1);
+ aSession->SetDataTransfer(transfer);
+ }
+ // Note, even though this fills the DataTransfer object with
+ // external data, the data is usually transfered over IPC lazily when
+ // needed.
+ transfer->FillAllExternalData();
+ nsCOMPtr<nsILoadContext> lc = aParent ? aParent->GetLoadContext() : nullptr;
+ nsCOMPtr<nsIArray> transferables = transfer->GetTransferables(lc);
+ nsContentUtils::TransferablesToIPCTransferableDatas(
+ transferables, aIPCTransferables, false, this);
+}
+
+void ContentParent::MaybeInvokeDragSession(BrowserParent* aParent,
+ EventMessage aMessage) {
+ // 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) {
+ return;
+ }
+
+ if (dragService->MaybeAddChildProcess(this)) {
+ nsCOMPtr<nsIDragSession> session;
+ dragService->GetCurrentSession(getter_AddRefs(session));
+ if (session) {
+ // We need to send transferable data to child process.
+ nsTArray<IPCTransferableData> ipcTransferables;
+ GetIPCTransferableData(session, aParent, ipcTransferables);
+ uint32_t action;
+ session->GetDragAction(&action);
+
+ RefPtr<WindowContext> sourceWC;
+ session->GetSourceWindowContext(getter_AddRefs(sourceWC));
+ RefPtr<WindowContext> sourceTopWC;
+ session->GetSourceTopWindowContext(getter_AddRefs(sourceTopWC));
+ mozilla::Unused << SendInvokeDragSession(
+ sourceWC, sourceTopWC, std::move(ipcTransferables), action);
+ }
+ return;
+ }
+
+ if (dragService->MustUpdateDataTransfer(aMessage)) {
+ nsCOMPtr<nsIDragSession> session;
+ dragService->GetCurrentSession(getter_AddRefs(session));
+ if (session) {
+ // We need to send transferable data to child process.
+ nsTArray<IPCTransferableData> ipcTransferables;
+ GetIPCTransferableData(session, aParent, ipcTransferables);
+ mozilla::Unused << SendUpdateDragSession(std::move(ipcTransferables),
+ aMessage);
+ }
+ }
+}
+
+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;
+}
+
+already_AddRefed<PWebBrowserPersistDocumentParent>
+ContentParent::AllocPWebBrowserPersistDocumentParent(
+ PBrowserParent* aBrowser, const MaybeDiscarded<BrowsingContext>& aContext) {
+ return MakeAndAddRef<WebBrowserPersistDocumentParent>();
+}
+
+mozilla::ipc::IPCResult ContentParent::CommonCreateWindow(
+ PBrowserParent* aThisTab, BrowsingContext& aParent, bool aSetOpener,
+ const uint32_t& aChromeFlags, const bool& aCalledFromJS,
+ const bool& aForPrinting, const bool& aForWindowDotPrint,
+ nsIURI* aURIToLoad, const nsACString& aFeatures,
+ const UserActivation::Modifiers& aModifiers,
+ BrowserParent* aNextRemoteBrowser, const nsAString& aName,
+ nsresult& aResult, nsCOMPtr<nsIRemoteTab>& aNewRemoteTab,
+ bool* aWindowIsNew, int32_t& aOpenLocation,
+ nsIPrincipal* aTriggeringPrincipal, nsIReferrerInfo* aReferrerInfo,
+ bool aLoadURI, nsIContentSecurityPolicy* aCsp,
+ const OriginAttributes& aOriginAttributes) {
+ // The content process should never be in charge of computing whether or
+ // not a window should be private - the parent will do that.
+ const uint32_t badFlags = nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW |
+ nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW |
+ nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME;
+ if (!!(aChromeFlags & badFlags)) {
+ return IPC_FAIL(this, "Forbidden aChromeFlags passed");
+ }
+
+ RefPtr<nsOpenWindowInfo> openInfo = new nsOpenWindowInfo();
+ openInfo->mForceNoOpener = !aSetOpener;
+ openInfo->mParent = &aParent;
+ openInfo->mIsRemote = true;
+ openInfo->mIsForPrinting = aForPrinting;
+ openInfo->mIsForWindowDotPrint = aForWindowDotPrint;
+ openInfo->mNextRemoteBrowser = aNextRemoteBrowser;
+ openInfo->mOriginAttributes = aOriginAttributes;
+
+ MOZ_ASSERT_IF(aForWindowDotPrint, aForPrinting);
+
+ RefPtr<BrowserParent> topParent = BrowserParent::GetFrom(aThisTab);
+ while (topParent && topParent->GetBrowserBridgeParent()) {
+ topParent = topParent->GetBrowserBridgeParent()->Manager();
+ }
+ RefPtr<BrowserHost> thisBrowserHost =
+ topParent ? topParent->GetBrowserHost() : nullptr;
+ MOZ_ASSERT_IF(topParent, thisBrowserHost);
+ RefPtr<BrowsingContext> topBC =
+ topParent ? topParent->GetBrowsingContext() : nullptr;
+ MOZ_ASSERT_IF(topParent, topBC);
+
+ // The content process should have set its remote and fission flags correctly.
+ if (topBC) {
+ if ((!!(aChromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW) !=
+ topBC->UseRemoteTabs()) ||
+ (!!(aChromeFlags & nsIWebBrowserChrome::CHROME_FISSION_WINDOW) !=
+ topBC->UseRemoteSubframes())) {
+ return IPC_FAIL(this, "Unexpected aChromeFlags passed");
+ }
+
+ if (!aOriginAttributes.EqualsIgnoringFPD(topBC->OriginAttributesRef())) {
+ return IPC_FAIL(this, "Passed-in OriginAttributes does not match opener");
+ }
+ }
+
+ nsCOMPtr<nsIContent> frame;
+ if (topParent) {
+ frame = topParent->GetOwnerElement();
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> outerWin;
+ if (frame) {
+ outerWin = frame->OwnerDoc()->GetWindow();
+
+ // If our chrome window is in the process of closing, don't try to open a
+ // new tab in it.
+ if (outerWin && outerWin->Closed()) {
+ outerWin = nullptr;
+ }
+ }
+
+ nsCOMPtr<nsIBrowserDOMWindow> browserDOMWin;
+ if (topParent) {
+ browserDOMWin = topParent->GetBrowserDOMWindow();
+ }
+
+ // If we haven't found a chrome window to open in, just use the most recently
+ // opened one.
+ if (!outerWin) {
+ outerWin = nsContentUtils::GetMostRecentNonPBWindow();
+ if (NS_WARN_IF(!outerWin)) {
+ aResult = NS_ERROR_FAILURE;
+ return IPC_OK();
+ }
+
+ if (nsGlobalWindowOuter::Cast(outerWin)->IsChromeWindow()) {
+ browserDOMWin =
+ nsGlobalWindowOuter::Cast(outerWin)->GetBrowserDOMWindow();
+ }
+ }
+
+ aOpenLocation = nsWindowWatcher::GetWindowOpenLocation(
+ outerWin, aChromeFlags, aModifiers, aCalledFromJS, aForPrinting);
+
+ MOZ_ASSERT(aOpenLocation == nsIBrowserDOMWindow::OPEN_NEWTAB ||
+ aOpenLocation == nsIBrowserDOMWindow::OPEN_NEWTAB_BACKGROUND ||
+ 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_NEWTAB_BACKGROUND ||
+ aOpenLocation == nsIBrowserDOMWindow::OPEN_PRINT_BROWSER) {
+ RefPtr<Element> openerElement = do_QueryObject(frame);
+
+ nsCOMPtr<nsIOpenURIInFrameParams> params =
+ new nsOpenURIInFrameParams(openInfo, openerElement);
+ params->SetReferrerInfo(aReferrerInfo);
+ MOZ_ASSERT(aTriggeringPrincipal, "need a valid triggeringPrincipal");
+ params->SetTriggeringPrincipal(aTriggeringPrincipal);
+ params->SetCsp(aCsp);
+
+ RefPtr<Element> el;
+
+ if (aLoadURI) {
+ aResult = browserDOMWin->OpenURIInFrame(aURIToLoad, params, aOpenLocation,
+ nsIBrowserDOMWindow::OPEN_NEW,
+ aName, getter_AddRefs(el));
+ } else {
+ aResult = browserDOMWin->CreateContentWindowInFrame(
+ aURIToLoad, params, aOpenLocation, nsIBrowserDOMWindow::OPEN_NEW,
+ aName, getter_AddRefs(el));
+ }
+ RefPtr<nsFrameLoaderOwner> frameLoaderOwner = do_QueryObject(el);
+ if (NS_SUCCEEDED(aResult) && frameLoaderOwner) {
+ RefPtr<nsFrameLoader> frameLoader = frameLoaderOwner->GetFrameLoader();
+ if (frameLoader) {
+ aNewRemoteTab = frameLoader->GetRemoteTab();
+ // At this point, it's possible the inserted frameloader hasn't gone
+ // through layout yet. To ensure that the dimensions that we send down
+ // when telling the frameloader to display will be correct (instead of
+ // falling back to a 10x10 default), we force layout if necessary to get
+ // the most up-to-date dimensions. See bug 1358712 for details.
+ frameLoader->ForceLayoutIfNecessary();
+ }
+ } else if (NS_SUCCEEDED(aResult) && !frameLoaderOwner) {
+ // Fall through to the normal window opening code path when there is no
+ // window which we can open a new tab in.
+ aOpenLocation = nsIBrowserDOMWindow::OPEN_NEWWINDOW;
+ } else {
+ *aWindowIsNew = false;
+ }
+
+ // If we didn't retarget our window open into a new window, we should return
+ // now.
+ if (aOpenLocation != nsIBrowserDOMWindow::OPEN_NEWWINDOW) {
+ return IPC_OK();
+ }
+ }
+
+ nsCOMPtr<nsPIWindowWatcher> pwwatch =
+ do_GetService(NS_WINDOWWATCHER_CONTRACTID, &aResult);
+ if (NS_WARN_IF(NS_FAILED(aResult))) {
+ return IPC_OK();
+ }
+
+ WindowFeatures features;
+ features.Tokenize(aFeatures);
+
+ aResult = pwwatch->OpenWindowWithRemoteTab(
+ thisBrowserHost, features, aModifiers, aCalledFromJS, aParent.FullZoom(),
+ openInfo, getter_AddRefs(aNewRemoteTab));
+ if (NS_WARN_IF(NS_FAILED(aResult))) {
+ return IPC_OK();
+ }
+
+ MOZ_ASSERT(aNewRemoteTab);
+ RefPtr<BrowserHost> newBrowserHost = BrowserHost::GetFrom(aNewRemoteTab);
+ RefPtr<BrowserParent> newBrowserParent = newBrowserHost->GetActor();
+
+ // At this point, it's possible the inserted frameloader hasn't gone through
+ // layout yet. To ensure that the dimensions that we send down when telling
+ // the frameloader to display will be correct (instead of falling back to a
+ // 10x10 default), we force layout if necessary to get the most up-to-date
+ // dimensions. See bug 1358712 for details.
+ nsCOMPtr<Element> frameElement = newBrowserHost->GetOwnerElement();
+ MOZ_ASSERT(frameElement);
+ if (nsWindowWatcher::HaveSpecifiedSize(features)) {
+ // We want to flush the layout anyway because of the resize to the specified
+ // size. (Bug 1793605).
+ RefPtr<Document> chromeDoc = frameElement->OwnerDoc();
+ MOZ_ASSERT(chromeDoc);
+ chromeDoc->FlushPendingNotifications(FlushType::Layout);
+ } else {
+ RefPtr<nsFrameLoaderOwner> frameLoaderOwner = do_QueryObject(frameElement);
+ MOZ_ASSERT(frameLoaderOwner);
+ RefPtr<nsFrameLoader> frameLoader = frameLoaderOwner->GetFrameLoader();
+ MOZ_ASSERT(frameLoader);
+ frameLoader->ForceLayoutIfNecessary();
+ }
+
+ // If we were passed a name for the window which would override the default,
+ // we should send it down to the new tab.
+ if (nsContentUtils::IsOverridingWindowName(aName)) {
+ MOZ_ALWAYS_SUCCEEDS(newBrowserHost->GetBrowsingContext()->SetName(aName));
+ }
+
+ MOZ_ASSERT(newBrowserHost->GetBrowsingContext()->OriginAttributesRef() ==
+ aOriginAttributes);
+
+ if (aURIToLoad && aLoadURI) {
+ nsCOMPtr<mozIDOMWindowProxy> openerWindow;
+ if (aSetOpener && topParent) {
+ openerWindow = topParent->GetParentWindowOuter();
+ }
+ nsCOMPtr<nsIBrowserDOMWindow> newBrowserDOMWin =
+ newBrowserParent->GetBrowserDOMWindow();
+ if (NS_WARN_IF(!newBrowserDOMWin)) {
+ aResult = NS_ERROR_ABORT;
+ return IPC_OK();
+ }
+ RefPtr<BrowsingContext> bc;
+ aResult = newBrowserDOMWin->OpenURI(
+ aURIToLoad, openInfo, nsIBrowserDOMWindow::OPEN_CURRENTWINDOW,
+ nsIBrowserDOMWindow::OPEN_NEW, aTriggeringPrincipal, aCsp,
+ getter_AddRefs(bc));
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvCreateWindow(
+ PBrowserParent* aThisTab, const MaybeDiscarded<BrowsingContext>& aParent,
+ PBrowserParent* aNewTab, const uint32_t& aChromeFlags,
+ const bool& aCalledFromJS, const bool& aForPrinting,
+ const bool& aForPrintPreview, nsIURI* aURIToLoad,
+ const nsACString& aFeatures, const UserActivation::Modifiers& aModifiers,
+ 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, aModifiers, 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 ||
+ openLocation == nsIBrowserDOMWindow::OPEN_NEWTAB_BACKGROUND));
+
+ 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 UserActivation::Modifiers& aModifiers,
+ 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, aModifiers,
+ /* 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::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 uint32_t& aFamilyIndex,
+ const bool& aAlias, const uint32_t& aFaceIndex,
+ const gfxSparseBitSet& aMap) {
+ auto* fontList = gfxPlatformFontList::PlatformFontList();
+ MOZ_RELEASE_ASSERT(fontList, "gfxPlatformFontList not initialized?");
+ fontList->SetCharacterMap(aGeneration, aFamilyIndex, aAlias, aFaceIndex,
+ aMap);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvInitOtherFamilyNames(
+ const uint32_t& aGeneration, const bool& aDefer, bool* aLoaded) {
+ auto* fontList = gfxPlatformFontList::PlatformFontList();
+ MOZ_RELEASE_ASSERT(fontList, "gfxPlatformFontList not initialized?");
+ *aLoaded = fontList->InitOtherFamilyNames(aGeneration, aDefer);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvSetupFamilyCharMap(
+ const uint32_t& aGeneration, const uint32_t& aIndex, const bool& aAlias) {
+ auto* fontList = gfxPlatformFontList::PlatformFontList();
+ MOZ_RELEASE_ASSERT(fontList, "gfxPlatformFontList not initialized?");
+ fontList->SetupFamilyCharMap(aGeneration, aIndex, aAlias);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvStartCmapLoading(
+ const uint32_t& aGeneration, const uint32_t& aStartIndex) {
+ auto* fontList = gfxPlatformFontList::PlatformFontList();
+ MOZ_RELEASE_ASSERT(fontList, "gfxPlatformFontList not initialized?");
+ fontList->StartCmapLoading(aGeneration, aStartIndex);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvGetHyphDict(
+ nsIURI* aURI, base::SharedMemoryHandle* aOutHandle, uint32_t* aOutSize) {
+ if (!aURI) {
+ return IPC_FAIL(this, "aURI must not be null.");
+ }
+ nsHyphenationManager::Instance()->ShareHyphDictToProcess(
+ aURI, Pid(), aOutHandle, aOutSize);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvGraphicsError(
+ const nsACString& aError) {
+ if (gfx::LogForwarder* lf = gfx::Factory::GetLogForwarder()) {
+ std::stringstream message;
+ message << "CP+" << aError;
+ lf->UpdateStringsVector(message.str());
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvBeginDriverCrashGuard(
+ const uint32_t& aGuardType, bool* aOutCrashed) {
+ // Only one driver crash guard should be active at a time, per-process.
+ MOZ_ASSERT(!mDriverCrashGuard);
+
+ UniquePtr<gfx::DriverCrashGuard> guard;
+ switch (gfx::CrashGuardType(aGuardType)) {
+ case gfx::CrashGuardType::D3D11Layers:
+ guard = MakeUnique<gfx::D3D11LayersCrashGuard>(this);
+ break;
+ case gfx::CrashGuardType::GLContext:
+ guard = MakeUnique<gfx::GLContextCrashGuard>(this);
+ break;
+ case gfx::CrashGuardType::WMFVPXVideo:
+ guard = MakeUnique<gfx::WMFVPXVideoCrashGuard>(this);
+ break;
+ default:
+ return IPC_FAIL(this, "unknown crash guard type");
+ }
+
+ if (guard->Crashed()) {
+ *aOutCrashed = true;
+ return IPC_OK();
+ }
+
+ *aOutCrashed = false;
+ mDriverCrashGuard = std::move(guard);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvEndDriverCrashGuard(
+ const uint32_t& aGuardType) {
+ mDriverCrashGuard = nullptr;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvNotifyBenchmarkResult(
+ const nsAString& aCodecName, const uint32_t& aDecodeFPS)
+
+{
+ if (aCodecName.EqualsLiteral("VP9")) {
+ Preferences::SetUint(VP9Benchmark::sBenchmarkFpsPref, aDecodeFPS);
+ Preferences::SetUint(VP9Benchmark::sBenchmarkFpsVersionCheck,
+ VP9Benchmark::sBenchmarkVersionID);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvNotifyPushObservers(
+ const nsACString& aScope, nsIPrincipal* aPrincipal,
+ const nsAString& aMessageId) {
+ if (!aPrincipal) {
+ return IPC_FAIL(this, "No principal");
+ }
+
+ if (!ValidatePrincipal(aPrincipal)) {
+ LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __func__);
+ }
+ PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId, Nothing());
+ Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers()));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvNotifyPushObserversWithData(
+ const nsACString& aScope, nsIPrincipal* aPrincipal,
+ const nsAString& aMessageId, nsTArray<uint8_t>&& aData) {
+ if (!aPrincipal) {
+ return IPC_FAIL(this, "No principal");
+ }
+
+ if (!ValidatePrincipal(aPrincipal)) {
+ LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __func__);
+ }
+ PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId,
+ Some(std::move(aData)));
+ Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers()));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+ContentParent::RecvNotifyPushSubscriptionChangeObservers(
+ const nsACString& aScope, nsIPrincipal* aPrincipal) {
+ if (!aPrincipal) {
+ return IPC_FAIL(this, "No principal");
+ }
+
+ if (!ValidatePrincipal(aPrincipal)) {
+ LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __func__);
+ }
+ PushSubscriptionChangeDispatcher dispatcher(aScope, aPrincipal);
+ Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers()));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvPushError(const nsACString& aScope,
+ nsIPrincipal* aPrincipal,
+ const nsAString& aMessage,
+ const uint32_t& aFlags) {
+ if (!aPrincipal) {
+ return IPC_FAIL(this, "No principal");
+ }
+
+ if (!ValidatePrincipal(aPrincipal)) {
+ LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __func__);
+ }
+ PushErrorDispatcher dispatcher(aScope, aPrincipal, aMessage, aFlags);
+ Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers()));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+ContentParent::RecvNotifyPushSubscriptionModifiedObservers(
+ const nsACString& aScope, nsIPrincipal* aPrincipal) {
+ if (!aPrincipal) {
+ return IPC_FAIL(this, "No principal");
+ }
+
+ if (!ValidatePrincipal(aPrincipal)) {
+ LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __func__);
+ }
+ PushSubscriptionModifiedDispatcher dispatcher(aScope, aPrincipal);
+ Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObservers()));
+ return IPC_OK();
+}
+
+/* static */
+void ContentParent::BroadcastBlobURLRegistration(const nsACString& aURI,
+ BlobImpl* aBlobImpl,
+ nsIPrincipal* aPrincipal,
+ const nsCString& aPartitionKey,
+ ContentParent* aIgnoreThisCP) {
+ uint64_t originHash = ComputeLoadedOriginHash(aPrincipal);
+
+ bool toBeSent =
+ BlobURLProtocolHandler::IsBlobURLBroadcastPrincipal(aPrincipal);
+
+ nsCString uri(aURI);
+
+ for (auto* cp : AllProcesses(eLive)) {
+ if (cp != aIgnoreThisCP) {
+ if (!toBeSent && !cp->mLoadedOriginHashes.Contains(originHash)) {
+ continue;
+ }
+
+ nsresult rv = cp->TransmitPermissionsForPrincipal(aPrincipal);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ break;
+ }
+
+ IPCBlob ipcBlob;
+ rv = IPCBlobUtils::Serialize(aBlobImpl, ipcBlob);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ break;
+ }
+
+ Unused << cp->SendBlobURLRegistration(uri, ipcBlob, aPrincipal,
+ aPartitionKey);
+ }
+ }
+}
+
+/* static */
+void ContentParent::BroadcastBlobURLUnregistration(
+ const nsACString& aURI, nsIPrincipal* aPrincipal,
+ ContentParent* aIgnoreThisCP) {
+ uint64_t originHash = ComputeLoadedOriginHash(aPrincipal);
+
+ bool toBeSent =
+ BlobURLProtocolHandler::IsBlobURLBroadcastPrincipal(aPrincipal);
+
+ nsCString uri(aURI);
+
+ for (auto* cp : AllProcesses(eLive)) {
+ if (cp != aIgnoreThisCP &&
+ (toBeSent || cp->mLoadedOriginHashes.Contains(originHash))) {
+ Unused << cp->SendBlobURLUnregistration(uri);
+ }
+ }
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvStoreAndBroadcastBlobURLRegistration(
+ const nsACString& aURI, const IPCBlob& aBlob, nsIPrincipal* aPrincipal,
+ const nsCString& aPartitionKey) {
+ if (!aPrincipal) {
+ return IPC_FAIL(this, "No principal");
+ }
+
+ if (!ValidatePrincipal(aPrincipal, {ValidatePrincipalOptions::AllowSystem})) {
+ LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __func__);
+ }
+ RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(aBlob);
+ if (NS_WARN_IF(!blobImpl)) {
+ return IPC_FAIL(this, "Blob deserialization failed.");
+ }
+
+ BlobURLProtocolHandler::AddDataEntry(aURI, aPrincipal, aPartitionKey,
+ blobImpl);
+ BroadcastBlobURLRegistration(aURI, blobImpl, aPrincipal, aPartitionKey, this);
+
+ // We want to store this blobURL, so we can unregister it if the child
+ // crashes.
+ mBlobURLs.AppendElement(aURI);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+ContentParent::RecvUnstoreAndBroadcastBlobURLUnregistration(
+ const nsACString& aURI, nsIPrincipal* aPrincipal) {
+ if (!aPrincipal) {
+ return IPC_FAIL(this, "No principal");
+ }
+
+ if (!ValidatePrincipal(aPrincipal, {ValidatePrincipalOptions::AllowSystem})) {
+ LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __func__);
+ }
+ BlobURLProtocolHandler::RemoveDataEntry(aURI, false /* Don't broadcast */);
+ BroadcastBlobURLUnregistration(aURI, aPrincipal, this);
+ mBlobURLs.RemoveElement(aURI);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvGetFilesRequest(
+ const nsID& aUUID, const nsAString& aDirectoryPath,
+ const bool& aRecursiveFlag) {
+ MOZ_ASSERT(!mGetFilesPendingRequests.GetWeak(aUUID));
+
+ if (!mozilla::Preferences::GetBool("dom.filesystem.pathcheck.disabled",
+ false)) {
+ RefPtr<FileSystemSecurity> fss = FileSystemSecurity::Get();
+ if (!fss) {
+ return IPC_FAIL(this, "Failed to get FileSystemSecurity.");
+ }
+
+ if (!fss->ContentProcessHasAccessTo(ChildID(), aDirectoryPath)) {
+ return IPC_FAIL(this, "ContentProcessHasAccessTo failed.");
+ }
+ }
+
+ ErrorResult rv;
+ RefPtr<GetFilesHelper> helper = GetFilesHelperParent::Create(
+ aUUID, aDirectoryPath, aRecursiveFlag, this, rv);
+
+ if (NS_WARN_IF(rv.Failed())) {
+ if (!SendGetFilesResponse(aUUID,
+ GetFilesResponseFailure(rv.StealNSResult()))) {
+ return IPC_FAIL(this, "SendGetFilesResponse failed.");
+ }
+ return IPC_OK();
+ }
+
+ mGetFilesPendingRequests.InsertOrUpdate(aUUID, std::move(helper));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvDeleteGetFilesRequest(
+ const nsID& aUUID) {
+ mGetFilesPendingRequests.Remove(aUUID);
+ return IPC_OK();
+}
+
+void ContentParent::SendGetFilesResponseAndForget(
+ const nsID& aUUID, const GetFilesResponseResult& aResult) {
+ if (mGetFilesPendingRequests.Remove(aUUID)) {
+ Unused << SendGetFilesResponse(aUUID, aResult);
+ }
+}
+
+void ContentParent::PaintTabWhileInterruptingJS(BrowserParent* aBrowserParent) {
+ if (!mHangMonitorActor) {
+ return;
+ }
+ ProcessHangMonitor::PaintWhileInterruptingJS(mHangMonitorActor,
+ aBrowserParent);
+}
+
+void ContentParent::UnloadLayersWhileInterruptingJS(
+ BrowserParent* aBrowserParent) {
+ if (!mHangMonitorActor) {
+ return;
+ }
+ ProcessHangMonitor::UnloadLayersWhileInterruptingJS(mHangMonitorActor,
+ aBrowserParent);
+}
+
+void ContentParent::CancelContentJSExecutionIfRunning(
+ BrowserParent* aBrowserParent, nsIRemoteTab::NavigationType aNavigationType,
+ const CancelContentJSOptions& aCancelContentJSOptions) {
+ if (!mHangMonitorActor) {
+ return;
+ }
+
+ ProcessHangMonitor::CancelContentJSExecutionIfRunning(
+ mHangMonitorActor, aBrowserParent, aNavigationType,
+ aCancelContentJSOptions);
+}
+
+void ContentParent::SetMainThreadQoSPriority(
+ nsIThread::QoSPriority aQoSPriority) {
+ if (!mHangMonitorActor) {
+ return;
+ }
+
+ ProcessHangMonitor::SetMainThreadQoSPriority(mHangMonitorActor, aQoSPriority);
+}
+
+void ContentParent::UpdateCookieStatus(nsIChannel* aChannel) {
+ PNeckoParent* neckoParent = LoneManagedOrNullAsserts(ManagedPNeckoParent());
+ PCookieServiceParent* csParent =
+ LoneManagedOrNullAsserts(neckoParent->ManagedPCookieServiceParent());
+ if (csParent) {
+ auto* cs = static_cast<CookieServiceParent*>(csParent);
+ cs->TrackCookieLoad(aChannel);
+ }
+}
+
+nsresult ContentParent::AboutToLoadHttpFtpDocumentForChild(
+ nsIChannel* aChannel, bool* aShouldWaitForPermissionCookieUpdate) {
+ MOZ_ASSERT(aChannel);
+
+ if (aShouldWaitForPermissionCookieUpdate) {
+ *aShouldWaitForPermissionCookieUpdate = false;
+ }
+
+ nsresult rv;
+ bool isDocument = aChannel->IsDocument();
+ if (!isDocument) {
+ // We may be looking at a nsIHttpChannel which has isMainDocumentChannel set
+ // (e.g. the internal http channel for a view-source: load.).
+ nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
+ if (httpChannel) {
+ rv = httpChannel->GetIsMainDocumentChannel(&isDocument);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+ if (!isDocument) {
+ return NS_OK;
+ }
+
+ // Get the principal for the channel result, so that we can get the permission
+ // key for the document which will be created from this response.
+ nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
+ if (NS_WARN_IF(!ssm)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIPrincipal> principal;
+ nsCOMPtr<nsIPrincipal> partitionedPrincipal;
+ rv = ssm->GetChannelResultPrincipals(aChannel, getter_AddRefs(principal),
+ getter_AddRefs(partitionedPrincipal));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Let the caller know we're going to send main thread IPC for updating
+ // permisssions/cookies.
+ if (aShouldWaitForPermissionCookieUpdate) {
+ *aShouldWaitForPermissionCookieUpdate = true;
+ }
+
+ TransmitBlobURLsForPrincipal(principal);
+
+ // Tranmit permissions for both regular and partitioned principal so that the
+ // content process can get permissions for the partitioned principal. For
+ // example, the desk-notification permission for a partitioned service worker.
+ rv = TransmitPermissionsForPrincipal(principal);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = TransmitPermissionsForPrincipal(partitionedPrincipal);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsLoadFlags newLoadFlags;
+ aChannel->GetLoadFlags(&newLoadFlags);
+ if (newLoadFlags & nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE) {
+ UpdateCookieStatus(aChannel);
+ }
+
+ RefPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
+ RefPtr<BrowsingContext> browsingContext;
+ rv = loadInfo->GetTargetBrowsingContext(getter_AddRefs(browsingContext));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!NextGenLocalStorageEnabled()) {
+ return NS_OK;
+ }
+
+ if (principal->GetIsContentPrincipal()) {
+ nsCOMPtr<nsILocalStorageManager> lsm =
+ do_GetService("@mozilla.org/dom/localStorage-manager;1");
+ if (NS_WARN_IF(!lsm)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIPrincipal> storagePrincipal;
+ rv = ssm->GetChannelResultStoragePrincipal(
+ aChannel, getter_AddRefs(storagePrincipal));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RefPtr<Promise> dummy;
+ rv = lsm->Preload(storagePrincipal, nullptr, getter_AddRefs(dummy));
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Failed to preload local storage!");
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult ContentParent::TransmitPermissionsForPrincipal(
+ nsIPrincipal* aPrincipal) {
+ // Create the key, and send it down to the content process.
+ nsTArray<std::pair<nsCString, nsCString>> pairs =
+ PermissionManager::GetAllKeysForPrincipal(aPrincipal);
+ MOZ_ASSERT(pairs.Length() >= 1);
+ for (auto& pair : pairs) {
+ EnsurePermissionsByKey(pair.first, pair.second);
+ }
+
+ // We need to add the Site to the secondary keys of interest here.
+ // This allows site-scoped permission updates to propogate when the
+ // port is non-standard.
+ nsAutoCString siteKey;
+ nsresult rv =
+ PermissionManager::GetKeyForPrincipal(aPrincipal, false, true, siteKey);
+ if (NS_SUCCEEDED(rv) && !siteKey.IsEmpty()) {
+ mActiveSecondaryPermissionKeys.EnsureInserted(siteKey);
+ }
+
+ return NS_OK;
+}
+
+void ContentParent::TransmitBlobURLsForPrincipal(nsIPrincipal* aPrincipal) {
+ // If we're already broadcasting BlobURLs with this principal, we don't need
+ // to send them here.
+ if (BlobURLProtocolHandler::IsBlobURLBroadcastPrincipal(aPrincipal)) {
+ return;
+ }
+
+ // We shouldn't have any Blob URLs with expanded principals, so transmit URLs
+ // for each principal in the AllowList instead.
+ if (nsCOMPtr<nsIExpandedPrincipal> ep = do_QueryInterface(aPrincipal)) {
+ for (const auto& prin : ep->AllowList()) {
+ TransmitBlobURLsForPrincipal(prin);
+ }
+ return;
+ }
+
+ uint64_t originHash = ComputeLoadedOriginHash(aPrincipal);
+
+ if (!mLoadedOriginHashes.Contains(originHash)) {
+ mLoadedOriginHashes.AppendElement(originHash);
+
+ nsTArray<BlobURLRegistrationData> registrations;
+ BlobURLProtocolHandler::ForEachBlobURL(
+ [&](BlobImpl* aBlobImpl, nsIPrincipal* aBlobPrincipal,
+ const nsCString& aPartitionKey, const nsACString& aURI,
+ bool aRevoked) {
+ // This check uses `ComputeLoadedOriginHash` to compare, rather than
+ // doing the more accurate `Equals` check, as it needs to match the
+ // behaviour of the logic to broadcast new registrations.
+ if (originHash != ComputeLoadedOriginHash(aBlobPrincipal)) {
+ return true;
+ }
+
+ IPCBlob ipcBlob;
+ nsresult rv = IPCBlobUtils::Serialize(aBlobImpl, ipcBlob);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return false;
+ }
+
+ registrations.AppendElement(
+ BlobURLRegistrationData(nsCString(aURI), ipcBlob, aPrincipal,
+ nsCString(aPartitionKey), aRevoked));
+
+ rv = TransmitPermissionsForPrincipal(aBlobPrincipal);
+ Unused << NS_WARN_IF(NS_FAILED(rv));
+ return true;
+ });
+
+ if (!registrations.IsEmpty()) {
+ Unused << SendInitBlobURLs(registrations);
+ }
+ }
+}
+
+void ContentParent::TransmitBlobDataIfBlobURL(nsIURI* aURI) {
+ MOZ_ASSERT(aURI);
+
+ nsCOMPtr<nsIPrincipal> principal;
+ if (BlobURLProtocolHandler::GetBlobURLPrincipal(aURI,
+ getter_AddRefs(principal))) {
+ TransmitBlobURLsForPrincipal(principal);
+ }
+}
+
+void ContentParent::EnsurePermissionsByKey(const nsACString& aKey,
+ const nsACString& aOrigin) {
+ // NOTE: Make sure to initialize the permission manager before updating the
+ // mActivePermissionKeys list. If the permission manager is being initialized
+ // by this call to GetPermissionManager, and we've added the key to
+ // mActivePermissionKeys, then the permission manager will send down a
+ // SendAddPermission before receiving the SendSetPermissionsWithKey message.
+ RefPtr<PermissionManager> permManager = PermissionManager::GetInstance();
+ if (!permManager) {
+ return;
+ }
+
+ if (!mActivePermissionKeys.EnsureInserted(aKey)) {
+ return;
+ }
+
+ nsTArray<IPC::Permission> perms;
+ if (permManager->GetPermissionsFromOriginOrKey(aOrigin, aKey, perms)) {
+ Unused << SendSetPermissionsWithKey(aKey, perms);
+ }
+}
+
+bool ContentParent::NeedsPermissionsUpdate(
+ const nsACString& aPermissionKey) const {
+ return mActivePermissionKeys.Contains(aPermissionKey);
+}
+
+bool ContentParent::NeedsSecondaryKeyPermissionsUpdate(
+ const nsACString& aPermissionKey) const {
+ return mActiveSecondaryPermissionKeys.Contains(aPermissionKey);
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvAccumulateChildHistograms(
+ nsTArray<HistogramAccumulation>&& aAccumulations) {
+ TelemetryIPC::AccumulateChildHistograms(GetTelemetryProcessID(mRemoteType),
+ aAccumulations);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvAccumulateChildKeyedHistograms(
+ nsTArray<KeyedHistogramAccumulation>&& aAccumulations) {
+ TelemetryIPC::AccumulateChildKeyedHistograms(
+ GetTelemetryProcessID(mRemoteType), aAccumulations);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvUpdateChildScalars(
+ nsTArray<ScalarAction>&& aScalarActions) {
+ TelemetryIPC::UpdateChildScalars(GetTelemetryProcessID(mRemoteType),
+ aScalarActions);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvUpdateChildKeyedScalars(
+ nsTArray<KeyedScalarAction>&& aScalarActions) {
+ TelemetryIPC::UpdateChildKeyedScalars(GetTelemetryProcessID(mRemoteType),
+ aScalarActions);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvRecordChildEvents(
+ nsTArray<mozilla::Telemetry::ChildEventData>&& aEvents) {
+ TelemetryIPC::RecordChildEvents(GetTelemetryProcessID(mRemoteType), aEvents);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvRecordDiscardedData(
+ const mozilla::Telemetry::DiscardedData& aDiscardedData) {
+ TelemetryIPC::RecordDiscardedData(GetTelemetryProcessID(mRemoteType),
+ aDiscardedData);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvRecordPageLoadEvent(
+ const mozilla::glean::perf::PageLoadExtra& aPageLoadEventExtra) {
+ mozilla::glean::perf::page_load.Record(mozilla::Some(aPageLoadEventExtra));
+
+ // Send the PageLoadPing after every 30 page loads, or on startup.
+ if (++sPageLoadEventCounter >= 30) {
+ NS_SUCCEEDED(NS_DispatchToMainThreadQueue(
+ NS_NewRunnableFunction(
+ "PageLoadPingIdleTask",
+ [] { mozilla::glean_pings::Pageload.Submit("threshold"_ns); }),
+ EventQueuePriority::Idle));
+ sPageLoadEventCounter = 0;
+ }
+
+ return IPC_OK();
+}
+
+//////////////////////////////////////////////////////////////////
+// PURLClassifierParent
+
+PURLClassifierParent* ContentParent::AllocPURLClassifierParent(
+ nsIPrincipal* aPrincipal, bool* aSuccess) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ *aSuccess = true;
+ RefPtr<URLClassifierParent> actor = new URLClassifierParent();
+ return actor.forget().take();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvPURLClassifierConstructor(
+ PURLClassifierParent* aActor, nsIPrincipal* aPrincipal, bool* aSuccess) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aActor);
+ *aSuccess = false;
+
+ auto* actor = static_cast<URLClassifierParent*>(aActor);
+ nsCOMPtr<nsIPrincipal> principal(aPrincipal);
+ if (!principal) {
+ actor->ClassificationFailed();
+ return IPC_OK();
+ }
+ if (!ValidatePrincipal(aPrincipal)) {
+ LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __func__);
+ }
+ return actor->StartClassify(principal, aSuccess);
+}
+
+bool ContentParent::DeallocPURLClassifierParent(PURLClassifierParent* aActor) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aActor);
+
+ RefPtr<URLClassifierParent> actor =
+ dont_AddRef(static_cast<URLClassifierParent*>(aActor));
+ return true;
+}
+
+//////////////////////////////////////////////////////////////////
+// PURLClassifierLocalParent
+
+PURLClassifierLocalParent* ContentParent::AllocPURLClassifierLocalParent(
+ nsIURI* aURI, const nsTArray<IPCURLClassifierFeature>& aFeatures) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ RefPtr<URLClassifierLocalParent> actor = new URLClassifierLocalParent();
+ return actor.forget().take();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvPURLClassifierLocalConstructor(
+ PURLClassifierLocalParent* aActor, nsIURI* aURI,
+ nsTArray<IPCURLClassifierFeature>&& aFeatures) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aActor);
+
+ nsTArray<IPCURLClassifierFeature> features = std::move(aFeatures);
+
+ if (!aURI) {
+ return IPC_FAIL(this, "aURI should not be null");
+ }
+
+ auto* actor = static_cast<URLClassifierLocalParent*>(aActor);
+ return actor->StartClassify(aURI, features);
+}
+
+bool ContentParent::DeallocPURLClassifierLocalParent(
+ PURLClassifierLocalParent* aActor) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aActor);
+
+ RefPtr<URLClassifierLocalParent> actor =
+ dont_AddRef(static_cast<URLClassifierLocalParent*>(aActor));
+ return true;
+}
+
+PSessionStorageObserverParent*
+ContentParent::AllocPSessionStorageObserverParent() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ return mozilla::dom::AllocPSessionStorageObserverParent();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvPSessionStorageObserverConstructor(
+ PSessionStorageObserverParent* aActor) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aActor);
+
+ if (!mozilla::dom::RecvPSessionStorageObserverConstructor(aActor)) {
+ return IPC_FAIL(this, "RecvPSessionStorageObserverConstructor failed.");
+ }
+ return IPC_OK();
+}
+
+bool ContentParent::DeallocPSessionStorageObserverParent(
+ PSessionStorageObserverParent* aActor) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aActor);
+
+ return mozilla::dom::DeallocPSessionStorageObserverParent(aActor);
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvBHRThreadHang(
+ const HangDetails& aDetails) {
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ // Copy the HangDetails recieved over the network into a nsIHangDetails, and
+ // then fire our own observer notification.
+ // XXX: We should be able to avoid this potentially expensive copy here by
+ // moving our deserialized argument.
+ nsCOMPtr<nsIHangDetails> hangDetails =
+ new nsHangDetails(HangDetails(aDetails), PersistedToDisk::No);
+ obs->NotifyObservers(hangDetails, "bhr-thread-hang", nullptr);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvAddCertException(
+ nsIX509Cert* aCert, const nsACString& aHostName, int32_t aPort,
+ const OriginAttributes& aOriginAttributes, bool aIsTemporary,
+ AddCertExceptionResolver&& aResolver) {
+ nsCOMPtr<nsICertOverrideService> overrideService =
+ do_GetService(NS_CERTOVERRIDE_CONTRACTID);
+ if (!overrideService) {
+ aResolver(NS_ERROR_FAILURE);
+ return IPC_OK();
+ }
+ nsresult rv = overrideService->RememberValidityOverride(
+ aHostName, aPort, aOriginAttributes, aCert, aIsTemporary);
+ aResolver(rv);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+ContentParent::RecvAutomaticStorageAccessPermissionCanBeGranted(
+ nsIPrincipal* aPrincipal,
+ AutomaticStorageAccessPermissionCanBeGrantedResolver&& aResolver) {
+ if (!aPrincipal) {
+ return IPC_FAIL(this, "No principal");
+ }
+
+ if (!ValidatePrincipal(aPrincipal)) {
+ LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __func__);
+ }
+ aResolver(Document::AutomaticStorageAccessPermissionCanBeGranted(aPrincipal));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+ContentParent::RecvStorageAccessPermissionGrantedForOrigin(
+ uint64_t aTopLevelWindowId,
+ const MaybeDiscarded<BrowsingContext>& aParentContext,
+ nsIPrincipal* aTrackingPrincipal, const nsACString& aTrackingOrigin,
+ const int& aAllowMode,
+ const Maybe<ContentBlockingNotifier::StorageAccessPermissionGrantedReason>&
+ aReason,
+ const bool& aFrameOnly,
+ StorageAccessPermissionGrantedForOriginResolver&& aResolver) {
+ if (aParentContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+
+ if (!aTrackingPrincipal) {
+ return IPC_FAIL(this, "No principal");
+ }
+
+ // We only report here if we cannot report the console directly in the content
+ // process. In that case, the `aReason` would be given a value. Otherwise, it
+ // will be nothing.
+ if (aReason) {
+ ContentBlockingNotifier::ReportUnblockingToConsole(
+ aParentContext.get_canonical(), NS_ConvertUTF8toUTF16(aTrackingOrigin),
+ aReason.value());
+ }
+
+ StorageAccessAPIHelper::SaveAccessForOriginOnParentProcess(
+ aTopLevelWindowId, aParentContext.get_canonical(), aTrackingPrincipal,
+ aAllowMode, aFrameOnly)
+ ->Then(GetCurrentSerialEventTarget(), __func__,
+ [aResolver = std::move(aResolver)](
+ StorageAccessAPIHelper::ParentAccessGrantPromise::
+ ResolveOrRejectValue&& aValue) {
+ bool success =
+ aValue.IsResolve() && NS_SUCCEEDED(aValue.ResolveValue());
+ aResolver(success);
+ });
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvCompleteAllowAccessFor(
+ const MaybeDiscarded<BrowsingContext>& aParentContext,
+ uint64_t aTopLevelWindowId, nsIPrincipal* aTrackingPrincipal,
+ const nsACString& aTrackingOrigin, uint32_t aCookieBehavior,
+ const ContentBlockingNotifier::StorageAccessPermissionGrantedReason&
+ aReason,
+ CompleteAllowAccessForResolver&& aResolver) {
+ if (aParentContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+
+ StorageAccessAPIHelper::CompleteAllowAccessForOnParentProcess(
+ aParentContext.get_canonical(), aTopLevelWindowId, aTrackingPrincipal,
+ aTrackingOrigin, aCookieBehavior, aReason, nullptr)
+ ->Then(GetCurrentSerialEventTarget(), __func__,
+ [aResolver = std::move(aResolver)](
+ StorageAccessAPIHelper::StorageAccessPermissionGrantPromise::
+ ResolveOrRejectValue&& aValue) {
+ Maybe<StorageAccessPromptChoices> choice;
+ if (aValue.IsResolve()) {
+ choice.emplace(static_cast<StorageAccessPromptChoices>(
+ aValue.ResolveValue()));
+ }
+ aResolver(choice);
+ });
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvSetAllowStorageAccessRequestFlag(
+ nsIPrincipal* aEmbeddedPrincipal, nsIURI* aEmbeddingOrigin,
+ SetAllowStorageAccessRequestFlagResolver&& aResolver) {
+ MOZ_ASSERT(aEmbeddedPrincipal);
+ MOZ_ASSERT(aEmbeddingOrigin);
+
+ if (!aEmbeddedPrincipal || !aEmbeddingOrigin) {
+ aResolver(false);
+ return IPC_OK();
+ }
+
+ // Get the permission manager and build the key.
+ RefPtr<PermissionManager> permManager = PermissionManager::GetInstance();
+ if (!permManager) {
+ aResolver(false);
+ return IPC_OK();
+ }
+ nsCOMPtr<nsIURI> embeddedURI = aEmbeddedPrincipal->GetURI();
+ nsCString permissionKey;
+ bool success = AntiTrackingUtils::CreateStorageRequestPermissionKey(
+ embeddedURI, permissionKey);
+ if (!success) {
+ aResolver(false);
+ return IPC_OK();
+ }
+
+ // Set the permission to ALLOW for a prefence specified amount of seconds.
+ // Time units are inconsistent, be careful
+ int64_t when = (PR_Now() / PR_USEC_PER_MSEC) +
+ StaticPrefs::dom_storage_access_forward_declared_lifetime() *
+ PR_MSEC_PER_SEC;
+ nsCOMPtr<nsIPrincipal> principal = BasePrincipal::CreateContentPrincipal(
+ aEmbeddingOrigin, aEmbeddedPrincipal->OriginAttributesRef());
+ nsresult rv = permManager->AddFromPrincipal(
+ principal, permissionKey, nsIPermissionManager::ALLOW_ACTION,
+ nsIPermissionManager::EXPIRE_TIME, when);
+ if (NS_FAILED(rv)) {
+ aResolver(false);
+ return IPC_OK();
+ }
+
+ // Resolve with success if we set the permission.
+ aResolver(true);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvTestAllowStorageAccessRequestFlag(
+ nsIPrincipal* aEmbeddingPrincipal, nsIURI* aEmbeddedOrigin,
+ TestAllowStorageAccessRequestFlagResolver&& aResolver) {
+ MOZ_ASSERT(aEmbeddingPrincipal);
+ MOZ_ASSERT(aEmbeddedOrigin);
+
+ // Get the permission manager and build the key.
+ RefPtr<PermissionManager> permManager = PermissionManager::GetInstance();
+ if (!permManager) {
+ aResolver(false);
+ return IPC_OK();
+ }
+ nsCString requestPermissionKey;
+ bool success = AntiTrackingUtils::CreateStorageRequestPermissionKey(
+ aEmbeddedOrigin, requestPermissionKey);
+ if (!success) {
+ aResolver(false);
+ return IPC_OK();
+ }
+
+ // Get the permission and resolve false if it is not set to ALLOW.
+ uint32_t access = nsIPermissionManager::UNKNOWN_ACTION;
+ nsresult rv = permManager->TestPermissionFromPrincipal(
+ aEmbeddingPrincipal, requestPermissionKey, &access);
+ if (NS_FAILED(rv)) {
+ aResolver(false);
+ return IPC_OK();
+ }
+ if (access != nsIPermissionManager::ALLOW_ACTION) {
+ aResolver(false);
+ return IPC_OK();
+ }
+
+ // Remove the permission, failing if the permission manager fails
+ rv = permManager->RemoveFromPrincipal(aEmbeddingPrincipal,
+ requestPermissionKey);
+ if (NS_FAILED(rv)) {
+ aResolver(false);
+ return IPC_OK();
+ }
+
+ // At this point, signal to our caller that the permission was set
+ aResolver(true);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvStoreUserInteractionAsPermission(
+ nsIPrincipal* aPrincipal) {
+ if (!aPrincipal) {
+ return IPC_FAIL(this, "No principal");
+ }
+
+ if (!ValidatePrincipal(aPrincipal)) {
+ LogAndAssertFailedPrincipalValidationInfo(aPrincipal, __func__);
+ }
+ ContentBlockingUserInteraction::Observe(aPrincipal);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvTestCookiePermissionDecided(
+ const MaybeDiscarded<BrowsingContext>& aContext, nsIPrincipal* aPrincipal,
+ const TestCookiePermissionDecidedResolver&& aResolver) {
+ if (aContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+
+ if (!aPrincipal) {
+ return IPC_FAIL(this, "No principal");
+ }
+
+ RefPtr<WindowGlobalParent> wgp =
+ aContext.get_canonical()->GetCurrentWindowGlobal();
+ nsCOMPtr<nsICookieJarSettings> cjs = wgp->CookieJarSettings();
+
+ Maybe<bool> result =
+ StorageAccessAPIHelper::CheckCookiesPermittedDecidesStorageAccessAPI(
+ cjs, aPrincipal);
+ aResolver(result);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvTestStorageAccessPermission(
+ nsIPrincipal* aEmbeddingPrincipal, const nsCString& aEmbeddedOrigin,
+ const TestStorageAccessPermissionResolver&& aResolver) {
+ // Get the permission manager and build the key.
+ RefPtr<PermissionManager> permManager = PermissionManager::GetInstance();
+ if (!permManager) {
+ aResolver(Nothing());
+ return IPC_OK();
+ }
+ nsCString requestPermissionKey;
+ AntiTrackingUtils::CreateStoragePermissionKey(aEmbeddedOrigin,
+ requestPermissionKey);
+
+ // Test the permission
+ uint32_t access = nsIPermissionManager::UNKNOWN_ACTION;
+ nsresult rv = permManager->TestPermissionFromPrincipal(
+ aEmbeddingPrincipal, requestPermissionKey, &access);
+ if (NS_FAILED(rv)) {
+ aResolver(Nothing());
+ return IPC_OK();
+ }
+ if (access == nsIPermissionManager::ALLOW_ACTION) {
+ aResolver(Some(true));
+ } else if (access == nsIPermissionManager::DENY_ACTION) {
+ aResolver(Some(false));
+ } else {
+ aResolver(Nothing());
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvNotifyMediaPlaybackChanged(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ MediaPlaybackState aState) {
+ if (aContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+ if (RefPtr<IMediaInfoUpdater> updater =
+ aContext.get_canonical()->GetMediaController()) {
+ updater->NotifyMediaPlaybackChanged(aContext.ContextId(), aState);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvNotifyMediaAudibleChanged(
+ const MaybeDiscarded<BrowsingContext>& aContext, MediaAudibleState aState) {
+ if (aContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+ if (RefPtr<IMediaInfoUpdater> updater =
+ aContext.get_canonical()->GetMediaController()) {
+ updater->NotifyMediaAudibleChanged(aContext.ContextId(), aState);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvNotifyPictureInPictureModeChanged(
+ const MaybeDiscarded<BrowsingContext>& aContext, bool aEnabled) {
+ if (aContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+ if (RefPtr<MediaController> controller =
+ aContext.get_canonical()->GetMediaController()) {
+ controller->SetIsInPictureInPictureMode(aContext.ContextId(), aEnabled);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvAbortOtherOrientationPendingPromises(
+ const MaybeDiscarded<BrowsingContext>& aContext) {
+ if (aContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+
+ CanonicalBrowsingContext* context = aContext.get_canonical();
+
+ context->Group()->EachOtherParent(this, [&](ContentParent* aParent) {
+ Unused << aParent->SendAbortOrientationPendingPromises(context);
+ });
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvNotifyMediaSessionUpdated(
+ const MaybeDiscarded<BrowsingContext>& aContext, bool aIsCreated) {
+ if (aContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+
+ RefPtr<IMediaInfoUpdater> updater =
+ aContext.get_canonical()->GetMediaController();
+ if (!updater) {
+ return IPC_OK();
+ }
+ if (aIsCreated) {
+ updater->NotifySessionCreated(aContext->Id());
+ } else {
+ updater->NotifySessionDestroyed(aContext->Id());
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvNotifyUpdateMediaMetadata(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const Maybe<MediaMetadataBase>& aMetadata) {
+ if (aContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+ if (RefPtr<IMediaInfoUpdater> updater =
+ aContext.get_canonical()->GetMediaController()) {
+ updater->UpdateMetadata(aContext.ContextId(), aMetadata);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+ContentParent::RecvNotifyMediaSessionPlaybackStateChanged(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ MediaSessionPlaybackState aPlaybackState) {
+ if (aContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+ if (RefPtr<IMediaInfoUpdater> updater =
+ aContext.get_canonical()->GetMediaController()) {
+ updater->SetDeclaredPlaybackState(aContext.ContextId(), aPlaybackState);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+ContentParent::RecvNotifyMediaSessionSupportedActionChanged(
+ const MaybeDiscarded<BrowsingContext>& aContext, MediaSessionAction aAction,
+ bool aEnabled) {
+ if (aContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+ RefPtr<IMediaInfoUpdater> updater =
+ aContext.get_canonical()->GetMediaController();
+ if (!updater) {
+ return IPC_OK();
+ }
+ if (aEnabled) {
+ updater->EnableAction(aContext.ContextId(), aAction);
+ } else {
+ updater->DisableAction(aContext.ContextId(), aAction);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvNotifyMediaFullScreenState(
+ const MaybeDiscarded<BrowsingContext>& aContext, bool aIsInFullScreen) {
+ if (aContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+ if (RefPtr<IMediaInfoUpdater> updater =
+ aContext.get_canonical()->GetMediaController()) {
+ updater->NotifyMediaFullScreenState(aContext.ContextId(), aIsInFullScreen);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvNotifyPositionStateChanged(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const PositionState& aState) {
+ if (aContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+ if (RefPtr<IMediaInfoUpdater> updater =
+ aContext.get_canonical()->GetMediaController()) {
+ updater->UpdatePositionState(aContext.ContextId(), aState);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvAddOrRemovePageAwakeRequest(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const bool& aShouldAddCount) {
+ if (aContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+ if (aShouldAddCount) {
+ aContext.get_canonical()->AddPageAwakeRequest();
+ } else {
+ aContext.get_canonical()->RemovePageAwakeRequest();
+ }
+ return IPC_OK();
+}
+
+#if defined(XP_WIN)
+mozilla::ipc::IPCResult ContentParent::RecvGetModulesTrust(
+ ModulePaths&& aModPaths, bool aRunAtNormalPriority,
+ GetModulesTrustResolver&& aResolver) {
+ RefPtr<DllServices> dllSvc(DllServices::Get());
+ dllSvc->GetModulesTrust(std::move(aModPaths), aRunAtNormalPriority)
+ ->Then(
+ GetMainThreadSerialEventTarget(), __func__,
+ [aResolver](ModulesMapResult&& aResult) {
+ aResolver(Some(ModulesMapResult(std::move(aResult))));
+ },
+ [aResolver](nsresult aRv) { aResolver(Nothing()); });
+ return IPC_OK();
+}
+#endif // defined(XP_WIN)
+
+mozilla::ipc::IPCResult ContentParent::RecvCreateBrowsingContext(
+ uint64_t aGroupId, BrowsingContext::IPCInitializer&& aInit) {
+ RefPtr<WindowGlobalParent> parent;
+ if (aInit.mParentId != 0) {
+ parent = WindowGlobalParent::GetByInnerWindowId(aInit.mParentId);
+ if (!parent) {
+ return IPC_FAIL(this, "Parent doesn't exist in parent process");
+ }
+ }
+
+ if (parent && parent->GetContentParent() != this) {
+ // We're trying attach a child BrowsingContext to a parent
+ // WindowContext in another process. This is illegal since the
+ // only thing that could create that child BrowsingContext is the parent
+ // window's process.
+ return IPC_FAIL(this,
+ "Must create BrowsingContext from the parent's process");
+ }
+
+ RefPtr<BrowsingContext> opener;
+ if (aInit.GetOpenerId() != 0) {
+ opener = BrowsingContext::Get(aInit.GetOpenerId());
+ if (!opener) {
+ return IPC_FAIL(this, "Opener doesn't exist in parent process");
+ }
+ }
+
+ RefPtr<BrowsingContext> child = BrowsingContext::Get(aInit.mId);
+ if (child) {
+ // This is highly suspicious. BrowsingContexts should only be created once,
+ // so finding one indicates that someone is doing something they shouldn't.
+ return IPC_FAIL(this, "A BrowsingContext with this ID already exists");
+ }
+
+ // Ensure that the passed-in BrowsingContextGroup is valid.
+ RefPtr<BrowsingContextGroup> group =
+ BrowsingContextGroup::GetOrCreate(aGroupId);
+ if (parent && parent->Group() != group) {
+ if (parent->Group()->Id() != aGroupId) {
+ return IPC_FAIL(this, "Parent has different group ID");
+ } else {
+ return IPC_FAIL(this, "Parent has different group object");
+ }
+ }
+ if (opener && opener->Group() != group) {
+ if (opener->Group()->Id() != aGroupId) {
+ return IPC_FAIL(this, "Opener has different group ID");
+ } else {
+ return IPC_FAIL(this, "Opener has different group object");
+ }
+ }
+ if (!parent && !opener && !group->Toplevels().IsEmpty()) {
+ return IPC_FAIL(this, "Unrelated context from child in stale group");
+ }
+
+ return BrowsingContext::CreateFromIPC(std::move(aInit), group, this);
+}
+
+bool ContentParent::CheckBrowsingContextEmbedder(CanonicalBrowsingContext* aBC,
+ const char* aOperation) const {
+ if (!aBC->IsEmbeddedInProcess(ChildID())) {
+ MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Warning,
+ ("ParentIPC: Trying to %s out of process context 0x%08" PRIx64,
+ aOperation, aBC->Id()));
+ return false;
+ }
+ return true;
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvDiscardBrowsingContext(
+ const MaybeDiscarded<BrowsingContext>& aContext, bool aDoDiscard,
+ DiscardBrowsingContextResolver&& aResolve) {
+ if (CanonicalBrowsingContext* context =
+ CanonicalBrowsingContext::Cast(aContext.GetMaybeDiscarded())) {
+ if (aDoDiscard && !context->IsDiscarded()) {
+ if (!CheckBrowsingContextEmbedder(context, "discard")) {
+ return IPC_FAIL(this, "Illegal Discard attempt");
+ }
+
+ context->Detach(/* aFromIPC */ true);
+ }
+ context->AddFinalDiscardListener(aResolve);
+ return IPC_OK();
+ }
+
+ // Resolve the promise, as we've received and handled the message. This will
+ // allow the content process to fully-discard references to this BC.
+ aResolve(true);
+ return IPC_OK();
+}
+
+void ContentParent::UnregisterRemoveWorkerActor() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ {
+ RecursiveMutexAutoLock lock(mThreadsafeHandle->mMutex);
+ if (--mThreadsafeHandle->mRemoteWorkerActorCount) {
+ return;
+ }
+ }
+
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
+ ("UnregisterRemoveWorkerActor %p", this));
+ MaybeBeginShutDown();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvWindowClose(
+ const MaybeDiscarded<BrowsingContext>& aContext, bool aTrustedCaller) {
+ if (aContext.IsNullOrDiscarded()) {
+ MOZ_LOG(
+ BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ParentIPC: Trying to send a message to dead or detached context"));
+ return IPC_OK();
+ }
+ CanonicalBrowsingContext* context = aContext.get_canonical();
+
+ // FIXME Need to check that the sending process has access to the unit of
+ // related
+ // browsing contexts of bc.
+
+ if (ContentParent* cp = context->GetContentParent()) {
+ Unused << cp->SendWindowClose(context, aTrustedCaller);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvWindowFocus(
+ const MaybeDiscarded<BrowsingContext>& aContext, CallerType aCallerType,
+ uint64_t aActionId) {
+ if (aContext.IsNullOrDiscarded()) {
+ MOZ_LOG(
+ BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ParentIPC: Trying to send a message to dead or detached context"));
+ return IPC_OK();
+ }
+ LOGFOCUS(("ContentParent::RecvWindowFocus actionid: %" PRIu64, aActionId));
+ CanonicalBrowsingContext* context = aContext.get_canonical();
+
+ if (ContentParent* cp = context->GetContentParent()) {
+ Unused << cp->SendWindowFocus(context, aCallerType, aActionId);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvWindowBlur(
+ const MaybeDiscarded<BrowsingContext>& aContext, CallerType aCallerType) {
+ if (aContext.IsNullOrDiscarded()) {
+ MOZ_LOG(
+ BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ParentIPC: Trying to send a message to dead or detached context"));
+ return IPC_OK();
+ }
+ CanonicalBrowsingContext* context = aContext.get_canonical();
+
+ if (ContentParent* cp = context->GetContentParent()) {
+ Unused << cp->SendWindowBlur(context, aCallerType);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvRaiseWindow(
+ const MaybeDiscarded<BrowsingContext>& aContext, CallerType aCallerType,
+ uint64_t aActionId) {
+ if (aContext.IsNullOrDiscarded()) {
+ MOZ_LOG(
+ BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ParentIPC: Trying to send a message to dead or detached context"));
+ return IPC_OK();
+ }
+ LOGFOCUS(("ContentParent::RecvRaiseWindow actionid: %" PRIu64, aActionId));
+
+ CanonicalBrowsingContext* context = aContext.get_canonical();
+
+ if (ContentParent* cp = context->GetContentParent()) {
+ Unused << cp->SendRaiseWindow(context, aCallerType, aActionId);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvAdjustWindowFocus(
+ const MaybeDiscarded<BrowsingContext>& aContext, bool aIsVisible,
+ uint64_t aActionId) {
+ if (aContext.IsNullOrDiscarded()) {
+ MOZ_LOG(
+ BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ParentIPC: Trying to send a message to dead or detached context"));
+ return IPC_OK();
+ }
+ LOGFOCUS(
+ ("ContentParent::RecvAdjustWindowFocus isVisible %d actionid: %" PRIu64,
+ aIsVisible, aActionId));
+
+ nsTHashMap<nsPtrHashKey<ContentParent>, bool> processes(2);
+ processes.InsertOrUpdate(this, true);
+
+ ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+ if (cpm) {
+ CanonicalBrowsingContext* context = aContext.get_canonical();
+ while (context) {
+ BrowsingContext* parent = context->GetParent();
+ if (!parent) {
+ break;
+ }
+
+ CanonicalBrowsingContext* canonicalParent = parent->Canonical();
+ ContentParent* cp = cpm->GetContentProcessById(
+ ContentParentId(canonicalParent->OwnerProcessId()));
+ if (cp && !processes.Get(cp)) {
+ Unused << cp->SendAdjustWindowFocus(context, aIsVisible, aActionId);
+ processes.InsertOrUpdate(cp, true);
+ }
+ context = canonicalParent;
+ }
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvClearFocus(
+ const MaybeDiscarded<BrowsingContext>& aContext) {
+ if (aContext.IsNullOrDiscarded()) {
+ MOZ_LOG(
+ BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ParentIPC: Trying to send a message to dead or detached context"));
+ return IPC_OK();
+ }
+ CanonicalBrowsingContext* context = aContext.get_canonical();
+
+ if (ContentParent* cp = context->GetContentParent()) {
+ Unused << cp->SendClearFocus(context);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvSetFocusedBrowsingContext(
+ const MaybeDiscarded<BrowsingContext>& aContext, uint64_t aActionId) {
+ if (aContext.IsNullOrDiscarded()) {
+ MOZ_LOG(
+ BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ParentIPC: Trying to send a message to dead or detached context"));
+ return IPC_OK();
+ }
+ LOGFOCUS(("ContentParent::RecvSetFocusedBrowsingContext actionid: %" PRIu64,
+ aActionId));
+ CanonicalBrowsingContext* context = aContext.get_canonical();
+
+ nsFocusManager* fm = nsFocusManager::GetFocusManager();
+ if (!fm) {
+ return IPC_OK();
+ }
+
+ if (!fm->SetFocusedBrowsingContextInChrome(context, aActionId)) {
+ LOGFOCUS((
+ "Ignoring out-of-sequence attempt [%p] to set focused browsing context "
+ "in parent.",
+ context));
+ Unused << SendReviseFocusedBrowsingContext(
+ aActionId, fm->GetFocusedBrowsingContextInChrome(),
+ fm->GetActionIdForFocusedBrowsingContextInChrome());
+ return IPC_OK();
+ }
+
+ BrowserParent::UpdateFocusFromBrowsingContext();
+
+ context->Group()->EachOtherParent(this, [&](ContentParent* aParent) {
+ Unused << aParent->SendSetFocusedBrowsingContext(context, aActionId);
+ });
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvSetActiveBrowsingContext(
+ const MaybeDiscarded<BrowsingContext>& aContext, uint64_t aActionId) {
+ if (aContext.IsNullOrDiscarded()) {
+ MOZ_LOG(
+ BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ParentIPC: Trying to send a message to dead or detached context"));
+ return IPC_OK();
+ }
+ LOGFOCUS(("ContentParent::RecvSetActiveBrowsingContext actionid: %" PRIu64,
+ aActionId));
+ CanonicalBrowsingContext* context = aContext.get_canonical();
+
+ nsFocusManager* fm = nsFocusManager::GetFocusManager();
+ if (!fm) {
+ return IPC_OK();
+ }
+
+ if (!fm->SetActiveBrowsingContextInChrome(context, aActionId)) {
+ LOGFOCUS(
+ ("Ignoring out-of-sequence attempt [%p] to set active browsing context "
+ "in parent.",
+ context));
+ Unused << SendReviseActiveBrowsingContext(
+ aActionId, fm->GetActiveBrowsingContextInChrome(),
+ fm->GetActionIdForActiveBrowsingContextInChrome());
+ return IPC_OK();
+ }
+
+ context->Group()->EachOtherParent(this, [&](ContentParent* aParent) {
+ Unused << aParent->SendSetActiveBrowsingContext(context, aActionId);
+ });
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvUnsetActiveBrowsingContext(
+ const MaybeDiscarded<BrowsingContext>& aContext, uint64_t aActionId) {
+ if (aContext.IsNullOrDiscarded()) {
+ MOZ_LOG(
+ BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ParentIPC: Trying to send a message to dead or detached context"));
+ return IPC_OK();
+ }
+ LOGFOCUS(("ContentParent::RecvUnsetActiveBrowsingContext actionid: %" PRIu64,
+ aActionId));
+ CanonicalBrowsingContext* context = aContext.get_canonical();
+
+ nsFocusManager* fm = nsFocusManager::GetFocusManager();
+ if (!fm) {
+ return IPC_OK();
+ }
+
+ if (!fm->SetActiveBrowsingContextInChrome(nullptr, aActionId)) {
+ LOGFOCUS(
+ ("Ignoring out-of-sequence attempt to unset active browsing context in "
+ "parent [%p].",
+ context));
+ Unused << SendReviseActiveBrowsingContext(
+ aActionId, fm->GetActiveBrowsingContextInChrome(),
+ fm->GetActionIdForActiveBrowsingContextInChrome());
+ return IPC_OK();
+ }
+
+ context->Group()->EachOtherParent(this, [&](ContentParent* aParent) {
+ Unused << aParent->SendUnsetActiveBrowsingContext(context, aActionId);
+ });
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvSetFocusedElement(
+ const MaybeDiscarded<BrowsingContext>& aContext, bool aNeedsFocus) {
+ if (aContext.IsNullOrDiscarded()) {
+ MOZ_LOG(
+ BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ParentIPC: Trying to send a message to dead or detached context"));
+ return IPC_OK();
+ }
+ LOGFOCUS(("ContentParent::RecvSetFocusedElement"));
+ CanonicalBrowsingContext* context = aContext.get_canonical();
+
+ if (ContentParent* cp = context->GetContentParent()) {
+ Unused << cp->SendSetFocusedElement(context, aNeedsFocus);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvFinalizeFocusOuter(
+ const MaybeDiscarded<BrowsingContext>& aContext, bool aCanFocus,
+ CallerType aCallerType) {
+ if (aContext.IsNullOrDiscarded()) {
+ MOZ_LOG(
+ BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ParentIPC: Trying to send a message to dead or detached context"));
+ return IPC_OK();
+ }
+ LOGFOCUS(("ContentParent::RecvFinalizeFocusOuter"));
+ CanonicalBrowsingContext* context = aContext.get_canonical();
+ ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+ if (cpm) {
+ ContentParent* cp = cpm->GetContentProcessById(
+ ContentParentId(context->EmbedderProcessId()));
+ if (cp) {
+ Unused << cp->SendFinalizeFocusOuter(context, aCanFocus, aCallerType);
+ }
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvInsertNewFocusActionId(
+ uint64_t aActionId) {
+ LOGFOCUS(("ContentParent::RecvInsertNewFocusActionId actionid: %" PRIu64,
+ aActionId));
+ nsFocusManager* fm = nsFocusManager::GetFocusManager();
+ if (fm) {
+ fm->InsertNewFocusActionId(aActionId);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvBlurToParent(
+ const MaybeDiscarded<BrowsingContext>& aFocusedBrowsingContext,
+ const MaybeDiscarded<BrowsingContext>& aBrowsingContextToClear,
+ const MaybeDiscarded<BrowsingContext>& aAncestorBrowsingContextToFocus,
+ bool aIsLeavingDocument, bool aAdjustWidget,
+ bool aBrowsingContextToClearHandled,
+ bool aAncestorBrowsingContextToFocusHandled, uint64_t aActionId) {
+ if (aFocusedBrowsingContext.IsNullOrDiscarded()) {
+ MOZ_LOG(
+ BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ParentIPC: Trying to send a message to dead or detached context"));
+ return IPC_OK();
+ }
+
+ LOGFOCUS(
+ ("ContentParent::RecvBlurToParent isLeavingDocument %d adjustWidget %d "
+ "browsingContextToClearHandled %d ancestorBrowsingContextToFocusHandled "
+ "%d actionid: %" PRIu64,
+ aIsLeavingDocument, aAdjustWidget, aBrowsingContextToClearHandled,
+ aAncestorBrowsingContextToFocusHandled, aActionId));
+
+ CanonicalBrowsingContext* focusedBrowsingContext =
+ aFocusedBrowsingContext.get_canonical();
+
+ // If aBrowsingContextToClear and aAncestorBrowsingContextToFocusHandled
+ // didn't get handled in the process that sent this IPC message and they
+ // aren't in the same process as aFocusedBrowsingContext, we need to split
+ // off their handling here and use SendSetFocusedElement to send them
+ // elsewhere than the blurring itself.
+
+ bool ancestorDifferent =
+ (!aAncestorBrowsingContextToFocusHandled &&
+ !aAncestorBrowsingContextToFocus.IsNullOrDiscarded() &&
+ (focusedBrowsingContext->OwnerProcessId() !=
+ aAncestorBrowsingContextToFocus.get_canonical()->OwnerProcessId()));
+ if (!aBrowsingContextToClearHandled &&
+ !aBrowsingContextToClear.IsNullOrDiscarded() &&
+ (focusedBrowsingContext->OwnerProcessId() !=
+ aBrowsingContextToClear.get_canonical()->OwnerProcessId())) {
+ MOZ_RELEASE_ASSERT(!ancestorDifferent,
+ "This combination is not supposed to happen.");
+ if (ContentParent* cp =
+ aBrowsingContextToClear.get_canonical()->GetContentParent()) {
+ Unused << cp->SendSetFocusedElement(aBrowsingContextToClear, false);
+ }
+ } else if (ancestorDifferent) {
+ if (ContentParent* cp = aAncestorBrowsingContextToFocus.get_canonical()
+ ->GetContentParent()) {
+ Unused << cp->SendSetFocusedElement(aAncestorBrowsingContextToFocus,
+ true);
+ }
+ }
+
+ if (ContentParent* cp = focusedBrowsingContext->GetContentParent()) {
+ Unused << cp->SendBlurToChild(aFocusedBrowsingContext,
+ aBrowsingContextToClear,
+ aAncestorBrowsingContextToFocus,
+ aIsLeavingDocument, aAdjustWidget, aActionId);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvMaybeExitFullscreen(
+ const MaybeDiscarded<BrowsingContext>& aContext) {
+ if (aContext.IsNullOrDiscarded()) {
+ MOZ_LOG(
+ BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ParentIPC: Trying to send a message to dead or detached context"));
+ return IPC_OK();
+ }
+ CanonicalBrowsingContext* context = aContext.get_canonical();
+
+ if (ContentParent* cp = context->GetContentParent()) {
+ Unused << cp->SendMaybeExitFullscreen(context);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvWindowPostMessage(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const ClonedOrErrorMessageData& aMessage, const PostMessageData& aData) {
+ if (aContext.IsNullOrDiscarded()) {
+ MOZ_LOG(
+ BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ParentIPC: Trying to send a message to dead or detached context"));
+ return IPC_OK();
+ }
+ CanonicalBrowsingContext* context = aContext.get_canonical();
+
+ if (aData.source().IsDiscarded()) {
+ MOZ_LOG(
+ BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ParentIPC: Trying to send a message from dead or detached context"));
+ return IPC_OK();
+ }
+
+ RefPtr<ContentParent> cp = context->GetContentParent();
+ if (!cp) {
+ MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ParentIPC: Trying to send PostMessage to dead content process"));
+ return IPC_OK();
+ }
+
+ ClonedOrErrorMessageData message;
+ StructuredCloneData messageFromChild;
+ if (aMessage.type() == ClonedOrErrorMessageData::TClonedMessageData) {
+ UnpackClonedMessageData(aMessage, messageFromChild);
+
+ ClonedMessageData clonedMessageData;
+ if (BuildClonedMessageData(messageFromChild, clonedMessageData)) {
+ message = std::move(clonedMessageData);
+ } else {
+ // FIXME Logging?
+ message = ErrorMessageData();
+ }
+ } else {
+ MOZ_ASSERT(aMessage.type() == ClonedOrErrorMessageData::TErrorMessageData);
+ message = ErrorMessageData();
+ }
+
+ Unused << cp->SendWindowPostMessage(context, message, aData);
+ return IPC_OK();
+}
+
+void ContentParent::AddBrowsingContextGroup(BrowsingContextGroup* aGroup) {
+ MOZ_DIAGNOSTIC_ASSERT(aGroup);
+ // Ensure that the group has been inserted, and if we're not launching
+ // anymore, also begin subscribing. Launching processes will be subscribed if
+ // they finish launching in `LaunchSubprocessResolve`.
+ if (mGroups.EnsureInserted(aGroup) && !IsLaunching()) {
+ aGroup->Subscribe(this);
+ }
+}
+
+void ContentParent::RemoveBrowsingContextGroup(BrowsingContextGroup* aGroup) {
+ MOZ_DIAGNOSTIC_ASSERT(aGroup);
+ // Remove the group from our list. This is called from the
+ // BrowsingContextGroup when unsubscribing, so we don't need to do it here.
+ if (mGroups.EnsureRemoved(aGroup) && CanSend()) {
+ // If we're removing the entry for the first time, tell the content process
+ // to clean up the group.
+ Unused << SendDestroyBrowsingContextGroup(aGroup->Id());
+ }
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvCommitBrowsingContextTransaction(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ BrowsingContext::BaseTransaction&& aTransaction, uint64_t aEpoch) {
+ // Record the new BrowsingContextFieldEpoch associated with this transaction.
+ // This should be done unconditionally, so that we're always in-sync.
+ //
+ // The order the parent process receives transactions is considered the
+ // "canonical" ordering, so we don't need to worry about doing any
+ // epoch-related validation.
+ MOZ_ASSERT(aEpoch == mBrowsingContextFieldEpoch + 1,
+ "Child process skipped an epoch?");
+ mBrowsingContextFieldEpoch = aEpoch;
+
+ return aTransaction.CommitFromIPC(aContext, this);
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvBlobURLDataRequest(
+ const nsACString& aBlobURL, nsIPrincipal* aTriggeringPrincipal,
+ nsIPrincipal* aLoadingPrincipal, const OriginAttributes& aOriginAttributes,
+ uint64_t aInnerWindowId, const nsCString& aPartitionKey,
+ BlobURLDataRequestResolver&& aResolver) {
+ RefPtr<BlobImpl> blobImpl;
+
+ // Since revoked blobs are also retrieved, it is possible that the blob no
+ // longer exists (due to the 5 second timeout) when execution reaches here
+ if (!BlobURLProtocolHandler::GetDataEntry(
+ aBlobURL, getter_AddRefs(blobImpl), aLoadingPrincipal,
+ aTriggeringPrincipal, aOriginAttributes, aInnerWindowId,
+ aPartitionKey, true /* AlsoIfRevoked */)) {
+ aResolver(NS_ERROR_DOM_BAD_URI);
+ return IPC_OK();
+ }
+
+ IPCBlob ipcBlob;
+ nsresult rv = IPCBlobUtils::Serialize(blobImpl, ipcBlob);
+
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ aResolver(rv);
+ return IPC_OK();
+ }
+
+ aResolver(ipcBlob);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvReportServiceWorkerShutdownProgress(
+ uint32_t aShutdownStateId, ServiceWorkerShutdownState::Progress aProgress) {
+ RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+ MOZ_RELEASE_ASSERT(swm, "ServiceWorkers should shutdown before SWM.");
+
+ swm->ReportServiceWorkerShutdownProgress(aShutdownStateId, aProgress);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvNotifyOnHistoryReload(
+ const MaybeDiscarded<BrowsingContext>& aContext, const bool& aForceReload,
+ NotifyOnHistoryReloadResolver&& aResolver) {
+ bool canReload = false;
+ Maybe<NotNull<RefPtr<nsDocShellLoadState>>> loadState;
+ Maybe<bool> reloadActiveEntry;
+ if (!aContext.IsNullOrDiscarded()) {
+ aContext.get_canonical()->NotifyOnHistoryReload(
+ aForceReload, canReload, loadState, reloadActiveEntry);
+ }
+ aResolver(
+ std::tuple<const bool&,
+ const Maybe<NotNull<RefPtr<nsDocShellLoadState>>>&,
+ const Maybe<bool>&>(canReload, loadState, reloadActiveEntry));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvHistoryCommit(
+ const MaybeDiscarded<BrowsingContext>& aContext, const uint64_t& aLoadID,
+ const nsID& aChangeID, const uint32_t& aLoadType, const bool& aPersist,
+ const bool& aCloneEntryChildren, const bool& aChannelExpired,
+ const uint32_t& aCacheKey) {
+ if (!aContext.IsDiscarded()) {
+ CanonicalBrowsingContext* canonical = aContext.get_canonical();
+ if (!canonical) {
+ return IPC_FAIL(
+ this, "Could not get canonical. aContext.get_canonical() fails.");
+ }
+ canonical->SessionHistoryCommit(aLoadID, aChangeID, aLoadType, aPersist,
+ aCloneEntryChildren, aChannelExpired,
+ aCacheKey);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvHistoryGo(
+ const MaybeDiscarded<BrowsingContext>& aContext, int32_t aOffset,
+ uint64_t aHistoryEpoch, bool aRequireUserInteraction, bool aUserActivation,
+ HistoryGoResolver&& aResolveRequestedIndex) {
+ if (!aContext.IsNullOrDiscarded()) {
+ RefPtr<CanonicalBrowsingContext> canonical = aContext.get_canonical();
+ aResolveRequestedIndex(
+ canonical->HistoryGo(aOffset, aHistoryEpoch, aRequireUserInteraction,
+ aUserActivation, Some(ChildID())));
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvSynchronizeLayoutHistoryState(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ nsILayoutHistoryState* aState) {
+ if (aContext.IsNull()) {
+ return IPC_OK();
+ }
+
+ BrowsingContext* bc = aContext.GetMaybeDiscarded();
+ if (!bc) {
+ return IPC_OK();
+ }
+ SessionHistoryEntry* entry = bc->Canonical()->GetActiveSessionHistoryEntry();
+ if (entry) {
+ entry->SetLayoutHistoryState(aState);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvSessionHistoryEntryTitle(
+ const MaybeDiscarded<BrowsingContext>& aContext, const nsAString& aTitle) {
+ if (aContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+
+ SessionHistoryEntry* entry =
+ aContext.get_canonical()->GetActiveSessionHistoryEntry();
+ if (entry) {
+ entry->SetTitle(aTitle);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+ContentParent::RecvSessionHistoryEntryScrollRestorationIsManual(
+ const MaybeDiscarded<BrowsingContext>& aContext, const bool& aIsManual) {
+ if (aContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+
+ SessionHistoryEntry* entry =
+ aContext.get_canonical()->GetActiveSessionHistoryEntry();
+ if (entry) {
+ entry->SetScrollRestorationIsManual(aIsManual);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvSessionHistoryEntryScrollPosition(
+ const MaybeDiscarded<BrowsingContext>& aContext, const int32_t& aX,
+ const int32_t& aY) {
+ if (aContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+
+ SessionHistoryEntry* entry =
+ aContext.get_canonical()->GetActiveSessionHistoryEntry();
+ if (entry) {
+ entry->SetScrollPosition(aX, aY);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+ContentParent::RecvSessionHistoryEntryStoreWindowNameInContiguousEntries(
+ const MaybeDiscarded<BrowsingContext>& aContext, const nsAString& aName) {
+ if (aContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+
+ // Per https://html.spec.whatwg.org/#history-traversal 4.2.1, we need to set
+ // the name to all contiguous entries. This has to be called before
+ // CanonicalBrowsingContext::SessionHistoryCommit(), so the active entry is
+ // still the old entry that we want to set.
+
+ SessionHistoryEntry* entry =
+ aContext.get_canonical()->GetActiveSessionHistoryEntry();
+
+ if (entry) {
+ nsSHistory::WalkContiguousEntries(
+ entry, [&](nsISHEntry* aEntry) { aEntry->SetName(aName); });
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvSessionHistoryEntryCacheKey(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const uint32_t& aCacheKey) {
+ if (aContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+
+ SessionHistoryEntry* entry =
+ aContext.get_canonical()->GetActiveSessionHistoryEntry();
+ if (entry) {
+ entry->SetCacheKey(aCacheKey);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvSessionHistoryEntryWireframe(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const Wireframe& aWireframe) {
+ if (aContext.IsNull()) {
+ return IPC_OK();
+ }
+
+ BrowsingContext* bc = aContext.GetMaybeDiscarded();
+ if (!bc) {
+ return IPC_OK();
+ }
+
+ SessionHistoryEntry* entry = bc->Canonical()->GetActiveSessionHistoryEntry();
+ if (entry) {
+ entry->SetWireframe(Some(aWireframe));
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+ContentParent::RecvGetLoadingSessionHistoryInfoFromParent(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ GetLoadingSessionHistoryInfoFromParentResolver&& aResolver) {
+ if (aContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+
+ Maybe<LoadingSessionHistoryInfo> info;
+ aContext.get_canonical()->GetLoadingSessionHistoryInfoFromParent(info);
+ aResolver(info);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvRemoveFromBFCache(
+ const MaybeDiscarded<BrowsingContext>& aContext) {
+ if (aContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsFrameLoaderOwner> owner =
+ do_QueryInterface(aContext.get_canonical()->GetEmbedderElement());
+ if (!owner) {
+ return IPC_OK();
+ }
+
+ RefPtr<nsFrameLoader> frameLoader = owner->GetFrameLoader();
+ if (!frameLoader || !frameLoader->GetMaybePendingBrowsingContext()) {
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsISHistory> shistory = frameLoader->GetMaybePendingBrowsingContext()
+ ->Canonical()
+ ->GetSessionHistory();
+ if (!shistory) {
+ return IPC_OK();
+ }
+
+ uint32_t count = shistory->GetCount();
+ for (uint32_t i = 0; i < count; ++i) {
+ nsCOMPtr<nsISHEntry> entry;
+ shistory->GetEntryAtIndex(i, getter_AddRefs(entry));
+ nsCOMPtr<SessionHistoryEntry> she = do_QueryInterface(entry);
+ if (she) {
+ if (RefPtr<nsFrameLoader> frameLoader = she->GetFrameLoader()) {
+ if (frameLoader->GetMaybePendingBrowsingContext() == aContext.get()) {
+ she->SetFrameLoader(nullptr);
+ frameLoader->Destroy();
+ break;
+ }
+ }
+ }
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvSetActiveSessionHistoryEntry(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const Maybe<nsPoint>& aPreviousScrollPos, SessionHistoryInfo&& aInfo,
+ uint32_t aLoadType, uint32_t aUpdatedCacheKey, const nsID& aChangeID) {
+ if (!aContext.IsNullOrDiscarded()) {
+ aContext.get_canonical()->SetActiveSessionHistoryEntry(
+ aPreviousScrollPos, &aInfo, aLoadType, aUpdatedCacheKey, aChangeID);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvReplaceActiveSessionHistoryEntry(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ SessionHistoryInfo&& aInfo) {
+ if (!aContext.IsNullOrDiscarded()) {
+ aContext.get_canonical()->ReplaceActiveSessionHistoryEntry(&aInfo);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+ContentParent::RecvRemoveDynEntriesFromActiveSessionHistoryEntry(
+ const MaybeDiscarded<BrowsingContext>& aContext) {
+ if (!aContext.IsNullOrDiscarded()) {
+ aContext.get_canonical()->RemoveDynEntriesFromActiveSessionHistoryEntry();
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvRemoveFromSessionHistory(
+ const MaybeDiscarded<BrowsingContext>& aContext, const nsID& aChangeID) {
+ if (!aContext.IsNullOrDiscarded()) {
+ aContext.get_canonical()->RemoveFromSessionHistory(aChangeID);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvHistoryReload(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const uint32_t aReloadFlags) {
+ if (!aContext.IsNullOrDiscarded()) {
+ nsCOMPtr<nsISHistory> shistory =
+ aContext.get_canonical()->GetSessionHistory();
+ if (shistory) {
+ shistory->Reload(aReloadFlags);
+ }
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvCommitWindowContextTransaction(
+ const MaybeDiscarded<WindowContext>& aContext,
+ WindowContext::BaseTransaction&& aTransaction, uint64_t aEpoch) {
+ // Record the new BrowsingContextFieldEpoch associated with this transaction.
+ // This should be done unconditionally, so that we're always in-sync.
+ //
+ // The order the parent process receives transactions is considered the
+ // "canonical" ordering, so we don't need to worry about doing any
+ // epoch-related validation.
+ MOZ_ASSERT(aEpoch == mBrowsingContextFieldEpoch + 1,
+ "Child process skipped an epoch?");
+ mBrowsingContextFieldEpoch = aEpoch;
+
+ return aTransaction.CommitFromIPC(aContext, this);
+}
+
+NS_IMETHODIMP ContentParent::GetChildID(uint64_t* aOut) {
+ *aOut = this->ChildID();
+ return NS_OK;
+}
+
+NS_IMETHODIMP ContentParent::GetOsPid(int32_t* aOut) {
+ *aOut = Pid();
+ return NS_OK;
+}
+
+NS_IMETHODIMP ContentParent::GetRemoteType(nsACString& aRemoteType) {
+ aRemoteType = GetRemoteType();
+ return NS_OK;
+}
+
+IPCResult ContentParent::RecvRawMessage(
+ const JSActorMessageMeta& aMeta, const Maybe<ClonedMessageData>& aData,
+ const Maybe<ClonedMessageData>& aStack) {
+ Maybe<StructuredCloneData> data;
+ if (aData) {
+ data.emplace();
+ data->BorrowFromClonedMessageData(*aData);
+ }
+ Maybe<StructuredCloneData> stack;
+ if (aStack) {
+ stack.emplace();
+ stack->BorrowFromClonedMessageData(*aStack);
+ }
+ ReceiveRawMessage(aMeta, std::move(data), std::move(stack));
+ return IPC_OK();
+}
+
+NS_IMETHODIMP ContentParent::GetActor(const nsACString& aName, JSContext* aCx,
+ JSProcessActorParent** retval) {
+ ErrorResult error;
+ RefPtr<JSProcessActorParent> actor =
+ JSActorManager::GetActor(aCx, aName, error)
+ .downcast<JSProcessActorParent>();
+ if (error.MaybeSetPendingException(aCx)) {
+ return NS_ERROR_FAILURE;
+ }
+ actor.forget(retval);
+ return NS_OK;
+}
+
+NS_IMETHODIMP ContentParent::GetExistingActor(const nsACString& aName,
+ JSProcessActorParent** retval) {
+ RefPtr<JSProcessActorParent> actor =
+ JSActorManager::GetExistingActor(aName).downcast<JSProcessActorParent>();
+ actor.forget(retval);
+ return NS_OK;
+}
+
+already_AddRefed<JSActor> ContentParent::InitJSActor(
+ JS::Handle<JSObject*> aMaybeActor, const nsACString& aName,
+ ErrorResult& aRv) {
+ RefPtr<JSProcessActorParent> actor;
+ if (aMaybeActor.get()) {
+ aRv = UNWRAP_OBJECT(JSProcessActorParent, aMaybeActor.get(), actor);
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+ } else {
+ actor = new JSProcessActorParent();
+ }
+
+ MOZ_RELEASE_ASSERT(!actor->Manager(),
+ "mManager was already initialized once!");
+ actor->Init(aName, this);
+ return actor.forget();
+}
+
+IPCResult ContentParent::RecvFOGData(ByteBuf&& buf) {
+ glean::FOGData(std::move(buf));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvSetContainerFeaturePolicy(
+ const MaybeDiscardedBrowsingContext& aContainerContext,
+ FeaturePolicy* aContainerFeaturePolicy) {
+ if (aContainerContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+
+ auto* context = aContainerContext.get_canonical();
+ context->SetContainerFeaturePolicy(aContainerFeaturePolicy);
+
+ return IPC_OK();
+}
+
+NS_IMETHODIMP ContentParent::GetCanSend(bool* aCanSend) {
+ *aCanSend = CanSend();
+ return NS_OK;
+}
+
+ContentParent* ContentParent::AsContentParent() { return this; }
+
+JSActorManager* ContentParent::AsJSActorManager() { return this; }
+
+IPCResult ContentParent::RecvGetSystemIcon(nsIURI* aURI,
+ GetSystemIconResolver&& aResolver) {
+ using ResolverArgs = std::tuple<const nsresult&, mozilla::Maybe<ByteBuf>&&>;
+
+ if (!aURI) {
+ Maybe<ByteBuf> bytebuf = Nothing();
+ aResolver(ResolverArgs(NS_ERROR_NULL_POINTER, std::move(bytebuf)));
+ return IPC_OK();
+ }
+
+#if defined(MOZ_WIDGET_GTK)
+ Maybe<ByteBuf> bytebuf = Some(ByteBuf{});
+ nsresult rv = nsIconChannel::GetIcon(aURI, bytebuf.ptr());
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ bytebuf = Nothing();
+ }
+ aResolver(ResolverArgs(rv, std::move(bytebuf)));
+ return IPC_OK();
+#elif defined(XP_WIN)
+ nsIconChannel::GetIconAsync(aURI)->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [aResolver](ByteBuf&& aByteBuf) {
+ Maybe<ByteBuf> bytebuf = Some(std::move(aByteBuf));
+ aResolver(ResolverArgs(NS_OK, std::move(bytebuf)));
+ },
+ [aResolver](nsresult aErr) {
+ Maybe<ByteBuf> bytebuf = Nothing();
+ aResolver(ResolverArgs(aErr, std::move(bytebuf)));
+ });
+ return IPC_OK();
+#else
+ MOZ_CRASH(
+ "This message is currently implemented only on GTK and Windows "
+ "platforms");
+#endif
+}
+
+#ifdef FUZZING_SNAPSHOT
+IPCResult ContentParent::RecvSignalFuzzingReady() {
+ // No action needed here, we already observe this message directly
+ // on the channel and act accordingly.
+ return IPC_OK();
+}
+#endif
+
+nsCString ThreadsafeContentParentHandle::GetRemoteType() {
+ RecursiveMutexAutoLock lock(mMutex);
+ return mRemoteType;
+}
+
+bool ThreadsafeContentParentHandle::MaybeRegisterRemoteWorkerActor(
+ MoveOnlyFunction<bool(uint32_t, bool)> aCallback) {
+ RecursiveMutexAutoLock lock(mMutex);
+ if (aCallback(mRemoteWorkerActorCount, mShutdownStarted)) {
+ // TODO: I'd wish we could assert here that our ContentParent is alive.
+ ++mRemoteWorkerActorCount;
+ return true;
+ }
+ return false;
+}
+
+} // namespace dom
+} // namespace mozilla
+
+NS_IMPL_ISUPPORTS(ParentIdleListener, nsIObserver)
+
+NS_IMETHODIMP
+ParentIdleListener::Observe(nsISupports*, const char* aTopic,
+ const char16_t* aData) {
+ mozilla::Unused << mParent->SendNotifyIdleObserver(
+ mObserver, nsDependentCString(aTopic), nsDependentString(aData));
+ return NS_OK;
+}
+
+#undef LOGPDM
diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h
new file mode 100644
index 0000000000..53994e45e7
--- /dev/null
+++ b/dom/ipc/ContentParent.h
@@ -0,0 +1,1699 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_ContentParent_h
+#define mozilla_dom_ContentParent_h
+
+#include "mozilla/dom/PContentParent.h"
+#include "mozilla/dom/ipc/IdType.h"
+#include "mozilla/dom/MessageManagerCallback.h"
+#include "mozilla/dom/MediaSessionBinding.h"
+#include "mozilla/dom/RemoteBrowser.h"
+#include "mozilla/dom/RemoteType.h"
+#include "mozilla/dom/JSProcessActorParent.h"
+#include "mozilla/dom/ProcessActor.h"
+#include "mozilla/dom/UserActivation.h"
+#include "mozilla/gfx/gfxVarReceiver.h"
+#include "mozilla/gfx/GPUProcessListener.h"
+#include "mozilla/ipc/BackgroundUtils.h"
+#include "mozilla/ipc/GeckoChildProcessHost.h"
+#include "mozilla/ipc/InputStreamUtils.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/DataMutex.h"
+#include "mozilla/HalTypes.h"
+#include "mozilla/LinkedList.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/MemoryReportingProcess.h"
+#include "mozilla/MozPromise.h"
+#include "mozilla/RecursiveMutex.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/UniquePtr.h"
+
+#include "MainThreadUtils.h"
+#include "nsClassHashtable.h"
+#include "nsTHashMap.h"
+#include "nsTHashSet.h"
+#include "nsHashKeys.h"
+#include "nsIAsyncShutdown.h"
+#include "nsIDOMProcessParent.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIObserver.h"
+#include "nsIRemoteTab.h"
+#include "nsIDOMGeoPositionCallback.h"
+#include "nsIDOMGeoPositionErrorCallback.h"
+#include "nsRefPtrHashtable.h"
+#include "PermissionMessageUtils.h"
+#include "DriverCrashGuard.h"
+#include "nsIReferrerInfo.h"
+
+#define CHILD_PROCESS_SHUTDOWN_MESSAGE u"child-process-shutdown"_ns
+
+class nsConsoleService;
+class nsIContentProcessInfo;
+class nsICycleCollectorLogSink;
+class nsIDumpGCAndCCLogsCallback;
+class nsIRemoteTab;
+class nsITimer;
+class ParentIdleListener;
+class nsIWidget;
+class nsIX509Cert;
+
+namespace mozilla {
+class PClipboardWriteRequestParent;
+class PRemoteSpellcheckEngineParent;
+
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+class SandboxBroker;
+class SandboxBrokerPolicyFactory;
+#endif
+
+class PreallocatedProcessManagerImpl;
+class BenchmarkStorageParent;
+
+using mozilla::loader::PScriptCacheParent;
+
+namespace ipc {
+class CrashReporterHost;
+class TestShellParent;
+class SharedPreferenceSerializer;
+} // namespace ipc
+
+namespace layers {
+struct TextureFactoryIdentifier;
+} // namespace layers
+
+namespace dom {
+
+class BrowsingContextGroup;
+class Element;
+class BrowserParent;
+class ClonedMessageData;
+class MemoryReport;
+class TabContext;
+class GetFilesHelper;
+class MemoryReportRequestHost;
+class RemoteWorkerManager;
+class ThreadsafeContentParentHandle;
+struct CancelContentJSOptions;
+
+#define NS_CONTENTPARENT_IID \
+ { \
+ 0xeeec9ebf, 0x8ecf, 0x4e38, { \
+ 0x81, 0xda, 0xb7, 0x34, 0x13, 0x7e, 0xac, 0xf3 \
+ } \
+ }
+
+class ContentParent final : public PContentParent,
+ public nsIDOMProcessParent,
+ public nsIObserver,
+ public nsIDOMGeoPositionCallback,
+ public nsIDOMGeoPositionErrorCallback,
+ public nsIAsyncShutdownBlocker,
+ public nsIInterfaceRequestor,
+ public gfx::gfxVarReceiver,
+ public mozilla::LinkedListElement<ContentParent>,
+ public gfx::GPUProcessListener,
+ public mozilla::MemoryReportingProcess,
+ public mozilla::dom::ipc::MessageManagerCallback,
+ public mozilla::ipc::IShmemAllocator,
+ public ProcessActor {
+ typedef mozilla::ipc::GeckoChildProcessHost GeckoChildProcessHost;
+ typedef mozilla::ipc::TestShellParent TestShellParent;
+ typedef mozilla::ipc::PrincipalInfo PrincipalInfo;
+ typedef mozilla::dom::ClonedMessageData ClonedMessageData;
+ typedef mozilla::dom::BrowsingContextGroup BrowsingContextGroup;
+
+ friend class mozilla::PreallocatedProcessManagerImpl;
+ friend class PContentParent;
+ friend class mozilla::dom::RemoteWorkerManager;
+
+ public:
+ using LaunchPromise =
+ mozilla::MozPromise<RefPtr<ContentParent>, nsresult, false>;
+
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_CONTENTPARENT_IID)
+
+ static LogModule* GetLog();
+
+ static ContentParent* Cast(PContentParent* aActor) {
+ return static_cast<ContentParent*>(aActor);
+ }
+
+ /**
+ * Create a ContentParent suitable for use later as a content process.
+ */
+ static already_AddRefed<ContentParent> MakePreallocProcess();
+
+ /**
+ * Start up the content-process machinery. This might include
+ * scheduling pre-launch tasks.
+ */
+ static void StartUp();
+
+ /** Shut down the content-process machinery. */
+ static void ShutDown();
+
+ static uint32_t GetPoolSize(const nsACString& aContentProcessType);
+
+ static uint32_t GetMaxProcessCount(const nsACString& aContentProcessType);
+
+ static bool IsMaxProcessCountReached(const nsACString& aContentProcessType);
+
+ static void ReleaseCachedProcesses();
+
+ static void LogAndAssertFailedPrincipalValidationInfo(
+ nsIPrincipal* aPrincipal, const char* aMethod);
+
+ /**
+ * Picks a random content parent from |aContentParents| respecting the index
+ * limit set by |aMaxContentParents|.
+ * Returns null if non available.
+ */
+ static already_AddRefed<ContentParent> MinTabSelect(
+ const nsTArray<ContentParent*>& aContentParents,
+ int32_t maxContentParents);
+
+ /**
+ * Get or create a content process for:
+ * 1. browser iframe
+ * 2. remote xul <browser>
+ * 3. normal iframe
+ */
+ static RefPtr<ContentParent::LaunchPromise> GetNewOrUsedBrowserProcessAsync(
+ const nsACString& aRemoteType, BrowsingContextGroup* aGroup = nullptr,
+ hal::ProcessPriority aPriority =
+ hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND,
+ bool aPreferUsed = false);
+ static already_AddRefed<ContentParent> GetNewOrUsedBrowserProcess(
+ const nsACString& aRemoteType, BrowsingContextGroup* aGroup = nullptr,
+ hal::ProcessPriority aPriority =
+ hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND,
+ bool aPreferUsed = false);
+
+ /**
+ * Get or create a content process, but without waiting for the process
+ * launch to have completed. The returned `ContentParent` may still be in the
+ * "Launching" state.
+ *
+ * Can return `nullptr` in the case of an error.
+ *
+ * Use the `WaitForLaunchAsync` or `WaitForLaunchSync` methods to wait for
+ * the process to be fully launched.
+ */
+ static already_AddRefed<ContentParent> GetNewOrUsedLaunchingBrowserProcess(
+ const nsACString& aRemoteType, BrowsingContextGroup* aGroup = nullptr,
+ hal::ProcessPriority aPriority =
+ hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND,
+ bool aPreferUsed = false);
+
+ RefPtr<ContentParent::LaunchPromise> WaitForLaunchAsync(
+ hal::ProcessPriority aPriority =
+ hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND);
+ bool WaitForLaunchSync(hal::ProcessPriority aPriority =
+ hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND);
+
+ /**
+ * Get or create a content process for the given TabContext. aFrameElement
+ * should be the frame/iframe element with which this process will
+ * associated.
+ */
+ static already_AddRefed<RemoteBrowser> CreateBrowser(
+ const TabContext& aContext, Element* aFrameElement,
+ const nsACString& aRemoteType, BrowsingContext* aBrowsingContext,
+ ContentParent* aOpenerContentParent);
+
+ /**
+ * Get all content parents.
+ *
+ * # Lifetime
+ *
+ * These pointers are ONLY valid for synchronous use from the main thread.
+ *
+ * Do NOT attempt to use them after the main thread has had a chance to handle
+ * messages or you could end up with dangling pointers.
+ */
+ static void GetAll(nsTArray<ContentParent*>& aArray);
+
+ static void GetAllEvenIfDead(nsTArray<ContentParent*>& aArray);
+
+ static void BroadcastStringBundle(const StringBundleDescriptor&);
+
+ static void BroadcastFontListChanged();
+ static void BroadcastShmBlockAdded(uint32_t aGeneration, uint32_t aIndex);
+
+ static void BroadcastThemeUpdate(widget::ThemeChangeKind);
+
+ static void BroadcastMediaCodecsSupportedUpdate(
+ RemoteDecodeIn aLocation, const media::MediaCodecsSupported& aSupported);
+
+ const nsACString& GetRemoteType() const override;
+
+ virtual void DoGetRemoteType(nsACString& aRemoteType,
+ ErrorResult& aError) const override {
+ aRemoteType = GetRemoteType();
+ }
+
+ enum CPIteratorPolicy { eLive, eAll };
+
+ class ContentParentIterator {
+ private:
+ ContentParent* mCurrent;
+ CPIteratorPolicy mPolicy;
+
+ public:
+ ContentParentIterator(CPIteratorPolicy aPolicy, ContentParent* aCurrent)
+ : mCurrent(aCurrent), mPolicy(aPolicy) {}
+
+ ContentParentIterator begin() {
+ // Move the cursor to the first element that matches the policy.
+ while (mPolicy != eAll && mCurrent && !mCurrent->IsAlive()) {
+ mCurrent = mCurrent->LinkedListElement<ContentParent>::getNext();
+ }
+
+ return *this;
+ }
+ ContentParentIterator end() {
+ return ContentParentIterator(mPolicy, nullptr);
+ }
+
+ const ContentParentIterator& operator++() {
+ MOZ_ASSERT(mCurrent);
+ do {
+ mCurrent = mCurrent->LinkedListElement<ContentParent>::getNext();
+ } while (mPolicy != eAll && mCurrent && !mCurrent->IsAlive());
+
+ return *this;
+ }
+
+ bool operator!=(const ContentParentIterator& aOther) const {
+ MOZ_ASSERT(mPolicy == aOther.mPolicy);
+ return mCurrent != aOther.mCurrent;
+ }
+
+ ContentParent* operator*() { return mCurrent; }
+ };
+
+ static ContentParentIterator AllProcesses(CPIteratorPolicy aPolicy) {
+ ContentParent* first =
+ sContentParents ? sContentParents->getFirst() : nullptr;
+ return ContentParentIterator(aPolicy, first);
+ }
+
+ static void NotifyUpdatedDictionaries();
+
+ // Tell content processes the font list has changed. If aFullRebuild is true,
+ // the shared list has been rebuilt and must be freshly mapped by child
+ // processes; if false, existing mappings are still valid but the data has
+ // been updated and so full reflows are in order.
+ static void NotifyUpdatedFonts(bool aFullRebuild);
+
+ mozilla::ipc::IPCResult RecvCreateGMPService();
+
+ NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ContentParent, nsIObserver)
+
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_NSIDOMPROCESSPARENT
+ NS_DECL_NSIOBSERVER
+ NS_DECL_NSIDOMGEOPOSITIONCALLBACK
+ NS_DECL_NSIDOMGEOPOSITIONERRORCALLBACK
+ NS_DECL_NSIASYNCSHUTDOWNBLOCKER
+ NS_DECL_NSIINTERFACEREQUESTOR
+
+ /**
+ * MessageManagerCallback methods that we override.
+ */
+ virtual bool DoLoadMessageManagerScript(const nsAString& aURL,
+ bool aRunInGlobalScope) override;
+
+ virtual nsresult DoSendAsyncMessage(const nsAString& aMessage,
+ StructuredCloneData& aData) override;
+
+ RecursiveMutex& ThreadsafeHandleMutex();
+
+ /** Notify that a tab is about to send Destroy to its child. */
+ void NotifyTabWillDestroy();
+
+ /** Notify that a tab is beginning its destruction sequence. */
+ void NotifyTabDestroying();
+
+ /** Notify that a tab was destroyed during normal operation. */
+ void NotifyTabDestroyed(const TabId& aTabId, bool aNotifiedDestroying);
+
+ // Manage the set of `KeepAlive`s on this ContentParent which are preventing
+ // it from being destroyed.
+ void AddKeepAlive();
+ void RemoveKeepAlive();
+
+ TestShellParent* CreateTestShell();
+
+ bool DestroyTestShell(TestShellParent* aTestShell);
+
+ TestShellParent* GetTestShellSingleton();
+
+ // This method can be called on any thread.
+ void RegisterRemoteWorkerActor();
+
+ // This method _must_ be called on main-thread because it can start the
+ // shutting down of the content process.
+ void UnregisterRemoveWorkerActor();
+
+ void ReportChildAlreadyBlocked();
+
+ bool RequestRunToCompletion();
+
+ void UpdateCookieStatus(nsIChannel* aChannel);
+
+ bool IsLaunching() const {
+ return mLifecycleState == LifecycleState::LAUNCHING;
+ }
+ bool IsAlive() const override;
+ bool IsInitialized() const;
+ bool IsSignaledImpendingShutdown() const {
+ return mIsSignaledImpendingShutdown;
+ }
+ bool IsShuttingDown() const {
+ return IsDead() || IsSignaledImpendingShutdown();
+ }
+ bool IsDead() const { return mLifecycleState == LifecycleState::DEAD; }
+
+ bool IsForBrowser() const { return mIsForBrowser; }
+
+ GeckoChildProcessHost* Process() const { return mSubprocess; }
+
+ nsIContentProcessInfo* ScriptableHelper() const { return mScriptableHelper; }
+
+ mozilla::dom::ProcessMessageManager* GetMessageManager() const {
+ return mMessageManager;
+ }
+
+ bool NeedsPermissionsUpdate(const nsACString& aPermissionKey) const;
+
+ // Getter for which permission keys should signal that a content
+ // process needs to know about the change of a permission with this as the
+ // secondary key, like for 3rdPartyFrameStorage^https://secondary.com
+ bool NeedsSecondaryKeyPermissionsUpdate(
+ const nsACString& aPermissionKey) const;
+
+ // Manage pending load states which have been sent to this process, and are
+ // expected to be used to start a load imminently.
+ already_AddRefed<nsDocShellLoadState> TakePendingLoadStateForId(
+ uint64_t aLoadIdentifier);
+ void StorePendingLoadState(nsDocShellLoadState* aLoadState);
+
+ /**
+ * Kill our subprocess and make sure it dies. Should only be used
+ * in emergency situations since it bypasses the normal shutdown
+ * process.
+ *
+ * WARNING: aReason appears in telemetry, so any new value passed in requires
+ * data review.
+ */
+ void KillHard(const char* aWhy);
+
+ ContentParentId ChildID() const { return mChildID; }
+
+ /**
+ * Get a user-friendly name for this ContentParent. We make no guarantees
+ * about this name: It might not be unique, apps can spoof special names,
+ * etc. So please don't use this name to make any decisions about the
+ * ContentParent based on the value returned here.
+ */
+ void FriendlyName(nsAString& aName, bool aAnonymize = false);
+
+ virtual void OnChannelError() override;
+
+ mozilla::ipc::IPCResult RecvInitCrashReporter(
+ const NativeThreadId& aThreadId);
+
+ already_AddRefed<PNeckoParent> AllocPNeckoParent();
+
+ virtual mozilla::ipc::IPCResult RecvPNeckoConstructor(
+ PNeckoParent* aActor) override {
+ return PContentParent::RecvPNeckoConstructor(aActor);
+ }
+
+ mozilla::ipc::IPCResult RecvInitStreamFilter(
+ const uint64_t& aChannelId, const nsAString& aAddonId,
+ InitStreamFilterResolver&& aResolver);
+
+ PHalParent* AllocPHalParent();
+
+ virtual mozilla::ipc::IPCResult RecvPHalConstructor(
+ PHalParent* aActor) override {
+ return PContentParent::RecvPHalConstructor(aActor);
+ }
+
+ PHeapSnapshotTempFileHelperParent* AllocPHeapSnapshotTempFileHelperParent();
+
+ PRemoteSpellcheckEngineParent* AllocPRemoteSpellcheckEngineParent();
+
+ bool CycleCollectWithLogs(bool aDumpAllTraces,
+ nsICycleCollectorLogSink* aSink,
+ nsIDumpGCAndCCLogsCallback* aCallback);
+
+ mozilla::ipc::IPCResult RecvNotifyTabDestroying(const TabId& aTabId,
+ const ContentParentId& aCpId);
+
+ mozilla::ipc::IPCResult RecvFinishShutdown();
+
+ mozilla::ipc::IPCResult RecvNotifyShutdownSuccess();
+
+ void MaybeInvokeDragSession(BrowserParent* aParent, EventMessage aMessage);
+
+ PContentPermissionRequestParent* AllocPContentPermissionRequestParent(
+ const nsTArray<PermissionRequest>& aRequests, nsIPrincipal* aPrincipal,
+ nsIPrincipal* aTopLevelPrincipal, const bool& aIsHandlingUserInput,
+ const bool& aMaybeUnsafePermissionDelegate, const TabId& aTabId);
+
+ bool DeallocPContentPermissionRequestParent(
+ PContentPermissionRequestParent* actor);
+
+ void ForkNewProcess(bool aBlocking);
+
+ mozilla::ipc::IPCResult RecvCreateWindow(
+ PBrowserParent* aThisBrowserParent,
+ const MaybeDiscarded<BrowsingContext>& aParent, PBrowserParent* aNewTab,
+ const uint32_t& aChromeFlags, const bool& aCalledFromJS,
+ const bool& aForPrinting, const bool& aForWindowDotPrint,
+ nsIURI* aURIToLoad, const nsACString& aFeatures,
+ const UserActivation::Modifiers& aModifiers,
+ nsIPrincipal* aTriggeringPrincipal, nsIContentSecurityPolicy* aCsp,
+ nsIReferrerInfo* aReferrerInfo, const OriginAttributes& aOriginAttributes,
+ CreateWindowResolver&& aResolve);
+
+ mozilla::ipc::IPCResult RecvCreateWindowInDifferentProcess(
+ PBrowserParent* aThisTab, const MaybeDiscarded<BrowsingContext>& aParent,
+ const uint32_t& aChromeFlags, const bool& aCalledFromJS,
+ nsIURI* aURIToLoad, const nsACString& aFeatures,
+ const UserActivation::Modifiers& aModifiers, const nsAString& aName,
+ nsIPrincipal* aTriggeringPrincipal, nsIContentSecurityPolicy* aCsp,
+ nsIReferrerInfo* aReferrerInfo,
+ const OriginAttributes& aOriginAttributes);
+
+ static void BroadcastBlobURLRegistration(
+ const nsACString& aURI, BlobImpl* aBlobImpl, nsIPrincipal* aPrincipal,
+ const nsCString& aPartitionKey, ContentParent* aIgnoreThisCP = nullptr);
+
+ static void BroadcastBlobURLUnregistration(
+ const nsACString& aURI, nsIPrincipal* aPrincipal,
+ ContentParent* aIgnoreThisCP = nullptr);
+
+ mozilla::ipc::IPCResult RecvStoreAndBroadcastBlobURLRegistration(
+ const nsACString& aURI, const IPCBlob& aBlob, nsIPrincipal* aPrincipal,
+ const nsCString& aPartitionKey);
+
+ mozilla::ipc::IPCResult RecvUnstoreAndBroadcastBlobURLUnregistration(
+ const nsACString& aURI, nsIPrincipal* aPrincipal);
+
+ virtual int32_t Pid() const override;
+
+ // PURLClassifierParent.
+ PURLClassifierParent* AllocPURLClassifierParent(nsIPrincipal* aPrincipal,
+ bool* aSuccess);
+ virtual mozilla::ipc::IPCResult RecvPURLClassifierConstructor(
+ PURLClassifierParent* aActor, nsIPrincipal* aPrincipal,
+ bool* aSuccess) override;
+
+ // PURLClassifierLocalParent.
+ PURLClassifierLocalParent* AllocPURLClassifierLocalParent(
+ nsIURI* aURI, const nsTArray<IPCURLClassifierFeature>& aFeatures);
+
+ virtual mozilla::ipc::IPCResult RecvPURLClassifierLocalConstructor(
+ PURLClassifierLocalParent* aActor, nsIURI* aURI,
+ nsTArray<IPCURLClassifierFeature>&& aFeatures) override;
+
+ PSessionStorageObserverParent* AllocPSessionStorageObserverParent();
+
+ virtual mozilla::ipc::IPCResult RecvPSessionStorageObserverConstructor(
+ PSessionStorageObserverParent* aActor) override;
+
+ bool DeallocPSessionStorageObserverParent(
+ PSessionStorageObserverParent* aActor);
+
+ bool DeallocPURLClassifierLocalParent(PURLClassifierLocalParent* aActor);
+
+ bool DeallocPURLClassifierParent(PURLClassifierParent* aActor);
+
+ // Use the PHangMonitor channel to ask the child to repaint a tab.
+ void PaintTabWhileInterruptingJS(BrowserParent*);
+
+ void UnloadLayersWhileInterruptingJS(BrowserParent*);
+
+ void CancelContentJSExecutionIfRunning(
+ BrowserParent* aBrowserParent,
+ nsIRemoteTab::NavigationType aNavigationType,
+ const CancelContentJSOptions& aCancelContentJSOptions);
+
+ void SetMainThreadQoSPriority(nsIThread::QoSPriority aQoSPriority);
+
+ // This function is called when we are about to load a document from an
+ // HTTP(S) or FTP channel for a content process. It is a useful place
+ // to start to kick off work as early as possible in response to such
+ // document loads.
+ // aShouldWaitForPermissionCookieUpdate is set to true if main thread IPCs for
+ // updating permissions/cookies are sent.
+ nsresult AboutToLoadHttpFtpDocumentForChild(
+ nsIChannel* aChannel,
+ bool* aShouldWaitForPermissionCookieUpdate = nullptr);
+
+ // Send Blob URLs for this aPrincipal if they are not already known to this
+ // content process and mark the process to receive any new/revoked Blob URLs
+ // to this content process forever.
+ void TransmitBlobURLsForPrincipal(nsIPrincipal* aPrincipal);
+
+ nsresult TransmitPermissionsForPrincipal(nsIPrincipal* aPrincipal);
+
+ // Whenever receiving a Principal we need to validate that Principal case
+ // by case, where we grant individual callsites to customize the checks!
+ enum class ValidatePrincipalOptions {
+ AllowNullPtr, // Not a NullPrincipal but a nullptr as Principal.
+ AllowSystem,
+ AllowExpanded,
+ };
+ bool ValidatePrincipal(
+ nsIPrincipal* aPrincipal,
+ const EnumSet<ValidatePrincipalOptions>& aOptions = {});
+
+ // This function is called in BrowsingContext immediately before IPC call to
+ // load a URI. If aURI is a BlobURL, this method transmits all BlobURLs for
+ // aURI's principal that were previously not transmitted. This allows for
+ // opening a locally created BlobURL in a new tab.
+ //
+ // The reason all previously untransmitted Blobs are transmitted is that the
+ // current BlobURL could contain html code, referring to another untransmitted
+ // BlobURL.
+ //
+ // Should eventually be made obsolete by broader design changes that only
+ // store BlobURLs in the parent process.
+ void TransmitBlobDataIfBlobURL(nsIURI* aURI);
+
+ void OnCompositorDeviceReset() override;
+
+ // Control the priority of the IPC messages for input events.
+ void SetInputPriorityEventEnabled(bool aEnabled);
+ bool IsInputPriorityEventEnabled() { return mIsInputPriorityEventEnabled; }
+
+ static bool IsInputEventQueueSupported();
+
+ mozilla::ipc::IPCResult RecvCreateBrowsingContext(
+ uint64_t aGroupId, BrowsingContext::IPCInitializer&& aInit);
+
+ mozilla::ipc::IPCResult RecvDiscardBrowsingContext(
+ const MaybeDiscarded<BrowsingContext>& aContext, bool aDoDiscard,
+ DiscardBrowsingContextResolver&& aResolve);
+
+ mozilla::ipc::IPCResult RecvWindowClose(
+ const MaybeDiscarded<BrowsingContext>& aContext, bool aTrustedCaller);
+ mozilla::ipc::IPCResult RecvWindowFocus(
+ const MaybeDiscarded<BrowsingContext>& aContext, CallerType aCallerType,
+ uint64_t aActionId);
+ mozilla::ipc::IPCResult RecvWindowBlur(
+ const MaybeDiscarded<BrowsingContext>& aContext, CallerType aCallerType);
+ mozilla::ipc::IPCResult RecvRaiseWindow(
+ const MaybeDiscarded<BrowsingContext>& aContext, CallerType aCallerType,
+ uint64_t aActionId);
+ mozilla::ipc::IPCResult RecvAdjustWindowFocus(
+ const MaybeDiscarded<BrowsingContext>& aContext, bool aIsVisible,
+ uint64_t aActionId);
+ mozilla::ipc::IPCResult RecvClearFocus(
+ const MaybeDiscarded<BrowsingContext>& aContext);
+ mozilla::ipc::IPCResult RecvSetFocusedBrowsingContext(
+ const MaybeDiscarded<BrowsingContext>& aContext, uint64_t aActionId);
+ mozilla::ipc::IPCResult RecvSetActiveBrowsingContext(
+ const MaybeDiscarded<BrowsingContext>& aContext, uint64_t aActionId);
+ mozilla::ipc::IPCResult RecvUnsetActiveBrowsingContext(
+ const MaybeDiscarded<BrowsingContext>& aContext, uint64_t aActionId);
+ mozilla::ipc::IPCResult RecvSetFocusedElement(
+ const MaybeDiscarded<BrowsingContext>& aContext, bool aNeedsFocus);
+ mozilla::ipc::IPCResult RecvFinalizeFocusOuter(
+ const MaybeDiscarded<BrowsingContext>& aContext, bool aCanFocus,
+ CallerType aCallerType);
+ mozilla::ipc::IPCResult RecvInsertNewFocusActionId(uint64_t aActionId);
+ mozilla::ipc::IPCResult RecvBlurToParent(
+ const MaybeDiscarded<BrowsingContext>& aFocusedBrowsingContext,
+ const MaybeDiscarded<BrowsingContext>& aBrowsingContextToClear,
+ const MaybeDiscarded<BrowsingContext>& aAncestorBrowsingContextToFocus,
+ bool aIsLeavingDocument, bool aAdjustWidget,
+ bool aBrowsingContextToClearHandled,
+ bool aAncestorBrowsingContextToFocusHandled, uint64_t aActionId);
+ mozilla::ipc::IPCResult RecvMaybeExitFullscreen(
+ const MaybeDiscarded<BrowsingContext>& aContext);
+
+ mozilla::ipc::IPCResult RecvWindowPostMessage(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const ClonedOrErrorMessageData& aMessage, const PostMessageData& aData);
+
+ FORWARD_SHMEM_ALLOCATOR_TO(PContentParent)
+
+ mozilla::ipc::IPCResult RecvBlobURLDataRequest(
+ const nsACString& aBlobURL, nsIPrincipal* pTriggeringPrincipal,
+ nsIPrincipal* pLoadingPrincipal,
+ const OriginAttributes& aOriginAttributes, uint64_t aInnerWindowId,
+ const nsCString& aPartitionKey, BlobURLDataRequestResolver&& aResolver);
+
+ protected:
+ bool CheckBrowsingContextEmbedder(CanonicalBrowsingContext* aBC,
+ const char* aOperation) const;
+
+ void ActorDestroy(ActorDestroyReason why) override;
+
+ bool ShouldContinueFromReplyTimeout() override;
+
+ void OnVarChanged(const GfxVarUpdate& aVar) override;
+ void OnCompositorUnexpectedShutdown() override;
+
+ private:
+ /**
+ * A map of the remote content process type to a list of content parents
+ * currently available to host *new* tabs/frames of that type.
+ *
+ * If a content process is identified as troubled or dead, it will be
+ * removed from this list, but will still be in the sContentParents list for
+ * the GetAll/GetAllEvenIfDead APIs.
+ */
+ static nsClassHashtable<nsCStringHashKey, nsTArray<ContentParent*>>*
+ sBrowserContentParents;
+ static mozilla::StaticAutoPtr<LinkedList<ContentParent>> sContentParents;
+
+ /**
+ * In order to avoid rapidly creating and destroying content processes when
+ * running under e10s, we may keep alive a single unused "web" content
+ * process if it previously had a very short lifetime.
+ *
+ * This process will be re-used during process selection, avoiding spawning a
+ * new process, if the "web" remote type is being requested.
+ */
+ static StaticRefPtr<ContentParent> sRecycledE10SProcess;
+
+ void AddShutdownBlockers();
+ void RemoveShutdownBlockers();
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+ // Cached Mac sandbox params used when launching content processes.
+ static mozilla::StaticAutoPtr<std::vector<std::string>> sMacSandboxParams;
+#endif
+
+ // Set aLoadUri to true to load aURIToLoad and to false to only create the
+ // window. aURIToLoad should always be provided, if available, to ensure
+ // compatibility with GeckoView.
+ mozilla::ipc::IPCResult 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,
+ const UserActivation::Modifiers& aModifiers,
+ BrowserParent* aNextRemoteBrowser, const nsAString& aName,
+ nsresult& aResult, nsCOMPtr<nsIRemoteTab>& aNewRemoteTab,
+ bool* aWindowIsNew, int32_t& aOpenLocation,
+ nsIPrincipal* aTriggeringPrincipal, nsIReferrerInfo* aReferrerInfo,
+ bool aLoadUri, nsIContentSecurityPolicy* aCsp,
+ const OriginAttributes& aOriginAttributes);
+
+ explicit ContentParent(const nsACString& aRemoteType);
+
+ // Launch the subprocess and associated initialization.
+ // Returns false if the process fails to start.
+ // Deprecated in favor of LaunchSubprocessAsync.
+ bool LaunchSubprocessSync(hal::ProcessPriority aInitialPriority);
+
+ // Launch the subprocess and associated initialization;
+ // returns a promise and signals failure by rejecting.
+ // OS-level launching work is dispatched to another thread, but some
+ // initialization (creating IPDL actors, etc.; see Init()) is run on
+ // the main thread.
+ RefPtr<LaunchPromise> LaunchSubprocessAsync(
+ hal::ProcessPriority aInitialPriority);
+
+ // Common implementation of LaunchSubprocess{Sync,Async}.
+ // Return `true` in case of success, `false` if launch was
+ // aborted because of shutdown.
+ bool BeginSubprocessLaunch(ProcessPriority aPriority);
+ void LaunchSubprocessReject();
+ bool LaunchSubprocessResolve(bool aIsSync, ProcessPriority aPriority);
+
+ // Common initialization after sub process launch.
+ bool InitInternal(ProcessPriority aPriority);
+
+ // Generate a minidump for the child process and one for the main process
+ void GeneratePairedMinidump(const char* aReason);
+ void HandleOrphanedMinidump(nsString* aDumpId);
+
+ virtual ~ContentParent();
+
+ void Init();
+
+ // Some information could be sent to content very early, it
+ // should be send from this function. This function should only be
+ // called after the process has been transformed to browser.
+ void ForwardKnownInfo();
+
+ /**
+ * We might want to reuse barely used content processes if certain criteria
+ * are met.
+ *
+ * With Fission this is a no-op.
+ */
+ bool TryToRecycleE10SOnly();
+
+ /**
+ * If this process is currently being recycled, unmark it as the recycled
+ * content process.
+ * If `aForeground` is true, will also restore the process' foreground
+ * priority if it was previously the recycled content process.
+ *
+ * With Fission this is a no-op.
+ */
+ void StopRecyclingE10SOnly(bool aForeground);
+
+ /**
+ * Removing it from the static array so it won't be returned for new tabs in
+ * GetNewOrUsedBrowserProcess.
+ */
+ void RemoveFromList();
+
+ /**
+ * Return if the process has an active worker.
+ */
+ bool HasActiveWorker();
+
+ /**
+ * Decide whether the process should be kept alive even when it would normally
+ * be shut down, for example when all its tabs are closed.
+ */
+ bool ShouldKeepProcessAlive();
+
+ /**
+ * Mark this ContentParent as dead for the purposes of Get*().
+ * This method is idempotent.
+ */
+ void MarkAsDead();
+
+ /**
+ * Let the process know we are about to send a shutdown through a
+ * non-mainthread side channel in order to bypass mainthread congestion.
+ * This potentially cancels mainthread content JS execution.
+ */
+ void SignalImpendingShutdownToContentJS();
+
+ bool CheckTabDestroyWillKeepAlive(uint32_t aExpectedBrowserCount);
+
+ /**
+ * Check if this process is ready to be shut down, and if it is, begin the
+ * shutdown process. Should be called whenever a change occurs which could
+ * cause the decisions made by `ShouldKeepProcessAlive` to change.
+ *
+ * @param aExpectedBrowserCount The number of PBrowser actors which should
+ * not block shutdown. This should usually be 0.
+ * @param aSendShutDown If true, will send the shutdown message in addition
+ * to marking the process as dead and starting the force
+ * kill timer.
+ */
+ void MaybeBeginShutDown(uint32_t aExpectedBrowserCount = 0,
+ bool aSendShutDown = true);
+
+ /**
+ * How we will shut down this ContentParent and its subprocess.
+ */
+ enum ShutDownMethod {
+ // Send a shutdown message and wait for FinishShutdown call back.
+ SEND_SHUTDOWN_MESSAGE,
+ // Close the channel ourselves and let the subprocess clean up itself.
+ CLOSE_CHANNEL,
+ };
+
+ void AsyncSendShutDownMessage();
+
+ /**
+ * Exit the subprocess and vamoose. After this call IsAlive()
+ * will return false and this ContentParent will not be returned
+ * by the Get*() funtions. However, the shutdown sequence itself
+ * may be asynchronous.
+ */
+ bool ShutDownProcess(ShutDownMethod aMethod);
+
+ // Perform any steps necesssary to gracefully shtudown the message
+ // manager and null out mMessageManager.
+ void ShutDownMessageManager();
+
+ // Start the send shutdown timer on shutdown.
+ void StartSendShutdownTimer();
+
+ // Start the force-kill timer on shutdown.
+ void StartForceKillTimer();
+
+ // Ensure that the permissions for the giben Permission key are set in the
+ // content process.
+ //
+ // See nsIPermissionManager::GetPermissionsForKey for more information on
+ // these keys.
+ void EnsurePermissionsByKey(const nsACString& aKey,
+ const nsACString& aOrigin);
+
+ static void SendShutdownTimerCallback(nsITimer* aTimer, void* aClosure);
+ static void ForceKillTimerCallback(nsITimer* aTimer, void* aClosure);
+
+ bool CanOpenBrowser(const IPCTabContext& aContext);
+
+ /**
+ * Get or create the corresponding content parent array to
+ * |aContentProcessType|.
+ */
+ static nsTArray<ContentParent*>& GetOrCreatePool(
+ const nsACString& aContentProcessType);
+
+ mozilla::ipc::IPCResult RecvInitBackground(
+ Endpoint<mozilla::ipc::PBackgroundStarterParent>&& aEndpoint);
+
+ mozilla::ipc::IPCResult RecvAddMemoryReport(const MemoryReport& aReport);
+
+ bool DeallocPRemoteSpellcheckEngineParent(PRemoteSpellcheckEngineParent*);
+
+ mozilla::ipc::IPCResult RecvCloneDocumentTreeInto(
+ const MaybeDiscarded<BrowsingContext>& aSource,
+ const MaybeDiscarded<BrowsingContext>& aTarget, PrintData&& aPrintData);
+
+ mozilla::ipc::IPCResult RecvUpdateRemotePrintSettings(
+ const MaybeDiscarded<BrowsingContext>& aTarget, PrintData&& aPrintData);
+
+ mozilla::ipc::IPCResult RecvConstructPopupBrowser(
+ ManagedEndpoint<PBrowserParent>&& actor,
+ ManagedEndpoint<PWindowGlobalParent>&& windowEp, const TabId& tabId,
+ const IPCTabContext& context, const WindowGlobalInit& initialWindowInit,
+ const uint32_t& chromeFlags);
+
+ mozilla::ipc::IPCResult RecvIsSecureURI(
+ nsIURI* aURI, const OriginAttributes& aOriginAttributes,
+ bool* aIsSecureURI);
+
+ mozilla::ipc::IPCResult RecvAccumulateMixedContentHSTS(
+ nsIURI* aURI, const bool& aActive,
+ const OriginAttributes& aOriginAttributes);
+
+ bool DeallocPHalParent(PHalParent*);
+
+ bool DeallocPHeapSnapshotTempFileHelperParent(
+ PHeapSnapshotTempFileHelperParent*);
+
+ PCycleCollectWithLogsParent* AllocPCycleCollectWithLogsParent(
+ const bool& aDumpAllTraces, const FileDescriptor& aGCLog,
+ const FileDescriptor& aCCLog);
+
+ bool DeallocPCycleCollectWithLogsParent(PCycleCollectWithLogsParent* aActor);
+
+ PScriptCacheParent* AllocPScriptCacheParent(const FileDescOrError& cacheFile,
+ const bool& wantCacheData);
+
+ bool DeallocPScriptCacheParent(PScriptCacheParent* shell);
+
+ already_AddRefed<PExternalHelperAppParent> AllocPExternalHelperAppParent(
+ nsIURI* aUri, const mozilla::net::LoadInfoArgs& aLoadInfoArgs,
+ const nsACString& aMimeContentType, const nsACString& aContentDisposition,
+ const uint32_t& aContentDispositionHint,
+ const nsAString& aContentDispositionFilename, const bool& aForceSave,
+ const int64_t& aContentLength, const bool& aWasFileChannel,
+ nsIURI* aReferrer, const MaybeDiscarded<BrowsingContext>& aContext,
+ const bool& aShouldCloseWindow);
+
+ mozilla::ipc::IPCResult RecvPExternalHelperAppConstructor(
+ PExternalHelperAppParent* actor, nsIURI* uri,
+ const LoadInfoArgs& loadInfoArgs, const nsACString& aMimeContentType,
+ const nsACString& aContentDisposition,
+ const uint32_t& aContentDispositionHint,
+ const nsAString& aContentDispositionFilename, const bool& aForceSave,
+ const int64_t& aContentLength, const bool& aWasFileChannel,
+ nsIURI* aReferrer, const MaybeDiscarded<BrowsingContext>& aContext,
+ const bool& aShouldCloseWindow) override;
+
+ already_AddRefed<PHandlerServiceParent> AllocPHandlerServiceParent();
+
+ PMediaParent* AllocPMediaParent();
+
+ bool DeallocPMediaParent(PMediaParent* aActor);
+
+ PBenchmarkStorageParent* AllocPBenchmarkStorageParent();
+
+ bool DeallocPBenchmarkStorageParent(PBenchmarkStorageParent* aActor);
+
+#ifdef MOZ_WEBSPEECH
+ already_AddRefed<PSpeechSynthesisParent> AllocPSpeechSynthesisParent();
+
+ virtual mozilla::ipc::IPCResult RecvPSpeechSynthesisConstructor(
+ PSpeechSynthesisParent* aActor) override;
+#endif
+
+ already_AddRefed<PWebBrowserPersistDocumentParent>
+ AllocPWebBrowserPersistDocumentParent(
+ PBrowserParent* aBrowser,
+ const MaybeDiscarded<BrowsingContext>& aContext);
+
+ mozilla::ipc::IPCResult RecvSetClipboard(const IPCTransferable& aTransferable,
+ const int32_t& aWhichClipboard);
+
+ mozilla::ipc::IPCResult RecvGetClipboard(
+ nsTArray<nsCString>&& aTypes, const int32_t& aWhichClipboard,
+ const MaybeDiscarded<WindowContext>& aRequestingWindowContext,
+ IPCTransferableData* aTransferableData);
+
+ mozilla::ipc::IPCResult RecvEmptyClipboard(const int32_t& aWhichClipboard);
+
+ mozilla::ipc::IPCResult RecvClipboardHasType(nsTArray<nsCString>&& aTypes,
+ const int32_t& aWhichClipboard,
+ bool* aHasType);
+
+ mozilla::ipc::IPCResult RecvGetExternalClipboardFormats(
+ const int32_t& aWhichClipboard, const bool& aPlainTextOnly,
+ nsTArray<nsCString>* aTypes);
+
+ mozilla::ipc::IPCResult RecvGetClipboardAsync(
+ nsTArray<nsCString>&& aTypes, const int32_t& aWhichClipboard,
+ const MaybeDiscarded<WindowContext>& aRequestingWindowContext,
+ mozilla::NotNull<nsIPrincipal*> aRequestingPrincipal,
+ GetClipboardAsyncResolver&& aResolver);
+
+ already_AddRefed<PClipboardWriteRequestParent>
+ AllocPClipboardWriteRequestParent(const int32_t& aClipboardType);
+
+ mozilla::ipc::IPCResult RecvGetIconForExtension(const nsACString& aFileExt,
+ const uint32_t& aIconSize,
+ nsTArray<uint8_t>* bits);
+
+ mozilla::ipc::IPCResult RecvStartVisitedQueries(
+ const nsTArray<RefPtr<nsIURI>>&);
+
+ mozilla::ipc::IPCResult RecvSetURITitle(nsIURI* uri, const nsAString& title);
+
+ mozilla::ipc::IPCResult RecvShowAlert(nsIAlertNotification* aAlert);
+
+ mozilla::ipc::IPCResult RecvCloseAlert(const nsAString& aName,
+ bool aContextClosed);
+
+ mozilla::ipc::IPCResult RecvDisableNotifications(nsIPrincipal* aPrincipal);
+
+ mozilla::ipc::IPCResult RecvOpenNotificationSettings(
+ nsIPrincipal* aPrincipal);
+
+ mozilla::ipc::IPCResult RecvNotificationEvent(
+ const nsAString& aType, const NotificationEventData& aData);
+
+ mozilla::ipc::IPCResult RecvLoadURIExternal(
+ nsIURI* uri, nsIPrincipal* triggeringPrincipal,
+ nsIPrincipal* redirectPrincipal,
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ bool aWasExternallyTriggered, bool aHasValidUserGestureActivation);
+ mozilla::ipc::IPCResult RecvExtProtocolChannelConnectParent(
+ const uint64_t& registrarId);
+
+ mozilla::ipc::IPCResult RecvSyncMessage(
+ const nsAString& aMsg, const ClonedMessageData& aData,
+ nsTArray<StructuredCloneData>* aRetvals);
+
+ mozilla::ipc::IPCResult RecvAsyncMessage(const nsAString& aMsg,
+ const ClonedMessageData& aData);
+
+ // MOZ_CAN_RUN_SCRIPT_BOUNDARY because we don't have MOZ_CAN_RUN_SCRIPT bits
+ // in IPC code yet.
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ mozilla::ipc::IPCResult RecvAddGeolocationListener(const bool& aHighAccuracy);
+ mozilla::ipc::IPCResult RecvRemoveGeolocationListener();
+
+ // MOZ_CAN_RUN_SCRIPT_BOUNDARY because we don't have MOZ_CAN_RUN_SCRIPT bits
+ // in IPC code yet.
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ mozilla::ipc::IPCResult RecvSetGeolocationHigherAccuracy(const bool& aEnable);
+
+ mozilla::ipc::IPCResult RecvConsoleMessage(const nsAString& aMessage);
+
+ mozilla::ipc::IPCResult 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& aIsFromPrivateWindow,
+ const uint64_t& aInnerWindowId, const bool& aIsFromChromeContext);
+
+ mozilla::ipc::IPCResult RecvReportFrameTimingData(
+ const LoadInfoArgs& loadInfoArgs, const nsAString& entryName,
+ const nsAString& initiatorType, UniquePtr<PerformanceTimingData>&& aData);
+
+ mozilla::ipc::IPCResult 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& aIsFromPrivateWindow,
+ const bool& aIsFromChromeContext, const ClonedMessageData& aStack);
+
+ private:
+ mozilla::ipc::IPCResult 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& aIsFromPrivateWindow,
+ const bool& aIsFromChromeContext,
+ const ClonedMessageData* aStack = nullptr);
+
+ public:
+ mozilla::ipc::IPCResult RecvCommitBrowsingContextTransaction(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ BrowsingContext::BaseTransaction&& aTransaction, uint64_t aEpoch);
+
+ mozilla::ipc::IPCResult RecvCommitWindowContextTransaction(
+ const MaybeDiscarded<WindowContext>& aContext,
+ WindowContext::BaseTransaction&& aTransaction, uint64_t aEpoch);
+
+ mozilla::ipc::IPCResult RecvAddSecurityState(
+ const MaybeDiscarded<WindowContext>& aContext, uint32_t aStateFlags);
+
+ mozilla::ipc::IPCResult RecvFirstIdle();
+
+ mozilla::ipc::IPCResult RecvDeviceReset();
+
+ mozilla::ipc::IPCResult RecvCopyFavicon(nsIURI* aOldURI, nsIURI* aNewURI,
+ const bool& aInPrivateBrowsing);
+
+ mozilla::ipc::IPCResult RecvFindImageText(IPCImage&&, nsTArray<nsCString>&&,
+ FindImageTextResolver&&);
+
+ virtual void ProcessingError(Result aCode, const char* aMsgName) override;
+
+ mozilla::ipc::IPCResult RecvGraphicsError(const nsACString& aError);
+
+ mozilla::ipc::IPCResult RecvBeginDriverCrashGuard(const uint32_t& aGuardType,
+ bool* aOutCrashed);
+
+ mozilla::ipc::IPCResult RecvEndDriverCrashGuard(const uint32_t& aGuardType);
+
+ mozilla::ipc::IPCResult RecvAddIdleObserver(const uint64_t& observerId,
+ const uint32_t& aIdleTimeInS);
+
+ mozilla::ipc::IPCResult RecvRemoveIdleObserver(const uint64_t& observerId,
+ const uint32_t& aIdleTimeInS);
+
+ mozilla::ipc::IPCResult RecvBackUpXResources(
+ const FileDescriptor& aXSocketFd);
+
+ mozilla::ipc::IPCResult RecvRequestAnonymousTemporaryFile(
+ const uint64_t& aID);
+
+ mozilla::ipc::IPCResult RecvCreateAudioIPCConnection(
+ CreateAudioIPCConnectionResolver&& aResolver);
+
+ already_AddRefed<extensions::PExtensionsParent> AllocPExtensionsParent();
+
+#ifdef MOZ_WEBRTC
+ PWebrtcGlobalParent* AllocPWebrtcGlobalParent();
+ bool DeallocPWebrtcGlobalParent(PWebrtcGlobalParent* aActor);
+#endif
+
+ mozilla::ipc::IPCResult RecvUpdateDropEffect(const uint32_t& aDragAction,
+ const uint32_t& aDropEffect);
+
+ mozilla::ipc::IPCResult RecvShutdownProfile(const nsACString& aProfile);
+
+ mozilla::ipc::IPCResult RecvShutdownPerfStats(const nsACString& aPerfStats);
+
+ mozilla::ipc::IPCResult RecvGetFontListShmBlock(
+ const uint32_t& aGeneration, const uint32_t& aIndex,
+ base::SharedMemoryHandle* aOut);
+
+ mozilla::ipc::IPCResult RecvInitializeFamily(const uint32_t& aGeneration,
+ const uint32_t& aFamilyIndex,
+ const bool& aLoadCmaps);
+
+ mozilla::ipc::IPCResult RecvSetCharacterMap(const uint32_t& aGeneration,
+ const uint32_t& aFamilyIndex,
+ const bool& aAlias,
+ const uint32_t& aFaceIndex,
+ const gfxSparseBitSet& aMap);
+
+ mozilla::ipc::IPCResult RecvInitOtherFamilyNames(const uint32_t& aGeneration,
+ const bool& aDefer,
+ bool* aLoaded);
+
+ mozilla::ipc::IPCResult RecvSetupFamilyCharMap(const uint32_t& aGeneration,
+ const uint32_t& aIndex,
+ const bool& aAlias);
+
+ mozilla::ipc::IPCResult RecvStartCmapLoading(const uint32_t& aGeneration,
+ const uint32_t& aStartIndex);
+
+ mozilla::ipc::IPCResult RecvGetHyphDict(nsIURI* aURIParams,
+ base::SharedMemoryHandle* aOutHandle,
+ uint32_t* aOutSize);
+
+ mozilla::ipc::IPCResult RecvNotifyBenchmarkResult(const nsAString& aCodecName,
+ const uint32_t& aDecodeFPS);
+
+ mozilla::ipc::IPCResult RecvNotifyPushObservers(const nsACString& aScope,
+ nsIPrincipal* aPrincipal,
+ const nsAString& aMessageId);
+
+ mozilla::ipc::IPCResult RecvNotifyPushObserversWithData(
+ const nsACString& aScope, nsIPrincipal* aPrincipal,
+ const nsAString& aMessageId, nsTArray<uint8_t>&& aData);
+
+ mozilla::ipc::IPCResult RecvNotifyPushSubscriptionChangeObservers(
+ const nsACString& aScope, nsIPrincipal* aPrincipal);
+
+ mozilla::ipc::IPCResult RecvPushError(const nsACString& aScope,
+ nsIPrincipal* aPrincipal,
+ const nsAString& aMessage,
+ const uint32_t& aFlags);
+
+ mozilla::ipc::IPCResult RecvNotifyPushSubscriptionModifiedObservers(
+ const nsACString& aScope, nsIPrincipal* aPrincipal);
+
+ mozilla::ipc::IPCResult RecvGetFilesRequest(const nsID& aID,
+ const nsAString& aDirectoryPath,
+ const bool& aRecursiveFlag);
+
+ mozilla::ipc::IPCResult RecvDeleteGetFilesRequest(const nsID& aID);
+
+ mozilla::ipc::IPCResult RecvAccumulateChildHistograms(
+ nsTArray<HistogramAccumulation>&& aAccumulations);
+ mozilla::ipc::IPCResult RecvAccumulateChildKeyedHistograms(
+ nsTArray<KeyedHistogramAccumulation>&& aAccumulations);
+ mozilla::ipc::IPCResult RecvUpdateChildScalars(
+ nsTArray<ScalarAction>&& aScalarActions);
+ mozilla::ipc::IPCResult RecvUpdateChildKeyedScalars(
+ nsTArray<KeyedScalarAction>&& aScalarActions);
+ mozilla::ipc::IPCResult RecvRecordChildEvents(
+ nsTArray<ChildEventData>&& events);
+ mozilla::ipc::IPCResult RecvRecordDiscardedData(
+ const DiscardedData& aDiscardedData);
+ mozilla::ipc::IPCResult RecvRecordPageLoadEvent(
+ const mozilla::glean::perf::PageLoadExtra& aPageLoadEventExtra);
+ mozilla::ipc::IPCResult RecvRecordOrigin(const uint32_t& aMetricId,
+ const nsACString& aOrigin);
+ mozilla::ipc::IPCResult RecvReportContentBlockingLog(
+ const IPCStream& aIPCStream);
+
+ mozilla::ipc::IPCResult RecvBHRThreadHang(const HangDetails& aHangDetails);
+
+ mozilla::ipc::IPCResult RecvAddCertException(
+ nsIX509Cert* aCert, const nsACString& aHostName, int32_t aPort,
+ const OriginAttributes& aOriginAttributes, bool aIsTemporary,
+ AddCertExceptionResolver&& aResolver);
+
+ mozilla::ipc::IPCResult RecvAutomaticStorageAccessPermissionCanBeGranted(
+ nsIPrincipal* aPrincipal,
+ AutomaticStorageAccessPermissionCanBeGrantedResolver&& aResolver);
+
+ mozilla::ipc::IPCResult RecvStorageAccessPermissionGrantedForOrigin(
+ uint64_t aTopLevelWindowId,
+ const MaybeDiscarded<BrowsingContext>& aParentContext,
+ nsIPrincipal* aTrackingPrincipal, const nsACString& aTrackingOrigin,
+ const int& aAllowMode,
+ const Maybe<
+ ContentBlockingNotifier::StorageAccessPermissionGrantedReason>&
+ aReason,
+ const bool& aFrameOnly,
+ StorageAccessPermissionGrantedForOriginResolver&& aResolver);
+
+ mozilla::ipc::IPCResult RecvCompleteAllowAccessFor(
+ const MaybeDiscarded<BrowsingContext>& aParentContext,
+ uint64_t aTopLevelWindowId, nsIPrincipal* aTrackingPrincipal,
+ const nsACString& aTrackingOrigin, uint32_t aCookieBehavior,
+ const ContentBlockingNotifier::StorageAccessPermissionGrantedReason&
+ aReason,
+ CompleteAllowAccessForResolver&& aResolver);
+
+ mozilla::ipc::IPCResult RecvSetAllowStorageAccessRequestFlag(
+ nsIPrincipal* aEmbeddedPrincipal, nsIURI* aEmbeddingOrigin,
+ SetAllowStorageAccessRequestFlagResolver&& aResolver);
+
+ mozilla::ipc::IPCResult RecvTestAllowStorageAccessRequestFlag(
+ nsIPrincipal* aEmbeddingPrincipal, nsIURI* aEmbeddedOrigin,
+ TestAllowStorageAccessRequestFlagResolver&& aResolver);
+
+ mozilla::ipc::IPCResult RecvStoreUserInteractionAsPermission(
+ nsIPrincipal* aPrincipal);
+
+ mozilla::ipc::IPCResult RecvTestCookiePermissionDecided(
+ const MaybeDiscarded<BrowsingContext>& aContext, nsIPrincipal* aPrincipal,
+ const TestCookiePermissionDecidedResolver&& aResolver);
+
+ mozilla::ipc::IPCResult RecvTestStorageAccessPermission(
+ nsIPrincipal* aEmbeddingPrincipal, const nsCString& aEmbeddedOrigin,
+ const TestStorageAccessPermissionResolver&& aResolver);
+
+ mozilla::ipc::IPCResult RecvNotifyMediaPlaybackChanged(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ MediaPlaybackState aState);
+
+ mozilla::ipc::IPCResult RecvNotifyMediaAudibleChanged(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ MediaAudibleState aState);
+
+ mozilla::ipc::IPCResult RecvNotifyPictureInPictureModeChanged(
+ const MaybeDiscarded<BrowsingContext>& aContext, bool aEnabled);
+
+ mozilla::ipc::IPCResult RecvNotifyMediaSessionUpdated(
+ const MaybeDiscarded<BrowsingContext>& aContext, bool aIsCreated);
+
+ mozilla::ipc::IPCResult RecvNotifyUpdateMediaMetadata(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const Maybe<MediaMetadataBase>& aMetadata);
+
+ mozilla::ipc::IPCResult RecvNotifyMediaSessionPlaybackStateChanged(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ MediaSessionPlaybackState aPlaybackState);
+
+ mozilla::ipc::IPCResult RecvNotifyMediaSessionSupportedActionChanged(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ MediaSessionAction aAction, bool aEnabled);
+
+ mozilla::ipc::IPCResult RecvNotifyMediaFullScreenState(
+ const MaybeDiscarded<BrowsingContext>& aContext, bool aIsInFullScreen);
+
+ mozilla::ipc::IPCResult RecvNotifyPositionStateChanged(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const PositionState& aState);
+
+ mozilla::ipc::IPCResult RecvAddOrRemovePageAwakeRequest(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const bool& aShouldAddCount);
+
+#if defined(XP_WIN)
+ mozilla::ipc::IPCResult RecvGetModulesTrust(
+ ModulePaths&& aModPaths, bool aRunAtNormalPriority,
+ GetModulesTrustResolver&& aResolver);
+#endif // defined(XP_WIN)
+
+ mozilla::ipc::IPCResult RecvReportServiceWorkerShutdownProgress(
+ uint32_t aShutdownStateId,
+ ServiceWorkerShutdownState::Progress aProgress);
+
+ mozilla::ipc::IPCResult RecvRawMessage(
+ const JSActorMessageMeta& aMeta, const Maybe<ClonedMessageData>& aData,
+ const Maybe<ClonedMessageData>& aStack);
+
+ mozilla::ipc::IPCResult RecvAbortOtherOrientationPendingPromises(
+ const MaybeDiscarded<BrowsingContext>& aContext);
+
+ mozilla::ipc::IPCResult RecvNotifyOnHistoryReload(
+ const MaybeDiscarded<BrowsingContext>& aContext, const bool& aForceReload,
+ NotifyOnHistoryReloadResolver&& aResolver);
+
+ mozilla::ipc::IPCResult 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);
+
+ mozilla::ipc::IPCResult RecvHistoryGo(
+ const MaybeDiscarded<BrowsingContext>& aContext, int32_t aOffset,
+ uint64_t aHistoryEpoch, bool aRequireUserInteraction,
+ bool aUserActivation, HistoryGoResolver&& aResolveRequestedIndex);
+
+ mozilla::ipc::IPCResult RecvSynchronizeLayoutHistoryState(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ nsILayoutHistoryState* aState);
+
+ mozilla::ipc::IPCResult RecvSessionHistoryEntryTitle(
+ const MaybeDiscarded<BrowsingContext>& aContext, const nsAString& aTitle);
+
+ mozilla::ipc::IPCResult RecvSessionHistoryEntryScrollRestorationIsManual(
+ const MaybeDiscarded<BrowsingContext>& aContext, const bool& aIsManual);
+
+ mozilla::ipc::IPCResult RecvSessionHistoryEntryScrollPosition(
+ const MaybeDiscarded<BrowsingContext>& aContext, const int32_t& aX,
+ const int32_t& aY);
+
+ mozilla::ipc::IPCResult RecvSessionHistoryEntryCacheKey(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const uint32_t& aCacheKey);
+
+ mozilla::ipc::IPCResult RecvSessionHistoryEntryWireframe(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const Wireframe& aWireframe);
+
+ mozilla::ipc::IPCResult
+ RecvSessionHistoryEntryStoreWindowNameInContiguousEntries(
+ const MaybeDiscarded<BrowsingContext>& aContext, const nsAString& aName);
+
+ mozilla::ipc::IPCResult RecvGetLoadingSessionHistoryInfoFromParent(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ GetLoadingSessionHistoryInfoFromParentResolver&& aResolver);
+
+ mozilla::ipc::IPCResult RecvRemoveFromBFCache(
+ const MaybeDiscarded<BrowsingContext>& aContext);
+
+ mozilla::ipc::IPCResult RecvSetActiveSessionHistoryEntry(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const Maybe<nsPoint>& aPreviousScrollPos, SessionHistoryInfo&& aInfo,
+ uint32_t aLoadType, uint32_t aUpdatedCacheKey, const nsID& aChangeID);
+
+ mozilla::ipc::IPCResult RecvReplaceActiveSessionHistoryEntry(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ SessionHistoryInfo&& aInfo);
+
+ mozilla::ipc::IPCResult RecvRemoveDynEntriesFromActiveSessionHistoryEntry(
+ const MaybeDiscarded<BrowsingContext>& aContext);
+
+ mozilla::ipc::IPCResult RecvRemoveFromSessionHistory(
+ const MaybeDiscarded<BrowsingContext>& aContext, const nsID& aChangeID);
+
+ mozilla::ipc::IPCResult RecvHistoryReload(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const uint32_t aReloadFlags);
+
+ mozilla::ipc::IPCResult RecvCleanupPendingLoadState(uint64_t aLoadIdentifier);
+
+ // Notify the ContentChild to enable the input event prioritization when
+ // initializing.
+ void MaybeEnableRemoteInputEventQueue();
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+ void AppendSandboxParams(std::vector<std::string>& aArgs);
+ void AppendDynamicSandboxParams(std::vector<std::string>& aArgs);
+#endif
+
+ mozilla::ipc::IPCResult RecvFOGData(ByteBuf&& buf);
+
+ mozilla::ipc::IPCResult RecvSetContainerFeaturePolicy(
+ const MaybeDiscardedBrowsingContext& aContainerContext,
+ FeaturePolicy* aContainerFeaturePolicy);
+
+ mozilla::ipc::IPCResult RecvGetSystemIcon(nsIURI* aURI,
+ GetSystemIconResolver&& aResolver);
+
+#ifdef FUZZING_SNAPSHOT
+ mozilla::ipc::IPCResult RecvSignalFuzzingReady();
+#endif
+
+ public:
+ void SendGetFilesResponseAndForget(const nsID& aID,
+ const GetFilesResponseResult& aResult);
+
+ bool SendRequestMemoryReport(const uint32_t& aGeneration,
+ const bool& aAnonymize,
+ const bool& aMinimizeMemoryUsage,
+ const Maybe<FileDescriptor>& aDMDFile) override;
+
+ void AddBrowsingContextGroup(BrowsingContextGroup* aGroup);
+ void RemoveBrowsingContextGroup(BrowsingContextGroup* aGroup);
+
+ // See `BrowsingContext::mEpochs` for an explanation of this field.
+ uint64_t GetBrowsingContextFieldEpoch() const {
+ return mBrowsingContextFieldEpoch;
+ }
+
+ void UpdateNetworkLinkType();
+
+ already_AddRefed<JSActor> InitJSActor(JS::Handle<JSObject*> aMaybeActor,
+ const nsACString& aName,
+ ErrorResult& aRv) override;
+ mozilla::ipc::IProtocol* AsNativeActor() override { return this; }
+
+ static already_AddRefed<nsIPrincipal> CreateRemoteTypeIsolationPrincipal(
+ const nsACString& aRemoteType);
+
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ bool IsBlockingShutdown() { return mBlockShutdownCalled; }
+#endif
+
+ ThreadsafeContentParentHandle* ThreadsafeHandle() const {
+ return mThreadsafeHandle;
+ }
+
+ void GetIPCTransferableData(nsIDragSession* aSession, BrowserParent* aParent,
+ nsTArray<IPCTransferableData>& aIPCTransferables);
+
+ private:
+ // Return an existing ContentParent if possible. Otherwise, `nullptr`.
+ static already_AddRefed<ContentParent> GetUsedBrowserProcess(
+ const nsACString& aRemoteType, nsTArray<ContentParent*>& aContentParents,
+ uint32_t aMaxContentParents, bool aPreferUsed, ProcessPriority aPriority);
+
+ void AddToPool(nsTArray<ContentParent*>&);
+ void RemoveFromPool(nsTArray<ContentParent*>&);
+ void AssertNotInPool();
+
+ void AssertAlive();
+
+ private:
+ // If you add strong pointers to cycle collected objects here, be sure to
+ // release these objects in ShutDownProcess. See the comment there for more
+ // details.
+
+ GeckoChildProcessHost* mSubprocess;
+ const TimeStamp mLaunchTS; // used to calculate time to start content process
+ TimeStamp mLaunchYieldTS; // used to calculate async launch main thread time
+ TimeStamp mActivateTS;
+
+ bool mIsAPreallocBlocker; // We called AddBlocker for this ContentParent
+
+ nsCString mRemoteType;
+ nsCString mProfile;
+ nsCOMPtr<nsIPrincipal> mRemoteTypeIsolationPrincipal;
+
+ ContentParentId mChildID;
+ int32_t mGeolocationWatchID;
+
+ // After we destroy the last Browser, we also start a timer to ensure
+ // that even content processes that are not responding will get a
+ // second chance and a shutdown message.
+ nsCOMPtr<nsITimer> mSendShutdownTimer;
+ bool mSentShutdownMessage = false;
+
+ // After we initiate shutdown, we also start a timer to ensure
+ // that even content processes that are 100% blocked (say from
+ // SIGSTOP), are still killed eventually. This task enforces that
+ // timer.
+ nsCOMPtr<nsITimer> mForceKillTimer;
+
+ // Threadsafe handle object which can be used by actors like PBackground to
+ // track the identity and other relevant information about the content process
+ // they're attached to.
+ const RefPtr<ThreadsafeContentParentHandle> mThreadsafeHandle;
+
+ // How many tabs we're waiting to finish their destruction
+ // sequence. Precisely, how many BrowserParents have called
+ // NotifyTabDestroying() but not called NotifyTabDestroyed().
+ int32_t mNumDestroyingTabs;
+
+ uint32_t mNumKeepaliveCalls;
+
+ // The process starts in the LAUNCHING state, and transitions to
+ // ALIVE once it can accept IPC messages. It remains ALIVE only
+ // while remote content is being actively used from this process.
+ // After the state becaomes DEAD, some previously scheduled IPC
+ // traffic may still pass through.
+ enum class LifecycleState : uint8_t {
+ LAUNCHING,
+ ALIVE,
+ INITIALIZED,
+ DEAD,
+ };
+
+ LifecycleState mLifecycleState;
+
+ uint8_t mIsForBrowser : 1;
+
+ // These variables track whether we've called Close() and KillHard() on our
+ // channel.
+ uint8_t mCalledClose : 1;
+ uint8_t mCalledKillHard : 1;
+ uint8_t mCreatedPairedMinidumps : 1;
+ uint8_t mShutdownPending : 1;
+
+ // Whether or not `LaunchSubprocessResolve` has been called, and whether or
+ // not it returned `true` when called.
+ uint8_t mLaunchResolved : 1;
+ uint8_t mLaunchResolvedOk : 1;
+
+ // True if the input event queue on the main thread of the content process is
+ // enabled.
+ uint8_t mIsRemoteInputEventQueueEnabled : 1;
+
+ // True if we send input events with input priority. Otherwise, we send input
+ // events with normal priority.
+ uint8_t mIsInputPriorityEventEnabled : 1;
+
+ uint8_t mIsInPool : 1;
+
+ // True if we already created a GMP service.
+ uint8_t mGMPCreated : 1;
+
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ bool mNotifiedImpendingShutdownOnTabWillDestroy = false;
+ bool mBlockShutdownCalled;
+#endif
+
+ nsCOMPtr<nsIContentProcessInfo> mScriptableHelper;
+
+ nsTArray<nsCOMPtr<nsIObserver>> mIdleListeners;
+
+#ifdef MOZ_X11
+ // Dup of child's X socket, used to scope its resources to this
+ // object instead of the child process's lifetime.
+ UniqueFileHandle mChildXSocketFdDup;
+#endif
+
+ RefPtr<PProcessHangMonitorParent> mHangMonitorActor;
+
+ UniquePtr<gfx::DriverCrashGuard> mDriverCrashGuard;
+ UniquePtr<MemoryReportRequestHost> mMemoryReportRequest;
+
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+ mozilla::UniquePtr<SandboxBroker> mSandboxBroker;
+ static mozilla::StaticAutoPtr<SandboxBrokerPolicyFactory>
+ sSandboxBrokerPolicyFactory;
+#endif
+
+ // This hashtable is used to run GetFilesHelper objects in the parent process.
+ // GetFilesHelper can be aborted by receiving RecvDeleteGetFilesRequest.
+ nsRefPtrHashtable<nsIDHashKey, GetFilesHelper> mGetFilesPendingRequests;
+
+ nsTHashSet<nsCString> mActivePermissionKeys;
+ nsTHashSet<nsCString> mActiveSecondaryPermissionKeys;
+
+ nsTArray<nsCString> mBlobURLs;
+
+ // This is intended to be a memory and time efficient means of determining
+ // whether an origin has ever existed in a process so that Blob URL broadcast
+ // doesn't need to transmit every Blob URL to every content process. False
+ // positives are acceptable because receiving a Blob URL does not grant access
+ // to its contents, and the act of creating/revoking a Blob is currently
+ // viewed as an acceptable side-channel leak. In the future bug 1491018 will
+ // moot the need for this structure.
+ nsTArray<uint64_t> mLoadedOriginHashes;
+
+ UniquePtr<mozilla::ipc::CrashReporterHost> mCrashReporter;
+
+ // Collects any pref changes that occur during process launch (after
+ // the initial map is passed in command-line arguments) to be sent
+ // when the process can receive IPC messages.
+ nsTArray<Pref> mQueuedPrefs;
+
+ RefPtr<mozilla::dom::ProcessMessageManager> mMessageManager;
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+ // When set to true, indicates that content processes should
+ // initialize their sandbox during startup instead of waiting
+ // for the SetProcessSandbox IPDL message.
+ static bool sEarlySandboxInit;
+#endif
+
+ nsTHashSet<RefPtr<BrowsingContextGroup>> mGroups;
+
+ // When we request a content process to load a document on our behalf, we'll
+ // record the nsDocShellLoadState we sent to the content process mapped by the
+ // load ID. If the load is then requested from the content process, we can
+ // compare the load state and ensure it matches.
+ nsTHashMap<uint64_t, RefPtr<nsDocShellLoadState>> mPendingLoadStates;
+
+ // See `BrowsingContext::mEpochs` for an explanation of this field.
+ uint64_t mBrowsingContextFieldEpoch = 0;
+
+ // A preference serializer used to share preferences with the process.
+ // Cleared once startup is complete.
+ UniquePtr<mozilla::ipc::SharedPreferenceSerializer> mPrefSerializer;
+
+ static uint32_t sMaxContentProcesses;
+ static uint32_t sPageLoadEventCounter;
+
+ bool mIsSignaledImpendingShutdown = false;
+ bool mIsNotifiedShutdownSuccess = false;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(ContentParent, NS_CONTENTPARENT_IID)
+
+// Threadsafe handle object allowing off-main-thread code to get some
+// information and maintain a weak reference to a ContentParent.
+class ThreadsafeContentParentHandle final {
+ friend class ContentParent;
+
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ThreadsafeContentParentHandle);
+
+ // Get the ChildID of this process. Safe to call from any thread.
+ ContentParentId ChildID() const { return mChildID; }
+
+ // Get the current RemoteType of this ContentParent. Safe to call from any
+ // thread. If the returned RemoteType is PREALLOC_REMOTE_TYPE, it may change
+ // again in the future.
+ nsCString GetRemoteType() MOZ_EXCLUDES(mMutex);
+
+ // Try to get a reference to the real `ContentParent` object from this weak
+ // reference. This may only be called on the main thread.
+ already_AddRefed<ContentParent> GetContentParent()
+ MOZ_REQUIRES(sMainThreadCapability) {
+ return do_AddRef(mWeakActor);
+ }
+
+ // Calls `aCallback` with the current remote worker count and whether or not
+ // shutdown has been started. If the callback returns `true`, registers a new
+ // actor, and returns `true`, otherwise returns `false`.
+ //
+ // NOTE: The internal mutex is held while evaluating `aCallback`.
+ bool MaybeRegisterRemoteWorkerActor(
+ MoveOnlyFunction<bool(uint32_t, bool)> aCallback) MOZ_EXCLUDES(mMutex);
+
+ // Like `MaybeRegisterRemoteWorkerActor`, but unconditional.
+ void RegisterRemoteWorkerActor() MOZ_EXCLUDES(mMutex) {
+ MaybeRegisterRemoteWorkerActor([](uint32_t, bool) { return true; });
+ }
+
+ RecursiveMutex& Mutex() { return mMutex; }
+
+ private:
+ ThreadsafeContentParentHandle(ContentParent* aActor, ContentParentId aChildID,
+ const nsACString& aRemoteType)
+ : mChildID(aChildID), mRemoteType(aRemoteType), mWeakActor(aActor) {}
+ ~ThreadsafeContentParentHandle() { MOZ_ASSERT(!mWeakActor); }
+
+ mozilla::RecursiveMutex mMutex{"ContentParentIdentity"};
+
+ const ContentParentId mChildID;
+
+ nsCString mRemoteType MOZ_GUARDED_BY(mMutex);
+ uint32_t mRemoteWorkerActorCount MOZ_GUARDED_BY(mMutex) = 0;
+ bool mShutdownStarted MOZ_GUARDED_BY(mMutex) = false;
+
+ // Weak reference to the actual ContentParent actor. Only touched on the main
+ // thread to read or clear.
+ ContentParent* mWeakActor MOZ_GUARDED_BY(sMainThreadCapability);
+};
+
+// This is the C++ version of remoteTypePrefix in E10SUtils.sys.mjs.
+const nsDependentCSubstring RemoteTypePrefix(
+ const nsACString& aContentProcessType);
+
+// This is based on isWebRemoteType in E10SUtils.sys.mjs.
+bool IsWebRemoteType(const nsACString& aContentProcessType);
+
+bool IsWebCoopCoepRemoteType(const nsACString& aContentProcessType);
+
+bool IsExtensionRemoteType(const nsACString& aContentProcessType);
+
+inline nsISupports* ToSupports(mozilla::dom::ContentParent* aContentParent) {
+ return static_cast<nsIDOMProcessParent*>(aContentParent);
+}
+
+} // namespace dom
+} // namespace mozilla
+
+class ParentIdleListener : public nsIObserver {
+ friend class mozilla::dom::ContentParent;
+
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ ParentIdleListener(mozilla::dom::ContentParent* aParent, uint64_t aObserver,
+ uint32_t aTime)
+ : mParent(aParent), mObserver(aObserver), mTime(aTime) {}
+
+ private:
+ virtual ~ParentIdleListener() = default;
+
+ RefPtr<mozilla::dom::ContentParent> mParent;
+ uint64_t mObserver;
+ uint32_t mTime;
+};
+
+#endif // mozilla_dom_ContentParent_h
diff --git a/dom/ipc/ContentProcess.cpp b/dom/ipc/ContentProcess.cpp
new file mode 100644
index 0000000000..10118cd38f
--- /dev/null
+++ b/dom/ipc/ContentProcess.cpp
@@ -0,0 +1,192 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ipc/IOThreadChild.h"
+
+#include "ContentProcess.h"
+#include "base/shared_memory.h"
+#include "mozilla/Preferences.h"
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+# include <stdlib.h>
+# include "mozilla/Sandbox.h"
+# include "mozilla/SandboxSettings.h"
+#endif
+
+#include "nsAppRunner.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/ProcessUtils.h"
+#include "mozilla/GeckoArgs.h"
+#include "mozilla/Omnijar.h"
+#include "nsCategoryManagerUtils.h"
+
+using mozilla::ipc::IOThreadChild;
+
+namespace mozilla::dom {
+
+static nsresult GetGREDir(nsIFile** aResult) {
+ nsCOMPtr<nsIFile> current;
+ nsresult rv = XRE_GetBinaryPath(getter_AddRefs(current));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+#ifdef XP_DARWIN
+ // Walk out of [subprocess].app/Contents/MacOS to the real GRE dir
+ const int depth = 4;
+#else
+ const int depth = 1;
+#endif
+
+ for (int i = 0; i < depth; ++i) {
+ nsCOMPtr<nsIFile> parent;
+ rv = current->GetParent(getter_AddRefs(parent));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ current = parent;
+ NS_ENSURE_TRUE(current, NS_ERROR_UNEXPECTED);
+ }
+
+#ifdef XP_DARWIN
+ rv = current->SetNativeLeafName("Resources"_ns);
+ NS_ENSURE_SUCCESS(rv, rv);
+#endif
+
+ current.forget(aResult);
+
+ return NS_OK;
+}
+
+ContentProcess::ContentProcess(ProcessId aParentPid,
+ const nsID& aMessageChannelId)
+ : ProcessChild(aParentPid, aMessageChannelId) {
+ NS_LogInit();
+}
+
+ContentProcess::~ContentProcess() { NS_LogTerm(); }
+
+bool ContentProcess::Init(int aArgc, char* aArgv[]) {
+ Maybe<uint64_t> childID = geckoargs::sChildID.Get(aArgc, aArgv);
+ Maybe<bool> isForBrowser = Nothing();
+ Maybe<const char*> parentBuildID =
+ geckoargs::sParentBuildID.Get(aArgc, aArgv);
+ Maybe<uint64_t> jsInitHandle;
+ Maybe<uint64_t> jsInitLen = geckoargs::sJsInitLen.Get(aArgc, aArgv);
+
+ nsCOMPtr<nsIFile> appDirArg;
+ Maybe<const char*> appDir = geckoargs::sAppDir.Get(aArgc, aArgv);
+ if (appDir.isSome()) {
+ bool flag;
+ nsresult rv = XRE_GetFileFromPath(*appDir, getter_AddRefs(appDirArg));
+ if (NS_FAILED(rv) || NS_FAILED(appDirArg->Exists(&flag)) || !flag) {
+ NS_WARNING("Invalid application directory passed to content process.");
+ appDirArg = nullptr;
+ }
+ }
+
+ Maybe<bool> safeMode = geckoargs::sSafeMode.Get(aArgc, aArgv);
+ if (safeMode.isSome()) {
+ gSafeMode = *safeMode;
+ }
+
+ Maybe<bool> isForBrowerParam = geckoargs::sIsForBrowser.Get(aArgc, aArgv);
+ Maybe<bool> notForBrowserParam = geckoargs::sNotForBrowser.Get(aArgc, aArgv);
+ if (isForBrowerParam.isSome()) {
+ isForBrowser = Some(true);
+ }
+ if (notForBrowserParam.isSome()) {
+ isForBrowser = Some(false);
+ }
+
+ // command line: [-jsInitHandle handle] -jsInitLen length
+#ifdef XP_WIN
+ jsInitHandle = geckoargs::sJsInitHandle.Get(aArgc, aArgv);
+#endif
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+ nsCOMPtr<nsIFile> profileDir;
+ bool flag;
+ Maybe<const char*> profile = geckoargs::sProfile.Get(aArgc, aArgv);
+ // xpcshell self-test on macOS will hit this, so check isSome() otherwise
+ // Maybe<> assertions will MOZ_CRASH() us.
+ if (profile.isSome()) {
+ nsresult rv = XRE_GetFileFromPath(*profile, getter_AddRefs(profileDir));
+ if (NS_FAILED(rv) || NS_FAILED(profileDir->Exists(&flag)) || !flag) {
+ NS_WARNING("Invalid profile directory passed to content process.");
+ profileDir = nullptr;
+ }
+ } else {
+ NS_WARNING("No profile directory passed to content process.");
+ }
+#endif /* XP_MACOSX && MOZ_SANDBOX */
+
+ // Did we find all the mandatory flags?
+ if (childID.isNothing() || isForBrowser.isNothing() ||
+ parentBuildID.isNothing()) {
+ return false;
+ }
+
+ if (!ProcessChild::InitPrefs(aArgc, aArgv)) {
+ return false;
+ }
+
+ if (!::mozilla::ipc::ImportSharedJSInit(jsInitHandle.valueOr(0),
+ jsInitLen.valueOr(0))) {
+ return false;
+ }
+
+ mContent.Init(TakeInitialEndpoint(), *parentBuildID, *childID, *isForBrowser);
+
+ nsCOMPtr<nsIFile> greDir;
+ nsresult rv = GetGREDir(getter_AddRefs(greDir));
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ nsCOMPtr<nsIFile> xpcomAppDir = appDirArg ? appDirArg : greDir;
+
+ rv = mDirProvider.Initialize(xpcomAppDir, greDir);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ // Handle the -greomni/-appomni flags (unless the forkserver already
+ // preloaded the jar(s)).
+ if (!Omnijar::IsInitialized()) {
+ Omnijar::ChildProcessInit(aArgc, aArgv);
+ }
+
+ rv = NS_InitXPCOM(nullptr, xpcomAppDir, &mDirProvider);
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ // "app-startup" is the name of both the category and the event
+ NS_CreateServicesFromCategory("app-startup", nullptr, "app-startup", nullptr);
+
+#if (defined(XP_MACOSX)) && defined(MOZ_SANDBOX)
+ mContent.SetProfileDir(profileDir);
+# if defined(DEBUG)
+ if (IsContentSandboxEnabled()) {
+ AssertMacSandboxEnabled();
+ }
+# endif /* DEBUG */
+#endif /* XP_MACOSX && MOZ_SANDBOX */
+
+ // Do this as early as possible to get the parent process to initialize the
+ // background thread since we'll likely need database information very soon.
+ mozilla::ipc::BackgroundChild::Startup();
+ mozilla::ipc::BackgroundChild::InitContentStarter(&mContent);
+
+ return true;
+}
+
+// Note: CleanUp() never gets called in non-debug builds because we exit early
+// in ContentChild::ActorDestroy().
+void ContentProcess::CleanUp() {
+ mDirProvider.DoShutdown();
+ NS_ShutdownXPCOM(nullptr);
+}
+
+} // namespace mozilla::dom
diff --git a/dom/ipc/ContentProcess.h b/dom/ipc/ContentProcess.h
new file mode 100644
index 0000000000..46d34098e7
--- /dev/null
+++ b/dom/ipc/ContentProcess.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef dom_tabs_ContentThread_h
+#define dom_tabs_ContentThread_h 1
+
+#include "mozilla/ipc/ProcessChild.h"
+#include "ContentChild.h"
+#include "nsXREDirProvider.h"
+
+#if defined(XP_WIN)
+# include "mozilla/mscom/ProcessRuntime.h"
+#endif
+
+namespace mozilla::dom {
+
+/**
+ * ContentProcess is a singleton on the content process which represents
+ * the main thread where tab instances live.
+ */
+class ContentProcess : public mozilla::ipc::ProcessChild {
+ using ProcessChild = mozilla::ipc::ProcessChild;
+
+ public:
+ ContentProcess(ProcessId aParentPid, const nsID& aMessageChannelId);
+ ~ContentProcess();
+
+ virtual bool Init(int aArgc, char* aArgv[]) override;
+ virtual void CleanUp() override;
+
+ private:
+ ContentChild mContent;
+#if defined(XP_WIN)
+ // This object initializes and configures COM. This must happen prior to
+ // constructing mXREEmbed.
+ mozilla::mscom::ProcessRuntime mCOMRuntime;
+#endif
+ nsXREDirProvider mDirProvider;
+};
+
+} // namespace mozilla::dom
+
+#endif // ifndef dom_tabs_ContentThread_h
diff --git a/dom/ipc/ContentProcessManager.cpp b/dom/ipc/ContentProcessManager.cpp
new file mode 100644
index 0000000000..c5647b4f26
--- /dev/null
+++ b/dom/ipc/ContentProcessManager.cpp
@@ -0,0 +1,142 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ContentProcessManager.h"
+#include "ContentParent.h"
+#include "mozilla/AppShutdown.h"
+#include "mozilla/dom/BrowserParent.h"
+#include "mozilla/dom/BrowsingContextGroup.h"
+#include "mozilla/dom/CanonicalBrowsingContext.h"
+
+#include "mozilla/StaticPtr.h"
+#include "mozilla/ClearOnShutdown.h"
+
+#include "nsPrintfCString.h"
+
+namespace mozilla::dom {
+
+/* static */
+StaticAutoPtr<ContentProcessManager> ContentProcessManager::sSingleton;
+
+/* static */
+ContentProcessManager* ContentProcessManager::GetSingleton() {
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ if (!sSingleton &&
+ !AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdownFinal)) {
+ sSingleton = new ContentProcessManager();
+ ClearOnShutdown(&sSingleton);
+ }
+ return sSingleton;
+}
+
+void ContentProcessManager::AddContentProcess(ContentParent* aChildCp) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aChildCp);
+
+ mContentParentMap.WithEntryHandle(aChildCp->ChildID(), [&](auto&& entry) {
+ MOZ_ASSERT_IF(entry, entry.Data() == aChildCp);
+ entry.OrInsert(aChildCp);
+ });
+}
+
+void ContentProcessManager::RemoveContentProcess(
+ const ContentParentId& aChildCpId) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ MOZ_ALWAYS_TRUE(mContentParentMap.Remove(aChildCpId));
+}
+
+ContentParent* ContentProcessManager::GetContentProcessById(
+ const ContentParentId& aChildCpId) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ ContentParent* contentParent = mContentParentMap.Get(aChildCpId);
+ if (NS_WARN_IF(!contentParent)) {
+ return nullptr;
+ }
+ return contentParent;
+}
+
+bool ContentProcessManager::RegisterRemoteFrame(BrowserParent* aChildBp) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aChildBp);
+
+ return mBrowserParentMap.WithEntryHandle(
+ aChildBp->GetTabId(), [&](auto&& entry) {
+ if (entry) {
+ MOZ_ASSERT(entry.Data() == aChildBp);
+ return false;
+ }
+
+ // Ensure that this BrowserParent's BrowsingContextGroup is kept alive
+ // until the BrowserParent has been unregistered, ensuring the group
+ // isn't destroyed while this BrowserParent can still send messages.
+ aChildBp->GetBrowsingContext()->Group()->AddKeepAlive();
+ entry.Insert(aChildBp);
+ return true;
+ });
+}
+
+void ContentProcessManager::UnregisterRemoteFrame(const TabId& aChildTabId) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ auto childBp = mBrowserParentMap.Extract(aChildTabId);
+ MOZ_DIAGNOSTIC_ASSERT(childBp);
+
+ // Clear the corresponding keepalive which was added in `RegisterRemoteFrame`.
+ (*childBp)->GetBrowsingContext()->Group()->RemoveKeepAlive();
+}
+
+ContentParentId ContentProcessManager::GetTabProcessId(
+ const TabId& aChildTabId) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (BrowserParent* browserParent = mBrowserParentMap.Get(aChildTabId)) {
+ return browserParent->Manager()->ChildID();
+ }
+ return ContentParentId(0);
+}
+
+uint32_t ContentProcessManager::GetBrowserParentCountByProcessId(
+ const ContentParentId& aChildCpId) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ ContentParent* contentParent = mContentParentMap.Get(aChildCpId);
+ if (NS_WARN_IF(!contentParent)) {
+ return 0;
+ }
+ return contentParent->ManagedPBrowserParent().Count();
+}
+
+already_AddRefed<BrowserParent>
+ContentProcessManager::GetBrowserParentByProcessAndTabId(
+ const ContentParentId& aChildCpId, const TabId& aChildTabId) {
+ RefPtr<BrowserParent> browserParent = mBrowserParentMap.Get(aChildTabId);
+ if (NS_WARN_IF(!browserParent)) {
+ return nullptr;
+ }
+
+ if (NS_WARN_IF(browserParent->Manager()->ChildID() != aChildCpId)) {
+ return nullptr;
+ }
+
+ return browserParent.forget();
+}
+
+already_AddRefed<BrowserParent>
+ContentProcessManager::GetTopLevelBrowserParentByProcessAndTabId(
+ const ContentParentId& aChildCpId, const TabId& aChildTabId) {
+ RefPtr<BrowserParent> browserParent =
+ GetBrowserParentByProcessAndTabId(aChildCpId, aChildTabId);
+ while (browserParent && browserParent->GetBrowserBridgeParent()) {
+ browserParent = browserParent->GetBrowserBridgeParent()->Manager();
+ }
+
+ return browserParent.forget();
+}
+
+} // namespace mozilla::dom
diff --git a/dom/ipc/ContentProcessManager.h b/dom/ipc/ContentProcessManager.h
new file mode 100644
index 0000000000..bf76051a66
--- /dev/null
+++ b/dom/ipc/ContentProcessManager.h
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_ContentProcessManager_h
+#define mozilla_dom_ContentProcessManager_h
+
+#include "mozilla/StaticPtr.h"
+#include "mozilla/dom/TabContext.h"
+#include "mozilla/dom/ipc/IdType.h"
+#include "nsTArray.h"
+#include "nsTHashMap.h"
+
+namespace mozilla::dom {
+class ContentParent;
+
+class ContentProcessManager final {
+ public:
+ static ContentProcessManager* GetSingleton();
+ MOZ_COUNTED_DTOR(ContentProcessManager);
+
+ /**
+ * Add a new content process into the map.
+ */
+ void AddContentProcess(ContentParent* aChildCp);
+
+ /**
+ * Remove the content process by id.
+ */
+ void RemoveContentProcess(const ContentParentId& aChildCpId);
+
+ /**
+ * Return the ContentParent pointer by id.
+ */
+ ContentParent* GetContentProcessById(const ContentParentId& aChildCpId);
+
+ /**
+ * Add a new browser parent into the map.
+ */
+ bool RegisterRemoteFrame(BrowserParent* aChildBp);
+
+ /**
+ * Remove the browser parent by the given tab id.
+ */
+ void UnregisterRemoteFrame(const TabId& aChildTabId);
+
+ /**
+ * Get the ContentParentId of the parent of the given tab id.
+ */
+ ContentParentId GetTabProcessId(const TabId& aChildTabId);
+
+ /**
+ * Get the number of BrowserParents managed by the givent content process.
+ * Return 0 when ContentParent couldn't be found via aChildCpId.
+ */
+ uint32_t GetBrowserParentCountByProcessId(const ContentParentId& aChildCpId);
+
+ /**
+ * Get the BrowserParent by the given content process and tab id.
+ * Return nullptr when BrowserParent couldn't be found via aChildCpId
+ * and aChildTabId.
+ */
+ already_AddRefed<BrowserParent> GetBrowserParentByProcessAndTabId(
+ const ContentParentId& aChildCpId, const TabId& aChildTabId);
+
+ /**
+ * Get the BrowserParent on top level by the given content process and tab id.
+ *
+ * This function returns the BrowserParent directly within a BrowserHost,
+ * called top-level BrowserParent here, by given aChildCpId and aChildTabId.
+ * The given aChildCpId and aChildTabId are related to a content process
+ * and a tab respectively.
+ */
+ already_AddRefed<BrowserParent> GetTopLevelBrowserParentByProcessAndTabId(
+ const ContentParentId& aChildCpId, const TabId& aChildTabId);
+
+ private:
+ static StaticAutoPtr<ContentProcessManager> sSingleton;
+
+ nsTHashMap<nsUint64HashKey, ContentParent*> mContentParentMap;
+ nsTHashMap<nsUint64HashKey, BrowserParent*> mBrowserParentMap;
+
+ MOZ_COUNTED_DEFAULT_CTOR(ContentProcessManager);
+};
+
+} // namespace mozilla::dom
+
+#endif // mozilla_dom_ContentProcessManager_h
diff --git a/dom/ipc/CustomElementTypes.ipdlh b/dom/ipc/CustomElementTypes.ipdlh
new file mode 100644
index 0000000000..bdbf004ca9
--- /dev/null
+++ b/dom/ipc/CustomElementTypes.ipdlh
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include "mozilla/dom/IPCBlobUtils.h";
+
+[RefCounted] using class mozilla::dom::BlobImpl from "mozilla/dom/BlobImpl.h";
+using struct mozilla::void_t from "mozilla/ipc/IPCCore.h";
+
+namespace mozilla {
+namespace dom {
+
+// Types used to store form-associated custom element state.
+union FormDataValue {
+ BlobImpl;
+ nsString;
+};
+
+struct FormDataTuple {
+ nsString name;
+ FormDataValue value;
+};
+
+union CustomElementFormValue {
+ void_t;
+ nullable BlobImpl;
+ nsString;
+ FormDataTuple[];
+};
+
+struct CustomElementTuple {
+ CustomElementFormValue value;
+ CustomElementFormValue state;
+};
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/DOMTypes.ipdlh b/dom/ipc/DOMTypes.ipdlh
new file mode 100644
index 0000000000..faee4e861c
--- /dev/null
+++ b/dom/ipc/DOMTypes.ipdlh
@@ -0,0 +1,329 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include "mozilla/GfxMessageUtils.h";
+include "mozilla/dom/CSPMessageUtils.h";
+include "mozilla/dom/DocShellMessageUtils.h";
+include "mozilla/dom/PermissionMessageUtils.h";
+include "mozilla/dom/PropertyBagUtils.h";
+include "mozilla/dom/ReferrerInfoUtils.h";
+include "mozilla/dom/TabMessageUtils.h";
+include "mozilla/ipc/URIUtils.h";
+include "mozilla/layers/LayersMessageUtils.h";
+include "mozilla/net/ClassOfService.h";
+
+include IPCBlob;
+include IPCStream;
+include ProtocolTypes;
+
+using struct mozilla::void_t from "mozilla/ipc/IPCCore.h";
+
+[MoveOnly=data] using struct mozilla::SerializedStructuredCloneBuffer
+ from "mozilla/ipc/SerializedStructuredCloneBuffer.h";
+
+using struct mozilla::dom::LoadingSessionHistoryInfo
+ from "mozilla/dom/SessionHistoryEntry.h";
+
+using mozilla::net::ClassOfService from "mozilla/net/ClassOfService.h";
+
+
+using mozilla::hal::ScreenOrientation from "mozilla/HalIPCUtils.h";
+using mozilla::LayoutDeviceIntRect from "Units.h";
+using mozilla::DesktopIntRect from "Units.h";
+using mozilla::DesktopToLayoutDeviceScale from "Units.h";
+using mozilla::CSSToLayoutDeviceScale from "Units.h";
+using mozilla::CSSRect from "Units.h";
+using mozilla::CSSSize from "Units.h";
+using mozilla::ScreenIntSize from "Units.h";
+using mozilla::LayoutDeviceIntPoint from "Units.h";
+using nsSizeMode from "nsIWidgetListener.h";
+using mozilla::ScrollbarPreference from "mozilla/ScrollbarPreferences.h";
+using mozilla::gfx::SurfaceFormat from "mozilla/gfx/Types.h";
+[RefCounted] using class nsIPrincipal from "nsIPrincipal.h";
+using mozilla::dom::MaybeDiscardedBrowsingContext from "mozilla/dom/BrowsingContext.h";
+[RefCounted] using class nsIURI from "nsIURI.h";
+[RefCounted] using class nsIContentSecurityPolicy from "nsIContentSecurityPolicy.h";
+[RefCounted] using class nsIInputStream from "mozilla/ipc/IPCStreamUtils.h";
+[RefCounted] using class nsIReferrerInfo from "nsIReferrerInfo.h";
+[RefCounted] using class nsIVariant from "nsIVariant.h";
+using mozilla::TimeStamp from "mozilla/TimeStamp.h";
+[RefCounted] using class mozilla::RemoteLazyInputStream from "mozilla/RemoteLazyInputStream.h";
+[MoveOnly] using class mozilla::ipc::BigBuffer from "mozilla/ipc/BigBuffer.h";
+
+namespace mozilla {
+namespace dom {
+
+struct MessagePortIdentifier
+{
+ nsID uuid;
+ nsID destinationUuid;
+ uint32_t sequenceId;
+ bool neutered;
+};
+
+/**
+ * Cross-process representation for postMessage() style payloads where Blobs may
+ * be referenced/"cloned" and (optionally) messageports transferred. Use
+ * StructuredCloneData in your code to convert between this wire representation
+ * and the StructuredCloneData StructuredCloneHolder-subclass.
+ */
+struct ClonedMessageData
+{
+ SerializedStructuredCloneBuffer data;
+ IPCBlob[] blobs;
+ IPCStream[] inputStreams;
+ MessagePortIdentifier[] identifiers;
+};
+
+struct ErrorMessageData {
+};
+
+union ClonedOrErrorMessageData {
+ ClonedMessageData;
+ ErrorMessageData;
+};
+
+struct RefMessageData {
+ nsID uuid;
+};
+
+union MessageDataType {
+ ClonedMessageData;
+ RefMessageData;
+};
+
+struct MessageData {
+ nsID? agentClusterId;
+ MessageDataType data;
+};
+
+struct ScreenDetails {
+ LayoutDeviceIntRect rect;
+ DesktopIntRect rectDisplayPix;
+ LayoutDeviceIntRect availRect;
+ DesktopIntRect availRectDisplayPix;
+ int32_t pixelDepth;
+ int32_t colorDepth;
+ uint32_t refreshRate; // In Hz, or 0 if not known.
+ DesktopToLayoutDeviceScale contentsScaleFactor;
+ CSSToLayoutDeviceScale defaultCSSScaleFactor;
+ float dpi;
+ ScreenOrientation orientation;
+ uint16_t orientationAngle;
+ bool isPseudoDisplay;
+};
+
+struct DimensionInfo
+{
+ CSSRect rect;
+ CSSSize size;
+ LayoutDeviceIntPoint clientOffset;
+ LayoutDeviceIntPoint chromeOffset;
+};
+
+struct FrameScriptInfo
+{
+ nsString url;
+ bool runInGlobalScope;
+};
+
+struct FeaturePolicyInfo
+{
+ nsString[] inheritedDeniedFeatureNames;
+ nsString[] attributeEnabledFeatureNames;
+ nsString declaredString;
+ nullable nsIPrincipal defaultOrigin;
+ nullable nsIPrincipal selfOrigin;
+ nullable nsIPrincipal srcOrigin;
+};
+
+/**
+ * The information required to complete a window creation request.
+ */
+struct CreatedWindowInfo
+{
+ nsresult rv;
+ bool windowOpened;
+ FrameScriptInfo[] frameScripts;
+ uint32_t maxTouchPoints;
+ DimensionInfo dimensions;
+};
+
+
+struct DocShellLoadStateInit
+{
+ nullable nsIURI URI;
+ nullable nsIURI OriginalURI;
+ nullable nsIURI ResultPrincipalURI;
+ nullable nsIPrincipal TriggeringPrincipal;
+ nullable nsIReferrerInfo ReferrerInfo;
+ nullable nsIPrincipal PrincipalToInherit;
+ nullable nsIPrincipal PartitionedPrincipalToInherit;
+ nullable nsIURI BaseURI;
+ // The Content Security Policy of the load, that is, the CSP of the entity
+ // responsible for causing the load to occur. Most likely this is the CSP
+ // of the document that started the load. In case the entity starting the
+ // load did not use a CSP, then Csp can be null. Please note that this is
+ // also the CSP that will be applied to the load in case the load
+ // encounters a server side redirect.
+ nullable nsIContentSecurityPolicy Csp;
+ nullable nsIInputStream PostDataStream;
+ nullable nsIInputStream HeadersStream;
+ nullable nsIURI UnstrippedURI;
+ uint64_t LoadIdentifier;
+ nsString Target;
+ nsCString TypeHint;
+ nsString FileName;
+
+ MaybeDiscardedBrowsingContext SourceBrowsingContext;
+ MaybeDiscardedBrowsingContext TargetBrowsingContext;
+
+ // The provided remote type of the process responsible for causing the load to
+ // occur. Validated in the parent process.
+ nsCString TriggeringRemoteType;
+
+ nsString SrcdocData; // useless without sourcedocshell
+
+ nsCString? OriginalURIString;
+
+ nsCString? RemoteTypeOverride;
+
+ LoadingSessionHistoryInfo? loadingSessionHistoryInfo;
+
+ uint32_t LoadType;
+ uint32_t LoadFlags;
+ uint32_t InternalLoadFlags;
+
+ // The TriggineringSandboxFlags are the SandboxFlags of the entity
+ // responsible for causing the load to occur.
+ uint32_t TriggeringSandboxFlags;
+ uint64_t TriggeringWindowId;
+ bool TriggeringStorageAccess;
+ int32_t? CancelContentJSEpoch;
+
+ bool ResultPrincipalURIIsSome;
+ bool KeepResultPrincipalURIIfSet;
+ bool LoadReplace;
+ bool InheritPrincipal;
+ bool PrincipalIsExplicit;
+ bool ForceAllowDataURI;
+ bool IsExemptFromHTTPSFirstMode;
+ bool OriginalFrameSrc;
+ bool IsFormSubmission;
+ bool FirstParty;
+ bool HasValidUserGestureActivation;
+ bool AllowFocusMove;
+ bool IsFromProcessingFrameAttributes;
+ bool WasSchemelessInput;
+
+ // Fields missing due to lack of need or serialization
+ // nsCOMPtr<nsIDocShell> mSourceDocShell;
+ // bool mIsSrcDocLoad; // useless without sourcedocshell
+ // nsIChannel pendingRedirectedChannel; // sent through other mechanism
+
+ bool ChannelInitialized;
+
+ bool TryToReplaceWithSessionHistoryLoad;
+
+ bool IsMetaRefresh;
+};
+
+struct TimedChannelInfo
+{
+ bool timingEnabled;
+ int8_t redirectCount;
+ int8_t internalRedirectCount;
+ TimeStamp asyncOpen;
+ TimeStamp channelCreation;
+ TimeStamp redirectStart;
+ TimeStamp redirectEnd;
+ nsString initiatorType;
+ bool allRedirectsSameOrigin;
+ bool allRedirectsPassTimingAllowCheck;
+ bool? timingAllowCheckForPrincipal;
+ TimeStamp launchServiceWorkerStart;
+ TimeStamp launchServiceWorkerEnd;
+ TimeStamp dispatchFetchEventStart;
+ TimeStamp dispatchFetchEventEnd;
+ TimeStamp handleFetchEventStart;
+ TimeStamp handleFetchEventEnd;
+ TimeStamp responseStart;
+ TimeStamp responseEnd;
+};
+
+struct ReplacementChannelConfigInit
+{
+ uint32_t redirectFlags;
+ ClassOfService classOfService;
+ bool? privateBrowsing;
+ nsCString? method;
+ nullable nsIReferrerInfo referrerInfo;
+ TimedChannelInfo? timedChannelInfo;
+ nullable RemoteLazyInputStream uploadStream;
+ uint64_t uploadStreamLength;
+ bool uploadStreamHasHeaders;
+ nsCString? contentType;
+ nsCString? contentLength;
+};
+
+union IPDLVariantValue
+{
+ bool;
+ uint8_t; // In practice, uint8_t and uint16_t are likely unneeded,
+ int16_t; // as signed->unsigned->signed has universal behavior.
+ uint16_t; // but those conversions are only guaranteed in C++20.
+ int32_t;
+ uint32_t;
+ float;
+ double;
+ nsID;
+ nsString;
+ nsCString;
+ nullable nsIURI;
+ nullable nsIPrincipal;
+};
+
+struct IDPLVariant
+{
+ uint32_t type; // We explicitly store the original nsIVariant type so that
+ // the conversion back into a nsVariant later is lossless.
+ IPDLVariantValue data;
+};
+
+struct IPDLProperty
+{
+ nsString name;
+ nullable nsIVariant value;
+};
+
+// Struct with information to show a frame from the parent process.
+struct ParentShowInfo
+{
+ nsString name;
+ bool fakeShowInfo;
+ bool isTransparent;
+ float dpi;
+ int32_t widgetRounding;
+ double defaultScale;
+};
+
+// Struct with information to show an iframe from the process that owns the
+// frame.
+struct OwnerShowInfo {
+ // This can be an IntSize rather than a Rect because content processes always
+ // render to a virtual <0, 0> top-left point.
+ ScreenIntSize size;
+
+ // TODO(emilio): Margin preferences go here.
+ ScrollbarPreference scrollbarPreference;
+
+ // TODO(emilio): I think we should really be able to figure this out from the
+ // parent process too instead.
+ nsSizeMode sizeMode;
+};
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/DocShellMessageUtils.cpp b/dom/ipc/DocShellMessageUtils.cpp
new file mode 100644
index 0000000000..011abe3246
--- /dev/null
+++ b/dom/ipc/DocShellMessageUtils.cpp
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/DocShellMessageUtils.h"
+#include "mozilla/dom/DOMTypes.h"
+#include "mozilla/ipc/IPDLParamTraits.h"
+#include "nsSerializationHelper.h"
+
+namespace IPC {
+
+void ParamTraits<nsDocShellLoadState*>::Write(IPC::MessageWriter* aWriter,
+ nsDocShellLoadState* aParam) {
+ MOZ_RELEASE_ASSERT(aParam);
+ WriteParam(aWriter, aParam->Serialize(aWriter->GetActor()));
+}
+
+bool ParamTraits<nsDocShellLoadState*>::Read(
+ IPC::MessageReader* aReader, RefPtr<nsDocShellLoadState>* aResult) {
+ mozilla::dom::DocShellLoadStateInit loadState;
+ if (!ReadParam(aReader, &loadState)) {
+ return false;
+ }
+
+ // Assert if we somehow don't have a URI in our IPDL type, because we can't
+ // construct anything out of it. This mimics the assertion in the constructor
+ // for nsDocShellLoadState, but makes it clearer that the
+ // DocShellLoadStateInit IPC object can't be clearly converted into a
+ // nsDocShellLoadState.
+ if (!loadState.URI()) {
+ MOZ_ASSERT_UNREACHABLE("no URI in load state from IPC");
+ return false;
+ }
+
+ bool readSuccess = false;
+ RefPtr result =
+ new nsDocShellLoadState(loadState, aReader->GetActor(), &readSuccess);
+ if (readSuccess) {
+ *aResult = result.forget();
+ }
+ return readSuccess;
+}
+
+} // namespace IPC
diff --git a/dom/ipc/DocShellMessageUtils.h b/dom/ipc/DocShellMessageUtils.h
new file mode 100644
index 0000000000..934ba77f48
--- /dev/null
+++ b/dom/ipc/DocShellMessageUtils.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_docshell_message_utils_h__
+#define mozilla_dom_docshell_message_utils_h__
+
+#include "ipc/EnumSerializer.h"
+#include "nsCOMPtr.h"
+#include "nsDocShellLoadState.h"
+#include "nsIDocumentViewer.h"
+#include "mozilla/ScrollbarPreferences.h"
+#include "mozilla/ipc/IPDLParamTraits.h"
+
+namespace IPC {
+
+template <>
+struct ParamTraits<nsDocShellLoadState*> {
+ static void Write(IPC::MessageWriter* aWriter, nsDocShellLoadState* aParam);
+ static bool Read(IPC::MessageReader* aReader,
+ RefPtr<nsDocShellLoadState>* aResult);
+};
+
+template <>
+struct ParamTraits<mozilla::ScrollbarPreference>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::ScrollbarPreference, mozilla::ScrollbarPreference::Auto,
+ mozilla::ScrollbarPreference::LAST> {};
+
+template <>
+struct ParamTraits<mozilla::dom::PermitUnloadResult>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::dom::PermitUnloadResult,
+ mozilla::dom::PermitUnloadResult::eAllowNavigation,
+ mozilla::dom::PermitUnloadResult::eRequestBlockNavigation> {};
+
+template <>
+struct ParamTraits<mozilla::dom::XPCOMPermitUnloadAction>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::dom::XPCOMPermitUnloadAction,
+ mozilla::dom::XPCOMPermitUnloadAction::ePrompt,
+ mozilla::dom::XPCOMPermitUnloadAction::eDontPromptAndUnload> {};
+
+} // namespace IPC
+
+#endif // mozilla_dom_docshell_message_utils_h__
diff --git a/dom/ipc/EffectsInfo.h b/dom/ipc/EffectsInfo.h
new file mode 100644
index 0000000000..3c24b7a1cf
--- /dev/null
+++ b/dom/ipc/EffectsInfo.h
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_EffectsInfo_h
+#define mozilla_dom_EffectsInfo_h
+
+#include "nsRect.h"
+#include "Units.h"
+
+namespace mozilla::dom {
+
+/**
+ * An EffectsInfo contains information for a remote browser about the graphical
+ * effects that are being applied to it by ancestor browsers in different
+ * processes.
+ */
+class EffectsInfo {
+ public:
+ EffectsInfo() = default;
+
+ static EffectsInfo VisibleWithinRect(
+ const Maybe<nsRect>& aVisibleRect, const Scale2D& aRasterScale,
+ const ParentLayerToScreenScale2D& aTransformToAncestorScale) {
+ return EffectsInfo{aVisibleRect, aRasterScale, aTransformToAncestorScale};
+ }
+ static EffectsInfo FullyHidden() { return {}; }
+
+ bool operator==(const EffectsInfo& aOther) const {
+ return mVisibleRect == aOther.mVisibleRect &&
+ mRasterScale == aOther.mRasterScale &&
+ mTransformToAncestorScale == aOther.mTransformToAncestorScale;
+ }
+ bool operator!=(const EffectsInfo& aOther) const {
+ return !(*this == aOther);
+ }
+
+ bool IsVisible() const { return mVisibleRect.isSome(); }
+
+ // The visible rect of this browser relative to the root frame. This might be
+ // empty in cases where we might still be considered visible, like if we're
+ // zero-size but inside the viewport.
+ Maybe<nsRect> mVisibleRect;
+ // The desired scale factors to apply to rasterized content to match
+ // transforms applied in ancestor browsers. This gets propagated into the
+ // scale in StackingContextHelper.
+ Scale2D mRasterScale;
+ // TransformToAncestorScale to be set on FrameMetrics. It includes CSS
+ // transform scales and cumulative presshell resolution.
+ ParentLayerToScreenScale2D mTransformToAncestorScale;
+
+ // If you add new fields here, you must also update operator== and
+ // TabMessageUtils.
+
+ private:
+ EffectsInfo(const Maybe<nsRect>& aVisibleRect, const Scale2D& aRasterScale,
+ const ParentLayerToScreenScale2D& aTransformToAncestorScale)
+ : mVisibleRect(aVisibleRect),
+ mRasterScale(aRasterScale),
+ mTransformToAncestorScale(aTransformToAncestorScale) {}
+};
+
+} // namespace mozilla::dom
+
+#endif // mozilla_dom_EffectsInfo_h
diff --git a/dom/ipc/FilePickerMessageUtils.h b/dom/ipc/FilePickerMessageUtils.h
new file mode 100644
index 0000000000..2f4338c8a1
--- /dev/null
+++ b/dom/ipc/FilePickerMessageUtils.h
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_filepicker_message_utils_h__
+#define mozilla_dom_filepicker_message_utils_h__
+
+#include "ipc/EnumSerializer.h"
+#include "nsIFilePicker.h"
+
+namespace IPC {
+template <>
+struct ParamTraits<nsIFilePicker::Mode>
+ : public ContiguousEnumSerializerInclusive<
+ nsIFilePicker::Mode, nsIFilePicker::Mode::modeOpen,
+ nsIFilePicker::Mode::modeOpenMultiple> {};
+
+template <>
+struct ParamTraits<nsIFilePicker::CaptureTarget>
+ : public ContiguousEnumSerializerInclusive<
+ nsIFilePicker::CaptureTarget,
+ nsIFilePicker::CaptureTarget::captureNone,
+ nsIFilePicker::CaptureTarget::captureEnv> {};
+
+template <>
+struct ParamTraits<nsIFilePicker::ResultCode>
+ : public ContiguousEnumSerializerInclusive<
+ nsIFilePicker::ResultCode, nsIFilePicker::ResultCode::returnOK,
+ nsIFilePicker::ResultCode::returnReplace> {};
+} // namespace IPC
+
+#endif // mozilla_dom_filepicker_message_utils_h__
diff --git a/dom/ipc/FilePickerParent.cpp b/dom/ipc/FilePickerParent.cpp
new file mode 100644
index 0000000000..ebd24cb0d3
--- /dev/null
+++ b/dom/ipc/FilePickerParent.cpp
@@ -0,0 +1,304 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "FilePickerParent.h"
+#include "nsComponentManagerUtils.h"
+#include "nsNetCID.h"
+#include "mozilla/dom/Document.h"
+#include "nsIFile.h"
+#include "nsISimpleEnumerator.h"
+#include "mozilla/Unused.h"
+#include "mozilla/dom/BrowserParent.h"
+#include "mozilla/dom/CanonicalBrowsingContext.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/FileBlobImpl.h"
+#include "mozilla/dom/FileSystemSecurity.h"
+#include "mozilla/dom/IPCBlobUtils.h"
+
+using mozilla::Unused;
+using namespace mozilla::dom;
+
+NS_IMPL_ISUPPORTS(FilePickerParent::FilePickerShownCallback,
+ nsIFilePickerShownCallback);
+
+NS_IMETHODIMP
+FilePickerParent::FilePickerShownCallback::Done(
+ nsIFilePicker::ResultCode aResult) {
+ if (mFilePickerParent) {
+ mFilePickerParent->Done(aResult);
+ }
+ return NS_OK;
+}
+
+void FilePickerParent::FilePickerShownCallback::Destroy() {
+ mFilePickerParent = nullptr;
+}
+
+FilePickerParent::~FilePickerParent() = default;
+
+// We run code in three places:
+// 1. The main thread calls Dispatch() to start the runnable.
+// 2. The stream transport thread stat()s the file in Run() and then dispatches
+// the same runnable on the main thread.
+// 3. The main thread sends the results over IPC.
+FilePickerParent::IORunnable::IORunnable(FilePickerParent* aFPParent,
+ nsTArray<nsCOMPtr<nsIFile>>&& aFiles,
+ bool aIsDirectory)
+ : mozilla::Runnable("dom::FilePickerParent::IORunnable"),
+ mFilePickerParent(aFPParent),
+ mFiles(std::move(aFiles)),
+ mIsDirectory(aIsDirectory) {
+ MOZ_ASSERT_IF(aIsDirectory, mFiles.Length() == 1);
+}
+
+bool FilePickerParent::IORunnable::Dispatch() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ mEventTarget = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
+ if (!mEventTarget) {
+ return false;
+ }
+
+ nsresult rv = mEventTarget->Dispatch(this, NS_DISPATCH_NORMAL);
+ return NS_SUCCEEDED(rv);
+}
+
+NS_IMETHODIMP
+FilePickerParent::IORunnable::Run() {
+ // If we're on the main thread, then that means we're done. Just send the
+ // results.
+ if (NS_IsMainThread()) {
+ if (mFilePickerParent) {
+ mFilePickerParent->SendFilesOrDirectories(mResults);
+ }
+ return NS_OK;
+ }
+
+ // We're not on the main thread, so do the IO.
+
+ for (uint32_t i = 0; i < mFiles.Length(); ++i) {
+ if (mIsDirectory) {
+ nsAutoString path;
+ nsresult rv = mFiles[i]->GetPath(path);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ continue;
+ }
+
+ BlobImplOrString* data = mResults.AppendElement();
+ data->mType = BlobImplOrString::eDirectoryPath;
+ data->mDirectoryPath = path;
+ continue;
+ }
+
+ RefPtr<BlobImpl> blobImpl = new FileBlobImpl(mFiles[i]);
+
+ ErrorResult error;
+ blobImpl->GetSize(error);
+ if (NS_WARN_IF(error.Failed())) {
+ error.SuppressException();
+ continue;
+ }
+
+ blobImpl->GetLastModified(error);
+ if (NS_WARN_IF(error.Failed())) {
+ error.SuppressException();
+ continue;
+ }
+
+ BlobImplOrString* data = mResults.AppendElement();
+ data->mType = BlobImplOrString::eBlobImpl;
+ data->mBlobImpl = blobImpl;
+ }
+
+ // Dispatch ourselves back on the main thread.
+ if (NS_FAILED(NS_DispatchToMainThread(this))) {
+ // It's hard to see how we can recover gracefully in this case. The child
+ // process is waiting for an IPC, but that can only happen on the main
+ // thread.
+ MOZ_CRASH();
+ }
+
+ return NS_OK;
+}
+
+void FilePickerParent::IORunnable::Destroy() { mFilePickerParent = nullptr; }
+
+void FilePickerParent::SendFilesOrDirectories(
+ const nsTArray<BlobImplOrString>& aData) {
+ ContentParent* parent = BrowserParent::GetFrom(Manager())->Manager();
+
+ if (mMode == nsIFilePicker::modeGetFolder) {
+ MOZ_ASSERT(aData.Length() <= 1);
+ if (aData.IsEmpty()) {
+ Unused << Send__delete__(this, void_t(), mResult);
+ return;
+ }
+
+ MOZ_ASSERT(aData[0].mType == BlobImplOrString::eDirectoryPath);
+
+ // Let's inform the security singleton about the given access of this tab on
+ // this directory path.
+ RefPtr<FileSystemSecurity> fss = FileSystemSecurity::GetOrCreate();
+ fss->GrantAccessToContentProcess(parent->ChildID(),
+ aData[0].mDirectoryPath);
+
+ InputDirectory input;
+ input.directoryPath() = aData[0].mDirectoryPath;
+ Unused << Send__delete__(this, input, mResult);
+ return;
+ }
+
+ nsTArray<IPCBlob> ipcBlobs;
+
+ for (unsigned i = 0; i < aData.Length(); i++) {
+ IPCBlob ipcBlob;
+
+ MOZ_ASSERT(aData[i].mType == BlobImplOrString::eBlobImpl);
+ nsresult rv = IPCBlobUtils::Serialize(aData[i].mBlobImpl, ipcBlob);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ break;
+ }
+
+ ipcBlobs.AppendElement(ipcBlob);
+ }
+
+ InputBlobs inblobs;
+ inblobs.blobs() = std::move(ipcBlobs);
+
+ Unused << Send__delete__(this, inblobs, mResult);
+}
+
+void FilePickerParent::Done(nsIFilePicker::ResultCode aResult) {
+ mResult = aResult;
+
+ if (mResult != nsIFilePicker::returnOK) {
+ Unused << Send__delete__(this, void_t(), mResult);
+ return;
+ }
+
+ nsTArray<nsCOMPtr<nsIFile>> files;
+ if (mMode == nsIFilePicker::modeOpenMultiple) {
+ nsCOMPtr<nsISimpleEnumerator> iter;
+ NS_ENSURE_SUCCESS_VOID(mFilePicker->GetFiles(getter_AddRefs(iter)));
+
+ nsCOMPtr<nsISupports> supports;
+ bool loop = true;
+ while (NS_SUCCEEDED(iter->HasMoreElements(&loop)) && loop) {
+ iter->GetNext(getter_AddRefs(supports));
+ if (supports) {
+ nsCOMPtr<nsIFile> file = do_QueryInterface(supports);
+ MOZ_ASSERT(file);
+ files.AppendElement(file);
+ }
+ }
+ } else {
+ nsCOMPtr<nsIFile> file;
+ mFilePicker->GetFile(getter_AddRefs(file));
+ if (file) {
+ files.AppendElement(file);
+ }
+ }
+
+ if (files.IsEmpty()) {
+ Unused << Send__delete__(this, void_t(), mResult);
+ return;
+ }
+
+ MOZ_ASSERT(!mRunnable);
+ mRunnable = new IORunnable(this, std::move(files),
+ mMode == nsIFilePicker::modeGetFolder);
+
+ // Dispatch to background thread to do I/O:
+ if (!mRunnable->Dispatch()) {
+ Unused << Send__delete__(this, void_t(), nsIFilePicker::returnCancel);
+ }
+}
+
+bool FilePickerParent::CreateFilePicker() {
+ mFilePicker = do_CreateInstance("@mozilla.org/filepicker;1");
+ if (!mFilePicker) {
+ return false;
+ }
+
+ auto* browserParent = BrowserParent::GetFrom(Manager());
+ auto* browsingContext = browserParent->GetBrowsingContext();
+ Element* element = browserParent->GetOwnerElement();
+ if (!element) {
+ return false;
+ }
+
+ nsCOMPtr<mozIDOMWindowProxy> window = element->OwnerDoc()->GetWindow();
+ if (!window) {
+ return false;
+ }
+
+ return NS_SUCCEEDED(
+ mFilePicker->Init(window, mTitle, mMode, browsingContext));
+}
+
+mozilla::ipc::IPCResult FilePickerParent::RecvOpen(
+ const int16_t& aSelectedType, const bool& aAddToRecentDocs,
+ const nsString& aDefaultFile, const nsString& aDefaultExtension,
+ nsTArray<nsString>&& aFilters, nsTArray<nsString>&& aFilterNames,
+ nsTArray<nsString>&& aRawFilters, const nsString& aDisplayDirectory,
+ const nsString& aDisplaySpecialDirectory, const nsString& aOkButtonLabel,
+ const nsIFilePicker::CaptureTarget& aCapture) {
+ if (!CreateFilePicker()) {
+ Unused << Send__delete__(this, void_t(), nsIFilePicker::returnCancel);
+ return IPC_OK();
+ }
+
+ mFilePicker->SetAddToRecentDocs(aAddToRecentDocs);
+
+ for (uint32_t i = 0; i < aFilters.Length(); ++i) {
+ mFilePicker->AppendFilter(aFilterNames[i], aFilters[i]);
+ }
+
+ for (uint32_t i = 0; i < aRawFilters.Length(); ++i) {
+ mFilePicker->AppendRawFilter(aRawFilters[i]);
+ }
+
+ mFilePicker->SetDefaultString(aDefaultFile);
+ mFilePicker->SetDefaultExtension(aDefaultExtension);
+ mFilePicker->SetFilterIndex(aSelectedType);
+ mFilePicker->SetOkButtonLabel(aOkButtonLabel);
+ mFilePicker->SetCapture(aCapture);
+
+ if (!aDisplayDirectory.IsEmpty()) {
+ nsCOMPtr<nsIFile> localFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
+ if (localFile) {
+ localFile->InitWithPath(aDisplayDirectory);
+ mFilePicker->SetDisplayDirectory(localFile);
+ }
+ } else if (!aDisplaySpecialDirectory.IsEmpty()) {
+ mFilePicker->SetDisplaySpecialDirectory(aDisplaySpecialDirectory);
+ }
+
+ MOZ_ASSERT(!mCallback);
+ mCallback = new FilePickerShownCallback(this);
+
+ mFilePicker->Open(mCallback);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult FilePickerParent::RecvClose() {
+ if (mFilePicker) {
+ mFilePicker->Close();
+ }
+ return IPC_OK();
+}
+
+void FilePickerParent::ActorDestroy(ActorDestroyReason aWhy) {
+ if (mCallback) {
+ mCallback->Destroy();
+ mCallback = nullptr;
+ }
+ if (mRunnable) {
+ mRunnable->Destroy();
+ mRunnable = nullptr;
+ }
+}
diff --git a/dom/ipc/FilePickerParent.h b/dom/ipc/FilePickerParent.h
new file mode 100644
index 0000000000..f0fe0dc2d0
--- /dev/null
+++ b/dom/ipc/FilePickerParent.h
@@ -0,0 +1,101 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_FilePickerParent_h
+#define mozilla_dom_FilePickerParent_h
+
+#include "nsIEventTarget.h"
+#include "nsIFilePicker.h"
+#include "nsCOMArray.h"
+#include "nsThreadUtils.h"
+#include "mozilla/dom/File.h"
+#include "mozilla/dom/PFilePickerParent.h"
+
+class nsIFile;
+
+namespace mozilla::dom {
+
+class FilePickerParent : public PFilePickerParent {
+ public:
+ FilePickerParent(const nsString& aTitle, const nsIFilePicker::Mode& aMode)
+ : mTitle(aTitle), mMode(aMode), mResult(nsIFilePicker::returnOK) {}
+
+ private:
+ virtual ~FilePickerParent();
+
+ public:
+ NS_INLINE_DECL_REFCOUNTING(FilePickerParent, final)
+
+ void Done(nsIFilePicker::ResultCode aResult);
+
+ struct BlobImplOrString {
+ RefPtr<BlobImpl> mBlobImpl;
+ nsString mDirectoryPath;
+
+ enum { eBlobImpl, eDirectoryPath } mType;
+ };
+
+ void SendFilesOrDirectories(const nsTArray<BlobImplOrString>& aData);
+
+ mozilla::ipc::IPCResult RecvOpen(
+ const int16_t& aSelectedType, const bool& aAddToRecentDocs,
+ const nsString& aDefaultFile, const nsString& aDefaultExtension,
+ nsTArray<nsString>&& aFilters, nsTArray<nsString>&& aFilterNames,
+ nsTArray<nsString>&& aRawFilters, const nsString& aDisplayDirectory,
+ const nsString& aDisplaySpecialDirectory, const nsString& aOkButtonLabel,
+ const nsIFilePicker::CaptureTarget& aCapture);
+
+ mozilla::ipc::IPCResult RecvClose();
+
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ class FilePickerShownCallback : public nsIFilePickerShownCallback {
+ public:
+ explicit FilePickerShownCallback(FilePickerParent* aFilePickerParent)
+ : mFilePickerParent(aFilePickerParent) {}
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIFILEPICKERSHOWNCALLBACK
+
+ void Destroy();
+
+ private:
+ virtual ~FilePickerShownCallback() = default;
+ RefPtr<FilePickerParent> mFilePickerParent;
+ };
+
+ private:
+ bool CreateFilePicker();
+
+ // This runnable is used to do some I/O operation on a separate thread.
+ class IORunnable : public Runnable {
+ RefPtr<FilePickerParent> mFilePickerParent;
+ nsTArray<nsCOMPtr<nsIFile>> mFiles;
+ nsTArray<BlobImplOrString> mResults;
+ nsCOMPtr<nsIEventTarget> mEventTarget;
+ bool mIsDirectory;
+
+ public:
+ IORunnable(FilePickerParent* aFPParent,
+ nsTArray<nsCOMPtr<nsIFile>>&& aFiles, bool aIsDirectory);
+
+ bool Dispatch();
+ NS_IMETHOD Run() override;
+ void Destroy();
+ };
+
+ RefPtr<IORunnable> mRunnable;
+ RefPtr<FilePickerShownCallback> mCallback;
+ nsCOMPtr<nsIFilePicker> mFilePicker;
+
+ nsString mTitle;
+ nsIFilePicker::Mode mMode;
+ nsIFilePicker::ResultCode mResult;
+};
+
+} // namespace mozilla::dom
+
+#endif // mozilla_dom_FilePickerParent_h
diff --git a/dom/ipc/IPCTransferable.ipdlh b/dom/ipc/IPCTransferable.ipdlh
new file mode 100644
index 0000000000..1e277d05dd
--- /dev/null
+++ b/dom/ipc/IPCTransferable.ipdlh
@@ -0,0 +1,93 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include "mozilla/GfxMessageUtils.h";
+include "mozilla/dom/PermissionMessageUtils.h";
+
+include IPCBlob;
+include NeckoChannelParams;
+
+using mozilla::gfx::SurfaceFormat from "mozilla/gfx/Types.h";
+[RefCounted] using class nsIPrincipal from "nsIPrincipal.h";
+[RefCounted] using class nsIReferrerInfo from "nsIReferrerInfo.h";
+[MoveOnly] using class mozilla::ipc::BigBuffer from "mozilla/ipc/BigBuffer.h";
+
+namespace mozilla {
+namespace dom {
+
+struct IPCTransferableDataString
+{
+ BigBuffer data;
+};
+
+struct IPCTransferableDataCString
+{
+ BigBuffer data;
+};
+
+struct IPCTransferableDataInputStream
+{
+ // NOTE: Editor currently relies on these input streams being synchronous, so
+ // we can't safely serialize them using IPCStream (see bug 1778565). Instead,
+ // they're serialized as a `BigBuffer`, and converted to a nsStringInputStream
+ // on the receiving side. If we are able to use async streams reliably in the
+ // future, we could consider switching the code which adds `nsIInputStream`s
+ // to the transferable to use `BlobImpl` instead, for more consistency between
+ // image formats.
+ BigBuffer data;
+};
+
+struct IPCTransferableDataImageContainer
+{
+ BigBuffer data;
+ uint32_t width;
+ uint32_t height;
+ uint32_t stride;
+ SurfaceFormat format;
+};
+
+struct IPCTransferableDataBlob
+{
+ IPCBlob blob;
+};
+
+union IPCTransferableDataType
+{
+ IPCTransferableDataString;
+ IPCTransferableDataCString;
+ IPCTransferableDataInputStream;
+ IPCTransferableDataImageContainer;
+ IPCTransferableDataBlob;
+};
+
+struct IPCTransferableDataItem
+{
+ nsCString flavor;
+ IPCTransferableDataType data;
+};
+
+struct IPCTransferableData
+{
+ IPCTransferableDataItem[] items;
+};
+
+union IPCTransferableDataOrError {
+ IPCTransferableData;
+ nsresult;
+};
+
+struct IPCTransferable
+{
+ IPCTransferableData data;
+ bool isPrivateData;
+ nullable nsIPrincipal requestingPrincipal;
+ CookieJarSettingsArgs? cookieJarSettings;
+ nsContentPolicyType contentPolicyType;
+ nullable nsIReferrerInfo referrerInfo;
+};
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/IdType.h b/dom/ipc/IdType.h
new file mode 100644
index 0000000000..ccf8b52693
--- /dev/null
+++ b/dom/ipc/IdType.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_IdType_h
+#define mozilla_dom_IdType_h
+
+#include "ipc/IPCMessageUtils.h"
+
+namespace IPC {
+template <typename T>
+struct ParamTraits;
+} // namespace IPC
+
+namespace mozilla::dom {
+class BrowsingContext;
+class ContentParent;
+class BrowserParent;
+
+template <typename T>
+class IdType {
+ friend struct IPC::ParamTraits<IdType<T>>;
+
+ public:
+ IdType() : mId(0) {}
+ explicit IdType(uint64_t aId) : mId(aId) {}
+
+ operator uint64_t() const { return mId; }
+
+ IdType& operator=(uint64_t aId) {
+ mId = aId;
+ return *this;
+ }
+
+ bool operator<(const IdType& rhs) { return mId < rhs.mId; }
+
+ private:
+ uint64_t mId;
+};
+
+using TabId = IdType<BrowserParent>;
+using ContentParentId = IdType<ContentParent>;
+} // namespace mozilla::dom
+
+namespace IPC {
+
+template <typename T>
+struct ParamTraits<mozilla::dom::IdType<T>> {
+ using paramType = mozilla::dom::IdType<T>;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mId);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ return ReadParam(aReader, &aResult->mId);
+ }
+};
+
+} // namespace IPC
+
+#endif // mozilla_dom_IdType_h
diff --git a/dom/ipc/InProcessChild.h b/dom/ipc/InProcessChild.h
new file mode 100644
index 0000000000..16176ef98b
--- /dev/null
+++ b/dom/ipc/InProcessChild.h
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_InProcessChild_h
+#define mozilla_dom_InProcessChild_h
+
+#include "mozilla/dom/PInProcessChild.h"
+#include "mozilla/dom/JSProcessActorChild.h"
+#include "mozilla/dom/ProcessActor.h"
+#include "mozilla/dom/RemoteType.h"
+#include "mozilla/StaticPtr.h"
+#include "nsIDOMProcessChild.h"
+
+namespace mozilla::dom {
+class PWindowGlobalParent;
+class PWindowGlobalChild;
+class InProcessParent;
+
+/**
+ * The `InProcessChild` class represents the child half of a main-thread to
+ * main-thread actor.
+ *
+ * The `PInProcess` actor should be used as an alternate manager to `PContent`
+ * for async actors which want to communicate uniformly between Content->Chrome
+ * and Chrome->Chrome situations.
+ */
+class InProcessChild final : public nsIDOMProcessChild,
+ public PInProcessChild,
+ public ProcessActor {
+ public:
+ friend class InProcessParent;
+ friend class PInProcessChild;
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIDOMPROCESSCHILD
+
+ // Get the singleton instance of this actor.
+ static InProcessChild* Singleton();
+
+ // Get the parent side of the in-process child actor |aActor|. If |aActor| is
+ // not an in-process actor, or is not connected, this method will return
+ // |nullptr|.
+ static IProtocol* ParentActorFor(IProtocol* aActor);
+
+ const nsACString& GetRemoteType() const override { return NOT_REMOTE_TYPE; }
+
+ protected:
+ already_AddRefed<JSActor> InitJSActor(JS::Handle<JSObject*> aMaybeActor,
+ const nsACString& aName,
+ ErrorResult& aRv) override;
+ mozilla::ipc::IProtocol* AsNativeActor() override { return this; }
+
+ private:
+ // NOTE: PInProcess lifecycle management is declared as staic methods and
+ // state on InProcessParent, and implemented in InProcessImpl.cpp.
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+ ~InProcessChild() = default;
+
+ static StaticRefPtr<InProcessChild> sSingleton;
+
+ nsRefPtrHashtable<nsCStringHashKey, JSProcessActorChild> mProcessActors;
+};
+
+} // namespace mozilla::dom
+
+#endif // defined(mozilla_dom_InProcessChild_h)
diff --git a/dom/ipc/InProcessImpl.cpp b/dom/ipc/InProcessImpl.cpp
new file mode 100644
index 0000000000..e076a84c44
--- /dev/null
+++ b/dom/ipc/InProcessImpl.cpp
@@ -0,0 +1,324 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/InProcessParent.h"
+#include "mozilla/dom/InProcessChild.h"
+#include "mozilla/dom/JSProcessActorBinding.h"
+#include "nsIObserverService.h"
+#include "mozilla/Services.h"
+
+using namespace mozilla::ipc;
+
+// This file contains the implementation of core InProcess lifecycle management
+// facilities.
+
+namespace mozilla::dom {
+
+StaticRefPtr<InProcessParent> InProcessParent::sSingleton;
+StaticRefPtr<InProcessChild> InProcessChild::sSingleton;
+bool InProcessParent::sShutdown = false;
+
+//////////////////////////////////////////
+// InProcess actor lifecycle management //
+//////////////////////////////////////////
+
+/* static */
+InProcessChild* InProcessChild::Singleton() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!sSingleton) {
+ InProcessParent::Startup();
+ }
+ return sSingleton;
+}
+
+/* static */
+InProcessParent* InProcessParent::Singleton() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!sSingleton) {
+ InProcessParent::Startup();
+ }
+ return sSingleton;
+}
+
+/* static */
+void InProcessParent::Startup() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (sShutdown) {
+ NS_WARNING("Could not get in-process actor while shutting down!");
+ return;
+ }
+
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (!obs) {
+ sShutdown = true;
+ NS_WARNING("Failed to get nsIObserverService for in-process actor");
+ return;
+ }
+
+ RefPtr<InProcessParent> parent = new InProcessParent();
+ RefPtr<InProcessChild> child = new InProcessChild();
+
+ // Observe the shutdown event to close & clean up after ourselves.
+ nsresult rv = obs->AddObserver(parent, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+
+ // Link the two actors
+ if (!child->OpenOnSameThread(parent, ChildSide)) {
+ MOZ_CRASH("Failed to open InProcessChild!");
+ }
+
+ parent->SetOtherProcessId(base::GetCurrentProcId());
+
+ // Stash global references to fetch the other side of the reference.
+ InProcessParent::sSingleton = std::move(parent);
+ InProcessChild::sSingleton = std::move(child);
+}
+
+/* static */
+void InProcessParent::Shutdown() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (!sSingleton || sShutdown) {
+ return;
+ }
+
+ sShutdown = true;
+
+ RefPtr<InProcessParent> parent = sSingleton;
+ InProcessParent::sSingleton = nullptr;
+ InProcessChild::sSingleton = nullptr;
+
+ // Calling `Close` on the actor will cause the `Dealloc` methods to be called,
+ // freeing the remaining references.
+ parent->Close();
+}
+
+NS_IMETHODIMP
+InProcessParent::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData) {
+ MOZ_ASSERT(!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID));
+ InProcessParent::Shutdown();
+ return NS_OK;
+}
+
+void InProcessParent::ActorDestroy(ActorDestroyReason aWhy) {
+ JSActorDidDestroy();
+ InProcessParent::Shutdown();
+}
+
+void InProcessChild::ActorDestroy(ActorDestroyReason aWhy) {
+ JSActorDidDestroy();
+ InProcessParent::Shutdown();
+}
+
+/////////////////////////
+// nsIDOMProcessParent //
+/////////////////////////
+
+NS_IMETHODIMP
+InProcessParent::GetChildID(uint64_t* aChildID) {
+ *aChildID = 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InProcessParent::GetOsPid(int32_t* aOsPid) {
+ // InProcessParent always run in the parent process,
+ // so we can return the current process id.
+ *aOsPid = base::GetCurrentProcId();
+ return NS_OK;
+}
+
+NS_IMETHODIMP InProcessParent::GetRemoteType(nsACString& aRemoteType) {
+ aRemoteType = NOT_REMOTE_TYPE;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InProcessParent::GetActor(const nsACString& aName, JSContext* aCx,
+ JSProcessActorParent** aActor) {
+ ErrorResult error;
+ RefPtr<JSProcessActorParent> actor =
+ JSActorManager::GetActor(aCx, aName, error)
+ .downcast<JSProcessActorParent>();
+ if (error.MaybeSetPendingException(aCx)) {
+ return NS_ERROR_FAILURE;
+ }
+ actor.forget(aActor);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InProcessParent::GetExistingActor(const nsACString& aName,
+ JSProcessActorParent** aActor) {
+ RefPtr<JSProcessActorParent> actor =
+ JSActorManager::GetExistingActor(aName).downcast<JSProcessActorParent>();
+ actor.forget(aActor);
+ return NS_OK;
+}
+
+already_AddRefed<JSActor> InProcessParent::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();
+}
+
+NS_IMETHODIMP
+InProcessParent::GetCanSend(bool* aCanSend) {
+ *aCanSend = CanSend();
+ return NS_OK;
+}
+
+ContentParent* InProcessParent::AsContentParent() { return nullptr; }
+
+JSActorManager* InProcessParent::AsJSActorManager() { return this; }
+
+////////////////////////
+// nsIDOMProcessChild //
+////////////////////////
+
+NS_IMETHODIMP
+InProcessChild::GetChildID(uint64_t* aChildID) {
+ *aChildID = 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InProcessChild::GetActor(const nsACString& aName, JSContext* aCx,
+ JSProcessActorChild** aActor) {
+ ErrorResult error;
+ RefPtr<JSProcessActorChild> actor =
+ JSActorManager::GetActor(aCx, aName, error)
+ .downcast<JSProcessActorChild>();
+ if (error.MaybeSetPendingException(aCx)) {
+ return NS_ERROR_FAILURE;
+ }
+ actor.forget(aActor);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+InProcessChild::GetExistingActor(const nsACString& aName,
+ JSProcessActorChild** aActor) {
+ RefPtr<JSProcessActorChild> actor =
+ JSActorManager::GetExistingActor(aName).downcast<JSProcessActorChild>();
+ actor.forget(aActor);
+ return NS_OK;
+}
+
+already_AddRefed<JSActor> InProcessChild::InitJSActor(
+ JS::Handle<JSObject*> aMaybeActor, const nsACString& aName,
+ ErrorResult& aRv) {
+ RefPtr<JSProcessActorChild> actor;
+ if (aMaybeActor.get()) {
+ aRv = UNWRAP_OBJECT(JSProcessActorChild, aMaybeActor.get(), actor);
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+ } else {
+ actor = new JSProcessActorChild();
+ }
+
+ MOZ_RELEASE_ASSERT(!actor->Manager(),
+ "mManager was already initialized once!");
+ actor->Init(aName, this);
+ return actor.forget();
+}
+
+NS_IMETHODIMP
+InProcessChild::GetCanSend(bool* aCanSend) {
+ *aCanSend = CanSend();
+ return NS_OK;
+}
+
+ContentChild* InProcessChild::AsContentChild() { return nullptr; }
+
+JSActorManager* InProcessChild::AsJSActorManager() { return this; }
+
+////////////////////////////////
+// In-Process Actor Utilities //
+////////////////////////////////
+
+// Helper method for implementing ParentActorFor and ChildActorFor.
+static IProtocol* GetOtherInProcessActor(IProtocol* aActor) {
+ MOZ_ASSERT(aActor->GetSide() != UnknownSide, "bad unknown side");
+
+ // Discover the manager of aActor which is PInProcess.
+ IProtocol* current = aActor;
+ while (current && current->CanRecv()) {
+ if (current->GetProtocolId() == PInProcessMsgStart) {
+ break; // Found the correct actor.
+ }
+ current = current->Manager();
+ }
+ if (!current || !current->CanRecv()) {
+ return nullptr; // Not a live PInProcess actor, return |nullptr|
+ }
+
+ MOZ_ASSERT(current->GetSide() == aActor->GetSide(), "side changed?");
+ MOZ_ASSERT_IF(aActor->GetSide() == ParentSide,
+ current == InProcessParent::Singleton());
+ MOZ_ASSERT_IF(aActor->GetSide() == ChildSide,
+ current == InProcessChild::Singleton());
+
+ // Check whether this is InProcessParent or InProcessChild, and get the other
+ // side's toplevel actor.
+ IProtocol* otherRoot = nullptr;
+ if (aActor->GetSide() == ParentSide) {
+ otherRoot = InProcessChild::Singleton();
+ } else {
+ otherRoot = InProcessParent::Singleton();
+ }
+ if (NS_WARN_IF(!otherRoot)) {
+ return nullptr;
+ }
+
+ // Look up the actor on the other side, and return it.
+ IProtocol* otherActor = otherRoot->Lookup(aActor->Id());
+ if (otherActor) {
+ MOZ_ASSERT(otherActor->GetSide() != UnknownSide, "bad unknown side");
+ MOZ_ASSERT(otherActor->GetSide() != aActor->GetSide(), "Wrong side!");
+ MOZ_ASSERT(otherActor->GetProtocolId() == aActor->GetProtocolId(),
+ "Wrong type of protocol!");
+ }
+
+ return otherActor;
+}
+
+/* static */
+IProtocol* InProcessParent::ChildActorFor(IProtocol* aActor) {
+ MOZ_ASSERT(aActor && aActor->GetSide() == ParentSide);
+ return GetOtherInProcessActor(aActor);
+}
+
+/* static */
+IProtocol* InProcessChild::ParentActorFor(IProtocol* aActor) {
+ MOZ_ASSERT(aActor && aActor->GetSide() == ChildSide);
+ return GetOtherInProcessActor(aActor);
+}
+
+NS_IMPL_ISUPPORTS(InProcessParent, nsIDOMProcessParent, nsIObserver)
+NS_IMPL_ISUPPORTS(InProcessChild, nsIDOMProcessChild)
+
+} // namespace mozilla::dom
diff --git a/dom/ipc/InProcessParent.h b/dom/ipc/InProcessParent.h
new file mode 100644
index 0000000000..112558f5bd
--- /dev/null
+++ b/dom/ipc/InProcessParent.h
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_InProcessParent_h
+#define mozilla_dom_InProcessParent_h
+
+#include "mozilla/dom/PInProcessParent.h"
+#include "mozilla/dom/JSProcessActorParent.h"
+#include "mozilla/dom/ProcessActor.h"
+#include "mozilla/dom/RemoteType.h"
+#include "mozilla/StaticPtr.h"
+#include "nsIDOMProcessParent.h"
+
+namespace mozilla::dom {
+class PWindowGlobalParent;
+class PWindowGlobalChild;
+class InProcessChild;
+
+/**
+ * The `InProcessParent` class represents the parent half of a main-thread to
+ * main-thread actor.
+ *
+ * The `PInProcess` actor should be used as an alternate manager to `PContent`
+ * for async actors which want to communicate uniformly between Content->Chrome
+ * and Chrome->Chrome situations.
+ */
+class InProcessParent final : public nsIDOMProcessParent,
+ public nsIObserver,
+ public PInProcessParent,
+ public ProcessActor {
+ public:
+ friend class InProcessChild;
+ friend class PInProcessParent;
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIDOMPROCESSPARENT
+ NS_DECL_NSIOBSERVER
+
+ // Get the singleton instance of this actor.
+ static InProcessParent* Singleton();
+
+ // Get the child side of the in-process child actor |aActor|. If |aActor| is
+ // not an in-process actor, or is not connected, this method will return
+ // |nullptr|.
+ static IProtocol* ChildActorFor(IProtocol* aActor);
+
+ const nsACString& GetRemoteType() const override { return NOT_REMOTE_TYPE; };
+
+ protected:
+ already_AddRefed<JSActor> InitJSActor(JS::Handle<JSObject*> aMaybeActor,
+ const nsACString& aName,
+ ErrorResult& aRv) override;
+ mozilla::ipc::IProtocol* AsNativeActor() override { return this; }
+
+ private:
+ // Lifecycle management is implemented in InProcessImpl.cpp
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+ ~InProcessParent() = default;
+
+ static void Startup();
+ static void Shutdown();
+
+ static StaticRefPtr<InProcessParent> sSingleton;
+ static bool sShutdown;
+
+ nsRefPtrHashtable<nsCStringHashKey, JSProcessActorParent> mProcessActors;
+};
+
+} // namespace mozilla::dom
+
+#endif // defined(mozilla_dom_InProcessParent_h)
diff --git a/dom/ipc/JSOracleChild.cpp b/dom/ipc/JSOracleChild.cpp
new file mode 100644
index 0000000000..1ac495ab87
--- /dev/null
+++ b/dom/ipc/JSOracleChild.cpp
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/JSOracleChild.h"
+
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/dom/JSValidatorChild.h"
+#include "mozilla/dom/PJSValidatorChild.h"
+#include "mozilla/ipc/Endpoint.h"
+
+using namespace mozilla::dom;
+
+static mozilla::StaticRefPtr<JSOracleChild> sOracleSingletonChild;
+
+static mozilla::StaticAutoPtr<JSFrontendContextHolder> sJSFrontendContextHolder;
+
+/* static */
+void JSFrontendContextHolder::MaybeInit() {
+ if (!sJSFrontendContextHolder) {
+ sJSFrontendContextHolder = new JSFrontendContextHolder();
+ ClearOnShutdown(&sJSFrontendContextHolder);
+ }
+}
+
+/* static */
+JS::FrontendContext* JSOracleChild::JSFrontendContext() {
+ MOZ_ASSERT(sJSFrontendContextHolder);
+ return sJSFrontendContextHolder->mFc;
+}
+
+JSOracleChild* JSOracleChild::GetSingleton() {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!sOracleSingletonChild) {
+ sOracleSingletonChild = new JSOracleChild();
+ ClearOnShutdown(&sOracleSingletonChild);
+ }
+ return sOracleSingletonChild;
+}
+
+already_AddRefed<PJSValidatorChild> JSOracleChild::AllocPJSValidatorChild() {
+ return MakeAndAddRef<JSValidatorChild>();
+}
+
+void JSOracleChild::Start(Endpoint<PJSOracleChild>&& aEndpoint) {
+ DebugOnly<bool> ok = std::move(aEndpoint).Bind(this);
+ JSFrontendContextHolder::MaybeInit();
+ MOZ_ASSERT(ok);
+}
diff --git a/dom/ipc/JSOracleChild.h b/dom/ipc/JSOracleChild.h
new file mode 100644
index 0000000000..f62ef7134c
--- /dev/null
+++ b/dom/ipc/JSOracleChild.h
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_JSOracleChild
+#define mozilla_dom_JSOracleChild
+
+#include "mozilla/dom/PJSOracleChild.h"
+
+#include "js/experimental/JSStencil.h"
+#include "js/experimental/CompileScript.h"
+#include "js/Initialization.h"
+#include "jsapi.h"
+
+namespace mozilla::ipc {
+class UtilityProcessParent;
+}
+
+namespace mozilla::dom {
+struct JSFrontendContextHolder {
+ JSFrontendContextHolder() {
+ MOZ_RELEASE_ASSERT(JS_IsInitialized(),
+ "UtilityProcessChild::Init should have JS initialized");
+
+ mFc = JS::NewFrontendContext();
+ if (!mFc) {
+ MOZ_CRASH("Failed to create JS FrontendContext");
+ return;
+ }
+
+ // See the comment in XPCJSContext::Initialize.
+ const size_t kDefaultStackQuota = 128 * sizeof(size_t) * 1024;
+
+ JS::SetNativeStackQuota(mFc, kDefaultStackQuota);
+ }
+
+ ~JSFrontendContextHolder() {
+ if (mFc) {
+ JS::DestroyFrontendContext(mFc);
+ }
+ }
+
+ static void MaybeInit();
+
+ JS::FrontendContext* mFc;
+};
+
+class PJSValidatorChild;
+
+class JSOracleChild final : public PJSOracleChild {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(JSOracleChild, override);
+
+ already_AddRefed<PJSValidatorChild> AllocPJSValidatorChild();
+
+ void Start(Endpoint<PJSOracleChild>&& aEndpoint);
+
+ static JS::FrontendContext* JSFrontendContext();
+
+ private:
+ ~JSOracleChild() = default;
+
+ static JSOracleChild* GetSingleton();
+};
+} // namespace mozilla::dom
+
+#endif // defined(mozilla_dom_JSOracleChild)
diff --git a/dom/ipc/JSOracleParent.cpp b/dom/ipc/JSOracleParent.cpp
new file mode 100644
index 0000000000..31a8768422
--- /dev/null
+++ b/dom/ipc/JSOracleParent.cpp
@@ -0,0 +1,81 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/JSOracleParent.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/dom/PJSOracle.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/ipc/UtilityProcessManager.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+static StaticRefPtr<JSOracleParent> sOracleSingleton;
+
+/* static */
+void JSOracleParent::WithJSOracle(
+ const std::function<void(JSOracleParent* aParent)>& aCallback) {
+ GetSingleton()->StartJSOracle()->Then(
+ GetMainThreadSerialEventTarget(), __func__,
+ [aCallback](const JSOraclePromise::ResolveOrRejectValue& aResult) {
+ aCallback(aResult.IsReject() || !aResult.ResolveValue()
+ ? nullptr
+ : GetSingleton());
+ });
+}
+
+void JSOracleParent::ActorDestroy(ActorDestroyReason aReason) {
+ // Given an actor can only be bound to one process,
+ // if the utility process crashes and we create a new one,
+ // we can't reuse the same JSOracleParent instance for it,
+ // so we always create a new JSOracleParent to replace
+ // the existing one.
+ if (aReason == ActorDestroyReason::AbnormalShutdown) {
+ sOracleSingleton = new JSOracleParent();
+ }
+}
+
+/* static */
+JSOracleParent* JSOracleParent::GetSingleton() {
+ if (!sOracleSingleton) {
+ sOracleSingleton = new JSOracleParent();
+ ClearOnShutdown(&sOracleSingleton);
+ }
+
+ return sOracleSingleton;
+}
+
+RefPtr<JSOracleParent::JSOraclePromise> JSOracleParent::StartJSOracle() {
+ using namespace mozilla::ipc;
+ RefPtr<JSOracleParent> parent = JSOracleParent::GetSingleton();
+ return UtilityProcessManager::GetSingleton()->StartJSOracle(parent);
+}
+
+nsresult JSOracleParent::BindToUtilityProcess(
+ const RefPtr<mozilla::ipc::UtilityProcessParent>& aUtilityParent) {
+ Endpoint<PJSOracleParent> parentEnd;
+ Endpoint<PJSOracleChild> childEnd;
+ MOZ_ASSERT(aUtilityParent);
+ if (NS_FAILED(PJSOracle::CreateEndpoints(base::GetCurrentProcId(),
+ aUtilityParent->OtherPid(),
+ &parentEnd, &childEnd))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!aUtilityParent->SendStartJSOracleService(std::move(childEnd))) {
+ return NS_ERROR_FAILURE;
+ }
+
+ Bind(std::move(parentEnd));
+
+ return NS_OK;
+}
+
+void JSOracleParent::Bind(Endpoint<PJSOracleParent>&& aEndpoint) {
+ DebugOnly<bool> ok = aEndpoint.Bind(this);
+ MOZ_ASSERT(ok);
+}
diff --git a/dom/ipc/JSOracleParent.h b/dom/ipc/JSOracleParent.h
new file mode 100644
index 0000000000..f318123369
--- /dev/null
+++ b/dom/ipc/JSOracleParent.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_JSOracleParent
+#define mozilla_dom_JSOracleParent
+
+#include "mozilla/MozPromise.h"
+#include "mozilla/ProcInfo.h"
+#include "mozilla/dom/PJSOracleParent.h"
+
+namespace mozilla::ipc {
+class UtilityProcessParent;
+}
+
+namespace mozilla::dom {
+
+class JSOracleParent final : public PJSOracleParent {
+ public:
+ JSOracleParent() = default;
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(JSOracleParent, override);
+
+ static void WithJSOracle(
+ const std::function<void(JSOracleParent* aParent)>& aCallback);
+
+ UtilityActorName GetActorName() { return UtilityActorName::JSOracle; }
+
+ void ActorDestroy(ActorDestroyReason aReason) override;
+
+ nsresult BindToUtilityProcess(
+ const RefPtr<mozilla::ipc::UtilityProcessParent>& aUtilityParent);
+
+ void Bind(Endpoint<PJSOracleParent>&& aEndpoint);
+
+ private:
+ ~JSOracleParent() = default;
+
+ static JSOracleParent* GetSingleton();
+
+ using JSOraclePromise = GenericNonExclusivePromise;
+ RefPtr<JSOraclePromise> StartJSOracle();
+};
+} // namespace mozilla::dom
+
+#endif // defined(mozilla_dom_JSOracleParent)
diff --git a/dom/ipc/JSValidatorChild.cpp b/dom/ipc/JSValidatorChild.cpp
new file mode 100644
index 0000000000..5070a46492
--- /dev/null
+++ b/dom/ipc/JSValidatorChild.cpp
@@ -0,0 +1,242 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/JSValidatorChild.h"
+#include "mozilla/dom/JSOracleChild.h"
+
+#include "mozilla/Encoding.h"
+#include "mozilla/dom/ScriptDecoding.h"
+#include "mozilla/ipc/Endpoint.h"
+
+#include "js/CompileOptions.h"
+#include "js/JSON.h"
+#include "js/SourceText.h"
+#include "js/experimental/CompileScript.h"
+#include "js/experimental/JSStencil.h"
+#include "xpcpublic.h"
+
+using namespace mozilla::dom;
+using Encoding = mozilla::Encoding;
+
+mozilla::UniquePtr<mozilla::Decoder> TryGetDecoder(
+ const mozilla::Span<const uint8_t>& aSourceBytes,
+ const nsACString& aContentCharset, const nsAString& aHintCharset,
+ const nsAString& aDocumentCharset) {
+ const Encoding* encoding;
+ mozilla::UniquePtr<mozilla::Decoder> unicodeDecoder;
+
+ std::tie(encoding, std::ignore) = Encoding::ForBOM(aSourceBytes);
+ if (encoding) {
+ unicodeDecoder = encoding->NewDecoderWithBOMRemoval();
+ }
+
+ if (!unicodeDecoder) {
+ encoding = Encoding::ForLabel(aContentCharset);
+ if (encoding) {
+ unicodeDecoder = encoding->NewDecoderWithoutBOMHandling();
+ }
+
+ if (!unicodeDecoder) {
+ encoding = Encoding::ForLabel(aHintCharset);
+ if (encoding) {
+ unicodeDecoder = encoding->NewDecoderWithoutBOMHandling();
+ }
+ }
+
+ if (!unicodeDecoder) {
+ encoding = Encoding::ForLabel(aDocumentCharset);
+ if (encoding) {
+ unicodeDecoder = encoding->NewDecoderWithoutBOMHandling();
+ }
+ }
+ }
+
+ if (!unicodeDecoder && !IsUtf8(mozilla::Span(reinterpret_cast<const char*>(
+ aSourceBytes.Elements()),
+ aSourceBytes.Length()))) {
+ // Curiously, there are various callers that don't pass aDocument. The
+ // fallback in the old code was ISO-8859-1, which behaved like
+ // windows-1252.
+ unicodeDecoder = WINDOWS_1252_ENCODING->NewDecoderWithoutBOMHandling();
+ }
+
+ return unicodeDecoder;
+}
+
+mozilla::ipc::IPCResult JSValidatorChild::RecvIsOpaqueResponseAllowed(
+ IsOpaqueResponseAllowedResolver&& aResolver) {
+ mResolver.emplace(aResolver);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult JSValidatorChild::RecvOnDataAvailable(Shmem&& aData) {
+ if (!mResolver) {
+ MOZ_ASSERT(!CanSend());
+ return IPC_OK();
+ }
+
+ if (!mSourceBytes.Append(Span(aData.get<char>(), aData.Size<char>()),
+ mozilla::fallible)) {
+ // To prevent an attacker from flood the validation process,
+ // we don't validate here.
+ Resolve(ValidatorResult::Failure);
+ }
+ DeallocShmem(aData);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult JSValidatorChild::RecvOnStopRequest(
+ const nsresult& aReason, const nsACString& aContentCharset,
+ const nsAString& aHintCharset, const nsAString& aDocumentCharset) {
+ if (!mResolver) {
+ return IPC_OK();
+ }
+
+ if (NS_FAILED(aReason)) {
+ Resolve(ValidatorResult::Failure);
+ } else if (mSourceBytes.IsEmpty()) {
+ // The empty document parses as JavaScript.
+ Resolve(ValidatorResult::JavaScript);
+ } else {
+ UniquePtr<Decoder> unicodeDecoder = TryGetDecoder(
+ mSourceBytes, aContentCharset, aHintCharset, aDocumentCharset);
+
+ if (!unicodeDecoder) {
+ Resolve(ShouldAllowJS(mSourceBytes));
+ } else {
+ BufferUniquePtr<Utf8Unit[]> buffer;
+ auto result = GetUTF8EncodedContent(mSourceBytes, buffer, unicodeDecoder);
+ if (result.isErr()) {
+ Resolve(ValidatorResult::Failure);
+ } else {
+ Resolve(ShouldAllowJS(result.unwrap()));
+ }
+ }
+ }
+
+ return IPC_OK();
+}
+
+void JSValidatorChild::ActorDestroy(ActorDestroyReason aReason) {
+ if (mResolver) {
+ Resolve(ValidatorResult::Failure);
+ }
+};
+
+void JSValidatorChild::Resolve(ValidatorResult aResult) {
+ MOZ_ASSERT(mResolver);
+ Maybe<Shmem> data = Nothing();
+ if (aResult == ValidatorResult::JavaScript && !mSourceBytes.IsEmpty()) {
+ Shmem sharedData;
+ nsresult rv =
+ JSValidatorUtils::CopyCStringToShmem(this, mSourceBytes, sharedData);
+ if (NS_SUCCEEDED(rv)) {
+ data = Some(std::move(sharedData));
+ }
+ }
+
+ mResolver.ref()(std::tuple<mozilla::Maybe<Shmem>&&, const ValidatorResult&>(
+ std::move(data), aResult));
+ mResolver.reset();
+}
+
+mozilla::Result<mozilla::Span<const char>, nsresult>
+JSValidatorChild::GetUTF8EncodedContent(
+ const mozilla::Span<const uint8_t>& aData,
+ BufferUniquePtr<Utf8Unit[]>& aBuffer, UniquePtr<Decoder>& aDecoder) {
+ MOZ_ASSERT(aDecoder);
+ // We need the output buffer to be UTF8
+ CheckedInt<size_t> bufferLength =
+ ScriptDecoding<Utf8Unit>::MaxBufferLength(aDecoder, aData.Length());
+ if (!bufferLength.isValid()) {
+ return mozilla::Err(NS_ERROR_FAILURE);
+ }
+
+ CheckedInt<size_t> bufferByteSize = bufferLength * sizeof(Utf8Unit);
+ if (!bufferByteSize.isValid()) {
+ return mozilla::Err(NS_ERROR_FAILURE);
+ }
+
+ aBuffer.reset(static_cast<Utf8Unit*>(js_malloc(bufferByteSize.value())));
+ if (!aBuffer) {
+ return mozilla::Err(NS_ERROR_FAILURE);
+ }
+
+ size_t written = ScriptDecoding<Utf8Unit>::DecodeInto(
+ aDecoder, aData, Span(aBuffer.get(), bufferLength.value()),
+ /* aEndOfSource = */ true);
+ MOZ_ASSERT(written <= bufferLength.value());
+ MOZ_ASSERT(
+ IsUtf8(Span(reinterpret_cast<const char*>(aBuffer.get()), written)));
+
+ return Span(reinterpret_cast<const char*>(aBuffer.get()), written);
+}
+
+JSValidatorChild::ValidatorResult JSValidatorChild::ShouldAllowJS(
+ const mozilla::Span<const char>& aSpan) const {
+ MOZ_ASSERT(!aSpan.IsEmpty());
+
+ MOZ_DIAGNOSTIC_ASSERT(IsUtf8(aSpan));
+
+ JS::FrontendContext* fc = JSOracleChild::JSFrontendContext();
+ if (!fc) {
+ return ValidatorResult::Failure;
+ }
+
+ JS::SourceText<Utf8Unit> srcBuf;
+ if (!srcBuf.init(fc, aSpan.Elements(), aSpan.Length(),
+ JS::SourceOwnership::Borrowed)) {
+ JS::ClearFrontendErrors(fc);
+ return ValidatorResult::Failure;
+ }
+
+ // Parse to JavaScript
+ JS::PrefableCompileOptions prefableOptions;
+ xpc::SetPrefableCompileOptions(prefableOptions);
+ // For the syntax validation purpose, asm.js doesn't need to be enabled.
+ prefableOptions.setAsmJSOption(JS::AsmJSOption::DisabledByAsmJSPref);
+
+ JS::CompileOptions options(prefableOptions);
+ JS::CompilationStorage storage;
+ RefPtr<JS::Stencil> stencil =
+ JS::CompileGlobalScriptToStencil(fc, options, srcBuf, storage);
+
+ if (!stencil) {
+ JS::ClearFrontendErrors(fc);
+ return ValidatorResult::Other;
+ }
+
+ MOZ_ASSERT(!aSpan.IsEmpty());
+
+ // Parse to JSON
+ if (IsAscii(aSpan)) {
+ // Ascii is a subset of Latin1, and JS_ParseJSON can take Latin1 directly
+ if (JS::IsValidJSON(
+ reinterpret_cast<const JS::Latin1Char*>(aSpan.Elements()),
+ aSpan.Length())) {
+ return ValidatorResult::JSON;
+ }
+ } else {
+ nsString decoded;
+ nsresult rv = UTF_8_ENCODING->DecodeWithBOMRemoval(
+ Span(reinterpret_cast<const uint8_t*>(aSpan.Elements()),
+ aSpan.Length()),
+ decoded);
+ if (NS_FAILED(rv)) {
+ return ValidatorResult::Failure;
+ }
+
+ if (JS::IsValidJSON(decoded.BeginReading(), decoded.Length())) {
+ return ValidatorResult::JSON;
+ }
+ }
+
+ // Since the JSON parsing failed, we confirmed the file is Javascript and not
+ // JSON.
+ return ValidatorResult::JavaScript;
+}
diff --git a/dom/ipc/JSValidatorChild.h b/dom/ipc/JSValidatorChild.h
new file mode 100644
index 0000000000..b8899afd5b
--- /dev/null
+++ b/dom/ipc/JSValidatorChild.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_JSValidatorChild
+#define mozilla_dom_JSValidatorChild
+
+#include "mozilla/Result.h"
+#include "mozilla/ProcInfo.h"
+#include "mozilla/dom/PJSValidatorChild.h"
+#include "mozilla/dom/JSValidatorUtils.h"
+#include "mozilla/net/OpaqueResponseUtils.h"
+
+template <typename T>
+using BufferUniquePtr = mozilla::UniquePtr<T, JS::FreePolicy>;
+
+namespace mozilla {
+class Decoder;
+} // namespace mozilla
+
+namespace mozilla::dom {
+class JSValidatorChild final : public PJSValidatorChild {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(JSValidatorChild, override);
+
+ mozilla::ipc::IPCResult RecvIsOpaqueResponseAllowed(
+ IsOpaqueResponseAllowedResolver&& aResolver);
+
+ mozilla::ipc::IPCResult RecvOnDataAvailable(Shmem&& aData);
+
+ mozilla::ipc::IPCResult RecvOnStopRequest(const nsresult& aReason,
+ const nsACString& aContentCharset,
+ const nsAString& aHintCharset,
+ const nsAString& aDocumentCharset);
+
+ void ActorDestroy(ActorDestroyReason aReason) override;
+
+ private:
+ virtual ~JSValidatorChild() = default;
+
+ using ValidatorResult = net::OpaqueResponseBlocker::ValidatorResult;
+ void Resolve(ValidatorResult aResult);
+ ValidatorResult ShouldAllowJS(const mozilla::Span<const char>& aSpan) const;
+
+ mozilla::Result<mozilla::Span<const char>, nsresult> GetUTF8EncodedContent(
+ const mozilla::Span<const uint8_t>& aData,
+ BufferUniquePtr<Utf8Unit[]>& aBuffer,
+ UniquePtr<mozilla::Decoder>& aDecoder);
+
+ nsCString mSourceBytes;
+ Maybe<IsOpaqueResponseAllowedResolver> mResolver;
+};
+} // namespace mozilla::dom
+
+#endif // defined(mozilla_dom_JSValidatorChild)
diff --git a/dom/ipc/JSValidatorParent.cpp b/dom/ipc/JSValidatorParent.cpp
new file mode 100644
index 0000000000..45b6d9de4c
--- /dev/null
+++ b/dom/ipc/JSValidatorParent.cpp
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/net/OpaqueResponseUtils.h"
+#include "mozilla/dom/JSValidatorParent.h"
+#include "mozilla/dom/JSValidatorUtils.h"
+#include "mozilla/dom/JSOracleParent.h"
+#include "mozilla/RefPtr.h"
+#include "nsCOMPtr.h"
+#include "HttpBaseChannel.h"
+
+namespace mozilla::dom {
+/* static */
+already_AddRefed<JSValidatorParent> JSValidatorParent::Create() {
+ RefPtr<JSValidatorParent> validator = new JSValidatorParent();
+ JSOracleParent::WithJSOracle([validator](JSOracleParent* aParent) {
+ MOZ_ASSERT_IF(aParent, aParent->CanSend());
+ if (aParent) {
+ MOZ_ALWAYS_TRUE(aParent->SendPJSValidatorConstructor(validator));
+ }
+ });
+ return validator.forget();
+}
+
+void JSValidatorParent::IsOpaqueResponseAllowed(
+ const std::function<void(Maybe<Shmem>, ValidatorResult)>& aCallback) {
+ JSOracleParent::WithJSOracle([=, self = RefPtr{this}](const auto* aParent) {
+ if (aParent) {
+ MOZ_DIAGNOSTIC_ASSERT(self->CanSend());
+ self->SendIsOpaqueResponseAllowed()->Then(
+ GetMainThreadSerialEventTarget(), __func__,
+ [aCallback](
+ const IsOpaqueResponseAllowedPromise::ResolveOrRejectValue&
+ aResult) {
+ if (aResult.IsResolve()) {
+ auto [data, result] = aResult.ResolveValue();
+ aCallback(std::move(data), result);
+ } else {
+ // For cases like the Utility Process crashes, the promise will be
+ // rejected due to sending failures, and we'll block the request
+ // since we can't validate it.
+ aCallback(Nothing(), ValidatorResult::Failure);
+ }
+ });
+ } else {
+ aCallback(Nothing(), ValidatorResult::Failure);
+ }
+ });
+}
+
+void JSValidatorParent::OnDataAvailable(const nsACString& aData) {
+ JSOracleParent::WithJSOracle(
+ [self = RefPtr{this}, data = nsCString{aData}](const auto* aParent) {
+ if (!aParent) {
+ return;
+ }
+
+ if (self->CanSend()) {
+ Shmem sharedData;
+ nsresult rv =
+ JSValidatorUtils::CopyCStringToShmem(self, data, sharedData);
+ if (NS_FAILED(rv)) {
+ return;
+ }
+ Unused << self->SendOnDataAvailable(std::move(sharedData));
+ }
+ });
+}
+
+void JSValidatorParent::OnStopRequest(nsresult aResult, nsIRequest& aRequest) {
+ JSOracleParent::WithJSOracle(
+ [self = RefPtr{this}, aResult,
+ request = nsCOMPtr{&aRequest}](const auto* aParent) {
+ if (!aParent) {
+ return;
+ }
+ if (self->CanSend() && request) {
+ nsCOMPtr<net::HttpBaseChannel> httpBaseChannel =
+ do_QueryInterface(request);
+ MOZ_ASSERT(httpBaseChannel);
+
+ nsAutoCString contentCharset;
+ Unused << httpBaseChannel->GetContentCharset(contentCharset);
+
+ nsAutoString hintCharset;
+ Unused << httpBaseChannel->GetClassicScriptHintCharset(hintCharset);
+
+ nsAutoString documentCharset;
+ Unused << httpBaseChannel->GetDocumentCharacterSet(documentCharset);
+
+ Unused << self->SendOnStopRequest(aResult, contentCharset,
+ hintCharset, documentCharset);
+ }
+ });
+}
+} // namespace mozilla::dom
diff --git a/dom/ipc/JSValidatorParent.h b/dom/ipc/JSValidatorParent.h
new file mode 100644
index 0000000000..66528a6470
--- /dev/null
+++ b/dom/ipc/JSValidatorParent.h
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_JSValidatorParent
+#define mozilla_dom_JSValidatorParent
+
+#include "mozilla/MozPromise.h"
+#include "mozilla/ProcInfo.h"
+#include "mozilla/dom/PJSValidatorParent.h"
+
+namespace mozilla::ipc {
+class UtilityProcessParent;
+class IProtocol;
+} // namespace mozilla::ipc
+
+namespace mozilla::dom {
+
+class JSValidatorParent final : public PJSValidatorParent {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(JSValidatorParent, override);
+
+ static already_AddRefed<JSValidatorParent> Create();
+
+ void IsOpaqueResponseAllowed(
+ const std::function<void(Maybe<mozilla::ipc::Shmem>, ValidatorResult)>&
+ aCallback);
+
+ void OnDataAvailable(const nsACString& aData);
+
+ void OnStopRequest(nsresult aResult, nsIRequest& aRequest);
+
+ private:
+ virtual ~JSValidatorParent() = default;
+};
+} // namespace mozilla::dom
+
+#endif // defined(mozilla_dom_JSValidatorParent)
diff --git a/dom/ipc/JSValidatorUtils.cpp b/dom/ipc/JSValidatorUtils.cpp
new file mode 100644
index 0000000000..cde22a453f
--- /dev/null
+++ b/dom/ipc/JSValidatorUtils.cpp
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/JSValidatorUtils.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/ipc/Shmem.h"
+#include "nsStringFwd.h"
+
+using namespace mozilla::ipc;
+
+namespace mozilla::dom {
+/* static */
+nsresult JSValidatorUtils::CopyCStringToShmem(IProtocol* aActor,
+ const nsCString& aCString,
+ Shmem& aMem) {
+ if (!aActor->AllocShmem(aCString.Length(), &aMem)) {
+ return NS_ERROR_FAILURE;
+ }
+ memcpy(aMem.get<char>(), aCString.BeginReading(), aCString.Length());
+ return NS_OK;
+}
+} // namespace mozilla::dom
diff --git a/dom/ipc/JSValidatorUtils.h b/dom/ipc/JSValidatorUtils.h
new file mode 100644
index 0000000000..a0541b3992
--- /dev/null
+++ b/dom/ipc/JSValidatorUtils.h
@@ -0,0 +1,26 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_JSValidatorUtils
+#define mozilla_dom_JSValidatorUtils
+#include "ErrorList.h"
+#include "nsStringFwd.h"
+
+namespace mozilla::ipc {
+class IProtocol;
+class Shmem;
+} // namespace mozilla::ipc
+
+using namespace mozilla::ipc;
+
+namespace mozilla::dom {
+class JSValidatorUtils final {
+ public:
+ static nsresult CopyCStringToShmem(IProtocol* aActor,
+ const nsCString& aCString, Shmem& aMem);
+};
+}; // namespace mozilla::dom
+#endif
diff --git a/dom/ipc/LoginDetectionService.cpp b/dom/ipc/LoginDetectionService.cpp
new file mode 100644
index 0000000000..ddbe1096fb
--- /dev/null
+++ b/dom/ipc/LoginDetectionService.cpp
@@ -0,0 +1,158 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "LoginDetectionService.h"
+
+#include "nsILoginInfo.h"
+#include "nsILoginManager.h"
+#include "nsIObserver.h"
+#include "nsIXULRuntime.h"
+#include "nsServiceManagerUtils.h"
+#include "nsXULAppAPI.h"
+
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/StaticPrefs_fission.h"
+#include "mozilla/dom/ProcessIsolation.h"
+
+namespace mozilla::dom {
+
+static StaticRefPtr<LoginDetectionService> gLoginDetectionService;
+
+namespace {
+
+void OnFissionPrefsChange(const char* aPrefName, void* aData) {
+ MOZ_ASSERT(gLoginDetectionService);
+
+ gLoginDetectionService->MaybeStartMonitoring();
+}
+
+} // namespace
+
+NS_IMPL_ISUPPORTS(LoginDetectionService, nsILoginDetectionService,
+ nsILoginSearchCallback, nsIObserver, nsISupportsWeakReference)
+
+// static
+already_AddRefed<LoginDetectionService> LoginDetectionService::GetSingleton() {
+ if (gLoginDetectionService) {
+ return do_AddRef(gLoginDetectionService);
+ }
+
+ gLoginDetectionService = new LoginDetectionService();
+ ClearOnShutdown(&gLoginDetectionService);
+
+ return do_AddRef(gLoginDetectionService);
+}
+
+LoginDetectionService::LoginDetectionService() : mIsLoginsLoaded(false) {}
+LoginDetectionService::~LoginDetectionService() { UnregisterObserver(); }
+
+void LoginDetectionService::MaybeStartMonitoring() {
+ if (IsIsolateHighValueSiteEnabled()) {
+ // We want to isolate sites with a saved password, so fetch saved logins
+ // from the password manager, and then add the 'HighValue' permission.
+
+ // Note that we don't monitor whether a login is added or removed after
+ // logins are fetched. For adding logins, this will be covered by form
+ // submission detection heuristic. As for removing logins, it doesn't
+ // provide security benefit just to NOT isolate the removed site. The site
+ // will not be isolated when its permission expired.
+ FetchLogins();
+ }
+
+ if (IsIsolateHighValueSiteEnabled() ||
+ StaticPrefs::fission_highValue_login_monitor()) {
+ // When the pref is on, we monitor users' login attempt event when we
+ // are not isolating high value sites. This is because We can't detect the
+ // case where a user is already logged in to a site, and don't save a
+ // password for it. So we want to start monitoring login attempts prior
+ // to releasing the feature.
+ if (!mObs) {
+ mObs = mozilla::services::GetObserverService();
+ mObs->AddObserver(this, "passwordmgr-form-submission-detected", false);
+ }
+ } else {
+ UnregisterObserver();
+ }
+}
+
+void LoginDetectionService::FetchLogins() {
+ nsresult rv;
+ nsCOMPtr<nsILoginManager> loginManager =
+ do_GetService(NS_LOGINMANAGER_CONTRACTID, &rv);
+ if (NS_WARN_IF(!loginManager)) {
+ return;
+ }
+
+ Unused << loginManager->GetAllLoginsWithCallback(this);
+}
+
+void LoginDetectionService::UnregisterObserver() {
+ if (mObs) {
+ mObs->RemoveObserver(this, "passwordmgr-form-submission-detected");
+ mObs = nullptr;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// nsILoginDetectionService implementation
+NS_IMETHODIMP LoginDetectionService::Init() {
+ if (XRE_IsContentProcess()) {
+ return NS_OK;
+ }
+
+ Preferences::RegisterCallback(OnFissionPrefsChange, "fission.autostart");
+ Preferences::RegisterCallback(OnFissionPrefsChange,
+ "fission.webContentIsolationStrategy");
+
+ MaybeStartMonitoring();
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP LoginDetectionService::IsLoginsLoaded(bool* aResult) {
+ if (IsIsolateHighValueSiteEnabled()) {
+ *aResult = mIsLoginsLoaded;
+ } else {
+ // When the feature is disabled, just returns true so testcases don't
+ // block on waiting for us to load logins.
+ *aResult = true;
+ }
+ return NS_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// nsILoginSearchObserver implementation
+NS_IMETHODIMP
+LoginDetectionService::OnSearchComplete(
+ const nsTArray<RefPtr<nsILoginInfo>>& aLogins) {
+ // Add all origins with saved passwords to the permission manager.
+ for (const auto& login : aLogins) {
+ nsString origin;
+ login->GetOrigin(origin);
+
+ AddHighValuePermission(NS_ConvertUTF16toUTF8(origin),
+ mozilla::dom::kHighValueHasSavedLoginPermission);
+ }
+
+ mIsLoginsLoaded = true;
+ return NS_OK;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// nsIObserver implementation
+NS_IMETHODIMP
+LoginDetectionService::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData) {
+ if ("passwordmgr-form-submission-detected"_ns.Equals(aTopic)) {
+ nsDependentString origin(aData);
+ AddHighValuePermission(NS_ConvertUTF16toUTF8(origin),
+ mozilla::dom::kHighValueIsLoggedInPermission);
+ }
+
+ return NS_OK;
+}
+
+} // namespace mozilla::dom
diff --git a/dom/ipc/LoginDetectionService.h b/dom/ipc/LoginDetectionService.h
new file mode 100644
index 0000000000..b0fd170855
--- /dev/null
+++ b/dom/ipc/LoginDetectionService.h
@@ -0,0 +1,60 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_LoginDetectionService_h
+#define mozilla_dom_LoginDetectionService_h
+
+#include "nsIObserver.h"
+#include "nsIObserverService.h"
+#include "nsILoginDetectionService.h"
+#include "nsILoginManager.h"
+#include "nsWeakReference.h"
+#include "mozilla/Logging.h"
+#include "mozilla/Preferences.h"
+
+namespace mozilla::dom {
+
+/**
+ * Detect whether the user is 'possibly' logged in to a site, and add the
+ * HighValue permission to the permission manager. We say 'possibly' because
+ * the detection is done in a very loose way. For example, for sites that have
+ * an associated login stored in the password manager are considered `logged in`
+ * by the service, which is not always true in terms of whether the users is
+ * really logged in to the site.
+ */
+class LoginDetectionService final : public nsILoginDetectionService,
+ public nsILoginSearchCallback,
+ public nsIObserver,
+ public nsSupportsWeakReference {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSILOGINDETECTIONSERVICE
+ NS_DECL_NSILOGINSEARCHCALLBACK
+ NS_DECL_NSIOBSERVER
+
+ static already_AddRefed<LoginDetectionService> GetSingleton();
+
+ void MaybeStartMonitoring();
+
+ private:
+ LoginDetectionService();
+ virtual ~LoginDetectionService();
+
+ // Fetch saved logins from the password manager.
+ void FetchLogins();
+
+ void RegisterObserver();
+ void UnregisterObserver();
+
+ nsCOMPtr<nsIObserverService> mObs;
+
+ // Used by testcase to make sure logins are fetched.
+ bool mIsLoginsLoaded;
+};
+
+} // namespace mozilla::dom
+
+#endif
diff --git a/dom/ipc/MMPrinter.cpp b/dom/ipc/MMPrinter.cpp
new file mode 100644
index 0000000000..be2911341a
--- /dev/null
+++ b/dom/ipc/MMPrinter.cpp
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "MMPrinter.h"
+
+#include "jsapi.h"
+#include "nsJSUtils.h"
+#include "Logging.h"
+#include "mozilla/Bootstrap.h"
+#include "mozilla/dom/ipc/StructuredCloneData.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/ErrorResult.h"
+#include "mozilla/RandomNum.h"
+#include "nsFrameMessageManager.h"
+#include "prenv.h"
+
+namespace mozilla::dom {
+
+LazyLogModule MMPrinter::sMMLog("MessageManager");
+
+// You can use
+// https://gist.github.com/tomrittervg/adb8688426a9a5340da96004e2c8af79 to parse
+// the output of the logs into logs more friendly to reading.
+
+/* static */
+void MMPrinter::PrintImpl(char const* aLocation, const nsAString& aMsg,
+ ClonedMessageData const& aData) {
+ NS_ConvertUTF16toUTF8 charMsg(aMsg);
+
+ /*
+ * The topic will be skipped if the topic name appears anywhere as a substring
+ * of the filter.
+ *
+ * Example:
+ * MOZ_LOG_MESSAGEMANAGER_SKIP="foobar|extension"
+ * Will match the topics 'foobar', 'foo', 'bar', and 'ten' (even though
+ * you may not have intended to match the latter three) and it will not match
+ * the topics 'extensionresult' or 'Foo'.
+ */
+ char* mmSkipLog = PR_GetEnv("MOZ_LOG_MESSAGEMANAGER_SKIP");
+
+ if (mmSkipLog && strstr(mmSkipLog, charMsg.get())) {
+ return;
+ }
+
+ uint64_t msg_id = RandomUint64OrDie();
+
+ MOZ_LOG(MMPrinter::sMMLog, LogLevel::Debug,
+ ("%" PRIu64 " %s Message: %s in process type: %s", msg_id, aLocation,
+ charMsg.get(), XRE_GetProcessTypeString()));
+
+ if (!MOZ_LOG_TEST(sMMLog, LogLevel::Verbose)) {
+ return;
+ }
+
+ ErrorResult rv;
+
+ AutoJSAPI jsapi;
+ // We're using this context to deserialize, stringify, and print a message
+ // manager message here. Since the messages are always sent from and to system
+ // scopes, we need to do this in a system scope, or attempting to deserialize
+ // certain privileged objects will fail.
+ MOZ_ALWAYS_TRUE(jsapi.Init(xpc::PrivilegedJunkScope()));
+ JSContext* cx = jsapi.cx();
+
+ ipc::StructuredCloneData data;
+ ipc::UnpackClonedMessageData(aData, data);
+
+ /* Read original StructuredCloneData. */
+ JS::Rooted<JS::Value> scdContent(cx);
+ data.Read(cx, &scdContent, rv);
+ if (rv.Failed()) {
+ // In testing, the only reason this would fail was if there was no data in
+ // the message; so it seems like this is safe-ish.
+ MOZ_LOG(MMPrinter::sMMLog, LogLevel::Verbose,
+ ("%" PRIu64 " (No Data)", msg_id));
+ rv.SuppressException();
+ return;
+ }
+
+ JS::Rooted<JSString*> unevalObj(cx, JS_ValueToSource(cx, scdContent));
+ nsAutoJSString srcString;
+ if (!srcString.init(cx, unevalObj)) return;
+
+ MOZ_LOG(MMPrinter::sMMLog, LogLevel::Verbose,
+ ("%" PRIu64 " %s", msg_id, NS_ConvertUTF16toUTF8(srcString).get()));
+}
+
+} // namespace mozilla::dom
diff --git a/dom/ipc/MMPrinter.h b/dom/ipc/MMPrinter.h
new file mode 100644
index 0000000000..7d89f9ee8f
--- /dev/null
+++ b/dom/ipc/MMPrinter.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef MMPrinter_h
+#define MMPrinter_h
+
+#include "mozilla/dom/DOMTypes.h"
+#include "nsString.h"
+
+namespace mozilla::dom {
+
+class MMPrinter {
+ public:
+ static void Print(char const* aLocation, const nsAString& aMsg,
+ ClonedMessageData const& aData) {
+ if (MOZ_UNLIKELY(MOZ_LOG_TEST(MMPrinter::sMMLog, LogLevel::Debug))) {
+ MMPrinter::PrintImpl(aLocation, aMsg, aData);
+ }
+ }
+
+ private:
+ static LazyLogModule sMMLog;
+ static void PrintImpl(char const* aLocation, const nsAString& aMsg,
+ ClonedMessageData const& aData);
+};
+
+} // namespace mozilla::dom
+
+#endif /* MMPrinter_h */
diff --git a/dom/ipc/ManifestMessagesChild.sys.mjs b/dom/ipc/ManifestMessagesChild.sys.mjs
new file mode 100644
index 0000000000..5a42fd3206
--- /dev/null
+++ b/dom/ipc/ManifestMessagesChild.sys.mjs
@@ -0,0 +1,112 @@
+/* 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/.*/
+/*
+ * Manifest obtainer frame script implementation of:
+ * http://www.w3.org/TR/appmanifest/#obtaining
+ *
+ * It searches a top-level browsing context for
+ * a <link rel=manifest> element. Then fetches
+ * and processes the linked manifest.
+ *
+ * BUG: https://bugzilla.mozilla.org/show_bug.cgi?id=1083410
+ */
+
+const lazy = {};
+
+ChromeUtils.defineESModuleGetters(lazy, {
+ ManifestFinder: "resource://gre/modules/ManifestFinder.sys.mjs",
+ ManifestIcons: "resource://gre/modules/ManifestIcons.sys.mjs",
+ ManifestObtainer: "resource://gre/modules/ManifestObtainer.sys.mjs",
+});
+
+export class ManifestMessagesChild extends JSWindowActorChild {
+ receiveMessage(message) {
+ switch (message.name) {
+ case "DOM:WebManifest:hasManifestLink":
+ return this.hasManifestLink();
+ case "DOM:ManifestObtainer:Obtain":
+ return this.obtainManifest(message.data);
+ case "DOM:WebManifest:fetchIcon":
+ return this.fetchIcon(message);
+ }
+ return undefined;
+ }
+
+ /**
+ * Check if the document includes a link to a web manifest.
+ */
+ hasManifestLink() {
+ const response = makeMsgResponse();
+ response.result = lazy.ManifestFinder.contentHasManifestLink(
+ this.contentWindow
+ );
+ response.success = true;
+ return response;
+ }
+
+ /**
+ * Asynchronously obtains a web manifest from this window by using the
+ * ManifestObtainer and returns the result.
+ * @param {Object} checkConformance True if spec conformance messages should be collected.
+ */
+ async obtainManifest(options) {
+ const { checkConformance } = options;
+ const response = makeMsgResponse();
+ try {
+ response.result = await lazy.ManifestObtainer.contentObtainManifest(
+ this.contentWindow,
+ { checkConformance }
+ );
+ response.success = true;
+ } catch (err) {
+ response.result = serializeError(err);
+ }
+ return response;
+ }
+
+ /**
+ * Given a manifest and an expected icon size, ask ManifestIcons
+ * to fetch the appropriate icon and send along result
+ */
+ async fetchIcon({ data: { manifest, iconSize } }) {
+ const response = makeMsgResponse();
+ try {
+ response.result = await lazy.ManifestIcons.contentFetchIcon(
+ this.contentWindow,
+ manifest,
+ iconSize
+ );
+ response.success = true;
+ } catch (err) {
+ response.result = serializeError(err);
+ }
+ return response;
+ }
+}
+
+/**
+ * Utility function to Serializes an JS Error, so it can be transferred over
+ * the message channel.
+ * FIX ME: https://bugzilla.mozilla.org/show_bug.cgi?id=1172586
+ * @param {Error} aError The error to serialize.
+ * @return {Object} The serialized object.
+ */
+function serializeError(aError) {
+ const clone = {
+ fileName: aError.fileName,
+ lineNumber: aError.lineNumber,
+ columnNumber: aError.columnNumber,
+ stack: aError.stack,
+ message: aError.message,
+ name: aError.name,
+ };
+ return clone;
+}
+
+function makeMsgResponse() {
+ return {
+ success: false,
+ result: undefined,
+ };
+}
diff --git a/dom/ipc/MaybeDiscarded.h b/dom/ipc/MaybeDiscarded.h
new file mode 100644
index 0000000000..32c0dde3b9
--- /dev/null
+++ b/dom/ipc/MaybeDiscarded.h
@@ -0,0 +1,133 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_MaybeDiscarded_h
+#define mozilla_dom_MaybeDiscarded_h
+
+#include "mozilla/RefPtr.h"
+
+namespace mozilla::dom {
+
+// Wrapper type for a WindowContext or BrowsingContext instance which may be
+// discarded, and thus unavailable in the current process. This type is used to
+// pass WindowContext and BrowsingContext instances over IPC, as they may be
+// discarded in the receiving process.
+//
+// A MaybeDiscarded can generally be implicitly converted to from a
+// BrowsingContext* or WindowContext*, but requires an explicit check of
+// |IsDiscarded| and call to |get| to read from.
+template <typename T>
+class MaybeDiscarded {
+ public:
+ MaybeDiscarded() = default;
+ MaybeDiscarded(MaybeDiscarded<T>&&) = default;
+ MaybeDiscarded(const MaybeDiscarded<T>&) = default;
+
+ // Construct from raw pointers and |nullptr|.
+ MOZ_IMPLICIT MaybeDiscarded(T* aRawPtr)
+ : mId(aRawPtr ? aRawPtr->Id() : 0), mPtr(aRawPtr) {}
+ MOZ_IMPLICIT MaybeDiscarded(decltype(nullptr)) {}
+
+ // Construct from |RefPtr<I>|
+ template <typename I,
+ typename = std::enable_if_t<std::is_convertible_v<I*, T*>>>
+ MOZ_IMPLICIT MaybeDiscarded(RefPtr<I>&& aPtr)
+ : mId(aPtr ? aPtr->Id() : 0), mPtr(std::move(aPtr)) {}
+ template <typename I,
+ typename = std::enable_if_t<std::is_convertible_v<I*, T*>>>
+ MOZ_IMPLICIT MaybeDiscarded(const RefPtr<I>& aPtr)
+ : mId(aPtr ? aPtr->Id() : 0), mPtr(aPtr) {}
+
+ // Basic assignment operators.
+ MaybeDiscarded<T>& operator=(const MaybeDiscarded<T>&) = default;
+ MaybeDiscarded<T>& operator=(MaybeDiscarded<T>&&) = default;
+ MaybeDiscarded<T>& operator=(decltype(nullptr)) {
+ mId = 0;
+ mPtr = nullptr;
+ return *this;
+ }
+ MaybeDiscarded<T>& operator=(T* aRawPtr) {
+ mId = aRawPtr ? aRawPtr->Id() : 0;
+ mPtr = aRawPtr;
+ return *this;
+ }
+ template <typename I>
+ MaybeDiscarded<T>& operator=(const RefPtr<I>& aRhs) {
+ mId = aRhs ? aRhs->Id() : 0;
+ mPtr = aRhs;
+ return *this;
+ }
+ template <typename I>
+ MaybeDiscarded<T>& operator=(RefPtr<I>&& aRhs) {
+ mId = aRhs ? aRhs->Id() : 0;
+ mPtr = std::move(aRhs);
+ return *this;
+ }
+
+ // Validate that the value is neither discarded nor null.
+ bool IsNullOrDiscarded() const { return !mPtr || mPtr->IsDiscarded(); }
+ bool IsDiscarded() const { return IsNullOrDiscarded() && !IsNull(); }
+ bool IsNull() const { return mId == 0; }
+
+ explicit operator bool() const { return !IsNullOrDiscarded(); }
+
+ // Extract the wrapped |T|. Must not be called on a discarded |T|.
+ T* get() const {
+ MOZ_DIAGNOSTIC_ASSERT(!IsDiscarded());
+ return mPtr.get();
+ }
+ already_AddRefed<T> forget() {
+ MOZ_DIAGNOSTIC_ASSERT(!IsDiscarded());
+ return mPtr.forget();
+ }
+
+ T* operator->() const {
+ MOZ_ASSERT(!IsNull());
+ return get();
+ }
+
+ // Like "get", but gets the "Canonical" version of the type. This method may
+ // only be called in the parent process.
+ auto get_canonical() const -> decltype(get()->Canonical()) {
+ if (get()) {
+ return get()->Canonical();
+ } else {
+ return nullptr;
+ }
+ }
+
+ // The ID for the context wrapped by this MaybeDiscarded. This ID comes from a
+ // remote process, and should generally only be used for logging. A
+ // BrowsingContext with this ID may not exist in the current process.
+ uint64_t ContextId() const { return mId; }
+
+ // Tries to get the wrapped value, disregarding discarded status.
+ // This may return |nullptr| for a non-null |MaybeDiscarded|, in the case that
+ // the target is no longer available in this process.
+ T* GetMaybeDiscarded() const { return mPtr.get(); }
+
+ // Clear the value to a discarded state with the given ID.
+ void SetDiscarded(uint64_t aId) {
+ mId = aId;
+ mPtr = nullptr;
+ }
+
+ // Comparison operators required by IPDL
+ bool operator==(const MaybeDiscarded<T>& aRhs) const {
+ return mId == aRhs.mId && mPtr == aRhs.mPtr;
+ }
+ bool operator!=(const MaybeDiscarded<T>& aRhs) const {
+ return !operator==(aRhs);
+ }
+
+ private:
+ uint64_t mId = 0;
+ RefPtr<T> mPtr;
+};
+
+} // namespace mozilla::dom
+
+#endif // mozilla_dom_MaybeDiscarded_h
diff --git a/dom/ipc/MemMapSnapshot.cpp b/dom/ipc/MemMapSnapshot.cpp
new file mode 100644
index 0000000000..53accfdb1b
--- /dev/null
+++ b/dom/ipc/MemMapSnapshot.cpp
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "MemMapSnapshot.h"
+
+#include "mozilla/AutoMemMap.h"
+#include "mozilla/ResultExtensions.h"
+#include "mozilla/Try.h"
+#include "mozilla/ipc/FileDescriptor.h"
+
+namespace mozilla::ipc {
+
+Result<Ok, nsresult> MemMapSnapshot::Init(size_t aSize) {
+ MOZ_ASSERT(!mInitialized);
+
+ if (NS_WARN_IF(!mMem.CreateFreezeable(aSize))) {
+ return Err(NS_ERROR_FAILURE);
+ }
+ if (NS_WARN_IF(!mMem.Map(aSize))) {
+ return Err(NS_ERROR_FAILURE);
+ }
+
+ mInitialized = true;
+ return Ok();
+}
+
+Result<Ok, nsresult> MemMapSnapshot::Finalize(loader::AutoMemMap& aMem) {
+ MOZ_ASSERT(mInitialized);
+
+ if (NS_WARN_IF(!mMem.Freeze())) {
+ return Err(NS_ERROR_FAILURE);
+ }
+ // TakeHandle resets mMem, so call max_size first.
+ size_t size = mMem.max_size();
+ FileDescriptor memHandle(mMem.TakeHandle());
+ MOZ_TRY(aMem.initWithHandle(memHandle, size));
+
+ mInitialized = false;
+ return Ok();
+}
+
+} // namespace mozilla::ipc
diff --git a/dom/ipc/MemMapSnapshot.h b/dom/ipc/MemMapSnapshot.h
new file mode 100644
index 0000000000..12b4d353ad
--- /dev/null
+++ b/dom/ipc/MemMapSnapshot.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef dom_ipc_MemMapSnapshot_h
+#define dom_ipc_MemMapSnapshot_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/RangedPtr.h"
+#include "mozilla/Result.h"
+#include "base/shared_memory.h"
+#include "ErrorList.h"
+
+namespace mozilla {
+namespace loader {
+class AutoMemMap;
+}
+
+namespace ipc {
+
+/**
+ * A helper class for creating a read-only snapshot of memory-mapped data.
+ *
+ * The Init() method initializes a read-write memory mapped region of the given
+ * size, which can be initialized with arbitrary data. The Finalize() method
+ * remaps that region as read-only (and backs it with a read-only file
+ * descriptor), and initializes an AutoMemMap with the new contents.
+ *
+ * The file descriptor for the resulting AutoMemMap can be shared among
+ * processes, to safely access a shared, read-only copy of the data snapshot.
+ */
+class MOZ_RAII MemMapSnapshot {
+ public:
+ Result<Ok, nsresult> Init(size_t aSize);
+ Result<Ok, nsresult> Finalize(loader::AutoMemMap& aMap);
+
+ template <typename T>
+ RangedPtr<T> Get() {
+ MOZ_ASSERT(mInitialized);
+ return {static_cast<T*>(mMem.memory()), mMem.max_size() / sizeof(T)};
+ }
+
+ private:
+ base::SharedMemory mMem;
+ bool mInitialized = false;
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // dom_ipc_MemMapSnapshot_h
diff --git a/dom/ipc/MemoryReportRequest.cpp b/dom/ipc/MemoryReportRequest.cpp
new file mode 100644
index 0000000000..794351461d
--- /dev/null
+++ b/dom/ipc/MemoryReportRequest.cpp
@@ -0,0 +1,170 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsMemoryReporterManager.h"
+#include "MemoryReportRequest.h"
+#include "mozilla/ipc/FileDescriptor.h"
+#include "mozilla/ipc/FileDescriptorUtils.h"
+
+using namespace mozilla::ipc;
+
+namespace mozilla::dom {
+
+MemoryReportRequestHost::MemoryReportRequestHost(uint32_t aGeneration)
+ : mGeneration(aGeneration), mSuccess(false) {
+ MOZ_COUNT_CTOR(MemoryReportRequestHost);
+ mReporterManager = nsMemoryReporterManager::GetOrCreate();
+ NS_WARNING_ASSERTION(mReporterManager, "GetOrCreate failed");
+}
+
+void MemoryReportRequestHost::RecvReport(const MemoryReport& aReport) {
+ // Skip reports from older generations. We need to do this here since we
+ // could receive older reports from a subprocesses before it acknowledges
+ // a new request, and we only track one active request per process.
+ if (aReport.generation() != mGeneration) {
+ return;
+ }
+
+ if (mReporterManager) {
+ mReporterManager->HandleChildReport(mGeneration, aReport);
+ }
+}
+
+void MemoryReportRequestHost::Finish(uint32_t aGeneration) {
+ // Skip reports from older generations. See the comment in RecvReport.
+ if (mGeneration != aGeneration) {
+ return;
+ }
+ mSuccess = true;
+}
+
+MemoryReportRequestHost::~MemoryReportRequestHost() {
+ MOZ_COUNT_DTOR(MemoryReportRequestHost);
+
+ if (mReporterManager) {
+ mReporterManager->EndProcessReport(mGeneration, mSuccess);
+ mReporterManager = nullptr;
+ }
+}
+
+NS_IMPL_ISUPPORTS(MemoryReportRequestClient, nsIRunnable)
+
+/* static */ void MemoryReportRequestClient::Start(
+ uint32_t aGeneration, bool aAnonymize, bool aMinimizeMemoryUsage,
+ const Maybe<FileDescriptor>& aDMDFile, const nsACString& aProcessString,
+ const ReportCallback& aReportCallback,
+ const FinishCallback& aFinishCallback) {
+ RefPtr<MemoryReportRequestClient> request = new MemoryReportRequestClient(
+ aGeneration, aAnonymize, aDMDFile, aProcessString, aReportCallback,
+ aFinishCallback);
+
+ DebugOnly<nsresult> rv;
+ if (aMinimizeMemoryUsage) {
+ nsCOMPtr<nsIMemoryReporterManager> mgr =
+ do_GetService("@mozilla.org/memory-reporter-manager;1");
+ rv = mgr->MinimizeMemoryUsage(request);
+ // mgr will eventually call actor->Run()
+ } else {
+ rv = request->Run();
+ }
+
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "actor operation failed");
+}
+
+MemoryReportRequestClient::MemoryReportRequestClient(
+ uint32_t aGeneration, bool aAnonymize,
+ const Maybe<FileDescriptor>& aDMDFile, const nsACString& aProcessString,
+ const ReportCallback& aReportCallback,
+ const FinishCallback& aFinishCallback)
+ : mGeneration(aGeneration),
+ mAnonymize(aAnonymize),
+ mProcessString(aProcessString),
+ mReportCallback(aReportCallback),
+ mFinishCallback(aFinishCallback) {
+ if (aDMDFile.isSome()) {
+ mDMDFile = aDMDFile.value();
+ }
+}
+
+MemoryReportRequestClient::~MemoryReportRequestClient() = default;
+
+class HandleReportCallback final : public nsIHandleReportCallback {
+ public:
+ using ReportCallback = typename MemoryReportRequestClient::ReportCallback;
+
+ NS_DECL_ISUPPORTS
+
+ explicit HandleReportCallback(uint32_t aGeneration,
+ const nsACString& aProcess,
+ const ReportCallback& aReportCallback)
+ : mGeneration(aGeneration),
+ mProcess(aProcess),
+ mReportCallback(aReportCallback) {}
+
+ NS_IMETHOD Callback(const nsACString& aProcess, const nsACString& aPath,
+ int32_t aKind, int32_t aUnits, int64_t aAmount,
+ const nsACString& aDescription,
+ nsISupports* aUnused) override {
+ MemoryReport memreport(mProcess, nsCString(aPath), aKind, aUnits, aAmount,
+ mGeneration, nsCString(aDescription));
+ mReportCallback(memreport);
+ return NS_OK;
+ }
+
+ private:
+ ~HandleReportCallback() = default;
+
+ uint32_t mGeneration;
+ const nsCString mProcess;
+ ReportCallback mReportCallback;
+};
+
+NS_IMPL_ISUPPORTS(HandleReportCallback, nsIHandleReportCallback)
+
+class FinishReportingCallback final : public nsIFinishReportingCallback {
+ public:
+ using FinishCallback = typename MemoryReportRequestClient::FinishCallback;
+
+ NS_DECL_ISUPPORTS
+
+ explicit FinishReportingCallback(uint32_t aGeneration,
+ const FinishCallback& aFinishCallback)
+ : mGeneration(aGeneration), mFinishCallback(aFinishCallback) {}
+
+ NS_IMETHOD Callback(nsISupports* aUnused) override {
+ mFinishCallback(mGeneration);
+ return NS_OK;
+ }
+
+ private:
+ ~FinishReportingCallback() = default;
+
+ uint32_t mGeneration;
+ FinishCallback mFinishCallback;
+};
+
+NS_IMPL_ISUPPORTS(FinishReportingCallback, nsIFinishReportingCallback)
+
+NS_IMETHODIMP MemoryReportRequestClient::Run() {
+ nsCOMPtr<nsIMemoryReporterManager> mgr =
+ do_GetService("@mozilla.org/memory-reporter-manager;1");
+
+ // Run the reporters. The callback will turn each measurement into a
+ // MemoryReport.
+ RefPtr<HandleReportCallback> handleReport =
+ new HandleReportCallback(mGeneration, mProcessString, mReportCallback);
+ RefPtr<FinishReportingCallback> finishReporting =
+ new FinishReportingCallback(mGeneration, mFinishCallback);
+
+ nsresult rv = mgr->GetReportsForThisProcessExtended(
+ handleReport, nullptr, mAnonymize, FileDescriptorToFILE(mDMDFile, "wb"),
+ finishReporting, nullptr);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+ "GetReportsForThisProcessExtended failed");
+ return rv;
+}
+
+} // namespace mozilla::dom
diff --git a/dom/ipc/MemoryReportRequest.h b/dom/ipc/MemoryReportRequest.h
new file mode 100644
index 0000000000..1e81a39bf6
--- /dev/null
+++ b/dom/ipc/MemoryReportRequest.h
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_MemoryReportRequest_h_
+#define mozilla_dom_MemoryReportRequest_h_
+
+#include "mozilla/dom/MemoryReportTypes.h"
+#include "mozilla/ipc/FileDescriptor.h"
+#include "nsISupports.h"
+
+#include <functional>
+
+class nsMemoryReporterManager;
+
+namespace mozilla::dom {
+
+class MemoryReport;
+
+class MemoryReportRequestHost final {
+ public:
+ explicit MemoryReportRequestHost(uint32_t aGeneration);
+ ~MemoryReportRequestHost();
+
+ void RecvReport(const MemoryReport& aReport);
+ void Finish(uint32_t aGeneration);
+
+ private:
+ const uint32_t mGeneration;
+ // Non-null if we haven't yet called EndProcessReport() on it.
+ RefPtr<nsMemoryReporterManager> mReporterManager;
+ bool mSuccess;
+};
+
+class MemoryReportRequestClient final : public nsIRunnable {
+ public:
+ using ReportCallback = std::function<void(const MemoryReport&)>;
+ using FinishCallback = std::function<void(const uint32_t&)>;
+
+ NS_DECL_ISUPPORTS
+
+ static void Start(uint32_t aGeneration, bool aAnonymize,
+ bool aMinimizeMemoryUsage,
+ const Maybe<mozilla::ipc::FileDescriptor>& aDMDFile,
+ const nsACString& aProcessString,
+ const ReportCallback& aReportCallback,
+ const FinishCallback& aFinishCallback);
+
+ NS_IMETHOD Run() override;
+
+ private:
+ MemoryReportRequestClient(uint32_t aGeneration, bool aAnonymize,
+ const Maybe<mozilla::ipc::FileDescriptor>& aDMDFile,
+ const nsACString& aProcessString,
+ const ReportCallback& aReportCallback,
+ const FinishCallback& aFinishCallback);
+
+ private:
+ ~MemoryReportRequestClient();
+
+ uint32_t mGeneration;
+ bool mAnonymize;
+ mozilla::ipc::FileDescriptor mDMDFile;
+ nsCString mProcessString;
+ ReportCallback mReportCallback;
+ FinishCallback mFinishCallback;
+};
+
+} // namespace mozilla::dom
+
+#endif // mozilla_dom_MemoryReportRequest_h_
diff --git a/dom/ipc/MemoryReportTypes.ipdlh b/dom/ipc/MemoryReportTypes.ipdlh
new file mode 100644
index 0000000000..fb52e575f2
--- /dev/null
+++ b/dom/ipc/MemoryReportTypes.ipdlh
@@ -0,0 +1,22 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+using struct mozilla::void_t from "mozilla/ipc/IPCCore.h";
+
+namespace mozilla {
+namespace dom {
+
+struct MemoryReport {
+ nsCString process;
+ nsCString path;
+ int32_t kind;
+ int32_t units;
+ int64_t amount;
+ uint32_t generation;
+ nsCString desc;
+};
+
+}
+}
diff --git a/dom/ipc/NativeThreadId.h b/dom/ipc/NativeThreadId.h
new file mode 100644
index 0000000000..9193aefde1
--- /dev/null
+++ b/dom/ipc/NativeThreadId.h
@@ -0,0 +1,16 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_NativeThreadId_h
+#define mozilla_dom_NativeThreadId_h
+
+#include "nsExceptionHandler.h"
+
+namespace mozilla::dom {
+typedef CrashReporter::ThreadId NativeThreadId;
+}
+
+#endif
diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl
new file mode 100644
index 0000000000..bc126724f4
--- /dev/null
+++ b/dom/ipc/PBrowser.ipdl
@@ -0,0 +1,1023 @@
+/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PColorPicker;
+include protocol PContent;
+#ifdef ACCESSIBILITY
+include protocol PDocAccessible;
+#endif
+include protocol PFilePicker;
+include protocol PRemotePrintJob;
+include protocol PPaymentRequest;
+include protocol PSessionStore;
+include protocol PWindowGlobal;
+include protocol PBrowserBridge;
+include protocol PVsync;
+
+include DOMTypes;
+include NeckoChannelParams;
+include WindowGlobalTypes;
+include IPCBlob;
+include IPCStream;
+include IPCTransferable;
+include URIParams;
+include PPrintingTypes;
+include PTabContext;
+include PBackgroundSharedTypes;
+
+include "mozilla/AntiTrackingIPCUtils.h";
+include "mozilla/dom/BindingIPCUtils.h";
+include "mozilla/dom/CSPMessageUtils.h";
+include "mozilla/dom/DocShellMessageUtils.h";
+include "mozilla/dom/FilePickerMessageUtils.h";
+include "mozilla/dom/PermissionMessageUtils.h";
+include "mozilla/dom/ReferrerInfoUtils.h";
+include "mozilla/dom/TabMessageUtils.h";
+include "mozilla/GfxMessageUtils.h";
+include "mozilla/LayoutMessageUtils.h";
+include "mozilla/layers/LayersMessageUtils.h";
+include "mozilla/ipc/TransportSecurityInfoUtils.h";
+include "mozilla/ipc/URIUtils.h";
+
+using struct nsID from "nsID.h";
+using mozilla::gfx::Matrix4x4 from "mozilla/gfx/Matrix.h";
+using mozilla::gfx::SurfaceFormat from "mozilla/gfx/Types.h";
+using mozilla::LayoutDeviceIntPoint from "Units.h";
+using mozilla::LayoutDevicePoint from "Units.h";
+using mozilla::ScreenIntCoord from "Units.h";
+using mozilla::ScreenIntMargin from "Units.h";
+using mozilla::ScreenIntPoint from "Units.h";
+using mozilla::ScreenRect from "Units.h";
+using struct mozilla::layers::ScrollableLayerGuid from "mozilla/layers/ScrollableLayerGuid.h";
+using struct mozilla::layers::ZoomConstraints from "mozilla/layers/ZoomConstraints.h";
+using struct mozilla::layers::DoubleTapToZoomMetrics from "mozilla/layers/DoubleTapToZoom.h";
+using mozilla::layers::LayersId from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::GeckoContentController_TapType from "mozilla/layers/GeckoContentControllerTypes.h";
+using mozilla::layers::ScrollableLayerGuid::ViewID from "mozilla/layers/ScrollableLayerGuid.h";
+using struct mozilla::void_t from "mozilla/ipc/IPCCore.h";
+using mozilla::WindowsHandle from "mozilla/ipc/IPCTypes.h";
+using class mozilla::WidgetCompositionEvent from "ipc/nsGUIEventIPC.h";
+using struct mozilla::widget::IMENotification from "mozilla/widget/IMEData.h";
+using struct mozilla::widget::IMENotificationRequests from "mozilla/widget/IMEData.h";
+using struct mozilla::widget::IMEState from "mozilla/widget/IMEData.h";
+using struct mozilla::widget::InputContext from "mozilla/widget/IMEData.h";
+using struct mozilla::widget::InputContextAction from "mozilla/widget/IMEData.h";
+using mozilla::gfx::IntSize from "mozilla/gfx/Point.h";
+using mozilla::gfx::IntPoint from "mozilla/gfx/Point.h";
+using class mozilla::ContentCache from "ipc/nsGUIEventIPC.h";
+using class mozilla::WidgetKeyboardEvent from "ipc/nsGUIEventIPC.h";
+using class mozilla::WidgetMouseEvent from "ipc/nsGUIEventIPC.h";
+using class mozilla::WidgetWheelEvent from "ipc/nsGUIEventIPC.h";
+using class mozilla::WidgetDragEvent from "ipc/nsGUIEventIPC.h";
+using struct nsRect from "nsRect.h";
+using class mozilla::WidgetSelectionEvent from "ipc/nsGUIEventIPC.h";
+using class mozilla::WidgetTouchEvent from "ipc/nsGUIEventIPC.h";
+using struct mozilla::dom::RemoteDOMEvent from "mozilla/dom/TabMessageTypes.h";
+using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
+using mozilla::layers::CompositorOptions from "mozilla/layers/CompositorOptions.h";
+using mozilla::CSSToScreenScale from "Units.h";
+using mozilla::CommandInt from "mozilla/EventForwards.h";
+using nsIWidget::TouchPointerState from "nsIWidget.h";
+using nsIWidget::TouchpadGesturePhase from "nsIWidget.h";
+using nsCursor from "nsIWidget.h";
+using struct LookAndFeelInt from "mozilla/widget/WidgetMessageUtils.h";
+using struct mozilla::DimensionRequest from "mozilla/widget/WidgetMessageUtils.h";
+using class mozilla::dom::MessagePort from "mozilla/dom/MessagePort.h";
+using class mozilla::dom::ipc::StructuredCloneData from "mozilla/dom/ipc/StructuredCloneData.h";
+using mozilla::dom::MaybeDiscardedWindowContext from "mozilla/dom/WindowContext.h";
+using mozilla::EventMessage from "mozilla/EventForwards.h";
+using nsEventStatus from "mozilla/EventForwards.h";
+using mozilla::Modifiers from "mozilla/EventForwards.h";
+using struct mozilla::widget::CandidateWindowPosition from "ipc/nsGUIEventIPC.h";
+using struct mozilla::FontRange from "ipc/nsGUIEventIPC.h";
+using mozilla::OriginAttributes from "mozilla/ipc/BackgroundUtils.h";
+using mozilla::dom::EffectsInfo from "mozilla/dom/EffectsInfo.h";
+using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
+using mozilla::ScrollAxis from "mozilla/PresShellForwards.h";
+using mozilla::ScrollFlags from "mozilla/PresShellForwards.h";
+using struct InputFormData from "mozilla/dom/SessionStoreMessageUtils.h";
+using struct CollectedInputDataValue from "mozilla/dom/SessionStoreMessageUtils.h";
+using mozilla::ContentBlockingNotifier::StorageAccessPermissionGrantedReason from "mozilla/ContentBlockingNotifier.h";
+using mozilla::ContentBlockingNotifier::CanvasFingerprinter from "mozilla/ContentBlockingNotifier.h";
+using mozilla::dom::CallerType from "mozilla/dom/BindingDeclarations.h";
+using mozilla::dom::EmbedderElementEventType from "mozilla/dom/TabMessageTypes.h";
+using mozilla::IntrinsicSize from "nsIFrame.h";
+using mozilla::AspectRatio from "mozilla/AspectRatio.h";
+using mozilla::NativeKeyBindingsType from "mozilla/NativeKeyBindingsType.h";
+using mozilla::StyleImageRendering from "mozilla/ServoStyleConsts.h";
+[MoveOnly] using class mozilla::ipc::BigBuffer from "mozilla/ipc/BigBuffer.h";
+using nsIFilePicker::Mode from "nsIFilePicker.h";
+
+namespace mozilla {
+namespace dom {
+
+struct WebProgressData
+{
+ MaybeDiscardedBrowsingContext browsingContext;
+ uint32_t loadType;
+};
+
+struct RequestData
+{
+ nullable nsIURI requestURI;
+ nullable nsIURI originalRequestURI;
+ nsCString matchedList;
+};
+
+struct WebProgressStateChangeData
+{
+ bool isNavigating;
+ bool mayEnableCharacterEncodingMenu;
+
+ // The following fields are only set when the aStateFlags param passed with
+ // this struct is |nsIWebProgress.STATE_STOP|.
+ nsString contentType;
+ nsString charset;
+ nullable nsIURI documentURI;
+};
+
+struct WebProgressLocationChangeData
+{
+ bool isNavigating;
+ bool isSyntheticDocument;
+ bool mayEnableCharacterEncodingMenu;
+ nsString contentType;
+ nsString title;
+ nsString charset;
+ nullable nsIURI documentURI;
+ nullable nsIPrincipal contentPrincipal;
+ nullable nsIPrincipal contentPartitionedPrincipal;
+ nullable nsIContentSecurityPolicy csp;
+ nullable nsIReferrerInfo referrerInfo;
+ uint64_t? requestContextID;
+};
+
+/**
+ * If creating the print preview document or updating it with new print
+ * settings fails, sheetCount will be zero.
+ */
+struct PrintPreviewResultInfo
+{
+ uint32_t sheetCount;
+ uint32_t totalPageCount;
+ bool isEmpty;
+ // Whether there's a selection in the previewed page, including its subframes.
+ bool hasSelection;
+ // Whether there's a selection in the previewed page, excluding its subframes.
+ bool hasSelfSelection;
+ // If present, indicates if the page should be printed landscape when true or
+ // portrait when false;
+ bool? printLandscape;
+ // The at-page specified page width or null when no width is provided.
+ float? pageWidth;
+ // The at-page specified page height or null when no height is provided.
+ float? pageHeight;
+};
+
+/**
+ * A PBrowser manages a maximal locally connected subtree of BrowsingContexts
+ * in a content process.
+ *
+ * See `dom/docs/Fission-IPC-Diagram.svg` for an overview of the DOM IPC
+ * actors.
+ */
+[NestedUpTo=inside_cpow] sync protocol PBrowser
+{
+ manager PContent;
+
+ manages PColorPicker;
+
+#ifdef ACCESSIBILITY
+ manages PDocAccessible;
+#endif
+
+ manages PFilePicker;
+ manages PPaymentRequest;
+ manages PSessionStore;
+ manages PWindowGlobal;
+ manages PBrowserBridge;
+ manages PVsync;
+
+both:
+ async AsyncMessage(nsString aMessage, ClonedMessageData aData);
+
+parent:
+#ifdef ACCESSIBILITY
+ /**
+ * Tell the parent process a new accessible document has been created.
+ * aParentDoc is the accessible document it was created in if any, and
+ * aParentAcc is the id of the accessible in that document the new document
+ * is a child of.
+ */
+ async PDocAccessible(nullable PDocAccessible aParentDoc, uint64_t aParentAcc,
+ MaybeDiscardedBrowsingContext aBrowsingContext);
+#endif
+
+ async PPaymentRequest();
+
+ /**
+ * Create a new Vsync connection for our associated root widget
+ */
+ async PVsync();
+
+ /**
+ * When the child process unsuppresses painting, we send the message for the
+ * parent process to start painting the new document, if it's still painting
+ * the old one.
+ */
+ [Priority=control] async DidUnsuppressPainting();
+
+ async DidUnsuppressPaintingNormalPriority();
+
+ /**
+ * When child sends this message, parent should move focus to
+ * the next or previous focusable element or document.
+ */
+ async MoveFocus(bool forward, bool forDocumentNavigation);
+
+ /**
+ * Called by the child to inform the parent that links are dropped into
+ * content area.
+ *
+ * aLinks A flat array of url, name, and type for each link
+ */
+ async DropLinks(nsString[] aLinks);
+
+ sync SyncMessage(nsString aMessage, ClonedMessageData aData)
+ returns (StructuredCloneData[] retval);
+
+ /**
+ * Notifies chrome that there is a focus change involving an editable
+ * object (input, textarea, document, contentEditable. etc.)
+ *
+ * contentCache Cache of content
+ * notification Whole data of the notification
+ * requests Requests of notification for IME of the native widget
+ */
+ [Nested=inside_cpow] async NotifyIMEFocus(ContentCache contentCache,
+ IMENotification notification)
+ returns (IMENotificationRequests requests);
+
+ /**
+ * Notifies chrome that there has been a change in text content
+ * One call can encompass both a delete and an insert operation
+ * Only called when NotifyIMEFocus returns PR_TRUE for mWantUpdates
+ *
+ * contentCache Cache of content
+ * notification Whole data of the notification
+ */
+ [Nested=inside_cpow] async NotifyIMETextChange(ContentCache contentCache,
+ IMENotification notification);
+
+ /**
+ * Notifies chrome that there is a IME compostion rect updated
+ *
+ * contentCache Cache of content
+ */
+ [Nested=inside_cpow] async NotifyIMECompositionUpdate(ContentCache contentCache,
+ IMENotification notification);
+
+ /**
+ * Notifies chrome that there has been a change in selection
+ * Only called when NotifyIMEFocus returns PR_TRUE for mWantUpdates
+ *
+ * contentCache Cache of content
+ * notification Whole data of the notification
+ */
+ [Nested=inside_cpow] async NotifyIMESelection(ContentCache contentCache,
+ IMENotification notification);
+
+ /**
+ * Notifies chrome of updating its content cache.
+ * This is useful if content is modified but we don't need to notify IME.
+ *
+ * contentCache Cache of content
+ */
+ [Nested=inside_cpow] async UpdateContentCache(ContentCache contentCache);
+
+ /**
+ * Notifies IME of mouse button event on a character in focused editor.
+ *
+ * Returns true if the mouse button event is consumed by IME.
+ */
+ [Nested=inside_cpow] sync NotifyIMEMouseButtonEvent(IMENotification notification)
+ returns (bool consumedByIME);
+
+ /**
+ * Notifies chrome to position change
+ *
+ * contentCache Cache of content
+ */
+ [Nested=inside_cpow] async NotifyIMEPositionChange(ContentCache contentCache,
+ IMENotification notification);
+
+ /**
+ * Requests chrome to commit or cancel composition of IME.
+ *
+ * cancel Set true if composition should be cancelled.
+ * compositionId Set the composition ID to requesting commit
+ * (stored in TextComposition).
+ *
+ * isCommitted Returns true if the request causes composition
+ * being committed synchronously.
+ * committedString Returns committed string. The may be non-empty
+ * string even if cancel is true because IME may
+ * try to restore selected string which was
+ * replaced with the composition.
+ */
+ [Nested=inside_cpow] sync RequestIMEToCommitComposition(bool cancel,
+ uint32_t aCompositionId)
+ returns (bool isCommitted, nsString committedString);
+
+ /**
+ * OnEventNeedingAckHandled() is called after a child process dispatches a
+ * composition event or a selection event which is sent from the parent
+ * process.
+ *
+ * message The message value of the handled event.
+ * compositionId The composition ID of handled composition event if
+ * the message is a composition event message. Otherwise,
+ * 0.
+ */
+ [Nested=inside_cpow] async OnEventNeedingAckHandled(EventMessage message,
+ uint32_t compositionId);
+
+ /**
+ * Request that the parent process move focus to the browser's frame. If
+ * canRaise is true, the window can be raised if it is inactive.
+ */
+ async RequestFocus(bool canRaise, CallerType aCallerType);
+
+ /**
+ * Sends a mouse wheel zoom change to the parent process, to be handled by
+ * the front end as needed.
+ */
+ async WheelZoomChange(bool increase);
+
+ /**
+ * Indicate, based on the current state, that some commands are enabled and
+ * some are disabled.
+ */
+ async EnableDisableCommands(MaybeDiscardedBrowsingContext bc,
+ nsString action,
+ nsCString[] enabledCommands,
+ nsCString[] disabledCommands);
+
+ [Nested=inside_cpow] sync GetInputContext() returns (IMEState state);
+
+ [Nested=inside_cpow] async SetInputContext(InputContext context,
+ InputContextAction action);
+
+ /**
+ * Set the native cursor.
+ * @param value
+ * The widget cursor to set.
+ * @param hasCustomCursor
+ * Whether there's any custom cursor represented by cursorData and
+ * company.
+ * @param customCursorData
+ * Serialized image data.
+ * @param width
+ * Width of the image.
+ * @param height
+ * Height of the image.
+ * @param resolutionX
+ * Resolution of the image X axis in dppx units.
+ * @param resolutionY
+ * Resolution of the image Y axis in dppx units.
+ * @param stride
+ * Stride used in the image data.
+ * @param format
+ * Image format, see gfx::SurfaceFormat for possible values.
+ * @param hotspotX
+ * Horizontal hotspot of the image, as specified by the css cursor property.
+ * @param hotspotY
+ * Vertical hotspot of the image, as specified by the css cursor property.
+ * @param force
+ * Invalidate any locally cached cursor settings and force an
+ * update.
+ */
+ async SetCursor(nsCursor value,
+ bool hasCustomCursor,
+ BigBuffer? customCursorData,
+ uint32_t width, uint32_t height,
+ float resolutionX, float resolutionY,
+ uint32_t stride, SurfaceFormat format,
+ uint32_t hotspotX, uint32_t hotspotY, bool force);
+
+ /**
+ * Used to set the current text of the status tooltip.
+ * Nowadays this is only used for link locations on hover.
+ */
+ async SetLinkStatus(nsString status);
+
+ /**
+ * Show/hide a tooltip when the mouse hovers over an element in the content
+ * document.
+ */
+ async ShowTooltip(uint32_t x, uint32_t y, nsString tooltip, nsString direction);
+ async HideTooltip();
+
+ /**
+ * Create an asynchronous color picker on the parent side,
+ * but don't open it yet.
+ */
+ async PColorPicker(nsString title, nsString initialColor, nsString[] defaultColors);
+
+ async PFilePicker(nsString aTitle, Mode aMode);
+
+ /**
+ * Tells the containing widget whether the given input block results in a
+ * swipe. Should be called in response to a WidgetWheelEvent that has
+ * mFlags.mCanTriggerSwipe set on it.
+ */
+ async RespondStartSwipeEvent(uint64_t aInputBlockId, bool aStartSwipe);
+
+ /**
+ * Look up dictionary by selected word for OSX
+ *
+ * @param aText The word to look up
+ * @param aFontRange Text decoration of aText
+ * @param aIsVertical true if vertical layout
+ */
+ async LookUpDictionary(nsString aText, FontRange[] aFontRangeArray,
+ bool aIsVertical, LayoutDeviceIntPoint aPoint);
+
+ async __delete__();
+
+ /**
+ * Send a reply of keyboard event to the parent. Then, parent can consider
+ * whether the event should kick a shortcut key or ignore.
+ *
+ * @param aEvent The event which was sent from the parent and handled
+ * in a remote process.
+ * @param aUUI The UUID which was generated when aEvent was sent to
+ * a remote process.
+ */
+ async ReplyKeyEvent(WidgetKeyboardEvent aEvent, nsID aUUID);
+
+ /**
+ * Retrieves edit commands for the key combination represented by aEvent.
+ *
+ * @param aType One of NativeKeyBindingsType.
+ * @param aEvent KeyboardEvent which represents a key combination.
+ * Note that this must be a trusted event.
+ * @return Array of edit commands which should be executed in
+ * editor of native applications.
+ */
+ sync RequestNativeKeyBindings(uint32_t aType, WidgetKeyboardEvent aEvent)
+ returns (CommandInt[] commands);
+
+ async SynthesizeNativeKeyEvent(int32_t aNativeKeyboardLayout,
+ int32_t aNativeKeyCode,
+ uint32_t aModifierFlags,
+ nsString aCharacters,
+ nsString aUnmodifiedCharacters,
+ uint64_t aObserverId);
+ async SynthesizeNativeMouseEvent(LayoutDeviceIntPoint aPoint,
+ uint32_t aNativeMessage,
+ int16_t aButton,
+ uint32_t aModifierFlags,
+ uint64_t aObserverId);
+ async SynthesizeNativeMouseMove(LayoutDeviceIntPoint aPoint,
+ uint64_t aObserverId);
+ async SynthesizeNativeMouseScrollEvent(LayoutDeviceIntPoint aPoint,
+ uint32_t aNativeMessage,
+ double aDeltaX,
+ double aDeltaY,
+ double aDeltaZ,
+ uint32_t aModifierFlags,
+ uint32_t aAdditionalFlags,
+ uint64_t aObserverId);
+ async SynthesizeNativeTouchPoint(uint32_t aPointerId,
+ TouchPointerState aPointerState,
+ LayoutDeviceIntPoint aPoint,
+ double aPointerPressure,
+ uint32_t aPointerOrientation,
+ uint64_t aObserverId);
+ async SynthesizeNativeTouchPadPinch(TouchpadGesturePhase aEventPhase,
+ float aScale,
+ LayoutDeviceIntPoint aPoint,
+ int32_t aModifierFlags);
+ async SynthesizeNativeTouchTap(LayoutDeviceIntPoint aPoint,
+ bool aLongTap,
+ uint64_t aObserverId);
+ async ClearNativeTouchSequence(uint64_t aObserverId);
+ async SynthesizeNativePenInput(uint32_t aPointerId,
+ TouchPointerState aPointerState,
+ LayoutDeviceIntPoint aPoint,
+ double aPressure,
+ uint32_t aRotation,
+ int32_t aTiltX,
+ int32_t aTiltY,
+ int32_t aButton,
+ uint64_t aObserverId);
+ async SynthesizeNativeTouchpadDoubleTap(LayoutDeviceIntPoint aPoint,
+ uint32_t aModifierFlags);
+ async SynthesizeNativeTouchpadPan(TouchpadGesturePhase aEventPhase,
+ LayoutDeviceIntPoint aPoint,
+ double aDeltaX, double aDeltaY,
+ int32_t aModifierFlags,
+ uint64_t aObserverId);
+
+ async LockNativePointer();
+ async UnlockNativePointer();
+
+ async AccessKeyNotHandled(WidgetKeyboardEvent event);
+
+ async RegisterProtocolHandler(nsString scheme, nullable nsIURI handlerURI, nsString title,
+ nullable nsIURI documentURI);
+
+ async OnStateChange(WebProgressData aWebProgressData,
+ RequestData aRequestData, uint32_t aStateFlags,
+ nsresult aStatus,
+ WebProgressStateChangeData? aStateChangeData);
+
+ async OnLocationChange(WebProgressData aWebProgressData,
+ RequestData aRequestData, nullable nsIURI aLocation,
+ uint32_t aFlags, bool aCanGoBack,
+ bool aCanGoForward,
+ WebProgressLocationChangeData? aLocationChangeData);
+
+ // We only track information about total progress in the parent process.
+ // This value is throttled using nsBrowserStatusFilter, and records the full
+ // total progress for nsDocShells managed by this actor.
+ async OnProgressChange(int32_t aCurTotalProgress, int32_t aMaxTotalProgress);
+
+ // Calls to OnStatusChange are throttled by nsBrowserStatusFilter, meaning
+ // they are only called with a status of `NS_OK`, and with no webProgress or
+ // request.
+ async OnStatusChange(nsString aMessage);
+
+ async NotifyContentBlockingEvent(uint32_t aEvent, RequestData aRequestData,
+ bool aBlocked, nsCString aTrackingOrigin,
+ nsCString[] aTrackingFullHashes,
+ StorageAccessPermissionGrantedReason? aReason,
+ CanvasFingerprinter? aCanvasFingerprinter,
+ bool? aCanvasFingerprinterKnownText);
+
+ async NavigationFinished();
+
+ async IntrinsicSizeOrRatioChanged(IntrinsicSize? aIntrinsicSize,
+ AspectRatio? aIntrinsicRatio);
+
+ async ImageLoadComplete(nsresult aResult);
+
+ /**
+ * Child informs the parent that a pointer lock has requested/released.
+ */
+ async RequestPointerLock() returns (nsCString error);
+ async ReleasePointerLock();
+
+ /**
+ * Child informs the parent that a pointer capture has requested/released.
+ */
+ async RequestPointerCapture(uint32_t aPointerId) returns (bool aSuccess);
+ async ReleasePointerCapture(uint32_t aPointerId);
+
+child:
+ async NativeSynthesisResponse(uint64_t aObserverId, nsCString aResponse);
+ async UpdateSHistory();
+ async CloneDocumentTreeIntoSelf(MaybeDiscardedBrowsingContext aBc, PrintData aPrintData) returns(bool aSuccess);
+ async UpdateRemotePrintSettings(PrintData aPrintData);
+
+ /**
+ * Parent informs the child to release all pointer capture.
+ */
+ [Priority=input] async ReleaseAllPointerCapture();
+
+parent:
+
+ /**
+ * Child informs the parent that the content is ready to handle input
+ * events. This is sent when the BrowserChild is created.
+ */
+ async RemoteIsReadyToHandleInputEvents();
+
+child:
+ /**
+ * Parent informs the child of graphical effects that are being applied
+ * to the child browser.
+ */
+ async UpdateEffects(EffectsInfo aEffects);
+
+parent:
+
+ /**
+ * Sent by the child to the parent to inform it that an update to the
+ * dimensions has been requested.
+ *
+ * @param aRequest The requested change of inner or outer dimensions.
+ * @param aScale The scale at the time of the request. This is to allow
+ * the parent to recompute the dimensions in case of an
+ * ongoing scale change.
+ */
+ async SetDimensions(DimensionRequest aRequest, double aScale);
+
+ [Nested=inside_sync] sync DispatchWheelEvent(WidgetWheelEvent event);
+ [Nested=inside_sync] sync DispatchMouseEvent(WidgetMouseEvent event);
+ [Nested=inside_sync] sync DispatchKeyboardEvent(WidgetKeyboardEvent event);
+ [Nested=inside_sync] sync DispatchTouchEvent(WidgetTouchEvent event);
+
+ async InvokeDragSession(IPCTransferableData[] transfers, uint32_t action,
+ BigBuffer? visualData,
+ uint32_t stride, SurfaceFormat format,
+ LayoutDeviceIntRect dragRect,
+ nullable nsIPrincipal principal,
+ nullable nsIContentSecurityPolicy csp,
+ CookieJarSettingsArgs cookieJarSettings,
+ MaybeDiscardedWindowContext sourceWindowContext,
+ MaybeDiscardedWindowContext sourceTopWindowContext);
+
+ // After a compositor reset, it is necessary to reconnect each layers ID to
+ // the compositor of the widget that will render those layers. Note that
+ // this is sync so we can ensure that messages to the window compositor
+ // arrive before the BrowserChild attempts to use its cross-process compositor
+ // bridge.
+ sync EnsureLayersConnected() returns (CompositorOptions compositorOptions);
+
+ /**
+ * This function is used to notify the parent that it should display a
+ * canvas permission prompt.
+ *
+ * @param aOrigin origin string of the document that is requesting access.
+ */
+ async ShowCanvasPermissionPrompt(nsCString aOrigin,
+ bool aHideDoorHanger);
+
+ sync SetSystemFont(nsCString aFontName);
+ sync GetSystemFont() returns (nsCString retval);
+
+ /**
+ * Called once this PBrowser's OOP subdoc no longer blocks its
+ * embedding element's and embedding doc's 'load' events.
+ */
+ async MaybeFireEmbedderLoadEvents(EmbedderElementEventType aFireEventAtEmbeddingElement);
+
+ async ScrollRectIntoView(nsRect aRect, ScrollAxis aVertical,
+ ScrollAxis aHorizontal, ScrollFlags aScrollFlags,
+ int32_t aAppUnitsPerDevPixel);
+
+ async ShowDynamicToolbar();
+
+child:
+ /**
+ * Notify the remote browser that it has been Show()n on this side. This
+ * message is expected to trigger creation of the remote browser's "widget".
+ */
+ async Show(ParentShowInfo parentInfo, OwnerShowInfo childInfo);
+
+ /**
+ * Sending an activate message moves focus to the child.
+ */
+ async Activate(uint64_t aActionId);
+
+ async Deactivate(uint64_t aActionId);
+
+ async ScrollbarPreferenceChanged(ScrollbarPreference pref);
+
+ async InitRendering(TextureFactoryIdentifier textureFactoryIdentifier,
+ LayersId layersId,
+ CompositorOptions compositorOptions,
+ bool layersConnected);
+
+ async CompositorOptionsChanged(CompositorOptions newOptions);
+
+ async LoadURL(nsDocShellLoadState loadState, ParentShowInfo info);
+
+ async CreateAboutBlankDocumentViewer(nullable nsIPrincipal principal,
+ nullable nsIPrincipal partitionedPrincipal);
+
+ async ResumeLoad(uint64_t pendingSwitchID, ParentShowInfo info);
+
+ [Compress=all] async UpdateDimensions(DimensionInfo dimensions);
+
+ async SizeModeChanged(nsSizeMode sizeMode);
+
+ async ChildToParentMatrix(Matrix4x4? aMatrix,
+ ScreenRect aRemoteDocumentRect);
+
+ async UpdateRemoteStyle(StyleImageRendering aImageRendering);
+
+ async DynamicToolbarMaxHeightChanged(ScreenIntCoord height);
+
+ async DynamicToolbarOffsetChanged(ScreenIntCoord height);
+
+ /**
+ * StopIMEStateManagement() is called when the process loses focus and
+ * should stop managing IME state.
+ */
+ async StopIMEStateManagement();
+
+ /**
+ * When two consecutive mouse move events would be added to the message queue,
+ * they are 'compressed' by dumping the oldest one.
+ */
+ [Compress, Priority=input]
+ async RealMouseMoveEvent(WidgetMouseEvent event,
+ ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId);
+ [Compress]
+ async NormalPriorityRealMouseMoveEvent(WidgetMouseEvent event,
+ ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId);
+
+ /**
+ * But don't compress mousemove events for tests since every event is
+ * important for the test since synthesizing various input events may
+ * be faster than what the user operates same things. If you need to
+ * test the `Compress`, send mouse move events with setting `isSyntehsized`
+ * of `aEvent` of `EventUtils#syntehsizeMouse*()`.
+ */
+ [Priority=input]
+ async RealMouseMoveEventForTests(WidgetMouseEvent event,
+ ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId);
+ async NormalPriorityRealMouseMoveEventForTests(WidgetMouseEvent event,
+ ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId);
+
+ /**
+ * Mouse move events with |reason == eSynthesized| are sent via a separate
+ * message because they do not generate DOM 'mousemove' events, and the
+ * 'Compress' attribute on RealMouseMoveEvent() could result in a
+ * |reason == eReal| event being dropped in favour of an |eSynthesized|
+ * event, and thus a DOM 'mousemove' event to be lost.
+ */
+ [Priority=input]
+ async SynthMouseMoveEvent(WidgetMouseEvent event,
+ ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId);
+ async NormalPrioritySynthMouseMoveEvent(WidgetMouseEvent event,
+ ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId);
+
+ [Priority=input]
+ async RealMouseButtonEvent(WidgetMouseEvent event,
+ ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId);
+ async NormalPriorityRealMouseButtonEvent(WidgetMouseEvent event,
+ ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId);
+
+ [Priority=input]
+ async RealMouseEnterExitWidgetEvent(WidgetMouseEvent event,
+ ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId);
+ async NormalPriorityRealMouseEnterExitWidgetEvent(WidgetMouseEvent event,
+ ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId);
+
+ /**
+ * Send a keyboard event which reporesents a user input to a remote process.
+ *
+ * @param aEvent The event which user typed a key.
+ * @param aUUID A UUID which is generated in the parent at sending it.
+ * This must be specified when the child sends a reply
+ * event to the parent.
+ */
+ [Priority=input]
+ async RealKeyEvent(WidgetKeyboardEvent aEvent, nsID aUUID);
+ async NormalPriorityRealKeyEvent(WidgetKeyboardEvent aEvent, nsID aUUID);
+
+ [Priority=input]
+ async MouseWheelEvent(WidgetWheelEvent event,
+ ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId);
+ async NormalPriorityMouseWheelEvent(WidgetWheelEvent event,
+ ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId);
+
+ [Priority=input]
+ async RealTouchEvent(WidgetTouchEvent aEvent,
+ ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId,
+ nsEventStatus aApzResponse);
+ async NormalPriorityRealTouchEvent(WidgetTouchEvent aEvent,
+ ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId,
+ nsEventStatus aApzResponse);
+
+ [Priority=input]
+ async HandleTap(GeckoContentController_TapType aType, LayoutDevicePoint point,
+ Modifiers aModifiers, ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId,
+ DoubleTapToZoomMetrics? aMetrics);
+ async NormalPriorityHandleTap(GeckoContentController_TapType aType, LayoutDevicePoint point,
+ Modifiers aModifiers, ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId,
+ DoubleTapToZoomMetrics? aMetrics);
+
+ [Compress, Priority=input]
+ async RealTouchMoveEvent(WidgetTouchEvent aEvent,
+ ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId,
+ nsEventStatus aApzResponse);
+ [Compress]
+ async NormalPriorityRealTouchMoveEvent(WidgetTouchEvent aEvent,
+ ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId,
+ nsEventStatus aApzResponse);
+ [Compress, Priority=input]
+ async RealTouchMoveEvent2(WidgetTouchEvent aEvent,
+ ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId,
+ nsEventStatus aApzResponse);
+ [Compress]
+ async NormalPriorityRealTouchMoveEvent2(WidgetTouchEvent aEvent,
+ ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId,
+ nsEventStatus aApzResponse);
+
+ /*
+ * We disable the input event queue when there is an active dnd session. We
+ * don't need support RealDragEvent with input priority.
+ */
+ async RealDragEvent(WidgetDragEvent aEvent, uint32_t aDragAction,
+ uint32_t aDropEffect, nullable nsIPrincipal aPrincipal,
+ nullable nsIContentSecurityPolicy csp);
+
+ [Priority=input] async CompositionEvent(WidgetCompositionEvent event);
+ async NormalPriorityCompositionEvent(WidgetCompositionEvent event);
+
+ [Priority=input] async SelectionEvent(WidgetSelectionEvent event);
+ async NormalPrioritySelectionEvent(WidgetSelectionEvent event);
+
+ /**
+ * Dispatch eContentCommandInsertText event in the remote process.
+ */
+ [Priority=input] async InsertText(nsString aStringToInsert);
+ async NormalPriorityInsertText(nsString aStringToInsert);
+
+ /**
+ * Call PasteTransferable via a controller on the content process
+ * to handle the command content event, "pasteTransferable".
+ */
+ async PasteTransferable(IPCTransferable aTransferable);
+
+ async LoadRemoteScript(nsString aURL, bool aRunInGlobalScope);
+
+ /**
+ * Sent by the chrome process when it no longer wants this remote
+ * <browser>. The child side cleans up in response, then
+ * finalizing its death by sending back __delete__() to the
+ * parent.
+ */
+ async Destroy();
+
+ /**
+ * If aEnabled is true, tells the child to paint and upload layers to
+ * the compositor. If aEnabled is false, the child stops painting and
+ * clears the layers from the compositor.
+ *
+ * @param aEnabled
+ * True if the child should render and upload layers, false if the
+ * child should clear layers.
+ */
+ async RenderLayers(bool aEnabled);
+
+ /**
+ * Communicates the child that we want layers to be preserved even when the
+ * browser is inactive.
+ */
+ async PreserveLayers(bool aPreserve);
+child:
+ /**
+ * Notify the child that it shouldn't paint the offscreen displayport.
+ * This is useful to speed up interactive operations over async
+ * scrolling performance like resize, tabswitch, pageload.
+ *
+ * Each enable call must be matched with a disable call. The child
+ * will remain in the suppress mode as long as there's
+ * a single unmatched call.
+ */
+ async SuppressDisplayport(bool aEnabled);
+
+ /**
+ * Navigate by key (Tab/Shift+Tab/F6/Shift+f6).
+ */
+ async NavigateByKey(bool aForward, bool aForDocumentNavigation);
+
+ /**
+ * Tell the child that the UI resolution changed for the containing
+ * window.
+ * To avoid some sync messages from child to parent, we also send the dpi
+ * and default scale with the notification.
+ * If we don't know the dpi and default scale, we just pass in a negative
+ * value (-1) but in the majority of the cases this saves us from two
+ * sync requests from the child to the parent.
+ */
+ async UIResolutionChanged(float dpi, int32_t rounding, double scale);
+
+ /**
+ * Tell the child that the safe area of widget has changed.
+ *
+ */
+ async SafeAreaInsetsChanged(ScreenIntMargin aSafeAreaInsets);
+
+ /**
+ * Tell the browser that its frame loader has been swapped
+ * with another.
+ */
+ async SwappedWithOtherRemoteLoader(IPCTabContext context);
+
+ /**
+ * A potential accesskey was just pressed. Look for accesskey targets
+ * using the list of provided charCodes.
+ *
+ * @param event keyboard event
+ * @param isTrusted true if triggered by a trusted key event
+ */
+ async HandleAccessKey(WidgetKeyboardEvent event,
+ uint32_t[] charCodes);
+
+ /**
+ * Tell the child to create a print preview document in this browser, or
+ * to update the existing print preview document with new print settings.
+ *
+ * @param aPrintData The serialized print settings to use to layout the
+ * print preview document.
+ * @param aSourceBrowsingContext Optionally, the browsing context that
+ * contains the document from which the print preview is to be generated.
+ * This should only be passed on the first call. It should not be passed
+ * for any subsequent calls that are made to update the existing print
+ * preview document with a new print settings object.
+ */
+ async PrintPreview(PrintData aPrintData,
+ MaybeDiscardedBrowsingContext aSourceBrowsingContext) returns (PrintPreviewResultInfo aInfo);
+
+ /**
+ * Inform the print preview document that we're done with it.
+ */
+ async ExitPrintPreview();
+
+ /**
+ * Tell the child to print the current page with the given settings.
+ *
+ * @param aBrowsingContext the browsing context to print.
+ * @param aPrintData the serialized settings to print with
+ */
+ async Print(MaybeDiscardedBrowsingContext aBC, PrintData aPrintData);
+
+ /**
+ * Update the child with the tab's current top-level native window handle.
+ * This is used by a11y objects who must expose their native window.
+ *
+ * @param aNewHandle The native window handle of the tab's top-level window.
+ */
+ async UpdateNativeWindowHandle(uintptr_t aNewHandle);
+
+ /**
+ * Tell the BrowserChild to allow scripts in the docshell to close the window.
+ */
+ async AllowScriptsToClose();
+
+ async WillChangeProcess();
+
+parent:
+ /**
+ * Fetches whether this window supports protected media, which is sent back in response.
+ */
+ async IsWindowSupportingProtectedMedia(uint64_t aOuterWindowID) returns(bool isSupported);
+
+ /**
+ * Fetches whether this window supports WebVR, which is sent back in response.
+ */
+ async IsWindowSupportingWebVR(uint64_t aOuterWindowID) returns(bool isSupported);
+
+ /** Records a history visit. */
+ async VisitURI(nullable nsIURI aURI, nullable nsIURI aLastVisitedURI, uint32_t aFlags, uint64_t aBrowserId);
+
+ /** Fetches the visited status for an array of URIs (Android-only). */
+ async QueryVisitedState(nullable nsIURI[] aURIs);
+
+ /** Create a session store for a browser child. */
+ async PSessionStore();
+
+ /**
+ * Construct a new WindowGlobal for an existing global in the content process
+ */
+ async NewWindowGlobal(ManagedEndpoint<PWindowGlobalParent> aEndpoint,
+ WindowGlobalInit aInit);
+
+/*
+ * FIXME: write protocol!
+
+state LIVE:
+ send LoadURL goto LIVE;
+//etc.
+ send Destroy goto DYING;
+
+state DYING:
+ discard send blah;
+// etc.
+ recv __delete__;
+ */
+};
+
+}
+}
diff --git a/dom/ipc/PBrowserBridge.ipdl b/dom/ipc/PBrowserBridge.ipdl
new file mode 100644
index 0000000000..c2af10d2a4
--- /dev/null
+++ b/dom/ipc/PBrowserBridge.ipdl
@@ -0,0 +1,134 @@
+/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PBrowser;
+#ifdef ACCESSIBILITY
+include protocol PDocAccessible;
+#endif
+
+include DOMTypes;
+include PPrintingTypes;
+
+include "mozilla/LayoutMessageUtils.h";
+include "mozilla/dom/BindingIPCUtils.h";
+include "mozilla/dom/DocShellMessageUtils.h";
+include "mozilla/dom/TabMessageUtils.h";
+
+using class mozilla::WidgetMouseEvent from "ipc/nsGUIEventIPC.h";
+using mozilla::dom::EffectsInfo from "mozilla/dom/EffectsInfo.h";
+using mozilla::ScrollAxis from "mozilla/PresShellForwards.h";
+using mozilla::ScrollFlags from "mozilla/PresShellForwards.h";
+using struct nsRect from "nsRect.h";
+using mozilla::dom::CallerType from "mozilla/dom/BindingDeclarations.h";
+using nsIntRect from "nsRect.h";
+using mozilla::dom::EmbedderElementEventType from "mozilla/dom/TabMessageTypes.h";
+[RefCounted] using class nsDocShellLoadState from "nsDocShellLoadState.h";
+using mozilla::IntrinsicSize from "nsIFrame.h";
+using mozilla::AspectRatio from "mozilla/AspectRatio.h";
+using mozilla::StyleImageRendering from "mozilla/ServoStyleConsts.h";
+
+namespace mozilla {
+namespace dom {
+
+/**
+ * A PBrowserBridge connects an iframe/browser in a content process to the
+ * PBrowser that manages the embedded content.
+ *
+ * See `dom/docs/Fission-IPC-Diagram.svg` for an overview of the DOM IPC
+ * actors.
+ */
+async protocol PBrowserBridge {
+ manager PBrowser;
+
+child:
+ /**
+ * Request that the IPC child / Web parent process move focus to the
+ * browser's frame. If canRaise is true, the window can be raised if
+ * it is inactive.
+ */
+ async RequestFocus(bool canRaise, CallerType aCallerType);
+
+ /**
+ * When IPC parent / Web child sends this message, the IPC child / Web parent
+ * should move focus to the next or previous focusable element or document.
+ */
+ async MoveFocus(bool forward, bool forDocumentNavigation);
+
+ /**
+ * Called once this PBrowserBridge's OOP subdoc no longer blocks its
+ * embedding element's and embedding doc's 'load' events.
+ */
+ async MaybeFireEmbedderLoadEvents(EmbedderElementEventType aFireEventAtEmbeddingElement);
+
+ async ScrollRectIntoView(nsRect aRect, ScrollAxis aVertical,
+ ScrollAxis aHorizontal, ScrollFlags aScrollFlags,
+ int32_t aAppUnitsPerDevPixel);
+
+ async SubFrameCrashed();
+
+ async IntrinsicSizeOrRatioChanged(IntrinsicSize? aIntrinsicSize,
+ AspectRatio? aIntrinsicRatio);
+
+ async ImageLoadComplete(nsresult aResult);
+
+both:
+
+ // Destroy the remote web browser due to the nsFrameLoader going away.
+ // Before initialization we sync-delete it from the child. After
+ // initialization we sync-delete it from the parent after BeginDestroy().
+ async __delete__();
+
+parent:
+
+ async BeginDestroy();
+
+ // DocShell messaging.
+ async LoadURL(nsDocShellLoadState aLoadState);
+ async ResumeLoad(uint64_t aPendingSwitchID);
+
+ // Out of process rendering.
+ async Show(OwnerShowInfo info);
+ async ScrollbarPreferenceChanged(ScrollbarPreference pref);
+ [Compress=all] async UpdateDimensions(nsIntRect rect, ScreenIntSize size);
+ async RenderLayers(bool aEnabled);
+
+ async UpdateEffects(EffectsInfo aEffects);
+ async UpdateRemotePrintSettings(PrintData aPrintData);
+
+ /**
+ * Navigate by key (Tab/Shift+Tab/F6/Shift+f6).
+ */
+ async NavigateByKey(bool aForward, bool aForDocumentNavigation);
+
+ /**
+ * Dispatch the given synthesized mousemove event to the child.
+ */
+ async DispatchSynthesizedMouseEvent(WidgetMouseEvent event);
+
+ /**
+ * Sending an activate message moves focus to the iframe.
+ */
+ async Activate(uint64_t aActionId);
+
+ async Deactivate(bool aWindowLowering, uint64_t aActionId);
+
+ async UpdateRemoteStyle(StyleImageRendering aImageRendering);
+
+ async WillChangeProcess();
+
+#ifdef ACCESSIBILITY
+ /**
+ * Tell the parent the accessible for this iframe's embedder
+ * OuterDocAccessible.
+ * aDoc is the actor for the containing document.
+ * aID is the unique id of the embedder accessible within that document.
+ */
+ async SetEmbedderAccessible(nullable PDocAccessible aDoc, uint64_t aID);
+#endif
+};
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/PColorPicker.ipdl b/dom/ipc/PColorPicker.ipdl
new file mode 100644
index 0000000000..9690327d1d
--- /dev/null
+++ b/dom/ipc/PColorPicker.ipdl
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PBrowser;
+
+namespace mozilla {
+namespace dom {
+
+[ChildImpl=virtual, ParentImpl=virtual]
+protocol PColorPicker
+{
+ manager PBrowser;
+
+parent:
+ async Open();
+
+child:
+ async Update(nsString color);
+
+ async __delete__(nsString color);
+};
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl
new file mode 100644
index 0000000000..b8155fd375
--- /dev/null
+++ b/dom/ipc/PContent.ipdl
@@ -0,0 +1,1957 @@
+/* -*- tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PBackgroundStarter;
+include protocol PBrowser;
+include protocol PClipboardReadRequest;
+include protocol PClipboardWriteRequest;
+include protocol PCompositorManager;
+include protocol PContentPermissionRequest;
+include protocol PCycleCollectWithLogs;
+include protocol PDocumentChannel;
+include protocol PExtensions;
+include protocol PExternalHelperApp;
+include protocol PHandlerService;
+include protocol PHal;
+include protocol PHeapSnapshotTempFileHelper;
+include protocol PProcessHangMonitor;
+include protocol PImageBridge;
+include protocol PRemotePrintJob;
+include protocol PMedia;
+include protocol PNecko;
+include protocol PStreamFilter;
+include protocol PGMPContent;
+include protocol PGMPService;
+include protocol PGMP;
+#ifdef MOZ_WEBSPEECH
+include protocol PSpeechSynthesis;
+#endif
+include protocol PTestShell;
+include protocol PRemoteSpellcheckEngine;
+include protocol PWebBrowserPersistDocument;
+#ifdef MOZ_WEBRTC
+include protocol PWebrtcGlobal;
+#endif
+include protocol PWindowGlobal;
+include protocol PURLClassifier;
+include protocol PURLClassifierLocal;
+include protocol PVRManager;
+include protocol PRemoteDecoderManager;
+include protocol PProfiler;
+include protocol PScriptCache;
+include protocol PSessionStorageObserver;
+include protocol PBenchmarkStorage;
+include DOMTypes;
+include WindowGlobalTypes;
+include IPCBlob;
+include IPCStream;
+include IPCTransferable;
+include PPrintingTypes;
+include PTabContext;
+include ProtocolTypes;
+include PBackgroundSharedTypes;
+include PContentPermission;
+include GraphicsMessages;
+include MemoryReportTypes;
+include ClientIPCTypes;
+include HangTypes;
+include PrefsTypes;
+include NeckoChannelParams;
+include PSMIPCTypes;
+include LookAndFeelTypes;
+
+#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS)
+include protocol PSandboxTesting;
+#endif
+
+include "ipc/MediaControlIPC.h";
+include "mozilla/AntiTrackingIPCUtils.h";
+include "mozilla/GfxMessageUtils.h";
+include "mozilla/dom/BindingIPCUtils.h";
+include "mozilla/dom/CSPMessageUtils.h";
+include "mozilla/dom/DocShellMessageUtils.h";
+include "mozilla/dom/FeaturePolicyUtils.h";
+include "mozilla/dom/MediaSessionIPCUtils.h";
+include "mozilla/dom/PageLoadEventUtils.h";
+include "mozilla/dom/ReferrerInfoUtils.h";
+include "mozilla/glean/GleanMetrics.h";
+include "mozilla/ipc/ByteBufUtils.h";
+include "mozilla/ipc/TransportSecurityInfoUtils.h";
+include "mozilla/ipc/URIUtils.h";
+include "mozilla/net/NeckoMessageUtils.h";
+include "mozilla/PermissionDelegateIPCUtils.h";
+
+[RefCounted] using class nsIDOMGeoPosition from "nsGeoPositionIPCSerialiser.h";
+[RefCounted] using class nsIAlertNotification from "mozilla/AlertNotificationIPCSerializer.h";
+
+using struct ChromePackage from "mozilla/chrome/RegistryMessageUtils.h";
+using struct SubstitutionMapping from "mozilla/chrome/RegistryMessageUtils.h";
+using struct OverrideMapping from "mozilla/chrome/RegistryMessageUtils.h";
+using base::ProcessId from "base/process.h";
+using struct IPC::Permission from "mozilla/net/NeckoMessageUtils.h";
+using mozilla::dom::NativeThreadId from "mozilla/dom/NativeThreadId.h";
+using mozilla::hal::ProcessPriority from "mozilla/HalTypes.h";
+using mozilla::gfx::IntSize from "mozilla/gfx/2D.h";
+using mozilla::gfx::SurfaceFormat from "mozilla/gfx/Types.h";
+using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
+using mozilla::dom::ContentParentId from "mozilla/dom/ipc/IdType.h";
+using mozilla::EventMessage from "mozilla/EventForwards.h";
+using mozilla::LayoutDeviceIntPoint from "Units.h";
+using mozilla::ImagePoint from "Units.h";
+using mozilla::ImageIntSize from "Units.h";
+using mozilla::widget::ThemeChangeKind from "mozilla/widget/WidgetMessageUtils.h";
+using class mozilla::dom::MessagePort from "mozilla/dom/MessagePort.h";
+using class mozilla::dom::ipc::StructuredCloneData from "mozilla/dom/ipc/StructuredCloneData.h";
+using mozilla::OriginAttributes from "mozilla/ipc/BackgroundUtils.h";
+using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
+using mozilla::layers::CompositorOptions from "mozilla/layers/CompositorOptions.h";
+using mozilla::layers::LayersId from "mozilla/layers/LayersTypes.h";
+using mozilla::Telemetry::HistogramAccumulation from "mozilla/TelemetryComms.h";
+using mozilla::Telemetry::KeyedHistogramAccumulation from "mozilla/TelemetryComms.h";
+using mozilla::Telemetry::ScalarAction from "mozilla/TelemetryComms.h";
+using mozilla::Telemetry::KeyedScalarAction from "mozilla/TelemetryComms.h";
+using mozilla::Telemetry::DynamicScalarDefinition from "mozilla/TelemetryComms.h";
+using mozilla::Telemetry::ChildEventData from "mozilla/TelemetryComms.h";
+
+#if defined(XP_WIN)
+[MoveOnly] using mozilla::UntrustedModulesData from "mozilla/UntrustedModulesData.h";
+[MoveOnly] using mozilla::ModulePaths from "mozilla/UntrustedModulesData.h";
+[MoveOnly] using mozilla::ModulesMapResult from "mozilla/UntrustedModulesData.h";
+#endif // defined(XP_WIN)
+
+using mozilla::Telemetry::DiscardedData from "mozilla/TelemetryComms.h";
+using mozilla::glean::perf::PageLoadExtra from "mozilla/glean/GleanMetrics.h";
+[MoveOnly] using mozilla::CrossProcessMutexHandle from "mozilla/ipc/CrossProcessMutex.h";
+using mozilla::dom::MaybeDiscardedBrowsingContext from "mozilla/dom/BrowsingContext.h";
+using mozilla::dom::BrowsingContextTransaction from "mozilla/dom/BrowsingContext.h";
+using mozilla::dom::BrowsingContextInitializer from "mozilla/dom/BrowsingContext.h";
+using mozilla::dom::PermitUnloadResult from "nsIDocumentViewer.h";
+using mozilla::dom::MaybeDiscardedWindowContext from "mozilla/dom/WindowContext.h";
+using mozilla::dom::WindowContextTransaction from "mozilla/dom/WindowContext.h";
+[MoveOnly] using base::SharedMemoryHandle from "base/shared_memory.h";
+using gfxSparseBitSet from "gfxFontUtils.h";
+using FontVisibility from "gfxFontEntry.h";
+using mozilla::dom::MediaControlAction from "mozilla/dom/MediaControlKeySource.h";
+using mozilla::dom::MediaPlaybackState from "mozilla/dom/MediaPlaybackStatus.h";
+using mozilla::dom::MediaAudibleState from "mozilla/dom/MediaPlaybackStatus.h";
+using mozilla::dom::MediaMetadataBase from "mozilla/dom/MediaMetadata.h";
+using mozilla::dom::MediaSessionAction from "mozilla/dom/MediaSessionBinding.h";
+using mozilla::dom::MediaSessionPlaybackState from "mozilla/dom/MediaSessionBinding.h";
+using mozilla::dom::PositionState from "mozilla/dom/MediaSession.h";
+using mozilla::dom::ServiceWorkerShutdownState::Progress from "mozilla/dom/ServiceWorkerShutdownState.h";
+using mozilla::ContentBlockingNotifier::StorageAccessPermissionGrantedReason from "mozilla/ContentBlockingNotifier.h";
+using mozilla::ContentBlockingNotifier::BlockingDecision from "mozilla/ContentBlockingNotifier.h";
+using mozilla::StorageAccessAPIHelper::StorageAccessPromptChoices from "mozilla/StorageAccessAPIHelper.h";
+using mozilla::dom::JSActorMessageKind from "mozilla/dom/JSActor.h";
+using mozilla::dom::JSActorMessageMeta from "mozilla/dom/PWindowGlobal.h";
+using mozilla::PermissionDelegateHandler::DelegatedPermissionList from "mozilla/PermissionDelegateHandler.h";
+[RefCounted] using class nsILayoutHistoryState from "nsILayoutHistoryState.h";
+using class mozilla::dom::SessionHistoryInfo from "mozilla/dom/SessionHistoryEntry.h";
+using struct nsPoint from "nsPoint.h";
+using struct mozilla::dom::LoadingSessionHistoryInfo from "mozilla/dom/SessionHistoryEntry.h";
+using mozilla::media::MediaCodecsSupported from "MediaCodecsSupport.h";
+using mozilla::RemoteDecodeIn from "mozilla/RemoteDecoderManagerChild.h";
+using mozilla::dom::PerformanceTimingData from "mozilla/dom/PerformanceTiming.h";
+[RefCounted] using mozilla::dom::FeaturePolicy from "mozilla/dom/FeaturePolicy.h";
+using mozilla::dom::Wireframe from "mozilla/dom/DocumentBinding.h";
+using mozilla::PerfStats::MetricMask from "mozilla/PerfStats.h";
+[RefCounted] using class nsIX509Cert from "nsIX509Cert.h";
+using nsIDNSService::ResolverMode from "nsIDNSService.h";
+using mozilla::dom::UserActivation::Modifiers from "mozilla/dom/UserActivation.h";
+
+union ChromeRegistryItem
+{
+ ChromePackage;
+ OverrideMapping;
+ SubstitutionMapping;
+};
+
+namespace mozilla {
+namespace dom {
+
+// SetXPCOMProcessAttributes passes an array of font data to the child,
+// but each platform needs different details so we have platform-specific
+// versions of the SystemFontListEntry type:
+#if defined(ANDROID)
+// Used on Android to pass the list of fonts on the device
+// to the child process
+struct SystemFontListEntry {
+ nsCString familyName;
+ nsCString faceName;
+ nsCString filepath;
+ uint32_t weightRange;
+ uint32_t stretchRange;
+ uint32_t styleRange;
+ uint8_t index;
+ FontVisibility visibility;
+};
+#elif defined(XP_MACOSX)
+// Used on Mac OS X to pass the list of font families (not faces)
+// from chrome to content processes.
+// The entryType field distinguishes several types of font family
+// record; see gfxMacPlatformFontList.h for values and meaning.
+struct SystemFontListEntry {
+ nsCString familyName;
+ FontVisibility visibility;
+ uint8_t entryType;
+};
+#else
+// Used on Linux to pass list of font patterns from chrome to content.
+// (Unused on Windows, but there needs to be a definition of the type.)
+struct SystemFontListEntry {
+ nsCString pattern;
+ bool appFontFamily;
+};
+#endif
+
+#ifdef MOZ_WIDGET_GTK
+struct SystemFontOptions {
+ int32_t antialias; // cairo_antialias_t
+ int32_t subpixelOrder; // cairo_subpixel_order_t
+ int32_t hintStyle; // cairo_hint_style_t
+ int32_t lcdFilter; // cairo_lcd_filter_t
+};
+#endif
+
+struct SystemFontList {
+ SystemFontListEntry[] entries;
+#ifdef MOZ_WIDGET_GTK
+ SystemFontOptions options;
+#endif
+};
+
+union SystemParameterValue {
+ bool;
+ float;
+};
+
+struct ClipboardCapabilities {
+ bool supportsSelectionClipboard;
+ bool supportsFindClipboard;
+ bool supportsSelectionCache;
+};
+
+union FileDescOrError {
+ FileDescriptor;
+ nsresult;
+};
+
+struct DomainPolicyClone
+{
+ bool active;
+ nullable nsIURI[] blocklist;
+ nullable nsIURI[] allowlist;
+ nullable nsIURI[] superBlocklist;
+ nullable nsIURI[] superAllowlist;
+};
+
+struct AndroidSystemInfo
+{
+ nsString device;
+ nsString manufacturer;
+ nsString release_version;
+ nsString hardware;
+ uint32_t sdk_version;
+ bool isTablet;
+};
+
+struct GetFilesResponseSuccess
+{
+ IPCBlob[] blobs;
+};
+
+struct GetFilesResponseFailure
+{
+ nsresult errorCode;
+};
+
+union GetFilesResponseResult
+{
+ GetFilesResponseSuccess;
+ GetFilesResponseFailure;
+};
+
+struct BlobURLRegistrationData
+{
+ nsCString url;
+ IPCBlob blob;
+ nullable nsIPrincipal principal;
+ nsCString partitionKey;
+ bool revoked;
+};
+
+struct JSWindowActorEventDecl
+{
+ nsString name;
+ bool capture;
+ bool systemGroup;
+ bool allowUntrusted;
+ bool? passive;
+ bool createActor;
+};
+
+struct JSWindowActorInfo
+{
+ nsCString name;
+ bool allFrames;
+
+ // True if `url` is for ESM.
+ // False if `url` is for JSM or nothing.
+ bool isESModule;
+
+ // This is to align with JSProcessActorInfo.
+ // This attribute isn't used for JSWindow Actors.
+ bool loadInDevToolsLoader;
+
+ // The module of the url.
+ nsCString? url;
+
+ JSWindowActorEventDecl[] events;
+
+ // Observer notifications this actor listens to.
+ nsCString[] observers;
+ nsString[] matches;
+ nsCString[] remoteTypes;
+ nsString[] messageManagerGroups;
+};
+
+struct JSProcessActorInfo
+{
+ // The name of the actor.
+ nsCString name;
+
+ // True if `url` is for ESM.
+ // False if `url` is for JSM or nothing.
+ bool isESModule;
+
+ // True if the actor should be loaded in the distinct loader dedicated to DevTools.
+ bool loadInDevToolsLoader;
+
+ // The module of the url.
+ nsCString? url;
+
+ // Observer notifications this actor listens to.
+ nsCString[] observers;
+ nsCString[] remoteTypes;
+};
+
+struct GMPAPITags
+{
+ nsCString api;
+ nsCString[] tags;
+};
+
+struct GMPCapabilityData
+{
+ nsCString name;
+ nsCString version;
+ GMPAPITags[] capabilities;
+};
+
+struct L10nFileSourceDescriptor {
+ nsCString name;
+ nsCString metasource;
+ nsCString[] locales;
+ nsCString prePath;
+ nsCString[] index;
+};
+
+struct XPCOMInitData
+{
+ bool isOffline;
+ bool isConnected;
+ int32_t captivePortalState;
+ bool isLangRTL;
+ bool haveBidiKeyboards;
+ nsCString[] dictionaries;
+ ClipboardCapabilities clipboardCaps;
+ DomainPolicyClone domainPolicy;
+ nullable nsIURI userContentSheetURL;
+ GfxVarUpdate[] gfxNonDefaultVarUpdates;
+ ContentDeviceData contentDeviceData;
+ GfxInfoFeatureStatus[] gfxFeatureStatus;
+ nsCString[] appLocales;
+ nsCString[] requestedLocales;
+ L10nFileSourceDescriptor[] l10nFileSources;
+ DynamicScalarDefinition[] dynamicScalarDefs;
+ MetricMask perfStatsMask;
+ nsCString trrDomain;
+ ResolverMode trrMode;
+ // This is the value of network.trr.mode and can be diffrent than trrMode.
+ ResolverMode trrModeFromPref;
+};
+
+struct VisitedQueryResult
+{
+ nullable nsIURI uri;
+ bool visited;
+};
+
+struct StringBundleDescriptor
+{
+ nsCString bundleURL;
+ FileDescriptor mapFile;
+ uint32_t mapSize;
+};
+
+struct IPCURLClassifierFeature
+{
+ nsCString featureName;
+ nsCString[] tables;
+ nsCString exceptionHostList;
+};
+
+// Transport structure for Notifications API notifications
+// (https://developer.mozilla.org/en-US/docs/Web/API/notification) instances
+// used exclusively by the NotificationEvent PContent method.
+struct NotificationEventData
+{
+ nsCString originSuffix;
+ nsCString scope;
+ nsString ID;
+ nsString title;
+ nsString dir;
+ nsString lang;
+ nsString body;
+ nsString tag;
+ nsString icon;
+ nsString data;
+ nsString behavior;
+};
+
+struct PostMessageData
+{
+ MaybeDiscardedBrowsingContext source;
+ nsString origin;
+ nsString targetOrigin;
+ nullable nsIURI targetOriginURI;
+ nullable nsIPrincipal callerPrincipal;
+ nullable nsIPrincipal subjectPrincipal;
+ nullable nsIURI callerURI;
+ bool isFromPrivateWindow;
+ nsCString scriptLocation;
+ uint64_t innerWindowId;
+};
+
+union SyncedContextInitializer
+{
+ BrowsingContextInitializer;
+ WindowContextInitializer;
+};
+
+union BlobURLDataRequestResult
+{
+ IPCBlob;
+ nsresult;
+};
+
+struct TextRecognitionQuad {
+ float confidence;
+ nsString string;
+ ImagePoint[] points;
+};
+
+struct TextRecognitionResult {
+ TextRecognitionQuad[] quads;
+};
+
+union TextRecognitionResultOrError {
+ TextRecognitionResult;
+ nsCString;
+};
+
+struct IPCImage {
+ BigBuffer data;
+ uint32_t stride;
+ SurfaceFormat format;
+ ImageIntSize size;
+};
+
+union PClipboardReadRequestOrError {
+ PClipboardReadRequest;
+ nsresult;
+};
+
+/**
+ * The PContent protocol is a top-level protocol between the UI process
+ * and a content process. There is exactly one PContentParent/PContentChild pair
+ * for each content process.
+ */
+[NestedUpTo=inside_cpow, NeedsOtherPid, ChildProc=Content]
+sync protocol PContent
+{
+ manages PBrowser;
+ manages PClipboardReadRequest;
+ manages PClipboardWriteRequest;
+ manages PContentPermissionRequest;
+ manages PCycleCollectWithLogs;
+ manages PExtensions;
+ manages PExternalHelperApp;
+ manages PHal;
+ manages PHandlerService;
+ manages PHeapSnapshotTempFileHelper;
+ manages PRemotePrintJob;
+ manages PMedia;
+ manages PNecko;
+#ifdef MOZ_WEBSPEECH
+ manages PSpeechSynthesis;
+#endif
+ manages PTestShell;
+ manages PRemoteSpellcheckEngine;
+ manages PWebBrowserPersistDocument;
+#ifdef MOZ_WEBRTC
+ manages PWebrtcGlobal;
+#endif
+ manages PURLClassifier;
+ manages PURLClassifierLocal;
+ manages PScriptCache;
+ manages PSessionStorageObserver;
+ manages PBenchmarkStorage;
+
+ // Depending on exactly how the new browser is being created, it might be
+ // created from either the child or parent process!
+ //
+ // The child creates the PBrowser as part of
+ // BrowserChild::BrowserFrameProvideWindow (which happens when the child's
+ // content calls window.open()), and the parent creates the PBrowser as part
+ // of ContentParent::CreateBrowser.
+ //
+ // When the parent constructs a PBrowser, the child trusts the attributes it
+ // receives from the parent. In that case, the context should be
+ // FrameIPCTabContext.
+ //
+ // When the child constructs a PBrowser, the parent doesn't trust the
+ // attributes it receives from the child. In this case, context must have
+ // type PopupIPCTabContext. The parent checks that if the opener is a
+ // browser element, the context is also for a browser element.
+ //
+ // If |sameTabGroupAs| is non-zero, the new tab should go in the same
+ // TabGroup as |sameTabGroupAs|. This parameter should always be zero
+ // for PBrowser messages sent from the child to the parent.
+ //
+ // Separate messages are used for the parent and child side constructors due
+ // to the differences in data and actor setup required.
+ //
+ // Keep the last 3 attributes in sync with GetProcessAttributes!
+parent:
+ async ConstructPopupBrowser(ManagedEndpoint<PBrowserParent> browserEp,
+ ManagedEndpoint<PWindowGlobalParent> windowEp,
+ TabId tabId, IPCTabContext context,
+ WindowGlobalInit windowInit,
+ uint32_t chromeFlags);
+
+ // TODO: Do I need to make this return something to watch for completion?
+ // Guess we'll see how we end up triggering the actual print, for preview
+ // this should be enough...
+ async CloneDocumentTreeInto(MaybeDiscardedBrowsingContext aSourceBc,
+ MaybeDiscardedBrowsingContext aTargetBc,
+ PrintData aPrintData);
+
+ async UpdateRemotePrintSettings(MaybeDiscardedBrowsingContext aBc,
+ PrintData aPrintData);
+
+ async PExtensions();
+
+child:
+ async ConstructBrowser(ManagedEndpoint<PBrowserChild> browserEp,
+ ManagedEndpoint<PWindowGlobalChild> windowEp,
+ TabId tabId,
+ IPCTabContext context,
+ WindowGlobalInit windowInit,
+ uint32_t chromeFlags, ContentParentId cpId,
+ bool isForBrowser, bool isTopLevel);
+
+both:
+ // For parent->child, aBrowser must be non-null; aContext can
+ // be null to indicate the browser's current root document, or non-null
+ // to persist a subdocument. For child->parent, arguments are
+ // ignored and should be null.
+ async PWebBrowserPersistDocument(nullable PBrowser aBrowser,
+ MaybeDiscardedBrowsingContext aContext);
+
+ async RawMessage(JSActorMessageMeta aMetadata, ClonedMessageData? aData,
+ ClonedMessageData? aStack);
+
+child:
+ async InitGMPService(Endpoint<PGMPServiceChild> service);
+ async InitProcessHangMonitor(Endpoint<PProcessHangMonitorChild> hangMonitor);
+ async InitProfiler(Endpoint<PProfilerChild> aEndpoint);
+
+ // Give the content process its endpoints to the compositor.
+ async InitRendering(
+ Endpoint<PCompositorManagerChild> compositor,
+ Endpoint<PImageBridgeChild> imageBridge,
+ Endpoint<PVRManagerChild> vr,
+ Endpoint<PRemoteDecoderManagerChild> video,
+ uint32_t[] namespaces);
+
+ // Re-create the rendering stack using the given endpoints. This is sent
+ // after the compositor process has crashed. The new endpoints may be to a
+ // newly launched GPU process, or the compositor thread of the UI process.
+ async ReinitRendering(
+ Endpoint<PCompositorManagerChild> compositor,
+ Endpoint<PImageBridgeChild> bridge,
+ Endpoint<PVRManagerChild> vr,
+ Endpoint<PRemoteDecoderManagerChild> video,
+ uint32_t[] namespaces);
+
+ async NetworkLinkTypeChange(uint32_t type);
+ async SocketProcessCrashed();
+
+ // Re-create the rendering stack for a device reset.
+ async ReinitRenderingForDeviceReset();
+
+ /**
+ * Enable system-level sandboxing features, if available. Can
+ * usually only be performed zero or one times. The child may
+ * abnormally exit if this fails; the details are OS-specific.
+ */
+ async SetProcessSandbox(FileDescriptor? aBroker);
+
+ async RequestMemoryReport(uint32_t generation,
+ bool anonymize,
+ bool minimizeMemoryUsage,
+ FileDescriptor? DMDFile)
+ returns (uint32_t aGeneration);
+
+#if defined(XP_WIN)
+ /**
+ * Used by third-party modules telemetry (aka "untrusted modules" telemetry)
+ * to pull data from content processes.
+ */
+ async GetUntrustedModulesData() returns (UntrustedModulesData? data);
+
+ /**
+ * This method is used to notifty a child process to start
+ * processing module loading events in UntrustedModulesProcessor.
+ * This should be called when the parent process has gone idle.
+ */
+ async UnblockUntrustedModulesThread();
+#endif // defined(XP_WIN)
+
+ /**
+ * Communication between the PuppetBidiKeyboard and the actual
+ * BidiKeyboard hosted by the parent
+ */
+ async BidiKeyboardNotify(bool isLangRTL, bool haveBidiKeyboards);
+
+ /**
+ * Dump this process's GC and CC logs to the provided files.
+ *
+ * For documentation on the other args, see dumpGCAndCCLogsToFile in
+ * nsIMemoryInfoDumper.idl
+ */
+ async PCycleCollectWithLogs(bool dumpAllTraces,
+ FileDescriptor gcLog,
+ FileDescriptor ccLog);
+
+ async PTestShell();
+
+ async PScriptCache(FileDescOrError cacheFile, bool wantCacheData);
+
+ async RegisterChrome(ChromePackage[] packages, SubstitutionMapping[] substitutions,
+ OverrideMapping[] overrides, nsCString locale, bool reset);
+ async RegisterChromeItem(ChromeRegistryItem item);
+
+ async ClearImageCacheFromPrincipal(nullable nsIPrincipal aPrincipal);
+
+ async ClearImageCacheFromBaseDomain(nsCString aBaseDomain);
+
+ async ClearImageCache(bool privateLoader, bool chrome);
+
+ async ClearStyleSheetCache(nullable nsIPrincipal? aForPrincipal,
+ nsCString? aBaseDomain);
+
+ async SetOffline(bool offline);
+ async SetConnectivity(bool connectivity);
+ async SetCaptivePortalState(int32_t aState);
+ async SetTRRMode(ResolverMode aMode, ResolverMode aModeFromPref);
+
+ async NotifyVisited(VisitedQueryResult[] uri);
+
+ /**
+ * Tell the child that the system theme has changed, and that a repaint is
+ * necessary.
+ */
+ async ThemeChanged(FullLookAndFeel lookAndFeelData, ThemeChangeKind aKind);
+
+ async PreferenceUpdate(Pref pref);
+ async VarUpdate(GfxVarUpdate var);
+
+ async UpdatePerfStatsCollectionMask(uint64_t aMask);
+ async CollectPerfStatsJSON() returns (nsCString aStats);
+
+ async CollectScrollingMetrics() returns (uint32_t pixelsScrolled, uint32_t scrollDurationMS);
+
+ async NotifyAlertsObserver(nsCString topic, nsString data);
+
+ async GeolocationUpdate(nullable nsIDOMGeoPosition aPosition);
+
+ async GeolocationError(uint16_t errorCode);
+
+ async UpdateDictionaryList(nsCString[] dictionaries);
+
+ async UpdateFontList(SystemFontList fontList);
+
+ /**
+ * The shared font list has been updated by the parent, so child processes
+ * should globally reflow everything to pick up new character coverage etc.
+ * If aFullRebuild is true, child processes must discard and recreate
+ * their mappings to the shmem blocks, as those are no longer valid.
+ * This message has raised priority so that it will take precedence over
+ * vsync messages to the child.
+ */
+ [Priority=mediumhigh] async RebuildFontList(bool aFullRebuild);
+
+ /**
+ * The shared font list has been modified, potentially adding matches
+ * for src:local() names that were previously not known, so content
+ * may need to be reflowed.
+ */
+ async FontListChanged();
+
+ /**
+ * The font list or prefs have been updated in such a way that we might need
+ * to do a reflow and maybe reframe.
+ */
+ async ForceGlobalReflow(bool aNeedsReframe);
+
+ /**
+ * A new shmem block has been added to the font list; the child process
+ * should map the new block and add to its index.
+ */
+ async FontListShmBlockAdded(uint32_t aGeneration, uint32_t aIndex,
+ SharedMemoryHandle aHandle);
+
+ async UpdateAppLocales(nsCString[] appLocales);
+ async UpdateRequestedLocales(nsCString[] requestedLocales);
+
+ /**
+ * The system timezone has changed; the child process should ensure that
+ * calls to get the default timezone return the new value.
+ */
+ async SystemTimezoneChanged();
+
+ async UpdateL10nFileSources(L10nFileSourceDescriptor[] sources);
+
+ async RegisterStringBundles(StringBundleDescriptor[] stringBundles);
+
+ async UpdateSharedData(FileDescriptor mapFile, uint32_t aSize,
+ IPCBlob[] blobs,
+ nsCString[] changedKeys);
+
+ // nsIPermissionManager messages
+ async AddPermission(Permission permission);
+ async RemoveAllPermissions();
+
+ async FlushMemory(nsString reason);
+
+ async ApplicationBackground();
+ async ApplicationForeground();
+ async GarbageCollect();
+ async CycleCollect();
+ async UnlinkGhosts();
+
+ /**
+ * Start accessibility engine in content process.
+ */
+ async ActivateA11y();
+
+ /**
+ * Shutdown accessibility engine in content process (if not in use).
+ */
+ async ShutdownA11y();
+
+ async AppInfo(nsCString version, nsCString buildID, nsCString name, nsCString UAName,
+ nsCString ID, nsCString vendor, nsCString sourceURL, nsCString updateURL);
+
+ /**
+ * Send the remote type associated with the content process.
+ */
+ async RemoteType(nsCString aRemoteType, nsCString aProfile);
+
+ /**
+ * Send BlobURLRegistrationData to child process.
+ */
+ async InitBlobURLs(BlobURLRegistrationData[] registrations);
+
+ /**
+ * Send JS{Content, Window}ActorInfos to child process.
+ */
+ async InitJSActorInfos(JSProcessActorInfo[] aContentInfos, JSWindowActorInfo[] aWindowInfos);
+
+ /**
+ * Unregister a previously registered JSWindowActor in the child process.
+ */
+ async UnregisterJSWindowActor(nsCString name);
+
+ /**
+ * Unregister a previously registered JSProcessActor in the child process.
+ */
+ async UnregisterJSProcessActor(nsCString name);
+
+ async SetXPCOMProcessAttributes(XPCOMInitData xpcomInit,
+ StructuredCloneData initialData,
+ FullLookAndFeel lookAndFeeldata,
+ /* used on MacOSX/Linux/Android only: */
+ SystemFontList systemFontList,
+ SharedMemoryHandle? sharedUASheetHandle,
+ uintptr_t sharedUASheetAddress,
+ SharedMemoryHandle[] sharedFontListBlocks,
+ bool aIsStartingUp);
+
+ // Notify child that last-pb-context-exited notification was observed
+ async LastPrivateDocShellDestroyed();
+
+ async NotifyProcessPriorityChanged(ProcessPriority priority);
+ async MinimizeMemoryUsage();
+
+ /**
+ * Used to manage nsIStyleSheetService across processes.
+ */
+ async LoadAndRegisterSheet(nullable nsIURI uri, uint32_t type);
+ async UnregisterSheet(nullable nsIURI uri, uint32_t type);
+
+ /**
+ * Notify idle observers in the child
+ */
+ async NotifyIdleObserver(uint64_t observerId, nsCString topic, nsString str);
+
+ async InvokeDragSession(MaybeDiscardedWindowContext aSourceWindowContext,
+ MaybeDiscardedWindowContext aSourceTopWindowContext,
+ IPCTransferableData[] transfers, uint32_t action);
+
+ async UpdateDragSession(IPCTransferableData[] transfers, EventMessage message);
+
+ async EndDragSession(bool aDoneDrag, bool aUserCancelled,
+ LayoutDeviceIntPoint aDragEndPoint,
+ uint32_t aKeyModifiers,
+ uint32_t aDropEffect);
+
+ async DomainSetChanged(uint32_t aSetType, uint32_t aChangeType, nullable nsIURI aDomain);
+
+ /**
+ * Notify the child that it will soon be asked to shutdown.
+ * This is sent with high priority right before the normal shutdown.
+ */
+ [Priority=control] async ShutdownConfirmedHP();
+
+ /**
+ * Notify the child to shutdown. The child will in turn call FinishShutdown
+ * and let the parent close the channel.
+ */
+ async Shutdown();
+
+ async LoadProcessScript(nsString url);
+
+ /**
+ * Requests a full native update of a native plugin child window. This is
+ * a Windows specific call.
+ */
+ async UpdateWindow(uintptr_t aChildId);
+
+ /**
+ * Notify the child that cache is emptied.
+ */
+ async NotifyEmptyHTTPCache();
+
+ /**
+ * Send a `push` event without data to a service worker in the child.
+ */
+ async Push(nsCString scope, nullable nsIPrincipal principal, nsString messageId);
+
+ /**
+ * Send a `push` event with data to a service worker in the child.
+ */
+ async PushWithData(nsCString scope, nullable nsIPrincipal principal,
+ nsString messageId, uint8_t[] data);
+
+ /**
+ * Send a `pushsubscriptionchange` event to a service worker in the child.
+ */
+ async PushSubscriptionChange(nsCString scope, nullable nsIPrincipal principal);
+
+ async GetFilesResponse(nsID aID, GetFilesResponseResult aResult);
+
+ async BlobURLRegistration(nsCString aURI, IPCBlob aBlob,
+ nullable nsIPrincipal aPrincipal, nsCString aPartitionKey);
+
+ async BlobURLUnregistration(nsCString aURI);
+
+ async GMPsChanged(GMPCapabilityData[] capabilities);
+
+
+ async ProvideAnonymousTemporaryFile(uint64_t aID, FileDescOrError aFD);
+
+ async SetPermissionsWithKey(nsCString aPermissionKey, Permission[] aPermissions);
+
+ async RefreshScreens(ScreenDetails[] aScreens);
+
+ async ShareCodeCoverageMutex(CrossProcessMutexHandle handle);
+ async FlushCodeCoverageCounters() returns (bool unused);
+
+ /*
+ * IPC message to enable the input event queue on the main thread of the
+ * content process.
+ */
+ async SetInputEventQueueEnabled();
+
+ /*
+ * IPC message to flush the input event queue on the main thread of the
+ * content process.
+ *
+ * When the ContentParent stops sending the input event with input priority,
+ * there may be some pending events in the input event queue and normal
+ * event queue. Here is a possible scenario.
+ * R: Runnables.
+ * D: Enable the input priority event.
+ * E: Disable the input priority evnet.
+ *
+ * D E
+ * Normal Queue: R1 R2 R3
+ * Input Queue: II I2 I3
+ *
+ * To avoid the newly added normal events (e.g. R2, which may be an input
+ * event) preempt the pending input events (e.g. I1), or the newly added
+ * input events (e.g. I3) preempt the pending normal events (e.g. R2), we
+ * have to flush all pending events before enabling and disabling the input
+ * priority event.
+ *
+ * To flush the normal event queue and the input event queue, we use three
+ * IPC messages as the followings.
+ * FI: Flush the input queue.
+ * SI: Suspend the input queue.
+ * RI: Resume the input queue.
+ *
+ * Normal Queue: R1 FI RI R2 FI RI R3
+ * Input Queue: II SI I2 SI I3
+ *
+ * When the flush input request is processed before the other two requests,
+ * we consume all input events until the suspend request. After handling the
+ * suspend request, we stop consuming the input events until the resume
+ * request to make sure we consume all pending normal events.
+ *
+ * If we process the suspend request before the other two requests, we
+ * ignore the flush request and consume all pending normal events until the
+ * resume request.
+ */
+ async FlushInputEventQueue();
+
+ /*
+ * IPC message to resume consuming the pending events in the input event
+ * queue.
+ */
+ async ResumeInputEventQueue();
+
+ /*
+ * IPC message to suspend consuming the pending events in the input event
+ * queue.
+ */
+ [Priority=input] async SuspendInputEventQueue();
+
+ /*
+ * IPC message to propagate dynamic scalar definitions, added after the
+ * content process is spawned, from the parent to the child.
+ * Dynamic scalar definitions added at the process startup are handled
+ * using the |TelemetryIPC::AddDynamicScalarDefinitions| functions.
+ */
+ async AddDynamicScalars(DynamicScalarDefinition[] definitions);
+
+ // This message is sent to content processes, and triggers the creation of a
+ // new HttpChannelChild that will be connected to the parent channel
+ // represented by registrarId.
+ // This is on PContent not PNecko, as PNecko may not be initialized yet.
+ // The returned loadInfo needs to be set on the channel - since the channel
+ // moved to a new process it now has different properties.
+
+ async CrossProcessRedirect(RedirectToRealChannelArgs args,
+ Endpoint<PStreamFilterParent>[] aEndpoint)
+ returns (nsresult rv);
+
+ /**
+ * This method is used to notifty content process to start delayed autoplay
+ * media via browsing context.
+ */
+ async StartDelayedAutoplayMediaComponents(MaybeDiscardedBrowsingContext aContext);
+
+ /**
+ * This method is used to dispatch MediaControlAction to content process in
+ * order to control media within a specific browsing context tree.
+ */
+ async UpdateMediaControlAction(MaybeDiscardedBrowsingContext aContext,
+ MediaControlAction aAction);
+
+ // Begin subscribing to a new BrowsingContextGroup, sending down the current
+ // value for every individual BrowsingContext.
+ async RegisterBrowsingContextGroup(uint64_t aGroupId, SyncedContextInitializer[] aInits);
+
+ // The BrowsingContextGroup has been destroyed in the parent process. The
+ // content process won't destroy the group until it receives this message or
+ // during shutdown.
+ //
+ // When the content process receives this message, all contexts in the group
+ // should have already been destroyed.
+ async DestroyBrowsingContextGroup(uint64_t aGroupId);
+
+#if defined(MOZ_SANDBOX) && defined(MOZ_DEBUG) && defined(ENABLE_TESTS)
+ // Initialize top-level actor for testing content process sandbox.
+ async InitSandboxTesting(Endpoint<PSandboxTestingChild> aEndpoint);
+#endif
+
+ async LoadURI(MaybeDiscardedBrowsingContext aContext, nsDocShellLoadState aLoadState, bool aSetNavigating)
+ returns (bool aSuccess);
+
+ async InternalLoad(nsDocShellLoadState aLoadState);
+
+ async DisplayLoadError(MaybeDiscardedBrowsingContext aContext, nsString aURI);
+
+ async GoBack(MaybeDiscardedBrowsingContext aContext, int32_t? aCancelContentJSEpoch, bool aRequireUserInteraction, bool aUserActivation);
+ async GoForward(MaybeDiscardedBrowsingContext aContext, int32_t? aCancelContentJSEpoch, bool aRequireUserInteraction, bool aUserActivation);
+ async GoToIndex(MaybeDiscardedBrowsingContext aContext, int32_t aIndex, int32_t? aCancelContentJSEpoch, bool aUserActivation);
+ async Reload(MaybeDiscardedBrowsingContext aContext, uint32_t aReloadFlags);
+ async StopLoad(MaybeDiscardedBrowsingContext aContext, uint32_t aStopFlags);
+
+ async OnAllowAccessFor(MaybeDiscardedBrowsingContext aParentContext,
+ nsCString aTrackingOrigin,
+ uint32_t aCookieBehavior,
+ StorageAccessPermissionGrantedReason aReason);
+
+ async OnContentBlockingDecision(MaybeDiscardedBrowsingContext aContext,
+ BlockingDecision aReason,
+ uint32_t aRejectedReason);
+
+ /**
+ * Abort orientationPendingPromises for documents in the child which
+ * are part of a BrowsingContextGroup.
+ */
+ async AbortOrientationPendingPromises(MaybeDiscardedBrowsingContext aContext);
+
+ async HistoryCommitIndexAndLength(MaybeDiscardedBrowsingContext aContext,
+ uint32_t aIndex, uint32_t aLength,
+ nsID aChangeID);
+
+ async GetLayoutHistoryState(MaybeDiscardedBrowsingContext aContext)
+ returns (nullable nsILayoutHistoryState aState, Wireframe? aWireframe);
+
+ async DispatchLocationChangeEvent(MaybeDiscardedBrowsingContext aContext);
+
+ // Dispatches a "beforeunload" event to each in-process content window in the
+ // subtree beginning at `aStartingAt`, and returns the result as documented in
+ // the `PermitUnloadResult` enum.
+ async DispatchBeforeUnloadToSubtree(MaybeDiscardedBrowsingContext aStartingAt)
+ returns (PermitUnloadResult result);
+
+ // Update the cached list of codec supported in the given process.
+ async UpdateMediaCodecsSupported(RemoteDecodeIn aLocation, MediaCodecsSupported aSupported);
+
+ // Send the list of the supported mimetypes in the given process. GeckoView-specific
+ async DecoderSupportedMimeTypes(nsCString[] supportedTypes);
+
+ // Used to initialize the global variable in content processes with the
+ // latched value in the parent process. See dom/LocalStorageCommon.h for more
+ // details.
+ async InitNextGenLocalStorageEnabled(bool enabled);
+
+ async PRemotePrintJob();
+
+ async PClipboardReadRequest(nsCString[] aTypes);
+
+parent:
+ async SynchronizeLayoutHistoryState(MaybeDiscardedBrowsingContext aContext,
+ nullable nsILayoutHistoryState aState);
+
+ async SessionHistoryEntryTitle(MaybeDiscardedBrowsingContext aContext,
+ nsString aTitle);
+
+ async SessionHistoryEntryScrollRestorationIsManual(MaybeDiscardedBrowsingContext aContext,
+ bool aIsManual);
+ async SessionHistoryEntryScrollPosition(MaybeDiscardedBrowsingContext aContext,
+ int32_t aX, int32_t aY);
+
+ async SessionHistoryEntryCacheKey(MaybeDiscardedBrowsingContext aContext,
+ uint32_t aCacheKey);
+
+ async SessionHistoryEntryStoreWindowNameInContiguousEntries(MaybeDiscardedBrowsingContext aContext,
+ nsString aName);
+
+ async SessionHistoryEntryWireframe(MaybeDiscardedBrowsingContext aContext,
+ Wireframe aWireframe);
+
+ async GetLoadingSessionHistoryInfoFromParent(MaybeDiscardedBrowsingContext aContext)
+ returns (LoadingSessionHistoryInfo? aLoadingInfo);
+
+ async RemoveFromBFCache(MaybeDiscardedBrowsingContext aContext);
+
+ async InitBackground(Endpoint<PBackgroundStarterParent> aEndpoint);
+
+ async CreateGMPService();
+
+ async InitStreamFilter(uint64_t channelId, nsString addonId)
+ returns (Endpoint<PStreamFilterChild> aEndpoint);
+
+ async PRemoteSpellcheckEngine();
+
+ async InitCrashReporter(NativeThreadId tid);
+
+ sync IsSecureURI(nullable nsIURI aURI, OriginAttributes aOriginAttributes)
+ returns (bool isSecureURI);
+
+ async AccumulateMixedContentHSTS(nullable nsIURI aURI, bool aActive,
+ OriginAttributes aOriginAttributes);
+
+ [Nested=inside_cpow] async PHal();
+
+ async PHeapSnapshotTempFileHelper();
+
+ async PNecko();
+
+#ifdef MOZ_WEBSPEECH
+ async PSpeechSynthesis();
+#endif
+
+ async PMedia();
+
+#ifdef MOZ_WEBRTC
+ async PWebrtcGlobal();
+#endif
+
+ async CreateAudioIPCConnection() returns (FileDescOrError fd);
+
+ sync PURLClassifier(nullable nsIPrincipal principal)
+ returns (bool success);
+
+ async PURLClassifierLocal(nullable nsIURI uri, IPCURLClassifierFeature[] features);
+
+ async PSessionStorageObserver();
+
+ async PBenchmarkStorage();
+
+ // Services remoting
+
+ async StartVisitedQueries(nullable nsIURI[] uri);
+ async SetURITitle(nullable nsIURI uri, nsString title);
+
+ async LoadURIExternal(nullable nsIURI uri,
+ nullable nsIPrincipal triggeringPrincipal,
+ nullable nsIPrincipal redirectPrincipal,
+ MaybeDiscardedBrowsingContext browsingContext,
+ bool wasExternallyTriggered,
+ bool hasValidUserGestureActivation);
+ async ExtProtocolChannelConnectParent(uint64_t registrarId);
+
+ sync SyncMessage(nsString aMessage, ClonedMessageData aData)
+ returns (StructuredCloneData[] retval);
+
+ async ShowAlert(nullable nsIAlertNotification alert);
+
+ async CloseAlert(nsString name, bool contextClosed);
+
+ async DisableNotifications(nullable nsIPrincipal principal);
+
+ async OpenNotificationSettings(nullable nsIPrincipal principal);
+
+ async AddSecurityState(MaybeDiscardedWindowContext aContext, uint32_t aStateFlags);
+
+ // Request that the ServiceWorkerManager in the parent process create a
+ // notification "click" or "close" event and dispatch it on the relevant
+ // ServiceWorker. This needs to happen because when a notification is
+ // created it is tied to a specific content process and when the user clicks
+ // on the notification, it will be that content process that is notified.
+ // However, even if the ServiceWorker lives in that process (it may no
+ // longer be in that process, or may have never lived there), the right/only
+ // way to talk through the ServiceWorker is through the parent.
+ //
+ // This happens on PContent because the ServiceWorkerManager lives on the
+ // main thread and bouncing this off of PBackground would be silly and
+ // complex. In the long run, the notification implementation will be
+ // overhauled to directly process the notification click/close and directly
+ // translate that to a ServiceWorker event.
+ async NotificationEvent(nsString type, NotificationEventData data);
+
+ // Creates a helper for forwarding data from an nsExternalAppHandler
+ // running in the content process, to one running in the parent
+ // process.
+ // Bug 1574372 aims to run nsExternalAppHandler entirely in the
+ // parent so that we can remove this.
+ //
+ // Serializes the uri, loadInfo, contentType, referrer, contentDisposition
+ // headers and contentLength of the channel so that we can make them
+ // available to the parent instance via a nsIChannel helper. Also
+ // passes whether the original channel was an instance of nsIFileChannel.
+ //
+ // aContext is the BrowsingContext that initiated the load, and created the
+ // channel.
+ //
+ // Pass true for aForceSave to always save this content to disk, regardless of
+ // nsIMIMEInfo and other such influences.
+ // Pass true for aShouldCloseWindow to specify that aContext was opened specifically
+ // for this load, and should be closed once we've handled it.
+ async PExternalHelperApp(nullable nsIURI uri,
+ LoadInfoArgs loadInfoArgs,
+ nsCString aMimeContentType,
+ nsCString aContentDisposition,
+ uint32_t aContentDispositionHint,
+ nsString aContentDispositionFilename,
+ bool aForceSave,
+ int64_t aContentLength,
+ bool aWasFileChannel,
+ nullable nsIURI aReferrer,
+ MaybeDiscardedBrowsingContext aContext,
+ bool aShouldCloseWindow);
+
+ async PHandlerService();
+
+ async AddGeolocationListener(bool highAccuracy);
+ async RemoveGeolocationListener();
+ async SetGeolocationHigherAccuracy(bool enable);
+
+ async ConsoleMessage(nsString message);
+ async ScriptErrorWithStack(nsString message, nsString sourceName, nsString sourceLine,
+ uint32_t lineNumber, uint32_t colNumber, uint32_t flags,
+ nsCString category, bool privateWindow,
+ bool fromChromeContext, ClonedMessageData stack);
+
+ // Places the items within dataTransfer on the clipboard.
+ async SetClipboard(IPCTransferable aTransferable,
+ int32_t aWhichClipboard);
+
+ // Given a list of supported types, returns the clipboard data for the
+ // first type that matches.
+ // aRequestingWindowContext is the window that is requesting the clipboard,
+ // which is used for content analysis.
+ sync GetClipboard(nsCString[] aTypes, int32_t aWhichClipboard,
+ MaybeDiscardedWindowContext aRequestingWindowContext)
+ returns (IPCTransferableData transferableData);
+
+ // Returns a list of formats supported by the clipboard
+ sync GetExternalClipboardFormats(int32_t aWhichClipboard, bool aPlainTextOnly) returns (nsCString[] aTypes);
+
+ // Requests getting data from clipboard.
+ async GetClipboardAsync(nsCString[] aTypes, int32_t aWhichClipboard,
+ MaybeDiscardedWindowContext aRequestingWindowContext,
+ nsIPrincipal aRequestingPrincipal)
+ returns (PClipboardReadRequestOrError aClipboardReadRequest);
+
+ // Clears the clipboard.
+ async EmptyClipboard(int32_t aWhichClipboard);
+
+ // Returns true if data of one of the specified types is on the clipboard.
+ sync ClipboardHasType(nsCString[] aTypes, int32_t aWhichClipboard)
+ returns (bool hasType);
+
+ /**
+ * Notify the parent that the child has started a clipboard write request,
+ * and that the data will be sent over another IPC message once it is ready.
+ * @param aClipboardType
+ * The clipboard type defined in nsIClipboard.
+ */
+ async PClipboardWriteRequest(int32_t aClipboardType);
+
+ sync GetIconForExtension(nsCString aFileExt, uint32_t aIconSize)
+ returns (uint8_t[] bits);
+
+ // Tell the parent that the child has gone idle for the first time.
+ async FirstIdle();
+
+ async CopyFavicon(nullable nsIURI oldURI, nullable nsIURI newURI, bool isPrivate);
+
+ async FindImageText(IPCImage image, nsCString[] languages)
+ returns (TextRecognitionResultOrError result);
+
+ // Graphics errors
+ async GraphicsError(nsCString aError);
+
+ // Driver crash guards. aGuardType must be a member of CrashGuardType.
+ sync BeginDriverCrashGuard(uint32_t aGuardType) returns (bool crashDetected);
+ sync EndDriverCrashGuard(uint32_t aGuardType);
+
+ async AddIdleObserver(uint64_t observerId, uint32_t idleTimeInS);
+ async RemoveIdleObserver(uint64_t observerId, uint32_t idleTimeInS);
+
+ /**
+ * This message is only used on X11 platforms.
+ *
+ * Send a dup of the plugin process's X socket to the parent
+ * process. In theory, this scheme keeps the plugin's X resources
+ * around until after both the plugin process shuts down *and* the
+ * parent process closes the dup fd. This is used to prevent the
+ * parent process from crashing on X errors if, e.g., the plugin
+ * crashes *just before* a repaint and the parent process tries to
+ * use the newly-invalid surface.
+ */
+ async BackUpXResources(FileDescriptor aXSocketFd);
+
+ async RequestAnonymousTemporaryFile(uint64_t aID);
+
+ /**
+ * Notifies the parent that the child needs no more ForceKill dump.
+ */
+ [Priority=control] async NotifyShutdownSuccess();
+
+ /**
+ * Notifies the parent to continue shutting down after the child performs
+ * its shutdown tasks.
+ */
+ async FinishShutdown();
+
+ async UpdateDropEffect(uint32_t aDragAction, uint32_t aDropEffect);
+
+ /**
+ * Initiates an asynchronous request for permission for the
+ * provided principal.
+ *
+ * @param aRequests
+ * The array of permissions to request.
+ * @param aPrincipal
+ * The principal of the request.
+ * @param aTopLevelPrincipal
+ * The principal of the top level page the request comes from.
+ * @param tabId
+ * To identify which tab issues this request.
+ *
+ * NOTE: The principal is untrusted in the parent process. Only
+ * principals that can live in the content process should
+ * provided.
+ */
+ async PContentPermissionRequest(PermissionRequest[] aRequests,
+ nullable nsIPrincipal aPrincipal,
+ nullable nsIPrincipal aTopLevelPrincipal,
+ bool aIsHandlingUserInput,
+ bool aMaybeUnsafePermissionDelegate,
+ TabId tabId);
+
+ /**
+ * If the profiler is running when the process shuts down, this sends the
+ * profile data collected so far.
+ *
+ * @param aProfile
+ * This may contain an empty string (unknown issue), an error message
+ * starting with '*', or a profile as a stringified JSON object.
+ */
+ async ShutdownProfile(nsCString aProfile);
+
+ /**
+ * This sends any collected perf stats data on shutdown.
+ */
+ async ShutdownPerfStats(nsCString aPerfStats);
+
+ /**
+ * A shared font list (see gfx/thebes/SharedFontList.*) contains a list
+ * of shared-memory blocks that are used to store all the font list data.
+ * The font list created in the parent process is the only one that can
+ * create or store objects into the shared memory; content processes font
+ * lists have read-only access to it.
+ *
+ * To minimize the cost of record allocations, the shared font list
+ * bump-allocates new objects that it adds to the shared memory blocks
+ * (i.e. the records stored in the shared memory blocks are only ever
+ * appended, and never freed except when the entire font list is
+ * reconstructed).
+ *
+ * When initially created by the parent process, the font list may contain
+ * nothing except a header, and the list of the system's installed font
+ * family names. Additional data about the families (styled faces available
+ * and character coverage) is appended to the font list during the session
+ * as a given font is considered for use, because loading all data for all
+ * installed fonts during startup is too expensive/slow.
+ *
+ * During content process launch, a content process's first step in
+ * gaining access to the font list is to call GetFontListShmBlock,
+ * passing index zero in order to get access to the first block, which
+ * contains the font list header and the list of font-family records
+ * (which may be virtually all uninitialized at this time, containing
+ * nothing but the family names). Once a content process determines a
+ * font-family name it wants to use (e.g. from a CSS font-family list, or
+ * from preferences), if that Family record has not yet been initialized,
+ * it will call InitializeFamily (below) to have the parent process
+ * populate Face records in the shared memory with the family's styles.
+ * The content process can then pick the face with best style match from
+ * the available faces according to the CSS font matching algorithm, load
+ * its character map, then send the map to the parent process using
+ * SetCharacterMap (so that the parent process can share the map with all
+ * processes to avoid duplication of work).
+ *
+ * At some point, as the parent process adds data to the font list, a new
+ * shared-memory block will probably be needed. At that point the parent
+ * will create a new block and append it to its share memory block list.
+ * The new Block index will start to appear in Pointer records in the
+ * shared memory, and the content process's can then fetch those other
+ * blocks using this function as needed.
+ *
+ * @param aGeneration
+ * The font list has a Generation ID stored in its Header, and any time
+ * the parent process needs to reinitialize the list (because of a change
+ * in the available font repertoire) a new Generation ID is assigned.
+ * Content processes pass the Generation of the list they're using in
+ * all messages, so that the parent can recognize if they're out of date
+ * and safely ignore such messages. (When the parent rebuilds the list,
+ * it will notify all content processes, but they may still send a few
+ * messages that relate to the obsolete list before they have processed
+ * this notification.)
+ * @param aIndex
+ * (Zero-based) index of the shared-memory block to be mapped.
+ * In a typical case, there will be a handful of blocks altogether, so
+ * each content process only needs to make this request a few times.
+ * @returns aHandle
+ * Handle that can be used to construct a SharedMemory that maps the
+ * requested block of memory.
+ * If aGeneration does not match the parent's font list generation ID, or
+ * if requesting a block that does not exist (i.e. with aIndex greater
+ * than or equal to the number of blocks actually in existence), returns
+ * a null handle.
+ *
+ * This is a sync message because the content process needs font data in
+ * order to perform font-matching (e.g. during reflow), and cannot continue
+ * until it has mapped the font-list memory.
+ */
+ sync GetFontListShmBlock(uint32_t aGeneration, uint32_t aIndex)
+ returns (SharedMemoryHandle aHandle);
+
+ /**
+ * Ask the parent to initialize a given font family, so that face metadata
+ * will be available. Content processes will only call this for families
+ * where the Face data has not yet been populated, so it will generally be
+ * called no more than once per family. (It may not be needed at all, if
+ * the parent process has already initialized the families that content
+ * wants to use.)
+ *
+ * @param aGeneration
+ * Font-list generation, so requests relating to an obsolete list can be
+ * ignored (see comments for GetFontListShmBlock).
+ * @param aFamilyIndex
+ * The 0-based index of the Family within the font-list that a content
+ * process needs to use.
+ * @param aLoadCmaps
+ * If true, the parent should eagerly load character maps for the faces
+ * in the family.
+ *
+ * This is a sync message because the content process cannot complete its
+ * font-matching until the family is fully populated with Face records.
+ * If we make it async, content processes will reflow using fallbacks,
+ * and then have to reflow again once all the font information needed
+ * becomes available.
+ */
+ sync InitializeFamily(uint32_t aGeneration, uint32_t aFamilyIndex,
+ bool aLoadCmaps);
+
+ /**
+ * Record the character map of a given Face in the font list.
+ *
+ * @param aGeneration
+ * Font-list generation, so requests relating to an obsolete list can be
+ * ignored (see comments for GetFontListShmBlock).
+ * @param aFamilyIndex
+ * Index of the font family in the font list (see aAlias for which list).
+ * @param aAlias
+ * Whether aFamilyIndex refers to the Families() or AliasFamilies() list.
+ * @param aFaceIndex
+ * Index of the face within the family's Faces() list.
+ * @param aMap
+ * The character coverage map of the face. (This will be stored as a
+ * SharedBitSet record within the shared font list, and the Face record
+ * will be updated to reference it.)
+ */
+ async SetCharacterMap(uint32_t aGeneration, uint32_t aFamilyIndex, bool aAlias,
+ uint32_t aFaceIndex, gfxSparseBitSet aMap);
+
+ /**
+ * Ask the parent to set up the merged charmap for a family, to accelerate
+ * future fallback searches.
+ * aFamilyIndex may refer to an element in either Families() or AliasFamilies(),
+ * with aAlias determining which.
+ */
+ async SetupFamilyCharMap(uint32_t aGeneration, uint32_t aFamilyIndex, bool aAlias);
+
+ /**
+ * Ask the parent to try and complete the InitOtherFamilyNames task, because
+ * we're trying to look up a localized font name. This is a sync method so that
+ * the update will be available before the child continues reflow; however, it
+ * is possible the task will have timed-out in the parent and not actually
+ * completed during this call.
+ *
+ * @param aGeneration
+ * Font-list generation, so requests relating to an obsolete list can be
+ * ignored (see comments for GetFontListShmBlock).
+ * @param aDefer
+ * Parameter aDeferOtherFamilyNamesLoading to be passed to
+ * gfxPlatformFontList::InitOtherFamilyNames, to determine whether name
+ * loading should be deferred to a background task or run immediately.
+ * @param aLoaded
+ * Returns whether the font name loading process has completed.
+ *
+ * TODO: This is currently a sync message but can probably be made async,
+ * at the cost of an increased chance of some testcases failing because
+ * they depend on lazily-loaded font names.
+ */
+ sync InitOtherFamilyNames(uint32_t aGeneration, bool aDefer) returns (bool aLoaded);
+
+ /**
+ * Ask the parent to load all font character maps, as we need to do an
+ * exhaustive font-fallback search. This is done asynchronously; when it
+ * finishes, the parent will trigger global reflow so that font selection
+ * is re-done in all content, making use of the newly-loaded cmaps.
+ * Normally this will only happen once per browser session (unless the
+ * font list is rebuilt due to installation/removal of system fonts).
+ *
+ * @param aGeneration
+ * Font-list generation, so requests relating to an obsolete list can be
+ * ignored (see comments for GetFontListShmBlock).
+ * @param aStartIndex
+ * The family index to start from; the sender has determined that cmaps
+ * up to this point are already loaded.
+ */
+ async StartCmapLoading(uint32_t aGeneration, uint32_t aStartIndex);
+
+ /**
+ * Ask the parent for a specific hyphenation resource (identified by URI)
+ * as a shared memory block.
+ *
+ * This is a sync method because at the point where a content process finds
+ * that it requires a particular hyphenation dictionary, this is blocking
+ * reflow; making it async would require scheduling another reflow after
+ * the resource is available, and a possible layout "jump" as line-breaks
+ * change. Note that the content process retains a reference to each such
+ * resource it requests, so it will only make this call once per locale for
+ * which hyphenation data exists.
+ *
+ * @param aURI
+ * The URI (which currently must always point to an omnijar resource)
+ * for the required hyphenation dictionary.
+ * @param aHandle
+ * Returns the shmem handle to the resource (or an invalid shmem handle
+ * in case of failure).
+ * @param aLoaded
+ * Returns the size in bytes of the resource.
+ */
+ sync GetHyphDict(nullable nsIURI aURI) returns (SharedMemoryHandle aHandle, uint32_t aSize);
+
+ async CreateWindow(PBrowser aThisTab,
+ MaybeDiscardedBrowsingContext aParent,
+ PBrowser aNewTab,
+ uint32_t aChromeFlags,
+ bool aCalledFromJS,
+ bool aForPrinting,
+ bool aForWindowDotPrint,
+ nullable nsIURI aURIToLoad,
+ nsCString aFeatures,
+ Modifiers aModifiers,
+ nullable nsIPrincipal aTriggeringPrincipal,
+ nullable nsIContentSecurityPolicy aCsp,
+ nullable nsIReferrerInfo aReferrerInfo,
+ OriginAttributes aOriginAttributes)
+ returns (CreatedWindowInfo window);
+
+ async CreateWindowInDifferentProcess(
+ PBrowser aThisTab,
+ MaybeDiscardedBrowsingContext aParent,
+ uint32_t aChromeFlags,
+ bool aCalledFromJS,
+ nullable nsIURI aURIToLoad,
+ nsCString aFeatures,
+ Modifiers aModifiers,
+ nsString aName,
+ nullable nsIPrincipal aTriggeringPrincipal,
+ nullable nsIContentSecurityPolicy aCsp,
+ nullable nsIReferrerInfo aReferrerInfo,
+ OriginAttributes aOriginAttributes);
+
+ /**
+ * Tell the parent that a decoder's' benchmark has been completed.
+ * The result can then be stored in permanent storage.
+ */
+ async NotifyBenchmarkResult(nsString aCodecName, uint32_t aDecodeFPS);
+
+ /**
+ * Notify `push-message` observers without data in the parent.
+ */
+ async NotifyPushObservers(nsCString scope, nullable nsIPrincipal principal,
+ nsString messageId);
+
+ /**
+ * Notify `push-message` observers with data in the parent.
+ */
+ async NotifyPushObserversWithData(nsCString scope, nullable nsIPrincipal principal,
+ nsString messageId, uint8_t[] data);
+
+ /**
+ * Notify `push-subscription-change` observers in the parent.
+ */
+ async NotifyPushSubscriptionChangeObservers(nsCString scope,
+ nullable nsIPrincipal principal);
+
+ async GetFilesRequest(nsID aID, nsString aDirectory, bool aRecursiveFlag);
+ async DeleteGetFilesRequest(nsID aID);
+
+ async StoreAndBroadcastBlobURLRegistration(nsCString url, IPCBlob blob,
+ nullable nsIPrincipal principal, nsCString aPartitionKey);
+
+ async UnstoreAndBroadcastBlobURLUnregistration(nsCString url, nullable nsIPrincipal principal);
+
+ /**
+ * Messages for communicating child Glean data to the parent process
+ */
+ async RecordPageLoadEvent(PageLoadExtra event);
+
+ /**
+ * Messages for communicating child Telemetry to the parent process
+ */
+ async AccumulateChildHistograms(HistogramAccumulation[] accumulations);
+ async AccumulateChildKeyedHistograms(KeyedHistogramAccumulation[] accumulations);
+ async UpdateChildScalars(ScalarAction[] updates);
+ async UpdateChildKeyedScalars(KeyedScalarAction[] updates);
+ async RecordChildEvents(ChildEventData[] events);
+ async RecordDiscardedData(DiscardedData data);
+
+ async AddMemoryReport(MemoryReport aReport);
+
+ async BHRThreadHang(HangDetails aHangDetails);
+
+ /*
+ * Adds a certificate exception for the given hostname and port.
+ */
+ async AddCertException(nullable nsIX509Cert aCert, nsCString aHostName,
+ int32_t aPort, OriginAttributes aOriginAttributes,
+ bool aIsTemporary)
+ returns (nsresult success);
+
+ /*
+ * Determines whether storage access can be granted automatically by the
+ * storage access API without showing a user prompt.
+ */
+ async AutomaticStorageAccessPermissionCanBeGranted(nullable nsIPrincipal aPrincipal)
+ returns (bool success);
+
+ /*
+ * A 3rd party tracking origin (aTrackingOrigin) has received the permission
+ * granted to have access to aGrantedOrigin when loaded by aParentWindowId.
+ */
+ async StorageAccessPermissionGrantedForOrigin(uint64_t aTopLevelWindowId,
+ MaybeDiscardedBrowsingContext aParentContext,
+ nullable nsIPrincipal aTrackingPrincipal,
+ nsCString aTrackingOrigin,
+ int aAllowMode,
+ StorageAccessPermissionGrantedReason? aReason,
+ bool aFrameOnly)
+ returns (bool unused);
+
+ async CompleteAllowAccessFor(MaybeDiscardedBrowsingContext aParentContext,
+ uint64_t aTopLevelWindowId,
+ nullable nsIPrincipal aTrackingPrincipal,
+ nsCString aTrackingOrigin,
+ uint32_t aCookieBehavior,
+ StorageAccessPermissionGrantedReason aReason)
+ returns (StorageAccessPromptChoices? choice);
+
+ async SetAllowStorageAccessRequestFlag(
+ nullable nsIPrincipal aEmbeddingPrincipal,
+ nullable nsIURI aEmbeddedOrigin)
+ returns (bool success);
+
+ async TestAllowStorageAccessRequestFlag(
+ nullable nsIPrincipal aEmbeddedPrincipal,
+ nullable nsIURI aEmbeddingOrigin)
+ returns (bool success);
+
+ async StoreUserInteractionAsPermission(nullable nsIPrincipal aPrincipal);
+
+ async TestCookiePermissionDecided(MaybeDiscardedBrowsingContext aContext,
+ nullable nsIPrincipal aPrincipal)
+ returns (bool? allowed);
+
+ async TestStorageAccessPermission(nullable nsIPrincipal aEmbeddingPrincipal,
+ nsCString aEmbeddedOrigin)
+ returns (bool? allowed);
+
+ /**
+ * When media element's controlled state changed in the content process, we
+ * have to notify the chrome process in order to update the status of the
+ * corresponding media controller, which is used to control all media in the
+ * certain tab. We would use the browsing context to find the corresponding
+ * controller.
+ */
+ async NotifyMediaPlaybackChanged(MaybeDiscardedBrowsingContext aContext,
+ MediaPlaybackState aState);
+
+ /**
+ * When media became audible or inaudible in content process, we have to
+ * notify chrome process in order to which tab is audible.
+ */
+ async NotifyMediaAudibleChanged(MaybeDiscardedBrowsingContext aContext,
+ MediaAudibleState aState);
+
+ /**
+ * When media enabled or disabled the Picture-in-Picture mode, we have to
+ * update that to the media controller in the chrome process.
+ */
+ async NotifyPictureInPictureModeChanged(
+ MaybeDiscardedBrowsingContext aContext, bool aEnabled);
+
+ /**
+ * This method is used to update media session's status when it's being
+ * created or destroyed.
+ */
+ async NotifyMediaSessionUpdated(MaybeDiscardedBrowsingContext aContext, bool aIsCreated);
+
+ /**
+ * This method is used to update media session's media metadata whenever its
+ * metadata is being updated.
+ */
+ async NotifyUpdateMediaMetadata(MaybeDiscardedBrowsingContext aContext,
+ MediaMetadataBase? aMetadata);
+
+ /**
+ * This method is used to update media session's playback state whenever its
+ * playback state is changed.
+ */
+ async NotifyMediaSessionPlaybackStateChanged(
+ MaybeDiscardedBrowsingContext aContext,
+ MediaSessionPlaybackState aMetadata);
+
+ /**
+ * This method is used to update media session's supported media session
+ * action when the action becomes supported or unsupported.
+ */
+ async NotifyMediaSessionSupportedActionChanged(
+ MaybeDiscardedBrowsingContext aContext,
+ MediaSessionAction aAction,
+ bool aEnabled);
+
+ /**
+ * This method is used to notify the media controller in chrome process that
+ * the media element in the browsing context entered fullscreen.
+ */
+ async NotifyMediaFullScreenState(
+ MaybeDiscardedBrowsingContext aContext,
+ bool aIsInFullScreen);
+
+ /**
+ * This method is used to update media session's position state whenever its
+ * position state is being updated.
+ */
+ async NotifyPositionStateChanged(
+ MaybeDiscardedBrowsingContext aContext,
+ PositionState aState);
+
+ /**
+ * This method will make canonical browsing context to update the count of
+ * callers which want to keep the page from being suspended even if the page
+ * is inactive.
+ */
+ async AddOrRemovePageAwakeRequest(MaybeDiscardedBrowsingContext aContext,
+ bool aShouldAddCount);
+
+#if defined(XP_WIN)
+ /**
+ * Due to sandboxing, a child process's UntrustedModulesProcessor cannot
+ * obtain enough information about a DLL file to determine its
+ * trustworthiness. This API asks the chrome process to perform that
+ * evaluation.
+ */
+ async GetModulesTrust(ModulePaths aModPaths, bool aRunAtNormalPriority)
+ returns (ModulesMapResult? modMapResult);
+#endif // defined(XP_WIN)
+
+ /**
+ * Used to route shutdown diagnostic info from the content process
+ * ServiceWorkers to the parent process' ServiceWorkerManager's
+ * ServiceWorkerShutdownBlocker. (The only other actor chain available
+ * for this would be very convoluted and create ordering problems).
+ */
+ async ReportServiceWorkerShutdownProgress(uint32_t aShutdownStateId,
+ Progress aProgress);
+
+ /**
+ * Whenever a document is updating the OrientationLock, we need to
+ * reject the orientationPendingPromises in other processes.
+ */
+ async AbortOtherOrientationPendingPromises(MaybeDiscardedBrowsingContext aContext);
+
+ async HistoryReload(MaybeDiscardedBrowsingContext aContext, uint32_t aReloadFlags);
+
+ async NotifyOnHistoryReload(MaybeDiscardedBrowsingContext aContext,
+ bool aForceReload)
+ returns (bool canReload, nsDocShellLoadState? loadState,
+ bool? reloadActiveEntry);
+
+ async HistoryCommit(MaybeDiscardedBrowsingContext aContext,
+ uint64_t aLoadID, nsID aChangeID, uint32_t aLoadType,
+ bool aPersist, bool aCloneEntryChildren,
+ bool aChannelExpired, uint32_t aCacheKey);
+
+ async HistoryGo(MaybeDiscardedBrowsingContext aContext, int32_t aOffset,
+ uint64_t aHistoryEpoch, bool aRequireUserInteraction,
+ bool aUserActivation) returns(int32_t? requestedIndex);
+
+ async BlobURLDataRequest(nsCString aBlobURL,
+ nullable nsIPrincipal aTriggeringPrincipal,
+ nullable nsIPrincipal aLoadingPrincipal,
+ OriginAttributes aOriginAttributes,
+ uint64_t aInnerWindowId,
+ nsCString aPartitionKey)
+ returns (BlobURLDataRequestResult aResult);
+
+ async SetActiveSessionHistoryEntry(MaybeDiscardedBrowsingContext context,
+ nsPoint? previousScrollPosition,
+ SessionHistoryInfo info, uint32_t loadType,
+ uint32_t updatedCacheKey, nsID changeID);
+ async ReplaceActiveSessionHistoryEntry(
+ MaybeDiscardedBrowsingContext context, SessionHistoryInfo info);
+
+ async RemoveDynEntriesFromActiveSessionHistoryEntry(
+ MaybeDiscardedBrowsingContext aContext);
+
+ async RemoveFromSessionHistory(
+ MaybeDiscardedBrowsingContext aContext, nsID changeID);
+
+ // Called when a nsDocShellLoadState which was received over IPC is
+ // destroyed in the content process to clean up pending state left behind
+ // tracking the load state in the parent process.
+ [LazySend] async CleanupPendingLoadState(uint64_t aLoadIdentifier);
+
+both:
+ async ScriptError(nsString message, nsString sourceName, nsString sourceLine,
+ uint32_t lineNumber, uint32_t colNumber, uint32_t flags,
+ nsCString category, bool privateWindow, uint64_t innerWindowId,
+ bool fromChromeContext);
+
+ /**
+ * Used in fission to report timing data when the parent window is in
+ * another process. Child frame will send data to its ContentParent which
+ * will then identify the ContentParent for the innerWindowId and pass
+ * the data to the correct process.
+ * loadInfo is passed in order to enforce same-origin security checks
+ * aData must be non-null.
+ */
+ async ReportFrameTimingData(LoadInfoArgs loadInfo, nsString entryName,
+ nsString initiatorType,
+ UniquePtr<PerformanceTimingData> aData);
+
+ async CommitBrowsingContextTransaction(MaybeDiscardedBrowsingContext aContext,
+ BrowsingContextTransaction aTransaction,
+ uint64_t aEpoch);
+
+ async AsyncMessage(nsString aMessage, ClonedMessageData aData);
+
+ /**
+ * Notify `push-subscription-modified` observers in the parent and child.
+ */
+ async NotifyPushSubscriptionModifiedObservers(nsCString scope,
+ nullable nsIPrincipal principal);
+
+ /**
+ * Send a Push error message to all service worker clients in the parent or
+ * child.
+ */
+ async PushError(nsCString scope, nullable nsIPrincipal principal, nsString message,
+ uint32_t flags);
+
+ /**
+ * Creates a new BrowsingContext, initialized with the values provided in
+ * `BrowsingContextInitializer`.
+ *
+ * This message may only be sent to the parent in limited situations. If the
+ * new BrowsingContext has a parent window, it must be owned by the
+ * embedding process, otherwise it must be owned by the opener, if set.
+ */
+ [LazySend] async CreateBrowsingContext(uint64_t aGroupId, BrowsingContextInitializer aInit);
+
+ /**
+ * If aDoDiscard is true, discards the passed-in BrowsingContext. If the
+ * BrowsingContext has already been discarded, this message does nothing.
+ * If the receiver is the parent process, resolves when all content
+ * processes have flagged the BrowsingContext as discarded, and if the
+ * receiver is a child process, resolves when that child process has flagged
+ * the BrowsingContext as discarded.
+ */
+ async DiscardBrowsingContext(MaybeDiscardedBrowsingContext aContext, bool aDoDiscard)
+ returns (uint64_t unused);
+
+ async AdjustWindowFocus(MaybeDiscardedBrowsingContext aContext,
+ bool aIsVisible, uint64_t aActionId);
+ async WindowClose(MaybeDiscardedBrowsingContext aContext,
+ bool aTrustedCaller);
+ async WindowFocus(MaybeDiscardedBrowsingContext aContext,
+ CallerType aCallerType, uint64_t aActionId);
+ async WindowBlur(MaybeDiscardedBrowsingContext aContext,
+ CallerType aCallerType);
+ async RaiseWindow(MaybeDiscardedBrowsingContext aContext, CallerType aCallerType, uint64_t aActionId);
+ async ClearFocus(MaybeDiscardedBrowsingContext aContext);
+ async SetFocusedBrowsingContext(MaybeDiscardedBrowsingContext aContext, uint64_t aActionId);
+ async SetActiveBrowsingContext(MaybeDiscardedBrowsingContext aContext, uint64_t aActionId);
+ async UnsetActiveBrowsingContext(MaybeDiscardedBrowsingContext aContext, uint64_t aActionId);
+ async SetFocusedElement(MaybeDiscardedBrowsingContext aContext, bool aNeedsFocus);
+ async FinalizeFocusOuter(MaybeDiscardedBrowsingContext aContext, bool aCanFocus,
+ CallerType aCallerType);
+parent:
+ [LazySend] async InsertNewFocusActionId(uint64_t aActionId);
+ async BlurToParent(MaybeDiscardedBrowsingContext aFocusedBrowsingContext,
+ MaybeDiscardedBrowsingContext aBrowsingContextToClear,
+ MaybeDiscardedBrowsingContext aAncestorBrowsingContextToFocus,
+ bool aIsLeavingDocument, bool aAdjustWidget,
+ bool aBrowsingContextToClearHandled,
+ bool aAncestorBrowsingContextToFocusHandled, uint64_t aActionId);
+child:
+ async BlurToChild(MaybeDiscardedBrowsingContext aFocusedBrowsingContext,
+ MaybeDiscardedBrowsingContext aBrowsingContextToClear,
+ MaybeDiscardedBrowsingContext aAncestorBrowsingContextToFocus,
+ bool aIsLeavingDocument, bool aAdjustWidget, uint64_t aActionId);
+ async SetupFocusedAndActive(MaybeDiscardedBrowsingContext aFocusedBrowsingContext,
+ uint64_t aActionIdForFocused,
+ MaybeDiscardedBrowsingContext aActiveBrowsingContext,
+ uint64_t aActionId);
+ async ReviseActiveBrowsingContext(uint64_t aOldActionId,
+ MaybeDiscardedBrowsingContext aActiveBrowsingContext,
+ uint64_t aNewActionId);
+ async ReviseFocusedBrowsingContext(uint64_t aOldActionId,
+ MaybeDiscardedBrowsingContext aFocusedBrowsingContext,
+ uint64_t aNewActionId);
+both:
+ async MaybeExitFullscreen(MaybeDiscardedBrowsingContext aContext);
+ async WindowPostMessage(MaybeDiscardedBrowsingContext aContext,
+ ClonedOrErrorMessageData aMessage,
+ PostMessageData aData);
+
+ async CommitWindowContextTransaction(MaybeDiscardedWindowContext aContext,
+ WindowContextTransaction aTransaction,
+ uint64_t aEpoch);
+
+child:
+ // NOTE: These methods are only needed on the child, as the parent
+ // WindowContext is managed using the PWindowGlobal actor's lifecycle.
+ [LazySend] async CreateWindowContext(WindowContextInitializer aInit);
+ async DiscardWindowContext(uint64_t aContextId) returns (bool unused);
+
+parent:
+ // Temporary (bug 1641989) conduit for Glean data in content processes.
+ // Sent from time-to-time to limit the amount of data vulnerable to loss.
+ // Buffer contains bincoded Rust structs.
+ async FOGData(ByteBuf buf);
+
+child:
+ // Temporary (bug 1641989) conduit for Glean data in content processes.
+ // Tells the child to flush any pending data. Used in tests and ping
+ // assembly. Buffer contains bincoded Rust structs.
+ async FlushFOGData() returns (ByteBuf buf);
+
+parent:
+ async SetContainerFeaturePolicy(MaybeDiscardedBrowsingContext aContainerContext,
+ nullable FeaturePolicy aContainerFeaturePolicy);
+
+ // Obtain an icon from the system widget toolkit, in nsIconDecoder
+ // format. Not supported (or needed) on all platforms; see the
+ // implementation in ContentParent::RecvGetSystemIcon for details.
+ async GetSystemIcon(nullable nsIURI aURI) returns (nsresult aResult, ByteBuf? aData);
+
+#ifdef FUZZING_SNAPSHOT
+ // Used by the child process to signal that it is ready to start fuzzing.
+ // This can in particular be used to wait for a particular event in a
+ // test document before taking the snapshot and starting e.g. IPC fuzzing.
+ async SignalFuzzingReady();
+#endif
+};
+
+}
+}
diff --git a/dom/ipc/PContentPermission.ipdlh b/dom/ipc/PContentPermission.ipdlh
new file mode 100644
index 0000000000..a70b5bca94
--- /dev/null
+++ b/dom/ipc/PContentPermission.ipdlh
@@ -0,0 +1,19 @@
+/* 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/. */
+
+namespace mozilla {
+namespace dom {
+
+struct PermissionRequest {
+ nsCString type;
+ nsString[] options;
+};
+
+struct PermissionChoice {
+ nsCString type;
+ nsString choice;
+};
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/PContentPermissionRequest.ipdl b/dom/ipc/PContentPermissionRequest.ipdl
new file mode 100644
index 0000000000..0ce5acbe95
--- /dev/null
+++ b/dom/ipc/PContentPermissionRequest.ipdl
@@ -0,0 +1,29 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PContent;
+include PContentPermission;
+
+include "nsContentPermissionHelper.h";
+
+namespace mozilla {
+namespace dom {
+
+[ManualDealloc, ChildImpl="RemotePermissionRequest", ParentImpl=virtual]
+protocol PContentPermissionRequest
+{
+ manager PContent;
+
+parent:
+ async prompt();
+ async Destroy();
+
+child:
+ async NotifyResult(bool allow, PermissionChoice[] choices);
+ async __delete__();
+};
+
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/PCycleCollectWithLogs.ipdl b/dom/ipc/PCycleCollectWithLogs.ipdl
new file mode 100644
index 0000000000..9ecfe3da4e
--- /dev/null
+++ b/dom/ipc/PCycleCollectWithLogs.ipdl
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PContent;
+
+namespace mozilla {
+namespace dom {
+
+[ManualDealloc, ChildImpl=virtual, ParentImpl=virtual]
+protocol PCycleCollectWithLogs {
+ manager PContent;
+
+parent:
+ async CloseGCLog();
+ async CloseCCLog();
+
+ async __delete__();
+};
+
+}
+}
diff --git a/dom/ipc/PFilePicker.ipdl b/dom/ipc/PFilePicker.ipdl
new file mode 100644
index 0000000000..0af98fd01b
--- /dev/null
+++ b/dom/ipc/PFilePicker.ipdl
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PBrowser;
+
+include IPCBlob;
+
+include "mozilla/dom/FilePickerMessageUtils.h";
+
+using struct mozilla::void_t from "mozilla/ipc/IPCCore.h";
+using nsIFilePicker::CaptureTarget from "nsIFilePicker.h";
+using nsIFilePicker::ResultCode from "nsIFilePicker.h";
+
+namespace mozilla {
+namespace dom {
+
+struct InputBlobs { IPCBlob[] blobs; };
+struct InputDirectory { nsString directoryPath; };
+union MaybeInputData
+{
+ InputBlobs;
+ InputDirectory;
+ void_t;
+};
+
+[ChildImpl=virtual]
+protocol PFilePicker
+{
+ manager PBrowser;
+
+parent:
+ async Open(int16_t selectedType, bool addToRecentDocs, nsString defaultFile,
+ nsString defaultExtension, nsString[] filters, nsString[] filterNames,
+ nsString[] rawFilters, nsString displayDirectory,
+ nsString displaySpecialDirectory, nsString okButtonLabel,
+ CaptureTarget capture);
+
+ async Close();
+
+child:
+ async __delete__(MaybeInputData data, ResultCode result);
+};
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/PInProcess.ipdl b/dom/ipc/PInProcess.ipdl
new file mode 100644
index 0000000000..4c2d7ba548
--- /dev/null
+++ b/dom/ipc/PInProcess.ipdl
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PExtensions;
+include protocol PSessionStore;
+include protocol PWindowGlobal;
+
+include DOMTypes;
+
+namespace mozilla {
+namespace dom {
+
+/**
+ * PInProcess is intended for use as an alternative actor manager to PContent
+ * for async actors which want to be used uniformly in both Content->Chrome and
+ * Chrome->Chrome circumstances.
+ *
+ * `mozilla::dom::InProcess{Parent, Child}::Singleton()` should be used to get
+ * an instance of this actor.
+ */
+[ChildProc=Parent]
+async protocol PInProcess
+{
+ manages PExtensions;
+ manages PSessionStore;
+ manages PWindowGlobal;
+};
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/PJSOracle.ipdl b/dom/ipc/PJSOracle.ipdl
new file mode 100644
index 0000000000..519c579aa4
--- /dev/null
+++ b/dom/ipc/PJSOracle.ipdl
@@ -0,0 +1,21 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PJSValidator;
+
+namespace mozilla {
+namespace dom {
+
+// PJSOracle is a top-level actor which manages PJSValidator
+[ChildProc=Utility]
+async protocol PJSOracle {
+ manages PJSValidator;
+
+child:
+ async PJSValidator();
+};
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/PJSValidator.ipdl b/dom/ipc/PJSValidator.ipdl
new file mode 100644
index 0000000000..7d369c5a21
--- /dev/null
+++ b/dom/ipc/PJSValidator.ipdl
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PJSOracle;
+
+using mozilla::net::OpaqueResponseBlocker::ValidatorResult from "mozilla/net/OpaqueResponseUtils.h";
+
+namespace mozilla {
+namespace dom {
+
+async protocol PJSValidator {
+ manager PJSOracle;
+
+child:
+ [ReplyPriority=control]
+ async IsOpaqueResponseAllowed() returns (Shmem? aMem, ValidatorResult aResult);
+
+ async OnDataAvailable(Shmem aData);
+
+ // aContentCharset, aHintCharset and aDocumentCharset
+ // are needed to determine the decoder for the received data
+ async OnStopRequest(nsresult aReason, nsCString aContentCharset,
+ nsString aHintCharset,
+ nsString aDocumentCharset);
+
+ async __delete__();
+};
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/PProcessHangMonitor.ipdl b/dom/ipc/PProcessHangMonitor.ipdl
new file mode 100644
index 0000000000..290f9b157b
--- /dev/null
+++ b/dom/ipc/PProcessHangMonitor.ipdl
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* 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/. */
+
+// ParamTraits stuff for nsIRemoteTab::NavigationType
+include "mozilla/dom/TabMessageUtils.h";
+using nsIRemoteTab::NavigationType from "nsIRemoteTab.h";
+
+using base::ProcessId from "base/process.h";
+using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
+using nsIThread::QoSPriority from "nsIThread.h";
+
+namespace mozilla {
+
+struct SlowScriptData
+{
+ TabId tabId;
+ nsCString filename;
+ nsString addonId;
+ double duration;
+};
+
+[ChildImpl=virtual, ParentImpl=virtual, ChildProc=Content]
+protocol PProcessHangMonitor
+{
+parent:
+ async HangEvidence(SlowScriptData data);
+ async ClearHang();
+
+child:
+ async TerminateScript();
+ async RequestContentJSInterrupt();
+
+ async BeginStartingDebugger();
+ async EndStartingDebugger();
+
+ async PaintWhileInterruptingJS(TabId tabId);
+ async UnloadLayersWhileInterruptingJS(TabId tabId);
+
+ async CancelContentJSExecutionIfRunning(
+ TabId tabId, NavigationType aNavigationType,
+ int32_t aNavigationIndex, nsCString? aNavigationURI, int32_t aEpoch);
+
+ // For MacOS QoS use
+ async SetMainThreadQoSPriority(QoSPriority qosPriority);
+
+};
+
+} // namespace mozilla
diff --git a/dom/ipc/PTabContext.ipdlh b/dom/ipc/PTabContext.ipdlh
new file mode 100644
index 0000000000..7a917cd232
--- /dev/null
+++ b/dom/ipc/PTabContext.ipdlh
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include "mozilla/dom/TabMessageUtils.h";
+
+include protocol PBrowser;
+
+using mozilla::OriginAttributes from "mozilla/ipc/BackgroundUtils.h";
+
+namespace mozilla {
+namespace dom {
+
+// An IPCTabContext which corresponds to a PBrowser opened by a child when it
+// receives window.open().
+struct PopupIPCTabContext
+{
+ PBrowser opener;
+ uint64_t chromeOuterWindowID;
+};
+
+// An IPCTabContext which corresponds to an app, browser, or normal frame.
+struct FrameIPCTabContext
+{
+ uint64_t chromeOuterWindowID;
+
+ // Maximum number of touch points on the screen.
+ uint32_t maxTouchPoints;
+};
+
+// IPCTabContext is an analog to mozilla::dom::TabContext. Both specify an
+// iframe/PBrowser's own and containing app-ids and tell you whether the
+// iframe/PBrowser is a browser frame. But only IPCTabContext is allowed to
+// travel over IPC.
+//
+// We need IPCTabContext (specifically, PopupIPCTabContext) to prevent a
+// privilege escalation attack by a compromised child process.
+union IPCTabContext
+{
+ PopupIPCTabContext;
+ FrameIPCTabContext;
+};
+
+}
+}
diff --git a/dom/ipc/PURLClassifier.ipdl b/dom/ipc/PURLClassifier.ipdl
new file mode 100644
index 0000000000..d4eb5480aa
--- /dev/null
+++ b/dom/ipc/PURLClassifier.ipdl
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PContent;
+include PURLClassifierInfo;
+
+namespace mozilla {
+namespace dom {
+
+[ManualDealloc]
+protocol PURLClassifier
+{
+ manager PContent;
+
+child:
+ async __delete__(ClassifierInfo? info, nsresult errorCode);
+};
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/PURLClassifierInfo.ipdlh b/dom/ipc/PURLClassifierInfo.ipdlh
new file mode 100644
index 0000000000..e4e198fa4c
--- /dev/null
+++ b/dom/ipc/PURLClassifierInfo.ipdlh
@@ -0,0 +1,15 @@
+/* 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/. */
+
+namespace mozilla {
+namespace dom {
+
+struct ClassifierInfo {
+ nsCString list;
+ nsCString provider;
+ nsCString fullhash;
+};
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/PURLClassifierLocal.ipdl b/dom/ipc/PURLClassifierLocal.ipdl
new file mode 100644
index 0000000000..a551fc779c
--- /dev/null
+++ b/dom/ipc/PURLClassifierLocal.ipdl
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PContent;
+
+include PURLClassifierInfo;
+
+include "mozilla/ipc/URIUtils.h";
+include "mozilla/dom/URLClassifierParent.h";
+include "mozilla/dom/URLClassifierChild.h";
+
+[RefCounted] using class nsIURI from "nsIURI.h";
+
+namespace mozilla {
+namespace dom {
+
+struct URLClassifierLocalResult
+{
+ nullable nsIURI uri;
+ nsCString featureName;
+ nsCString matchingList;
+};
+
+[ManualDealloc, ChildImpl="URLClassifierLocalChild", ParentImpl="URLClassifierLocalParent"]
+protocol PURLClassifierLocal
+{
+ manager PContent;
+
+child:
+ async __delete__(URLClassifierLocalResult[] results);
+};
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/PVsync.ipdl b/dom/ipc/PVsync.ipdl
new file mode 100644
index 0000000000..10d8ab4760
--- /dev/null
+++ b/dom/ipc/PVsync.ipdl
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PBackground;
+include protocol PBrowser;
+include "mozilla/layers/LayersMessageUtils.h";
+
+using class mozilla::TimeStamp from "mozilla/TimeStamp.h";
+using mozilla::VsyncEvent from "mozilla/VsyncDispatcher.h";
+
+namespace mozilla {
+namespace dom {
+
+/*
+ * The PVsync is a sub-protocol in PBackground or PBrowser and it is used to
+ * notify the vsync event from chrome to content process. It also provides the
+ * interfaces for content to observe/unobserve vsync event notifications.
+ */
+async protocol PVsync
+{
+ manager PBackground or PBrowser;
+
+child:
+ // Send vsync event and vsync rate from chrome to content process.
+ [Compress, Priority=vsync] async Notify(VsyncEvent aVsync, float aVsyncRate);
+
+parent:
+ // Content process use these messages to acquire the vsync event.
+ async Observe();
+ async Unobserve();
+
+ // This message is never sent. Each PVsync actor will stay alive as long as
+ // its PBackground or PBrowser manager.
+ async __delete__();
+};
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/PWindowGlobal.ipdl b/dom/ipc/PWindowGlobal.ipdl
new file mode 100644
index 0000000000..a063852f56
--- /dev/null
+++ b/dom/ipc/PWindowGlobal.ipdl
@@ -0,0 +1,227 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include "mozilla/dom/DocShellMessageUtils.h";
+include "mozilla/dom/FeaturePolicyUtils.h";
+include "mozilla/dom/IdentityCredentialSerializationHelpers.h";
+include "mozilla/dom/PermissionMessageUtils.h";
+include "mozilla/dom/SessionStoreMessageUtils.h";
+include "mozilla/ipc/TransportSecurityInfoUtils.h";
+include "mozilla/ipc/URIUtils.h";
+
+include protocol PBrowser;
+include protocol PInProcess;
+include protocol PBrowserBridge;
+
+include DOMTypes;
+include ClientIPCTypes;
+include IPCIdentityCredential;
+include NeckoChannelParams;
+include SessionStoreTypes;
+
+include "mozilla/layers/LayersMessageUtils.h";
+
+using mozilla::dom::JSActorMessageKind from "mozilla/dom/JSActor.h";
+using mozilla::gfx::IntRect from "mozilla/gfx/Rect.h";
+[MoveOnly] using mozilla::gfx::PaintFragment from "mozilla/gfx/CrossProcessPaint.h";
+using nscolor from "nsColor.h";
+using mozilla::dom::XPCOMPermitUnloadAction from "nsIDocumentViewer.h";
+using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
+using mozilla::layers::LayersId from "mozilla/layers/LayersTypes.h";
+[RefCounted] using class nsITransportSecurityInfo from "nsITransportSecurityInfo.h";
+using mozilla::UseCounters from "mozilla/UseCounter.h";
+using mozilla::dom::MaybeDiscardedWindowContext from "mozilla/dom/WindowContext.h";
+[RefCounted] using mozilla::dom::FeaturePolicy from "mozilla/dom/FeaturePolicy.h";
+[RefCounted] using mozilla::dom::SessionStoreRestoreData from "mozilla/dom/SessionStoreRestoreData.h";
+using mozilla::dom::IdentityCredentialRequestOptions from "mozilla/dom/IdentityCredentialBinding.h";
+
+namespace mozilla {
+namespace dom {
+
+struct JSActorMessageMeta {
+ nsCString actorName;
+ nsString messageName;
+ uint64_t queryId;
+ JSActorMessageKind kind;
+};
+
+struct IPCWebShareData
+{
+ nsCString title;
+ nsCString text;
+ nullable nsIURI url;
+};
+
+/**
+ * A PWindowGlobal actor has a lifetime matching that of a single Window Global,
+ * specifically a |nsGlobalWindowInner|. These actors will form a parent/child
+ * link either between the chrome/content process, or will be in-process, for
+ * documents which are loaded in the chrome process.
+ */
+async protocol PWindowGlobal
+{
+ manager PBrowser or PInProcess;
+
+child:
+ async __delete__();
+
+ async MakeFrameLocal(MaybeDiscardedBrowsingContext aFrameContext,
+ uint64_t aSwitchId);
+ async MakeFrameRemote(MaybeDiscardedBrowsingContext aFrameContext,
+ ManagedEndpoint<PBrowserBridgeChild> aEndpoint,
+ TabId aTabId, LayersId aLayersId) returns (bool success);
+
+ async DrawSnapshot(IntRect? aRect, float aScale, nscolor aBackgroundColor,
+ uint32_t aFlags) returns (PaintFragment retval);
+
+ async DispatchSecurityPolicyViolation(nsString aViolationEventJSON);
+
+ async SaveStorageAccessPermissionGranted();
+
+ async AddBlockedFrameNodeByClassifier(MaybeDiscardedBrowsingContext aNode);
+
+ /**
+ * Request from UI to reset the scaling zoom that is controlled by APZ.
+ */
+ async ResetScalingZoom();
+
+ async SetContainerFeaturePolicy(nullable FeaturePolicy aContainerFeaturePolicy);
+
+ async RestoreDocShellState(DocShellRestoreState aState)
+ returns (bool success);
+
+ async RestoreTabContent(nullable SessionStoreRestoreData aData) returns (bool success);
+
+both:
+ async RawMessage(JSActorMessageMeta aMetadata, ClonedMessageData? aData,
+ ClonedMessageData? aStack);
+
+parent:
+ // Load the given URI load state into the current owner process of the given
+ // BrowsingContext. aTargetBC must be in the same BrowsingContextGroup as this
+ // window global.
+ async LoadURI(MaybeDiscardedBrowsingContext aTargetBC,
+ nsDocShellLoadState aLoadState, bool aSetNavigating);
+
+ async InternalLoad(nsDocShellLoadState aLoadState);
+
+ /// Update the URI of the document in this WindowGlobal.
+ [LazySend] async UpdateDocumentURI(nsIURI aUri);
+
+ // We expose frameAncestors to web-extensions and they extract URIs from the
+ // principals collected. In order to be compatible with that API, we need to
+ // update the document's principal. This is only allowed if the principals are
+ // `equals` to each other.
+ [LazySend] async UpdateDocumentPrincipal(nullable nsIPrincipal aPrincipal,
+ nullable nsIPrincipal aStoragePrincipal);
+
+ // Update document's `documentHasLoaded` bit in this WindowGlobal.
+ [LazySend] async UpdateDocumentHasLoaded(bool aDocumentHasLoaded);
+
+ // Update document's 'documentHasUserInteracted' bit in this WindowGlobal.
+ [LazySend] async UpdateDocumentHasUserInteracted(bool aDocumentHasUserInteracted);
+
+ // Update document's sandbox flags in this WindowGlobal.
+ [LazySend] async UpdateSandboxFlags(uint32_t aSandboxFlags);
+
+ // Update document csp's fields in this WindowGlobal.
+ [LazySend] async UpdateDocumentCspSettings(bool aBlockAllMixedContent, bool aUpgradeInsecureRequests);
+
+ // Update document's cookie settings in this WindowGlobal.
+ [LazySend] async UpdateCookieJarSettings(CookieJarSettingsArgs cookieJarSettings);
+
+ // Update the title of the document in this WindowGlobal.
+ [LazySend] async UpdateDocumentTitle(nsString aTitle);
+
+ [LazySend] async UpdateDocumentSecurityInfo(nullable nsITransportSecurityInfo aSecurityInfo);
+
+ // Update the document's HTTPS-Only Mode flags in this WindowGlobal.
+ [LazySend] async UpdateHttpsOnlyStatus(uint32_t aHttpsOnlyStatus);
+
+ /// Send down initial document bit to the parent.
+ [LazySend] async SetIsInitialDocument(bool aIsInitialDocument);
+
+ // Attempts to perform a "Web Share".
+ async Share(IPCWebShareData aData) returns (nsresult rv);
+
+ // Get content blocking events from the parent process.
+ async GetContentBlockingEvents() returns (uint32_t events);
+
+ // Send the ClientInfo associated with a top-level document load.
+ [LazySend] async SetClientInfo(IPCClientInfo aClientInfo);
+
+ // Checks whether any "beforeunload" event listener in the document subtree
+ // wants to block unload, and prompts the user to allow if any does (depending
+ // on the action specified, using nsIDocumentViewer::PermitUnloadAction
+ // values). The sender is responsible for checking documents in its own
+ // process, and passing true for `aHasInProcessBlocker` if any exist. Windows
+ // hosted outside of the caller process will be checked automatically.
+ async CheckPermitUnload(bool aHasInProcessBlocker, XPCOMPermitUnloadAction aAction)
+ returns (bool permitUnload);
+
+ /**
+ * Informs the parent process that the document in aTop should expect to
+ * receive page use counter contributions from the document in this
+ * WindowGlobal.
+ */
+ async ExpectPageUseCounters(MaybeDiscardedWindowContext aTop);
+
+ /**
+ * Accumulates use counter data from the document in this WindowGlobal into
+ * the document previously passed into the ExpectPageUseCounters call.
+ */
+ async AccumulatePageUseCounters(UseCounters aUseCounters);
+
+ async RequestRestoreTabContent();
+
+ // Add the flags in aOnFlags to the current BFCache status and remove the
+ // flags in aOffFlags from the current BFCache status. See the BFCacheStatus
+ // enum for the valid flags.
+ async UpdateBFCacheStatus(uint32_t aOnFlags, uint32_t aOffFlags);
+
+ // Signal whether the first connection is added (aIsAdded = true) or
+ // the last connection is removed (aIsAdded = false).
+ async UpdateActivePeerConnectionStatus(bool aIsAdded);
+
+ /**
+ * Used to notify the parent when there's a change in the number of requests
+ * in the loadgroup. If there are no requests this will be set to Nothing().
+ * If there is one request this will be set to the ID of that request, if it
+ * implements nsIIdentChannel. If there are more than one requests this will
+ * be set to 0.
+ * Note that some requests are ignored (eg. favicon loads).
+ */
+ async SetSingleChannelId(uint64_t? singleChannelId);
+
+ async SetDocumentDomain(nsIURI aDomain);
+
+ async Destroy();
+
+ async ReloadWithHttpsOnlyException();
+
+ // Used by the Credential Manager API and FedCM to keep the discovery of
+ // a credential abstracted from the content process. This is required because
+ // credentialed requests that are specifically not partitioned are made and
+ // the results must not enter the child process until the user consents via
+ // purpose built UI.
+ async DiscoverIdentityCredentialFromExternalSource(IdentityCredentialRequestOptions aOptions)
+ returns (IPCIdentityCredential? identityCredential);
+
+ async GetStorageAccessPermission() returns(uint32_t permission_action);
+
+
+ async SetCookies(nsCString baseDomain,
+ OriginAttributes attrs,
+ nullable nsIURI host,
+ bool fromHttp,
+ CookieStruct[] cookies);
+
+child:
+ async NotifyPermissionChange(nsCString type, uint32_t permission);
+};
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/PageLoadEventUtils.h b/dom/ipc/PageLoadEventUtils.h
new file mode 100644
index 0000000000..243386ca6c
--- /dev/null
+++ b/dom/ipc/PageLoadEventUtils.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_page_load_event_utils_h__
+#define mozilla_dom_page_load_event_utils_h__
+
+#include "ipc/IPCMessageUtils.h"
+#include "mozilla/glean/GleanMetrics.h"
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::glean::perf::PageLoadExtra> {
+ typedef mozilla::glean::perf::PageLoadExtra paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.fcpTime);
+ WriteParam(aWriter, aParam.lcpTime);
+ WriteParam(aWriter, aParam.jsExecTime);
+ WriteParam(aWriter, aParam.loadTime);
+ WriteParam(aWriter, aParam.loadType);
+ WriteParam(aWriter, aParam.responseTime);
+ WriteParam(aWriter, aParam.httpVer);
+ WriteParam(aWriter, aParam.redirectCount);
+ WriteParam(aWriter, aParam.redirectTime);
+ WriteParam(aWriter, aParam.sameOriginNav);
+ WriteParam(aWriter, aParam.trrDomain);
+ WriteParam(aWriter, aParam.dnsLookupTime);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ return ReadParam(aReader, &aResult->fcpTime) &&
+ ReadParam(aReader, &aResult->lcpTime) &&
+ ReadParam(aReader, &aResult->jsExecTime) &&
+ ReadParam(aReader, &aResult->loadTime) &&
+ ReadParam(aReader, &aResult->loadType) &&
+ ReadParam(aReader, &aResult->responseTime) &&
+ ReadParam(aReader, &aResult->httpVer) &&
+ ReadParam(aReader, &aResult->redirectCount) &&
+ ReadParam(aReader, &aResult->redirectTime) &&
+ ReadParam(aReader, &aResult->sameOriginNav) &&
+ ReadParam(aReader, &aResult->trrDomain) &&
+ ReadParam(aReader, &aResult->dnsLookupTime);
+ }
+};
+
+} // namespace IPC
+
+#endif // mozilla_dom_page_load_event_utils_h__
diff --git a/dom/ipc/PermissionMessageUtils.cpp b/dom/ipc/PermissionMessageUtils.cpp
new file mode 100644
index 0000000000..19d232a7bc
--- /dev/null
+++ b/dom/ipc/PermissionMessageUtils.cpp
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/PermissionMessageUtils.h"
+#include "mozilla/ipc/BackgroundUtils.h"
+#include "mozilla/ipc/PBackgroundSharedTypes.h"
+#include "nsCOMPtr.h"
+#include "nsIPrincipal.h"
+
+namespace mozilla::ipc {
+
+void IPDLParamTraits<nsIPrincipal*>::Write(IPC::MessageWriter* aWriter,
+ IProtocol* aActor,
+ nsIPrincipal* aParam) {
+ Maybe<PrincipalInfo> info;
+ if (aParam) {
+ info.emplace();
+ nsresult rv = PrincipalToPrincipalInfo(aParam, info.ptr());
+ MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
+ }
+
+ WriteIPDLParam(aWriter, aActor, info);
+}
+
+bool IPDLParamTraits<nsIPrincipal*>::Read(IPC::MessageReader* aReader,
+ IProtocol* aActor,
+ RefPtr<nsIPrincipal>* aResult) {
+ Maybe<PrincipalInfo> info;
+ if (!ReadIPDLParam(aReader, aActor, &info)) {
+ return false;
+ }
+
+ if (info.isNothing()) {
+ return true;
+ }
+
+ auto principalOrErr = PrincipalInfoToPrincipal(info.ref());
+
+ if (NS_WARN_IF(principalOrErr.isErr())) {
+ return false;
+ }
+
+ nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap();
+ *aResult = principal;
+ return true;
+}
+
+} // namespace mozilla::ipc
diff --git a/dom/ipc/PermissionMessageUtils.h b/dom/ipc/PermissionMessageUtils.h
new file mode 100644
index 0000000000..7a32b9e930
--- /dev/null
+++ b/dom/ipc/PermissionMessageUtils.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_permission_message_utils_h__
+#define mozilla_dom_permission_message_utils_h__
+
+#include "mozilla/ipc/IPDLParamTraits.h"
+#include "ipc/IPCMessageUtils.h"
+#include "nsCOMPtr.h"
+#include "nsIPrincipal.h"
+
+namespace mozilla::ipc {
+
+template <>
+struct IPDLParamTraits<nsIPrincipal*> {
+ static void Write(IPC::MessageWriter* aWriter, IProtocol* aActor,
+ nsIPrincipal* aParam);
+ static bool Read(IPC::MessageReader* aReader, IProtocol* aActor,
+ RefPtr<nsIPrincipal>* aResult);
+
+ // Overload to support deserializing nsCOMPtr<nsIPrincipal> directly.
+ static bool Read(IPC::MessageReader* aReader, IProtocol* aActor,
+ nsCOMPtr<nsIPrincipal>* aResult) {
+ RefPtr<nsIPrincipal> result;
+ if (!Read(aReader, aActor, &result)) {
+ return false;
+ }
+ *aResult = std::move(result);
+ return true;
+ }
+};
+
+} // namespace mozilla::ipc
+
+#endif // mozilla_dom_permission_message_utils_h__
diff --git a/dom/ipc/PreallocatedProcessManager.cpp b/dom/ipc/PreallocatedProcessManager.cpp
new file mode 100644
index 0000000000..b96521492f
--- /dev/null
+++ b/dom/ipc/PreallocatedProcessManager.cpp
@@ -0,0 +1,440 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/PreallocatedProcessManager.h"
+
+#include "mozilla/AppShutdown.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/ProfilerMarkers.h"
+#include "mozilla/Unused.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/StaticPrefs_dom.h"
+#include "nsIPropertyBag2.h"
+#include "ProcessPriorityManager.h"
+#include "nsServiceManagerUtils.h"
+#include "nsIXULRuntime.h"
+#include "nsTArray.h"
+#include "prsystem.h"
+
+using namespace mozilla::hal;
+using namespace mozilla::dom;
+
+namespace mozilla {
+/**
+ * This singleton class implements the static methods on
+ * PreallocatedProcessManager.
+ */
+class PreallocatedProcessManagerImpl final : public nsIObserver {
+ friend class PreallocatedProcessManager;
+
+ public:
+ static PreallocatedProcessManagerImpl* Singleton();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ // See comments on PreallocatedProcessManager for these methods.
+ void AddBlocker(ContentParent* aParent);
+ void RemoveBlocker(ContentParent* aParent);
+ already_AddRefed<ContentParent> Take(const nsACString& aRemoteType);
+ void Erase(ContentParent* aParent);
+
+ private:
+ static const char* const kObserverTopics[];
+
+ static StaticRefPtr<PreallocatedProcessManagerImpl> sSingleton;
+
+ PreallocatedProcessManagerImpl();
+ ~PreallocatedProcessManagerImpl();
+ PreallocatedProcessManagerImpl(const PreallocatedProcessManagerImpl&) =
+ delete;
+
+ const PreallocatedProcessManagerImpl& operator=(
+ const PreallocatedProcessManagerImpl&) = delete;
+
+ void Init();
+
+ bool CanAllocate();
+ void AllocateAfterDelay(bool aStartup = false);
+ void AllocateOnIdle();
+ void AllocateNow();
+
+ void RereadPrefs();
+ void Enable(uint32_t aProcesses);
+ void Disable();
+ void CloseProcesses();
+
+ bool IsEmpty() const { return mPreallocatedProcesses.IsEmpty(); }
+ static bool IsShutdown() {
+ return AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed);
+ }
+ bool IsEnabled() { return mEnabled && !IsShutdown(); }
+
+ bool mEnabled;
+ uint32_t mNumberPreallocs;
+ AutoTArray<RefPtr<ContentParent>, 3> mPreallocatedProcesses;
+ // Even if we have multiple PreallocatedProcessManagerImpls, we'll have
+ // one blocker counter
+ static uint32_t sNumBlockers;
+ TimeStamp mBlockingStartTime;
+};
+
+/* static */
+uint32_t PreallocatedProcessManagerImpl::sNumBlockers = 0;
+
+const char* const PreallocatedProcessManagerImpl::kObserverTopics[] = {
+ "memory-pressure",
+ "profile-change-teardown",
+ NS_XPCOM_SHUTDOWN_OBSERVER_ID,
+};
+
+/* static */
+StaticRefPtr<PreallocatedProcessManagerImpl>
+ PreallocatedProcessManagerImpl::sSingleton;
+
+/* static */
+PreallocatedProcessManagerImpl* PreallocatedProcessManagerImpl::Singleton() {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!sSingleton) {
+ sSingleton = new PreallocatedProcessManagerImpl;
+ sSingleton->Init();
+ ClearOnShutdown(&sSingleton);
+ }
+ return sSingleton;
+ // PreallocatedProcessManagers live until shutdown
+}
+
+NS_IMPL_ISUPPORTS(PreallocatedProcessManagerImpl, nsIObserver)
+
+PreallocatedProcessManagerImpl::PreallocatedProcessManagerImpl()
+ : mEnabled(false), mNumberPreallocs(1) {}
+
+PreallocatedProcessManagerImpl::~PreallocatedProcessManagerImpl() {
+ // Note: mPreallocatedProcesses may not be null, but all processes should
+ // be dead (IsDead==true). We block Erase() when our observer sees
+ // shutdown starting.
+}
+
+void PreallocatedProcessManagerImpl::Init() {
+ Preferences::AddStrongObserver(this, "dom.ipc.processPrelaunch.enabled");
+ // We have to respect processCount at all time. This is especially important
+ // for testing.
+ Preferences::AddStrongObserver(this, "dom.ipc.processCount");
+ // A StaticPref, but we need to adjust the number of preallocated processes
+ // if the value goes up or down, so we need to run code on change.
+ Preferences::AddStrongObserver(this,
+ "dom.ipc.processPrelaunch.fission.number");
+
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ MOZ_ASSERT(os);
+ for (auto topic : kObserverTopics) {
+ os->AddObserver(this, topic, /* ownsWeak */ false);
+ }
+ RereadPrefs();
+}
+
+NS_IMETHODIMP
+PreallocatedProcessManagerImpl::Observe(nsISupports* aSubject,
+ const char* aTopic,
+ const char16_t* aData) {
+ if (!strcmp("nsPref:changed", aTopic)) {
+ // The only other observer we registered was for our prefs.
+ RereadPrefs();
+ } else if (!strcmp(NS_XPCOM_SHUTDOWN_OBSERVER_ID, aTopic) ||
+ !strcmp("profile-change-teardown", aTopic)) {
+ Preferences::RemoveObserver(this, "dom.ipc.processPrelaunch.enabled");
+ Preferences::RemoveObserver(this, "dom.ipc.processCount");
+ Preferences::RemoveObserver(this,
+ "dom.ipc.processPrelaunch.fission.number");
+
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ MOZ_ASSERT(os);
+ for (auto topic : kObserverTopics) {
+ os->RemoveObserver(this, topic);
+ }
+ } else if (!strcmp("memory-pressure", aTopic)) {
+ CloseProcesses();
+ } else {
+ MOZ_ASSERT_UNREACHABLE("Unknown topic");
+ }
+
+ return NS_OK;
+}
+
+void PreallocatedProcessManagerImpl::RereadPrefs() {
+ if (mozilla::BrowserTabsRemoteAutostart() &&
+ Preferences::GetBool("dom.ipc.processPrelaunch.enabled")) {
+ int32_t number = 1;
+ if (mozilla::FissionAutostart()) {
+ number = StaticPrefs::dom_ipc_processPrelaunch_fission_number();
+ // limit preallocated processes on low-mem machines
+ PRUint64 bytes = PR_GetPhysicalMemorySize();
+ if (bytes > 0 &&
+ bytes <=
+ StaticPrefs::dom_ipc_processPrelaunch_lowmem_mb() * 1024 * 1024) {
+ number = 1;
+ }
+ }
+ if (number >= 0) {
+ Enable(number);
+ // We have one prealloc queue for all types except File now
+ if (static_cast<uint64_t>(number) < mPreallocatedProcesses.Length()) {
+ CloseProcesses();
+ }
+ }
+ } else {
+ Disable();
+ }
+}
+
+already_AddRefed<ContentParent> PreallocatedProcessManagerImpl::Take(
+ const nsACString& aRemoteType) {
+ if (!IsEnabled()) {
+ return nullptr;
+ }
+ RefPtr<ContentParent> process;
+ if (!IsEmpty()) {
+ process = mPreallocatedProcesses.ElementAt(0);
+ mPreallocatedProcesses.RemoveElementAt(0);
+
+ // Don't set the priority to FOREGROUND here, since it may not have
+ // finished starting
+
+ // We took a preallocated process. Let's try to start up a new one
+ // soon.
+ ContentParent* last = mPreallocatedProcesses.SafeLastElement(nullptr);
+ // There could be a launching process that isn't the last, but that's
+ // ok (and unlikely)
+ if (!last || !last->IsLaunching()) {
+ AllocateAfterDelay();
+ }
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("Use prealloc process %p%s, %lu available", process.get(),
+ process->IsLaunching() ? " (still launching)" : "",
+ (unsigned long)mPreallocatedProcesses.Length()));
+ }
+ if (process && !process->IsLaunching()) {
+ ProcessPriorityManager::SetProcessPriority(process,
+ PROCESS_PRIORITY_FOREGROUND);
+ } // else this will get set by the caller when they call InitInternal()
+
+ return process.forget();
+}
+
+void PreallocatedProcessManagerImpl::Erase(ContentParent* aParent) {
+ (void)mPreallocatedProcesses.RemoveElement(aParent);
+}
+
+void PreallocatedProcessManagerImpl::Enable(uint32_t aProcesses) {
+ mNumberPreallocs = aProcesses;
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("Enabling preallocation: %u", aProcesses));
+ if (mEnabled || IsShutdown()) {
+ return;
+ }
+
+ mEnabled = true;
+ AllocateAfterDelay(/* aStartup */ true);
+}
+
+void PreallocatedProcessManagerImpl::AddBlocker(ContentParent* aParent) {
+ if (sNumBlockers == 0) {
+ mBlockingStartTime = TimeStamp::Now();
+ }
+ sNumBlockers++;
+}
+
+void PreallocatedProcessManagerImpl::RemoveBlocker(ContentParent* aParent) {
+ // This used to assert that the blocker existed, but preallocated
+ // processes aren't blockers anymore because it's not useful and
+ // interferes with async launch, and it's simpler if content
+ // processes don't need to remember whether they were preallocated.
+
+ MOZ_DIAGNOSTIC_ASSERT(sNumBlockers > 0);
+ sNumBlockers--;
+ if (sNumBlockers == 0) {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("Blocked preallocation for %fms",
+ (TimeStamp::Now() - mBlockingStartTime).ToMilliseconds()));
+ PROFILER_MARKER_TEXT("Process", DOM,
+ MarkerTiming::IntervalUntilNowFrom(mBlockingStartTime),
+ "Blocked preallocation");
+ if (IsEmpty()) {
+ AllocateAfterDelay();
+ }
+ }
+}
+
+bool PreallocatedProcessManagerImpl::CanAllocate() {
+ return IsEnabled() && sNumBlockers == 0 &&
+ mPreallocatedProcesses.Length() < mNumberPreallocs && !IsShutdown() &&
+ (FissionAutostart() ||
+ !ContentParent::IsMaxProcessCountReached(DEFAULT_REMOTE_TYPE));
+}
+
+void PreallocatedProcessManagerImpl::AllocateAfterDelay(bool aStartup) {
+ if (!IsEnabled()) {
+ return;
+ }
+ long delay = aStartup ? StaticPrefs::dom_ipc_processPrelaunch_startupDelayMs()
+ : StaticPrefs::dom_ipc_processPrelaunch_delayMs();
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("Starting delayed process start, delay=%ld", delay));
+ NS_DelayedDispatchToCurrentThread(
+ NewRunnableMethod("PreallocatedProcessManagerImpl::AllocateOnIdle", this,
+ &PreallocatedProcessManagerImpl::AllocateOnIdle),
+ delay);
+}
+
+void PreallocatedProcessManagerImpl::AllocateOnIdle() {
+ if (!IsEnabled()) {
+ return;
+ }
+
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("Starting process allocate on idle"));
+ NS_DispatchToCurrentThreadQueue(
+ NewRunnableMethod("PreallocatedProcessManagerImpl::AllocateNow", this,
+ &PreallocatedProcessManagerImpl::AllocateNow),
+ EventQueuePriority::Idle);
+}
+
+void PreallocatedProcessManagerImpl::AllocateNow() {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("Trying to start process now"));
+ if (!CanAllocate()) {
+ if (IsEnabled() && IsEmpty() && sNumBlockers > 0) {
+ // If it's too early to allocate a process let's retry later.
+ AllocateAfterDelay();
+ }
+ return;
+ }
+
+ RefPtr<ContentParent> process = ContentParent::MakePreallocProcess();
+ mPreallocatedProcesses.AppendElement(process);
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("Preallocated = %lu of %d processes",
+ (unsigned long)mPreallocatedProcesses.Length(), mNumberPreallocs));
+
+ RefPtr<PreallocatedProcessManagerImpl> self(this);
+ process->LaunchSubprocessAsync(PROCESS_PRIORITY_PREALLOC)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [self, this, process](const RefPtr<ContentParent>&) {
+ if (process->IsDead()) {
+ Erase(process);
+ // Process died in startup (before we could add it). If it
+ // dies after this, MarkAsDead() will Erase() this entry.
+ // Shouldn't be in the sBrowserContentParents, so we don't need
+ // RemoveFromList(). We won't try to kick off a new
+ // preallocation here, to avoid possible looping if something is
+ // causing them to consistently fail; if everything is ok on the
+ // next allocation request we'll kick off creation.
+ } else {
+ // Continue prestarting processes if needed
+ if (CanAllocate()) {
+ if (mPreallocatedProcesses.Length() < mNumberPreallocs) {
+ AllocateOnIdle();
+ }
+ } else if (!IsEnabled()) {
+ // if this has a remote type set, it's been allocated for use
+ // already
+ if (process->mRemoteType == PREALLOC_REMOTE_TYPE) {
+ // This will Erase() it
+ process->ShutDownProcess(
+ ContentParent::SEND_SHUTDOWN_MESSAGE);
+ }
+ }
+ }
+ },
+ [self, this, process]() { Erase(process); });
+}
+
+void PreallocatedProcessManagerImpl::Disable() {
+ if (!mEnabled) {
+ return;
+ }
+
+ mEnabled = false;
+ CloseProcesses();
+}
+
+void PreallocatedProcessManagerImpl::CloseProcesses() {
+ while (!IsEmpty()) {
+ RefPtr<ContentParent> process(mPreallocatedProcesses.ElementAt(0));
+ mPreallocatedProcesses.RemoveElementAt(0);
+ process->ShutDownProcess(ContentParent::SEND_SHUTDOWN_MESSAGE);
+ // drop ref and let it free
+ }
+
+ // Make sure to also clear out the recycled E10S process cache, as it's also
+ // controlled by the same preference, and can be cleaned up due to memory
+ // pressure.
+ if (RefPtr<ContentParent> recycled =
+ ContentParent::sRecycledE10SProcess.forget()) {
+ recycled->MaybeBeginShutDown();
+ }
+}
+
+inline PreallocatedProcessManagerImpl*
+PreallocatedProcessManager::GetPPMImpl() {
+ if (PreallocatedProcessManagerImpl::IsShutdown()) {
+ return nullptr;
+ }
+ return PreallocatedProcessManagerImpl::Singleton();
+}
+
+/* static */
+bool PreallocatedProcessManager::Enabled() {
+ if (auto impl = GetPPMImpl()) {
+ return impl->IsEnabled();
+ }
+ return false;
+}
+
+/* static */
+void PreallocatedProcessManager::AddBlocker(const nsACString& aRemoteType,
+ ContentParent* aParent) {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("AddBlocker: %s %p (sNumBlockers=%d)",
+ PromiseFlatCString(aRemoteType).get(), aParent,
+ PreallocatedProcessManagerImpl::sNumBlockers));
+ if (auto impl = GetPPMImpl()) {
+ impl->AddBlocker(aParent);
+ }
+}
+
+/* static */
+void PreallocatedProcessManager::RemoveBlocker(const nsACString& aRemoteType,
+ ContentParent* aParent) {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("RemoveBlocker: %s %p (sNumBlockers=%d)",
+ PromiseFlatCString(aRemoteType).get(), aParent,
+ PreallocatedProcessManagerImpl::sNumBlockers));
+ if (auto impl = GetPPMImpl()) {
+ impl->RemoveBlocker(aParent);
+ }
+}
+
+/* static */
+already_AddRefed<ContentParent> PreallocatedProcessManager::Take(
+ const nsACString& aRemoteType) {
+ if (auto impl = GetPPMImpl()) {
+ return impl->Take(aRemoteType);
+ }
+ return nullptr;
+}
+
+/* static */
+void PreallocatedProcessManager::Erase(ContentParent* aParent) {
+ if (auto impl = GetPPMImpl()) {
+ impl->Erase(aParent);
+ }
+}
+
+} // namespace mozilla
diff --git a/dom/ipc/PreallocatedProcessManager.h b/dom/ipc/PreallocatedProcessManager.h
new file mode 100644
index 0000000000..9dea534dbd
--- /dev/null
+++ b/dom/ipc/PreallocatedProcessManager.h
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_PreallocatedProcessManager_h
+#define mozilla_PreallocatedProcessManager_h
+
+#include "base/basictypes.h"
+#include "mozilla/AlreadyAddRefed.h"
+#include "nsStringFwd.h"
+
+namespace mozilla {
+namespace dom {
+class ContentParent;
+} // namespace dom
+
+/**
+ * This class manages a ContentParent that it starts up ahead of any particular
+ * need. You can then call Take() to get this process and use it. Since we
+ * already started it up, it should be ready for use faster than if you'd
+ * created the process when you needed it.
+ *
+ * This class watches the dom.ipc.processPrelaunch.enabled pref. If it changes
+ * from false to true, it preallocates a process. If it changes from true to
+ * false, it kills the preallocated process, if any.
+ *
+ * We don't expect this pref to flip between true and false in production, but
+ * flipping the pref is important for tests.
+ */
+class PreallocatedProcessManagerImpl;
+
+class PreallocatedProcessManager final {
+ typedef mozilla::dom::ContentParent ContentParent;
+
+ public:
+ static PreallocatedProcessManagerImpl* GetPPMImpl();
+
+ static bool Enabled();
+
+ /**
+ * Before first paint we don't want to allocate any processes in the
+ * background. To avoid that, the PreallocatedProcessManager won't start up
+ * any processes while there is a blocker active.
+ */
+ static void AddBlocker(const nsACString& aRemoteType, ContentParent* aParent);
+ static void RemoveBlocker(const nsACString& aRemoteType,
+ ContentParent* aParent);
+
+ /**
+ * Take a preallocated process, if we have one. If we don't have a
+ * preallocated process to return, this returns null.
+ *
+ * If we use a preallocated process, it will schedule the start of
+ * another on Idle (AllocateOnIdle()).
+ */
+ static already_AddRefed<ContentParent> Take(const nsACString& aRemoteType);
+
+ /**
+ * Note that a process was shut down, and should no longer be tracked as a
+ * preallocated process.
+ */
+ static void Erase(ContentParent* aParent);
+
+ private:
+ PreallocatedProcessManager();
+ DISALLOW_EVIL_CONSTRUCTORS(PreallocatedProcessManager);
+};
+
+} // namespace mozilla
+
+#endif // defined mozilla_PreallocatedProcessManager_h
diff --git a/dom/ipc/PrefsTypes.ipdlh b/dom/ipc/PrefsTypes.ipdlh
new file mode 100644
index 0000000000..403c7aaf42
--- /dev/null
+++ b/dom/ipc/PrefsTypes.ipdlh
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* 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/. */
+
+using struct mozilla::null_t from "mozilla/ipc/IPCCore.h";
+
+namespace mozilla {
+namespace dom {
+
+union PrefValue {
+ nsCString;
+ int32_t;
+ bool;
+};
+
+// This serialization form mirrors that used in mozilla::Pref in
+// Preferences.cpp. The two should be kept in sync, e.g. if something is added
+// to one it should also be added to the other.
+//
+// Note: there is no need to pass the isSticky attribute because that's an
+// immutable attribute obtained from file at startup.
+struct Pref {
+ nsCString name;
+ bool isLocked;
+ bool isSanitized;
+ PrefValue? defaultValue;
+ PrefValue? userValue;
+};
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/ProcessActor.cpp b/dom/ipc/ProcessActor.cpp
new file mode 100644
index 0000000000..b036f625c2
--- /dev/null
+++ b/dom/ipc/ProcessActor.cpp
@@ -0,0 +1,37 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/ProcessActor.h"
+
+#include "nsContentUtils.h"
+#include "mozilla/ContentBlockingAllowList.h"
+#include "mozilla/Logging.h"
+#include "mozilla/dom/JSActorService.h"
+#include "mozilla/dom/JSProcessActorParent.h"
+#include "mozilla/dom/JSProcessActorChild.h"
+#include "mozilla/dom/JSProcessActorProtocol.h"
+
+namespace mozilla::dom {
+
+already_AddRefed<JSActorProtocol> ProcessActor::MatchingJSActorProtocol(
+ JSActorService* aActorSvc, const nsACString& aName, ErrorResult& aRv) {
+ RefPtr<JSProcessActorProtocol> proto =
+ aActorSvc->GetJSProcessActorProtocol(aName);
+ if (!proto) {
+ aRv.ThrowNotFoundError(nsPrintfCString("No such JSProcessActor '%s'",
+ PromiseFlatCString(aName).get()));
+ return nullptr;
+ }
+
+ if (!proto->Matches(GetRemoteType(), aRv)) {
+ MOZ_ASSERT(aRv.Failed());
+ return nullptr;
+ }
+ MOZ_ASSERT(!aRv.Failed());
+ return proto.forget();
+}
+
+} // namespace mozilla::dom
diff --git a/dom/ipc/ProcessActor.h b/dom/ipc/ProcessActor.h
new file mode 100644
index 0000000000..a80300b672
--- /dev/null
+++ b/dom/ipc/ProcessActor.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_ProcessActor_h
+#define mozilla_dom_ProcessActor_h
+
+#include "mozilla/dom/JSActorManager.h"
+#include "nsStringFwd.h"
+
+namespace mozilla {
+class ErrorResult;
+
+namespace dom {
+
+class JSActorProtocol;
+class JSActorService;
+
+// Common base class for Content{Parent, Child} and InProcess{Parent, Child}.
+class ProcessActor : public JSActorManager {
+ protected:
+ virtual ~ProcessActor() = default;
+
+ already_AddRefed<JSActorProtocol> MatchingJSActorProtocol(
+ JSActorService* aActorSvc, const nsACString& aName,
+ ErrorResult& aRv) final;
+
+ virtual const nsACString& GetRemoteType() const = 0;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_ProcessActor_h
diff --git a/dom/ipc/ProcessHangMonitor.cpp b/dom/ipc/ProcessHangMonitor.cpp
new file mode 100644
index 0000000000..1dcba24406
--- /dev/null
+++ b/dom/ipc/ProcessHangMonitor.cpp
@@ -0,0 +1,1399 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/ProcessHangMonitor.h"
+#include "mozilla/ProcessHangMonitorIPC.h"
+
+#include "jsapi.h"
+#include "xpcprivate.h"
+
+#include "mozilla/Atomics.h"
+#include "mozilla/BackgroundHangMonitor.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/dom/CancelContentJSOptionsBinding.h"
+#include "mozilla/dom/CanonicalBrowsingContext.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/Document.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/dom/BrowserChild.h"
+#include "mozilla/dom/BrowserParent.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/ipc/ProcessChild.h"
+#include "mozilla/ipc/TaskFactory.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/StaticMonitor.h"
+#include "mozilla/StaticPrefs_browser.h"
+#include "mozilla/StaticPrefs_dom.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/Unused.h"
+#include "mozilla/WeakPtr.h"
+
+#include "MainThreadUtils.h"
+#include "nsExceptionHandler.h"
+#include "nsFrameLoader.h"
+#include "nsIHangReport.h"
+#include "nsIRemoteTab.h"
+#include "nsNetUtil.h"
+#include "nsQueryObject.h"
+#include "nsThreadUtils.h"
+
+#include "base/task.h"
+#include "base/thread.h"
+
+#ifdef XP_WIN
+// For IsDebuggerPresent()
+# include <windows.h>
+#endif
+
+#ifdef XP_MACOSX
+// for qos controls
+# include <sys/qos.h>
+#endif
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace mozilla::ipc;
+
+/*
+ * Basic architecture:
+ *
+ * Each process has its own ProcessHangMonitor singleton. This singleton exists
+ * as long as there is at least one content process in the system. Each content
+ * process has a HangMonitorChild and the chrome process has one
+ * HangMonitorParent per process. Each process (including the chrome process)
+ * runs a hang monitoring thread. The PHangMonitor actors are bound to this
+ * thread so that they never block on the main thread.
+ *
+ * When the content process detects a hang, it posts a task to its hang thread,
+ * which sends an IPC message to the hang thread in the parent. The parent
+ * cancels any ongoing CPOW requests and then posts a runnable to the main
+ * thread that notifies Firefox frontend code of the hang. The frontend code is
+ * passed an nsIHangReport, which can be used to terminate the hang.
+ *
+ * If the user chooses to terminate a script, a task is posted to the chrome
+ * process's hang monitoring thread, which sends an IPC message to the hang
+ * thread in the content process. That thread sets a flag to indicate that JS
+ * execution should be terminated the next time it hits the interrupt
+ * callback. A similar scheme is used for debugging slow scripts. If a content
+ * process or plug-in needs to be terminated, the chrome process does so
+ * directly, without messaging the content process.
+ */
+
+namespace {
+
+/* Child process objects */
+
+class HangMonitorChild : public PProcessHangMonitorChild,
+ public BackgroundHangAnnotator {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DELETE_ON_MAIN_THREAD(
+ HangMonitorChild, override)
+
+ void Bind(Endpoint<PProcessHangMonitorChild>&& aEndpoint);
+
+ using SlowScriptAction = ProcessHangMonitor::SlowScriptAction;
+ SlowScriptAction NotifySlowScript(nsIBrowserChild* aBrowserChild,
+ const char* aFileName,
+ const nsString& aAddonId,
+ const double aDuration);
+ void NotifySlowScriptAsync(TabId aTabId, const nsCString& aFileName,
+ const nsString& aAddonId, const double aDuration);
+
+ bool IsDebuggerStartupComplete();
+
+ void ClearHang();
+ void ClearHangAsync();
+ void ClearPaintWhileInterruptingJS();
+
+ // MaybeStartPaintWhileInterruptingJS will notify the background hang monitor
+ // of activity if this is the first time calling it since
+ // ClearPaintWhileInterruptingJS. It should be callable from any thread, but
+ // you must be holding mMonitor if using it off the main thread, since it
+ // could race with ClearPaintWhileInterruptingJS.
+ void MaybeStartPaintWhileInterruptingJS();
+
+ mozilla::ipc::IPCResult RecvTerminateScript() override;
+ mozilla::ipc::IPCResult RecvRequestContentJSInterrupt() override;
+ mozilla::ipc::IPCResult RecvBeginStartingDebugger() override;
+ mozilla::ipc::IPCResult RecvEndStartingDebugger() override;
+
+ mozilla::ipc::IPCResult RecvPaintWhileInterruptingJS(
+ const TabId& aTabId) override;
+
+ mozilla::ipc::IPCResult RecvUnloadLayersWhileInterruptingJS(
+ const TabId& aTabId) override;
+
+ mozilla::ipc::IPCResult RecvCancelContentJSExecutionIfRunning(
+ const TabId& aTabId, const nsIRemoteTab::NavigationType& aNavigationType,
+ const int32_t& aNavigationIndex,
+ const mozilla::Maybe<nsCString>& aNavigationURI,
+ const int32_t& aEpoch) override;
+
+ mozilla::ipc::IPCResult RecvSetMainThreadQoSPriority(
+ const nsIThread::QoSPriority& aQoSPriority) override;
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ bool InterruptCallback();
+ void Shutdown();
+
+ static HangMonitorChild* Get() MOZ_REQUIRES(sMainThreadCapability) {
+ return sInstance;
+ }
+
+ static void CreateAndBind(ProcessHangMonitor* aMonitor,
+ Endpoint<PProcessHangMonitorChild>&& aEndpoint);
+
+ void Dispatch(already_AddRefed<nsIRunnable> aRunnable) {
+ mHangMonitor->Dispatch(std::move(aRunnable));
+ }
+ bool IsOnThread() { return mHangMonitor->IsOnThread(); }
+
+ void AnnotateHang(BackgroundHangAnnotations& aAnnotations) override;
+
+ protected:
+ friend class mozilla::ProcessHangMonitor;
+
+ private:
+ explicit HangMonitorChild(ProcessHangMonitor* aMonitor);
+ ~HangMonitorChild() override;
+
+ void ShutdownOnThread();
+
+ static StaticRefPtr<HangMonitorChild> sInstance
+ MOZ_GUARDED_BY(sMainThreadCapability);
+
+ const RefPtr<ProcessHangMonitor> mHangMonitor;
+
+#ifdef XP_MACOSX
+ // On macOS, the pthread_t is required to start a QoS class override. As we
+ // can't recover this from a PRThread*, we need to record it when the
+ // HangMonitorChild is initially created on the main thread.
+ const pthread_t mMainPThread;
+#endif
+
+ Monitor mMonitor;
+
+ // Main thread-only.
+ bool mSentReport;
+
+ // These fields must be accessed with mMonitor held.
+ bool mTerminateScript MOZ_GUARDED_BY(mMonitor);
+ bool mStartDebugger MOZ_GUARDED_BY(mMonitor);
+ bool mFinishedStartingDebugger MOZ_GUARDED_BY(mMonitor);
+
+ // this variable is used to paint/unload layers
+ // if not set, no action required
+ // true means, we will paint. false - unload layers
+ Maybe<bool> mPaintWhileInterruptingJS MOZ_GUARDED_BY(mMonitor);
+ TabId mPaintWhileInterruptingJSTab MOZ_GUARDED_BY(mMonitor);
+ bool mCancelContentJS MOZ_GUARDED_BY(mMonitor);
+ TabId mCancelContentJSTab MOZ_GUARDED_BY(mMonitor);
+ nsIRemoteTab::NavigationType mCancelContentJSNavigationType
+ MOZ_GUARDED_BY(mMonitor);
+ int32_t mCancelContentJSNavigationIndex MOZ_GUARDED_BY(mMonitor);
+ mozilla::Maybe<nsCString> mCancelContentJSNavigationURI
+ MOZ_GUARDED_BY(mMonitor);
+ int32_t mCancelContentJSEpoch MOZ_GUARDED_BY(mMonitor);
+ bool mShutdownDone MOZ_GUARDED_BY(mMonitor);
+
+ JSContext* mContext; // const after constructor
+
+ // This field is only accessed on the hang thread.
+ bool mIPCOpen;
+
+ // Allows us to ensure we NotifyActivity only once, allowing
+ // either thread to do so.
+ Atomic<bool> mPaintWhileInterruptingJSActive;
+};
+
+StaticRefPtr<HangMonitorChild> HangMonitorChild::sInstance;
+
+/* Parent process objects */
+
+class HangMonitorParent;
+
+class HangMonitoredProcess final : public nsIHangReport {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ HangMonitoredProcess(HangMonitorParent* aActor, ContentParent* aContentParent)
+ : mActor(aActor), mContentParent(aContentParent) {}
+
+ NS_DECL_NSIHANGREPORT
+
+ // Called when a content process shuts down.
+ void Clear() {
+ mContentParent = nullptr;
+ mActor = nullptr;
+ }
+
+ /**
+ * Sets the information associated with this hang: this includes the tab ID,
+ * filename, duration, and an add-on ID if it was caused by an add-on.
+ *
+ * @param aDumpId The ID of a minidump taken when the hang occurred
+ */
+ void SetSlowScriptData(const SlowScriptData& aSlowScriptData,
+ const nsAString& aDumpId) {
+ mSlowScriptData = aSlowScriptData;
+ mDumpId = aDumpId;
+ }
+
+ void ClearHang() {
+ mSlowScriptData = SlowScriptData();
+ mDumpId.Truncate();
+ }
+
+ private:
+ ~HangMonitoredProcess() = default;
+
+ // Everything here is main thread-only.
+ HangMonitorParent* mActor;
+ ContentParent* mContentParent;
+ SlowScriptData mSlowScriptData;
+ nsAutoString mDumpId;
+};
+
+class HangMonitorParent : public PProcessHangMonitorParent {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING_WITH_DELETE_ON_MAIN_THREAD(
+ HangMonitorParent, override)
+
+ explicit HangMonitorParent(ProcessHangMonitor* aMonitor);
+
+ void Bind(Endpoint<PProcessHangMonitorParent>&& aEndpoint);
+
+ mozilla::ipc::IPCResult RecvHangEvidence(
+ const SlowScriptData& aSlowScriptData) override;
+ mozilla::ipc::IPCResult RecvClearHang() override;
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ void SetProcess(HangMonitoredProcess* aProcess) { mProcess = aProcess; }
+
+ void Shutdown();
+
+ void PaintWhileInterruptingJS(dom::BrowserParent* aTab);
+
+ void UnloadLayersWhileInterruptingJS(dom::BrowserParent* aTab);
+ void CancelContentJSExecutionIfRunning(
+ dom::BrowserParent* aBrowserParent,
+ nsIRemoteTab::NavigationType aNavigationType,
+ const dom::CancelContentJSOptions& aCancelContentJSOptions);
+
+ void SetMainThreadQoSPriority(nsIThread::QoSPriority aQoSPriority);
+
+ void TerminateScript();
+ void BeginStartingDebugger();
+ void EndStartingDebugger();
+
+ nsresult Dispatch(already_AddRefed<nsIRunnable> aRunnable) {
+ return mHangMonitor->Dispatch(std::move(aRunnable));
+ }
+ bool IsOnThread() { return mHangMonitor->IsOnThread(); }
+
+ private:
+ ~HangMonitorParent() override = default;
+
+ void SendHangNotification(const SlowScriptData& aSlowScriptData,
+ const nsString& aBrowserDumpId);
+
+ void ClearHangNotification();
+
+ void PaintOrUnloadLayersWhileInterruptingJSOnThread(bool aPaint,
+ TabId aTabId);
+ void CancelContentJSExecutionIfRunningOnThread(
+ TabId aTabId, nsIRemoteTab::NavigationType aNavigationType,
+ int32_t aNavigationIndex, nsIURI* aNavigationURI, int32_t aEpoch);
+
+#ifdef XP_MACOSX
+ void SetMainThreadQoSPriorityOnThread(nsIThread::QoSPriority aQoSPriority);
+#endif
+
+ void ShutdownOnThread();
+
+ const RefPtr<ProcessHangMonitor> mHangMonitor;
+
+ // This field is only accessed on the hang thread.
+ bool mIPCOpen;
+
+ Monitor mMonitor;
+
+ // MainThread only
+ RefPtr<HangMonitoredProcess> mProcess;
+
+ // Must be accessed with mMonitor held.
+ bool mShutdownDone MOZ_GUARDED_BY(mMonitor);
+ mozilla::ipc::TaskFactory<HangMonitorParent> mMainThreadTaskFactory
+ MOZ_GUARDED_BY(mMonitor);
+};
+
+} // namespace
+
+/* HangMonitorChild implementation */
+
+HangMonitorChild::HangMonitorChild(ProcessHangMonitor* aMonitor)
+ : mHangMonitor(aMonitor),
+#ifdef XP_MACOSX
+ mMainPThread(pthread_self()),
+#endif
+ mMonitor("HangMonitorChild lock"),
+ mSentReport(false),
+ mTerminateScript(false),
+ mStartDebugger(false),
+ mFinishedStartingDebugger(false),
+ mCancelContentJS(false),
+ mCancelContentJSNavigationType(nsIRemoteTab::NAVIGATE_BACK),
+ mCancelContentJSNavigationIndex(0),
+ mCancelContentJSEpoch(0),
+ mShutdownDone(false),
+ mIPCOpen(true),
+ mPaintWhileInterruptingJSActive(false) {
+ ReleaseAssertIsOnMainThread();
+ MOZ_ASSERT(!sInstance);
+
+ mContext = danger::GetJSContext();
+}
+
+HangMonitorChild::~HangMonitorChild() {
+ ReleaseAssertIsOnMainThread();
+ MOZ_ASSERT(sInstance != this);
+}
+
+void HangMonitorChild::CreateAndBind(
+ ProcessHangMonitor* aMonitor,
+ Endpoint<PProcessHangMonitorChild>&& aEndpoint) {
+ ReleaseAssertIsOnMainThread();
+ MOZ_ASSERT(!sInstance);
+
+ sInstance = new HangMonitorChild(aMonitor);
+
+ BackgroundHangMonitor::RegisterAnnotator(*sInstance);
+
+ aMonitor->Dispatch(NewRunnableMethod<Endpoint<PProcessHangMonitorChild>&&>(
+ "HangMonitorChild::Bind", sInstance.get(), &HangMonitorChild::Bind,
+ std::move(aEndpoint)));
+}
+
+bool HangMonitorChild::InterruptCallback() {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ if (StaticPrefs::dom_abort_script_on_child_shutdown() &&
+ mozilla::ipc::ProcessChild::ExpectingShutdown()) {
+ // We preserve chrome JS from cancel, but not extension content JS.
+ if (!nsContentUtils::IsCallerChrome()) {
+ NS_WARNING(
+ "HangMonitorChild::InterruptCallback: ExpectingShutdown, "
+ "canceling content JS execution.\n");
+ return false;
+ }
+ return true;
+ }
+
+ // Don't start painting if we're not in a good place to run script. We run
+ // chrome script during layout and such, and it wouldn't be good to interrupt
+ // painting code from there.
+ if (!nsContentUtils::IsSafeToRunScript()) {
+ return true;
+ }
+
+ Maybe<bool> paintWhileInterruptingJS;
+ TabId paintWhileInterruptingJSTab;
+
+ {
+ MonitorAutoLock lock(mMonitor);
+ paintWhileInterruptingJS = mPaintWhileInterruptingJS;
+ paintWhileInterruptingJSTab = mPaintWhileInterruptingJSTab;
+
+ mPaintWhileInterruptingJS.reset();
+ }
+
+ if (paintWhileInterruptingJS.isSome()) {
+ RefPtr<BrowserChild> browserChild =
+ BrowserChild::FindBrowserChild(paintWhileInterruptingJSTab);
+ if (browserChild) {
+ js::AutoAssertNoContentJS nojs(mContext);
+ if (paintWhileInterruptingJS.value()) {
+ browserChild->PaintWhileInterruptingJS();
+ } else {
+ browserChild->UnloadLayersWhileInterruptingJS();
+ }
+ }
+ }
+
+ // Only handle the interrupt for cancelling content JS if we have a
+ // non-privileged script (i.e. not part of Gecko or an add-on).
+ JS::Rooted<JSObject*> global(mContext, JS::CurrentGlobalOrNull(mContext));
+ nsIPrincipal* principal = xpc::GetObjectPrincipal(global);
+ if (principal && (principal->IsSystemPrincipal() ||
+ principal->GetIsAddonOrExpandedAddonPrincipal())) {
+ return true;
+ }
+
+ nsCOMPtr<nsPIDOMWindowInner> win = xpc::WindowOrNull(global);
+ if (!win) {
+ return true;
+ }
+
+ bool cancelContentJS;
+ TabId cancelContentJSTab;
+ nsIRemoteTab::NavigationType cancelContentJSNavigationType;
+ int32_t cancelContentJSNavigationIndex;
+ mozilla::Maybe<nsCString> cancelContentJSNavigationURI;
+ int32_t cancelContentJSEpoch;
+
+ {
+ MonitorAutoLock lock(mMonitor);
+ cancelContentJS = mCancelContentJS;
+ cancelContentJSTab = mCancelContentJSTab;
+ cancelContentJSNavigationType = mCancelContentJSNavigationType;
+ cancelContentJSNavigationIndex = mCancelContentJSNavigationIndex;
+ cancelContentJSNavigationURI = std::move(mCancelContentJSNavigationURI);
+ cancelContentJSEpoch = mCancelContentJSEpoch;
+
+ mCancelContentJS = false;
+ }
+
+ if (cancelContentJS) {
+ js::AutoAssertNoContentJS nojs(mContext);
+
+ RefPtr<BrowserChild> browserChild =
+ BrowserChild::FindBrowserChild(cancelContentJSTab);
+ RefPtr<BrowserChild> browserChildFromWin = BrowserChild::GetFrom(win);
+ if (!browserChild || !browserChildFromWin) {
+ return true;
+ }
+
+ TabId tabIdFromWin = browserChildFromWin->GetTabId();
+ if (tabIdFromWin != cancelContentJSTab) {
+ // The currently-executing content JS doesn't belong to the tab that
+ // requested cancellation of JS. Just return and let the JS continue.
+ return true;
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsIURI> uri;
+
+ if (cancelContentJSNavigationURI) {
+ rv = NS_NewURI(getter_AddRefs(uri), cancelContentJSNavigationURI.value());
+ if (NS_FAILED(rv)) {
+ return true;
+ }
+ }
+
+ bool canCancel;
+ rv = browserChild->CanCancelContentJS(cancelContentJSNavigationType,
+ cancelContentJSNavigationIndex, uri,
+ cancelContentJSEpoch, &canCancel);
+ if (NS_SUCCEEDED(rv) && canCancel) {
+ // Don't add this page to the BF cache, since we're cancelling its JS.
+ if (Document* doc = win->GetExtantDoc()) {
+ doc->DisallowBFCaching();
+ }
+
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void HangMonitorChild::AnnotateHang(BackgroundHangAnnotations& aAnnotations) {
+ if (mPaintWhileInterruptingJSActive) {
+ aAnnotations.AddAnnotation(u"PaintWhileInterruptingJS"_ns, true);
+ }
+}
+
+void HangMonitorChild::Shutdown() {
+ ReleaseAssertIsOnMainThread();
+
+ BackgroundHangMonitor::UnregisterAnnotator(*this);
+
+ {
+ MonitorAutoLock lock(mMonitor);
+ while (!mShutdownDone) {
+ mMonitor.Wait();
+ }
+ }
+
+ MOZ_ASSERT(sInstance == this);
+ sInstance = nullptr;
+}
+
+void HangMonitorChild::ShutdownOnThread() {
+ MOZ_RELEASE_ASSERT(IsOnThread());
+
+ MonitorAutoLock lock(mMonitor);
+ mShutdownDone = true;
+ mMonitor.Notify();
+}
+
+void HangMonitorChild::ActorDestroy(ActorDestroyReason aWhy) {
+ MOZ_RELEASE_ASSERT(IsOnThread());
+
+ mIPCOpen = false;
+
+ // We use a task here to ensure that IPDL is finished with this
+ // HangMonitorChild before it gets deleted on the main thread.
+ Dispatch(NewNonOwningRunnableMethod("HangMonitorChild::ShutdownOnThread",
+ this,
+ &HangMonitorChild::ShutdownOnThread));
+}
+
+mozilla::ipc::IPCResult HangMonitorChild::RecvTerminateScript() {
+ MOZ_RELEASE_ASSERT(IsOnThread());
+
+ MonitorAutoLock lock(mMonitor);
+ mTerminateScript = true;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult HangMonitorChild::RecvRequestContentJSInterrupt() {
+ MOZ_RELEASE_ASSERT(IsOnThread());
+
+ // In order to cancel JS execution on shutdown, we expect that
+ // ProcessChild::NotifiedImpendingShutdown has been called before.
+ if (mozilla::ipc::ProcessChild::ExpectingShutdown()) {
+ CrashReporter::AppendToCrashReportAnnotation(
+ CrashReporter::Annotation::IPCShutdownState,
+ "HangMonitorChild::RecvRequestContentJSInterrupt (expected)"_ns);
+ } else {
+ CrashReporter::AppendToCrashReportAnnotation(
+ CrashReporter::Annotation::IPCShutdownState,
+ "HangMonitorChild::RecvRequestContentJSInterrupt (unexpected)"_ns);
+ }
+ JS_RequestInterruptCallback(mContext);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult HangMonitorChild::RecvBeginStartingDebugger() {
+ MOZ_RELEASE_ASSERT(IsOnThread());
+
+ MonitorAutoLock lock(mMonitor);
+ mStartDebugger = true;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult HangMonitorChild::RecvEndStartingDebugger() {
+ MOZ_RELEASE_ASSERT(IsOnThread());
+
+ MonitorAutoLock lock(mMonitor);
+ mFinishedStartingDebugger = true;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult HangMonitorChild::RecvPaintWhileInterruptingJS(
+ const TabId& aTabId) {
+ MOZ_RELEASE_ASSERT(IsOnThread());
+
+ {
+ MonitorAutoLock lock(mMonitor);
+ MaybeStartPaintWhileInterruptingJS();
+ mPaintWhileInterruptingJS = Some(true);
+ mPaintWhileInterruptingJSTab = aTabId;
+ }
+
+ JS_RequestInterruptCallback(mContext);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult HangMonitorChild::RecvUnloadLayersWhileInterruptingJS(
+ const TabId& aTabId) {
+ MOZ_RELEASE_ASSERT(IsOnThread());
+
+ {
+ MonitorAutoLock lock(mMonitor);
+ MaybeStartPaintWhileInterruptingJS();
+ mPaintWhileInterruptingJS = Some(false);
+ mPaintWhileInterruptingJSTab = aTabId;
+ }
+
+ JS_RequestInterruptCallback(mContext);
+
+ return IPC_OK();
+}
+
+void HangMonitorChild::MaybeStartPaintWhileInterruptingJS() {
+ mPaintWhileInterruptingJSActive = true;
+}
+
+void HangMonitorChild::ClearPaintWhileInterruptingJS() {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ MOZ_RELEASE_ASSERT(XRE_IsContentProcess());
+ mPaintWhileInterruptingJSActive = false;
+}
+
+mozilla::ipc::IPCResult HangMonitorChild::RecvCancelContentJSExecutionIfRunning(
+ const TabId& aTabId, const nsIRemoteTab::NavigationType& aNavigationType,
+ const int32_t& aNavigationIndex,
+ const mozilla::Maybe<nsCString>& aNavigationURI, const int32_t& aEpoch) {
+ MOZ_RELEASE_ASSERT(IsOnThread());
+
+ {
+ MonitorAutoLock lock(mMonitor);
+ mCancelContentJS = true;
+ mCancelContentJSTab = aTabId;
+ mCancelContentJSNavigationType = aNavigationType;
+ mCancelContentJSNavigationIndex = aNavigationIndex;
+ mCancelContentJSNavigationURI = aNavigationURI;
+ mCancelContentJSEpoch = aEpoch;
+ }
+
+ JS_RequestInterruptCallback(mContext);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult HangMonitorChild::RecvSetMainThreadQoSPriority(
+ const nsIThread::QoSPriority& aQoSPriority) {
+ MOZ_RELEASE_ASSERT(IsOnThread());
+
+#ifdef XP_MACOSX
+ // If the new priority is the background (low) priority, we can tell the OS to
+ // put the main thread on low-power cores. Alternately, if we are changing
+ // from the background to a higher priority, we change the main thread back to
+ // the |user-interactive| state, defined in MacOS's QoS documentation as
+ // reserved for main threads.
+ qos_class_t qosClass = aQoSPriority == nsIThread::QOS_PRIORITY_LOW
+ ? QOS_CLASS_BACKGROUND
+ : QOS_CLASS_USER_INTERACTIVE;
+
+ // We can't directly set the main thread's QoS class from off-main-thread.
+ // However, we can start a QoS class override to raise the QoS, then dispatch
+ // a runnable to set the QoS class and clear the override once complete.
+ pthread_override_t qosOverride =
+ pthread_override_qos_class_start_np(mMainPThread, qosClass, 0);
+ if (NS_FAILED(NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "HangMonitorChild::RecvSetMainThreadQoSPriority",
+ [qosClass, qosOverride] {
+ pthread_set_qos_class_self_np(qosClass, 0);
+ if (qosOverride) {
+ pthread_override_qos_class_end_np(qosOverride);
+ }
+ })))) {
+ // If we fail to dispatch, go ahead and end the override anyway.
+ pthread_override_qos_class_end_np(qosOverride);
+ }
+#endif
+
+ return IPC_OK();
+}
+
+void HangMonitorChild::Bind(Endpoint<PProcessHangMonitorChild>&& aEndpoint) {
+ MOZ_RELEASE_ASSERT(IsOnThread());
+
+ DebugOnly<bool> ok = aEndpoint.Bind(this);
+ MOZ_ASSERT(ok);
+}
+
+void HangMonitorChild::NotifySlowScriptAsync(TabId aTabId,
+ const nsCString& aFileName,
+ const nsString& aAddonId,
+ const double aDuration) {
+ if (mIPCOpen) {
+ Unused << SendHangEvidence(
+ SlowScriptData(aTabId, aFileName, aAddonId, aDuration));
+ }
+}
+
+HangMonitorChild::SlowScriptAction HangMonitorChild::NotifySlowScript(
+ nsIBrowserChild* aBrowserChild, const char* aFileName,
+ const nsString& aAddonId, const double aDuration) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ mSentReport = true;
+
+ {
+ MonitorAutoLock lock(mMonitor);
+
+ if (mTerminateScript) {
+ mTerminateScript = false;
+ return SlowScriptAction::Terminate;
+ }
+
+ if (mStartDebugger) {
+ mStartDebugger = false;
+ return SlowScriptAction::StartDebugger;
+ }
+ }
+
+ TabId id;
+ if (aBrowserChild) {
+ RefPtr<BrowserChild> browserChild =
+ static_cast<BrowserChild*>(aBrowserChild);
+ id = browserChild->GetTabId();
+ }
+ nsAutoCString filename(aFileName);
+
+ Dispatch(NewNonOwningRunnableMethod<TabId, nsCString, nsString, double>(
+ "HangMonitorChild::NotifySlowScriptAsync", this,
+ &HangMonitorChild::NotifySlowScriptAsync, id, filename, aAddonId,
+ aDuration));
+ return SlowScriptAction::Continue;
+}
+
+bool HangMonitorChild::IsDebuggerStartupComplete() {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ MonitorAutoLock lock(mMonitor);
+
+ if (mFinishedStartingDebugger) {
+ mFinishedStartingDebugger = false;
+ return true;
+ }
+
+ return false;
+}
+
+void HangMonitorChild::ClearHang() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (mSentReport) {
+ // bounce to background thread
+ Dispatch(NewNonOwningRunnableMethod("HangMonitorChild::ClearHangAsync",
+ this,
+ &HangMonitorChild::ClearHangAsync));
+
+ MonitorAutoLock lock(mMonitor);
+ mSentReport = false;
+ mTerminateScript = false;
+ mStartDebugger = false;
+ mFinishedStartingDebugger = false;
+ }
+}
+
+void HangMonitorChild::ClearHangAsync() {
+ MOZ_RELEASE_ASSERT(IsOnThread());
+
+ // bounce back to parent on background thread
+ if (mIPCOpen) {
+ Unused << SendClearHang();
+ }
+}
+
+/* HangMonitorParent implementation */
+
+HangMonitorParent::HangMonitorParent(ProcessHangMonitor* aMonitor)
+ : mHangMonitor(aMonitor),
+ mIPCOpen(true),
+ mMonitor("HangMonitorParent lock"),
+ mShutdownDone(false),
+ mMainThreadTaskFactory(this) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+}
+
+void HangMonitorParent::Shutdown() {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ MonitorAutoLock lock(mMonitor);
+
+ if (mProcess) {
+ mProcess->Clear();
+ mProcess = nullptr;
+ }
+
+ nsresult rv = Dispatch(
+ NewNonOwningRunnableMethod("HangMonitorParent::ShutdownOnThread", this,
+ &HangMonitorParent::ShutdownOnThread));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+
+ while (!mShutdownDone) {
+ mMonitor.Wait();
+ }
+}
+
+void HangMonitorParent::ShutdownOnThread() {
+ MOZ_RELEASE_ASSERT(IsOnThread());
+
+ // mIPCOpen is only written from this thread, so need need to take the lock
+ // here. We'd be shooting ourselves in the foot, because ActorDestroy takes
+ // it.
+ if (mIPCOpen) {
+ Close();
+ }
+
+ MonitorAutoLock lock(mMonitor);
+ mShutdownDone = true;
+ mMonitor.Notify();
+}
+
+void HangMonitorParent::PaintWhileInterruptingJS(dom::BrowserParent* aTab) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ if (StaticPrefs::browser_tabs_remote_force_paint()) {
+ TabId id = aTab->GetTabId();
+ Dispatch(NewNonOwningRunnableMethod<bool, TabId>(
+ "HangMonitorParent::PaintOrUnloadLayersWhileInterruptingJSOnThread ",
+ this,
+ &HangMonitorParent::PaintOrUnloadLayersWhileInterruptingJSOnThread,
+ true, id));
+ }
+}
+
+void HangMonitorParent::UnloadLayersWhileInterruptingJS(
+ dom::BrowserParent* aTab) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ TabId id = aTab->GetTabId();
+ Dispatch(NewNonOwningRunnableMethod<bool, TabId>(
+ "HangMonitorParent::PaintOrUnloadLayersWhileInterruptingJSOnThread ",
+ this, &HangMonitorParent::PaintOrUnloadLayersWhileInterruptingJSOnThread,
+ false, id));
+}
+
+void HangMonitorParent::PaintOrUnloadLayersWhileInterruptingJSOnThread(
+ const bool aPaint, TabId aTabId) {
+ MOZ_RELEASE_ASSERT(IsOnThread());
+
+ if (mIPCOpen) {
+ if (aPaint) {
+ Unused << SendPaintWhileInterruptingJS(aTabId);
+ } else {
+ Unused << SendUnloadLayersWhileInterruptingJS(aTabId);
+ }
+ }
+}
+
+void HangMonitorParent::CancelContentJSExecutionIfRunning(
+ dom::BrowserParent* aBrowserParent,
+ nsIRemoteTab::NavigationType aNavigationType,
+ const dom::CancelContentJSOptions& aCancelContentJSOptions) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ if (!aBrowserParent->CanCancelContentJS(aNavigationType,
+ aCancelContentJSOptions.mIndex,
+ aCancelContentJSOptions.mUri)) {
+ return;
+ }
+
+ TabId id = aBrowserParent->GetTabId();
+ Dispatch(NewNonOwningRunnableMethod<TabId, nsIRemoteTab::NavigationType,
+ int32_t, nsIURI*, int32_t>(
+ "HangMonitorParent::CancelContentJSExecutionIfRunningOnThread", this,
+ &HangMonitorParent::CancelContentJSExecutionIfRunningOnThread, id,
+ aNavigationType, aCancelContentJSOptions.mIndex,
+ aCancelContentJSOptions.mUri, aCancelContentJSOptions.mEpoch));
+}
+
+void HangMonitorParent::CancelContentJSExecutionIfRunningOnThread(
+ TabId aTabId, nsIRemoteTab::NavigationType aNavigationType,
+ int32_t aNavigationIndex, nsIURI* aNavigationURI, int32_t aEpoch) {
+ MOZ_RELEASE_ASSERT(IsOnThread());
+
+ mozilla::Maybe<nsCString> spec;
+ if (aNavigationURI) {
+ nsAutoCString tmp;
+ nsresult rv = aNavigationURI->GetSpec(tmp);
+ if (NS_SUCCEEDED(rv)) {
+ spec.emplace(tmp);
+ }
+ }
+
+ if (mIPCOpen) {
+ Unused << SendCancelContentJSExecutionIfRunning(
+ aTabId, aNavigationType, aNavigationIndex, spec, aEpoch);
+ }
+}
+
+void HangMonitorParent::SetMainThreadQoSPriority(
+ nsIThread::QoSPriority aQoSPriority) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+#ifdef XP_MACOSX // Should not be using outside of MacOS.
+
+ Dispatch(NewNonOwningRunnableMethod<nsIThread::QoSPriority>(
+ "HangMonitorParent::SetMainThreadQoSPriorityOnThread", this,
+ &HangMonitorParent::SetMainThreadQoSPriorityOnThread, aQoSPriority));
+#endif
+}
+
+#ifdef XP_MACOSX
+void HangMonitorParent::SetMainThreadQoSPriorityOnThread(
+ nsIThread::QoSPriority aQoSPriority) {
+ MOZ_RELEASE_ASSERT(IsOnThread());
+ if (mIPCOpen) {
+ Unused << SendSetMainThreadQoSPriority(aQoSPriority);
+ }
+}
+#endif
+
+void HangMonitorParent::ActorDestroy(ActorDestroyReason aWhy) {
+ MOZ_RELEASE_ASSERT(IsOnThread());
+ mIPCOpen = false;
+}
+
+void HangMonitorParent::Bind(Endpoint<PProcessHangMonitorParent>&& aEndpoint) {
+ MOZ_RELEASE_ASSERT(IsOnThread());
+
+ DebugOnly<bool> ok = aEndpoint.Bind(this);
+ MOZ_ASSERT(ok);
+}
+
+void HangMonitorParent::SendHangNotification(
+ const SlowScriptData& aSlowScriptData, const nsString& aBrowserDumpId) {
+ // chrome process, main thread
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ nsString dumpId;
+
+ // We already have a full minidump; go ahead and use it.
+ dumpId = aBrowserDumpId;
+
+ mProcess->SetSlowScriptData(aSlowScriptData, dumpId);
+
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ observerService->NotifyObservers(mProcess, "process-hang-report", nullptr);
+}
+
+void HangMonitorParent::ClearHangNotification() {
+ // chrome process, main thread
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ observerService->NotifyObservers(mProcess, "clear-hang-report", nullptr);
+
+ mProcess->ClearHang();
+}
+
+mozilla::ipc::IPCResult HangMonitorParent::RecvHangEvidence(
+ const SlowScriptData& aSlowScriptData) {
+ // chrome process, background thread
+ MOZ_RELEASE_ASSERT(IsOnThread());
+
+ if (!StaticPrefs::dom_ipc_reportProcessHangs()) {
+ return IPC_OK();
+ }
+
+#ifdef XP_WIN
+ // Don't report hangs if we're debugging the process. You can comment this
+ // line out for testing purposes.
+ if (IsDebuggerPresent()) {
+ return IPC_OK();
+ }
+#endif
+
+ // Before we wake up the browser main thread we want to take a
+ // browser minidump.
+ nsAutoString crashId;
+
+ mHangMonitor->InitiateCPOWTimeout();
+
+ MonitorAutoLock lock(mMonitor);
+
+ NS_DispatchToMainThread(mMainThreadTaskFactory.NewRunnableMethod(
+ &HangMonitorParent::SendHangNotification, aSlowScriptData, crashId));
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult HangMonitorParent::RecvClearHang() {
+ // chrome process, background thread
+ MOZ_RELEASE_ASSERT(IsOnThread());
+
+ if (!StaticPrefs::dom_ipc_reportProcessHangs()) {
+ return IPC_OK();
+ }
+
+ mHangMonitor->InitiateCPOWTimeout();
+
+ MonitorAutoLock lock(mMonitor);
+
+ NS_DispatchToMainThread(mMainThreadTaskFactory.NewRunnableMethod(
+ &HangMonitorParent::ClearHangNotification));
+
+ return IPC_OK();
+}
+
+void HangMonitorParent::TerminateScript() {
+ MOZ_RELEASE_ASSERT(IsOnThread());
+
+ if (mIPCOpen) {
+ Unused << SendTerminateScript();
+ }
+}
+
+void HangMonitorParent::BeginStartingDebugger() {
+ MOZ_RELEASE_ASSERT(IsOnThread());
+
+ if (mIPCOpen) {
+ Unused << SendBeginStartingDebugger();
+ }
+}
+
+void HangMonitorParent::EndStartingDebugger() {
+ MOZ_RELEASE_ASSERT(IsOnThread());
+
+ if (mIPCOpen) {
+ Unused << SendEndStartingDebugger();
+ }
+}
+
+/* HangMonitoredProcess implementation */
+
+NS_IMPL_ISUPPORTS(HangMonitoredProcess, nsIHangReport)
+
+NS_IMETHODIMP
+HangMonitoredProcess::GetHangDuration(double* aHangDuration) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ *aHangDuration = mSlowScriptData.duration();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+HangMonitoredProcess::GetScriptBrowser(Element** aBrowser) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ TabId tabId = mSlowScriptData.tabId();
+ if (!mContentParent) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ nsTArray<PBrowserParent*> tabs;
+ mContentParent->ManagedPBrowserParent(tabs);
+ for (size_t i = 0; i < tabs.Length(); i++) {
+ BrowserParent* tp = BrowserParent::GetFrom(tabs[i]);
+ if (tp->GetTabId() == tabId) {
+ RefPtr<Element> node = tp->GetOwnerElement();
+ node.forget(aBrowser);
+ return NS_OK;
+ }
+ }
+
+ *aBrowser = nullptr;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+HangMonitoredProcess::GetScriptFileName(nsACString& aFileName) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ aFileName = mSlowScriptData.filename();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+HangMonitoredProcess::GetAddonId(nsAString& aAddonId) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ aAddonId = mSlowScriptData.addonId();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+HangMonitoredProcess::TerminateScript() {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ if (!mActor) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ ProcessHangMonitor::Get()->Dispatch(
+ NewNonOwningRunnableMethod("HangMonitorParent::TerminateScript", mActor,
+ &HangMonitorParent::TerminateScript));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+HangMonitoredProcess::BeginStartingDebugger() {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ if (!mActor) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ ProcessHangMonitor::Get()->Dispatch(NewNonOwningRunnableMethod(
+ "HangMonitorParent::BeginStartingDebugger", mActor,
+ &HangMonitorParent::BeginStartingDebugger));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+HangMonitoredProcess::EndStartingDebugger() {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ if (!mActor) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ ProcessHangMonitor::Get()->Dispatch(NewNonOwningRunnableMethod(
+ "HangMonitorParent::EndStartingDebugger", mActor,
+ &HangMonitorParent::EndStartingDebugger));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+HangMonitoredProcess::IsReportForBrowserOrChildren(nsFrameLoader* aFrameLoader,
+ bool* aResult) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
+
+ if (!mActor) {
+ *aResult = false;
+ return NS_OK;
+ }
+
+ NS_ENSURE_STATE(aFrameLoader);
+
+ AutoTArray<RefPtr<BrowsingContext>, 10> bcs;
+ bcs.AppendElement(aFrameLoader->GetExtantBrowsingContext());
+ while (!bcs.IsEmpty()) {
+ RefPtr<BrowsingContext> bc = bcs[bcs.Length() - 1];
+ bcs.RemoveLastElement();
+ if (!bc) {
+ continue;
+ }
+ if (mContentParent == bc->Canonical()->GetContentParent()) {
+ *aResult = true;
+ return NS_OK;
+ }
+ bc->GetChildren(bcs);
+ }
+
+ *aResult = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+HangMonitoredProcess::UserCanceled() { return NS_OK; }
+
+NS_IMETHODIMP
+HangMonitoredProcess::GetChildID(uint64_t* aChildID) {
+ if (!mContentParent) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ *aChildID = mContentParent->ChildID();
+ return NS_OK;
+}
+
+static bool InterruptCallback(JSContext* cx) {
+ AssertIsOnMainThread();
+ if (HangMonitorChild* child = HangMonitorChild::Get()) {
+ return child->InterruptCallback();
+ }
+
+ return true;
+}
+
+ProcessHangMonitor* ProcessHangMonitor::sInstance;
+
+ProcessHangMonitor::ProcessHangMonitor() : mCPOWTimeout(false) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ if (XRE_IsContentProcess()) {
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ obs->AddObserver(this, "xpcom-shutdown", false);
+ }
+
+ if (NS_FAILED(NS_NewNamedThread("ProcessHangMon", getter_AddRefs(mThread)))) {
+ mThread = nullptr;
+ }
+#ifdef XP_MACOSX
+ // On MacOS, ensure the priority is high enough to handle dispatches at
+ // high cpu load. USER_INITIATED class threads are prioritized just below
+ // the main thread.
+ mThread->Dispatch(NS_NewRunnableFunction(
+ "ProcessHangMonitor::SetPriority",
+ [] { pthread_set_qos_class_self_np(QOS_CLASS_USER_INITIATED, 0); }));
+#endif
+}
+
+ProcessHangMonitor::~ProcessHangMonitor() {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ MOZ_ASSERT(sInstance == this);
+ sInstance = nullptr;
+
+ mThread->Shutdown();
+ mThread = nullptr;
+}
+
+ProcessHangMonitor* ProcessHangMonitor::GetOrCreate() {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ if (!sInstance) {
+ sInstance = new ProcessHangMonitor();
+ }
+ return sInstance;
+}
+
+NS_IMPL_ISUPPORTS(ProcessHangMonitor, nsIObserver)
+
+NS_IMETHODIMP
+ProcessHangMonitor::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData) {
+ ReleaseAssertIsOnMainThread();
+ if (!strcmp(aTopic, "xpcom-shutdown")) {
+ if (RefPtr<HangMonitorChild> child = HangMonitorChild::Get()) {
+ child->Shutdown();
+ }
+
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ obs->RemoveObserver(this, "xpcom-shutdown");
+ }
+ return NS_OK;
+}
+
+ProcessHangMonitor::SlowScriptAction ProcessHangMonitor::NotifySlowScript(
+ nsIBrowserChild* aBrowserChild, const char* aFileName,
+ const nsString& aAddonId, const double aDuration) {
+ ReleaseAssertIsOnMainThread();
+ return HangMonitorChild::Get()->NotifySlowScript(aBrowserChild, aFileName,
+ aAddonId, aDuration);
+}
+
+bool ProcessHangMonitor::IsDebuggerStartupComplete() {
+ ReleaseAssertIsOnMainThread();
+ return HangMonitorChild::Get()->IsDebuggerStartupComplete();
+}
+
+bool ProcessHangMonitor::ShouldTimeOutCPOWs() {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ if (mCPOWTimeout) {
+ mCPOWTimeout = false;
+ return true;
+ }
+ return false;
+}
+
+void ProcessHangMonitor::InitiateCPOWTimeout() {
+ MOZ_RELEASE_ASSERT(IsOnThread());
+ mCPOWTimeout = true;
+}
+
+static already_AddRefed<PProcessHangMonitorParent> CreateHangMonitorParent(
+ ContentParent* aContentParent,
+ Endpoint<PProcessHangMonitorParent>&& aEndpoint) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ ProcessHangMonitor* monitor = ProcessHangMonitor::GetOrCreate();
+ RefPtr<HangMonitorParent> parent = new HangMonitorParent(monitor);
+
+ auto* process = new HangMonitoredProcess(parent, aContentParent);
+ parent->SetProcess(process);
+
+ monitor->Dispatch(
+ NewNonOwningRunnableMethod<Endpoint<PProcessHangMonitorParent>&&>(
+ "HangMonitorParent::Bind", parent, &HangMonitorParent::Bind,
+ std::move(aEndpoint)));
+
+ return parent.forget();
+}
+
+void mozilla::CreateHangMonitorChild(
+ Endpoint<PProcessHangMonitorChild>&& aEndpoint) {
+ ReleaseAssertIsOnMainThread();
+
+ JSContext* cx = danger::GetJSContext();
+ JS_AddInterruptCallback(cx, InterruptCallback);
+
+ ProcessHangMonitor* monitor = ProcessHangMonitor::GetOrCreate();
+ HangMonitorChild::CreateAndBind(monitor, std::move(aEndpoint));
+}
+
+nsresult ProcessHangMonitor::Dispatch(already_AddRefed<nsIRunnable> aRunnable) {
+ return mThread->Dispatch(std::move(aRunnable),
+ nsIEventTarget::NS_DISPATCH_NORMAL);
+}
+
+bool ProcessHangMonitor::IsOnThread() {
+ bool on;
+ return NS_SUCCEEDED(mThread->IsOnCurrentThread(&on)) && on;
+}
+
+/* static */
+already_AddRefed<PProcessHangMonitorParent> ProcessHangMonitor::AddProcess(
+ ContentParent* aContentParent) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ if (!StaticPrefs::dom_ipc_processHangMonitor_AtStartup()) {
+ return nullptr;
+ }
+
+ Endpoint<PProcessHangMonitorParent> parent;
+ Endpoint<PProcessHangMonitorChild> child;
+ nsresult rv;
+ rv = PProcessHangMonitor::CreateEndpoints(&parent, &child);
+ if (NS_FAILED(rv)) {
+ MOZ_ASSERT(false, "PProcessHangMonitor::CreateEndpoints failed");
+ return nullptr;
+ }
+
+ if (!aContentParent->SendInitProcessHangMonitor(std::move(child))) {
+ MOZ_ASSERT(false);
+ return nullptr;
+ }
+
+ return CreateHangMonitorParent(aContentParent, std::move(parent));
+}
+
+/* static */
+void ProcessHangMonitor::RemoveProcess(PProcessHangMonitorParent* aParent) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ auto parent = static_cast<HangMonitorParent*>(aParent);
+ parent->Shutdown();
+}
+
+/* static */
+void ProcessHangMonitor::ClearHang() {
+ AssertIsOnMainThread();
+ if (HangMonitorChild* child = HangMonitorChild::Get()) {
+ child->ClearHang();
+ }
+}
+
+/* static */
+void ProcessHangMonitor::PaintWhileInterruptingJS(
+ PProcessHangMonitorParent* aParent, dom::BrowserParent* aTab) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ auto* parent = static_cast<HangMonitorParent*>(aParent);
+ parent->PaintWhileInterruptingJS(aTab);
+}
+
+/* static */
+void ProcessHangMonitor::UnloadLayersWhileInterruptingJS(
+ PProcessHangMonitorParent* aParent, dom::BrowserParent* aTab) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ auto* parent = static_cast<HangMonitorParent*>(aParent);
+ parent->UnloadLayersWhileInterruptingJS(aTab);
+}
+
+/* static */
+void ProcessHangMonitor::ClearPaintWhileInterruptingJS() {
+ ReleaseAssertIsOnMainThread();
+ MOZ_RELEASE_ASSERT(XRE_IsContentProcess());
+
+ if (HangMonitorChild* child = HangMonitorChild::Get()) {
+ child->ClearPaintWhileInterruptingJS();
+ }
+}
+
+/* static */
+void ProcessHangMonitor::MaybeStartPaintWhileInterruptingJS() {
+ ReleaseAssertIsOnMainThread();
+ MOZ_RELEASE_ASSERT(XRE_IsContentProcess());
+
+ if (HangMonitorChild* child = HangMonitorChild::Get()) {
+ child->MaybeStartPaintWhileInterruptingJS();
+ }
+}
+
+/* static */
+void ProcessHangMonitor::CancelContentJSExecutionIfRunning(
+ PProcessHangMonitorParent* aParent, dom::BrowserParent* aBrowserParent,
+ nsIRemoteTab::NavigationType aNavigationType,
+ const dom::CancelContentJSOptions& aCancelContentJSOptions) {
+ ReleaseAssertIsOnMainThread();
+ auto* parent = static_cast<HangMonitorParent*>(aParent);
+ parent->CancelContentJSExecutionIfRunning(aBrowserParent, aNavigationType,
+ aCancelContentJSOptions);
+}
+
+/* static */
+void ProcessHangMonitor::SetMainThreadQoSPriority(
+ PProcessHangMonitorParent* aParent, nsIThread::QoSPriority aQoSPriority) {
+ ReleaseAssertIsOnMainThread();
+ auto* parent = static_cast<HangMonitorParent*>(aParent);
+ parent->SetMainThreadQoSPriority(aQoSPriority);
+}
diff --git a/dom/ipc/ProcessHangMonitor.h b/dom/ipc/ProcessHangMonitor.h
new file mode 100644
index 0000000000..9e4ce5758d
--- /dev/null
+++ b/dom/ipc/ProcessHangMonitor.h
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_ProcessHangMonitor_h
+#define mozilla_ProcessHangMonitor_h
+
+#include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/Atomics.h"
+#include "nsCOMPtr.h"
+#include "nsIObserver.h"
+#include "nsIRemoteTab.h"
+#include "nsIThread.h"
+#include "nsStringFwd.h"
+
+class nsIRunnable;
+class nsIBrowserChild;
+class nsIThread;
+
+namespace mozilla {
+
+namespace dom {
+class ContentParent;
+class BrowserParent;
+struct CancelContentJSOptions;
+} // namespace dom
+
+class PProcessHangMonitorParent;
+
+class ProcessHangMonitor final : public nsIObserver {
+ private:
+ ProcessHangMonitor();
+ virtual ~ProcessHangMonitor();
+
+ public:
+ static ProcessHangMonitor* Get() { return sInstance; }
+ static ProcessHangMonitor* GetOrCreate();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ static already_AddRefed<PProcessHangMonitorParent> AddProcess(
+ dom::ContentParent* aContentParent);
+ static void RemoveProcess(PProcessHangMonitorParent* aParent);
+
+ static void ClearHang();
+
+ static void PaintWhileInterruptingJS(PProcessHangMonitorParent* aParent,
+ dom::BrowserParent* aTab);
+
+ static void UnloadLayersWhileInterruptingJS(
+ PProcessHangMonitorParent* aParent, dom::BrowserParent* aTab);
+
+ static void ClearPaintWhileInterruptingJS();
+ static void MaybeStartPaintWhileInterruptingJS();
+
+ static void CancelContentJSExecutionIfRunning(
+ PProcessHangMonitorParent* aParent, dom::BrowserParent* aTab,
+ nsIRemoteTab::NavigationType aNavigationType,
+ const dom::CancelContentJSOptions& aCancelContentJSOptions);
+
+ static void SetMainThreadQoSPriority(PProcessHangMonitorParent* aParent,
+ nsIThread::QoSPriority aQoSPriority);
+
+ enum SlowScriptAction {
+ Continue,
+ Terminate,
+ StartDebugger,
+ };
+ SlowScriptAction NotifySlowScript(nsIBrowserChild* aBrowserChild,
+ const char* aFileName,
+ const nsString& aAddonId,
+ const double aDuration);
+
+ void NotifyPluginHang(uint32_t aPluginId);
+
+ bool IsDebuggerStartupComplete();
+
+ void InitiateCPOWTimeout();
+ bool ShouldTimeOutCPOWs();
+
+ nsresult Dispatch(already_AddRefed<nsIRunnable> aRunnable);
+ bool IsOnThread();
+
+ private:
+ static ProcessHangMonitor* sInstance;
+
+ Atomic<bool> mCPOWTimeout;
+
+ nsCOMPtr<nsIThread> mThread;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_ProcessHangMonitor_h
diff --git a/dom/ipc/ProcessHangMonitorIPC.h b/dom/ipc/ProcessHangMonitorIPC.h
new file mode 100644
index 0000000000..c40ffa7d3b
--- /dev/null
+++ b/dom/ipc/ProcessHangMonitorIPC.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_ProcessHangMonitorIPC_h
+#define mozilla_ProcessHangMonitorIPC_h
+
+#include "base/task.h"
+#include "base/thread.h"
+
+#include "mozilla/PProcessHangMonitor.h"
+#include "mozilla/PProcessHangMonitorParent.h"
+#include "mozilla/PProcessHangMonitorChild.h"
+
+namespace mozilla {
+
+namespace dom {
+class ContentParent;
+} // namespace dom
+
+void CreateHangMonitorChild(
+ ipc::Endpoint<PProcessHangMonitorChild>&& aEndpoint);
+
+} // namespace mozilla
+
+#endif // mozilla_ProcessHangMonitorIPC_h
diff --git a/dom/ipc/ProcessIsolation.cpp b/dom/ipc/ProcessIsolation.cpp
new file mode 100644
index 0000000000..f0c6dd8863
--- /dev/null
+++ b/dom/ipc/ProcessIsolation.cpp
@@ -0,0 +1,1139 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/ProcessIsolation.h"
+
+#include "mozilla/Assertions.h"
+#include "mozilla/dom/BrowsingContextGroup.h"
+#include "mozilla/dom/CanonicalBrowsingContext.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/RemoteType.h"
+#include "mozilla/dom/WindowGlobalParent.h"
+#include "mozilla/extensions/WebExtensionPolicy.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/ContentPrincipal.h"
+#include "mozilla/ExtensionPolicyService.h"
+#include "mozilla/Logging.h"
+#include "mozilla/NullPrincipal.h"
+#include "mozilla/PermissionManager.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/StaticPrefs_browser.h"
+#include "mozilla/StaticPrefs_fission.h"
+#include "mozilla/StaticPtr.h"
+#include "nsAboutProtocolUtils.h"
+#include "nsDocShell.h"
+#include "nsError.h"
+#include "nsIChromeRegistry.h"
+#include "nsIHttpChannel.h"
+#include "nsIHttpChannelInternal.h"
+#include "nsIProtocolHandler.h"
+#include "nsIXULRuntime.h"
+#include "nsNetUtil.h"
+#include "nsServiceManagerUtils.h"
+#include "nsSHistory.h"
+#include "nsURLHelper.h"
+
+namespace mozilla::dom {
+
+mozilla::LazyLogModule gProcessIsolationLog{"ProcessIsolation"};
+
+namespace {
+
+// Strategy used to determine whether or not a particular site should load into
+// a webIsolated content process. The particular strategy chosen is controlled
+// by the `fission.webContentIsolationStrategy` pref, which must hold one of the
+// following values.
+enum class WebContentIsolationStrategy : uint32_t {
+ // All web content is loaded into a shared `web` content process. This is
+ // similar to the non-Fission behaviour, however remote subframes may still
+ // be used for sites with special isolation behaviour, such as extension or
+ // mozillaweb content processes.
+ IsolateNothing = 0,
+ // Web content is always isolated into its own `webIsolated` content process
+ // based on site-origin, and will only load in a shared `web` content process
+ // if site-origin could not be determined.
+ IsolateEverything = 1,
+ // Only isolates web content loaded by sites which are considered "high
+ // value". A site is considered "high value" if it has been granted a
+ // `highValue*` permission by the permission manager, which is done in
+ // response to certain actions.
+ IsolateHighValue = 2,
+};
+
+/**
+ * Helper class for caching the result of splitting prefs which are represented
+ * as a comma-separated list of strings.
+ */
+struct CommaSeparatedPref {
+ public:
+ explicit constexpr CommaSeparatedPref(nsLiteralCString aPrefName)
+ : mPrefName(aPrefName) {}
+
+ void OnChange() {
+ if (mValues) {
+ mValues->Clear();
+ nsAutoCString prefValue;
+ if (NS_SUCCEEDED(Preferences::GetCString(mPrefName.get(), prefValue))) {
+ for (const auto& value :
+ nsCCharSeparatedTokenizer(prefValue, ',').ToRange()) {
+ mValues->EmplaceBack(value);
+ }
+ }
+ }
+ }
+
+ const nsTArray<nsCString>& Get() {
+ if (!mValues) {
+ mValues = new nsTArray<nsCString>;
+ Preferences::RegisterCallbackAndCall(
+ [](const char*, void* aData) {
+ static_cast<CommaSeparatedPref*>(aData)->OnChange();
+ },
+ mPrefName, this);
+ RunOnShutdown([this] {
+ delete this->mValues;
+ this->mValues = nullptr;
+ });
+ }
+ return *mValues;
+ }
+
+ auto begin() { return Get().cbegin(); }
+ auto end() { return Get().cend(); }
+
+ private:
+ nsLiteralCString mPrefName;
+ nsTArray<nsCString>* MOZ_OWNING_REF mValues = nullptr;
+};
+
+CommaSeparatedPref sSeparatedMozillaDomains{
+ "browser.tabs.remote.separatedMozillaDomains"_ns};
+
+/**
+ * Certain URIs have special isolation behaviour, and need to be loaded within
+ * specific process types.
+ */
+enum class IsolationBehavior {
+ // This URI loads web content and should be treated as a content load, being
+ // isolated based on the response principal if enabled.
+ WebContent,
+ // Forcibly load in a process with the "web" remote type. This will ignore the
+ // response principal completely.
+ // This is generally reserved for internal documents which are loaded in
+ // content, but not in the privilegedabout content process.
+ ForceWebRemoteType,
+ // Load this URI in the privileged about content process.
+ PrivilegedAbout,
+ // Load this URI in the extension process.
+ Extension,
+ // Load this URI in the file content process.
+ File,
+ // Load this URI in the priviliged mozilla content process.
+ PrivilegedMozilla,
+ // Load this URI explicitly in the parent process.
+ Parent,
+ // Load this URI wherever the browsing context is currently loaded. This is
+ // generally used for error pages.
+ Anywhere,
+ // May only be returned for subframes. Inherits the remote type of the parent
+ // document which is embedding this document.
+ Inherit,
+ // Special case for the `about:reader` URI which should be loaded in the same
+ // process which would be used for the "url" query parameter.
+ AboutReader,
+ // There was a fatal error, and the load should be aborted.
+ Error,
+};
+
+/**
+ * Returns a static string with the name of the given isolation behaviour. For
+ * use in logging code.
+ */
+static const char* IsolationBehaviorName(IsolationBehavior aBehavior) {
+ switch (aBehavior) {
+ case IsolationBehavior::WebContent:
+ return "WebContent";
+ case IsolationBehavior::ForceWebRemoteType:
+ return "ForceWebRemoteType";
+ case IsolationBehavior::PrivilegedAbout:
+ return "PrivilegedAbout";
+ case IsolationBehavior::Extension:
+ return "Extension";
+ case IsolationBehavior::File:
+ return "File";
+ case IsolationBehavior::PrivilegedMozilla:
+ return "PrivilegedMozilla";
+ case IsolationBehavior::Parent:
+ return "Parent";
+ case IsolationBehavior::Anywhere:
+ return "Anywhere";
+ case IsolationBehavior::Inherit:
+ return "Inherit";
+ case IsolationBehavior::AboutReader:
+ return "AboutReader";
+ case IsolationBehavior::Error:
+ return "Error";
+ default:
+ return "Unknown";
+ }
+}
+
+/**
+ * Returns a static string with the name of the given worker kind. For use in
+ * logging code.
+ */
+static const char* WorkerKindName(WorkerKind aWorkerKind) {
+ switch (aWorkerKind) {
+ case WorkerKindDedicated:
+ return "Dedicated";
+ case WorkerKindShared:
+ return "Shared";
+ case WorkerKindService:
+ return "Service";
+ default:
+ return "Unknown";
+ }
+}
+
+/**
+ * Check if a given URI has specialized process isolation behaviour, such as
+ * needing to be loaded within a specific type of content process.
+ *
+ * When handling a navigation, this method will be called twice: first with the
+ * channel's creation URI, and then it will be called with a result principal's
+ * URI.
+ */
+static IsolationBehavior IsolationBehaviorForURI(nsIURI* aURI, bool aIsSubframe,
+ bool aForChannelCreationURI) {
+ nsAutoCString scheme;
+ MOZ_ALWAYS_SUCCEEDS(aURI->GetScheme(scheme));
+
+ if (scheme == "chrome"_ns) {
+ // `chrome://` URIs are always loaded in the parent process, unless they
+ // have opted in to loading in a content process. This is currently only
+ // done in tests.
+ //
+ // FIXME: These flags should be removed from `chrome` URIs at some point.
+ nsCOMPtr<nsIXULChromeRegistry> chromeReg =
+ do_GetService("@mozilla.org/chrome/chrome-registry;1");
+ bool mustLoadRemotely = false;
+ if (NS_SUCCEEDED(chromeReg->MustLoadURLRemotely(aURI, &mustLoadRemotely)) &&
+ mustLoadRemotely) {
+ return IsolationBehavior::ForceWebRemoteType;
+ }
+ bool canLoadRemotely = false;
+ if (NS_SUCCEEDED(chromeReg->CanLoadURLRemotely(aURI, &canLoadRemotely)) &&
+ canLoadRemotely) {
+ return IsolationBehavior::Anywhere;
+ }
+ return IsolationBehavior::Parent;
+ }
+
+ if (scheme == "about"_ns) {
+ nsAutoCString path;
+ MOZ_ALWAYS_SUCCEEDS(NS_GetAboutModuleName(aURI, path));
+
+ // The `about:blank` and `about:srcdoc` pages are loaded by normal web
+ // content, and should be allocated processes based on their simple content
+ // principals.
+ if (path == "blank"_ns || path == "srcdoc"_ns) {
+ MOZ_ASSERT(NS_IsContentAccessibleAboutURI(aURI));
+ return IsolationBehavior::WebContent;
+ }
+
+ MOZ_ASSERT(!NS_IsContentAccessibleAboutURI(aURI));
+ // If we're loading an `about:reader` URI, perform isolation based on the
+ // principal of the URI being loaded.
+ if (path == "reader"_ns && aForChannelCreationURI) {
+ return IsolationBehavior::AboutReader;
+ }
+
+ // Otherwise, we're going to be loading an about: page. Consult the module.
+ nsCOMPtr<nsIAboutModule> aboutModule;
+ if (NS_FAILED(NS_GetAboutModule(aURI, getter_AddRefs(aboutModule))) ||
+ !aboutModule) {
+ // If we don't know of an about: module for this load, it's going to end
+ // up being a network error. Allow the load to finish as normal.
+ return IsolationBehavior::WebContent;
+ }
+
+ // NOTE: about modules can be implemented in JS, so this may run script, and
+ // therefore can spuriously fail.
+ uint32_t flags = 0;
+ if (NS_FAILED(aboutModule->GetURIFlags(aURI, &flags))) {
+ NS_WARNING(
+ "nsIAboutModule::GetURIFlags unexpectedly failed. Abort the load");
+ return IsolationBehavior::Error;
+ }
+
+ if (flags & nsIAboutModule::URI_MUST_LOAD_IN_EXTENSION_PROCESS) {
+ return IsolationBehavior::Extension;
+ }
+
+ if (flags & nsIAboutModule::URI_MUST_LOAD_IN_CHILD) {
+ if (flags & nsIAboutModule::URI_CAN_LOAD_IN_PRIVILEGEDABOUT_PROCESS) {
+ return IsolationBehavior::PrivilegedAbout;
+ }
+ return IsolationBehavior::ForceWebRemoteType;
+ }
+
+ if (flags & nsIAboutModule::URI_CAN_LOAD_IN_CHILD) {
+ return IsolationBehavior::Anywhere;
+ }
+
+ return IsolationBehavior::Parent;
+ }
+
+ // If the test-only `dataUriInDefaultWebProcess` pref is enabled, dump all
+ // `data:` URIs in a "web" content process, rather than loading them in
+ // content processes based on their precursor origins.
+ if (StaticPrefs::browser_tabs_remote_dataUriInDefaultWebProcess() &&
+ scheme == "data"_ns) {
+ return IsolationBehavior::ForceWebRemoteType;
+ }
+
+ // Make sure to unwrap nested URIs before we early return for channel creation
+ // URI. The checks past this point are intended to operate on the principal,
+ // which has it's origin constructed from the innermost URI.
+ nsCOMPtr<nsIURI> inner;
+ if (nsCOMPtr<nsINestedURI> nested = do_QueryInterface(aURI);
+ nested && NS_SUCCEEDED(nested->GetInnerURI(getter_AddRefs(inner)))) {
+ return IsolationBehaviorForURI(inner, aIsSubframe, aForChannelCreationURI);
+ }
+
+ // If we're doing the initial check based on the channel creation URI, stop
+ // here as we want to only perform the following checks on the true channel
+ // result principal.
+ if (aForChannelCreationURI) {
+ return IsolationBehavior::WebContent;
+ }
+
+ // Protocols used by Thunderbird to display email messages.
+ if (scheme == "imap"_ns || scheme == "mailbox"_ns || scheme == "news"_ns ||
+ scheme == "nntp"_ns || scheme == "snews"_ns) {
+ return IsolationBehavior::Parent;
+ }
+
+ // There is more handling for extension content processes in the caller, but
+ // they should load in an extension content process unless we're loading a
+ // subframe.
+ if (scheme == "moz-extension"_ns) {
+ if (aIsSubframe) {
+ // As a temporary measure, extension iframes must be loaded within the
+ // same process as their parent document.
+ return IsolationBehavior::Inherit;
+ }
+ return IsolationBehavior::Extension;
+ }
+
+ if (scheme == "file"_ns) {
+ return IsolationBehavior::File;
+ }
+
+ // Check if the URI is listed as a privileged mozilla content process.
+ if (scheme == "https"_ns &&
+ StaticPrefs::
+ browser_tabs_remote_separatePrivilegedMozillaWebContentProcess()) {
+ nsAutoCString host;
+ if (NS_SUCCEEDED(aURI->GetAsciiHost(host))) {
+ for (const auto& separatedDomain : sSeparatedMozillaDomains) {
+ // If the domain exactly matches our host, or our host ends with "." +
+ // separatedDomain, we consider it matching.
+ if (separatedDomain == host ||
+ (separatedDomain.Length() < host.Length() &&
+ host.CharAt(host.Length() - separatedDomain.Length() - 1) == '.' &&
+ StringEndsWith(host, separatedDomain))) {
+ return IsolationBehavior::PrivilegedMozilla;
+ }
+ }
+ }
+ }
+
+ nsCOMPtr<nsIScriptSecurityManager> secMan =
+ nsContentUtils::GetSecurityManager();
+ bool inFileURIAllowList = false;
+ if (NS_SUCCEEDED(secMan->InFileURIAllowlist(aURI, &inFileURIAllowList)) &&
+ inFileURIAllowList) {
+ return IsolationBehavior::File;
+ }
+
+ return IsolationBehavior::WebContent;
+}
+
+/**
+ * Helper method for logging the origin of a principal as a string.
+ */
+static nsAutoCString OriginString(nsIPrincipal* aPrincipal) {
+ nsAutoCString origin;
+ aPrincipal->GetOrigin(origin);
+ return origin;
+}
+
+/**
+ * Trim the OriginAttributes from aPrincipal, and use it to create a
+ * OriginSuffix string appropriate to use within a remoteType string.
+ */
+static nsAutoCString OriginSuffixForRemoteType(nsIPrincipal* aPrincipal) {
+ nsAutoCString originSuffix;
+ OriginAttributes attrs = aPrincipal->OriginAttributesRef();
+ attrs.StripAttributes(OriginAttributes::STRIP_FIRST_PARTY_DOMAIN |
+ OriginAttributes::STRIP_PARITION_KEY);
+ attrs.CreateSuffix(originSuffix);
+ return originSuffix;
+}
+
+/**
+ * Given an about:reader URI, extract the "url" query parameter, and use it to
+ * construct a principal which should be used for process selection.
+ */
+static already_AddRefed<BasePrincipal> GetAboutReaderURLPrincipal(
+ nsIURI* aURI, const OriginAttributes& aAttrs) {
+#ifdef DEBUG
+ MOZ_ASSERT(aURI->SchemeIs("about"));
+ nsAutoCString path;
+ MOZ_ALWAYS_SUCCEEDS(NS_GetAboutModuleName(aURI, path));
+ MOZ_ASSERT(path == "reader"_ns);
+#endif
+
+ nsAutoCString query;
+ MOZ_ALWAYS_SUCCEEDS(aURI->GetQuery(query));
+
+ // Extract the "url" parameter from the `about:reader`'s query parameters,
+ // and recover a content principal from it.
+ nsAutoString readerSpec;
+ if (URLParams::Extract(query, u"url"_ns, readerSpec)) {
+ nsCOMPtr<nsIURI> readerUri;
+ if (NS_SUCCEEDED(NS_NewURI(getter_AddRefs(readerUri), readerSpec))) {
+ return BasePrincipal::CreateContentPrincipal(readerUri, aAttrs);
+ }
+ }
+ return nullptr;
+}
+
+/**
+ * Returns `true` if loads for this site should be isolated on a per-site basis.
+ * If `aTopBC` is nullptr, this is being called to check if a shared or service
+ * worker should be isolated.
+ */
+static bool ShouldIsolateSite(nsIPrincipal* aPrincipal,
+ bool aUseRemoteSubframes) {
+ // If Fission is disabled, we never want to isolate. We check the toplevel BC
+ // if it's available, or the global pref if checking for shared or service
+ // workers.
+ if (!aUseRemoteSubframes) {
+ return false;
+ }
+
+ // non-content principals currently can't have webIsolated remote types
+ // assigned to them, so should not be isolated.
+ if (!aPrincipal->GetIsContentPrincipal()) {
+ return false;
+ }
+
+ switch (WebContentIsolationStrategy(
+ StaticPrefs::fission_webContentIsolationStrategy())) {
+ case WebContentIsolationStrategy::IsolateNothing:
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Verbose,
+ ("Not isolating '%s' as isolation is disabled",
+ OriginString(aPrincipal).get()));
+ return false;
+ case WebContentIsolationStrategy::IsolateEverything:
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Verbose,
+ ("Isolating '%s' as isolation is enabled for all sites",
+ OriginString(aPrincipal).get()));
+ return true;
+ case WebContentIsolationStrategy::IsolateHighValue: {
+ RefPtr<PermissionManager> perms = PermissionManager::GetInstance();
+ if (NS_WARN_IF(!perms)) {
+ // If we somehow have no permission manager, fall back to the safest
+ // option, and try to isolate.
+ MOZ_ASSERT_UNREACHABLE("Permission manager is missing");
+ return true;
+ }
+
+ static constexpr nsLiteralCString kHighValuePermissions[] = {
+ mozilla::dom::kHighValueCOOPPermission,
+ mozilla::dom::kHighValueHasSavedLoginPermission,
+ mozilla::dom::kHighValueIsLoggedInPermission,
+ };
+
+ for (const auto& type : kHighValuePermissions) {
+ uint32_t permission = nsIPermissionManager::UNKNOWN_ACTION;
+ if (NS_SUCCEEDED(perms->TestPermissionFromPrincipal(aPrincipal, type,
+ &permission)) &&
+ permission == nsIPermissionManager::ALLOW_ACTION) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Verbose,
+ ("Isolating '%s' due to high-value permission '%s'",
+ OriginString(aPrincipal).get(), type.get()));
+ return true;
+ }
+ }
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Verbose,
+ ("Not isolating '%s' as it is not high-value",
+ OriginString(aPrincipal).get()));
+ return false;
+ }
+ default:
+ // An invalid pref value was used. Fall back to the safest option and
+ // isolate everything.
+ NS_WARNING("Invalid pref value for fission.webContentIsolationStrategy");
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Verbose,
+ ("Isolating '%s' due to unknown strategy pref value",
+ OriginString(aPrincipal).get()));
+ return true;
+ }
+}
+
+static Result<nsCString, nsresult> SpecialBehaviorRemoteType(
+ IsolationBehavior aBehavior, const nsACString& aCurrentRemoteType,
+ WindowGlobalParent* aParentWindow) {
+ switch (aBehavior) {
+ case IsolationBehavior::ForceWebRemoteType:
+ return {WEB_REMOTE_TYPE};
+ case IsolationBehavior::PrivilegedAbout:
+ // The privileged about: content process cannot be disabled, as it
+ // causes various actors to break.
+ return {PRIVILEGEDABOUT_REMOTE_TYPE};
+ case IsolationBehavior::Extension:
+ if (ExtensionPolicyService::GetSingleton().UseRemoteExtensions()) {
+ return {EXTENSION_REMOTE_TYPE};
+ }
+ return {NOT_REMOTE_TYPE};
+ case IsolationBehavior::File:
+ if (StaticPrefs::browser_tabs_remote_separateFileUriProcess()) {
+ return {FILE_REMOTE_TYPE};
+ }
+ return {WEB_REMOTE_TYPE};
+ case IsolationBehavior::PrivilegedMozilla:
+ return {PRIVILEGEDMOZILLA_REMOTE_TYPE};
+ case IsolationBehavior::Parent:
+ return {NOT_REMOTE_TYPE};
+ case IsolationBehavior::Anywhere:
+ return {nsCString(aCurrentRemoteType)};
+ case IsolationBehavior::Inherit:
+ MOZ_DIAGNOSTIC_ASSERT(aParentWindow);
+ return {nsCString(aParentWindow->GetRemoteType())};
+
+ case IsolationBehavior::Error:
+ return Err(NS_ERROR_UNEXPECTED);
+
+ default:
+ MOZ_ASSERT_UNREACHABLE();
+ return Err(NS_ERROR_UNEXPECTED);
+ }
+}
+
+enum class WebProcessType {
+ Web,
+ WebIsolated,
+ WebCoopCoep,
+};
+
+} // namespace
+
+Result<NavigationIsolationOptions, nsresult> IsolationOptionsForNavigation(
+ CanonicalBrowsingContext* aTopBC, WindowGlobalParent* aParentWindow,
+ nsIURI* aChannelCreationURI, nsIChannel* aChannel,
+ const nsACString& aCurrentRemoteType, bool aHasCOOPMismatch,
+ bool aForNewTab, uint32_t aLoadStateLoadType,
+ const Maybe<uint64_t>& aChannelId,
+ const Maybe<nsCString>& aRemoteTypeOverride) {
+ // Get the final principal, used to select which process to load into.
+ nsCOMPtr<nsIPrincipal> resultPrincipal;
+ nsresult rv = nsContentUtils::GetSecurityManager()->GetChannelResultPrincipal(
+ aChannel, getter_AddRefs(resultPrincipal));
+ if (NS_FAILED(rv)) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Error,
+ ("failed to get channel result principal"));
+ return Err(rv);
+ }
+
+ MOZ_LOG(
+ gProcessIsolationLog, LogLevel::Verbose,
+ ("IsolationOptionsForNavigation principal:%s, uri:%s, parentUri:%s",
+ OriginString(resultPrincipal).get(),
+ aChannelCreationURI->GetSpecOrDefault().get(),
+ aParentWindow ? aParentWindow->GetDocumentURI()->GetSpecOrDefault().get()
+ : ""));
+
+ // If we're loading a null principal, we can't easily make a process
+ // selection decision off ot it. Instead, we'll use our null principal's
+ // precursor principal to make process selection decisions.
+ bool isNullPrincipalPrecursor = false;
+ nsCOMPtr<nsIPrincipal> resultOrPrecursor(resultPrincipal);
+ if (nsCOMPtr<nsIPrincipal> precursor =
+ resultOrPrecursor->GetPrecursorPrincipal()) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Verbose,
+ ("using null principal precursor origin %s",
+ OriginString(precursor).get()));
+ resultOrPrecursor = precursor;
+ isNullPrincipalPrecursor = true;
+ }
+
+ NavigationIsolationOptions options;
+ options.mReplaceBrowsingContext = aHasCOOPMismatch;
+
+ // Check if this load has an explicit remote type override. This is used to
+ // perform an about:blank load within a specific content process.
+ if (aRemoteTypeOverride) {
+ MOZ_DIAGNOSTIC_ASSERT(
+ NS_IsAboutBlank(aChannelCreationURI),
+ "Should only have aRemoteTypeOverride for about:blank URIs");
+ if (NS_WARN_IF(!resultPrincipal->GetIsNullPrincipal())) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Error,
+ ("invalid remote type override on non-null principal"));
+ return Err(NS_ERROR_DOM_SECURITY_ERR);
+ }
+
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Verbose,
+ ("using remote type override (%s) for load",
+ aRemoteTypeOverride->get()));
+ options.mRemoteType = *aRemoteTypeOverride;
+ return options;
+ }
+
+ // First, check for any special cases which should be handled using the
+ // channel creation URI, and handle them.
+ auto behavior = IsolationBehaviorForURI(aChannelCreationURI, aParentWindow,
+ /* aForChannelCreationURI */ true);
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Verbose,
+ ("Channel Creation Isolation Behavior: %s",
+ IsolationBehaviorName(behavior)));
+
+ // In the about:reader special case, we want to fetch the relevant information
+ // from the URI, an then treat it as a normal web content load.
+ if (behavior == IsolationBehavior::AboutReader) {
+ if (RefPtr<BasePrincipal> readerURIPrincipal = GetAboutReaderURLPrincipal(
+ aChannelCreationURI, resultOrPrecursor->OriginAttributesRef())) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Verbose,
+ ("using about:reader's url origin %s",
+ OriginString(readerURIPrincipal).get()));
+ resultOrPrecursor = readerURIPrincipal;
+ }
+ behavior = IsolationBehavior::WebContent;
+ // If loading an about:reader page in a BrowsingContext which shares a
+ // BrowsingContextGroup with other toplevel documents, replace the
+ // BrowsingContext to destroy any references.
+ //
+ // With SHIP we can apply this to all about:reader loads, but otherwise
+ // do it at least where there are opener/group relationships.
+ if (mozilla::SessionHistoryInParent() ||
+ aTopBC->Group()->Toplevels().Length() > 1) {
+ options.mReplaceBrowsingContext = true;
+ }
+ }
+
+ // If we're running in a test which is requesting that system-triggered
+ // about:blank documents load within the current process, override the
+ // behaviour for loads which meet the requirements.
+ if (StaticPrefs::browser_tabs_remote_systemTriggeredAboutBlankAnywhere() &&
+ NS_IsAboutBlank(aChannelCreationURI)) {
+ nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
+ if (loadInfo->TriggeringPrincipal()->IsSystemPrincipal() &&
+ resultOrPrecursor->GetIsNullPrincipal()) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Warning,
+ ("Forcing system-principal triggered about:blank load to "
+ "complete in the current process"));
+ behavior = IsolationBehavior::Anywhere;
+ }
+ }
+
+#ifdef MOZ_WIDGET_ANDROID
+ // If we're loading an error page on android, it must complete within the same
+ // process as the errored page load would complete in due to code expecting
+ // that behavior. See bug 1673763.
+ if (aLoadStateLoadType == LOAD_ERROR_PAGE) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Verbose,
+ ("Forcing error page load to complete in the current process"));
+ behavior = IsolationBehavior::Anywhere;
+ }
+#endif
+
+ // If we're loading for a specific extension, we'll need to perform a
+ // BCG-switching load to get our toplevel extension window in the correct
+ // BrowsingContextGroup.
+ if (auto* addonPolicy =
+ BasePrincipal::Cast(resultOrPrecursor)->AddonPolicy()) {
+ if (aParentWindow) {
+ // As a temporary measure, extension iframes must be loaded within the
+ // same process as their parent document.
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Verbose,
+ ("Loading extension subframe in same process as parent"));
+ behavior = IsolationBehavior::Inherit;
+ } else {
+ MOZ_LOG(
+ gProcessIsolationLog, LogLevel::Verbose,
+ ("Found extension frame with addon policy. Will use group id %" PRIx64
+ " (currentId: %" PRIx64 ")",
+ addonPolicy->GetBrowsingContextGroupId(), aTopBC->Group()->Id()));
+ behavior = IsolationBehavior::Extension;
+ if (aTopBC->Group()->Id() != addonPolicy->GetBrowsingContextGroupId()) {
+ options.mReplaceBrowsingContext = true;
+ options.mSpecificGroupId = addonPolicy->GetBrowsingContextGroupId();
+ }
+ }
+ }
+
+ // Do a second run of `GetIsolationBehavior`, this time using the
+ // principal's URI to handle additional special cases such as the file and
+ // privilegedmozilla content process.
+ if (behavior == IsolationBehavior::WebContent) {
+ if (resultOrPrecursor->IsSystemPrincipal()) {
+ // We're loading something with a system principal which isn't caught in
+ // one of our other edge-cases. If the load started in the parent process,
+ // and it's safe for it to end in the parent process, we should finish the
+ // load there.
+ bool isUIResource = false;
+ if (aCurrentRemoteType.IsEmpty() &&
+ (aChannelCreationURI->SchemeIs("about") ||
+ (NS_SUCCEEDED(NS_URIChainHasFlags(
+ aChannelCreationURI, nsIProtocolHandler::URI_IS_UI_RESOURCE,
+ &isUIResource)) &&
+ isUIResource))) {
+ behavior = IsolationBehavior::Parent;
+ } else {
+ // In general, we don't want to load documents with a system principal
+ // in a content process, however we need to in some cases, such as when
+ // loading blob: URLs created by system code. We can force the load to
+ // finish in a content process instead.
+ behavior = IsolationBehavior::ForceWebRemoteType;
+ }
+ } else if (nsCOMPtr<nsIURI> principalURI = resultOrPrecursor->GetURI()) {
+ behavior = IsolationBehaviorForURI(principalURI, aParentWindow,
+ /* aForChannelCreationURI */ false);
+ }
+ }
+
+ // If we're currently loaded in the extension process, and are going to switch
+ // to some other remote type, make sure we leave the extension's BCG which we
+ // may have entered earlier to separate extension and non-extension BCGs from
+ // each-other.
+ if (!aParentWindow && aCurrentRemoteType == EXTENSION_REMOTE_TYPE &&
+ behavior != IsolationBehavior::Extension &&
+ behavior != IsolationBehavior::Anywhere) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Verbose,
+ ("Forcing BC replacement to leave extension BrowsingContextGroup "
+ "%" PRIx64 " on navigation",
+ aTopBC->Group()->Id()));
+ options.mReplaceBrowsingContext = true;
+ }
+
+ // We don't want to load documents with sandboxed null principals, like
+ // `data:` URIs, in the parent process, even if they were created by a
+ // document which would otherwise be loaded in the parent process.
+ if (behavior == IsolationBehavior::Parent && isNullPrincipalPrecursor) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Debug,
+ ("Ensuring sandboxed null-principal load doesn't occur in the "
+ "parent process"));
+ behavior = IsolationBehavior::ForceWebRemoteType;
+ }
+
+ MOZ_LOG(
+ gProcessIsolationLog, LogLevel::Debug,
+ ("Using IsolationBehavior %s for %s (original uri %s)",
+ IsolationBehaviorName(behavior), OriginString(resultOrPrecursor).get(),
+ aChannelCreationURI->GetSpecOrDefault().get()));
+
+ // Check if we can put the previous document into the BFCache.
+ if (mozilla::BFCacheInParent() && nsSHistory::GetMaxTotalViewers() > 0 &&
+ !aForNewTab && !aParentWindow && !aTopBC->HadOriginalOpener() &&
+ behavior != IsolationBehavior::Parent &&
+ (ExtensionPolicyService::GetSingleton().UseRemoteExtensions() ||
+ behavior != IsolationBehavior::Extension) &&
+ !aCurrentRemoteType.IsEmpty() &&
+ aTopBC->GetHasLoadedNonInitialDocument() &&
+ (aLoadStateLoadType == LOAD_NORMAL ||
+ aLoadStateLoadType == LOAD_HISTORY || aLoadStateLoadType == LOAD_LINK ||
+ aLoadStateLoadType == LOAD_STOP_CONTENT ||
+ aLoadStateLoadType == LOAD_STOP_CONTENT_AND_REPLACE) &&
+ (!aTopBC->GetActiveSessionHistoryEntry() ||
+ aTopBC->GetActiveSessionHistoryEntry()->GetSaveLayoutStateFlag())) {
+ if (nsCOMPtr<nsIURI> uri = aTopBC->GetCurrentURI()) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Verbose,
+ ("current uri: %s", uri->GetSpecOrDefault().get()));
+ }
+ options.mTryUseBFCache =
+ aTopBC->AllowedInBFCache(aChannelId, aChannelCreationURI);
+ if (options.mTryUseBFCache) {
+ options.mReplaceBrowsingContext = true;
+ options.mActiveSessionHistoryEntry =
+ aTopBC->GetActiveSessionHistoryEntry();
+ }
+ }
+
+ // If the load has any special remote type handling, do so at this point.
+ if (behavior != IsolationBehavior::WebContent) {
+ MOZ_TRY_VAR(
+ options.mRemoteType,
+ SpecialBehaviorRemoteType(behavior, aCurrentRemoteType, aParentWindow));
+
+ if (options.mRemoteType != aCurrentRemoteType &&
+ (options.mRemoteType.IsEmpty() || aCurrentRemoteType.IsEmpty())) {
+ options.mReplaceBrowsingContext = true;
+ }
+
+ MOZ_LOG(
+ gProcessIsolationLog, LogLevel::Debug,
+ ("Selecting specific remote type (%s) due to a special case isolation "
+ "behavior %s",
+ options.mRemoteType.get(), IsolationBehaviorName(behavior)));
+ return options;
+ }
+
+ // At this point we're definitely not going to be loading in the parent
+ // process anymore, so we're definitely going to be replacing BrowsingContext
+ // if we're in the parent process.
+ if (aCurrentRemoteType.IsEmpty()) {
+ MOZ_ASSERT(!aParentWindow);
+ options.mReplaceBrowsingContext = true;
+ }
+
+ nsAutoCString siteOriginNoSuffix;
+ MOZ_TRY(resultOrPrecursor->GetSiteOriginNoSuffix(siteOriginNoSuffix));
+
+ // Check if we've already loaded a document with the given principal in some
+ // content process. We want to finish the load in the same process in that
+ // case.
+ //
+ // The exception to that is with extension loads and the system principal,
+ // where we may have multiple documents with the same principal in different
+ // processes. Those have been handled above, and will not be reaching here.
+ //
+ // If we're doing a replace load or opening a new tab, we won't be staying in
+ // the same BrowsingContextGroup, so ignore this step.
+ if (!options.mReplaceBrowsingContext && !aForNewTab) {
+ // Helper for efficiently determining if a given origin is same-site. This
+ // will attempt to do a fast equality check, and will only fall back to
+ // computing the site-origin for content principals.
+ auto principalIsSameSite = [&](nsIPrincipal* aDocumentPrincipal) -> bool {
+ // If we're working with a null principal with a precursor, compare
+ // precursors, as `resultOrPrecursor` has already been stripped to its
+ // precursor.
+ nsCOMPtr<nsIPrincipal> documentPrincipal(aDocumentPrincipal);
+ if (nsCOMPtr<nsIPrincipal> precursor =
+ documentPrincipal->GetPrecursorPrincipal()) {
+ documentPrincipal = precursor;
+ }
+
+ // First, attempt to use `Equals` to compare principals, and if that
+ // fails compare siteOrigins. Only compare siteOrigin for content
+ // principals, as non-content principals will never have siteOrigin !=
+ // origin.
+ nsAutoCString documentSiteOrigin;
+ return resultOrPrecursor->Equals(documentPrincipal) ||
+ (documentPrincipal->GetIsContentPrincipal() &&
+ resultOrPrecursor->GetIsContentPrincipal() &&
+ NS_SUCCEEDED(documentPrincipal->GetSiteOriginNoSuffix(
+ documentSiteOrigin)) &&
+ documentSiteOrigin == siteOriginNoSuffix);
+ };
+
+ // XXX: Consider also checking in-flight process switches to see if any have
+ // matching principals?
+ AutoTArray<RefPtr<BrowsingContext>, 8> contexts;
+ aTopBC->Group()->GetToplevels(contexts);
+ while (!contexts.IsEmpty()) {
+ auto bc = contexts.PopLastElement();
+ for (const auto& wc : bc->GetWindowContexts()) {
+ WindowGlobalParent* wgp = wc->Canonical();
+
+ // Check if this WindowGlobalParent has the given resultPrincipal, and
+ // if it does, we need to load in that process.
+ if (!wgp->GetRemoteType().IsEmpty() &&
+ principalIsSameSite(wgp->DocumentPrincipal())) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Debug,
+ ("Found existing frame with matching principal "
+ "(remoteType:(%s), origin:%s)",
+ PromiseFlatCString(wgp->GetRemoteType()).get(),
+ OriginString(wgp->DocumentPrincipal()).get()));
+ options.mRemoteType = wgp->GetRemoteType();
+ return options;
+ }
+
+ // Also enumerate over this WindowContexts' subframes.
+ contexts.AppendElements(wc->Children());
+ }
+ }
+ }
+
+ nsAutoCString originSuffix = OriginSuffixForRemoteType(resultOrPrecursor);
+
+ WebProcessType webProcessType = WebProcessType::Web;
+ if (ShouldIsolateSite(resultOrPrecursor, aTopBC->UseRemoteSubframes())) {
+ webProcessType = WebProcessType::WebIsolated;
+ }
+
+ // Check if we should be loading in a webCOOP+COEP remote type due to our COOP
+ // status.
+ nsILoadInfo::CrossOriginOpenerPolicy coop =
+ nsILoadInfo::OPENER_POLICY_UNSAFE_NONE;
+ if (aParentWindow) {
+ coop = aTopBC->GetOpenerPolicy();
+ } else if (nsCOMPtr<nsIHttpChannelInternal> httpChannel =
+ do_QueryInterface(aChannel)) {
+ MOZ_ALWAYS_SUCCEEDS(httpChannel->GetCrossOriginOpenerPolicy(&coop));
+ }
+ if (coop ==
+ nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP) {
+ webProcessType = WebProcessType::WebCoopCoep;
+
+ // If we're changing BrowsingContext, and are going to end up within a
+ // webCOOP+COEP group, ensure we use a cross-origin isolated BCG ID.
+ if (options.mReplaceBrowsingContext) {
+ MOZ_ASSERT(!options.mSpecificGroupId,
+ "overriding previously-specified BCG ID");
+ options.mSpecificGroupId = BrowsingContextGroup::CreateId(
+ /* aPotentiallyCrossOriginIsolated */ true);
+ }
+ }
+
+ switch (webProcessType) {
+ case WebProcessType::Web:
+ options.mRemoteType = WEB_REMOTE_TYPE;
+ break;
+ case WebProcessType::WebIsolated:
+ options.mRemoteType =
+ FISSION_WEB_REMOTE_TYPE "="_ns + siteOriginNoSuffix + originSuffix;
+ break;
+ case WebProcessType::WebCoopCoep:
+ options.mRemoteType =
+ WITH_COOP_COEP_REMOTE_TYPE "="_ns + siteOriginNoSuffix + originSuffix;
+ break;
+ }
+ return options;
+}
+
+Result<WorkerIsolationOptions, nsresult> IsolationOptionsForWorker(
+ nsIPrincipal* aPrincipal, WorkerKind aWorkerKind,
+ const nsACString& aCurrentRemoteType, bool aUseRemoteSubframes) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Verbose,
+ ("IsolationOptionsForWorker principal:%s, kind:%s, current:%s",
+ OriginString(aPrincipal).get(), WorkerKindName(aWorkerKind),
+ PromiseFlatCString(aCurrentRemoteType).get()));
+
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_RELEASE_ASSERT(
+ aWorkerKind == WorkerKindService || aWorkerKind == WorkerKindShared,
+ "Unexpected remote worker kind");
+
+ if (aWorkerKind == WorkerKindService &&
+ !aPrincipal->GetIsContentPrincipal()) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Warning,
+ ("Rejecting service worker with non-content principal"));
+ return Err(NS_ERROR_UNEXPECTED);
+ }
+
+ if (aPrincipal->GetIsExpandedPrincipal()) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Warning,
+ ("Rejecting remote worker with expanded principal"));
+ return Err(NS_ERROR_UNEXPECTED);
+ }
+
+ // In some cases, such as for null principals without precursors, we will want
+ // to load a shared worker in a process based on the current process. This is
+ // not done for service workers - process selection for those should function
+ // the same in all processes.
+ //
+ // We only allow the current remote type to be used if it is not a COOP+COEP
+ // remote type, in order to avoid loading a shared worker in one of these
+ // processes. Currently process selection for workers occurs before response
+ // headers are available, so we will never select to load a shared worker in a
+ // COOP+COEP content process.
+ nsCString preferredRemoteType = DEFAULT_REMOTE_TYPE;
+ if (aWorkerKind == WorkerKind::WorkerKindShared &&
+ !StringBeginsWith(aCurrentRemoteType,
+ WITH_COOP_COEP_REMOTE_TYPE_PREFIX)) {
+ preferredRemoteType = aCurrentRemoteType;
+ }
+
+ WorkerIsolationOptions options;
+
+ // If we're loading a null principal, we can't easily make a process
+ // selection decision off ot it. Instead, we'll use our null principal's
+ // precursor principal to make process selection decisions.
+ bool isNullPrincipalPrecursor = false;
+ nsCOMPtr<nsIPrincipal> resultOrPrecursor(aPrincipal);
+ if (nsCOMPtr<nsIPrincipal> precursor =
+ resultOrPrecursor->GetPrecursorPrincipal()) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Verbose,
+ ("using null principal precursor origin %s",
+ OriginString(precursor).get()));
+ resultOrPrecursor = precursor;
+ isNullPrincipalPrecursor = true;
+ }
+
+ IsolationBehavior behavior = IsolationBehavior::WebContent;
+ if (resultOrPrecursor->GetIsContentPrincipal()) {
+ nsCOMPtr<nsIURI> uri = resultOrPrecursor->GetURI();
+ behavior = IsolationBehaviorForURI(uri, /* aIsSubframe */ false,
+ /* aForChannelCreationURI */ false);
+ } else if (resultOrPrecursor->IsSystemPrincipal()) {
+ MOZ_ASSERT(aWorkerKind == WorkerKindShared);
+
+ // Allow system principal shared workers to load within either the
+ // parent process or privilegedabout process, depending on the
+ // responsible process.
+ if (preferredRemoteType == NOT_REMOTE_TYPE) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Debug,
+ ("Loading system principal shared worker in parent process"));
+ behavior = IsolationBehavior::Parent;
+ } else if (preferredRemoteType == PRIVILEGEDABOUT_REMOTE_TYPE) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Debug,
+ ("Loading system principal shared worker in privilegedabout "
+ "process"));
+ behavior = IsolationBehavior::PrivilegedAbout;
+ } else {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Warning,
+ ("Cannot load system-principal shared worker in "
+ "non-privilegedabout content process"));
+ return Err(NS_ERROR_UNEXPECTED);
+ }
+ } else {
+ MOZ_ASSERT(resultOrPrecursor->GetIsNullPrincipal());
+ MOZ_ASSERT(aWorkerKind == WorkerKindShared);
+
+ if (preferredRemoteType == NOT_REMOTE_TYPE) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Debug,
+ ("Ensuring precursorless null principal shared worker loads in a "
+ "content process"));
+ behavior = IsolationBehavior::ForceWebRemoteType;
+ } else {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Debug,
+ ("Loading precursorless null principal shared worker within "
+ "current remotetype: (%s)",
+ preferredRemoteType.get()));
+ behavior = IsolationBehavior::Anywhere;
+ }
+ }
+
+ if (behavior == IsolationBehavior::Parent && isNullPrincipalPrecursor) {
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Debug,
+ ("Ensuring sandboxed null-principal shared worker doesn't load in "
+ "the parent process"));
+ behavior = IsolationBehavior::ForceWebRemoteType;
+ }
+
+ if (behavior != IsolationBehavior::WebContent) {
+ MOZ_TRY_VAR(
+ options.mRemoteType,
+ SpecialBehaviorRemoteType(behavior, preferredRemoteType, nullptr));
+
+ MOZ_LOG(
+ gProcessIsolationLog, LogLevel::Debug,
+ ("Selecting specific %s worker remote type (%s) due to a special case "
+ "isolation behavior %s",
+ WorkerKindName(aWorkerKind), options.mRemoteType.get(),
+ IsolationBehaviorName(behavior)));
+ return options;
+ }
+
+ // If we should be isolating this site, we can determine the correct fission
+ // remote type from the principal's site-origin.
+ if (ShouldIsolateSite(resultOrPrecursor, aUseRemoteSubframes)) {
+ nsAutoCString siteOriginNoSuffix;
+ MOZ_TRY(resultOrPrecursor->GetSiteOriginNoSuffix(siteOriginNoSuffix));
+ nsAutoCString originSuffix = OriginSuffixForRemoteType(resultOrPrecursor);
+
+ nsCString prefix = aWorkerKind == WorkerKindService
+ ? SERVICEWORKER_REMOTE_TYPE
+ : FISSION_WEB_REMOTE_TYPE;
+ options.mRemoteType = prefix + "="_ns + siteOriginNoSuffix + originSuffix;
+
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Debug,
+ ("Isolating web content %s worker in remote type (%s)",
+ WorkerKindName(aWorkerKind), options.mRemoteType.get()));
+ } else {
+ options.mRemoteType = WEB_REMOTE_TYPE;
+
+ MOZ_LOG(gProcessIsolationLog, LogLevel::Debug,
+ ("Loading web content %s worker in shared web remote type",
+ WorkerKindName(aWorkerKind)));
+ }
+ return options;
+}
+
+void AddHighValuePermission(nsIPrincipal* aResultPrincipal,
+ const nsACString& aPermissionType) {
+ RefPtr<PermissionManager> perms = PermissionManager::GetInstance();
+ if (NS_WARN_IF(!perms)) {
+ return;
+ }
+
+ // We can't act on non-content principals, so if the load was sandboxed, try
+ // to use the unsandboxed precursor principal to add the highValue permission.
+ nsCOMPtr<nsIPrincipal> resultOrPrecursor(aResultPrincipal);
+ if (!aResultPrincipal->GetIsContentPrincipal()) {
+ resultOrPrecursor = aResultPrincipal->GetPrecursorPrincipal();
+ if (!resultOrPrecursor) {
+ return;
+ }
+ }
+
+ // Use the site-origin principal as we want to add the permission for the
+ // entire site, rather than a specific subdomain, as process isolation acts on
+ // a site granularity.
+ nsAutoCString siteOrigin;
+ if (NS_FAILED(resultOrPrecursor->GetSiteOrigin(siteOrigin))) {
+ return;
+ }
+
+ nsCOMPtr<nsIPrincipal> sitePrincipal =
+ BasePrincipal::CreateContentPrincipal(siteOrigin);
+ if (!sitePrincipal || !sitePrincipal->GetIsContentPrincipal()) {
+ return;
+ }
+
+ MOZ_LOG(dom::gProcessIsolationLog, LogLevel::Verbose,
+ ("Adding %s Permission for site '%s'", aPermissionType.BeginReading(),
+ siteOrigin.get()));
+
+ uint32_t expiration = 0;
+ if (aPermissionType.Equals(mozilla::dom::kHighValueCOOPPermission)) {
+ expiration = StaticPrefs::fission_highValue_coop_expiration();
+ } else if (aPermissionType.Equals(
+ mozilla::dom::kHighValueHasSavedLoginPermission) ||
+ aPermissionType.Equals(
+ mozilla::dom::kHighValueIsLoggedInPermission)) {
+ expiration = StaticPrefs::fission_highValue_login_expiration();
+ } else {
+ MOZ_ASSERT_UNREACHABLE("Unknown permission type");
+ }
+
+ // XXX: Would be nice if we could use `TimeStamp` here, but there's
+ // unfortunately no convenient way to recover a time in milliseconds since the
+ // unix epoch from `TimeStamp`.
+ int64_t expirationTime =
+ (PR_Now() / PR_USEC_PER_MSEC) + (int64_t(expiration) * PR_MSEC_PER_SEC);
+ Unused << perms->AddFromPrincipal(
+ sitePrincipal, aPermissionType, nsIPermissionManager::ALLOW_ACTION,
+ nsIPermissionManager::EXPIRE_TIME, expirationTime);
+}
+
+void AddHighValuePermission(const nsACString& aOrigin,
+ const nsACString& aPermissionType) {
+ nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
+ nsCOMPtr<nsIPrincipal> principal;
+ nsresult rv =
+ ssm->CreateContentPrincipalFromOrigin(aOrigin, getter_AddRefs(principal));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+
+ AddHighValuePermission(principal, aPermissionType);
+}
+
+bool IsIsolateHighValueSiteEnabled() {
+ return mozilla::FissionAutostart() &&
+ WebContentIsolationStrategy(
+ StaticPrefs::fission_webContentIsolationStrategy()) ==
+ WebContentIsolationStrategy::IsolateHighValue;
+}
+
+} // namespace mozilla::dom
diff --git a/dom/ipc/ProcessIsolation.h b/dom/ipc/ProcessIsolation.h
new file mode 100644
index 0000000000..e9703a8d7a
--- /dev/null
+++ b/dom/ipc/ProcessIsolation.h
@@ -0,0 +1,111 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_ProcessIsolation_h
+#define mozilla_dom_ProcessIsolation_h
+
+#include <stdint.h>
+
+#include "mozilla/Logging.h"
+#include "mozilla/dom/RemoteType.h"
+#include "mozilla/dom/SessionHistoryEntry.h"
+#include "mozilla/dom/WorkerPrivate.h"
+#include "nsString.h"
+#include "nsIPrincipal.h"
+#include "nsIURI.h"
+
+namespace mozilla::dom {
+
+class CanonicalBrowsingContext;
+class WindowGlobalParent;
+
+extern mozilla::LazyLogModule gProcessIsolationLog;
+
+constexpr nsLiteralCString kHighValueCOOPPermission = "highValueCOOP"_ns;
+constexpr nsLiteralCString kHighValueHasSavedLoginPermission =
+ "highValueHasSavedLogin"_ns;
+constexpr nsLiteralCString kHighValueIsLoggedInPermission =
+ "highValueIsLoggedIn"_ns;
+
+// NavigationIsolationOptions is passed through the methods to store the state
+// of the possible process and/or browsing context change.
+struct NavigationIsolationOptions {
+ nsCString mRemoteType;
+ bool mReplaceBrowsingContext = false;
+ uint64_t mSpecificGroupId = 0;
+ bool mTryUseBFCache = false;
+ RefPtr<SessionHistoryEntry> mActiveSessionHistoryEntry;
+};
+
+/**
+ * Given a specific channel, determines which process the navigation should
+ * complete in, and whether or not to perform a BrowsingContext-replace load
+ * or enter the BFCache.
+ *
+ * This method will always return a `NavigationIsolationOptions` even if the
+ * current remote type is compatible. Compatibility with the current process
+ * should be checked at the call-site. An error should only be returned in
+ * exceptional circumstances, and should lead to the load being cancelled.
+ *
+ * This method is only intended for use with document navigations.
+ */
+Result<NavigationIsolationOptions, nsresult> IsolationOptionsForNavigation(
+ CanonicalBrowsingContext* aTopBC, WindowGlobalParent* aParentWindow,
+ nsIURI* aChannelCreationURI, nsIChannel* aChannel,
+ const nsACString& aCurrentRemoteType, bool aHasCOOPMismatch,
+ bool aForNewTab, uint32_t aLoadStateLoadType,
+ const Maybe<uint64_t>& aChannelId,
+ const Maybe<nsCString>& aRemoteTypeOverride);
+
+// WorkerIsolationOptions is passed back to the RemoteWorkerManager to store the
+// destination process information for remote worker loads.
+struct WorkerIsolationOptions {
+ nsCString mRemoteType;
+};
+
+/**
+ * Given a specific worker principal and kind, determines which process the
+ * remote worker load should complete in.
+ *
+ * This method is only intended for use with remote workers.
+ */
+Result<WorkerIsolationOptions, nsresult> IsolationOptionsForWorker(
+ nsIPrincipal* aPrincipal, WorkerKind aWorkerKind,
+ const nsACString& aCurrentRemoteType, bool aUseRemoteSubframes);
+
+/**
+ * Adds a `highValue` permission to the permissions database, and make loads of
+ * that origin isolated.
+ *
+ * The 'aPermissionType' parameter indicates why the site is treated as a high
+ * value site. The possible values are:
+ *
+ * kHighValueCOOPPermission
+ * Called when a document request responds with a
+ * `Cross-Origin-Opener-Policy` header.
+ *
+ * kHighValueHasSavedLoginPermission
+ * Called for sites that have an associated login saved in the password
+ * manager.
+ *
+ * kHighValueIsLoggedInPermission
+ * Called when we detect a form with a password is submitted.
+ */
+void AddHighValuePermission(nsIPrincipal* aResultPrincipal,
+ const nsACString& aPermissionType);
+
+void AddHighValuePermission(const nsACString& aOrigin,
+ const nsACString& aPermissionType);
+
+/**
+ * Returns true when fission is enabled and the
+ * `fission.webContentIsolationStrategy` pref is set to `IsolateHighValue`.
+ */
+bool IsIsolateHighValueSiteEnabled();
+
+} // namespace mozilla::dom
+
+#endif
diff --git a/dom/ipc/ProcessPriorityManager.cpp b/dom/ipc/ProcessPriorityManager.cpp
new file mode 100644
index 0000000000..2c54b43295
--- /dev/null
+++ b/dom/ipc/ProcessPriorityManager.cpp
@@ -0,0 +1,1067 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ProcessPriorityManager.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/dom/CanonicalBrowsingContext.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/BrowserHost.h"
+#include "mozilla/dom/BrowserParent.h"
+#include "mozilla/Hal.h"
+#include "mozilla/IntegerPrintfMacros.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/ProfilerMarkers.h"
+#include "mozilla/ProfilerState.h"
+#include "mozilla/Services.h"
+#include "mozilla/StaticPrefs_dom.h"
+#include "mozilla/StaticPrefs_threads.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/Unused.h"
+#include "mozilla/Logging.h"
+#include "nsPrintfCString.h"
+#include "nsXULAppAPI.h"
+#include "nsFrameLoader.h"
+#include "nsINamed.h"
+#include "nsIObserverService.h"
+#include "StaticPtr.h"
+#include "nsIObserver.h"
+#include "nsITimer.h"
+#include "nsIPropertyBag2.h"
+#include "nsComponentManagerUtils.h"
+#include "nsCRT.h"
+#include "nsTHashSet.h"
+#include "nsQueryObject.h"
+#include "nsTHashMap.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace mozilla::hal;
+
+#ifdef XP_WIN
+# include <process.h>
+# define getpid _getpid
+#else
+# include <unistd.h>
+#endif
+
+#ifdef LOG
+# undef LOG
+#endif
+
+// Use LOGP inside a ParticularProcessPriorityManager method; use LOG
+// everywhere else. LOGP prints out information about the particular process
+// priority manager.
+//
+// (Wow, our logging story is a huge mess.)
+
+// #define ENABLE_LOGGING 1
+
+#if defined(ANDROID) && defined(ENABLE_LOGGING)
+# include <android/log.h>
+# define LOG(fmt, ...) \
+ __android_log_print(ANDROID_LOG_INFO, "Gecko:ProcessPriorityManager", fmt, \
+ ##__VA_ARGS__)
+# define LOGP(fmt, ...) \
+ __android_log_print( \
+ ANDROID_LOG_INFO, "Gecko:ProcessPriorityManager", \
+ "[%schild-id=%" PRIu64 ", pid=%d] " fmt, NameWithComma().get(), \
+ static_cast<uint64_t>(ChildID()), Pid(), ##__VA_ARGS__)
+
+#elif defined(ENABLE_LOGGING)
+# define LOG(fmt, ...) \
+ printf("ProcessPriorityManager - " fmt "\n", ##__VA_ARGS__)
+# define LOGP(fmt, ...) \
+ printf("ProcessPriorityManager[%schild-id=%" PRIu64 ", pid=%d] - " fmt \
+ "\n", \
+ NameWithComma().get(), static_cast<uint64_t>(ChildID()), Pid(), \
+ ##__VA_ARGS__)
+#else
+static LogModule* GetPPMLog() {
+ static LazyLogModule sLog("ProcessPriorityManager");
+ return sLog;
+}
+# define LOG(fmt, ...) \
+ MOZ_LOG(GetPPMLog(), LogLevel::Debug, \
+ ("ProcessPriorityManager - " fmt, ##__VA_ARGS__))
+# define LOGP(fmt, ...) \
+ MOZ_LOG(GetPPMLog(), LogLevel::Debug, \
+ ("ProcessPriorityManager[%schild-id=%" PRIu64 ", pid=%d] - " fmt, \
+ NameWithComma().get(), static_cast<uint64_t>(ChildID()), Pid(), \
+ ##__VA_ARGS__))
+#endif
+
+namespace geckoprofiler::markers {
+struct SubProcessPriorityChange {
+ static constexpr Span<const char> MarkerTypeName() {
+ return MakeStringSpan("subprocessprioritychange");
+ }
+ static void StreamJSONMarkerData(baseprofiler::SpliceableJSONWriter& aWriter,
+ int32_t aPid,
+ const ProfilerString8View& aPreviousPriority,
+ const ProfilerString8View& aNewPriority) {
+ aWriter.IntProperty("pid", aPid);
+ aWriter.StringProperty("Before", aPreviousPriority);
+ aWriter.StringProperty("After", aNewPriority);
+ }
+ static MarkerSchema MarkerTypeDisplay() {
+ using MS = MarkerSchema;
+ MS schema{MS::Location::MarkerChart, MS::Location::MarkerTable};
+ schema.AddKeyFormat("pid", MS::Format::Integer);
+ schema.AddKeyFormat("Before", MS::Format::String);
+ schema.AddKeyFormat("After", MS::Format::String);
+ schema.SetAllLabels(
+ "priority of child {marker.data.pid}:"
+ " {marker.data.Before} -> {marker.data.After}");
+ return schema;
+ }
+};
+
+struct SubProcessPriority {
+ static constexpr Span<const char> MarkerTypeName() {
+ return MakeStringSpan("subprocesspriority");
+ }
+ static void StreamJSONMarkerData(baseprofiler::SpliceableJSONWriter& aWriter,
+ int32_t aPid,
+ const ProfilerString8View& aPriority,
+ const ProfilingState& aProfilingState) {
+ aWriter.IntProperty("pid", aPid);
+ aWriter.StringProperty("Priority", aPriority);
+ aWriter.StringProperty("Marker cause",
+ ProfilerString8View::WrapNullTerminatedString(
+ ProfilingStateToString(aProfilingState)));
+ }
+ static MarkerSchema MarkerTypeDisplay() {
+ using MS = MarkerSchema;
+ MS schema{MS::Location::MarkerChart, MS::Location::MarkerTable};
+ schema.AddKeyFormat("pid", MS::Format::Integer);
+ schema.AddKeyFormat("Priority", MS::Format::String);
+ schema.AddKeyFormat("Marker cause", MS::Format::String);
+ schema.SetAllLabels(
+ "priority of child {marker.data.pid}: {marker.data.Priority}");
+ return schema;
+ }
+};
+} // namespace geckoprofiler::markers
+
+namespace {
+
+class ParticularProcessPriorityManager;
+
+/**
+ * This singleton class does the work to implement the process priority manager
+ * in the main process. This class may not be used in child processes. (You
+ * can call StaticInit, but it won't do anything, and GetSingleton() will
+ * return null.)
+ *
+ * ProcessPriorityManager::CurrentProcessIsForeground() and
+ * ProcessPriorityManager::AnyProcessHasHighPriority() which can be called in
+ * any process, are handled separately, by the ProcessPriorityManagerChild
+ * class.
+ */
+class ProcessPriorityManagerImpl final : public nsIObserver,
+ public nsSupportsWeakReference {
+ public:
+ /**
+ * If we're in the main process, get the ProcessPriorityManagerImpl
+ * singleton. If we're in a child process, return null.
+ */
+ static ProcessPriorityManagerImpl* GetSingleton();
+
+ static void StaticInit();
+ static bool PrefsEnabled();
+ static void SetProcessPriorityIfEnabled(int aPid, ProcessPriority aPriority);
+ static bool TestMode();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ /**
+ * This function implements ProcessPriorityManager::SetProcessPriority.
+ */
+ void SetProcessPriority(ContentParent* aContentParent,
+ ProcessPriority aPriority);
+
+ /**
+ * If a magic testing-only pref is set, notify the observer service on the
+ * given topic with the given data. This is used for testing
+ */
+ void FireTestOnlyObserverNotification(const char* aTopic,
+ const nsACString& aData);
+
+ /**
+ * This must be called by a ParticularProcessPriorityManager when it changes
+ * its priority.
+ */
+ void NotifyProcessPriorityChanged(
+ ParticularProcessPriorityManager* aParticularManager,
+ hal::ProcessPriority aOldPriority);
+
+ void BrowserPriorityChanged(CanonicalBrowsingContext* aBC, bool aPriority);
+ void BrowserPriorityChanged(BrowserParent* aBrowserParent, bool aPriority);
+
+ void ResetPriority(ContentParent* aContentParent);
+
+ private:
+ static bool sPrefListenersRegistered;
+ static bool sInitialized;
+ static StaticRefPtr<ProcessPriorityManagerImpl> sSingleton;
+
+ static void PrefChangedCallback(const char* aPref, void* aClosure);
+
+ ProcessPriorityManagerImpl();
+ ~ProcessPriorityManagerImpl();
+ ProcessPriorityManagerImpl(const ProcessPriorityManagerImpl&) = delete;
+
+ const ProcessPriorityManagerImpl& operator=(
+ const ProcessPriorityManagerImpl&) = delete;
+
+ void Init();
+
+ already_AddRefed<ParticularProcessPriorityManager>
+ GetParticularProcessPriorityManager(ContentParent* aContentParent);
+
+ void ObserveContentParentDestroyed(nsISupports* aSubject);
+
+ nsTHashMap<uint64_t, RefPtr<ParticularProcessPriorityManager> >
+ mParticularManagers;
+
+ /** Contains the PIDs of child processes holding high-priority wakelocks */
+ nsTHashSet<uint64_t> mHighPriorityChildIDs;
+};
+
+/**
+ * This singleton class implements the parts of the process priority manager
+ * that are available from all processes.
+ */
+class ProcessPriorityManagerChild final : public nsIObserver {
+ public:
+ static void StaticInit();
+ static ProcessPriorityManagerChild* Singleton();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ bool CurrentProcessIsForeground();
+
+ private:
+ static StaticRefPtr<ProcessPriorityManagerChild> sSingleton;
+
+ ProcessPriorityManagerChild();
+ ~ProcessPriorityManagerChild() = default;
+ ProcessPriorityManagerChild(const ProcessPriorityManagerChild&) = delete;
+
+ const ProcessPriorityManagerChild& operator=(
+ const ProcessPriorityManagerChild&) = delete;
+
+ void Init();
+
+ hal::ProcessPriority mCachedPriority;
+};
+
+/**
+ * This class manages the priority of one particular process. It is
+ * main-process only.
+ */
+class ParticularProcessPriorityManager final : public WakeLockObserver,
+ public nsITimerCallback,
+ public nsINamed,
+ public nsSupportsWeakReference {
+ ~ParticularProcessPriorityManager();
+
+ public:
+ explicit ParticularProcessPriorityManager(ContentParent* aContentParent);
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSITIMERCALLBACK
+
+ virtual void Notify(const WakeLockInformation& aInfo) override;
+ void Init();
+
+ int32_t Pid() const;
+ uint64_t ChildID() const;
+
+ /**
+ * Used in logging, this method returns the ContentParent's name followed by
+ * ", ". If we can't get the ContentParent's name for some reason, it
+ * returns an empty string.
+ *
+ * The reference returned here is guaranteed to be live until the next call
+ * to NameWithComma() or until the ParticularProcessPriorityManager is
+ * destroyed, whichever comes first.
+ */
+ const nsAutoCString& NameWithComma();
+
+ ProcessPriority CurrentPriority();
+ ProcessPriority ComputePriority();
+
+ enum TimeoutPref {
+ BACKGROUND_PERCEIVABLE_GRACE_PERIOD,
+ BACKGROUND_GRACE_PERIOD,
+ };
+
+ void ScheduleResetPriority(TimeoutPref aTimeoutPref);
+ void ResetPriority();
+ void ResetPriorityNow();
+ void SetPriorityNow(ProcessPriority aPriority);
+
+ void BrowserPriorityChanged(BrowserParent* aBrowserParent, bool aPriority);
+
+ void ShutDown();
+
+ NS_IMETHOD GetName(nsACString& aName) override {
+ aName.AssignLiteral("ParticularProcessPriorityManager");
+ return NS_OK;
+ }
+
+ private:
+ void FireTestOnlyObserverNotification(const char* aTopic, const char* aData);
+
+ bool IsHoldingWakeLock(const nsAString& aTopic);
+
+ ContentParent* mContentParent;
+ uint64_t mChildID;
+ ProcessPriority mPriority;
+ bool mHoldsCPUWakeLock;
+ bool mHoldsHighPriorityWakeLock;
+ bool mHoldsPlayingAudioWakeLock;
+ bool mHoldsPlayingVideoWakeLock;
+
+ /**
+ * Used to implement NameWithComma().
+ */
+ nsAutoCString mNameWithComma;
+
+ nsCOMPtr<nsITimer> mResetPriorityTimer;
+
+ // This hashtable contains the list of high priority TabIds for this process.
+ nsTHashSet<uint64_t> mHighPriorityBrowserParents;
+};
+
+/* static */
+bool ProcessPriorityManagerImpl::sInitialized = false;
+/* static */
+bool ProcessPriorityManagerImpl::sPrefListenersRegistered = false;
+/* static */
+StaticRefPtr<ProcessPriorityManagerImpl> ProcessPriorityManagerImpl::sSingleton;
+
+NS_IMPL_ISUPPORTS(ProcessPriorityManagerImpl, nsIObserver,
+ nsISupportsWeakReference);
+
+/* static */
+void ProcessPriorityManagerImpl::PrefChangedCallback(const char* aPref,
+ void* aClosure) {
+ StaticInit();
+ if (!PrefsEnabled() && sSingleton) {
+ sSingleton = nullptr;
+ sInitialized = false;
+ }
+}
+
+/* static */
+bool ProcessPriorityManagerImpl::PrefsEnabled() {
+ return StaticPrefs::dom_ipc_processPriorityManager_enabled();
+}
+
+/* static */
+void ProcessPriorityManagerImpl::SetProcessPriorityIfEnabled(
+ int aPid, ProcessPriority aPriority) {
+ // The preference doesn't disable the process priority manager, but only its
+ // effect. This way the IPCs still happen and can be used to collect telemetry
+ // about CPU use.
+ if (PrefsEnabled()) {
+ hal::SetProcessPriority(aPid, aPriority);
+ }
+}
+
+/* static */
+bool ProcessPriorityManagerImpl::TestMode() {
+ return StaticPrefs::dom_ipc_processPriorityManager_testMode();
+}
+
+/* static */
+void ProcessPriorityManagerImpl::StaticInit() {
+ if (sInitialized) {
+ return;
+ }
+
+ // The process priority manager is main-process only.
+ if (!XRE_IsParentProcess()) {
+ sInitialized = true;
+ return;
+ }
+
+ // Run StaticInit() again if the pref changes. We don't expect this to
+ // happen in normal operation, but it happens during testing.
+ if (!sPrefListenersRegistered) {
+ sPrefListenersRegistered = true;
+ Preferences::RegisterCallback(PrefChangedCallback,
+ "dom.ipc.processPriorityManager.enabled");
+ }
+
+ sInitialized = true;
+
+ sSingleton = new ProcessPriorityManagerImpl();
+ sSingleton->Init();
+ ClearOnShutdown(&sSingleton);
+}
+
+/* static */
+ProcessPriorityManagerImpl* ProcessPriorityManagerImpl::GetSingleton() {
+ if (!sSingleton) {
+ StaticInit();
+ }
+
+ return sSingleton;
+}
+
+ProcessPriorityManagerImpl::ProcessPriorityManagerImpl() {
+ MOZ_ASSERT(XRE_IsParentProcess());
+}
+
+ProcessPriorityManagerImpl::~ProcessPriorityManagerImpl() = default;
+
+void ProcessPriorityManagerImpl::Init() {
+ LOG("Starting up. This is the parent process.");
+
+ // The parent process's priority never changes; set it here and then forget
+ // about it. We'll manage only subprocesses' priorities using the process
+ // priority manager.
+ SetProcessPriorityIfEnabled(getpid(), PROCESS_PRIORITY_PARENT_PROCESS);
+
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ if (os) {
+ os->AddObserver(this, "ipc:content-shutdown", /* ownsWeak */ true);
+ }
+}
+
+NS_IMETHODIMP
+ProcessPriorityManagerImpl::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData) {
+ nsDependentCString topic(aTopic);
+ if (topic.EqualsLiteral("ipc:content-shutdown")) {
+ ObserveContentParentDestroyed(aSubject);
+ } else {
+ MOZ_ASSERT(false);
+ }
+
+ return NS_OK;
+}
+
+already_AddRefed<ParticularProcessPriorityManager>
+ProcessPriorityManagerImpl::GetParticularProcessPriorityManager(
+ ContentParent* aContentParent) {
+ // If this content parent is already being shut down, there's no
+ // need to adjust its priority.
+ if (aContentParent->IsDead()) {
+ return nullptr;
+ }
+
+ const uint64_t cpId = aContentParent->ChildID();
+ return mParticularManagers.WithEntryHandle(cpId, [&](auto&& entry) {
+ if (!entry) {
+ entry.Insert(new ParticularProcessPriorityManager(aContentParent));
+ entry.Data()->Init();
+ }
+ return do_AddRef(entry.Data());
+ });
+}
+
+void ProcessPriorityManagerImpl::SetProcessPriority(
+ ContentParent* aContentParent, ProcessPriority aPriority) {
+ MOZ_ASSERT(aContentParent);
+ if (RefPtr pppm = GetParticularProcessPriorityManager(aContentParent)) {
+ pppm->SetPriorityNow(aPriority);
+ }
+}
+
+void ProcessPriorityManagerImpl::ObserveContentParentDestroyed(
+ nsISupports* aSubject) {
+ nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
+ NS_ENSURE_TRUE_VOID(props);
+
+ uint64_t childID = CONTENT_PROCESS_ID_UNKNOWN;
+ props->GetPropertyAsUint64(u"childID"_ns, &childID);
+ NS_ENSURE_TRUE_VOID(childID != CONTENT_PROCESS_ID_UNKNOWN);
+
+ if (auto entry = mParticularManagers.Lookup(childID)) {
+ entry.Data()->ShutDown();
+ mHighPriorityChildIDs.Remove(childID);
+ entry.Remove();
+ }
+}
+
+void ProcessPriorityManagerImpl::NotifyProcessPriorityChanged(
+ ParticularProcessPriorityManager* aParticularManager,
+ ProcessPriority aOldPriority) {
+ ProcessPriority newPriority = aParticularManager->CurrentPriority();
+
+ if (newPriority >= PROCESS_PRIORITY_FOREGROUND_HIGH &&
+ aOldPriority < PROCESS_PRIORITY_FOREGROUND_HIGH) {
+ mHighPriorityChildIDs.Insert(aParticularManager->ChildID());
+ } else if (newPriority < PROCESS_PRIORITY_FOREGROUND_HIGH &&
+ aOldPriority >= PROCESS_PRIORITY_FOREGROUND_HIGH) {
+ mHighPriorityChildIDs.Remove(aParticularManager->ChildID());
+ }
+}
+
+static nsCString BCToString(dom::CanonicalBrowsingContext* aBC) {
+ nsCOMPtr<nsIURI> uri = aBC->GetCurrentURI();
+ return nsPrintfCString("id=%" PRIu64 " uri=%s active=%d pactive=%d",
+ aBC->Id(),
+ uri ? uri->GetSpecOrDefault().get() : "(no uri)",
+ aBC->IsActive(), aBC->IsPriorityActive());
+}
+
+void ProcessPriorityManagerImpl::BrowserPriorityChanged(
+ dom::CanonicalBrowsingContext* aBC, bool aPriority) {
+ MOZ_ASSERT(aBC->IsTop());
+
+ LOG("BrowserPriorityChanged(%s, %d)\n", BCToString(aBC).get(), aPriority);
+
+ bool alreadyActive = aBC->IsPriorityActive();
+ if (alreadyActive == aPriority) {
+ return;
+ }
+
+ Telemetry::ScalarAdd(
+ Telemetry::ScalarID::DOM_CONTENTPROCESS_OS_PRIORITY_CHANGE_CONSIDERED, 1);
+
+ aBC->SetPriorityActive(aPriority);
+
+ aBC->PreOrderWalk([&](BrowsingContext* aContext) {
+ CanonicalBrowsingContext* canonical = aContext->Canonical();
+ LOG("PreOrderWalk for %p: %p -> %p, %p\n", aBC, canonical,
+ canonical->GetContentParent(), canonical->GetBrowserParent());
+ if (ContentParent* cp = canonical->GetContentParent()) {
+ if (RefPtr pppm = GetParticularProcessPriorityManager(cp)) {
+ if (auto* bp = canonical->GetBrowserParent()) {
+ pppm->BrowserPriorityChanged(bp, aPriority);
+ }
+ }
+ }
+ });
+}
+
+void ProcessPriorityManagerImpl::BrowserPriorityChanged(
+ BrowserParent* aBrowserParent, bool aPriority) {
+ LOG("BrowserPriorityChanged(bp=%p, %d)\n", aBrowserParent, aPriority);
+
+ if (RefPtr pppm =
+ GetParticularProcessPriorityManager(aBrowserParent->Manager())) {
+ Telemetry::ScalarAdd(
+ Telemetry::ScalarID::DOM_CONTENTPROCESS_OS_PRIORITY_CHANGE_CONSIDERED,
+ 1);
+ pppm->BrowserPriorityChanged(aBrowserParent, aPriority);
+ }
+}
+
+void ProcessPriorityManagerImpl::ResetPriority(ContentParent* aContentParent) {
+ if (RefPtr pppm = GetParticularProcessPriorityManager(aContentParent)) {
+ pppm->ResetPriority();
+ }
+}
+
+NS_IMPL_ISUPPORTS(ParticularProcessPriorityManager, nsITimerCallback,
+ nsISupportsWeakReference, nsINamed);
+
+ParticularProcessPriorityManager::ParticularProcessPriorityManager(
+ ContentParent* aContentParent)
+ : mContentParent(aContentParent),
+ mChildID(aContentParent->ChildID()),
+ mPriority(PROCESS_PRIORITY_UNKNOWN),
+ mHoldsCPUWakeLock(false),
+ mHoldsHighPriorityWakeLock(false),
+ mHoldsPlayingAudioWakeLock(false),
+ mHoldsPlayingVideoWakeLock(false) {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ MOZ_RELEASE_ASSERT(!aContentParent->IsDead());
+ LOGP("Creating ParticularProcessPriorityManager.");
+ // Our static analysis doesn't allow capturing ref-counted pointers in
+ // lambdas, so we need to hide it in a uintptr_t. This is safe because this
+ // lambda will be destroyed in ~ParticularProcessPriorityManager().
+ uintptr_t self = reinterpret_cast<uintptr_t>(this);
+ profiler_add_state_change_callback(
+ AllProfilingStates(),
+ [self](ProfilingState aProfilingState) {
+ const ParticularProcessPriorityManager* selfPtr =
+ reinterpret_cast<const ParticularProcessPriorityManager*>(self);
+ PROFILER_MARKER("Subprocess Priority", OTHER,
+ MarkerThreadId::MainThread(), SubProcessPriority,
+ selfPtr->Pid(),
+ ProfilerString8View::WrapNullTerminatedString(
+ ProcessPriorityToString(selfPtr->mPriority)),
+ aProfilingState);
+ },
+ self);
+}
+
+void ParticularProcessPriorityManager::Init() {
+ RegisterWakeLockObserver(this);
+
+ // This process may already hold the CPU lock; for example, our parent may
+ // have acquired it on our behalf.
+ mHoldsCPUWakeLock = IsHoldingWakeLock(u"cpu"_ns);
+ mHoldsHighPriorityWakeLock = IsHoldingWakeLock(u"high-priority"_ns);
+ mHoldsPlayingAudioWakeLock = IsHoldingWakeLock(u"audio-playing"_ns);
+ mHoldsPlayingVideoWakeLock = IsHoldingWakeLock(u"video-playing"_ns);
+
+ LOGP(
+ "Done starting up. mHoldsCPUWakeLock=%d, "
+ "mHoldsHighPriorityWakeLock=%d, mHoldsPlayingAudioWakeLock=%d, "
+ "mHoldsPlayingVideoWakeLock=%d",
+ mHoldsCPUWakeLock, mHoldsHighPriorityWakeLock, mHoldsPlayingAudioWakeLock,
+ mHoldsPlayingVideoWakeLock);
+}
+
+bool ParticularProcessPriorityManager::IsHoldingWakeLock(
+ const nsAString& aTopic) {
+ WakeLockInformation info;
+ GetWakeLockInfo(aTopic, &info);
+ return info.lockingProcesses().Contains(ChildID());
+}
+
+ParticularProcessPriorityManager::~ParticularProcessPriorityManager() {
+ LOGP("Destroying ParticularProcessPriorityManager.");
+
+ profiler_remove_state_change_callback(reinterpret_cast<uintptr_t>(this));
+
+ ShutDown();
+}
+
+/* virtual */
+void ParticularProcessPriorityManager::Notify(
+ const WakeLockInformation& aInfo) {
+ if (!mContentParent) {
+ // We've been shut down.
+ return;
+ }
+
+ bool* dest = nullptr;
+ if (aInfo.topic().EqualsLiteral("cpu")) {
+ dest = &mHoldsCPUWakeLock;
+ } else if (aInfo.topic().EqualsLiteral("high-priority")) {
+ dest = &mHoldsHighPriorityWakeLock;
+ } else if (aInfo.topic().EqualsLiteral("audio-playing")) {
+ dest = &mHoldsPlayingAudioWakeLock;
+ } else if (aInfo.topic().EqualsLiteral("video-playing")) {
+ dest = &mHoldsPlayingVideoWakeLock;
+ }
+
+ if (dest) {
+ bool thisProcessLocks = aInfo.lockingProcesses().Contains(ChildID());
+ if (thisProcessLocks != *dest) {
+ *dest = thisProcessLocks;
+ LOGP(
+ "Got wake lock changed event. "
+ "Now mHoldsCPUWakeLock=%d, mHoldsHighPriorityWakeLock=%d, "
+ "mHoldsPlayingAudioWakeLock=%d, mHoldsPlayingVideoWakeLock=%d",
+ mHoldsCPUWakeLock, mHoldsHighPriorityWakeLock,
+ mHoldsPlayingAudioWakeLock, mHoldsPlayingVideoWakeLock);
+ ResetPriority();
+ }
+ }
+}
+
+uint64_t ParticularProcessPriorityManager::ChildID() const {
+ // We have to cache mContentParent->ChildID() instead of getting it from the
+ // ContentParent each time because after ShutDown() is called, mContentParent
+ // is null. If we didn't cache ChildID(), then we wouldn't be able to run
+ // LOGP() after ShutDown().
+ return mChildID;
+}
+
+int32_t ParticularProcessPriorityManager::Pid() const {
+ return mContentParent ? mContentParent->Pid() : -1;
+}
+
+const nsAutoCString& ParticularProcessPriorityManager::NameWithComma() {
+ mNameWithComma.Truncate();
+ if (!mContentParent) {
+ return mNameWithComma; // empty string
+ }
+
+ nsAutoString name;
+ mContentParent->FriendlyName(name);
+ if (name.IsEmpty()) {
+ return mNameWithComma; // empty string
+ }
+
+ CopyUTF16toUTF8(name, mNameWithComma);
+ mNameWithComma.AppendLiteral(", ");
+ return mNameWithComma;
+}
+
+void ParticularProcessPriorityManager::ResetPriority() {
+ ProcessPriority processPriority = ComputePriority();
+ if (mPriority == PROCESS_PRIORITY_UNKNOWN || mPriority > processPriority) {
+ // Apps set at a perceivable background priority are often playing media.
+ // Most media will have short gaps while changing tracks between songs,
+ // switching videos, etc. Give these apps a longer grace period so they
+ // can get their next track started, if there is one, before getting
+ // downgraded.
+ if (mPriority == PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE) {
+ ScheduleResetPriority(BACKGROUND_PERCEIVABLE_GRACE_PERIOD);
+ } else {
+ ScheduleResetPriority(BACKGROUND_GRACE_PERIOD);
+ }
+ return;
+ }
+
+ SetPriorityNow(processPriority);
+}
+
+void ParticularProcessPriorityManager::ResetPriorityNow() {
+ SetPriorityNow(ComputePriority());
+}
+
+void ParticularProcessPriorityManager::ScheduleResetPriority(
+ TimeoutPref aTimeoutPref) {
+ if (mResetPriorityTimer) {
+ LOGP("ScheduleResetPriority bailing; the timer is already running.");
+ return;
+ }
+
+ uint32_t timeout = 0;
+ switch (aTimeoutPref) {
+ case BACKGROUND_PERCEIVABLE_GRACE_PERIOD:
+ timeout = StaticPrefs::
+ dom_ipc_processPriorityManager_backgroundPerceivableGracePeriodMS();
+ break;
+ case BACKGROUND_GRACE_PERIOD:
+ timeout =
+ StaticPrefs::dom_ipc_processPriorityManager_backgroundGracePeriodMS();
+ break;
+ default:
+ MOZ_ASSERT(false, "Unrecognized timeout pref");
+ break;
+ }
+
+ LOGP("Scheduling reset timer to fire in %dms.", timeout);
+ NS_NewTimerWithCallback(getter_AddRefs(mResetPriorityTimer), this, timeout,
+ nsITimer::TYPE_ONE_SHOT);
+}
+
+NS_IMETHODIMP
+ParticularProcessPriorityManager::Notify(nsITimer* aTimer) {
+ LOGP("Reset priority timer callback; about to ResetPriorityNow.");
+ ResetPriorityNow();
+ mResetPriorityTimer = nullptr;
+ return NS_OK;
+}
+
+ProcessPriority ParticularProcessPriorityManager::CurrentPriority() {
+ return mPriority;
+}
+
+ProcessPriority ParticularProcessPriorityManager::ComputePriority() {
+ if (!mHighPriorityBrowserParents.IsEmpty() ||
+ mContentParent->GetRemoteType() == EXTENSION_REMOTE_TYPE ||
+ mHoldsPlayingAudioWakeLock) {
+ return PROCESS_PRIORITY_FOREGROUND;
+ }
+
+ if (mHoldsCPUWakeLock || mHoldsHighPriorityWakeLock ||
+ mHoldsPlayingVideoWakeLock) {
+ return PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE;
+ }
+
+ return PROCESS_PRIORITY_BACKGROUND;
+}
+
+#ifdef XP_MACOSX
+// Method used for setting QoS levels on background main threads.
+static bool PriorityUsesLowPowerMainThread(
+ const hal::ProcessPriority& aPriority) {
+ return aPriority == hal::PROCESS_PRIORITY_BACKGROUND ||
+ aPriority == hal::PROCESS_PRIORITY_PREALLOC;
+}
+// Method reduces redundancy in pref check while addressing the edge case
+// where a pref is flipped to false during active browser use.
+static bool PrefsUseLowPriorityThreads() {
+ return StaticPrefs::threads_use_low_power_enabled() &&
+ StaticPrefs::threads_lower_mainthread_priority_in_background_enabled();
+}
+#endif
+
+void ParticularProcessPriorityManager::SetPriorityNow(
+ ProcessPriority aPriority) {
+ if (aPriority == PROCESS_PRIORITY_UNKNOWN) {
+ MOZ_ASSERT(false);
+ return;
+ }
+
+ LOGP("Changing priority from %s to %s (cp=%p).",
+ ProcessPriorityToString(mPriority), ProcessPriorityToString(aPriority),
+ mContentParent);
+
+ if (!mContentParent || mPriority == aPriority) {
+ return;
+ }
+
+ PROFILER_MARKER(
+ "Subprocess Priority", OTHER,
+ MarkerOptions(MarkerThreadId::MainThread(), MarkerStack::Capture()),
+ SubProcessPriorityChange, this->Pid(),
+ ProfilerString8View::WrapNullTerminatedString(
+ ProcessPriorityToString(mPriority)),
+ ProfilerString8View::WrapNullTerminatedString(
+ ProcessPriorityToString(aPriority)));
+
+ ProcessPriority oldPriority = mPriority;
+
+ mPriority = aPriority;
+
+ // We skip incrementing the DOM_CONTENTPROCESS_OS_PRIORITY_RAISED if we're
+ // transitioning from the PROCESS_PRIORITY_UNKNOWN level, which is where
+ // we initialize at.
+ if (oldPriority < mPriority && oldPriority != PROCESS_PRIORITY_UNKNOWN) {
+ Telemetry::ScalarAdd(
+ Telemetry::ScalarID::DOM_CONTENTPROCESS_OS_PRIORITY_RAISED, 1);
+ } else if (oldPriority > mPriority) {
+ Telemetry::ScalarAdd(
+ Telemetry::ScalarID::DOM_CONTENTPROCESS_OS_PRIORITY_LOWERED, 1);
+ }
+
+ ProcessPriorityManagerImpl::SetProcessPriorityIfEnabled(Pid(), mPriority);
+
+ if (oldPriority != mPriority) {
+ ProcessPriorityManagerImpl::GetSingleton()->NotifyProcessPriorityChanged(
+ this, oldPriority);
+
+#ifdef XP_MACOSX
+ // In cases where we have low-power threads enabled (such as on MacOS) we
+ // can go ahead and put the main thread in the background here. If the new
+ // priority is the background priority, we can tell the OS to put the main
+ // thread on low-power cores. Alternately, if we are changing from the
+ // background to a higher priority, we change the main thread back to its
+ // normal state.
+ //
+ // The messages for this will be relayed using the ProcessHangMonitor such
+ // that the priority can be raised even if the main thread is unresponsive.
+ if (PriorityUsesLowPowerMainThread(mPriority) !=
+ (PriorityUsesLowPowerMainThread(oldPriority))) {
+ if (PriorityUsesLowPowerMainThread(mPriority) &&
+ PrefsUseLowPriorityThreads()) {
+ mContentParent->SetMainThreadQoSPriority(nsIThread::QOS_PRIORITY_LOW);
+ } else if (PriorityUsesLowPowerMainThread(oldPriority)) {
+ // In the event that the user changes prefs while tabs are in the
+ // background, we still want to have the ability to put the main thread
+ // back in the foreground to keep tabs from being stuck in the
+ // background priority.
+ mContentParent->SetMainThreadQoSPriority(
+ nsIThread::QOS_PRIORITY_NORMAL);
+ }
+ }
+#endif
+
+ Unused << mContentParent->SendNotifyProcessPriorityChanged(mPriority);
+ }
+
+ FireTestOnlyObserverNotification("process-priority-set",
+ ProcessPriorityToString(mPriority));
+}
+
+void ParticularProcessPriorityManager::BrowserPriorityChanged(
+ BrowserParent* aBrowserParent, bool aPriority) {
+ MOZ_ASSERT(aBrowserParent);
+
+ if (!aPriority) {
+ mHighPriorityBrowserParents.Remove(aBrowserParent->GetTabId());
+ } else {
+ mHighPriorityBrowserParents.Insert(aBrowserParent->GetTabId());
+ }
+
+ ResetPriority();
+}
+
+void ParticularProcessPriorityManager::ShutDown() {
+ LOGP("shutdown for %p (mContentParent %p)", this, mContentParent);
+
+ // Unregister our wake lock observer if ShutDown hasn't been called. (The
+ // wake lock observer takes raw refs, so we don't want to take chances here!)
+ // We don't call UnregisterWakeLockObserver unconditionally because the code
+ // will print a warning if it's called unnecessarily.
+ if (mContentParent) {
+ UnregisterWakeLockObserver(this);
+ }
+
+ if (mResetPriorityTimer) {
+ mResetPriorityTimer->Cancel();
+ mResetPriorityTimer = nullptr;
+ }
+
+ mContentParent = nullptr;
+}
+
+void ProcessPriorityManagerImpl::FireTestOnlyObserverNotification(
+ const char* aTopic, const nsACString& aData) {
+ if (!TestMode()) {
+ return;
+ }
+
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ NS_ENSURE_TRUE_VOID(os);
+
+ nsPrintfCString topic("process-priority-manager:TEST-ONLY:%s", aTopic);
+
+ LOG("Notifying observer %s, data %s", topic.get(),
+ PromiseFlatCString(aData).get());
+ os->NotifyObservers(nullptr, topic.get(), NS_ConvertUTF8toUTF16(aData).get());
+}
+
+void ParticularProcessPriorityManager::FireTestOnlyObserverNotification(
+ const char* aTopic, const char* aData) {
+ MOZ_ASSERT(aData, "Pass in data");
+
+ if (!ProcessPriorityManagerImpl::TestMode()) {
+ return;
+ }
+
+ nsAutoCString data(nsPrintfCString("%" PRIu64, ChildID()));
+ data.Append(':');
+ data.AppendASCII(aData);
+
+ // ProcessPriorityManagerImpl::GetSingleton() is guaranteed not to return
+ // null, since ProcessPriorityManagerImpl is the only class which creates
+ // ParticularProcessPriorityManagers.
+
+ ProcessPriorityManagerImpl::GetSingleton()->FireTestOnlyObserverNotification(
+ aTopic, data);
+}
+
+StaticRefPtr<ProcessPriorityManagerChild>
+ ProcessPriorityManagerChild::sSingleton;
+
+/* static */
+void ProcessPriorityManagerChild::StaticInit() {
+ if (!sSingleton) {
+ sSingleton = new ProcessPriorityManagerChild();
+ sSingleton->Init();
+ ClearOnShutdown(&sSingleton);
+ }
+}
+
+/* static */
+ProcessPriorityManagerChild* ProcessPriorityManagerChild::Singleton() {
+ StaticInit();
+ return sSingleton;
+}
+
+NS_IMPL_ISUPPORTS(ProcessPriorityManagerChild, nsIObserver)
+
+ProcessPriorityManagerChild::ProcessPriorityManagerChild() {
+ if (XRE_IsParentProcess()) {
+ mCachedPriority = PROCESS_PRIORITY_PARENT_PROCESS;
+ } else {
+ mCachedPriority = PROCESS_PRIORITY_UNKNOWN;
+ }
+}
+
+void ProcessPriorityManagerChild::Init() {
+ // The process priority should only be changed in child processes; don't even
+ // bother listening for changes if we're in the main process.
+ if (!XRE_IsParentProcess()) {
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ NS_ENSURE_TRUE_VOID(os);
+ os->AddObserver(this, "ipc:process-priority-changed", /* weak = */ false);
+ }
+}
+
+NS_IMETHODIMP
+ProcessPriorityManagerChild::Observe(nsISupports* aSubject, const char* aTopic,
+ const char16_t* aData) {
+ MOZ_ASSERT(!strcmp(aTopic, "ipc:process-priority-changed"));
+
+ nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
+ NS_ENSURE_TRUE(props, NS_OK);
+
+ int32_t priority = static_cast<int32_t>(PROCESS_PRIORITY_UNKNOWN);
+ props->GetPropertyAsInt32(u"priority"_ns, &priority);
+ NS_ENSURE_TRUE(ProcessPriority(priority) != PROCESS_PRIORITY_UNKNOWN, NS_OK);
+
+ mCachedPriority = static_cast<ProcessPriority>(priority);
+
+ return NS_OK;
+}
+
+bool ProcessPriorityManagerChild::CurrentProcessIsForeground() {
+ return mCachedPriority == PROCESS_PRIORITY_UNKNOWN ||
+ mCachedPriority >= PROCESS_PRIORITY_FOREGROUND;
+}
+
+} // namespace
+
+namespace mozilla {
+
+/* static */
+void ProcessPriorityManager::Init() {
+ ProcessPriorityManagerImpl::StaticInit();
+ ProcessPriorityManagerChild::StaticInit();
+}
+
+/* static */
+void ProcessPriorityManager::SetProcessPriority(ContentParent* aContentParent,
+ ProcessPriority aPriority) {
+ MOZ_ASSERT(aContentParent);
+ MOZ_ASSERT(aContentParent->Pid() != -1);
+
+ ProcessPriorityManagerImpl* singleton =
+ ProcessPriorityManagerImpl::GetSingleton();
+ if (singleton) {
+ singleton->SetProcessPriority(aContentParent, aPriority);
+ }
+}
+
+/* static */
+bool ProcessPriorityManager::CurrentProcessIsForeground() {
+ return ProcessPriorityManagerChild::Singleton()->CurrentProcessIsForeground();
+}
+
+/* static */
+void ProcessPriorityManager::BrowserPriorityChanged(
+ CanonicalBrowsingContext* aBC, bool aPriority) {
+ if (auto* singleton = ProcessPriorityManagerImpl::GetSingleton()) {
+ singleton->BrowserPriorityChanged(aBC, aPriority);
+ }
+}
+
+/* static */
+void ProcessPriorityManager::BrowserPriorityChanged(
+ BrowserParent* aBrowserParent, bool aPriority) {
+ MOZ_ASSERT(aBrowserParent);
+
+ ProcessPriorityManagerImpl* singleton =
+ ProcessPriorityManagerImpl::GetSingleton();
+ if (!singleton) {
+ return;
+ }
+ singleton->BrowserPriorityChanged(aBrowserParent, aPriority);
+}
+
+/* static */
+void ProcessPriorityManager::RemoteBrowserFrameShown(
+ nsFrameLoader* aFrameLoader) {
+ ProcessPriorityManagerImpl* singleton =
+ ProcessPriorityManagerImpl::GetSingleton();
+ if (!singleton) {
+ return;
+ }
+
+ BrowserParent* bp = BrowserParent::GetFrom(aFrameLoader);
+ NS_ENSURE_TRUE_VOID(bp);
+
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ // Ignore calls that aren't from a Browser.
+ if (!aFrameLoader->OwnerIsMozBrowserFrame()) {
+ return;
+ }
+
+ singleton->ResetPriority(bp->Manager());
+}
+
+} // namespace mozilla
diff --git a/dom/ipc/ProcessPriorityManager.h b/dom/ipc/ProcessPriorityManager.h
new file mode 100644
index 0000000000..8153a60993
--- /dev/null
+++ b/dom/ipc/ProcessPriorityManager.h
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_ProcessPriorityManager_h_
+#define mozilla_ProcessPriorityManager_h_
+
+#include "mozilla/HalTypes.h"
+
+class nsFrameLoader;
+
+namespace mozilla {
+namespace dom {
+class BrowserParent;
+class CanonicalBrowsingContext;
+class ContentParent;
+} // namespace dom
+
+/**
+ * This class sets the priority of subprocesses in response to explicit
+ * requests and events in the system.
+ *
+ * A process's priority changes e.g. when it goes into the background via
+ * mozbrowser's setVisible(false). Process priority affects CPU scheduling and
+ * also which processes get killed when we run out of memory.
+ *
+ * After you call Initialize(), the only thing you probably have to do is call
+ * SetProcessPriority on processes immediately after creating them in order to
+ * set their initial priority. The ProcessPriorityManager takes care of the
+ * rest.
+ */
+class ProcessPriorityManager final {
+ public:
+ /**
+ * Initialize the ProcessPriorityManager machinery, causing the
+ * ProcessPriorityManager to actively manage the priorities of all
+ * subprocesses. You should call this before creating any subprocesses.
+ *
+ * You should also call this function even if you're in a child process,
+ * since it will initialize ProcessPriorityManagerChild.
+ */
+ static void Init();
+
+ /**
+ * Set the process priority of a given ContentParent's process.
+ *
+ * Note that because this method takes a ContentParent*, you can only set the
+ * priority of your subprocesses. In fact, because we don't support nested
+ * content processes (bug 761935), you can only call this method from the
+ * main process.
+ *
+ * It probably only makes sense to call this function immediately after a
+ * process is created. At this point, the process priority manager doesn't
+ * have enough context about the processs to know what its priority should
+ * be.
+ *
+ * Eventually whatever priority you set here can and probably will be
+ * overwritten by the process priority manager.
+ */
+ static void SetProcessPriority(dom::ContentParent* aContentParent,
+ hal::ProcessPriority aPriority);
+
+ /**
+ * Returns true iff this process's priority is FOREGROUND*.
+ *
+ * Note that because process priorities are set in the main process, it's
+ * possible for this method to return a stale value. So be careful about
+ * what you use this for.
+ */
+ static bool CurrentProcessIsForeground();
+
+ /**
+ * Updates the contents of mHighPriorityBrowserParents to keep track of
+ * the list of TabIds for this process that are high priority.
+ */
+ static void BrowserPriorityChanged(dom::CanonicalBrowsingContext* aBC,
+ bool aPriority);
+ static void BrowserPriorityChanged(dom::BrowserParent* aBrowserParent,
+ bool aPriority);
+
+ static void RemoteBrowserFrameShown(nsFrameLoader* aFrameLoader);
+
+ private:
+ ProcessPriorityManager();
+ ProcessPriorityManager(const ProcessPriorityManager&) = delete;
+
+ const ProcessPriorityManager& operator=(const ProcessPriorityManager&) =
+ delete;
+};
+
+} // namespace mozilla
+
+#endif
diff --git a/dom/ipc/PropertyBagUtils.cpp b/dom/ipc/PropertyBagUtils.cpp
new file mode 100644
index 0000000000..6c1b3e8fea
--- /dev/null
+++ b/dom/ipc/PropertyBagUtils.cpp
@@ -0,0 +1,261 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PropertyBagUtils.h"
+
+#include "mozilla/SimpleEnumerator.h"
+#include "mozilla/dom/DOMTypes.h"
+#include "nsCOMPtr.h"
+#include "nsHashPropertyBag.h"
+#include "nsID.h"
+#include "nsIProperty.h"
+#include "nsIURI.h"
+#include "nsVariant.h"
+
+using namespace IPC;
+using namespace mozilla::dom;
+
+namespace mozilla::ipc {
+
+void IPDLParamTraits<nsIVariant*>::Write(MessageWriter* aWriter,
+ IProtocol* aActor,
+ nsIVariant* aParam) {
+ IDPLVariant variant;
+
+ variant.type() = aParam->GetDataType();
+
+ switch (variant.type()) {
+ case nsIDataType::VTYPE_INT8:
+ case nsIDataType::VTYPE_UINT8:
+ case nsIDataType::VTYPE_CHAR: {
+ uint8_t value;
+ MOZ_ALWAYS_SUCCEEDS(aParam->GetAsUint8(&value));
+ variant.data() = value;
+ break;
+ }
+ case nsIDataType::VTYPE_WCHAR:
+ case nsIDataType::VTYPE_INT16: {
+ int16_t value;
+ MOZ_ALWAYS_SUCCEEDS(aParam->GetAsInt16(&value));
+ variant.data() = value;
+ break;
+ }
+ case nsIDataType::VTYPE_UINT16: {
+ uint16_t value;
+ MOZ_ALWAYS_SUCCEEDS(aParam->GetAsUint16(&value));
+ variant.data() = value;
+ break;
+ }
+ case nsIDataType::VTYPE_INT32: {
+ int32_t value;
+ MOZ_ALWAYS_SUCCEEDS(aParam->GetAsInt32(&value));
+ variant.data() = value;
+ break;
+ }
+ case nsIDataType::VTYPE_UINT32: {
+ uint32_t value;
+ MOZ_ALWAYS_SUCCEEDS(aParam->GetAsUint32(&value));
+ variant.data() = value;
+ break;
+ }
+ case nsIDataType::VTYPE_FLOAT: {
+ float value;
+ MOZ_ALWAYS_SUCCEEDS(aParam->GetAsFloat(&value));
+ variant.data() = value;
+ break;
+ }
+ case nsIDataType::VTYPE_DOUBLE: {
+ double value;
+ MOZ_ALWAYS_SUCCEEDS(aParam->GetAsDouble(&value));
+ variant.data() = value;
+ break;
+ }
+ case nsIDataType::VTYPE_BOOL: {
+ bool value;
+ MOZ_ALWAYS_SUCCEEDS(aParam->GetAsBool(&value));
+ variant.data() = value;
+ break;
+ }
+ case nsIDataType::VTYPE_ID: {
+ nsID value;
+ MOZ_ALWAYS_SUCCEEDS(aParam->GetAsID(&value));
+ variant.data() = value;
+ break;
+ }
+ case nsIDataType::VTYPE_ASTRING:
+ case nsIDataType::VTYPE_WCHAR_STR:
+ case nsIDataType::VTYPE_WSTRING_SIZE_IS: {
+ nsString value;
+ MOZ_ALWAYS_SUCCEEDS(aParam->GetAsAString(value));
+ variant.data() = value;
+ break;
+ }
+ case nsIDataType::VTYPE_CSTRING:
+ case nsIDataType::VTYPE_CHAR_STR:
+ case nsIDataType::VTYPE_STRING_SIZE_IS:
+ case nsIDataType::VTYPE_UTF8STRING: {
+ nsCString value;
+ MOZ_ALWAYS_SUCCEEDS(aParam->GetAsACString(value));
+ variant.data() = value;
+ break;
+ }
+ case nsIDataType::VTYPE_INTERFACE:
+ case nsIDataType::VTYPE_INTERFACE_IS: {
+ nsIID* iid;
+ nsCOMPtr<nsISupports> value;
+ MOZ_ALWAYS_SUCCEEDS(aParam->GetAsInterface(&iid, getter_AddRefs(value)));
+ free(iid);
+ // We only accept nsIURI and nsIPrincipal interface types, patch welcome.
+ if (nsCOMPtr<nsIURI> uri = do_QueryInterface(value)) {
+ variant.data() = uri;
+ } else if (nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(value)) {
+ variant.data() = principal;
+ } else if (value) {
+ variant.type() = nsIDataType::VTYPE_EMPTY;
+ variant.data() = false; // because we need something.
+ } else {
+ // Let's pretend like we had a null URI, though how do we know
+ // it wasn't a null principal?
+ variant.data() = (nsIURI*)nullptr;
+ }
+ break;
+ }
+ case nsIDataType::VTYPE_VOID:
+ case nsIDataType::VTYPE_EMPTY:
+ variant.data() = false; // because we need something.
+ break;
+ default:
+ MOZ_CRASH("Non handled variant type, patch welcome");
+ break;
+ }
+ WriteIPDLParam(aWriter, aActor, variant);
+}
+
+bool IPDLParamTraits<nsIVariant*>::Read(MessageReader* aReader,
+ IProtocol* aActor,
+ RefPtr<nsIVariant>* aResult) {
+ IDPLVariant value;
+ if (!ReadIPDLParam(aReader, aActor, &value)) {
+ return false;
+ }
+
+ auto variant = MakeRefPtr<nsVariant>();
+
+ switch (value.type()) {
+ case nsIDataType::VTYPE_INT8:
+ case nsIDataType::VTYPE_UINT8:
+ if (value.type() == nsIDataType::VTYPE_INT8) {
+ variant->SetAsInt8(value.data().get_uint8_t());
+ } else {
+ variant->SetAsUint8(value.data().get_uint8_t());
+ }
+ break;
+ case nsIDataType::VTYPE_INT16:
+ variant->SetAsInt16(value.data().get_int16_t());
+ break;
+ case nsIDataType::VTYPE_INT32:
+ variant->SetAsInt32(value.data().get_int32_t());
+ break;
+ case nsIDataType::VTYPE_UINT16:
+ variant->SetAsUint16(value.data().get_uint16_t());
+ break;
+ case nsIDataType::VTYPE_UINT32:
+ variant->SetAsUint32(value.data().get_uint32_t());
+ break;
+ case nsIDataType::VTYPE_FLOAT:
+ variant->SetAsFloat(value.data().get_float());
+ break;
+ case nsIDataType::VTYPE_DOUBLE:
+ variant->SetAsDouble(value.data().get_double());
+ break;
+ case nsIDataType::VTYPE_BOOL:
+ variant->SetAsBool(value.data().get_bool());
+ break;
+ case nsIDataType::VTYPE_CHAR:
+ variant->SetAsChar(value.data().get_uint8_t());
+ break;
+ case nsIDataType::VTYPE_WCHAR:
+ variant->SetAsWChar(value.data().get_int16_t());
+ break;
+ case nsIDataType::VTYPE_ID:
+ variant->SetAsID(value.data().get_nsID());
+ break;
+ case nsIDataType::VTYPE_ASTRING:
+ case nsIDataType::VTYPE_WCHAR_STR:
+ case nsIDataType::VTYPE_WSTRING_SIZE_IS:
+ variant->SetAsAString(value.data().get_nsString());
+ break;
+ case nsIDataType::VTYPE_CSTRING:
+ case nsIDataType::VTYPE_CHAR_STR:
+ case nsIDataType::VTYPE_STRING_SIZE_IS:
+ variant->SetAsACString(value.data().get_nsCString());
+ break;
+ case nsIDataType::VTYPE_UTF8STRING:
+ variant->SetAsAUTF8String(value.data().get_nsCString());
+ break;
+ case nsIDataType::VTYPE_INTERFACE:
+ case nsIDataType::VTYPE_INTERFACE_IS:
+ if (value.data().type() == IPDLVariantValue::TnsIURI) {
+ variant->SetAsISupports(value.data().get_nsIURI());
+ } else if (value.data().type() == IPDLVariantValue::TnsIPrincipal) {
+ variant->SetAsISupports(value.data().get_nsIPrincipal());
+ } else {
+ MOZ_CRASH("Unexpected interface type");
+ }
+ break;
+ case nsIDataType::VTYPE_VOID:
+ variant->SetAsVoid();
+ break;
+ case nsIDataType::VTYPE_EMPTY:
+ break;
+ default:
+ MOZ_CRASH("Non handled variant type, patch welcome");
+ return false;
+ }
+ *aResult = std::move(variant);
+ return true;
+}
+
+void IPDLParamTraits<nsIPropertyBag2*>::Write(MessageWriter* aWriter,
+ IProtocol* aActor,
+ nsIPropertyBag2* aParam) {
+ // We send a nsIPropertyBag as an array of IPDLProperty
+ nsTArray<IPDLProperty> bag;
+
+ nsCOMPtr<nsISimpleEnumerator> enumerator;
+ if (aParam &&
+ NS_SUCCEEDED(aParam->GetEnumerator(getter_AddRefs(enumerator)))) {
+ for (auto& property : SimpleEnumerator<nsIProperty>(enumerator)) {
+ nsString name;
+ nsCOMPtr<nsIVariant> value;
+ MOZ_ALWAYS_SUCCEEDS(property->GetName(name));
+ MOZ_ALWAYS_SUCCEEDS(property->GetValue(getter_AddRefs(value)));
+ bag.AppendElement(IPDLProperty{name, value});
+ }
+ }
+ WriteIPDLParam(aWriter, aActor, bag);
+}
+
+bool IPDLParamTraits<nsIPropertyBag2*>::Read(MessageReader* aReader,
+ IProtocol* aActor,
+ RefPtr<nsIPropertyBag2>* aResult) {
+ nsTArray<IPDLProperty> bag;
+ if (!ReadIPDLParam(aReader, aActor, &bag)) {
+ return false;
+ }
+
+ auto properties = MakeRefPtr<nsHashPropertyBag>();
+
+ for (auto& entry : bag) {
+ nsCOMPtr<nsIVariant> variant = std::move(entry.value());
+ MOZ_ALWAYS_SUCCEEDS(
+ properties->SetProperty(std::move(entry.name()), variant));
+ }
+ *aResult = std::move(properties);
+ return true;
+}
+
+} // namespace mozilla::ipc
diff --git a/dom/ipc/PropertyBagUtils.h b/dom/ipc/PropertyBagUtils.h
new file mode 100644
index 0000000000..7eb94b46a7
--- /dev/null
+++ b/dom/ipc/PropertyBagUtils.h
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef IPC_PropertyBagUtils_h
+#define IPC_PropertyBagUtils_h
+
+#include "mozilla/ipc/IPDLParamTraits.h"
+#include "nsIPropertyBag2.h"
+#include "nsIVariant.h"
+
+namespace mozilla::ipc {
+
+/**
+ * Limited nsIVariant support. Not all types are implemented and only
+ * nsIURI is implemented with nsIVariant::GetAsInterface.
+ */
+template <>
+struct IPDLParamTraits<nsIVariant*> {
+ static void Write(IPC::MessageWriter* aWriter, IProtocol* aActor,
+ nsIVariant* aParam);
+ static bool Read(IPC::MessageReader* aReader, IProtocol* aActor,
+ RefPtr<nsIVariant>* aResult);
+};
+
+template <>
+struct IPDLParamTraits<nsIPropertyBag2*> {
+ static void Write(IPC::MessageWriter* aWriter, IProtocol* aActor,
+ nsIPropertyBag2* aParam);
+ static bool Read(IPC::MessageReader* aReader, IProtocol* aActor,
+ RefPtr<nsIPropertyBag2>* aResult);
+};
+
+} // namespace mozilla::ipc
+
+#endif // mozilla_ipc_URIUtils_h
diff --git a/dom/ipc/RefMessageBodyService.cpp b/dom/ipc/RefMessageBodyService.cpp
new file mode 100644
index 0000000000..e340443a53
--- /dev/null
+++ b/dom/ipc/RefMessageBodyService.cpp
@@ -0,0 +1,161 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "RefMessageBodyService.h"
+
+#include <cstdint>
+#include <cstdlib>
+#include "mozilla/ErrorResult.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/dom/ipc/StructuredCloneData.h"
+#include "nsBaseHashtable.h"
+#include "nsDebug.h"
+
+namespace mozilla::dom {
+
+// Guards sService and its members.
+StaticMutex sRefMessageBodyServiceMutex;
+
+// Raw pointer because the service is kept alive by other objects.
+// See the CTOR and the DTOR of this object.
+RefMessageBodyService* sService;
+
+// static
+already_AddRefed<RefMessageBodyService> RefMessageBodyService::GetOrCreate() {
+ StaticMutexAutoLock lock(sRefMessageBodyServiceMutex);
+
+ RefPtr<RefMessageBodyService> service = GetOrCreateInternal(lock);
+ return service.forget();
+}
+
+// static
+RefMessageBodyService* RefMessageBodyService::GetOrCreateInternal(
+ const StaticMutexAutoLock& aProofOfLock) {
+ if (!sService) {
+ sService = new RefMessageBodyService(aProofOfLock);
+ }
+ return sService;
+}
+
+RefMessageBodyService::RefMessageBodyService(
+ const StaticMutexAutoLock& aProofOfLock) {
+ MOZ_DIAGNOSTIC_ASSERT(sService == nullptr);
+}
+
+RefMessageBodyService::~RefMessageBodyService() {
+ StaticMutexAutoLock lock(sRefMessageBodyServiceMutex);
+ MOZ_DIAGNOSTIC_ASSERT(sService == this);
+ sService = nullptr;
+}
+
+const nsID RefMessageBodyService::Register(
+ already_AddRefed<RefMessageBody> aBody, ErrorResult& aRv) {
+ RefPtr<RefMessageBody> body = aBody;
+ MOZ_ASSERT(body);
+
+ nsID uuid = {};
+ aRv = nsID::GenerateUUIDInPlace(uuid);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nsID();
+ }
+
+ StaticMutexAutoLock lock(sRefMessageBodyServiceMutex);
+ GetOrCreateInternal(lock)->mMessages.InsertOrUpdate(uuid, std::move(body));
+ return uuid;
+}
+
+already_AddRefed<RefMessageBody> RefMessageBodyService::Steal(const nsID& aID) {
+ StaticMutexAutoLock lock(sRefMessageBodyServiceMutex);
+ if (!sService) {
+ return nullptr;
+ }
+
+ RefPtr<RefMessageBody> body;
+ sService->mMessages.Remove(aID, getter_AddRefs(body));
+
+ return body.forget();
+}
+
+already_AddRefed<RefMessageBody> RefMessageBodyService::GetAndCount(
+ const nsID& aID) {
+ StaticMutexAutoLock lock(sRefMessageBodyServiceMutex);
+ if (!sService) {
+ return nullptr;
+ }
+
+ RefPtr<RefMessageBody> body = sService->mMessages.Get(aID);
+ if (!body) {
+ return nullptr;
+ }
+
+ ++body->mCount;
+
+ MOZ_ASSERT_IF(body->mMaxCount.isSome(),
+ body->mCount <= body->mMaxCount.value());
+ if (body->mMaxCount.isSome() && body->mCount >= body->mMaxCount.value()) {
+ sService->mMessages.Remove(aID);
+ }
+
+ return body.forget();
+}
+
+void RefMessageBodyService::SetMaxCount(const nsID& aID, uint32_t aMaxCount) {
+ StaticMutexAutoLock lock(sRefMessageBodyServiceMutex);
+ if (!sService) {
+ return;
+ }
+
+ RefPtr<RefMessageBody> body = sService->mMessages.Get(aID);
+ if (!body) {
+ return;
+ }
+
+ MOZ_ASSERT(body->mMaxCount.isNothing());
+ body->mMaxCount.emplace(aMaxCount);
+
+ MOZ_ASSERT(body->mCount <= body->mMaxCount.value());
+ if (body->mCount >= body->mMaxCount.value()) {
+ sService->mMessages.Remove(aID);
+ }
+}
+
+void RefMessageBodyService::ForgetPort(const nsID& aPortID) {
+ StaticMutexAutoLock lock(sRefMessageBodyServiceMutex);
+ if (!sService) {
+ return;
+ }
+
+ for (auto iter = sService->mMessages.Iter(); !iter.Done(); iter.Next()) {
+ if (iter.UserData()->PortID() == aPortID) {
+ iter.Remove();
+ }
+ }
+}
+
+RefMessageBody::RefMessageBody(const nsID& aPortID,
+ UniquePtr<ipc::StructuredCloneData>&& aCloneData)
+ : mPortID(aPortID),
+ mMutex("RefMessageBody::mMutex"),
+ mCloneData(std::move(aCloneData)),
+ mMaxCount(Nothing()),
+ mCount(0) {}
+
+RefMessageBody::~RefMessageBody() = default;
+
+void RefMessageBody::Read(JSContext* aCx, JS::MutableHandle<JS::Value> aValue,
+ const JS::CloneDataPolicy& aCloneDataPolicy,
+ ErrorResult& aRv) {
+ MutexAutoLock lock(mMutex);
+ mCloneData->Read(aCx, aValue, aCloneDataPolicy, aRv);
+}
+
+bool RefMessageBody::TakeTransferredPortsAsSequence(
+ Sequence<OwningNonNull<mozilla::dom::MessagePort>>& aPorts) {
+ MOZ_ASSERT(mMaxCount.isNothing());
+ return mCloneData->TakeTransferredPortsAsSequence(aPorts);
+}
+
+} // namespace mozilla::dom
diff --git a/dom/ipc/RefMessageBodyService.h b/dom/ipc/RefMessageBodyService.h
new file mode 100644
index 0000000000..6cca2a836e
--- /dev/null
+++ b/dom/ipc/RefMessageBodyService.h
@@ -0,0 +1,137 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_RefMessageBodyService_h
+#define mozilla_dom_RefMessageBodyService_h
+
+#include <cstdint>
+#include "js/TypeDecls.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/StaticMutex.h"
+#include "mozilla/UniquePtr.h"
+#include "nsHashKeys.h"
+#include "nsID.h"
+#include "nsISupports.h"
+#include "nsRefPtrHashtable.h"
+
+namespace JS {
+class CloneDataPolicy;
+} // namespace JS
+
+namespace mozilla {
+
+class ErrorResult;
+template <class T>
+class OwningNonNull;
+
+namespace dom {
+
+class MessagePort;
+template <typename T>
+class Sequence;
+
+namespace ipc {
+class StructuredCloneData;
+}
+
+/**
+ * At the time a BroadcastChannel or MessagePort sends messages, we don't know
+ * which process is going to receive it. Because of this, we need to check if
+ * the message is able to cross the process boundary.
+ * If the message contains objects such as SharedArrayBuffers, WASM modules or
+ * ImageBitmaps, it can be delivered on the current process only.
+ * Instead of sending the whole message via IPC, we send a unique ID, while the
+ * message is kept alive by RefMessageBodyService, on the current process using
+ * a ref-counted RefMessageBody object.
+ * When the receiver obtains the message ID, it checks if the local
+ * RefMessageBodyService knows that ID. If yes, the sender and the receiver are
+ * on the same process and the delivery can be completed; if not, a
+ * messageerror event has to be dispatched instead.
+ *
+ * For MessagePort communication is 1-to-1 and because of this, the
+ * receiver takes ownership of the message (RefMessageBodyService::Steal()).
+ * If the receiver port is on a different process, RefMessageBodyService will
+ * return a nullptr and a messageerror event will be dispatched.
+
+ * For BroadcastChannel, the life-time of a message is a bit different than for
+ * MessagePort. It has a 1-to-many communication and we could have multiple
+ * receivers. Each one needs to deliver the message without taking full
+ * ownership of it.
+ * In order to support this feature, BroadcastChannel needs to call
+ * RefMessageBodyService::SetMaxCount() to inform how many ports are allowed to
+ * retrieve the current message, on the current process. Receivers on other
+ * processes are not kept in consideration because they will not be able to
+ * retrieve the message from RefMessageBodyService. When the last allowed
+ * port has called RefMessageBodyService::GetAndCount(), the message is
+ * released.
+ */
+class RefMessageBody final {
+ friend class RefMessageBodyService;
+
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RefMessageBody)
+
+ RefMessageBody(const nsID& aPortID,
+ UniquePtr<ipc::StructuredCloneData>&& aCloneData);
+
+ const nsID& PortID() const { return mPortID; }
+
+ void Read(JSContext* aCx, JS::MutableHandle<JS::Value> aValue,
+ const JS::CloneDataPolicy& aCloneDataPolicy, ErrorResult& aRv);
+
+ // This method can be called only if the RefMessageBody is not supposed to be
+ // ref-counted (see mMaxCount).
+ bool TakeTransferredPortsAsSequence(
+ Sequence<OwningNonNull<mozilla::dom::MessagePort>>& aPorts);
+
+ private:
+ ~RefMessageBody();
+
+ const nsID mPortID;
+
+ // In case the RefMessageBody is shared and refcounted (see mCount/mMaxCount),
+ // we must enforce that the reading does not happen simultaneously on
+ // different threads.
+ Mutex mMutex MOZ_UNANNOTATED;
+
+ UniquePtr<ipc::StructuredCloneData> mCloneData;
+
+ // When mCount reaches mMaxCount, this object is released by the service.
+ Maybe<uint32_t> mMaxCount;
+ uint32_t mCount;
+};
+
+class RefMessageBodyService final {
+ public:
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RefMessageBodyService)
+
+ static already_AddRefed<RefMessageBodyService> GetOrCreate();
+
+ void ForgetPort(const nsID& aPortID);
+
+ const nsID Register(already_AddRefed<RefMessageBody> aBody, ErrorResult& aRv);
+
+ already_AddRefed<RefMessageBody> Steal(const nsID& aID);
+
+ already_AddRefed<RefMessageBody> GetAndCount(const nsID& aID);
+
+ void SetMaxCount(const nsID& aID, uint32_t aMaxCount);
+
+ private:
+ explicit RefMessageBodyService(const StaticMutexAutoLock& aProofOfLock);
+ ~RefMessageBodyService();
+
+ static RefMessageBodyService* GetOrCreateInternal(
+ const StaticMutexAutoLock& aProofOfLock);
+
+ nsRefPtrHashtable<nsIDHashKey, RefMessageBody> mMessages;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_RefMessageBodyService_h
diff --git a/dom/ipc/ReferrerInfoUtils.cpp b/dom/ipc/ReferrerInfoUtils.cpp
new file mode 100644
index 0000000000..a2728e342c
--- /dev/null
+++ b/dom/ipc/ReferrerInfoUtils.cpp
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/ReferrerInfoUtils.h"
+
+#include "ipc/IPCMessageUtilsSpecializations.h"
+#include "nsSerializationHelper.h"
+#include "nsString.h"
+
+namespace IPC {
+
+void ParamTraits<nsIReferrerInfo*>::Write(MessageWriter* aWriter,
+ nsIReferrerInfo* aParam) {
+ bool isNull = !aParam;
+ WriteParam(aWriter, isNull);
+ if (isNull) {
+ return;
+ }
+ nsAutoCString infoString;
+ nsresult rv = NS_SerializeToString(aParam, infoString);
+ if (NS_FAILED(rv)) {
+ MOZ_CRASH("Unable to serialize referrer info.");
+ return;
+ }
+ WriteParam(aWriter, infoString);
+}
+
+bool ParamTraits<nsIReferrerInfo*>::Read(MessageReader* aReader,
+ RefPtr<nsIReferrerInfo>* aResult) {
+ bool isNull;
+ if (!ReadParam(aReader, &isNull)) {
+ return false;
+ }
+ if (isNull) {
+ *aResult = nullptr;
+ return true;
+ }
+ nsAutoCString infoString;
+ if (!ReadParam(aReader, &infoString)) {
+ return false;
+ }
+ nsCOMPtr<nsISupports> iSupports;
+ nsresult rv = NS_DeserializeObject(infoString, getter_AddRefs(iSupports));
+ NS_ENSURE_SUCCESS(rv, false);
+ nsCOMPtr<nsIReferrerInfo> referrerInfo = do_QueryInterface(iSupports);
+ NS_ENSURE_TRUE(referrerInfo, false);
+ *aResult = ToRefPtr(std::move(referrerInfo));
+ return true;
+}
+
+} // namespace IPC
diff --git a/dom/ipc/ReferrerInfoUtils.h b/dom/ipc/ReferrerInfoUtils.h
new file mode 100644
index 0000000000..9a17f9d148
--- /dev/null
+++ b/dom/ipc/ReferrerInfoUtils.h
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_referrer_info_utils_h__
+#define mozilla_dom_referrer_info_utils_h__
+
+#include "ipc/IPCMessageUtils.h"
+#include "nsCOMPtr.h"
+#include "nsIReferrerInfo.h"
+
+namespace IPC {
+
+template <>
+struct ParamTraits<nsIReferrerInfo*> {
+ static void Write(MessageWriter* aWriter, nsIReferrerInfo* aParam);
+ static bool Read(MessageReader* aReader, RefPtr<nsIReferrerInfo>* aResult);
+};
+
+} // namespace IPC
+
+#endif // mozilla_dom_referrer_info_utils_h__
diff --git a/dom/ipc/RemoteBrowser.cpp b/dom/ipc/RemoteBrowser.cpp
new file mode 100644
index 0000000000..9fabb8e67f
--- /dev/null
+++ b/dom/ipc/RemoteBrowser.cpp
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "RemoteBrowser.h"
+
+#include "nsFrameLoader.h"
+#include "nsFrameLoaderOwner.h"
+#include "nsQueryObject.h"
+
+namespace mozilla::dom {
+
+RemoteBrowser* RemoteBrowser::GetFrom(nsFrameLoader* aFrameLoader) {
+ if (!aFrameLoader) {
+ return nullptr;
+ }
+ return aFrameLoader->GetRemoteBrowser();
+}
+
+RemoteBrowser* RemoteBrowser::GetFrom(nsIContent* aContent) {
+ RefPtr<nsFrameLoaderOwner> loaderOwner = do_QueryObject(aContent);
+ if (!loaderOwner) {
+ return nullptr;
+ }
+ RefPtr<nsFrameLoader> frameLoader = loaderOwner->GetFrameLoader();
+ return GetFrom(frameLoader);
+}
+
+} // namespace mozilla::dom
diff --git a/dom/ipc/RemoteBrowser.h b/dom/ipc/RemoteBrowser.h
new file mode 100644
index 0000000000..7ea0b669fd
--- /dev/null
+++ b/dom/ipc/RemoteBrowser.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_ipc_RemoteBrowser_h
+#define mozilla_dom_ipc_RemoteBrowser_h
+
+#include "mozilla/dom/ipc/IdType.h"
+#include "mozilla/layers/LayersTypes.h"
+#include "nsISupports.h"
+#include "nsRect.h"
+#include "Units.h"
+
+class nsDocShellLoadState;
+class nsFrameLoader;
+class nsILoadContext;
+class nsIContent;
+
+namespace mozilla::dom {
+
+class BrowserHost;
+class BrowserBridgeHost;
+class BrowsingContext;
+class EffectsInfo;
+class OwnerShowInfo;
+
+/**
+ * An interface to control a browser hosted in another process.
+ *
+ * This is used by nsFrameLoader to abstract between hosting a top-level remote
+ * browser in the chrome process and hosting an OOP-iframe in a content process.
+ *
+ * There are two concrete implementations that are used depending on whether
+ * the nsFrameLoader is in the chrome or content process. A chrome process
+ * nsFrameLoader will use BrowserHost, and a content process nsFrameLoader will
+ * use BrowserBridgeHost.
+ */
+class RemoteBrowser : public nsISupports {
+ public:
+ using LayersId = mozilla::layers::LayersId;
+
+ static RemoteBrowser* GetFrom(nsFrameLoader* aFrameLoader);
+ static RemoteBrowser* GetFrom(nsIContent* aContent);
+
+ // Try to cast this RemoteBrowser to a BrowserHost, may return null
+ virtual BrowserHost* AsBrowserHost() = 0;
+ // Try to cast this RemoteBrowser to a BrowserBridgeHost, may return null
+ virtual BrowserBridgeHost* AsBrowserBridgeHost() = 0;
+
+ virtual TabId GetTabId() const = 0;
+ virtual LayersId GetLayersId() const = 0;
+ virtual BrowsingContext* GetBrowsingContext() const = 0;
+ virtual nsILoadContext* GetLoadContext() const = 0;
+ virtual bool CanRecv() const = 0;
+
+ virtual void LoadURL(nsDocShellLoadState* aLoadState) = 0;
+ virtual void ResumeLoad(uint64_t aPendingSwitchId) = 0;
+ virtual void DestroyStart() = 0;
+ virtual void DestroyComplete() = 0;
+
+ virtual bool Show(const OwnerShowInfo&) = 0;
+ virtual void UpdateDimensions(const nsIntRect& aRect,
+ const ScreenIntSize& aSize) = 0;
+
+ virtual void UpdateEffects(EffectsInfo aInfo) = 0;
+};
+
+} // namespace mozilla::dom
+
+#endif // mozilla_dom_ipc_RemoteBrowser_h
diff --git a/dom/ipc/RemoteType.h b/dom/ipc/RemoteType.h
new file mode 100644
index 0000000000..43183c7dee
--- /dev/null
+++ b/dom/ipc/RemoteType.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_RemoteType_h
+#define mozilla_dom_RemoteType_h
+
+#include "nsString.h"
+#include "nsReadableUtils.h"
+
+// These must match the similar ones in E10SUtils.sys.mjs and ProcInfo.h and
+// ChromeUtils.webidl Process names as reported by about:memory are defined in
+// ContentChild:RecvRemoteType. Add your value there too or it will be called
+// "Web Content".
+#define PREALLOC_REMOTE_TYPE "prealloc"_ns
+#define WEB_REMOTE_TYPE "web"_ns
+#define FILE_REMOTE_TYPE "file"_ns
+#define EXTENSION_REMOTE_TYPE "extension"_ns
+#define PRIVILEGEDABOUT_REMOTE_TYPE "privilegedabout"_ns
+#define PRIVILEGEDMOZILLA_REMOTE_TYPE "privilegedmozilla"_ns
+
+#define DEFAULT_REMOTE_TYPE WEB_REMOTE_TYPE
+
+// These must start with the WEB_REMOTE_TYPE above.
+#define FISSION_WEB_REMOTE_TYPE "webIsolated"_ns
+#define WITH_COOP_COEP_REMOTE_TYPE "webCOOP+COEP"_ns
+#define WITH_COOP_COEP_REMOTE_TYPE_PREFIX "webCOOP+COEP="_ns
+#define SERVICEWORKER_REMOTE_TYPE "webServiceWorker"_ns
+
+// Remote type value used to represent being non-remote.
+#define NOT_REMOTE_TYPE VoidCString()
+
+#endif // mozilla_dom_RemoteType_h
diff --git a/dom/ipc/RemoteWebProgressRequest.cpp b/dom/ipc/RemoteWebProgressRequest.cpp
new file mode 100644
index 0000000000..72c80b3e6b
--- /dev/null
+++ b/dom/ipc/RemoteWebProgressRequest.cpp
@@ -0,0 +1,270 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public *
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "RemoteWebProgressRequest.h"
+
+#include "nsIURI.h"
+
+namespace mozilla::dom {
+
+NS_IMPL_ISUPPORTS(RemoteWebProgressRequest, nsIRequest, nsIChannel,
+ nsIClassifiedChannel)
+
+// nsIChannel methods
+
+NS_IMETHODIMP RemoteWebProgressRequest::GetOriginalURI(nsIURI** aOriginalURI) {
+ NS_ENSURE_ARG_POINTER(aOriginalURI);
+ NS_ADDREF(*aOriginalURI = mOriginalURI);
+ return NS_OK;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::SetOriginalURI(nsIURI* aOriginalURI) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::GetURI(nsIURI** aURI) {
+ NS_ENSURE_ARG_POINTER(aURI);
+ NS_ADDREF(*aURI = mURI);
+ return NS_OK;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::GetOwner(nsISupports** aOwner) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::SetOwner(nsISupports* aOwner) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::GetNotificationCallbacks(
+ nsIInterfaceRequestor** aNotificationCallbacks) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::SetNotificationCallbacks(
+ nsIInterfaceRequestor* aNotificationCallbacks) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::GetSecurityInfo(
+ nsITransportSecurityInfo** aSecurityInfo) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::GetContentType(
+ nsACString& aContentType) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::SetContentType(
+ const nsACString& aContentType) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::GetContentCharset(
+ nsACString& aContentCharset) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::SetContentCharset(
+ const nsACString& aContentCharset) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::GetContentLength(
+ int64_t* aContentLength) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::SetContentLength(
+ int64_t aContentLength) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::Open(nsIInputStream** _retval) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::AsyncOpen(
+ nsIStreamListener* aListener) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::GetContentDisposition(
+ uint32_t* aContentDisposition) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::SetContentDisposition(
+ uint32_t aContentDisposition) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::GetContentDispositionFilename(
+ nsAString& aContentDispositionFilename) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::SetContentDispositionFilename(
+ const nsAString& aContentDispositionFilename) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::GetContentDispositionHeader(
+ nsACString& aContentDispositionHeader) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::GetLoadInfo(nsILoadInfo** aLoadInfo) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::SetLoadInfo(nsILoadInfo* aLoadInfo) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::GetIsDocument(bool* aIsDocument) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+// nsIClassifiedChannel methods
+
+NS_IMETHODIMP RemoteWebProgressRequest::SetMatchedInfo(
+ const nsACString& aList, const nsACString& aProvider,
+ const nsACString& aFullHash) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::GetMatchedList(
+ nsACString& aMatchedList) {
+ aMatchedList = mMatchedList;
+ return NS_OK;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::GetMatchedProvider(
+ nsACString& aMatchedProvider) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::GetMatchedFullHash(
+ nsACString& aMatchedFullHash) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::SetMatchedTrackingInfo(
+ const nsTArray<nsCString>& aLists, const nsTArray<nsCString>& aFullHashes) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::GetMatchedTrackingLists(
+ nsTArray<nsCString>& aLists) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::GetMatchedTrackingFullHashes(
+ nsTArray<nsCString>& aFullHashes) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+// nsIRequest methods
+
+NS_IMETHODIMP RemoteWebProgressRequest::GetName(nsACString& aName) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::IsPending(bool* _retval) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::GetStatus(nsresult* aStatus) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::SetCanceledReason(
+ const nsACString& aReason) {
+ return SetCanceledReasonImpl(aReason);
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::GetCanceledReason(nsACString& aReason) {
+ return GetCanceledReasonImpl(aReason);
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::CancelWithReason(
+ nsresult aStatus, const nsACString& aReason) {
+ return CancelWithReasonImpl(aStatus, aReason);
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::Cancel(nsresult aStatus) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::GetCanceled(bool* aCanceled) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::Suspend(void) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::Resume(void) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::GetLoadGroup(
+ nsILoadGroup** aLoadGroup) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::SetLoadGroup(nsILoadGroup* aLoadGroup) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::GetLoadFlags(nsLoadFlags* aLoadFlags) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::GetTRRMode(
+ nsIRequest::TRRMode* aTRRMode) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::SetTRRMode(
+ nsIRequest::TRRMode aTRRMode) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgressRequest::SetLoadFlags(nsLoadFlags aLoadFlags) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteWebProgressRequest::IsThirdPartyTrackingResource(
+ bool* aIsTrackingResource) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteWebProgressRequest::IsThirdPartySocialTrackingResource(
+ bool* aIsThirdPartySocialTrackingResource) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteWebProgressRequest::GetClassificationFlags(
+ uint32_t* aClassificationFlags) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteWebProgressRequest::GetFirstPartyClassificationFlags(
+ uint32_t* aClassificationFlags) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RemoteWebProgressRequest::GetThirdPartyClassificationFlags(
+ uint32_t* aClassificationFlags) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+} // namespace mozilla::dom
diff --git a/dom/ipc/RemoteWebProgressRequest.h b/dom/ipc/RemoteWebProgressRequest.h
new file mode 100644
index 0000000000..cef1cb76f6
--- /dev/null
+++ b/dom/ipc/RemoteWebProgressRequest.h
@@ -0,0 +1,36 @@
+/* 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/. */
+
+#ifndef mozilla_dom_RemoteWebProgressRequest_h
+#define mozilla_dom_RemoteWebProgressRequest_h
+
+#include "nsIChannel.h"
+#include "nsIClassifiedChannel.h"
+
+namespace mozilla::dom {
+
+class RemoteWebProgressRequest final : public nsIChannel,
+ public nsIClassifiedChannel {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSICHANNEL
+ NS_DECL_NSICLASSIFIEDCHANNEL
+ NS_DECL_NSIREQUEST
+
+ RemoteWebProgressRequest(nsIURI* aURI, nsIURI* aOriginalURI,
+ const nsACString& aMatchedList)
+ : mURI(aURI), mOriginalURI(aOriginalURI), mMatchedList(aMatchedList) {}
+
+ protected:
+ ~RemoteWebProgressRequest() = default;
+
+ private:
+ nsCOMPtr<nsIURI> mURI;
+ nsCOMPtr<nsIURI> mOriginalURI;
+ nsCString mMatchedList;
+};
+
+} // namespace mozilla::dom
+
+#endif // mozilla_dom_RemoteWebProgressRequest_h
diff --git a/dom/ipc/ServiceWorkerConfiguration.ipdlh b/dom/ipc/ServiceWorkerConfiguration.ipdlh
new file mode 100644
index 0000000000..16e0d46eab
--- /dev/null
+++ b/dom/ipc/ServiceWorkerConfiguration.ipdlh
@@ -0,0 +1,18 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include ServiceWorkerRegistrarTypes;
+
+namespace mozilla {
+namespace dom {
+
+struct ServiceWorkerConfiguration
+{
+ ServiceWorkerRegistrationData[] serviceWorkerRegistrations;
+};
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/SharedMap.cpp b/dom/ipc/SharedMap.cpp
new file mode 100644
index 0000000000..dc6602d377
--- /dev/null
+++ b/dom/ipc/SharedMap.cpp
@@ -0,0 +1,467 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SharedMap.h"
+#include "SharedMapChangeEvent.h"
+
+#include "MemMapSnapshot.h"
+#include "ScriptPreloader-inl.h"
+
+#include "mozilla/dom/AutoEntryScript.h"
+#include "mozilla/dom/BlobImpl.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/ContentProcessMessageManager.h"
+#include "mozilla/dom/IPCBlobUtils.h"
+#include "mozilla/dom/RootedDictionary.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/IOBuffers.h"
+#include "mozilla/ScriptPreloader.h"
+#include "mozilla/Try.h"
+
+using namespace mozilla::loader;
+
+namespace mozilla {
+
+using namespace ipc;
+
+namespace dom::ipc {
+
+// Align to size of uintptr_t here, to be safe. It's probably not strictly
+// necessary, though.
+constexpr size_t kStructuredCloneAlign = sizeof(uintptr_t);
+
+static inline void AlignTo(size_t* aOffset, size_t aAlign) {
+ if (auto mod = *aOffset % aAlign) {
+ *aOffset += aAlign - mod;
+ }
+}
+
+SharedMap::SharedMap() = default;
+
+SharedMap::SharedMap(nsIGlobalObject* aGlobal, const FileDescriptor& aMapFile,
+ size_t aMapSize, nsTArray<RefPtr<BlobImpl>>&& aBlobs)
+ : DOMEventTargetHelper(aGlobal), mBlobImpls(std::move(aBlobs)) {
+ mMapFile.reset(new FileDescriptor(aMapFile));
+ mMapSize = aMapSize;
+}
+
+bool SharedMap::Has(const nsACString& aName) {
+ Unused << MaybeRebuild();
+ return mEntries.Contains(aName);
+}
+
+void SharedMap::Get(JSContext* aCx, const nsACString& aName,
+ JS::MutableHandle<JS::Value> aRetVal, ErrorResult& aRv) {
+ auto res = MaybeRebuild();
+ if (res.isErr()) {
+ aRv.Throw(res.unwrapErr());
+ return;
+ }
+
+ Entry* entry = mEntries.Get(aName);
+ if (!entry) {
+ aRetVal.setNull();
+ return;
+ }
+
+ entry->Read(aCx, aRetVal, aRv);
+}
+
+void SharedMap::Entry::Read(JSContext* aCx,
+ JS::MutableHandle<JS::Value> aRetVal,
+ ErrorResult& aRv) {
+ if (mData.is<StructuredCloneData>()) {
+ // We have a temporary buffer for a key that was changed after the last
+ // snapshot. Just decode it directly.
+ auto& holder = mData.as<StructuredCloneData>();
+ holder.Read(aCx, aRetVal, aRv);
+ return;
+ }
+
+ // We have a pointer to a shared memory region containing our structured
+ // clone data. Create a temporary buffer to decode that data, and then
+ // discard it so that we don't keep a separate process-local copy around any
+ // longer than necessary.
+ StructuredCloneData holder;
+ if (!holder.CopyExternalData(Data(), Size())) {
+ aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+ return;
+ }
+ if (mBlobCount) {
+ holder.BlobImpls().AppendElements(Blobs());
+ }
+ holder.Read(aCx, aRetVal, aRv);
+}
+
+FileDescriptor SharedMap::CloneMapFile() const {
+ if (mMap.initialized()) {
+ return mMap.cloneHandle();
+ }
+ return *mMapFile;
+}
+
+void SharedMap::Update(const FileDescriptor& aMapFile, size_t aMapSize,
+ nsTArray<RefPtr<BlobImpl>>&& aBlobs,
+ nsTArray<nsCString>&& aChangedKeys) {
+ MOZ_DIAGNOSTIC_ASSERT(!mWritable);
+
+ mMap.reset();
+ if (mMapFile) {
+ *mMapFile = aMapFile;
+ } else {
+ mMapFile.reset(new FileDescriptor(aMapFile));
+ }
+ mMapSize = aMapSize;
+ mEntries.Clear();
+ mEntryArray.reset();
+
+ mBlobImpls = std::move(aBlobs);
+
+ AutoEntryScript aes(GetParentObject(), "SharedMap change event");
+ JSContext* cx = aes.cx();
+
+ RootedDictionary<MozSharedMapChangeEventInit> init(cx);
+ if (!init.mChangedKeys.SetCapacity(aChangedKeys.Length(), fallible)) {
+ NS_WARNING("Failed to dispatch SharedMap change event");
+ return;
+ }
+ for (auto& key : aChangedKeys) {
+ Unused << init.mChangedKeys.AppendElement(NS_ConvertUTF8toUTF16(key),
+ fallible);
+ }
+
+ RefPtr<SharedMapChangeEvent> event =
+ SharedMapChangeEvent::Constructor(this, u"change"_ns, init);
+ event->SetTrusted(true);
+
+ DispatchEvent(*event);
+}
+
+const nsTArray<SharedMap::Entry*>& SharedMap::EntryArray() const {
+ if (mEntryArray.isNothing()) {
+ MaybeRebuild();
+
+ mEntryArray.emplace(mEntries.Count());
+ auto& array = mEntryArray.ref();
+ for (auto& entry : mEntries) {
+ array.AppendElement(entry.GetWeak());
+ }
+ }
+
+ return mEntryArray.ref();
+}
+
+const nsString SharedMap::GetKeyAtIndex(uint32_t aIndex) const {
+ return NS_ConvertUTF8toUTF16(EntryArray()[aIndex]->Name());
+}
+
+bool SharedMap::GetValueAtIndex(JSContext* aCx, uint32_t aIndex,
+ JS::MutableHandle<JS::Value> aResult) const {
+ ErrorResult rv;
+ EntryArray()[aIndex]->Read(aCx, aResult, rv);
+ if (rv.MaybeSetPendingException(aCx)) {
+ return false;
+ }
+ return true;
+}
+
+void SharedMap::Entry::TakeData(StructuredCloneData&& aHolder) {
+ mData = AsVariant(std::move(aHolder));
+
+ mSize = Holder().Data().Size();
+ mBlobCount = Holder().BlobImpls().Length();
+}
+
+void SharedMap::Entry::ExtractData(char* aDestPtr, uint32_t aNewOffset,
+ uint16_t aNewBlobOffset) {
+ if (mData.is<StructuredCloneData>()) {
+ char* ptr = aDestPtr;
+ Holder().Data().ForEachDataChunk([&](const char* aData, size_t aSize) {
+ memcpy(ptr, aData, aSize);
+ ptr += aSize;
+ return true;
+ });
+ MOZ_ASSERT(uint32_t(ptr - aDestPtr) == mSize);
+ } else {
+ memcpy(aDestPtr, Data(), mSize);
+ }
+
+ mData = AsVariant(aNewOffset);
+ mBlobOffset = aNewBlobOffset;
+}
+
+Result<Ok, nsresult> SharedMap::MaybeRebuild() {
+ if (!mMapFile) {
+ return Ok();
+ }
+
+ // This function maps a shared memory region created by Serialize() and reads
+ // its header block to build a new mEntries hashtable of its contents.
+ //
+ // The entries created by this function contain a pointer to this SharedMap
+ // instance, and the offsets and sizes of their structured clone data within
+ // its shared memory region. When needed, that structured clone data is
+ // retrieved directly as indexes into the SharedMap's shared memory region.
+
+ MOZ_TRY(mMap.initWithHandle(*mMapFile, mMapSize));
+ mMapFile.reset();
+
+ // We should be able to pass this range as an initializer list or an immediate
+ // param, but gcc currently chokes on that if optimization is enabled, and
+ // initializes everything to 0.
+ Range<uint8_t> range(&mMap.get<uint8_t>()[0], mMap.size());
+ InputBuffer buffer(range);
+
+ uint32_t count;
+ buffer.codeUint32(count);
+
+ for (uint32_t i = 0; i < count; i++) {
+ auto entry = MakeUnique<Entry>(*this);
+ entry->Code(buffer);
+
+ // This buffer was created at runtime, during this session, so any errors
+ // indicate memory corruption, and are fatal.
+ MOZ_RELEASE_ASSERT(!buffer.error());
+
+ // Note: While the order of evaluation of the arguments to Put doesn't
+ // matter for this (the actual move will only happen within Put), to be
+ // clear about this, we call entry->Name() before calling Put.
+ const auto& name = entry->Name();
+ mEntries.InsertOrUpdate(name, std::move(entry));
+ }
+
+ return Ok();
+}
+
+void SharedMap::MaybeRebuild() const {
+ Unused << const_cast<SharedMap*>(this)->MaybeRebuild();
+}
+
+WritableSharedMap::WritableSharedMap() {
+ mWritable = true;
+ // Serialize the initial empty contents of the map immediately so that we
+ // always have a file descriptor to send to callers of CloneMapFile().
+ Unused << Serialize();
+ MOZ_RELEASE_ASSERT(mMap.initialized());
+}
+
+SharedMap* WritableSharedMap::GetReadOnly() {
+ if (!mReadOnly) {
+ nsTArray<RefPtr<BlobImpl>> blobs(mBlobImpls.Clone());
+ mReadOnly =
+ new SharedMap(ContentProcessMessageManager::Get()->GetParentObject(),
+ CloneMapFile(), MapSize(), std::move(blobs));
+ }
+ return mReadOnly;
+}
+
+Result<Ok, nsresult> WritableSharedMap::Serialize() {
+ // Serializes a new snapshot of the map, initializes a new read-only shared
+ // memory region with its contents, and updates all entries to point to that
+ // new snapshot.
+ //
+ // The layout of the snapshot is as follows:
+ //
+ // - A header containing a uint32 count field containing the number of
+ // entries in the map, followed by that number of serialized entry headers,
+ // as produced by Entry::Code.
+ //
+ // - A data block containing structured clone data for each of the entries'
+ // values. This data is referenced by absolute byte offsets from the start
+ // of the shared memory region, encoded in each of the entry header values.
+ // Each entry's data is aligned to kStructuredCloneAlign, and therefore may
+ // have alignment padding before it.
+ //
+ // This serialization format is decoded by the MaybeRebuild() method of
+ // read-only SharedMap() instances, and used to populate their mEntries
+ // hashtables.
+ //
+ // Writable instances never read the header blocks, but instead directly
+ // update their Entry instances to point to the appropriate offsets in the
+ // shared memory region created by this function.
+
+ uint32_t count = mEntries.Count();
+
+ size_t dataSize = 0;
+ size_t headerSize = sizeof(count);
+ size_t blobCount = 0;
+
+ for (const auto& entry : mEntries.Values()) {
+ headerSize += entry->HeaderSize();
+ blobCount += entry->BlobCount();
+
+ dataSize += entry->Size();
+ AlignTo(&dataSize, kStructuredCloneAlign);
+ }
+
+ size_t offset = headerSize;
+ AlignTo(&offset, kStructuredCloneAlign);
+
+ OutputBuffer header;
+ header.codeUint32(count);
+
+ MemMapSnapshot mem;
+ MOZ_TRY(mem.Init(offset + dataSize));
+
+ auto ptr = mem.Get<char>();
+
+ // We need to build the new array of blobs before we overwrite the existing
+ // one, since previously-serialized entries will store their blob references
+ // as indexes into our blobs array.
+ nsTArray<RefPtr<BlobImpl>> blobImpls(blobCount);
+
+ for (const auto& entry : mEntries.Values()) {
+ AlignTo(&offset, kStructuredCloneAlign);
+
+ size_t blobOffset = blobImpls.Length();
+ if (entry->BlobCount()) {
+ blobImpls.AppendElements(entry->Blobs());
+ }
+
+ entry->ExtractData(&ptr[offset], offset, blobOffset);
+ entry->Code(header);
+
+ offset += entry->Size();
+ }
+
+ mBlobImpls = std::move(blobImpls);
+
+ // FIXME: We should create a separate OutputBuffer class which can encode to
+ // a static memory region rather than dynamically allocating and then
+ // copying.
+ MOZ_ASSERT(header.cursor() == headerSize);
+ memcpy(ptr.get(), header.Get(), header.cursor());
+
+ // We've already updated offsets at this point. We need this to succeed.
+ mMap.reset();
+ MOZ_RELEASE_ASSERT(mem.Finalize(mMap).isOk());
+
+ return Ok();
+}
+
+void WritableSharedMap::SendTo(ContentParent* aParent) const {
+ nsTArray<IPCBlob> blobs(mBlobImpls.Length());
+
+ for (auto& blobImpl : mBlobImpls) {
+ nsresult rv = IPCBlobUtils::Serialize(blobImpl, *blobs.AppendElement());
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ continue;
+ }
+ }
+
+ Unused << aParent->SendUpdateSharedData(CloneMapFile(), mMap.size(), blobs,
+ mChangedKeys);
+}
+
+void WritableSharedMap::BroadcastChanges() {
+ if (mChangedKeys.IsEmpty()) {
+ return;
+ }
+
+ if (!Serialize().isOk()) {
+ return;
+ }
+
+ nsTArray<ContentParent*> parents;
+ ContentParent::GetAll(parents);
+ for (auto& parent : parents) {
+ SendTo(parent);
+ }
+
+ if (mReadOnly) {
+ nsTArray<RefPtr<BlobImpl>> blobImpls(mBlobImpls.Clone());
+ mReadOnly->Update(CloneMapFile(), mMap.size(), std::move(blobImpls),
+ std::move(mChangedKeys));
+ }
+
+ mChangedKeys.Clear();
+}
+
+void WritableSharedMap::Delete(const nsACString& aName) {
+ if (mEntries.Remove(aName)) {
+ KeyChanged(aName);
+ }
+}
+
+void WritableSharedMap::Set(JSContext* aCx, const nsACString& aName,
+ JS::Handle<JS::Value> aValue, ErrorResult& aRv) {
+ StructuredCloneData holder;
+
+ holder.Write(aCx, aValue, aRv);
+ if (aRv.Failed()) {
+ return;
+ }
+
+ if (!holder.InputStreams().IsEmpty()) {
+ aRv.Throw(NS_ERROR_INVALID_ARG);
+ return;
+ }
+
+ Entry* entry = mEntries.GetOrInsertNew(aName, *this, aName);
+ entry->TakeData(std::move(holder));
+
+ KeyChanged(aName);
+}
+
+void WritableSharedMap::Flush() { BroadcastChanges(); }
+
+void WritableSharedMap::IdleFlush() {
+ mPendingFlush = false;
+ Flush();
+}
+
+nsresult WritableSharedMap::KeyChanged(const nsACString& aName) {
+ if (!mChangedKeys.ContainsSorted(aName)) {
+ mChangedKeys.InsertElementSorted(aName);
+ }
+ mEntryArray.reset();
+
+ if (!mPendingFlush) {
+ MOZ_TRY(NS_DispatchToCurrentThreadQueue(
+ NewRunnableMethod("WritableSharedMap::IdleFlush", this,
+ &WritableSharedMap::IdleFlush),
+ EventQueuePriority::Idle));
+ mPendingFlush = true;
+ }
+ return NS_OK;
+}
+
+JSObject* SharedMap::WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) {
+ return MozSharedMap_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+JSObject* WritableSharedMap::WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) {
+ return MozWritableSharedMap_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+/* static */
+already_AddRefed<SharedMapChangeEvent> SharedMapChangeEvent::Constructor(
+ EventTarget* aEventTarget, const nsAString& aType,
+ const MozSharedMapChangeEventInit& aInit) {
+ RefPtr<SharedMapChangeEvent> event = new SharedMapChangeEvent(aEventTarget);
+
+ bool trusted = event->Init(aEventTarget);
+ event->InitEvent(aType, aInit.mBubbles, aInit.mCancelable);
+ event->SetTrusted(trusted);
+ event->SetComposed(aInit.mComposed);
+
+ event->mChangedKeys = aInit.mChangedKeys;
+
+ return event.forget();
+}
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(WritableSharedMap, SharedMap, mReadOnly)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WritableSharedMap)
+NS_INTERFACE_MAP_END_INHERITING(SharedMap)
+
+NS_IMPL_ADDREF_INHERITED(WritableSharedMap, SharedMap)
+NS_IMPL_RELEASE_INHERITED(WritableSharedMap, SharedMap)
+
+} // namespace dom::ipc
+} // namespace mozilla
diff --git a/dom/ipc/SharedMap.h b/dom/ipc/SharedMap.h
new file mode 100644
index 0000000000..0a5b686c62
--- /dev/null
+++ b/dom/ipc/SharedMap.h
@@ -0,0 +1,361 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef dom_ipc_SharedMap_h
+#define dom_ipc_SharedMap_h
+
+#include "mozilla/dom/MozSharedMapBinding.h"
+
+#include "mozilla/AutoMemMap.h"
+#include "mozilla/dom/ipc/StructuredCloneData.h"
+#include "mozilla/DOMEventTargetHelper.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Variant.h"
+#include "nsClassHashtable.h"
+#include "nsTArray.h"
+
+class nsIGlobalObject;
+
+namespace mozilla::dom {
+
+class ContentParent;
+
+namespace ipc {
+
+/**
+ * Together, the SharedMap and WritableSharedMap classes allow sharing a
+ * dynamically-updated, shared-memory key-value store across processes.
+ *
+ * The maps may only ever be updated in the parent process, via
+ * WritableSharedMap instances. When that map changes, its entire contents are
+ * serialized into a contiguous shared memory buffer, and broadcast to all child
+ * processes, which in turn update their entire map contents wholesale.
+ *
+ * Keys are arbitrary UTF-8 strings (currently exposed to JavaScript as UTF-16),
+ * and values are structured clone buffers. Values are eagerly encoded whenever
+ * they are updated, and lazily decoded each time they're read.
+ *
+ * Updates are batched. Rather than each key change triggering an immediate
+ * update, combined updates are broadcast after a delay. Changes are flushed
+ * immediately any time a new process is created. Additionally, any time a key
+ * is changed, a flush task is scheduled for the next time the event loop
+ * becomes idle. Changes can be flushed immediately by calling the flush()
+ * method.
+ *
+ *
+ * Whenever a read-only SharedMap is updated, it dispatches a "change" event.
+ * The event contains a "changedKeys" property with a list of all keys which
+ * were changed in the last update batch. Change events are never dispatched to
+ * WritableSharedMap instances.
+ */
+class SharedMap : public DOMEventTargetHelper {
+ using FileDescriptor = mozilla::ipc::FileDescriptor;
+
+ public:
+ SharedMap();
+
+ SharedMap(nsIGlobalObject* aGlobal, const FileDescriptor&, size_t,
+ nsTArray<RefPtr<BlobImpl>>&& aBlobs);
+
+ // Returns true if the map contains the given (UTF-8) key.
+ bool Has(const nsACString& name);
+
+ // If the map contains the given (UTF-8) key, decodes and returns a new copy
+ // of its value. Otherwise returns null.
+ void Get(JSContext* cx, const nsACString& name,
+ JS::MutableHandle<JS::Value> aRetVal, ErrorResult& aRv);
+
+ // Conversion helpers for WebIDL callers
+ bool Has(const nsAString& aName) { return Has(NS_ConvertUTF16toUTF8(aName)); }
+
+ void Get(JSContext* aCx, const nsAString& aName,
+ JS::MutableHandle<JS::Value> aRetVal, ErrorResult& aRv) {
+ return Get(aCx, NS_ConvertUTF16toUTF8(aName), aRetVal, aRv);
+ }
+
+ /**
+ * WebIDL iterator glue.
+ */
+ uint32_t GetIterableLength() const { return EntryArray().Length(); }
+
+ /**
+ * These functions return the key or value, respectively, at the given index.
+ * The index *must* be less than the value returned by GetIterableLength(), or
+ * the program will crash.
+ */
+ const nsString GetKeyAtIndex(uint32_t aIndex) const;
+ bool GetValueAtIndex(JSContext* aCx, uint32_t aIndex,
+ JS::MutableHandle<JS::Value> aResult) const;
+
+ /**
+ * Returns a copy of the read-only file descriptor which backs the shared
+ * memory region for this map. The file descriptor may be passed between
+ * processes, and used to update corresponding instances in child processes.
+ */
+ FileDescriptor CloneMapFile() const;
+
+ /**
+ * Returns the size of the memory mapped region that backs this map. Must be
+ * passed to the SharedMap() constructor or Update() method along with the
+ * descriptor returned by CloneMapFile() in order to initialize or update a
+ * child SharedMap.
+ */
+ size_t MapSize() const { return mMap.size(); }
+
+ /**
+ * Updates this instance to reflect the contents of the shared memory region
+ * in the given map file, and broadcasts a change event for the given set of
+ * changed (UTF-8-encoded) keys.
+ */
+ void Update(const FileDescriptor& aMapFile, size_t aMapSize,
+ nsTArray<RefPtr<BlobImpl>>&& aBlobs,
+ nsTArray<nsCString>&& aChangedKeys);
+
+ JSObject* WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) override;
+
+ protected:
+ ~SharedMap() override = default;
+
+ class Entry {
+ public:
+ Entry(Entry&&) = delete;
+
+ explicit Entry(SharedMap& aMap, const nsACString& aName = ""_ns)
+ : mMap(aMap), mName(aName), mData(AsVariant(uint32_t(0))) {}
+
+ ~Entry() = default;
+
+ /**
+ * Encodes or decodes this entry into or from the given OutputBuffer or
+ * InputBuffer.
+ */
+ template <typename Buffer>
+ void Code(Buffer& buffer) {
+ DebugOnly<size_t> startOffset = buffer.cursor();
+
+ buffer.codeString(mName);
+ buffer.codeUint32(DataOffset());
+ buffer.codeUint32(mSize);
+ buffer.codeUint16(mBlobOffset);
+ buffer.codeUint16(mBlobCount);
+
+ MOZ_ASSERT(buffer.cursor() == startOffset + HeaderSize());
+ }
+
+ /**
+ * Returns the size that this entry will take up in the map header. This
+ * must be equal to the number of bytes encoded by Code().
+ */
+ size_t HeaderSize() const {
+ return (sizeof(uint16_t) + mName.Length() + sizeof(DataOffset()) +
+ sizeof(mSize) + sizeof(mBlobOffset) + sizeof(mBlobCount));
+ }
+
+ /**
+ * Updates the value of this entry to the given structured clone data, of
+ * which it takes ownership. The passed StructuredCloneData object must not
+ * be used after this call.
+ */
+ void TakeData(StructuredCloneData&&);
+
+ /**
+ * This is called while building a new snapshot of the SharedMap. aDestPtr
+ * must point to a buffer within the new snapshot with Size() bytes reserved
+ * for it, and `aNewOffset` must be the offset of that buffer from the start
+ * of the snapshot's memory region.
+ *
+ * This function copies the raw structured clone data for the entry's value
+ * to the new buffer, and updates its internal state for use with the new
+ * data. Its offset is updated to aNewOffset, and any StructuredCloneData
+ * object it holds is destroyed.
+ *
+ * After this call, the entry is only valid in reference to the new
+ * snapshot, and must not be accessed again until the SharedMap mMap has
+ * been updated to point to it.
+ */
+ void ExtractData(char* aDestPtr, uint32_t aNewOffset,
+ uint16_t aNewBlobOffset);
+
+ // Returns the UTF-8-encoded name of the entry, which is used as its key in
+ // the map.
+ const nsCString& Name() const { return mName; }
+
+ // Decodes the entry's value into the current Realm of the given JS context
+ // and puts the result in aRetVal on success.
+ void Read(JSContext* aCx, JS::MutableHandle<JS::Value> aRetVal,
+ ErrorResult& aRv);
+
+ // Returns the byte size of the entry's raw structured clone data.
+ uint32_t Size() const { return mSize; }
+
+ private:
+ // Returns a pointer to the entry value's structured clone data within the
+ // SharedMap's mapped memory region. This is *only* valid shen mData
+ // contains a uint32_t.
+ const char* Data() const { return mMap.Data() + DataOffset(); }
+
+ // Returns the offset of the entry value's structured clone data within the
+ // SharedMap's mapped memory region. This is *only* valid shen mData
+ // contains a uint32_t.
+ uint32_t& DataOffset() { return mData.as<uint32_t>(); }
+ const uint32_t& DataOffset() const { return mData.as<uint32_t>(); }
+
+ public:
+ uint16_t BlobOffset() const { return mBlobOffset; }
+ uint16_t BlobCount() const { return mBlobCount; }
+
+ Span<const RefPtr<BlobImpl>> Blobs() {
+ if (mData.is<StructuredCloneData>()) {
+ return mData.as<StructuredCloneData>().BlobImpls();
+ }
+ return {&mMap.mBlobImpls[mBlobOffset], BlobCount()};
+ }
+
+ private:
+ // Returns the temporary StructuredCloneData object containing the entry's
+ // value. This is *only* value when mData contains a StructuredCloneDAta
+ // object.
+ const StructuredCloneData& Holder() const {
+ return mData.as<StructuredCloneData>();
+ }
+
+ SharedMap& mMap;
+
+ // The entry's (UTF-8 encoded) name, which serves as its key in the map.
+ nsCString mName;
+
+ /**
+ * This member provides a reference to the entry's structured clone data.
+ * Its type varies depending on the state of the entry:
+ *
+ * - For entries which have been snapshotted into a shared memory region,
+ * this is a uint32_t offset into the parent SharedMap's Data() buffer.
+ *
+ * - For entries which have been changed in a WritableSharedMap instance,
+ * but not serialized to a shared memory snapshot yet, this is a
+ * StructuredCloneData instance, containing a process-local copy of the
+ * data. This will be discarded the next time the map is serialized, and
+ * replaced with a buffer offset, as described above.
+ */
+ Variant<uint32_t, StructuredCloneData> mData;
+
+ // The size, in bytes, of the entry's structured clone data.
+ uint32_t mSize = 0;
+
+ uint16_t mBlobOffset = 0;
+ uint16_t mBlobCount = 0;
+ };
+
+ const nsTArray<Entry*>& EntryArray() const;
+
+ nsTArray<RefPtr<BlobImpl>> mBlobImpls;
+
+ // Rebuilds the entry hashtable mEntries from the values serialized in the
+ // current snapshot, if necessary. The hashtable is rebuilt lazily after
+ // construction and after every Update() call, so this function must be called
+ // before any attempt to access mEntries.
+ Result<Ok, nsresult> MaybeRebuild();
+ void MaybeRebuild() const;
+
+ // Note: This header is included by WebIDL binding headers, and therefore
+ // can't include "windows.h". Since FileDescriptor.h does include "windows.h"
+ // on Windows, we can only forward declare FileDescriptor, and can't include
+ // it as an inline member.
+ UniquePtr<FileDescriptor> mMapFile;
+ // The size of the memory-mapped region backed by mMapFile, in bytes.
+ size_t mMapSize = 0;
+
+ mutable nsClassHashtable<nsCStringHashKey, Entry> mEntries;
+ mutable Maybe<nsTArray<Entry*>> mEntryArray;
+
+ // Manages the memory mapping of the current snapshot. This is initialized
+ // lazily after each SharedMap construction or updated, based on the values in
+ // mMapFile and mMapSize.
+ loader::AutoMemMap mMap;
+
+ bool mWritable = false;
+
+ // Returns a pointer to the beginning of the memory mapped snapshot. Entry
+ // offsets are relative to this pointer, and Entry objects access their
+ // structured clone data by indexing this pointer.
+ char* Data() { return mMap.get<char>().get(); }
+};
+
+class WritableSharedMap final : public SharedMap {
+ public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(WritableSharedMap, SharedMap)
+
+ WritableSharedMap();
+
+ // Sets the value of the given (UTF-8 encoded) key to a structured clone
+ // snapshot of the given value.
+ void Set(JSContext* cx, const nsACString& name, JS::Handle<JS::Value> value,
+ ErrorResult& aRv);
+
+ // Deletes the given (UTF-8 encoded) key from the map.
+ void Delete(const nsACString& name);
+
+ // Conversion helpers for WebIDL callers
+ void Set(JSContext* aCx, const nsAString& aName, JS::Handle<JS::Value> aValue,
+ ErrorResult& aRv) {
+ return Set(aCx, NS_ConvertUTF16toUTF8(aName), aValue, aRv);
+ }
+
+ void Delete(const nsAString& aName) {
+ return Delete(NS_ConvertUTF16toUTF8(aName));
+ }
+
+ // Flushes any queued changes to a new snapshot, and broadcasts it to all
+ // child SharedMap instances.
+ void Flush();
+
+ // Sends the current set of shared map data to the given content process.
+ void SendTo(ContentParent* aContentParent) const;
+
+ /**
+ * Returns the read-only SharedMap instance corresponding to this
+ * WritableSharedMap for use in the parent process.
+ */
+ SharedMap* GetReadOnly();
+
+ JSObject* WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) override;
+
+ protected:
+ ~WritableSharedMap() override = default;
+
+ private:
+ // The set of (UTF-8 encoded) keys which have changed, or been deleted, since
+ // the last snapshot.
+ nsTArray<nsCString> mChangedKeys;
+
+ RefPtr<SharedMap> mReadOnly;
+
+ bool mPendingFlush = false;
+
+ // Creates a new snapshot of the map, and updates all Entry instance to
+ // reference its data.
+ Result<Ok, nsresult> Serialize();
+
+ void IdleFlush();
+
+ // If there have been any changes since the last snapshot, creates a new
+ // serialization and broadcasts it to all child SharedMap instances.
+ void BroadcastChanges();
+
+ // Marks the given (UTF-8 encoded) key as having changed. This adds it to
+ // mChangedKeys, if not already present, and schedules a flush for the next
+ // time the event loop is idle.
+ nsresult KeyChanged(const nsACString& aName);
+};
+
+} // namespace ipc
+} // namespace mozilla::dom
+
+#endif // dom_ipc_SharedMap_h
diff --git a/dom/ipc/SharedMapChangeEvent.h b/dom/ipc/SharedMapChangeEvent.h
new file mode 100644
index 0000000000..06bc53933b
--- /dev/null
+++ b/dom/ipc/SharedMapChangeEvent.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef dom_ipc_SharedMapChangeEvent_h
+#define dom_ipc_SharedMapChangeEvent_h
+
+#include "mozilla/dom/MozSharedMapBinding.h"
+
+#include "mozilla/dom/Event.h"
+#include "nsTArray.h"
+
+namespace mozilla::dom::ipc {
+
+class SharedMapChangeEvent final : public Event {
+ public:
+ NS_INLINE_DECL_REFCOUNTING_INHERITED(SharedMapChangeEvent, Event)
+
+ JSObject* WrapObjectInternal(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) override {
+ return MozSharedMapChangeEvent_Binding::Wrap(aCx, this, aGivenProto);
+ }
+
+ static already_AddRefed<SharedMapChangeEvent> Constructor(
+ EventTarget* aEventTarget, const nsAString& aType,
+ const MozSharedMapChangeEventInit& aInit);
+
+ void GetChangedKeys(nsTArray<nsString>& aChangedKeys) const {
+ aChangedKeys.AppendElements(mChangedKeys);
+ }
+
+ protected:
+ ~SharedMapChangeEvent() override = default;
+
+ private:
+ explicit SharedMapChangeEvent(EventTarget* aEventTarget)
+ : Event(aEventTarget, nullptr, nullptr) {}
+
+ nsTArray<nsString> mChangedKeys;
+};
+
+} // namespace mozilla::dom::ipc
+
+#endif // dom_ipc_SharedMapChangeEvent_h
diff --git a/dom/ipc/SharedMessageBody.cpp b/dom/ipc/SharedMessageBody.cpp
new file mode 100644
index 0000000000..e09ff2fd55
--- /dev/null
+++ b/dom/ipc/SharedMessageBody.cpp
@@ -0,0 +1,298 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SharedMessageBody.h"
+#include "mozilla/dom/File.h"
+#include "mozilla/dom/MessagePort.h"
+#include "mozilla/dom/RefMessageBodyService.h"
+#include "mozilla/dom/PMessagePort.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/BackgroundParent.h"
+#include "xpcpublic.h"
+
+namespace mozilla {
+
+using namespace ipc;
+
+namespace dom {
+
+SharedMessageBody::SharedMessageBody(
+ StructuredCloneHolder::TransferringSupport aSupportsTransferring,
+ const Maybe<nsID>& aAgentClusterId)
+ : mRefDataId(Nothing()),
+ mSupportsTransferring(aSupportsTransferring),
+ mAgentClusterId(aAgentClusterId) {}
+
+void SharedMessageBody::Write(JSContext* aCx, JS::Handle<JS::Value> aValue,
+ JS::Handle<JS::Value> aTransfers, nsID& aPortID,
+ RefMessageBodyService* aRefMessageBodyService,
+ ErrorResult& aRv) {
+ MOZ_ASSERT(!mCloneData && !mRefData);
+ MOZ_ASSERT(aRefMessageBodyService);
+
+ JS::CloneDataPolicy cloneDataPolicy;
+ // During a writing, we don't know the destination, so we assume it is part of
+ // the same agent cluster.
+ cloneDataPolicy.allowIntraClusterClonableSharedObjects();
+
+ nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx);
+ MOZ_ASSERT(global);
+
+ if (global->IsSharedMemoryAllowed()) {
+ cloneDataPolicy.allowSharedMemoryObjects();
+ }
+
+ mCloneData = MakeUnique<ipc::StructuredCloneData>(
+ JS::StructuredCloneScope::UnknownDestination, mSupportsTransferring);
+ mCloneData->Write(aCx, aValue, aTransfers, cloneDataPolicy, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
+
+ if (mCloneData->CloneScope() == JS::StructuredCloneScope::DifferentProcess) {
+ return;
+ }
+
+ MOZ_ASSERT(mCloneData->CloneScope() == JS::StructuredCloneScope::SameProcess);
+ RefPtr<RefMessageBody> refData =
+ new RefMessageBody(aPortID, std::move(mCloneData));
+
+ mRefDataId.emplace(aRefMessageBodyService->Register(refData.forget(), aRv));
+}
+
+void SharedMessageBody::Read(JSContext* aCx,
+ JS::MutableHandle<JS::Value> aValue,
+ RefMessageBodyService* aRefMessageBodyService,
+ SharedMessageBody::ReadMethod aReadMethod,
+ ErrorResult& aRv) {
+ MOZ_ASSERT(aRefMessageBodyService);
+
+ if (mCloneData) {
+ // Use a default cloneDataPolicy here, because SharedArrayBuffers and WASM
+ // are not supported.
+ return mCloneData->Read(aCx, aValue, JS::CloneDataPolicy(), aRv);
+ }
+
+ JS::CloneDataPolicy cloneDataPolicy;
+
+ nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx);
+ MOZ_ASSERT(global);
+
+ // Clones within the same agent cluster are allowed to use shared array
+ // buffers and WASM modules.
+ if (mAgentClusterId.isSome()) {
+ Maybe<nsID> agentClusterId = global->GetAgentClusterId();
+ if (agentClusterId.isSome() &&
+ mAgentClusterId.value().Equals(agentClusterId.value())) {
+ cloneDataPolicy.allowIntraClusterClonableSharedObjects();
+ }
+ }
+
+ if (global->IsSharedMemoryAllowed()) {
+ cloneDataPolicy.allowSharedMemoryObjects();
+ }
+
+ MOZ_ASSERT(!mRefData);
+ MOZ_ASSERT(mRefDataId.isSome());
+
+ if (aReadMethod == SharedMessageBody::StealRefMessageBody) {
+ mRefData = aRefMessageBodyService->Steal(mRefDataId.value());
+ } else {
+ MOZ_ASSERT(aReadMethod == SharedMessageBody::KeepRefMessageBody);
+ mRefData = aRefMessageBodyService->GetAndCount(mRefDataId.value());
+ }
+
+ if (!mRefData) {
+ aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR);
+ return;
+ }
+
+ mRefData->Read(aCx, aValue, cloneDataPolicy, aRv);
+}
+
+bool SharedMessageBody::TakeTransferredPortsAsSequence(
+ Sequence<OwningNonNull<mozilla::dom::MessagePort>>& aPorts) {
+ if (mCloneData) {
+ return mCloneData->TakeTransferredPortsAsSequence(aPorts);
+ }
+
+ MOZ_ASSERT(mRefData);
+ return mRefData->TakeTransferredPortsAsSequence(aPorts);
+}
+
+/* static */
+void SharedMessageBody::FromSharedToMessageChild(
+ mozilla::ipc::PBackgroundChild* aManager, SharedMessageBody* aData,
+ MessageData& aMessage) {
+ MOZ_ASSERT(aManager);
+ MOZ_ASSERT(aData);
+
+ aMessage.agentClusterId() = aData->mAgentClusterId;
+
+ if (aData->mCloneData) {
+ ClonedMessageData clonedData;
+ aData->mCloneData->BuildClonedMessageData(clonedData);
+ aMessage.data() = std::move(clonedData);
+ return;
+ }
+
+ MOZ_ASSERT(aData->mRefDataId.isSome());
+ aMessage.data() = RefMessageData(aData->mRefDataId.value());
+}
+
+/* static */
+void SharedMessageBody::FromSharedToMessagesChild(
+ PBackgroundChild* aManager,
+ const nsTArray<RefPtr<SharedMessageBody>>& aData,
+ nsTArray<MessageData>& aArray) {
+ MOZ_ASSERT(aManager);
+ MOZ_ASSERT(aArray.IsEmpty());
+ aArray.SetCapacity(aData.Length());
+
+ for (auto& data : aData) {
+ MessageData* message = aArray.AppendElement();
+ FromSharedToMessageChild(aManager, data, *message);
+ }
+}
+
+/* static */
+already_AddRefed<SharedMessageBody> SharedMessageBody::FromMessageToSharedChild(
+ MessageData& aMessage,
+ StructuredCloneHolder::TransferringSupport aSupportsTransferring) {
+ RefPtr<SharedMessageBody> data =
+ new SharedMessageBody(aSupportsTransferring, aMessage.agentClusterId());
+
+ if (aMessage.data().type() == MessageDataType::TClonedMessageData) {
+ data->mCloneData = MakeUnique<ipc::StructuredCloneData>(
+ JS::StructuredCloneScope::UnknownDestination, aSupportsTransferring);
+ data->mCloneData->StealFromClonedMessageData(
+ aMessage.data().get_ClonedMessageData());
+ } else {
+ MOZ_ASSERT(aMessage.data().type() == MessageDataType::TRefMessageData);
+ data->mRefDataId.emplace(aMessage.data().get_RefMessageData().uuid());
+ }
+
+ return data.forget();
+}
+
+/* static */
+already_AddRefed<SharedMessageBody> SharedMessageBody::FromMessageToSharedChild(
+ const MessageData& aMessage,
+ StructuredCloneHolder::TransferringSupport aSupportsTransferring) {
+ RefPtr<SharedMessageBody> data =
+ new SharedMessageBody(aSupportsTransferring, aMessage.agentClusterId());
+
+ if (aMessage.data().type() == MessageDataType::TClonedMessageData) {
+ data->mCloneData = MakeUnique<ipc::StructuredCloneData>(
+ JS::StructuredCloneScope::UnknownDestination, aSupportsTransferring);
+ data->mCloneData->BorrowFromClonedMessageData(
+ aMessage.data().get_ClonedMessageData());
+ } else {
+ MOZ_ASSERT(aMessage.data().type() == MessageDataType::TRefMessageData);
+ data->mRefDataId.emplace(aMessage.data().get_RefMessageData().uuid());
+ }
+
+ return data.forget();
+}
+
+/* static */
+bool SharedMessageBody::FromMessagesToSharedChild(
+ nsTArray<MessageData>& aArray,
+ FallibleTArray<RefPtr<SharedMessageBody>>& aData,
+ StructuredCloneHolder::TransferringSupport aSupportsTransferring) {
+ MOZ_ASSERT(aData.IsEmpty());
+
+ if (NS_WARN_IF(!aData.SetCapacity(aArray.Length(), mozilla::fallible))) {
+ return false;
+ }
+
+ for (auto& message : aArray) {
+ RefPtr<SharedMessageBody> data =
+ FromMessageToSharedChild(message, aSupportsTransferring);
+ if (!data || !aData.AppendElement(data, mozilla::fallible)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/* static */
+bool SharedMessageBody::FromSharedToMessagesParent(
+ PBackgroundParent* aManager,
+ const nsTArray<RefPtr<SharedMessageBody>>& aData,
+ nsTArray<MessageData>& aArray) {
+ MOZ_ASSERT(aManager);
+ MOZ_ASSERT(aArray.IsEmpty());
+
+ if (NS_WARN_IF(!aArray.SetCapacity(aData.Length(), mozilla::fallible))) {
+ return false;
+ }
+
+ for (auto& data : aData) {
+ MessageData* message = aArray.AppendElement();
+ message->agentClusterId() = data->mAgentClusterId;
+
+ if (data->mCloneData) {
+ ClonedMessageData clonedData;
+ data->mCloneData->BuildClonedMessageData(clonedData);
+ message->data() = std::move(clonedData);
+ continue;
+ }
+
+ MOZ_ASSERT(data->mRefDataId.isSome());
+ message->data() = RefMessageData(data->mRefDataId.value());
+ }
+
+ return true;
+}
+
+/* static */
+already_AddRefed<SharedMessageBody>
+SharedMessageBody::FromMessageToSharedParent(
+ MessageData& aMessage,
+ StructuredCloneHolder::TransferringSupport aSupportsTransferring) {
+ // TODO: This alloc is not fallible and there is no codepath that returns
+ // nullptr. But the caller checks for nullptr and handles array allocations
+ // for these items as fallible. See bug 1750497.
+ RefPtr<SharedMessageBody> data =
+ new SharedMessageBody(aSupportsTransferring, aMessage.agentClusterId());
+
+ if (aMessage.data().type() == MessageDataType::TClonedMessageData) {
+ data->mCloneData = MakeUnique<ipc::StructuredCloneData>(
+ JS::StructuredCloneScope::UnknownDestination, aSupportsTransferring);
+ data->mCloneData->StealFromClonedMessageData(
+ aMessage.data().get_ClonedMessageData());
+ } else {
+ MOZ_ASSERT(aMessage.data().type() == MessageDataType::TRefMessageData);
+ data->mRefDataId.emplace(aMessage.data().get_RefMessageData().uuid());
+ }
+
+ return data.forget();
+}
+
+bool SharedMessageBody::FromMessagesToSharedParent(
+ nsTArray<MessageData>& aArray,
+ FallibleTArray<RefPtr<SharedMessageBody>>& aData,
+ StructuredCloneHolder::TransferringSupport aSupportsTransferring) {
+ MOZ_ASSERT(aData.IsEmpty());
+
+ if (NS_WARN_IF(!aData.SetCapacity(aArray.Length(), mozilla::fallible))) {
+ return false;
+ }
+
+ for (auto& message : aArray) {
+ RefPtr<SharedMessageBody> data = FromMessageToSharedParent(message);
+ if (!data || !aData.AppendElement(data, mozilla::fallible)) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/SharedMessageBody.h b/dom/ipc/SharedMessageBody.h
new file mode 100644
index 0000000000..2c82473678
--- /dev/null
+++ b/dom/ipc/SharedMessageBody.h
@@ -0,0 +1,112 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_SharedMessageBody_h
+#define mozilla_dom_SharedMessageBody_h
+
+#include "mozilla/dom/ipc/StructuredCloneData.h"
+#include "mozilla/Maybe.h"
+
+namespace mozilla {
+
+namespace ipc {
+class PBackgroundChild;
+}
+
+namespace dom {
+
+class MessagePort;
+class RefMessageBody;
+class RefMessageBodyService;
+
+class SharedMessageBody final {
+ public:
+ NS_INLINE_DECL_REFCOUNTING(SharedMessageBody)
+
+ SharedMessageBody(
+ StructuredCloneHolder::TransferringSupport aSupportsTransferring,
+ const Maybe<nsID>& aAgentClusterId);
+
+ // Note that the populated MessageData borrows the underlying
+ // JSStructuredCloneData from the SharedMessageBody, so the caller is
+ // required to ensure that the MessageData instances are destroyed prior to
+ // the SharedMessageBody instances.
+ static void FromSharedToMessageChild(
+ mozilla::ipc::PBackgroundChild* aBackgroundManager,
+ SharedMessageBody* aData, MessageData& aMessage);
+ static void FromSharedToMessagesChild(
+ mozilla::ipc::PBackgroundChild* aBackgroundManager,
+ const nsTArray<RefPtr<SharedMessageBody>>& aData,
+ nsTArray<MessageData>& aArray);
+
+ // Const MessageData.
+ static already_AddRefed<SharedMessageBody> FromMessageToSharedChild(
+ MessageData& aMessage,
+ StructuredCloneHolder::TransferringSupport aSupportsTransferring =
+ StructuredCloneHolder::TransferringSupported);
+ // Non-const MessageData.
+ static already_AddRefed<SharedMessageBody> FromMessageToSharedChild(
+ const MessageData& aMessage,
+ StructuredCloneHolder::TransferringSupport aSupportsTransferring =
+ StructuredCloneHolder::TransferringSupported);
+ // Array of MessageData objects
+ static bool FromMessagesToSharedChild(
+ nsTArray<MessageData>& aArray,
+ FallibleTArray<RefPtr<SharedMessageBody>>& aData,
+ StructuredCloneHolder::TransferringSupport aSupportsTransferring =
+ StructuredCloneHolder::TransferringSupported);
+
+ // Note that the populated MessageData borrows the underlying
+ // JSStructuredCloneData from the SharedMessageBody, so the caller is
+ // required to ensure that the MessageData instances are destroyed prior to
+ // the SharedMessageBody instances.
+ static bool FromSharedToMessagesParent(
+ mozilla::ipc::PBackgroundParent* aManager,
+ const nsTArray<RefPtr<SharedMessageBody>>& aData,
+ nsTArray<MessageData>& aArray);
+
+ static already_AddRefed<SharedMessageBody> FromMessageToSharedParent(
+ MessageData& aMessage,
+ StructuredCloneHolder::TransferringSupport aSupportsTransferring =
+ StructuredCloneHolder::TransferringSupported);
+ static bool FromMessagesToSharedParent(
+ nsTArray<MessageData>& aArray,
+ FallibleTArray<RefPtr<SharedMessageBody>>& aData,
+ StructuredCloneHolder::TransferringSupport aSupportsTransferring =
+ StructuredCloneHolder::TransferringSupported);
+
+ enum ReadMethod {
+ StealRefMessageBody,
+ KeepRefMessageBody,
+ };
+
+ void Read(JSContext* aCx, JS::MutableHandle<JS::Value> aValue,
+ RefMessageBodyService* aRefMessageBodyService,
+ ReadMethod aReadMethod, ErrorResult& aRv);
+
+ void Write(JSContext* aCx, JS::Handle<JS::Value> aValue,
+ JS::Handle<JS::Value> aTransfers, nsID& aPortID,
+ RefMessageBodyService* aRefMessageBodyService, ErrorResult& aRv);
+
+ bool TakeTransferredPortsAsSequence(
+ Sequence<OwningNonNull<mozilla::dom::MessagePort>>& aPorts);
+
+ private:
+ ~SharedMessageBody() = default;
+
+ UniquePtr<ipc::StructuredCloneData> mCloneData;
+
+ RefPtr<RefMessageBody> mRefData;
+ Maybe<nsID> mRefDataId;
+
+ const StructuredCloneHolder::TransferringSupport mSupportsTransferring;
+ const Maybe<nsID> mAgentClusterId;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_SharedMessageBody_h
diff --git a/dom/ipc/SharedStringMap.cpp b/dom/ipc/SharedStringMap.cpp
new file mode 100644
index 0000000000..8d50de6863
--- /dev/null
+++ b/dom/ipc/SharedStringMap.cpp
@@ -0,0 +1,140 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "SharedStringMap.h"
+
+#include "MemMapSnapshot.h"
+#include "ScriptPreloader-inl.h"
+
+#include "mozilla/BinarySearch.h"
+#include "mozilla/Try.h"
+#include "mozilla/ipc/FileDescriptor.h"
+
+using namespace mozilla::loader;
+
+namespace mozilla {
+
+using namespace ipc;
+
+namespace dom::ipc {
+
+static constexpr uint32_t kSharedStringMapMagic = 0x9e3779b9;
+
+static inline size_t GetAlignmentOffset(size_t aOffset, size_t aAlign) {
+ auto mod = aOffset % aAlign;
+ return mod ? aAlign - mod : 0;
+}
+
+SharedStringMap::SharedStringMap(const FileDescriptor& aMapFile,
+ size_t aMapSize) {
+ auto result = mMap.initWithHandle(aMapFile, aMapSize);
+ MOZ_RELEASE_ASSERT(result.isOk());
+ MOZ_RELEASE_ASSERT(GetHeader().mMagic == kSharedStringMapMagic);
+ // We return literal nsStrings and nsCStrings pointing to the mapped data,
+ // which means that we may still have references to the mapped data even
+ // after this instance is destroyed. That means that we need to keep the
+ // mapping alive until process shutdown, in order to be safe.
+ mMap.setPersistent();
+}
+
+SharedStringMap::SharedStringMap(SharedStringMapBuilder&& aBuilder) {
+ auto result = aBuilder.Finalize(mMap);
+ MOZ_RELEASE_ASSERT(result.isOk());
+ MOZ_RELEASE_ASSERT(GetHeader().mMagic == kSharedStringMapMagic);
+ mMap.setPersistent();
+}
+
+mozilla::ipc::FileDescriptor SharedStringMap::CloneFileDescriptor() const {
+ return mMap.cloneHandle();
+}
+
+bool SharedStringMap::Has(const nsCString& aKey) {
+ size_t index;
+ return Find(aKey, &index);
+}
+
+bool SharedStringMap::Get(const nsCString& aKey, nsAString& aValue) {
+ const auto& entries = Entries();
+
+ size_t index;
+ if (!Find(aKey, &index)) {
+ return false;
+ }
+
+ aValue.Assign(ValueTable().Get(entries[index].mValue));
+ return true;
+}
+
+bool SharedStringMap::Find(const nsCString& aKey, size_t* aIndex) {
+ const auto& keys = KeyTable();
+
+ return BinarySearchIf(
+ Entries(), 0, EntryCount(),
+ [&](const Entry& aEntry) { return Compare(aKey, keys.Get(aEntry.mKey)); },
+ aIndex);
+}
+
+void SharedStringMapBuilder::Add(const nsCString& aKey,
+ const nsString& aValue) {
+ mEntries.InsertOrUpdate(aKey,
+ Entry{mKeyTable.Add(aKey), mValueTable.Add(aValue)});
+}
+
+Result<Ok, nsresult> SharedStringMapBuilder::Finalize(
+ loader::AutoMemMap& aMap) {
+ using Header = SharedStringMap::Header;
+
+ MOZ_ASSERT(mEntries.Count() == mKeyTable.Count());
+
+ auto keys = ToTArray<nsTArray<nsCString>>(mEntries.Keys());
+ keys.Sort();
+
+ Header header = {kSharedStringMapMagic, uint32_t(keys.Length())};
+
+ size_t offset = sizeof(header);
+ offset += GetAlignmentOffset(offset, alignof(Header));
+
+ offset += keys.Length() * sizeof(SharedStringMap::Entry);
+
+ header.mKeyStringsOffset = offset;
+ header.mKeyStringsSize = mKeyTable.Size();
+
+ offset += header.mKeyStringsSize;
+ offset +=
+ GetAlignmentOffset(offset, alignof(decltype(mValueTable)::ElemType));
+
+ header.mValueStringsOffset = offset;
+ header.mValueStringsSize = mValueTable.Size();
+
+ offset += header.mValueStringsSize;
+
+ MemMapSnapshot mem;
+ MOZ_TRY(mem.Init(offset));
+
+ auto headerPtr = mem.Get<Header>();
+ headerPtr[0] = header;
+
+ auto* entry = reinterpret_cast<Entry*>(&headerPtr[1]);
+ for (auto& key : keys) {
+ *entry++ = mEntries.Get(key);
+ }
+
+ auto ptr = mem.Get<uint8_t>();
+
+ mKeyTable.Write({&ptr[header.mKeyStringsOffset], header.mKeyStringsSize});
+
+ mValueTable.Write(
+ {&ptr[header.mValueStringsOffset], header.mValueStringsSize});
+
+ mKeyTable.Clear();
+ mValueTable.Clear();
+ mEntries.Clear();
+
+ return mem.Finalize(aMap);
+}
+
+} // namespace dom::ipc
+} // namespace mozilla
diff --git a/dom/ipc/SharedStringMap.h b/dom/ipc/SharedStringMap.h
new file mode 100644
index 0000000000..cb14e09dbc
--- /dev/null
+++ b/dom/ipc/SharedStringMap.h
@@ -0,0 +1,220 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef dom_ipc_SharedStringMap_h
+#define dom_ipc_SharedStringMap_h
+
+#include "mozilla/AutoMemMap.h"
+#include "mozilla/Result.h"
+#include "mozilla/dom/ipc/StringTable.h"
+#include "nsTHashMap.h"
+
+namespace mozilla::dom::ipc {
+
+class SharedStringMapBuilder;
+
+/**
+ * This class provides a simple, read-only key-value string store, with all
+ * data packed into a single segment of memory, which can be shared between
+ * processes.
+ *
+ * Look-ups are performed by binary search of a static table in the mapped
+ * memory region, and all returned strings are literals which reference the
+ * mapped data. No copies are performed on instantiation or look-up.
+ *
+ * Important: The mapped memory created by this class is persistent. Once an
+ * instance has been initialized, the memory that it allocates can never be
+ * freed before process shutdown. Do not use it for short-lived mappings.
+ */
+class SharedStringMap {
+ using FileDescriptor = mozilla::ipc::FileDescriptor;
+
+ public:
+ /**
+ * The header at the beginning of the shared memory region describing its
+ * layout. The layout of the shared memory is as follows:
+ *
+ * - Header:
+ * A Header struct describing the contents of the rest of the memory region.
+ *
+ * - Optional alignment padding for Header[].
+ *
+ * - Entry[header.mEntryCount]:
+ * An array of Entry structs, one for each entry in the map. Entries are
+ * lexocographically sorted by key.
+ *
+ * - StringTable<nsCString>:
+ * A region of flat, null-terminated C strings. Entry key strings are
+ * encoded as character offsets into this region.
+ *
+ * - Optional alignment padding for char16_t[]
+ *
+ * - StringTable<nsString>:
+ * A region of flat, null-terminated UTF-16 strings. Entry value strings are
+ * encoded as character (*not* byte) offsets into this region.
+ */
+ struct Header {
+ uint32_t mMagic;
+ // The number of entries in this map.
+ uint32_t mEntryCount;
+
+ // The raw byte offset of the beginning of the key string table, from the
+ // start of the shared memory region, and its size in bytes.
+ size_t mKeyStringsOffset;
+ size_t mKeyStringsSize;
+
+ // The raw byte offset of the beginning of the value string table, from the
+ // start of the shared memory region, and its size in bytes (*not*
+ // characters).
+ size_t mValueStringsOffset;
+ size_t mValueStringsSize;
+ };
+
+ /**
+ * Describes a value in the string map, as offsets into the key and value
+ * string tables.
+ */
+ struct Entry {
+ // The offset and size of the entry's UTF-8 key in the key string table.
+ StringTableEntry mKey;
+ // The offset and size of the entry's UTF-16 value in the value string
+ // table.
+ StringTableEntry mValue;
+ };
+
+ NS_INLINE_DECL_REFCOUNTING(SharedStringMap)
+
+ // Note: These constructors are infallible on the premise that this class
+ // is used primarily in cases where it is critical to platform
+ // functionality.
+ explicit SharedStringMap(const FileDescriptor&, size_t);
+ explicit SharedStringMap(SharedStringMapBuilder&&);
+
+ /**
+ * Searches for the given value in the map, and returns true if it exists.
+ */
+ bool Has(const nsCString& aKey);
+
+ /**
+ * Searches for the given value in the map, and, if it exists, returns true
+ * and places its value in aValue.
+ *
+ * The returned value is a literal string which references the mapped memory
+ * region.
+ */
+ bool Get(const nsCString& aKey, nsAString& aValue);
+
+ private:
+ /**
+ * Searches for an entry for the given key. If found, returns true, and
+ * places its index in the entry array in aIndex.
+ */
+ bool Find(const nsCString& aKey, size_t* aIndex);
+
+ public:
+ /**
+ * Returns the number of entries in the map.
+ */
+ uint32_t Count() const { return EntryCount(); }
+
+ /**
+ * Returns the string entry at the given index. Keys are guaranteed to be
+ * sorted lexographically.
+ *
+ * The given index *must* be less than the value returned by Count().
+ *
+ * The returned value is a literal string which references the mapped memory
+ * region.
+ */
+ nsCString GetKeyAt(uint32_t aIndex) const {
+ MOZ_ASSERT(aIndex < Count());
+ return KeyTable().Get(Entries()[aIndex].mKey);
+ }
+
+ /**
+ * Returns the string value for the entry at the given index.
+ *
+ * The given index *must* be less than the value returned by Count().
+ *
+ * The returned value is a literal string which references the mapped memory
+ * region.
+ */
+ nsString GetValueAt(uint32_t aIndex) const {
+ MOZ_ASSERT(aIndex < Count());
+ return ValueTable().Get(Entries()[aIndex].mValue);
+ }
+
+ /**
+ * Returns a copy of the read-only file descriptor which backs the shared
+ * memory region for this map. The file descriptor may be passed between
+ * processes, and used to construct new instances of SharedStringMap with
+ * the same data as this instance.
+ */
+ FileDescriptor CloneFileDescriptor() const;
+
+ size_t MapSize() const { return mMap.size(); }
+
+ protected:
+ ~SharedStringMap() = default;
+
+ private:
+ // Type-safe getters for values in the shared memory region:
+ const Header& GetHeader() const { return mMap.get<Header>()[0]; }
+
+ RangedPtr<const Entry> Entries() const {
+ return {reinterpret_cast<const Entry*>(&GetHeader() + 1), EntryCount()};
+ }
+
+ uint32_t EntryCount() const { return GetHeader().mEntryCount; }
+
+ StringTable<nsCString> KeyTable() const {
+ auto& header = GetHeader();
+ return {{&mMap.get<uint8_t>()[header.mKeyStringsOffset],
+ header.mKeyStringsSize}};
+ }
+
+ StringTable<nsString> ValueTable() const {
+ auto& header = GetHeader();
+ return {{&mMap.get<uint8_t>()[header.mValueStringsOffset],
+ header.mValueStringsSize}};
+ }
+
+ loader::AutoMemMap mMap;
+};
+
+/**
+ * A helper class which builds the contiguous look-up table used by
+ * SharedStringMap. Each key-value pair in the final map is added to the
+ * builder, before it is finalized and transformed into a snapshot.
+ */
+class MOZ_RAII SharedStringMapBuilder {
+ public:
+ SharedStringMapBuilder() = default;
+
+ /**
+ * Adds a key-value pair to the map.
+ */
+ void Add(const nsCString& aKey, const nsString& aValue);
+
+ /**
+ * Finalizes the binary representation of the map, writes it to a shared
+ * memory region, and then initializes the given AutoMemMap with a reference
+ * to the read-only copy of it.
+ */
+ Result<Ok, nsresult> Finalize(loader::AutoMemMap& aMap);
+
+ private:
+ using Entry = SharedStringMap::Entry;
+
+ StringTableBuilder<nsCStringHashKey, nsCString> mKeyTable;
+ StringTableBuilder<nsStringHashKey, nsString> mValueTable;
+
+ nsTHashMap<nsCStringHashKey, Entry> mEntries;
+};
+
+} // namespace mozilla::dom::ipc
+
+#endif // dom_ipc_SharedStringMap_h
diff --git a/dom/ipc/StringTable.h b/dom/ipc/StringTable.h
new file mode 100644
index 0000000000..5de3cb9226
--- /dev/null
+++ b/dom/ipc/StringTable.h
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef dom_ipc_StringTable_h
+#define dom_ipc_StringTable_h
+
+#include "mozilla/RangedPtr.h"
+#include "nsTHashMap.h"
+
+/**
+ * This file contains helper classes for creating and accessing compact string
+ * tables, which can be used as the building blocks of shared memory databases.
+ * Each string table a de-duplicated set of strings which can be referenced
+ * using their character offsets within a data block and their lengths. The
+ * string tables, once created, cannot be modified, and are primarily useful in
+ * read-only shared memory or memory mapped files.
+ */
+
+namespace mozilla::dom::ipc {
+
+/**
+ * Contains the character offset and character length of an entry in a string
+ * table. This may be used for either 8-bit or 16-bit strings, and is required
+ * to retrieve an entry from a string table.
+ */
+struct StringTableEntry {
+ uint32_t mOffset;
+ uint32_t mLength;
+
+ // Ignore mLength. It must be the same for any two strings with the same
+ // offset.
+ uint32_t Hash() const { return mOffset; }
+
+ bool operator==(const StringTableEntry& aOther) const {
+ return mOffset == aOther.mOffset;
+ }
+};
+
+template <typename StringType>
+class StringTable {
+ using ElemType = typename StringType::char_type;
+
+ public:
+ MOZ_IMPLICIT StringTable(const RangedPtr<uint8_t>& aBuffer)
+ : mBuffer(aBuffer.ReinterpretCast<ElemType>()) {
+ MOZ_ASSERT(uintptr_t(aBuffer.get()) % alignof(ElemType) == 0,
+ "Got misalinged buffer");
+ }
+
+ StringType Get(const StringTableEntry& aEntry) const {
+ StringType res;
+ res.AssignLiteral(GetBare(aEntry), aEntry.mLength);
+ return res;
+ }
+
+ const ElemType* GetBare(const StringTableEntry& aEntry) const {
+ return &mBuffer[aEntry.mOffset];
+ }
+
+ private:
+ RangedPtr<ElemType> mBuffer;
+};
+
+template <typename KeyType, typename StringType>
+class StringTableBuilder {
+ public:
+ using ElemType = typename StringType::char_type;
+
+ StringTableEntry Add(const StringType& aKey) {
+ return mEntries.WithEntryHandle(aKey,
+ [&](auto&& entry) -> StringTableEntry {
+ auto length = uint32_t(aKey.Length());
+ entry.OrInsertWith([&]() {
+ Entry newEntry{mSize, aKey};
+ mSize += length + 1;
+
+ return newEntry;
+ });
+
+ return {entry->mOffset, length};
+ });
+ }
+
+ void Write(const RangedPtr<uint8_t>& aBuffer) {
+ auto buffer = aBuffer.ReinterpretCast<ElemType>();
+
+ for (const auto& entry : mEntries.Values()) {
+ memcpy(&buffer[entry.mOffset], entry.mValue.BeginReading(),
+ sizeof(ElemType) * (entry.mValue.Length() + 1));
+ }
+ }
+
+ uint32_t Count() const { return mEntries.Count(); }
+
+ uint32_t Size() const { return mSize * sizeof(ElemType); }
+
+ void Clear() { mEntries.Clear(); }
+
+ static constexpr size_t Alignment() { return alignof(ElemType); }
+
+ private:
+ struct Entry {
+ uint32_t mOffset;
+ StringType mValue;
+ };
+
+ nsTHashMap<KeyType, Entry> mEntries;
+ uint32_t mSize = 0;
+};
+
+} // namespace mozilla::dom::ipc
+
+#endif
diff --git a/dom/ipc/StructuredCloneData.cpp b/dom/ipc/StructuredCloneData.cpp
new file mode 100644
index 0000000000..7406083f60
--- /dev/null
+++ b/dom/ipc/StructuredCloneData.cpp
@@ -0,0 +1,329 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "StructuredCloneData.h"
+
+#include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/BlobBinding.h"
+#include "mozilla/dom/BlobImpl.h"
+#include "mozilla/dom/DOMTypes.h"
+#include "mozilla/dom/File.h"
+#include "mozilla/dom/IPCBlobUtils.h"
+#include "mozilla/ipc/BackgroundParent.h"
+#include "mozilla/ipc/IPCStreamUtils.h"
+#include "mozilla/ipc/SerializedStructuredCloneBuffer.h"
+#include "nsContentUtils.h"
+#include "nsJSEnvironment.h"
+#include "MainThreadUtils.h"
+#include "StructuredCloneTags.h"
+#include "jsapi.h"
+
+using namespace mozilla::ipc;
+
+namespace mozilla::dom::ipc {
+
+using mozilla::ipc::IPCStream;
+using mozilla::ipc::PBackgroundChild;
+using mozilla::ipc::PBackgroundParent;
+
+StructuredCloneData::StructuredCloneData()
+ : StructuredCloneData(
+ StructuredCloneHolder::StructuredCloneScope::DifferentProcess,
+ StructuredCloneHolder::TransferringSupported) {}
+
+StructuredCloneData::StructuredCloneData(StructuredCloneData&& aOther)
+ : StructuredCloneData(
+ StructuredCloneHolder::StructuredCloneScope::DifferentProcess,
+ StructuredCloneHolder::TransferringSupported) {
+ *this = std::move(aOther);
+}
+
+StructuredCloneData::StructuredCloneData(
+ StructuredCloneHolder::StructuredCloneScope aScope,
+ TransferringSupport aSupportsTransferring)
+ : StructuredCloneHolder(StructuredCloneHolder::CloningSupported,
+ aSupportsTransferring, aScope),
+ mExternalData(JS::StructuredCloneScope::DifferentProcess),
+ mInitialized(false) {
+ MOZ_ASSERT(
+ aScope == StructuredCloneHolder::StructuredCloneScope::DifferentProcess ||
+ aScope ==
+ StructuredCloneHolder::StructuredCloneScope::UnknownDestination);
+}
+
+StructuredCloneData::~StructuredCloneData() = default;
+
+StructuredCloneData& StructuredCloneData::operator=(
+ StructuredCloneData&& aOther) {
+ mBlobImplArray = std::move(aOther.mBlobImplArray);
+ mExternalData = std::move(aOther.mExternalData);
+ mSharedData = std::move(aOther.mSharedData);
+ mPortIdentifiers = std::move(aOther.mPortIdentifiers);
+ mInitialized = aOther.mInitialized;
+
+ return *this;
+}
+
+bool StructuredCloneData::Copy(const StructuredCloneData& aData) {
+ if (!aData.mInitialized) {
+ return true;
+ }
+
+ if (aData.SharedData()) {
+ mSharedData = aData.SharedData();
+ } else {
+ mSharedData = SharedJSAllocatedData::CreateFromExternalData(aData.Data());
+ NS_ENSURE_TRUE(mSharedData, false);
+ }
+
+ if (mSupportsTransferring) {
+ PortIdentifiers().AppendElements(aData.PortIdentifiers());
+ }
+
+ MOZ_ASSERT(BlobImpls().IsEmpty());
+ BlobImpls().AppendElements(aData.BlobImpls());
+
+ MOZ_ASSERT(GetSurfaces().IsEmpty());
+ MOZ_ASSERT(WasmModules().IsEmpty());
+
+ MOZ_ASSERT(InputStreams().IsEmpty());
+ InputStreams().AppendElements(aData.InputStreams());
+
+ mInitialized = true;
+
+ return true;
+}
+
+void StructuredCloneData::Read(JSContext* aCx,
+ JS::MutableHandle<JS::Value> aValue,
+ ErrorResult& aRv) {
+ Read(aCx, aValue, JS::CloneDataPolicy(), aRv);
+}
+
+void StructuredCloneData::Read(JSContext* aCx,
+ JS::MutableHandle<JS::Value> aValue,
+ const JS::CloneDataPolicy& aCloneDataPolicy,
+ ErrorResult& aRv) {
+ MOZ_ASSERT(mInitialized);
+
+ nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx);
+ MOZ_ASSERT(global);
+
+ ReadFromBuffer(global, aCx, Data(), aValue, aCloneDataPolicy, aRv);
+}
+
+void StructuredCloneData::Write(JSContext* aCx, JS::Handle<JS::Value> aValue,
+ ErrorResult& aRv) {
+ Write(aCx, aValue, JS::UndefinedHandleValue, JS::CloneDataPolicy(), aRv);
+}
+
+void StructuredCloneData::Write(JSContext* aCx, JS::Handle<JS::Value> aValue,
+ JS::Handle<JS::Value> aTransfer,
+ const JS::CloneDataPolicy& aCloneDataPolicy,
+ ErrorResult& aRv) {
+ MOZ_ASSERT(!mInitialized);
+
+ StructuredCloneHolder::Write(aCx, aValue, aTransfer, aCloneDataPolicy, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
+
+ JSStructuredCloneData data(mBuffer->scope());
+ mBuffer->giveTo(&data);
+ mBuffer = nullptr;
+ mSharedData = new SharedJSAllocatedData(std::move(data));
+ mInitialized = true;
+}
+
+bool StructuredCloneData::BuildClonedMessageData(
+ ClonedMessageData& aClonedData) {
+ SerializedStructuredCloneBuffer& buffer = aClonedData.data();
+ auto iter = Data().Start();
+ size_t size = Data().Size();
+ bool success;
+ buffer.data = Data().Borrow(iter, size, &success);
+ if (NS_WARN_IF(!success)) {
+ return false;
+ }
+ if (SupportsTransferring()) {
+ aClonedData.identifiers().AppendElements(PortIdentifiers());
+ }
+
+ const nsTArray<RefPtr<BlobImpl>>& blobImpls = BlobImpls();
+
+ if (!blobImpls.IsEmpty()) {
+ if (NS_WARN_IF(
+ !aClonedData.blobs().SetLength(blobImpls.Length(), fallible))) {
+ return false;
+ }
+
+ for (uint32_t i = 0; i < blobImpls.Length(); ++i) {
+ nsresult rv =
+ IPCBlobUtils::Serialize(blobImpls[i], aClonedData.blobs()[i]);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return false;
+ }
+ }
+ }
+
+ const nsTArray<nsCOMPtr<nsIInputStream>>& inputStreams = InputStreams();
+ if (!inputStreams.IsEmpty()) {
+ nsTArray<IPCStream>& streams = aClonedData.inputStreams();
+ uint32_t length = inputStreams.Length();
+ streams.SetCapacity(length);
+ for (uint32_t i = 0; i < length; ++i) {
+ IPCStream value;
+ if (!mozilla::ipc::SerializeIPCStream(do_AddRef(inputStreams[i]), value,
+ /* aAllowLazy */ false)) {
+ return false;
+ }
+ streams.AppendElement(value);
+ }
+ }
+
+ return true;
+}
+
+// See the StructuredCloneData class block comment for the meanings of each val.
+enum MemoryFlavorEnum { BorrowMemory = 0, CopyMemory, StealMemory };
+
+template <MemoryFlavorEnum>
+struct MemoryTraits {};
+
+template <>
+struct MemoryTraits<BorrowMemory> {
+ using ClonedMessageType = const mozilla::dom::ClonedMessageData;
+
+ static void ProvideBuffer(const ClonedMessageData& aClonedData,
+ StructuredCloneData& aData) {
+ const SerializedStructuredCloneBuffer& buffer = aClonedData.data();
+ aData.UseExternalData(buffer.data);
+ }
+};
+
+template <>
+struct MemoryTraits<CopyMemory> {
+ using ClonedMessageType = const mozilla::dom::ClonedMessageData;
+
+ static void ProvideBuffer(const ClonedMessageData& aClonedData,
+ StructuredCloneData& aData) {
+ const SerializedStructuredCloneBuffer& buffer = aClonedData.data();
+ aData.CopyExternalData(buffer.data);
+ }
+};
+
+template <>
+struct MemoryTraits<StealMemory> {
+ // note: not const!
+ using ClonedMessageType = mozilla::dom::ClonedMessageData;
+
+ static void ProvideBuffer(ClonedMessageData& aClonedData,
+ StructuredCloneData& aData) {
+ SerializedStructuredCloneBuffer& buffer = aClonedData.data();
+ aData.StealExternalData(buffer.data);
+ }
+};
+
+// Note that there isn't actually a difference between Parent/BackgroundParent
+// and Child/BackgroundChild in this implementation. The calling methods,
+// however, do maintain the distinction for code-reading purposes and are backed
+// by assertions to enforce there is no misuse.
+template <MemoryFlavorEnum MemoryFlavor>
+static void UnpackClonedMessageData(
+ typename MemoryTraits<MemoryFlavor>::ClonedMessageType& aClonedData,
+ StructuredCloneData& aData) {
+ const nsTArray<MessagePortIdentifier>& identifiers =
+ aClonedData.identifiers();
+
+ MemoryTraits<MemoryFlavor>::ProvideBuffer(aClonedData, aData);
+
+ if (aData.SupportsTransferring()) {
+ aData.PortIdentifiers().AppendElements(identifiers);
+ }
+
+ const nsTArray<IPCBlob>& blobs = aClonedData.blobs();
+ if (!blobs.IsEmpty()) {
+ uint32_t length = blobs.Length();
+ aData.BlobImpls().SetCapacity(length);
+ for (uint32_t i = 0; i < length; ++i) {
+ RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(blobs[i]);
+ MOZ_ASSERT(blobImpl);
+
+ aData.BlobImpls().AppendElement(blobImpl);
+ }
+ }
+
+ const nsTArray<IPCStream>& streams = aClonedData.inputStreams();
+ if (!streams.IsEmpty()) {
+ uint32_t length = streams.Length();
+ aData.InputStreams().SetCapacity(length);
+ for (uint32_t i = 0; i < length; ++i) {
+ nsCOMPtr<nsIInputStream> stream = DeserializeIPCStream(streams[i]);
+ aData.InputStreams().AppendElement(stream);
+ }
+ }
+}
+
+void StructuredCloneData::BorrowFromClonedMessageData(
+ const ClonedMessageData& aClonedData) {
+ UnpackClonedMessageData<BorrowMemory>(aClonedData, *this);
+}
+
+void StructuredCloneData::CopyFromClonedMessageData(
+ const ClonedMessageData& aClonedData) {
+ UnpackClonedMessageData<CopyMemory>(aClonedData, *this);
+}
+
+void StructuredCloneData::StealFromClonedMessageData(
+ ClonedMessageData& aClonedData) {
+ UnpackClonedMessageData<StealMemory>(aClonedData, *this);
+}
+
+void StructuredCloneData::WriteIPCParams(IPC::MessageWriter* aWriter) const {
+ WriteParam(aWriter, Data());
+}
+
+bool StructuredCloneData::ReadIPCParams(IPC::MessageReader* aReader) {
+ MOZ_ASSERT(!mInitialized);
+ JSStructuredCloneData data(JS::StructuredCloneScope::DifferentProcess);
+ if (!ReadParam(aReader, &data)) {
+ return false;
+ }
+ mSharedData = new SharedJSAllocatedData(std::move(data));
+ mInitialized = true;
+ return true;
+}
+
+bool StructuredCloneData::CopyExternalData(const char* aData,
+ size_t aDataLength) {
+ MOZ_ASSERT(!mInitialized);
+ mSharedData =
+ SharedJSAllocatedData::CreateFromExternalData(aData, aDataLength);
+ NS_ENSURE_TRUE(mSharedData, false);
+ mInitialized = true;
+ return true;
+}
+
+bool StructuredCloneData::CopyExternalData(const JSStructuredCloneData& aData) {
+ MOZ_ASSERT(!mInitialized);
+ mSharedData = SharedJSAllocatedData::CreateFromExternalData(aData);
+ NS_ENSURE_TRUE(mSharedData, false);
+ mInitialized = true;
+ return true;
+}
+
+bool StructuredCloneData::StealExternalData(JSStructuredCloneData& aData) {
+ MOZ_ASSERT(!mInitialized);
+ mSharedData = new SharedJSAllocatedData(std::move(aData));
+ mInitialized = true;
+ return true;
+}
+
+already_AddRefed<SharedJSAllocatedData> StructuredCloneData::TakeSharedData() {
+ return mSharedData.forget();
+}
+
+} // namespace mozilla::dom::ipc
diff --git a/dom/ipc/StructuredCloneData.h b/dom/ipc/StructuredCloneData.h
new file mode 100644
index 0000000000..0566a3b07f
--- /dev/null
+++ b/dom/ipc/StructuredCloneData.h
@@ -0,0 +1,268 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_ipc_StructuredCloneData_h
+#define mozilla_dom_ipc_StructuredCloneData_h
+
+#include <algorithm>
+#include "mozilla/RefPtr.h"
+#include "mozilla/dom/StructuredCloneHolder.h"
+#include "nsISupportsImpl.h"
+#include "nsIInputStream.h"
+
+namespace IPC {
+class Message;
+class MessageReader;
+class MessageWriter;
+} // namespace IPC
+class PickleIterator;
+
+namespace mozilla {
+namespace ipc {
+
+class PBackgroundChild;
+class PBackgroundParent;
+
+} // namespace ipc
+
+namespace dom {
+
+class ContentChild;
+class ContentParent;
+
+namespace ipc {
+
+/**
+ * Wraps the non-reference-counted JSStructuredCloneData class to have a
+ * reference count so that multiple StructuredCloneData instances can reference
+ * a single underlying serialized representation.
+ *
+ * As used by StructuredCloneData, it is an invariant that our
+ * JSStructuredCloneData owns its buffers. (For the non-owning case,
+ * StructuredCloneData uses mExternalData which holds a BufferList::Borrow()ed
+ * read-only view of the data.)
+ */
+class SharedJSAllocatedData final {
+ public:
+ explicit SharedJSAllocatedData(JSStructuredCloneData&& aData)
+ : mData(std::move(aData)) {}
+
+ static already_AddRefed<SharedJSAllocatedData> CreateFromExternalData(
+ const char* aData, size_t aDataLength) {
+ JSStructuredCloneData buf(JS::StructuredCloneScope::DifferentProcess);
+ NS_ENSURE_TRUE(buf.AppendBytes(aData, aDataLength), nullptr);
+ RefPtr<SharedJSAllocatedData> sharedData =
+ new SharedJSAllocatedData(std::move(buf));
+ return sharedData.forget();
+ }
+
+ static already_AddRefed<SharedJSAllocatedData> CreateFromExternalData(
+ const JSStructuredCloneData& aData) {
+ JSStructuredCloneData buf(aData.scope());
+ NS_ENSURE_TRUE(buf.Append(aData), nullptr);
+ RefPtr<SharedJSAllocatedData> sharedData =
+ new SharedJSAllocatedData(std::move(buf));
+ return sharedData.forget();
+ }
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedJSAllocatedData)
+
+ JSStructuredCloneData& Data() { return mData; }
+ size_t DataLength() const { return mData.Size(); }
+
+ private:
+ ~SharedJSAllocatedData() = default;
+
+ JSStructuredCloneData mData;
+};
+
+/**
+ * IPC-aware StructuredCloneHolder subclass that serves as both a helper class
+ * for dealing with message data (blobs, transferables) and also an IPDL
+ * data-type in cases where message data is not needed. If your use-case does
+ * not (potentially) involve IPC, then you should use StructuredCloneHolder or
+ * one of its other subclasses instead.
+ *
+ * ## Usage ##
+ *
+ * The general recipe for using this class is:
+ * - In your IPDL definition, use the ClonedMessageData type whenever you want
+ * to send a structured clone that may include blobs or transferables such as
+ * message ports.
+ * - To send the data, instantiate a StructuredCloneData instance and Write()
+ * into it like a normal structure clone. When you are ready to send the
+ * ClonedMessageData-bearing IPC message, use the appropriate
+ * BuildClonedMessageDataFor{Parent,Child,BackgroundParent,BackgroundChild}
+ * method to populate the ClonedMessageData and then send it before your
+ * StructuredCloneData instance is destroyed. (Buffer borrowing is used
+ * under-the-hood to avoid duplicating the serialized data, requiring this.)
+ * - To receive the data, instantiate a StructuredCloneData and use the
+ * appropriate {Borrow,Copy,Steal}FromClonedMessageDataFor{Parent,Child,
+ * BackgroundParent,BackgroundChild} method. See the memory management
+ * section for more info.
+ *
+ * Variations:
+ * - If transferables are not allowed (ex: BroadcastChannel), then use the
+ * StructuredCloneDataNoTransfers subclass instead of StructuredCloneData.
+ *
+ * ## Memory Management ##
+ *
+ * Serialized structured clone representations can be quite large. So it's best
+ * to avoid wasteful duplication. When Write()ing into the StructuredCloneData,
+ * you don't need to worry about this[1], but when you already have serialized
+ * structured clone data you plan to Read(), you do need to. Similarly, if
+ * you're using StructuredCloneData as an IPDL type, it efficiently unmarshals.
+ *
+ * The from-ClonedMessageData memory management strategies available are:
+ * - Borrow: Create a JSStructuredCloneData that holds a non-owning, read-only
+ * BufferList::Borrow()ed copy of the source. Your StructuredCloneData needs
+ * to be destroyed before the source is. Commonly used when the
+ * StructuredCloneData instance is stack-allocated (and Read() is used before
+ * the function returns).
+ * - Copy: Makes a reference-counted copy of the source JSStructuredCloneData,
+ * making it safe for the StructuredCloneData to outlive the source data.
+ * - Steal: Steal the buffers from the underlying JSStructuredCloneData so that
+ * it's safe for the StructuredCloneData to outlive the source data. This is
+ * safe to use with IPC-provided ClonedMessageData instances because
+ * JSStructuredCloneData's IPC ParamTraits::Read method copies the relevant
+ * data into owned buffers. But if it's possible the ClonedMessageData came
+ * from a different source that might have borrowed the buffers itself, then
+ * things will crash. That would be a pretty strange implementation; if you
+ * see one, change it to use SharedJSAllocatedData.
+ *
+ * 1: Specifically, in the Write() case an owning SharedJSAllocatedData is
+ * created efficiently (by stealing from StructuredCloneHolder). The
+ * BuildClonedMessageDataFor* method can be called at any time and it will
+ * borrow the underlying memory. While it would be even better if
+ * SerializedStructuredCloneBuffer could hold a SharedJSAllocatedData ref,
+ * there's no reason you can't wait to BuildClonedMessageDataFor* until you
+ * need to make the IPC Send* call.
+ */
+class StructuredCloneData : public StructuredCloneHolder {
+ public:
+ StructuredCloneData();
+
+ StructuredCloneData(const StructuredCloneData&) = delete;
+
+ StructuredCloneData(StructuredCloneData&& aOther);
+
+ // Only DifferentProcess and UnknownDestination scopes are supported.
+ StructuredCloneData(StructuredCloneScope aScope,
+ TransferringSupport aSupportsTransferring);
+
+ ~StructuredCloneData();
+
+ StructuredCloneData& operator=(const StructuredCloneData& aOther) = delete;
+
+ StructuredCloneData& operator=(StructuredCloneData&& aOther);
+
+ const nsTArray<RefPtr<BlobImpl>>& BlobImpls() const { return mBlobImplArray; }
+
+ nsTArray<RefPtr<BlobImpl>>& BlobImpls() { return mBlobImplArray; }
+
+ const nsTArray<nsCOMPtr<nsIInputStream>>& InputStreams() const {
+ return mInputStreamArray;
+ }
+
+ nsTArray<nsCOMPtr<nsIInputStream>>& InputStreams() {
+ return mInputStreamArray;
+ }
+
+ bool Copy(const StructuredCloneData& aData);
+
+ void Read(JSContext* aCx, JS::MutableHandle<JS::Value> aValue,
+ ErrorResult& aRv);
+
+ void Read(JSContext* aCx, JS::MutableHandle<JS::Value> aValue,
+ const JS::CloneDataPolicy& aCloneDataPolicy, ErrorResult& aRv);
+
+ // Write with no transfer objects and with the default CloneDataPolicy. With
+ // a default CloneDataPolicy, read and write will not be considered as part of
+ // the same agent cluster and shared memory objects will not be supported.
+ void Write(JSContext* aCx, JS::Handle<JS::Value> aValue,
+ ErrorResult& aRv) override;
+
+ // The most generic Write method, with tansfers and CloneDataPolicy.
+ void Write(JSContext* aCx, JS::Handle<JS::Value> aValue,
+ JS::Handle<JS::Value> aTransfers,
+ const JS::CloneDataPolicy& aCloneDataPolicy,
+ ErrorResult& aRv) override;
+
+ // Method to convert the structured clone stored in this holder by a previous
+ // call to Write() into ClonedMessageData IPC representation.
+ bool BuildClonedMessageData(ClonedMessageData& aClonedData);
+
+ // Memory-management-strategy-varying methods to initialize this holder from a
+ // ClonedMessageData representation.
+ void BorrowFromClonedMessageData(const ClonedMessageData& aClonedData);
+
+ void CopyFromClonedMessageData(const ClonedMessageData& aClonedData);
+
+ // The steal variant of course takes a non-const ClonedMessageData.
+ void StealFromClonedMessageData(ClonedMessageData& aClonedData);
+
+ // Initialize this instance, borrowing the contents of the given
+ // JSStructuredCloneData. You are responsible for ensuring that this
+ // StructuredCloneData instance is destroyed before aData is destroyed.
+ bool UseExternalData(const JSStructuredCloneData& aData) {
+ auto iter = aData.Start();
+ bool success = false;
+ mExternalData = aData.Borrow(iter, aData.Size(), &success);
+ mInitialized = true;
+ return success;
+ }
+
+ // Initialize this instance by copying the given data that probably came from
+ // nsStructuredClone doing a base64 decode. Don't use this.
+ bool CopyExternalData(const char* aData, size_t aDataLength);
+ // Initialize this instance by copying the contents of an existing
+ // JSStructuredCloneData. Use when this StructuredCloneData instance may
+ // outlive aData.
+ bool CopyExternalData(const JSStructuredCloneData& aData);
+
+ // Initialize this instance by stealing the contents of aData via Move
+ // constructor, clearing the original aData as a side-effect. This is only
+ // safe if aData owns the underlying buffers. This is the case for instances
+ // provided by IPC to Recv calls.
+ bool StealExternalData(JSStructuredCloneData& aData);
+
+ JSStructuredCloneData& Data() {
+ return mSharedData ? mSharedData->Data() : mExternalData;
+ }
+
+ const JSStructuredCloneData& Data() const {
+ return mSharedData ? mSharedData->Data() : mExternalData;
+ }
+
+ void InitScope(JS::StructuredCloneScope aScope) { Data().initScope(aScope); }
+
+ size_t DataLength() const {
+ return mSharedData ? mSharedData->DataLength() : mExternalData.Size();
+ }
+
+ SharedJSAllocatedData* SharedData() const { return mSharedData; }
+
+ bool SupportsTransferring() { return mSupportsTransferring; }
+
+ // For IPC serialization
+ void WriteIPCParams(IPC::MessageWriter* aWriter) const;
+ bool ReadIPCParams(IPC::MessageReader* aReader);
+
+ protected:
+ already_AddRefed<SharedJSAllocatedData> TakeSharedData();
+
+ private:
+ JSStructuredCloneData mExternalData;
+ RefPtr<SharedJSAllocatedData> mSharedData;
+
+ bool mInitialized;
+};
+
+} // namespace ipc
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_ipc_StructuredCloneData_h
diff --git a/dom/ipc/TabContext.cpp b/dom/ipc/TabContext.cpp
new file mode 100644
index 0000000000..4344ec221a
--- /dev/null
+++ b/dom/ipc/TabContext.cpp
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/TabContext.h"
+#include "mozilla/dom/PTabContext.h"
+#include "mozilla/dom/BrowserParent.h"
+#include "mozilla/dom/BrowserChild.h"
+#include "mozilla/StaticPrefs_dom.h"
+#include "nsServiceManagerUtils.h"
+
+using namespace mozilla::dom::ipc;
+using namespace mozilla::layout;
+
+namespace mozilla::dom {
+
+TabContext::TabContext()
+ : mInitialized(false),
+ mChromeOuterWindowID(0),
+ mMaxTouchPoints(0) {}
+
+uint64_t TabContext::ChromeOuterWindowID() const {
+ return mChromeOuterWindowID;
+}
+
+bool TabContext::SetTabContext(const TabContext& aContext) {
+ NS_ENSURE_FALSE(mInitialized, false);
+
+ *this = aContext;
+ mInitialized = true;
+
+ return true;
+}
+
+bool TabContext::UpdateTabContextAfterSwap(const TabContext& aContext) {
+ // This is only used after already initialized.
+ MOZ_ASSERT(mInitialized);
+
+ // The only permissable changes are to mChromeOuterWindowID. All other fields
+ // must match for the change to be accepted.
+
+ mChromeOuterWindowID = aContext.mChromeOuterWindowID;
+ return true;
+}
+
+bool TabContext::SetTabContext(uint64_t aChromeOuterWindowID,
+ uint32_t aMaxTouchPoints) {
+ NS_ENSURE_FALSE(mInitialized, false);
+
+ mInitialized = true;
+ mChromeOuterWindowID = aChromeOuterWindowID;
+ mMaxTouchPoints = aMaxTouchPoints;
+ return true;
+}
+
+IPCTabContext TabContext::AsIPCTabContext() const {
+ return IPCTabContext(
+ FrameIPCTabContext(mChromeOuterWindowID, mMaxTouchPoints));
+}
+
+MaybeInvalidTabContext::MaybeInvalidTabContext(const IPCTabContext& aParams)
+ : mInvalidReason(nullptr) {
+ uint64_t chromeOuterWindowID = 0;
+ uint32_t maxTouchPoints = 0;
+
+ switch (aParams.type()) {
+ case IPCTabContext::TPopupIPCTabContext: {
+ const PopupIPCTabContext& ipcContext = aParams.get_PopupIPCTabContext();
+
+ chromeOuterWindowID = ipcContext.chromeOuterWindowID();
+ break;
+ }
+ case IPCTabContext::TFrameIPCTabContext: {
+ const FrameIPCTabContext& ipcContext = aParams.get_FrameIPCTabContext();
+
+ chromeOuterWindowID = ipcContext.chromeOuterWindowID();
+ maxTouchPoints = ipcContext.maxTouchPoints();
+ break;
+ }
+ default: {
+ MOZ_CRASH();
+ }
+ }
+
+ if (!mTabContext.SetTabContext(chromeOuterWindowID, maxTouchPoints)) {
+ mInvalidReason = "Couldn't initialize TabContext.";
+ }
+}
+
+bool MaybeInvalidTabContext::IsValid() { return mInvalidReason == nullptr; }
+
+const char* MaybeInvalidTabContext::GetInvalidReason() {
+ return mInvalidReason;
+}
+
+const TabContext& MaybeInvalidTabContext::GetTabContext() {
+ if (!IsValid()) {
+ MOZ_CRASH("Can't GetTabContext() if !IsValid().");
+ }
+
+ return mTabContext;
+}
+
+} // namespace mozilla::dom
diff --git a/dom/ipc/TabContext.h b/dom/ipc/TabContext.h
new file mode 100644
index 0000000000..00bf69c2a2
--- /dev/null
+++ b/dom/ipc/TabContext.h
@@ -0,0 +1,171 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_TabContext_h
+#define mozilla_dom_TabContext_h
+
+#include "nsCOMPtr.h"
+#include "mozilla/BasePrincipal.h"
+#include "nsPIDOMWindow.h"
+#include "nsPIWindowRoot.h"
+
+namespace mozilla::dom {
+
+class IPCTabContext;
+
+/**
+ * TabContext encapsulates information about an iframe.
+ *
+ * BrowserParent and BrowserChild both inherit from TabContext, and you can also
+ * have standalone TabContext objects.
+ *
+ * This class is immutable except by calling one of the protected
+ * SetTabContext*() methods (and those methods can only be called once). See
+ * also MutableTabContext.
+ */
+class TabContext {
+ public:
+ TabContext();
+
+ /* (The implicit copy-constructor and operator= are fine.) */
+
+ /**
+ * Generates IPCTabContext of type BrowserFrameIPCTabContext from this
+ * TabContext's information.
+ */
+ IPCTabContext AsIPCTabContext() const;
+
+ uint64_t ChromeOuterWindowID() const;
+
+ uint32_t MaxTouchPoints() const { return mMaxTouchPoints; }
+
+ protected:
+ friend class MaybeInvalidTabContext;
+
+ /**
+ * These protected mutator methods let you modify a TabContext once. Further
+ * attempts to modify a given TabContext will fail (the method will return
+ * false).
+ *
+ * These mutators will also fail if the TabContext was created with anything
+ * other than the no-args constructor.
+ */
+
+ /**
+ * Set this TabContext to match the given TabContext.
+ */
+ bool SetTabContext(const TabContext& aContext);
+
+ bool SetTabContext(uint64_t aChromeOuterWindowID, uint32_t aMaxTouchPoints);
+
+ /**
+ * Modify this TabContext to match the given TabContext. This is a special
+ * case triggered by nsFrameLoader::SwapWithOtherRemoteLoader which may have
+ * caused the owner content to change.
+ *
+ * This special case only allows the field `mChromeOuterWindowID` to be
+ * changed. If any other fields have changed, the update is ignored and
+ * returns false.
+ */
+ bool UpdateTabContextAfterSwap(const TabContext& aContext);
+
+ void SetMaxTouchPoints(uint32_t aMaxTouchPoints) {
+ mMaxTouchPoints = aMaxTouchPoints;
+ }
+
+ private:
+ /**
+ * Has this TabContext been initialized? If so, mutator methods will fail.
+ */
+ bool mInitialized;
+
+ /**
+ * The outerWindowID of the window hosting the remote frameloader.
+ */
+ uint64_t mChromeOuterWindowID;
+
+ /**
+ * Maximum number of touch points.
+ */
+ uint32_t mMaxTouchPoints;
+};
+
+/**
+ * MutableTabContext is the same as MaybeInvalidTabContext, except the mutation
+ * methods are public instead of protected. You can still only call these
+ * mutation methods once on a given object.
+ */
+class MutableTabContext : public TabContext {
+ public:
+ bool SetTabContext(const TabContext& aContext) {
+ return TabContext::SetTabContext(aContext);
+ }
+
+ bool SetTabContext(uint64_t aChromeOuterWindowID, uint32_t aMaxTouchPoints) {
+ return TabContext::SetTabContext(aChromeOuterWindowID, aMaxTouchPoints);
+ }
+};
+
+/**
+ * MaybeInvalidTabContext is a simple class that lets you transform an
+ * IPCTabContext into a TabContext.
+ *
+ * The issue is that an IPCTabContext is not necessarily valid. So to convert
+ * an IPCTabContext into a TabContext, you construct a MaybeInvalidTabContext,
+ * check whether it's valid, and, if so, read out your TabContext.
+ *
+ * Example usage:
+ *
+ * void UseTabContext(const TabContext& aTabContext);
+ *
+ * void CreateTab(const IPCTabContext& aContext) {
+ * MaybeInvalidTabContext tc(aContext);
+ * if (!tc.IsValid()) {
+ * NS_ERROR(nsPrintfCString("Got an invalid IPCTabContext: %s",
+ * tc.GetInvalidReason()));
+ * return;
+ * }
+ * UseTabContext(tc.GetTabContext());
+ * }
+ */
+class MaybeInvalidTabContext {
+ public:
+ /**
+ * This constructor copies the information in aContext and sets IsValid() as
+ * appropriate.
+ */
+ explicit MaybeInvalidTabContext(const IPCTabContext& aContext);
+
+ /**
+ * Was the IPCTabContext we received in our constructor valid?
+ */
+ bool IsValid();
+
+ /**
+ * If IsValid(), this function returns null. Otherwise, it returns a
+ * human-readable string indicating why the IPCTabContext passed to our
+ * constructor was not valid.
+ */
+ const char* GetInvalidReason();
+
+ /**
+ * If IsValid(), this function returns a reference to a TabContext
+ * corresponding to the IPCTabContext passed to our constructor. If
+ * !IsValid(), this function crashes.
+ */
+ const TabContext& GetTabContext();
+
+ private:
+ MaybeInvalidTabContext(const MaybeInvalidTabContext&) = delete;
+ MaybeInvalidTabContext& operator=(const MaybeInvalidTabContext&) = delete;
+
+ const char* mInvalidReason;
+ MutableTabContext mTabContext;
+};
+
+} // namespace mozilla::dom
+
+#endif
diff --git a/dom/ipc/TabMessageTypes.h b/dom/ipc/TabMessageTypes.h
new file mode 100644
index 0000000000..7768c9879c
--- /dev/null
+++ b/dom/ipc/TabMessageTypes.h
@@ -0,0 +1,23 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_TabMessageTypes_h
+#define mozilla_dom_TabMessageTypes_h
+
+#include "mozilla/RefPtr.h"
+
+namespace mozilla::dom {
+
+enum class EmbedderElementEventType {
+ NoEvent,
+ LoadEvent,
+ ErrorEvent,
+ EndGuard_,
+};
+
+} // namespace mozilla::dom
+
+#endif // TABMESSAGE_TYPES_H
diff --git a/dom/ipc/TabMessageUtils.h b/dom/ipc/TabMessageUtils.h
new file mode 100644
index 0000000000..794d3dffa2
--- /dev/null
+++ b/dom/ipc/TabMessageUtils.h
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef TABMESSAGE_UTILS_H
+#define TABMESSAGE_UTILS_H
+
+#include "ipc/EnumSerializer.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/dom/Event.h"
+#include "nsIRemoteTab.h"
+#include "nsPIDOMWindow.h"
+#include "nsCOMPtr.h"
+#include "mozilla/dom/EffectsInfo.h"
+#include "mozilla/layers/LayersMessageUtils.h"
+#include "TabMessageTypes.h"
+#include "X11UndefineNone.h"
+
+namespace IPC {
+
+template <>
+struct ParamTraits<nsSizeMode>
+ : public ContiguousEnumSerializer<nsSizeMode, nsSizeMode_Normal,
+ nsSizeMode_Invalid> {};
+
+template <>
+struct ParamTraits<nsIRemoteTab::NavigationType>
+ : public ContiguousEnumSerializerInclusive<
+ nsIRemoteTab::NavigationType,
+ nsIRemoteTab::NavigationType::NAVIGATE_BACK,
+ nsIRemoteTab::NavigationType::NAVIGATE_URL> {};
+
+template <>
+struct ParamTraits<mozilla::dom::EffectsInfo> {
+ typedef mozilla::dom::EffectsInfo paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mVisibleRect);
+ WriteParam(aWriter, aParam.mRasterScale);
+ WriteParam(aWriter, aParam.mTransformToAncestorScale);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ return ReadParam(aReader, &aResult->mVisibleRect) &&
+ ReadParam(aReader, &aResult->mRasterScale) &&
+ ReadParam(aReader, &aResult->mTransformToAncestorScale);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::WhenToScroll>
+ : public ContiguousEnumSerializerInclusive<
+ mozilla::WhenToScroll, mozilla::WhenToScroll::Always,
+ mozilla::WhenToScroll::IfNotFullyVisible> {};
+
+template <>
+struct ParamTraits<mozilla::ScrollFlags>
+ : public BitFlagsEnumSerializer<mozilla::ScrollFlags,
+ mozilla::ScrollFlags::ALL_BITS> {};
+
+template <>
+struct ParamTraits<mozilla::WhereToScroll> {
+ using paramType = mozilla::WhereToScroll;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mPercentage);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ return ReadParam(aReader, &aResult->mPercentage);
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::ScrollAxis> {
+ typedef mozilla::ScrollAxis paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.mWhereToScroll);
+ WriteParam(aWriter, aParam.mWhenToScroll);
+ WriteParam(aWriter, aParam.mOnlyIfPerceivedScrollableDirection);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ if (!ReadParam(aReader, &aResult->mWhereToScroll)) {
+ return false;
+ }
+ if (!ReadParam(aReader, &aResult->mWhenToScroll)) {
+ return false;
+ }
+
+ // We can't set mOnlyIfPerceivedScrollableDirection directly since it's
+ // a bitfield.
+ bool value;
+ if (!ReadParam(aReader, &value)) {
+ return false;
+ }
+ aResult->mOnlyIfPerceivedScrollableDirection = value;
+
+ return true;
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::dom::EmbedderElementEventType>
+ : public ContiguousEnumSerializer<
+ mozilla::dom::EmbedderElementEventType,
+ mozilla::dom::EmbedderElementEventType::NoEvent,
+ mozilla::dom::EmbedderElementEventType::EndGuard_> {};
+
+} // namespace IPC
+
+#endif // TABMESSAGE_UTILS_H
diff --git a/dom/ipc/URLClassifierChild.h b/dom/ipc/URLClassifierChild.h
new file mode 100644
index 0000000000..b5f0537ddc
--- /dev/null
+++ b/dom/ipc/URLClassifierChild.h
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_URLClassifierChild_h
+#define mozilla_dom_URLClassifierChild_h
+
+#include "mozilla/dom/PURLClassifierChild.h"
+#include "mozilla/dom/PURLClassifierLocalChild.h"
+#include "mozilla/ipc/URIUtils.h"
+#include "mozilla/net/UrlClassifierFeatureResult.h"
+#include "nsIURIClassifier.h"
+#include "nsIUrlClassifierFeature.h"
+
+namespace mozilla::dom {
+
+class URLClassifierChild : public PURLClassifierChild {
+ public:
+ void SetCallback(nsIURIClassifierCallback* aCallback) {
+ mCallback = aCallback;
+ }
+
+ mozilla::ipc::IPCResult Recv__delete__(const Maybe<ClassifierInfo>& aInfo,
+ const nsresult& aResult) {
+ MOZ_ASSERT(mCallback);
+ if (aInfo.isSome()) {
+ mCallback->OnClassifyComplete(aResult, aInfo.ref().list(),
+ aInfo.ref().provider(),
+ aInfo.ref().fullhash());
+ }
+ return IPC_OK();
+ }
+
+ private:
+ nsCOMPtr<nsIURIClassifierCallback> mCallback;
+};
+
+class URLClassifierLocalChild : public PURLClassifierLocalChild {
+ public:
+ void SetFeaturesAndCallback(
+ const nsTArray<RefPtr<nsIUrlClassifierFeature>>& aFeatures,
+ nsIUrlClassifierFeatureCallback* aCallback) {
+ mCallback = aCallback;
+ mFeatures = aFeatures.Clone();
+ }
+
+ mozilla::ipc::IPCResult Recv__delete__(
+ nsTArray<URLClassifierLocalResult>&& aResults) {
+ nsTArray<RefPtr<nsIUrlClassifierFeatureResult>> finalResults;
+
+ nsTArray<URLClassifierLocalResult> results = std::move(aResults);
+ for (URLClassifierLocalResult& result : results) {
+ for (nsIUrlClassifierFeature* feature : mFeatures) {
+ nsAutoCString name;
+ nsresult rv = feature->GetName(name);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ continue;
+ }
+
+ if (result.featureName() != name) {
+ continue;
+ }
+
+ RefPtr<nsIURI> uri = result.uri();
+ if (NS_WARN_IF(!uri)) {
+ continue;
+ }
+
+ RefPtr<net::UrlClassifierFeatureResult> r =
+ new net::UrlClassifierFeatureResult(uri, feature,
+ result.matchingList());
+ finalResults.AppendElement(r);
+ break;
+ }
+ }
+
+ mCallback->OnClassifyComplete(finalResults);
+ return IPC_OK();
+ }
+
+ private:
+ nsCOMPtr<nsIUrlClassifierFeatureCallback> mCallback;
+ nsTArray<RefPtr<nsIUrlClassifierFeature>> mFeatures;
+};
+
+} // namespace mozilla::dom
+
+#endif // mozilla_dom_URLClassifierChild_h
diff --git a/dom/ipc/URLClassifierParent.cpp b/dom/ipc/URLClassifierParent.cpp
new file mode 100644
index 0000000000..d0d2f8f26e
--- /dev/null
+++ b/dom/ipc/URLClassifierParent.cpp
@@ -0,0 +1,186 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "URLClassifierParent.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIUrlClassifierFeature.h"
+#include "nsNetCID.h"
+#include "mozilla/net/UrlClassifierFeatureResult.h"
+#include "mozilla/Unused.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+/////////////////////////////////////////////////////////////////////
+// URLClassifierParent.
+
+NS_IMPL_ISUPPORTS(URLClassifierParent, nsIURIClassifierCallback)
+
+mozilla::ipc::IPCResult URLClassifierParent::StartClassify(
+ nsIPrincipal* aPrincipal, bool* aSuccess) {
+ *aSuccess = false;
+ nsresult rv = NS_OK;
+ // Note that in safe mode, the URL classifier service isn't available, so we
+ // should handle the service not being present gracefully.
+ nsCOMPtr<nsIURIClassifier> uriClassifier =
+ do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv);
+ if (NS_SUCCEEDED(rv)) {
+ rv = uriClassifier->Classify(aPrincipal, this, aSuccess);
+ }
+ if (NS_FAILED(rv) || !*aSuccess) {
+ // We treat the case where we fail to classify and the case where the
+ // classifier returns successfully but doesn't perform a lookup as the
+ // classification not yielding any results, so we just kill the child actor
+ // without ever calling out callback in both cases.
+ // This means that code using this in the child process will only get a hit
+ // on its callback if some classification actually happens.
+ *aSuccess = false;
+ ClassificationFailed();
+ }
+ return IPC_OK();
+}
+
+/////////////////////////////////////////////////////////////////////
+// URLClassifierLocalParent.
+
+namespace {
+
+// This class implements a nsIUrlClassifierFeature on the parent side, starting
+// from an IPC data struct.
+class IPCFeature final : public nsIUrlClassifierFeature {
+ public:
+ NS_DECL_ISUPPORTS
+
+ IPCFeature(nsIURI* aURI, const IPCURLClassifierFeature& aFeature)
+ : mURI(aURI), mIPCFeature(aFeature) {}
+
+ NS_IMETHOD
+ GetName(nsACString& aName) override {
+ aName = mIPCFeature.featureName();
+ return NS_OK;
+ }
+
+ NS_IMETHOD
+ GetTables(nsIUrlClassifierFeature::listType,
+ nsTArray<nsCString>& aTables) override {
+ aTables.AppendElements(mIPCFeature.tables());
+ return NS_OK;
+ }
+
+ NS_IMETHOD
+ HasTable(const nsACString& aTable, nsIUrlClassifierFeature::listType,
+ bool* aResult) override {
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = mIPCFeature.tables().Contains(aTable);
+ return NS_OK;
+ }
+
+ NS_IMETHOD
+ HasHostInPreferences(const nsACString& aHost,
+ nsIUrlClassifierFeature::listType,
+ nsACString& aTableName, bool* aResult) override {
+ NS_ENSURE_ARG_POINTER(aResult);
+ *aResult = false;
+ return NS_OK;
+ }
+
+ NS_IMETHOD
+ GetExceptionHostList(nsACString& aList) override {
+ aList = mIPCFeature.exceptionHostList();
+ return NS_OK;
+ }
+
+ NS_IMETHOD
+ ProcessChannel(nsIChannel* aChannel, const nsTArray<nsCString>& aList,
+ const nsTArray<nsCString>& aHashes,
+ bool* aShouldContinue) override {
+ NS_ENSURE_ARG_POINTER(aShouldContinue);
+ *aShouldContinue = true;
+
+ // Nothing to do here.
+ return NS_OK;
+ }
+
+ NS_IMETHOD
+ GetURIByListType(nsIChannel* aChannel,
+ nsIUrlClassifierFeature::listType aListType,
+ nsIUrlClassifierFeature::URIType* aURIType,
+ nsIURI** aURI) override {
+ NS_ENSURE_ARG_POINTER(aURI);
+
+ // This method should not be called, but we have a URI, let's return it.
+ nsCOMPtr<nsIURI> uri = mURI;
+ uri.forget(aURI);
+ *aURIType = aListType == nsIUrlClassifierFeature::blocklist
+ ? nsIUrlClassifierFeature::URIType::blocklistURI
+ : nsIUrlClassifierFeature::URIType::entitylistURI;
+ return NS_OK;
+ }
+
+ private:
+ ~IPCFeature() = default;
+
+ nsCOMPtr<nsIURI> mURI;
+ IPCURLClassifierFeature mIPCFeature;
+};
+
+NS_IMPL_ISUPPORTS(IPCFeature, nsIUrlClassifierFeature)
+
+} // namespace
+
+NS_IMPL_ISUPPORTS(URLClassifierLocalParent, nsIUrlClassifierFeatureCallback)
+
+mozilla::ipc::IPCResult URLClassifierLocalParent::StartClassify(
+ nsIURI* aURI, const nsTArray<IPCURLClassifierFeature>& aFeatures) {
+ MOZ_ASSERT(aURI);
+
+ nsresult rv = NS_OK;
+ // Note that in safe mode, the URL classifier service isn't available, so we
+ // should handle the service not being present gracefully.
+ nsCOMPtr<nsIURIClassifier> uriClassifier =
+ do_GetService(NS_URICLASSIFIERSERVICE_CONTRACTID, &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ OnClassifyComplete(nsTArray<RefPtr<nsIUrlClassifierFeatureResult>>());
+ return IPC_OK();
+ }
+
+ nsTArray<RefPtr<nsIUrlClassifierFeature>> features;
+ for (const IPCURLClassifierFeature& feature : aFeatures) {
+ features.AppendElement(new IPCFeature(aURI, feature));
+ }
+
+ // Doesn't matter if we pass blocklist, entitylist or any other list.
+ // IPCFeature returns always the same values.
+ rv = uriClassifier->AsyncClassifyLocalWithFeatures(
+ aURI, features, nsIUrlClassifierFeature::blocklist, this);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ OnClassifyComplete(nsTArray<RefPtr<nsIUrlClassifierFeatureResult>>());
+ return IPC_OK();
+ }
+
+ return IPC_OK();
+}
+
+NS_IMETHODIMP
+URLClassifierLocalParent::OnClassifyComplete(
+ const nsTArray<RefPtr<nsIUrlClassifierFeatureResult>>& aResults) {
+ if (mIPCOpen) {
+ nsTArray<URLClassifierLocalResult> ipcResults;
+ for (nsIUrlClassifierFeatureResult* result : aResults) {
+ URLClassifierLocalResult* ipcResult = ipcResults.AppendElement();
+
+ net::UrlClassifierFeatureResult* r =
+ static_cast<net::UrlClassifierFeatureResult*>(result);
+
+ ipcResult->uri() = r->URI();
+ r->Feature()->GetName(ipcResult->featureName());
+ ipcResult->matchingList() = r->List();
+ }
+
+ Unused << Send__delete__(this, ipcResults);
+ }
+ return NS_OK;
+}
diff --git a/dom/ipc/URLClassifierParent.h b/dom/ipc/URLClassifierParent.h
new file mode 100644
index 0000000000..1eb6b2fcce
--- /dev/null
+++ b/dom/ipc/URLClassifierParent.h
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_URLClassifierParent_h
+#define mozilla_dom_URLClassifierParent_h
+
+#include "mozilla/dom/PContent.h"
+#include "mozilla/dom/PURLClassifierParent.h"
+#include "mozilla/dom/PURLClassifierLocalParent.h"
+#include "nsIURIClassifier.h"
+#include "nsIUrlClassifierFeature.h"
+
+namespace mozilla::dom {
+
+class IPCURLClassifierFeature;
+
+//////////////////////////////////////////////////////////////
+// URLClassifierParent
+
+class URLClassifierParent : public nsIURIClassifierCallback,
+ public PURLClassifierParent {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ mozilla::ipc::IPCResult StartClassify(nsIPrincipal* aPrincipal,
+ bool* aSuccess);
+
+ // nsIURIClassifierCallback.
+ NS_IMETHOD OnClassifyComplete(nsresult aErrorCode, const nsACString& aList,
+ const nsACString& aProvider,
+ const nsACString& aFullHash) override {
+ if (mIPCOpen) {
+ ClassifierInfo info = ClassifierInfo(
+ nsCString(aList), nsCString(aProvider), nsCString(aFullHash));
+ Unused << Send__delete__(this, Some(info), aErrorCode);
+ }
+ return NS_OK;
+ }
+
+ // Custom.
+ void ClassificationFailed() {
+ if (mIPCOpen) {
+ Unused << Send__delete__(this, Nothing(), NS_ERROR_FAILURE);
+ }
+ }
+
+ private:
+ ~URLClassifierParent() = default;
+
+ // Override PURLClassifierParent::ActorDestroy. We seem to unable to
+ // override from the base template class.
+ void ActorDestroy(ActorDestroyReason aWhy) override { mIPCOpen = false; }
+
+ bool mIPCOpen = true;
+};
+
+//////////////////////////////////////////////////////////////
+// URLClassifierLocalParent
+
+class URLClassifierLocalParent : public nsIUrlClassifierFeatureCallback,
+ public PURLClassifierLocalParent {
+ public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ mozilla::ipc::IPCResult StartClassify(
+ nsIURI* aURI, const nsTArray<IPCURLClassifierFeature>& aFeatureNames);
+
+ // nsIUrlClassifierFeatureCallback.
+ NS_IMETHOD
+ OnClassifyComplete(
+ const nsTArray<RefPtr<nsIUrlClassifierFeatureResult>>& aResults) override;
+
+ private:
+ ~URLClassifierLocalParent() = default;
+
+ // Override PURLClassifierLocalParent::ActorDestroy.
+ void ActorDestroy(ActorDestroyReason aWhy) override { mIPCOpen = false; }
+
+ bool mIPCOpen = true;
+};
+
+} // namespace mozilla::dom
+
+#endif // mozilla_dom_URLClassifierParent_h
diff --git a/dom/ipc/UserActivationIPCUtils.h b/dom/ipc/UserActivationIPCUtils.h
new file mode 100644
index 0000000000..ebf0aa20c9
--- /dev/null
+++ b/dom/ipc/UserActivationIPCUtils.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_useractivation_ipc_utils_h__
+#define mozilla_dom_useractivation_ipc_utils_h__
+
+#include "ipc/EnumSerializer.h"
+
+// Undo X11/X.h's definition of None
+#undef None
+
+#include "mozilla/dom/UserActivation.h"
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::dom::UserActivation::State>
+ : public ContiguousEnumSerializer<
+ mozilla::dom::UserActivation::State,
+ mozilla::dom::UserActivation::State::None,
+ mozilla::dom::UserActivation::State::EndGuard_> {};
+
+} // namespace IPC
+
+#endif // mozilla_dom_useractivation_ipc_utils_h__
diff --git a/dom/ipc/VsyncChild.h b/dom/ipc/VsyncChild.h
new file mode 100644
index 0000000000..98250e950d
--- /dev/null
+++ b/dom/ipc/VsyncChild.h
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_ipc_VsyncChild_h
+#define mozilla_dom_ipc_VsyncChild_h
+
+#include "mozilla/dom/PVsyncChild.h"
+
+namespace mozilla::dom {
+
+class VsyncChild : public PVsyncChild {
+ friend class PVsyncChild;
+
+ protected:
+ virtual mozilla::ipc::IPCResult RecvNotify(const VsyncEvent& aVsync,
+ const float& aVsyncRate) = 0;
+};
+
+} // namespace mozilla::dom
+
+#endif // mozilla_dom_ipc_VsyncChild_h
diff --git a/dom/ipc/VsyncMainChild.cpp b/dom/ipc/VsyncMainChild.cpp
new file mode 100644
index 0000000000..a1ea332e75
--- /dev/null
+++ b/dom/ipc/VsyncMainChild.cpp
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "VsyncMainChild.h"
+
+#include "mozilla/SchedulerGroup.h"
+#include "mozilla/VsyncDispatcher.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla::dom {
+
+VsyncMainChild::VsyncMainChild()
+ : mIsShutdown(false), mVsyncRate(TimeDuration::Forever()) {
+ MOZ_ASSERT(NS_IsMainThread());
+}
+
+VsyncMainChild::~VsyncMainChild() { MOZ_ASSERT(NS_IsMainThread()); }
+
+void VsyncMainChild::AddChildRefreshTimer(VsyncObserver* aVsyncObserver) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!mObservers.Contains(aVsyncObserver));
+
+ if (mIsShutdown) {
+ return;
+ }
+
+ if (mObservers.IsEmpty()) {
+ Unused << PVsyncChild::SendObserve();
+ }
+ mObservers.AppendElement(std::move(aVsyncObserver));
+}
+
+void VsyncMainChild::RemoveChildRefreshTimer(VsyncObserver* aVsyncObserver) {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (mIsShutdown) {
+ return;
+ }
+
+ if (mObservers.RemoveElement(aVsyncObserver) && mObservers.IsEmpty()) {
+ Unused << PVsyncChild::SendUnobserve();
+ }
+}
+
+void VsyncMainChild::ActorDestroy(ActorDestroyReason aActorDestroyReason) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!mIsShutdown);
+ mIsShutdown = true;
+
+ if (!mObservers.IsEmpty()) {
+ Unused << PVsyncChild::SendUnobserve();
+ }
+ mObservers.Clear();
+}
+
+mozilla::ipc::IPCResult VsyncMainChild::RecvNotify(const VsyncEvent& aVsync,
+ const float& aVsyncRate) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!mIsShutdown);
+
+ mVsyncRate = TimeDuration::FromMilliseconds(aVsyncRate);
+
+ for (RefPtr<VsyncObserver> observer : mObservers.ForwardRange()) {
+ observer->NotifyVsync(aVsync);
+ }
+ return IPC_OK();
+}
+
+TimeDuration VsyncMainChild::GetVsyncRate() { return mVsyncRate; }
+
+} // namespace mozilla::dom
diff --git a/dom/ipc/VsyncMainChild.h b/dom/ipc/VsyncMainChild.h
new file mode 100644
index 0000000000..f5678ab0e9
--- /dev/null
+++ b/dom/ipc/VsyncMainChild.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_ipc_VsyncMainChild_h
+#define mozilla_dom_ipc_VsyncMainChild_h
+
+#include "mozilla/dom/VsyncChild.h"
+#include "nsTObserverArray.h"
+
+namespace mozilla {
+
+class VsyncObserver;
+
+namespace dom {
+
+// The PVsyncChild actor receives a vsync event from the main process and
+// delivers it to the child process. Currently this is restricted to the main
+// thread only. The actor will stay alive until the process dies or its
+// PVsyncParent actor dies.
+class VsyncMainChild final : public VsyncChild {
+ NS_INLINE_DECL_REFCOUNTING(VsyncMainChild, override)
+
+ friend class PVsyncChild;
+
+ public:
+ VsyncMainChild();
+
+ void AddChildRefreshTimer(VsyncObserver* aVsyncObserver);
+ void RemoveChildRefreshTimer(VsyncObserver* aVsyncObserver);
+
+ TimeDuration GetVsyncRate();
+
+ private:
+ ~VsyncMainChild() override;
+
+ mozilla::ipc::IPCResult RecvNotify(const VsyncEvent& aVsync,
+ const float& aVsyncRate) override;
+ void ActorDestroy(ActorDestroyReason aActorDestroyReason) override;
+
+ bool mIsShutdown;
+ TimeDuration mVsyncRate;
+ nsTObserverArray<VsyncObserver*> mObservers;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_ipc_VsyncChild_h
diff --git a/dom/ipc/VsyncParent.cpp b/dom/ipc/VsyncParent.cpp
new file mode 100644
index 0000000000..617e054cd3
--- /dev/null
+++ b/dom/ipc/VsyncParent.cpp
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "VsyncParent.h"
+
+#include "mozilla/Unused.h"
+#include "nsThreadUtils.h"
+#include "nsIThread.h"
+
+namespace mozilla::dom {
+
+VsyncParent::VsyncParent()
+ : mObservingVsync(false),
+ mDestroyed(false),
+ mInitialThread(NS_GetCurrentThread()) {}
+
+void VsyncParent::UpdateVsyncDispatcher(
+ const RefPtr<VsyncDispatcher>& aVsyncDispatcher) {
+ if (aVsyncDispatcher == mVsyncDispatcher) {
+ return;
+ }
+
+ if (mObservingVsync && mVsyncDispatcher) {
+ mVsyncDispatcher->RemoveVsyncObserver(this);
+ }
+ mVsyncDispatcher = aVsyncDispatcher;
+ if (mObservingVsync) {
+ mVsyncDispatcher->AddVsyncObserver(this);
+ }
+}
+
+void VsyncParent::NotifyVsync(const VsyncEvent& aVsync) {
+ if (IsOnInitialThread()) {
+ DispatchVsyncEvent(aVsync);
+ return;
+ }
+
+ // Called on hardware vsync thread. We should post to current ipc thread.
+ nsCOMPtr<nsIRunnable> vsyncEvent = NewRunnableMethod<VsyncEvent>(
+ "dom::VsyncParent::DispatchVsyncEvent", this,
+ &VsyncParent::DispatchVsyncEvent, aVsync);
+ MOZ_ALWAYS_SUCCEEDS(NS_DispatchToThreadQueue(
+ vsyncEvent.forget(), mInitialThread, EventQueuePriority::Vsync));
+}
+
+void VsyncParent::DispatchVsyncEvent(const VsyncEvent& aVsync) {
+ AssertIsOnInitialThread();
+
+ // If we call NotifyVsync() when we handle ActorDestroy() message, we might
+ // still call DispatchVsyncEvent().
+ // Similarly, we might also receive RecvUnobserveVsync() when call
+ // NotifyVsync(). We use mObservingVsync and mDestroyed flags to skip this
+ // notification.
+ if (mObservingVsync && !mDestroyed) {
+ TimeDuration vsyncRate = mVsyncDispatcher->GetVsyncRate();
+ Unused << SendNotify(aVsync, vsyncRate.ToMilliseconds());
+ }
+}
+
+mozilla::ipc::IPCResult VsyncParent::RecvObserve() {
+ AssertIsOnInitialThread();
+ if (!mObservingVsync) {
+ if (mVsyncDispatcher) {
+ mVsyncDispatcher->AddVsyncObserver(this);
+ }
+ mObservingVsync = true;
+ return IPC_OK();
+ }
+ return IPC_FAIL_NO_REASON(this);
+}
+
+mozilla::ipc::IPCResult VsyncParent::RecvUnobserve() {
+ AssertIsOnInitialThread();
+ if (mObservingVsync) {
+ if (mVsyncDispatcher) {
+ mVsyncDispatcher->RemoveVsyncObserver(this);
+ }
+ mObservingVsync = false;
+ return IPC_OK();
+ }
+ return IPC_FAIL_NO_REASON(this);
+}
+
+void VsyncParent::ActorDestroy(ActorDestroyReason aActorDestroyReason) {
+ MOZ_ASSERT(!mDestroyed);
+ AssertIsOnInitialThread();
+ if (mObservingVsync && mVsyncDispatcher) {
+ mVsyncDispatcher->RemoveVsyncObserver(this);
+ }
+ mVsyncDispatcher = nullptr;
+ mDestroyed = true;
+}
+
+bool VsyncParent::IsOnInitialThread() {
+ return NS_GetCurrentThread() == mInitialThread;
+}
+
+void VsyncParent::AssertIsOnInitialThread() { MOZ_ASSERT(IsOnInitialThread()); }
+
+} // namespace mozilla::dom
diff --git a/dom/ipc/VsyncParent.h b/dom/ipc/VsyncParent.h
new file mode 100644
index 0000000000..3fe8a8e5f2
--- /dev/null
+++ b/dom/ipc/VsyncParent.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_ipc_VsyncParent_h
+#define mozilla_dom_ipc_VsyncParent_h
+
+#include "mozilla/dom/PVsyncParent.h"
+#include "mozilla/VsyncDispatcher.h"
+#include "nsCOMPtr.h"
+#include "mozilla/RefPtr.h"
+
+class nsIThread;
+
+namespace mozilla::dom {
+
+// Use PBackground thread in the main process to send vsync notifications to
+// content process. This actor will be released when its parent protocol calls
+// DeallocPVsyncParent().
+class VsyncParent final : public PVsyncParent, public VsyncObserver {
+ friend class PVsyncParent;
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(VsyncParent, override)
+
+ public:
+ VsyncParent();
+ void UpdateVsyncDispatcher(const RefPtr<VsyncDispatcher>& aVsyncDispatcher);
+
+ private:
+ virtual ~VsyncParent() = default;
+
+ void NotifyVsync(const VsyncEvent& aVsync) override;
+ virtual void ActorDestroy(ActorDestroyReason aActorDestroyReason) override;
+
+ mozilla::ipc::IPCResult RecvObserve();
+ mozilla::ipc::IPCResult RecvUnobserve();
+
+ void DispatchVsyncEvent(const VsyncEvent& aVsync);
+ void UpdateVsyncRate();
+
+ bool IsOnInitialThread();
+ void AssertIsOnInitialThread();
+
+ bool mObservingVsync;
+ bool mDestroyed;
+ nsCOMPtr<nsIThread> mInitialThread;
+ RefPtr<VsyncDispatcher> mVsyncDispatcher;
+};
+
+} // namespace mozilla::dom
+
+#endif // mozilla_dom_ipc_VsyncParent_h
diff --git a/dom/ipc/VsyncWorkerChild.cpp b/dom/ipc/VsyncWorkerChild.cpp
new file mode 100644
index 0000000000..470ff73a27
--- /dev/null
+++ b/dom/ipc/VsyncWorkerChild.cpp
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "VsyncWorkerChild.h"
+#include "mozilla/dom/WorkerPrivate.h"
+#include "mozilla/dom/WorkerRef.h"
+#include "mozilla/dom/WorkerScope.h"
+
+namespace mozilla::dom {
+
+VsyncWorkerChild::VsyncWorkerChild() = default;
+
+VsyncWorkerChild::~VsyncWorkerChild() = default;
+
+bool VsyncWorkerChild::Initialize(WorkerPrivate* aWorkerPrivate) {
+ MOZ_ASSERT(aWorkerPrivate);
+ MOZ_ASSERT(!mWorkerRef);
+
+ mWorkerRef =
+ IPCWorkerRef::Create(aWorkerPrivate, "VsyncWorkerChild",
+ [self = RefPtr{this}]() { self->Destroy(); });
+ return !!mWorkerRef;
+}
+
+void VsyncWorkerChild::Destroy() {
+ MOZ_ASSERT_IF(!CanSend(), !mWorkerRef);
+ MOZ_ASSERT_IF(!CanSend(), !mObserving);
+ Send__delete__(this);
+ MOZ_ASSERT(!mWorkerRef);
+ MOZ_ASSERT(!mObserving);
+}
+
+void VsyncWorkerChild::TryObserve() {
+ if (CanSend() && !mObserving) {
+ mObserving = SendObserve();
+ }
+}
+
+void VsyncWorkerChild::TryUnobserve() {
+ if (CanSend() && mObserving) {
+ mObserving = !SendUnobserve();
+ }
+}
+
+mozilla::ipc::IPCResult VsyncWorkerChild::RecvNotify(const VsyncEvent& aVsync,
+ const float& aVsyncRate) {
+ // For MOZ_CAN_RUN_SCRIPT_BOUNDARY purposes: We know IPDL is explicitly
+ // keeping our actor alive until it is done processing the messages. We know
+ // that the WorkerGlobalScope callee is responsible keeping itself alive
+ // during the OnVsync callback.
+ WorkerPrivate* workerPrivate = mWorkerRef->Private();
+ WorkerGlobalScope* scope = workerPrivate->GlobalScope();
+ if (scope) {
+ scope->OnVsync(aVsync);
+ }
+ return IPC_OK();
+}
+
+void VsyncWorkerChild::ActorDestroy(ActorDestroyReason aActorDestroyReason) {
+ mWorkerRef = nullptr;
+ mObserving = false;
+}
+
+} // namespace mozilla::dom
diff --git a/dom/ipc/VsyncWorkerChild.h b/dom/ipc/VsyncWorkerChild.h
new file mode 100644
index 0000000000..e34555936d
--- /dev/null
+++ b/dom/ipc/VsyncWorkerChild.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_ipc_VsyncWorkerChild_h
+#define mozilla_dom_ipc_VsyncWorkerChild_h
+
+#include "mozilla/dom/VsyncChild.h"
+#include "mozilla/RefPtr.h"
+#include "nsISupportsImpl.h"
+
+namespace mozilla::dom {
+class DedicatedWorkerGlobalScope;
+class IPCWorkerRef;
+class WorkerPrivate;
+
+// The PVsyncWorkerChild actor receives a vsync event from the main process and
+// delivers it to the child process. This is specific for workers which wish to
+// subscribe to vsync events to drive requestAnimationFrame callbacks.
+class VsyncWorkerChild final : public VsyncChild {
+ NS_INLINE_DECL_REFCOUNTING(VsyncWorkerChild, override)
+
+ friend class PVsyncChild;
+
+ public:
+ VsyncWorkerChild();
+
+ bool Initialize(WorkerPrivate* aWorkerPrivate);
+
+ void Destroy();
+
+ void TryObserve();
+
+ void TryUnobserve();
+
+ private:
+ ~VsyncWorkerChild() override;
+
+ mozilla::ipc::IPCResult RecvNotify(const VsyncEvent& aVsync,
+ const float& aVsyncRate) override;
+
+ void ActorDestroy(ActorDestroyReason aActorDestroyReason) override;
+
+ RefPtr<IPCWorkerRef> mWorkerRef;
+ bool mObserving = false;
+};
+
+} // namespace mozilla::dom
+
+#endif // mozilla_dom_ipc_VsyncWorkerChild_h
diff --git a/dom/ipc/WindowGlobalActor.cpp b/dom/ipc/WindowGlobalActor.cpp
new file mode 100644
index 0000000000..20dd775bad
--- /dev/null
+++ b/dom/ipc/WindowGlobalActor.cpp
@@ -0,0 +1,191 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/WindowGlobalActor.h"
+
+#include "AutoplayPolicy.h"
+#include "nsContentUtils.h"
+#include "mozilla/Components.h"
+#include "mozilla/ContentBlockingAllowList.h"
+#include "mozilla/Logging.h"
+#include "mozilla/dom/Document.h"
+#include "mozilla/dom/JSActorService.h"
+#include "mozilla/dom/JSWindowActorParent.h"
+#include "mozilla/dom/JSWindowActorChild.h"
+#include "mozilla/dom/JSWindowActorProtocol.h"
+#include "mozilla/dom/PopupBlocker.h"
+#include "mozilla/net/CookieJarSettings.h"
+#include "mozilla/dom/WindowContext.h"
+#include "mozilla/dom/WindowGlobalChild.h"
+#include "mozilla/dom/WindowGlobalParent.h"
+
+#include "nsGlobalWindowInner.h"
+#include "nsNetUtil.h"
+
+namespace mozilla::dom {
+
+// CORPP 3.1.3 https://mikewest.github.io/corpp/#integration-html
+static nsILoadInfo::CrossOriginEmbedderPolicy InheritedPolicy(
+ dom::BrowsingContext* aBrowsingContext) {
+ WindowContext* inherit = aBrowsingContext->GetParentWindowContext();
+ if (inherit) {
+ return inherit->GetEmbedderPolicy();
+ }
+
+ return nsILoadInfo::EMBEDDER_POLICY_NULL;
+}
+
+// Common WindowGlobalInit creation code used by both `AboutBlankInitializer`
+// and `WindowInitializer`.
+WindowGlobalInit WindowGlobalActor::BaseInitializer(
+ dom::BrowsingContext* aBrowsingContext, uint64_t aInnerWindowId,
+ uint64_t aOuterWindowId) {
+ MOZ_DIAGNOSTIC_ASSERT(aBrowsingContext);
+
+ using Indexes = WindowContext::FieldIndexes;
+
+ WindowGlobalInit init;
+ auto& ctx = init.context();
+ ctx.mInnerWindowId = aInnerWindowId;
+ ctx.mOuterWindowId = aOuterWindowId;
+ ctx.mBrowsingContextId = aBrowsingContext->Id();
+
+ // If any synced fields need to be initialized from our BrowsingContext, we
+ // can initialize them here.
+ auto& fields = ctx.mFields;
+ fields.Get<Indexes::IDX_EmbedderPolicy>() = InheritedPolicy(aBrowsingContext);
+ fields.Get<Indexes::IDX_AutoplayPermission>() =
+ nsIPermissionManager::UNKNOWN_ACTION;
+ fields.Get<Indexes::IDX_AllowJavascript>() = true;
+ return init;
+}
+
+WindowGlobalInit WindowGlobalActor::AboutBlankInitializer(
+ dom::BrowsingContext* aBrowsingContext, nsIPrincipal* aPrincipal) {
+ WindowGlobalInit init =
+ BaseInitializer(aBrowsingContext, nsContentUtils::GenerateWindowId(),
+ nsContentUtils::GenerateWindowId());
+
+ init.principal() = aPrincipal;
+ init.storagePrincipal() = aPrincipal;
+ Unused << NS_NewURI(getter_AddRefs(init.documentURI()), "about:blank");
+ init.isInitialDocument() = true;
+
+ return init;
+}
+
+WindowGlobalInit WindowGlobalActor::WindowInitializer(
+ nsGlobalWindowInner* aWindow) {
+ WindowGlobalInit init =
+ BaseInitializer(aWindow->GetBrowsingContext(), aWindow->WindowID(),
+ aWindow->GetOuterWindow()->WindowID());
+
+ init.principal() = aWindow->GetPrincipal();
+ init.storagePrincipal() = aWindow->GetEffectiveStoragePrincipal();
+ init.documentURI() = aWindow->GetDocumentURI();
+
+ Document* doc = aWindow->GetDocument();
+
+ init.isInitialDocument() = doc->IsInitialDocument();
+ init.blockAllMixedContent() = doc->GetBlockAllMixedContent(false);
+ init.upgradeInsecureRequests() = doc->GetUpgradeInsecureRequests(false);
+ init.sandboxFlags() = doc->GetSandboxFlags();
+ net::CookieJarSettings::Cast(doc->CookieJarSettings())
+ ->Serialize(init.cookieJarSettings());
+ init.httpsOnlyStatus() = doc->HttpsOnlyStatus();
+
+ using Indexes = WindowContext::FieldIndexes;
+
+ auto& fields = init.context().mFields;
+ fields.Get<Indexes::IDX_CookieBehavior>() =
+ Some(doc->CookieJarSettings()->GetCookieBehavior());
+ fields.Get<Indexes::IDX_IsOnContentBlockingAllowList>() =
+ doc->CookieJarSettings()->GetIsOnContentBlockingAllowList();
+ fields.Get<Indexes::IDX_IsThirdPartyWindow>() = doc->HasThirdPartyChannel();
+ fields.Get<Indexes::IDX_IsThirdPartyTrackingResourceWindow>() =
+ nsContentUtils::IsThirdPartyTrackingResourceWindow(aWindow);
+ fields.Get<Indexes::IDX_ShouldResistFingerprinting>() =
+ doc->ShouldResistFingerprinting(RFPTarget::IsAlwaysEnabledForPrecompute);
+ fields.Get<Indexes::IDX_OverriddenFingerprintingSettings>() =
+ doc->GetOverriddenFingerprintingSettings();
+ fields.Get<Indexes::IDX_IsSecureContext>() = aWindow->IsSecureContext();
+
+ // Initialze permission fields
+ fields.Get<Indexes::IDX_AutoplayPermission>() =
+ media::AutoplayPolicy::GetSiteAutoplayPermission(init.principal());
+ fields.Get<Indexes::IDX_PopupPermission>() =
+ PopupBlocker::GetPopupPermission(init.principal());
+
+ // Initialize top level permission fields
+ if (aWindow->GetBrowsingContext()->IsTop()) {
+ fields.Get<Indexes::IDX_AllowMixedContent>() = [&] {
+ uint32_t permit = nsIPermissionManager::UNKNOWN_ACTION;
+ nsCOMPtr<nsIPermissionManager> permissionManager =
+ components::PermissionManager::Service();
+
+ if (permissionManager) {
+ permissionManager->TestPermissionFromPrincipal(
+ init.principal(), "mixed-content"_ns, &permit);
+ }
+
+ return permit == nsIPermissionManager::ALLOW_ACTION;
+ }();
+
+ fields.Get<Indexes::IDX_ShortcutsPermission>() =
+ nsGlobalWindowInner::GetShortcutsPermission(init.principal());
+ }
+
+ if (auto policy = doc->GetEmbedderPolicy()) {
+ fields.Get<Indexes::IDX_EmbedderPolicy>() = *policy;
+ }
+
+ // Init Mixed Content Fields
+ nsCOMPtr<nsIURI> innerDocURI = NS_GetInnermostURI(doc->GetDocumentURI());
+ fields.Get<Indexes::IDX_IsSecure>() =
+ innerDocURI && innerDocURI->SchemeIs("https");
+
+ nsCOMPtr<nsITransportSecurityInfo> securityInfo;
+ if (nsCOMPtr<nsIChannel> channel = doc->GetChannel()) {
+ nsCOMPtr<nsILoadInfo> loadInfo(channel->LoadInfo());
+ fields.Get<Indexes::IDX_IsOriginalFrameSource>() =
+ loadInfo->GetOriginalFrameSrcLoad();
+ fields.Get<Indexes::IDX_UsingStorageAccess>() =
+ loadInfo->GetStoragePermission() != nsILoadInfo::NoStoragePermission;
+
+ channel->GetSecurityInfo(getter_AddRefs(securityInfo));
+ }
+ init.securityInfo() = securityInfo;
+
+ fields.Get<Indexes::IDX_IsLocalIP>() =
+ init.principal()->GetIsLocalIpAddress();
+
+ // Most data here is specific to the Document, which can change without
+ // creating a new WindowGlobal. Anything new added here which fits that
+ // description should also be synchronized in
+ // WindowGlobalChild::OnNewDocument.
+ return init;
+}
+
+already_AddRefed<JSActorProtocol> WindowGlobalActor::MatchingJSActorProtocol(
+ JSActorService* aActorSvc, const nsACString& aName, ErrorResult& aRv) {
+ RefPtr<JSWindowActorProtocol> proto =
+ aActorSvc->GetJSWindowActorProtocol(aName);
+ if (!proto) {
+ aRv.ThrowNotFoundError(nsPrintfCString("No such JSWindowActor '%s'",
+ PromiseFlatCString(aName).get()));
+ return nullptr;
+ }
+
+ if (!proto->Matches(BrowsingContext(), GetDocumentURI(), GetRemoteType(),
+ aRv)) {
+ MOZ_ASSERT(aRv.Failed());
+ return nullptr;
+ }
+ MOZ_ASSERT(!aRv.Failed());
+ return proto.forget();
+}
+
+} // namespace mozilla::dom
diff --git a/dom/ipc/WindowGlobalActor.h b/dom/ipc/WindowGlobalActor.h
new file mode 100644
index 0000000000..7ab19c292e
--- /dev/null
+++ b/dom/ipc/WindowGlobalActor.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_WindowGlobalActor_h
+#define mozilla_dom_WindowGlobalActor_h
+
+#include "nsWrapperCache.h"
+#include "nsISupports.h"
+#include "mozilla/dom/BrowsingContext.h"
+#include "nsIURI.h"
+#include "nsString.h"
+#include "mozilla/dom/JSActor.h"
+#include "mozilla/dom/JSActorManager.h"
+#include "mozilla/dom/WindowGlobalTypes.h"
+
+namespace mozilla {
+class ErrorResult;
+
+namespace dom {
+
+// Common base class for WindowGlobal{Parent, Child}.
+class WindowGlobalActor : public JSActorManager {
+ public:
+ // Called to determine initial state for a window global actor created for an
+ // initial about:blank document.
+ static WindowGlobalInit AboutBlankInitializer(
+ dom::BrowsingContext* aBrowsingContext, nsIPrincipal* aPrincipal);
+
+ // Called to determine initial state for a window global actor created for a
+ // specific existing nsGlobalWindowInner.
+ static WindowGlobalInit WindowInitializer(nsGlobalWindowInner* aWindow);
+
+ protected:
+ virtual ~WindowGlobalActor() = default;
+
+ already_AddRefed<JSActorProtocol> MatchingJSActorProtocol(
+ JSActorService* aActorSvc, const nsACString& aName,
+ ErrorResult& aRv) final;
+
+ virtual nsIURI* GetDocumentURI() = 0;
+ virtual const nsACString& GetRemoteType() = 0;
+ virtual dom::BrowsingContext* BrowsingContext() = 0;
+
+ static WindowGlobalInit BaseInitializer(
+ dom::BrowsingContext* aBrowsingContext, uint64_t aInnerWindowId,
+ uint64_t aOuterWindowId);
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_WindowGlobalActor_h
diff --git a/dom/ipc/WindowGlobalChild.cpp b/dom/ipc/WindowGlobalChild.cpp
new file mode 100644
index 0000000000..ec2811f972
--- /dev/null
+++ b/dom/ipc/WindowGlobalChild.cpp
@@ -0,0 +1,893 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/WindowGlobalChild.h"
+
+#include "mozilla/AntiTrackingUtils.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/dom/WindowGlobalParent.h"
+#include "mozilla/dom/BrowsingContext.h"
+#include "mozilla/dom/BrowsingContextGroup.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/MozFrameLoaderOwnerBinding.h"
+#include "mozilla/dom/BrowserChild.h"
+#include "mozilla/dom/BrowserBridgeChild.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/SecurityPolicyViolationEvent.h"
+#include "mozilla/dom/SessionStoreRestoreData.h"
+#include "mozilla/dom/WindowGlobalActorsBinding.h"
+#include "mozilla/dom/WindowContext.h"
+#include "mozilla/dom/InProcessChild.h"
+#include "mozilla/dom/InProcessParent.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/PresShell.h"
+#include "mozilla/ScopeExit.h"
+#include "GeckoProfiler.h"
+#include "nsContentUtils.h"
+#include "nsDocShell.h"
+#include "nsFocusManager.h"
+#include "nsFrameLoaderOwner.h"
+#include "nsGlobalWindowInner.h"
+#include "nsNetUtil.h"
+#include "nsQueryObject.h"
+#include "nsSerializationHelper.h"
+#include "nsFrameLoader.h"
+#include "nsIScriptSecurityManager.h"
+
+#include "mozilla/dom/JSWindowActorBinding.h"
+#include "mozilla/dom/JSWindowActorChild.h"
+#include "mozilla/dom/JSActorService.h"
+#include "nsIHttpChannelInternal.h"
+#include "nsIURIMutator.h"
+#include "nsURLHelper.h"
+
+using namespace mozilla::ipc;
+using namespace mozilla::dom::ipc;
+
+namespace mozilla::dom {
+
+WindowGlobalChild::WindowGlobalChild(dom::WindowContext* aWindowContext,
+ nsIPrincipal* aPrincipal,
+ nsIURI* aDocumentURI)
+ : mWindowContext(aWindowContext),
+ mDocumentPrincipal(aPrincipal),
+ mDocumentURI(aDocumentURI) {
+ MOZ_DIAGNOSTIC_ASSERT(mWindowContext);
+ MOZ_DIAGNOSTIC_ASSERT(mDocumentPrincipal);
+
+ if (!mDocumentURI) {
+ NS_NewURI(getter_AddRefs(mDocumentURI), "about:blank");
+ }
+
+ // Registers a DOM Window with the profiler. It re-registers the same Inner
+ // Window ID with different URIs because when a Browsing context is first
+ // loaded, the first url loaded in it will be about:blank. This call keeps the
+ // first non-about:blank registration of window and discards the previous one.
+ uint64_t embedderInnerWindowID = 0;
+ if (BrowsingContext()->GetParent()) {
+ embedderInnerWindowID = BrowsingContext()->GetEmbedderInnerWindowId();
+ }
+ profiler_register_page(
+ BrowsingContext()->BrowserId(), InnerWindowId(),
+ nsContentUtils::TruncatedURLForDisplay(aDocumentURI, 1024),
+ embedderInnerWindowID, BrowsingContext()->UsePrivateBrowsing());
+}
+
+already_AddRefed<WindowGlobalChild> WindowGlobalChild::Create(
+ nsGlobalWindowInner* aWindow) {
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ // Opener policy is set when we start to load a document. Here, we ensure we
+ // have set the correct Opener policy so that it will be available in the
+ // parent process through window global child.
+ nsCOMPtr<nsIChannel> chan = aWindow->GetDocument()->GetChannel();
+ nsCOMPtr<nsILoadInfo> loadInfo = chan ? chan->LoadInfo() : nullptr;
+ nsCOMPtr<nsIHttpChannelInternal> httpChan = do_QueryInterface(chan);
+ nsILoadInfo::CrossOriginOpenerPolicy policy;
+ if (httpChan &&
+ loadInfo->GetExternalContentPolicyType() ==
+ ExtContentPolicy::TYPE_DOCUMENT &&
+ NS_SUCCEEDED(httpChan->GetCrossOriginOpenerPolicy(&policy))) {
+ MOZ_DIAGNOSTIC_ASSERT(policy ==
+ aWindow->GetBrowsingContext()->GetOpenerPolicy());
+ }
+#endif
+
+ WindowGlobalInit init = WindowGlobalActor::WindowInitializer(aWindow);
+ RefPtr<WindowGlobalChild> wgc = CreateDisconnected(init);
+
+ // Send the link constructor over PBrowser, or link over PInProcess.
+ if (XRE_IsParentProcess()) {
+ InProcessChild* ipChild = InProcessChild::Singleton();
+ InProcessParent* ipParent = InProcessParent::Singleton();
+ if (!ipChild || !ipParent) {
+ return nullptr;
+ }
+
+ ManagedEndpoint<PWindowGlobalParent> endpoint =
+ ipChild->OpenPWindowGlobalEndpoint(wgc);
+ ipParent->BindPWindowGlobalEndpoint(std::move(endpoint),
+ wgc->WindowContext()->Canonical());
+ } else {
+ RefPtr<BrowserChild> browserChild =
+ BrowserChild::GetFrom(static_cast<mozIDOMWindow*>(aWindow));
+ MOZ_ASSERT(browserChild);
+
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ dom::BrowsingContext* bc = aWindow->GetBrowsingContext();
+#endif
+
+ MOZ_DIAGNOSTIC_ASSERT(bc->AncestorsAreCurrent());
+ MOZ_DIAGNOSTIC_ASSERT(bc->IsInProcess());
+
+ ManagedEndpoint<PWindowGlobalParent> endpoint =
+ browserChild->OpenPWindowGlobalEndpoint(wgc);
+ browserChild->SendNewWindowGlobal(std::move(endpoint), init);
+ }
+
+ wgc->Init();
+ wgc->InitWindowGlobal(aWindow);
+ return wgc.forget();
+}
+
+already_AddRefed<WindowGlobalChild> WindowGlobalChild::CreateDisconnected(
+ const WindowGlobalInit& aInit) {
+ RefPtr<dom::BrowsingContext> browsingContext =
+ dom::BrowsingContext::Get(aInit.context().mBrowsingContextId);
+
+ RefPtr<dom::WindowContext> windowContext =
+ dom::WindowContext::GetById(aInit.context().mInnerWindowId);
+ MOZ_RELEASE_ASSERT(!windowContext, "Creating duplicate WindowContext");
+
+ // Create our new WindowContext
+ if (XRE_IsParentProcess()) {
+ windowContext = WindowGlobalParent::CreateDisconnected(aInit);
+ } else {
+ dom::WindowContext::FieldValues fields = aInit.context().mFields;
+ windowContext = new dom::WindowContext(
+ browsingContext, aInit.context().mInnerWindowId,
+ aInit.context().mOuterWindowId, std::move(fields));
+ }
+
+ RefPtr<WindowGlobalChild> windowChild = new WindowGlobalChild(
+ windowContext, aInit.principal(), aInit.documentURI());
+ windowContext->mIsInProcess = true;
+ windowContext->mWindowGlobalChild = windowChild;
+ return windowChild.forget();
+}
+
+void WindowGlobalChild::Init() {
+ MOZ_ASSERT(mWindowContext->mWindowGlobalChild == this);
+ mWindowContext->Init();
+}
+
+void WindowGlobalChild::InitWindowGlobal(nsGlobalWindowInner* aWindow) {
+ mWindowGlobal = aWindow;
+}
+
+void WindowGlobalChild::OnNewDocument(Document* aDocument) {
+ MOZ_RELEASE_ASSERT(mWindowGlobal);
+ MOZ_RELEASE_ASSERT(aDocument);
+
+ // Send a series of messages to update document-specific state on
+ // WindowGlobalParent, when we change documents on an existing WindowGlobal.
+ // This data is also all sent when we construct a WindowGlobal, so anything
+ // added here should also be added to WindowGlobalActor::WindowInitializer.
+
+ // FIXME: Perhaps these should be combined into a smaller number of messages?
+ SendSetIsInitialDocument(aDocument->IsInitialDocument());
+ SetDocumentURI(aDocument->GetDocumentURI());
+ SetDocumentPrincipal(aDocument->NodePrincipal(),
+ aDocument->EffectiveStoragePrincipal());
+
+ nsCOMPtr<nsITransportSecurityInfo> securityInfo;
+ if (nsCOMPtr<nsIChannel> channel = aDocument->GetChannel()) {
+ channel->GetSecurityInfo(getter_AddRefs(securityInfo));
+ }
+ SendUpdateDocumentSecurityInfo(securityInfo);
+
+ SendUpdateDocumentCspSettings(aDocument->GetBlockAllMixedContent(false),
+ aDocument->GetUpgradeInsecureRequests(false));
+ SendUpdateSandboxFlags(aDocument->GetSandboxFlags());
+
+ net::CookieJarSettingsArgs csArgs;
+ net::CookieJarSettings::Cast(aDocument->CookieJarSettings())
+ ->Serialize(csArgs);
+ if (!SendUpdateCookieJarSettings(csArgs)) {
+ NS_WARNING(
+ "Failed to update document's cookie jar settings on the "
+ "WindowGlobalParent");
+ }
+
+ SendUpdateHttpsOnlyStatus(aDocument->HttpsOnlyStatus());
+
+ // Update window context fields for the newly loaded Document.
+ WindowContext::Transaction txn;
+ txn.SetCookieBehavior(
+ Some(aDocument->CookieJarSettings()->GetCookieBehavior()));
+ txn.SetIsOnContentBlockingAllowList(
+ aDocument->CookieJarSettings()->GetIsOnContentBlockingAllowList());
+ txn.SetIsThirdPartyWindow(aDocument->HasThirdPartyChannel());
+ txn.SetIsThirdPartyTrackingResourceWindow(
+ nsContentUtils::IsThirdPartyTrackingResourceWindow(mWindowGlobal));
+ txn.SetIsSecureContext(mWindowGlobal->IsSecureContext());
+ if (auto policy = aDocument->GetEmbedderPolicy()) {
+ txn.SetEmbedderPolicy(*policy);
+ }
+ txn.SetShouldResistFingerprinting(aDocument->ShouldResistFingerprinting(
+ RFPTarget::IsAlwaysEnabledForPrecompute));
+ txn.SetOverriddenFingerprintingSettings(
+ aDocument->GetOverriddenFingerprintingSettings());
+
+ if (nsCOMPtr<nsIChannel> channel = aDocument->GetChannel()) {
+ nsCOMPtr<nsILoadInfo> loadInfo(channel->LoadInfo());
+ txn.SetIsOriginalFrameSource(loadInfo->GetOriginalFrameSrcLoad());
+ txn.SetUsingStorageAccess(loadInfo->GetStoragePermission() !=
+ nsILoadInfo::NoStoragePermission);
+ } else {
+ txn.SetIsOriginalFrameSource(false);
+ }
+
+ // Init Mixed Content Fields
+ nsCOMPtr<nsIURI> innerDocURI =
+ NS_GetInnermostURI(aDocument->GetDocumentURI());
+ if (innerDocURI) {
+ txn.SetIsSecure(innerDocURI->SchemeIs("https"));
+ }
+
+ MOZ_DIAGNOSTIC_ASSERT(mDocumentPrincipal->GetIsLocalIpAddress() ==
+ mWindowContext->IsLocalIP());
+
+ MOZ_ALWAYS_SUCCEEDS(txn.Commit(mWindowContext));
+}
+
+/* static */
+already_AddRefed<WindowGlobalChild> WindowGlobalChild::GetByInnerWindowId(
+ uint64_t aInnerWindowId) {
+ if (RefPtr<dom::WindowContext> context =
+ dom::WindowContext::GetById(aInnerWindowId)) {
+ return do_AddRef(context->GetWindowGlobalChild());
+ }
+ return nullptr;
+}
+
+dom::BrowsingContext* WindowGlobalChild::BrowsingContext() {
+ return mWindowContext->GetBrowsingContext();
+}
+
+uint64_t WindowGlobalChild::InnerWindowId() {
+ return mWindowContext->InnerWindowId();
+}
+
+uint64_t WindowGlobalChild::OuterWindowId() {
+ return mWindowContext->OuterWindowId();
+}
+
+bool WindowGlobalChild::IsCurrentGlobal() {
+ return CanSend() && mWindowGlobal->IsCurrentInnerWindow();
+}
+
+already_AddRefed<WindowGlobalParent> WindowGlobalChild::GetParentActor() {
+ if (!CanSend()) {
+ return nullptr;
+ }
+ IProtocol* otherSide = InProcessChild::ParentActorFor(this);
+ return do_AddRef(static_cast<WindowGlobalParent*>(otherSide));
+}
+
+already_AddRefed<BrowserChild> WindowGlobalChild::GetBrowserChild() {
+ if (IsInProcess() || !CanSend()) {
+ return nullptr;
+ }
+ return do_AddRef(static_cast<BrowserChild*>(Manager()));
+}
+
+uint64_t WindowGlobalChild::ContentParentId() {
+ if (XRE_IsParentProcess()) {
+ return 0;
+ }
+ return ContentChild::GetSingleton()->GetID();
+}
+
+// A WindowGlobalChild is the root in its process if it has no parent, or its
+// embedder is in a different process.
+bool WindowGlobalChild::IsProcessRoot() {
+ if (!BrowsingContext()->GetParent()) {
+ return true;
+ }
+
+ return !BrowsingContext()->GetEmbedderElement();
+}
+
+void WindowGlobalChild::BeforeUnloadAdded() {
+ // Don't bother notifying the parent if we don't have an IPC link open.
+ if (mBeforeUnloadListeners == 0 && CanSend()) {
+ Unused << mWindowContext->SetHasBeforeUnload(true);
+ }
+
+ mBeforeUnloadListeners++;
+ MOZ_ASSERT(mBeforeUnloadListeners > 0);
+}
+
+void WindowGlobalChild::BeforeUnloadRemoved() {
+ mBeforeUnloadListeners--;
+ MOZ_ASSERT(mBeforeUnloadListeners >= 0);
+
+ if (mBeforeUnloadListeners == 0) {
+ Unused << mWindowContext->SetHasBeforeUnload(false);
+ }
+}
+
+void WindowGlobalChild::Destroy() {
+ JSActorWillDestroy();
+
+ mWindowContext->Discard();
+
+ // Perform async IPC shutdown unless we're not in-process, and our
+ // BrowserChild is in the process of being destroyed, which will destroy
+ // us as well.
+ RefPtr<BrowserChild> browserChild = GetBrowserChild();
+ if (!browserChild || !browserChild->IsDestroyed()) {
+ SendDestroy();
+ }
+}
+
+mozilla::ipc::IPCResult WindowGlobalChild::RecvMakeFrameLocal(
+ const MaybeDiscarded<dom::BrowsingContext>& aFrameContext,
+ uint64_t aPendingSwitchId) {
+ MOZ_DIAGNOSTIC_ASSERT(XRE_IsContentProcess());
+
+ MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
+ ("RecvMakeFrameLocal ID=%" PRIx64, aFrameContext.ContextId()));
+
+ if (NS_WARN_IF(aFrameContext.IsNullOrDiscarded())) {
+ return IPC_OK();
+ }
+ dom::BrowsingContext* frameContext = aFrameContext.get();
+
+ RefPtr<Element> embedderElt = frameContext->GetEmbedderElement();
+ if (NS_WARN_IF(!embedderElt)) {
+ return IPC_OK();
+ }
+
+ if (NS_WARN_IF(embedderElt->GetOwnerGlobal() != GetWindowGlobal())) {
+ return IPC_OK();
+ }
+
+ RefPtr<nsFrameLoaderOwner> flo = do_QueryObject(embedderElt);
+ MOZ_DIAGNOSTIC_ASSERT(flo, "Embedder must be a nsFrameLoaderOwner");
+
+ // Trigger a process switch into the current process.
+ RemotenessOptions options;
+ options.mRemoteType = NOT_REMOTE_TYPE;
+ options.mPendingSwitchID.Construct(aPendingSwitchId);
+ options.mSwitchingInProgressLoad = true;
+ flo->ChangeRemoteness(options, IgnoreErrors());
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult WindowGlobalChild::RecvMakeFrameRemote(
+ const MaybeDiscarded<dom::BrowsingContext>& aFrameContext,
+ ManagedEndpoint<PBrowserBridgeChild>&& aEndpoint, const TabId& aTabId,
+ const LayersId& aLayersId, MakeFrameRemoteResolver&& aResolve) {
+ MOZ_DIAGNOSTIC_ASSERT(XRE_IsContentProcess());
+
+ MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
+ ("RecvMakeFrameRemote ID=%" PRIx64, aFrameContext.ContextId()));
+
+ if (!aLayersId.IsValid()) {
+ return IPC_FAIL(this, "Received an invalid LayersId");
+ }
+
+ // Resolve the promise when this function exits, as we'll have fully unloaded
+ // at that point.
+ auto scopeExit = MakeScopeExit([&] { aResolve(true); });
+
+ // Get a BrowsingContext if we're not null or discarded. We don't want to
+ // early-return before we connect the BrowserBridgeChild, as otherwise we'll
+ // never break the channel in the parent.
+ RefPtr<dom::BrowsingContext> frameContext;
+ if (!aFrameContext.IsDiscarded()) {
+ frameContext = aFrameContext.get();
+ }
+
+ // Immediately construct the BrowserBridgeChild so we can destroy it cleanly
+ // if the process switch fails.
+ RefPtr<BrowserBridgeChild> bridge =
+ new BrowserBridgeChild(frameContext, aTabId, aLayersId);
+ RefPtr<BrowserChild> manager = GetBrowserChild();
+ if (NS_WARN_IF(
+ !manager->BindPBrowserBridgeEndpoint(std::move(aEndpoint), bridge))) {
+ return IPC_OK();
+ }
+
+ // Synchronously delete de actor here rather than using SendBeginDestroy(), as
+ // we haven't initialized it yet.
+ auto deleteBridge =
+ MakeScopeExit([&] { BrowserBridgeChild::Send__delete__(bridge); });
+
+ // Immediately tear down the actor if we don't have a valid FrameContext.
+ if (NS_WARN_IF(aFrameContext.IsNullOrDiscarded())) {
+ return IPC_OK();
+ }
+
+ RefPtr<Element> embedderElt = frameContext->GetEmbedderElement();
+ if (NS_WARN_IF(!embedderElt)) {
+ return IPC_OK();
+ }
+
+ if (NS_WARN_IF(embedderElt->GetOwnerGlobal() != GetWindowGlobal())) {
+ return IPC_OK();
+ }
+
+ RefPtr<nsFrameLoaderOwner> flo = do_QueryObject(embedderElt);
+ MOZ_DIAGNOSTIC_ASSERT(flo, "Embedder must be a nsFrameLoaderOwner");
+
+ // Trgger a process switch into the specified process.
+ IgnoredErrorResult rv;
+ flo->ChangeRemotenessWithBridge(bridge, rv);
+ if (NS_WARN_IF(rv.Failed())) {
+ return IPC_OK();
+ }
+
+ // Everything succeeded, so don't delete the bridge.
+ deleteBridge.release();
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult WindowGlobalChild::RecvDrawSnapshot(
+ const Maybe<IntRect>& aRect, const float& aScale,
+ const nscolor& aBackgroundColor, const uint32_t& aFlags,
+ DrawSnapshotResolver&& aResolve) {
+ aResolve(gfx::PaintFragment::Record(BrowsingContext(), aRect, aScale,
+ aBackgroundColor,
+ (gfx::CrossProcessPaintFlags)aFlags));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+WindowGlobalChild::RecvSaveStorageAccessPermissionGranted() {
+ nsCOMPtr<nsPIDOMWindowInner> inner = GetWindowGlobal();
+ if (inner) {
+ inner->SaveStorageAccessPermissionGranted();
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult WindowGlobalChild::RecvDispatchSecurityPolicyViolation(
+ const nsString& aViolationEventJSON) {
+ nsGlobalWindowInner* window = GetWindowGlobal();
+ if (!window) {
+ return IPC_OK();
+ }
+
+ Document* doc = window->GetDocument();
+ if (!doc) {
+ return IPC_OK();
+ }
+
+ SecurityPolicyViolationEventInit violationEvent;
+ if (!violationEvent.Init(aViolationEventJSON)) {
+ return IPC_OK();
+ }
+
+ RefPtr<Event> event = SecurityPolicyViolationEvent::Constructor(
+ doc, u"securitypolicyviolation"_ns, violationEvent);
+ event->SetTrusted(true);
+ doc->DispatchEvent(*event, IgnoreErrors());
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult WindowGlobalChild::RecvAddBlockedFrameNodeByClassifier(
+ const MaybeDiscardedBrowsingContext& aNode) {
+ if (aNode.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+
+ nsGlobalWindowInner* window = GetWindowGlobal();
+ if (!window) {
+ return IPC_OK();
+ }
+
+ Document* doc = window->GetDocument();
+ if (!doc) {
+ return IPC_OK();
+ }
+
+ MOZ_ASSERT(aNode.get()->GetEmbedderElement()->OwnerDoc() == doc);
+ doc->AddBlockedNodeByClassifier(aNode.get()->GetEmbedderElement());
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult WindowGlobalChild::RecvResetScalingZoom() {
+ if (Document* doc = mWindowGlobal->GetExtantDoc()) {
+ if (PresShell* ps = doc->GetPresShell()) {
+ ps->SetResolutionAndScaleTo(1.0,
+ ResolutionChangeOrigin::MainThreadAdjustment);
+ }
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult WindowGlobalChild::RecvSetContainerFeaturePolicy(
+ dom::FeaturePolicy* aContainerFeaturePolicy) {
+ mContainerFeaturePolicy = aContainerFeaturePolicy;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult WindowGlobalChild::RecvRestoreDocShellState(
+ const dom::sessionstore::DocShellRestoreState& aState,
+ RestoreDocShellStateResolver&& aResolve) {
+ if (mWindowGlobal) {
+ SessionStoreUtils::RestoreDocShellState(mWindowGlobal->GetDocShell(),
+ aState);
+ }
+ aResolve(true);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult WindowGlobalChild::RecvRestoreTabContent(
+ dom::SessionStoreRestoreData* aData, RestoreTabContentResolver&& aResolve) {
+ aData->RestoreInto(BrowsingContext());
+ aResolve(true);
+ return IPC_OK();
+}
+
+IPCResult WindowGlobalChild::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();
+}
+
+IPCResult WindowGlobalChild::RecvNotifyPermissionChange(const nsCString& aType,
+ uint32_t aPermission) {
+ nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
+ NS_ENSURE_TRUE(observerService,
+ IPC_FAIL(this, "Failed to get observer service"));
+ nsPIDOMWindowInner* notifyTarget =
+ static_cast<nsPIDOMWindowInner*>(this->GetWindowGlobal());
+ observerService->NotifyObservers(notifyTarget, "perm-changed-notify-only",
+ NS_ConvertUTF8toUTF16(aType).get());
+ // We only need to handle the revoked permission case here. The permission
+ // grant case is handled via the Storage Access API code.
+ if (this->GetWindowGlobal() &&
+ this->GetWindowGlobal()->UsingStorageAccess() &&
+ aPermission != nsIPermissionManager::ALLOW_ACTION) {
+ this->GetWindowGlobal()->SaveStorageAccessPermissionRevoked();
+ }
+ return IPC_OK();
+}
+
+void WindowGlobalChild::SetDocumentURI(nsIURI* aDocumentURI) {
+ // Registers a DOM Window with the profiler. It re-registers the same Inner
+ // Window ID with different URIs because when a Browsing context is first
+ // loaded, the first url loaded in it will be about:blank. This call keeps the
+ // first non-about:blank registration of window and discards the previous one.
+ uint64_t embedderInnerWindowID = 0;
+ if (BrowsingContext()->GetParent()) {
+ embedderInnerWindowID = BrowsingContext()->GetEmbedderInnerWindowId();
+ }
+ profiler_register_page(
+ BrowsingContext()->BrowserId(), InnerWindowId(),
+ nsContentUtils::TruncatedURLForDisplay(aDocumentURI, 1024),
+ embedderInnerWindowID, BrowsingContext()->UsePrivateBrowsing());
+
+ if (StaticPrefs::dom_security_setdocumenturi()) {
+ auto isLoadableViaInternet = [](nsIURI* uri) {
+ return (uri && (net::SchemeIsHTTP(uri) || net::SchemeIsHTTPS(uri)));
+ };
+ if (isLoadableViaInternet(aDocumentURI)) {
+ nsCOMPtr<nsIURI> principalURI = mDocumentPrincipal->GetURI();
+ if (mDocumentPrincipal->GetIsNullPrincipal()) {
+ nsCOMPtr<nsIPrincipal> precursor =
+ mDocumentPrincipal->GetPrecursorPrincipal();
+ if (precursor) {
+ principalURI = precursor->GetURI();
+ }
+ }
+
+ if (isLoadableViaInternet(principalURI)) {
+ nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
+
+ if (!NS_SUCCEEDED(ssm->CheckSameOriginURI(principalURI, aDocumentURI,
+ false, false))) {
+ MOZ_DIAGNOSTIC_ASSERT(false,
+ "Setting DocumentURI with a different origin "
+ "than principal URI");
+ }
+ }
+ }
+ }
+
+ mDocumentURI = aDocumentURI;
+ SendUpdateDocumentURI(WrapNotNull(aDocumentURI));
+}
+
+void WindowGlobalChild::SetDocumentPrincipal(
+ nsIPrincipal* aNewDocumentPrincipal,
+ nsIPrincipal* aNewDocumentStoragePrincipal) {
+ MOZ_ASSERT(mDocumentPrincipal->Equals(aNewDocumentPrincipal));
+ mDocumentPrincipal = aNewDocumentPrincipal;
+ SendUpdateDocumentPrincipal(aNewDocumentPrincipal,
+ aNewDocumentStoragePrincipal);
+}
+
+const nsACString& WindowGlobalChild::GetRemoteType() {
+ if (XRE_IsContentProcess()) {
+ return ContentChild::GetSingleton()->GetRemoteType();
+ }
+
+ return NOT_REMOTE_TYPE;
+}
+
+already_AddRefed<JSWindowActorChild> WindowGlobalChild::GetActor(
+ JSContext* aCx, const nsACString& aName, ErrorResult& aRv) {
+ return JSActorManager::GetActor(aCx, aName, aRv)
+ .downcast<JSWindowActorChild>();
+}
+
+already_AddRefed<JSWindowActorChild> WindowGlobalChild::GetExistingActor(
+ const nsACString& aName) {
+ return JSActorManager::GetExistingActor(aName).downcast<JSWindowActorChild>();
+}
+
+already_AddRefed<JSActor> WindowGlobalChild::InitJSActor(
+ JS::Handle<JSObject*> aMaybeActor, const nsACString& aName,
+ ErrorResult& aRv) {
+ RefPtr<JSWindowActorChild> actor;
+ if (aMaybeActor.get()) {
+ aRv = UNWRAP_OBJECT(JSWindowActorChild, aMaybeActor.get(), actor);
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+ } else {
+ actor = new JSWindowActorChild();
+ }
+
+ MOZ_RELEASE_ASSERT(!actor->GetManager(),
+ "mManager was already initialized once!");
+ actor->Init(aName, this);
+ return actor.forget();
+}
+
+void WindowGlobalChild::ActorDestroy(ActorDestroyReason aWhy) {
+ MOZ_ASSERT(nsContentUtils::IsSafeToRunScript(),
+ "Destroying WindowGlobalChild can run script");
+
+ // If our WindowContext hasn't been marked as discarded yet, ensure it's
+ // marked as discarded at this point.
+ mWindowContext->Discard();
+
+ profiler_unregister_page(InnerWindowId());
+
+ // Destroy our JSActors, and reject any pending queries.
+ JSActorDidDestroy();
+}
+
+bool WindowGlobalChild::IsSameOriginWith(
+ const dom::WindowContext* aOther) const {
+ if (aOther == WindowContext()) {
+ return true;
+ }
+
+ MOZ_DIAGNOSTIC_ASSERT(WindowContext()->Group() == aOther->Group());
+ if (nsGlobalWindowInner* otherWin = aOther->GetInnerWindow()) {
+ return mDocumentPrincipal->Equals(otherWin->GetPrincipal());
+ }
+ return false;
+}
+
+bool WindowGlobalChild::SameOriginWithTop() {
+ return IsSameOriginWith(WindowContext()->TopWindowContext());
+}
+
+// For historical context, see:
+//
+// Bug 13871: Prevent frameset spoofing
+// Bug 103638: Targets with same name in different windows open in wrong
+// window with javascript
+// Bug 408052: Adopt "ancestor" frame navigation policy
+// Bug 1570207: Refactor logic to rely on BrowsingContextGroups to enforce
+// origin attribute isolation
+// Bug 1810619: Crash at null in nsDocShell::ValidateOrigin
+bool WindowGlobalChild::CanNavigate(dom::BrowsingContext* aTarget,
+ bool aConsiderOpener) {
+ MOZ_DIAGNOSTIC_ASSERT(WindowContext()->Group() == aTarget->Group(),
+ "A WindowGlobalChild should never try to navigate a "
+ "BrowsingContext from another group");
+
+ auto isFileScheme = [](nsIPrincipal* aPrincipal) -> bool {
+ // NOTE: This code previously checked for a file scheme using
+ // `nsIPrincipal::GetURI()` combined with `NS_GetInnermostURI`. We no longer
+ // use GetURI, as it has been deprecated, and it makes more sense to take
+ // advantage of the pre-computed origin, which will already use the
+ // innermost URI (bug 1810619)
+ nsAutoCString origin, scheme;
+ return NS_SUCCEEDED(aPrincipal->GetOriginNoSuffix(origin)) &&
+ NS_SUCCEEDED(net_ExtractURLScheme(origin, scheme)) &&
+ scheme == "file"_ns;
+ };
+
+ // A frame can navigate itself and its own root.
+ if (aTarget == BrowsingContext() || aTarget == BrowsingContext()->Top()) {
+ return true;
+ }
+
+ // If the target frame doesn't yet have a WindowContext, start checking
+ // principals from its direct ancestor instead. It would inherit its principal
+ // from this document upon creation.
+ dom::WindowContext* initialWc = aTarget->GetCurrentWindowContext();
+ if (!initialWc) {
+ initialWc = aTarget->GetParentWindowContext();
+ }
+
+ // A frame can navigate any frame with a same-origin ancestor.
+ bool isFileDocument = isFileScheme(DocumentPrincipal());
+ for (dom::WindowContext* wc = initialWc; wc;
+ wc = wc->GetParentWindowContext()) {
+ dom::WindowGlobalChild* wgc = wc->GetWindowGlobalChild();
+ if (!wgc) {
+ continue; // out-of process, so not same-origin.
+ }
+
+ if (DocumentPrincipal()->Equals(wgc->DocumentPrincipal())) {
+ return true;
+ }
+
+ // Not strictly equal, special case if both are file: URIs.
+ //
+ // file: URIs are considered the same domain for the purpose of frame
+ // navigation, regardless of script accessibility (bug 420425).
+ if (isFileDocument && isFileScheme(wgc->DocumentPrincipal())) {
+ return true;
+ }
+ }
+
+ // If the target is a top-level document, a frame can navigate it if it can
+ // navigate its opener.
+ if (aConsiderOpener && !aTarget->GetParent()) {
+ if (RefPtr<dom::BrowsingContext> opener = aTarget->GetOpener()) {
+ return CanNavigate(opener, false);
+ }
+ }
+
+ return false;
+}
+
+// FindWithName follows the rules for choosing a browsing context,
+// with the exception of sandboxing for iframes. The implementation
+// for arbitrarily choosing between two browsing contexts with the
+// same name is as follows:
+//
+// 1) The start browsing context, i.e. 'this'
+// 2) Descendants in insertion order
+// 3) The parent
+// 4) Siblings and their children, both in insertion order
+// 5) After this we iteratively follow the parent chain, repeating 3
+// and 4 until
+// 6) If there is no parent, consider all other top level browsing
+// contexts and their children, both in insertion order
+//
+// See
+// https://html.spec.whatwg.org/multipage/browsers.html#the-rules-for-choosing-a-browsing-context-given-a-browsing-context-name
+dom::BrowsingContext* WindowGlobalChild::FindBrowsingContextWithName(
+ const nsAString& aName, bool aUseEntryGlobalForAccessCheck) {
+ RefPtr<WindowGlobalChild> requestingContext = this;
+ if (aUseEntryGlobalForAccessCheck) {
+ if (nsGlobalWindowInner* caller = nsContentUtils::EntryInnerWindow()) {
+ if (caller->GetBrowsingContextGroup() == WindowContext()->Group()) {
+ requestingContext = caller->GetWindowGlobalChild();
+ } else {
+ MOZ_RELEASE_ASSERT(caller->GetPrincipal()->IsSystemPrincipal(),
+ "caller must be either same-group or system");
+ }
+ }
+ }
+ MOZ_ASSERT(requestingContext, "must have a requestingContext");
+
+ dom::BrowsingContext* found = nullptr;
+ if (aName.IsEmpty()) {
+ // You can't find a browsing context with an empty name.
+ found = nullptr;
+ } else if (aName.LowerCaseEqualsLiteral("_blank")) {
+ // Just return null. Caller must handle creating a new window with
+ // a blank name.
+ found = nullptr;
+ } else if (nsContentUtils::IsSpecialName(aName)) {
+ found = BrowsingContext()->FindWithSpecialName(aName, *requestingContext);
+ } else if (dom::BrowsingContext* child =
+ BrowsingContext()->FindWithNameInSubtree(aName,
+ requestingContext)) {
+ found = child;
+ } else {
+ dom::WindowContext* current = WindowContext();
+
+ do {
+ Span<RefPtr<dom::BrowsingContext>> siblings;
+ dom::WindowContext* parent = current->GetParentWindowContext();
+
+ if (!parent) {
+ // We've reached the root of the tree, consider browsing
+ // contexts in the same browsing context group.
+ siblings = WindowContext()->Group()->Toplevels();
+ } else if (dom::BrowsingContext* bc = parent->GetBrowsingContext();
+ bc && bc->NameEquals(aName) &&
+ requestingContext->CanNavigate(bc) && bc->IsTargetable()) {
+ found = bc;
+ break;
+ } else {
+ siblings = parent->NonSyntheticChildren();
+ }
+
+ for (dom::BrowsingContext* sibling : siblings) {
+ if (sibling == current->GetBrowsingContext()) {
+ continue;
+ }
+
+ if (dom::BrowsingContext* relative =
+ sibling->FindWithNameInSubtree(aName, requestingContext)) {
+ found = relative;
+ // Breaks the outer loop
+ parent = nullptr;
+ break;
+ }
+ }
+
+ current = parent;
+ } while (current);
+ }
+
+ // Helpers should perform access control checks, which means that we
+ // only need to assert that we can access found.
+ MOZ_DIAGNOSTIC_ASSERT(!found || requestingContext->CanNavigate(found));
+
+ return found;
+}
+
+void WindowGlobalChild::UnblockBFCacheFor(BFCacheStatus aStatus) {
+ SendUpdateBFCacheStatus(0, aStatus);
+}
+
+void WindowGlobalChild::BlockBFCacheFor(BFCacheStatus aStatus) {
+ SendUpdateBFCacheStatus(aStatus, 0);
+}
+
+WindowGlobalChild::~WindowGlobalChild() = default;
+
+JSObject* WindowGlobalChild::WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) {
+ return WindowGlobalChild_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+nsISupports* WindowGlobalChild::GetParentObject() {
+ return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
+}
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WEAK_PTR(WindowGlobalChild, mWindowGlobal,
+ mContainerFeaturePolicy,
+ mWindowContext)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WindowGlobalChild)
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(WindowGlobalChild)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(WindowGlobalChild)
+
+} // namespace mozilla::dom
diff --git a/dom/ipc/WindowGlobalChild.h b/dom/ipc/WindowGlobalChild.h
new file mode 100644
index 0000000000..107b3f471d
--- /dev/null
+++ b/dom/ipc/WindowGlobalChild.h
@@ -0,0 +1,225 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+/* 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/. */
+
+#ifndef mozilla_dom_WindowGlobalChild_h
+#define mozilla_dom_WindowGlobalChild_h
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/WeakPtr.h"
+#include "mozilla/dom/PWindowGlobalChild.h"
+#include "nsRefPtrHashtable.h"
+#include "nsWrapperCache.h"
+#include "mozilla/dom/Document.h"
+#include "mozilla/dom/WindowGlobalActor.h"
+
+class nsGlobalWindowInner;
+class nsDocShell;
+
+namespace mozilla::dom {
+
+class BrowsingContext;
+class FeaturePolicy;
+class WindowContext;
+class WindowGlobalParent;
+class JSWindowActorChild;
+class JSActorMessageMeta;
+class BrowserChild;
+
+/**
+ * Actor for a single nsGlobalWindowInner. This actor is used to communicate
+ * information to the parent process asynchronously.
+ */
+class WindowGlobalChild final : public WindowGlobalActor,
+ public nsWrapperCache,
+ public PWindowGlobalChild,
+ public SupportsWeakPtr {
+ friend class PWindowGlobalChild;
+
+ public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(WindowGlobalChild)
+
+ static already_AddRefed<WindowGlobalChild> GetByInnerWindowId(
+ uint64_t aInnerWindowId);
+
+ static already_AddRefed<WindowGlobalChild> GetByInnerWindowId(
+ const GlobalObject& aGlobal, uint64_t aInnerWindowId) {
+ return GetByInnerWindowId(aInnerWindowId);
+ }
+
+ dom::BrowsingContext* BrowsingContext() override;
+ dom::WindowContext* WindowContext() const { return mWindowContext; }
+ nsGlobalWindowInner* GetWindowGlobal() const { return mWindowGlobal; }
+
+ // Has this actor been shut down
+ bool IsClosed() { return !CanSend(); }
+ void Destroy();
+
+ // Check if this actor is managed by PInProcess, as-in the document is loaded
+ // in the chrome process.
+ bool IsInProcess() { return XRE_IsParentProcess(); }
+
+ nsIURI* GetDocumentURI() override { return mDocumentURI; }
+ void SetDocumentURI(nsIURI* aDocumentURI);
+ // See the corresponding comment for `UpdateDocumentPrincipal` in
+ // PWindowGlobal on why and when this is allowed
+ void SetDocumentPrincipal(nsIPrincipal* aNewDocumentPrincipal,
+ nsIPrincipal* aNewDocumentStoragePrincipal);
+
+ nsIPrincipal* DocumentPrincipal() { return mDocumentPrincipal; }
+
+ // The Window ID for this WindowGlobal
+ uint64_t InnerWindowId();
+ uint64_t OuterWindowId();
+
+ uint64_t ContentParentId();
+
+ int64_t BeforeUnloadListeners() { return mBeforeUnloadListeners; }
+ void BeforeUnloadAdded();
+ void BeforeUnloadRemoved();
+
+ bool IsCurrentGlobal();
+
+ bool IsProcessRoot();
+
+ // Get the other side of this actor if it is an in-process actor. Returns
+ // |nullptr| if the actor has been torn down, or is not in-process.
+ already_AddRefed<WindowGlobalParent> GetParentActor();
+
+ // Get this actor's manager if it is not an in-process actor. Returns
+ // |nullptr| if the actor has been torn down, or is in-process.
+ already_AddRefed<BrowserChild> GetBrowserChild();
+
+ // Get a JS actor object by name.
+ already_AddRefed<JSWindowActorChild> GetActor(JSContext* aCx,
+ const nsACString& aName,
+ ErrorResult& aRv);
+ already_AddRefed<JSWindowActorChild> GetExistingActor(
+ const nsACString& aName);
+
+ // Create and initialize the WindowGlobalChild object.
+ static already_AddRefed<WindowGlobalChild> Create(
+ nsGlobalWindowInner* aWindow);
+ static already_AddRefed<WindowGlobalChild> CreateDisconnected(
+ const WindowGlobalInit& aInit);
+
+ void Init();
+
+ void InitWindowGlobal(nsGlobalWindowInner* aWindow);
+
+ // Called when a new document is loaded in this WindowGlobalChild.
+ void OnNewDocument(Document* aNewDocument);
+
+ // Returns true if this WindowGlobal is same-origin with the given
+ // WindowContext. Out-of-process WindowContexts are supported, and are assumed
+ // to be cross-origin.
+ //
+ // The given WindowContext must be in the same BrowsingContextGroup as this
+ // window global.
+ bool IsSameOriginWith(const dom::WindowContext* aOther) const;
+
+ bool SameOriginWithTop();
+
+ // Returns `true` if this WindowGlobal is allowed to navigate the given
+ // BrowsingContext. BrowsingContexts which are currently out-of-process are
+ // supported, and assumed to be cross-origin.
+ //
+ // The given BrowsingContext must be in the same BrowsingContextGroup as this
+ // WindowGlobal.
+ bool CanNavigate(dom::BrowsingContext* aTarget, bool aConsiderOpener = true);
+
+ // Using the rules for choosing a browsing context we try to find
+ // the browsing context with the given name in the set of
+ // transitively reachable browsing contexts. Performs access control
+ // checks with regard to this.
+ // See
+ // https://html.spec.whatwg.org/multipage/browsers.html#the-rules-for-choosing-a-browsing-context-given-a-browsing-context-name.
+ dom::BrowsingContext* FindBrowsingContextWithName(
+ const nsAString& aName, bool aUseEntryGlobalForAccessCheck = true);
+
+ nsISupports* GetParentObject();
+ JSObject* WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) override;
+
+ dom::FeaturePolicy* GetContainerFeaturePolicy() const {
+ return mContainerFeaturePolicy;
+ }
+
+ void UnblockBFCacheFor(BFCacheStatus aStatus);
+ void BlockBFCacheFor(BFCacheStatus aStatus);
+
+ protected:
+ const nsACString& GetRemoteType() override;
+
+ already_AddRefed<JSActor> InitJSActor(JS::Handle<JSObject*> aMaybeActor,
+ const nsACString& aName,
+ ErrorResult& aRv) override;
+ mozilla::ipc::IProtocol* AsNativeActor() override { return this; }
+
+ // IPC messages
+ mozilla::ipc::IPCResult RecvRawMessage(
+ const JSActorMessageMeta& aMeta, const Maybe<ClonedMessageData>& aData,
+ const Maybe<ClonedMessageData>& aStack);
+
+ mozilla::ipc::IPCResult RecvMakeFrameLocal(
+ const MaybeDiscarded<dom::BrowsingContext>& aFrameContext,
+ uint64_t aPendingSwitchId);
+
+ mozilla::ipc::IPCResult RecvMakeFrameRemote(
+ const MaybeDiscarded<dom::BrowsingContext>& aFrameContext,
+ ManagedEndpoint<PBrowserBridgeChild>&& aEndpoint, const TabId& aTabId,
+ const LayersId& aLayersId, MakeFrameRemoteResolver&& aResolve);
+
+ mozilla::ipc::IPCResult RecvDrawSnapshot(const Maybe<IntRect>& aRect,
+ const float& aScale,
+ const nscolor& aBackgroundColor,
+ const uint32_t& aFlags,
+ DrawSnapshotResolver&& aResolve);
+
+ mozilla::ipc::IPCResult RecvDispatchSecurityPolicyViolation(
+ const nsString& aViolationEventJSON);
+
+ mozilla::ipc::IPCResult RecvSaveStorageAccessPermissionGranted();
+
+ mozilla::ipc::IPCResult RecvAddBlockedFrameNodeByClassifier(
+ const MaybeDiscardedBrowsingContext& aNode);
+
+ mozilla::ipc::IPCResult RecvResetScalingZoom();
+
+ mozilla::ipc::IPCResult RecvSetContainerFeaturePolicy(
+ dom::FeaturePolicy* aContainerFeaturePolicy);
+
+ mozilla::ipc::IPCResult RecvRestoreDocShellState(
+ const dom::sessionstore::DocShellRestoreState& aState,
+ RestoreDocShellStateResolver&& aResolve);
+
+ // TODO: Use MOZ_CAN_RUN_SCRIPT when it gains IPDL support (bug 1539864)
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY mozilla::ipc::IPCResult RecvRestoreTabContent(
+ dom::SessionStoreRestoreData* aData,
+ RestoreTabContentResolver&& aResolve);
+
+ mozilla::ipc::IPCResult RecvNotifyPermissionChange(const nsCString& aType,
+ uint32_t aPermission);
+
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ private:
+ WindowGlobalChild(dom::WindowContext* aWindowContext,
+ nsIPrincipal* aPrincipal, nsIURI* aURI);
+
+ ~WindowGlobalChild();
+
+ RefPtr<nsGlobalWindowInner> mWindowGlobal;
+ RefPtr<dom::WindowContext> mWindowContext;
+ nsCOMPtr<nsIPrincipal> mDocumentPrincipal;
+ RefPtr<dom::FeaturePolicy> mContainerFeaturePolicy;
+ nsCOMPtr<nsIURI> mDocumentURI;
+ int64_t mBeforeUnloadListeners = 0;
+};
+
+} // namespace mozilla::dom
+
+#endif // !defined(mozilla_dom_WindowGlobalChild_h)
diff --git a/dom/ipc/WindowGlobalParent.cpp b/dom/ipc/WindowGlobalParent.cpp
new file mode 100644
index 0000000000..13fbe2940c
--- /dev/null
+++ b/dom/ipc/WindowGlobalParent.cpp
@@ -0,0 +1,1724 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/WindowGlobalParent.h"
+
+#include <algorithm>
+
+#include "mozilla/AntiTrackingUtils.h"
+#include "mozilla/AsyncEventDispatcher.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/ContentBlockingAllowList.h"
+#include "mozilla/dom/InProcessParent.h"
+#include "mozilla/dom/BrowserBridgeParent.h"
+#include "mozilla/dom/BrowsingContextGroup.h"
+#include "mozilla/dom/CanonicalBrowsingContext.h"
+#include "mozilla/dom/ClientInfo.h"
+#include "mozilla/dom/ClientIPCTypes.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/BrowserHost.h"
+#include "mozilla/dom/BrowserParent.h"
+#include "mozilla/dom/IdentityCredential.h"
+#include "mozilla/dom/MediaController.h"
+#include "mozilla/dom/WindowGlobalChild.h"
+#include "mozilla/dom/ChromeUtils.h"
+#include "mozilla/dom/UseCounterMetrics.h"
+#include "mozilla/dom/ipc/IdType.h"
+#include "mozilla/dom/ipc/StructuredCloneData.h"
+#include "mozilla/glean/GleanMetrics.h"
+#include "mozilla/Components.h"
+#include "mozilla/ScopeExit.h"
+#include "mozilla/ServoCSSParser.h"
+#include "mozilla/ServoStyleSet.h"
+#include "mozilla/StaticPrefs_dom.h"
+#include "mozilla/StaticPrefs_network.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/Variant.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "nsContentUtils.h"
+#include "nsDocShell.h"
+#include "nsDocShellLoadState.h"
+#include "nsError.h"
+#include "nsFrameLoader.h"
+#include "nsFrameLoaderOwner.h"
+#include "nsICookieManager.h"
+#include "nsICookieService.h"
+#include "nsQueryObject.h"
+#include "nsNetUtil.h"
+#include "nsSandboxFlags.h"
+#include "nsSerializationHelper.h"
+#include "nsIBrowser.h"
+#include "nsIEffectiveTLDService.h"
+#include "nsIHttpsOnlyModePermission.h"
+#include "nsIPromptCollection.h"
+#include "nsITimer.h"
+#include "nsITransportSecurityInfo.h"
+#include "nsISharePicker.h"
+#include "nsIURIMutator.h"
+#include "nsIWebProgressListener.h"
+#include "nsScriptSecurityManager.h"
+#include "nsIOService.h"
+
+#include "mozilla/dom/DOMException.h"
+#include "mozilla/dom/DOMExceptionBinding.h"
+
+#include "mozilla/dom/JSActorService.h"
+#include "mozilla/dom/JSWindowActorBinding.h"
+#include "mozilla/dom/JSWindowActorParent.h"
+
+#include "mozilla/net/NeckoParent.h"
+#include "mozilla/net/PCookieServiceParent.h"
+#include "mozilla/net/CookieServiceParent.h"
+
+#include "SessionStoreFunctions.h"
+#include "nsIXPConnect.h"
+#include "nsImportModule.h"
+#include "nsIXULRuntime.h"
+
+#include "mozilla/dom/PBackgroundSessionStorageCache.h"
+
+using namespace mozilla::ipc;
+using namespace mozilla::dom::ipc;
+
+extern mozilla::LazyLogModule gSHIPBFCacheLog;
+extern mozilla::LazyLogModule gUseCountersLog;
+
+namespace mozilla::dom {
+
+WindowGlobalParent::WindowGlobalParent(
+ CanonicalBrowsingContext* aBrowsingContext, uint64_t aInnerWindowId,
+ uint64_t aOuterWindowId, FieldValues&& aInit)
+ : WindowContext(aBrowsingContext, aInnerWindowId, aOuterWindowId,
+ std::move(aInit)),
+ mSandboxFlags(0),
+ mDocumentHasLoaded(false),
+ mDocumentHasUserInteracted(false),
+ mBlockAllMixedContent(false),
+ mUpgradeInsecureRequests(false) {
+ MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess(), "Parent process only");
+}
+
+already_AddRefed<WindowGlobalParent> WindowGlobalParent::CreateDisconnected(
+ const WindowGlobalInit& aInit) {
+ RefPtr<CanonicalBrowsingContext> browsingContext =
+ CanonicalBrowsingContext::Get(aInit.context().mBrowsingContextId);
+ if (NS_WARN_IF(!browsingContext)) {
+ return nullptr;
+ }
+
+ RefPtr<WindowGlobalParent> wgp =
+ GetByInnerWindowId(aInit.context().mInnerWindowId);
+ MOZ_RELEASE_ASSERT(!wgp, "Creating duplicate WindowGlobalParent");
+
+ FieldValues fields(aInit.context().mFields);
+ wgp =
+ new WindowGlobalParent(browsingContext, aInit.context().mInnerWindowId,
+ aInit.context().mOuterWindowId, std::move(fields));
+ wgp->mDocumentPrincipal = aInit.principal();
+ wgp->mDocumentURI = aInit.documentURI();
+ wgp->mIsInitialDocument = Some(aInit.isInitialDocument());
+ wgp->mBlockAllMixedContent = aInit.blockAllMixedContent();
+ wgp->mUpgradeInsecureRequests = aInit.upgradeInsecureRequests();
+ wgp->mSandboxFlags = aInit.sandboxFlags();
+ wgp->mHttpsOnlyStatus = aInit.httpsOnlyStatus();
+ wgp->mSecurityInfo = aInit.securityInfo();
+ net::CookieJarSettings::Deserialize(aInit.cookieJarSettings(),
+ getter_AddRefs(wgp->mCookieJarSettings));
+ MOZ_RELEASE_ASSERT(wgp->mDocumentPrincipal, "Must have a valid principal");
+
+ nsresult rv = wgp->SetDocumentStoragePrincipal(aInit.storagePrincipal());
+ MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv),
+ "Must succeed in setting storage principal");
+
+ return wgp.forget();
+}
+
+void WindowGlobalParent::Init() {
+ MOZ_ASSERT(Manager(), "Should have a manager!");
+
+ // Invoke our base class' `Init` method. This will register us in
+ // `gWindowContexts`.
+ WindowContext::Init();
+
+ // Determine which content process the window global is coming from.
+ dom::ContentParentId processId(0);
+ ContentParent* cp = nullptr;
+ if (!IsInProcess()) {
+ cp = static_cast<ContentParent*>(Manager()->Manager());
+ processId = cp->ChildID();
+
+ // Ensure the content process has permissions for this principal.
+ cp->TransmitPermissionsForPrincipal(mDocumentPrincipal);
+ }
+
+ MOZ_DIAGNOSTIC_ASSERT(
+ !BrowsingContext()->GetParent() ||
+ BrowsingContext()->GetEmbedderInnerWindowId(),
+ "When creating a non-root WindowGlobalParent, the WindowGlobalParent "
+ "for our embedder should've already been created.");
+
+ // Ensure we have a document URI
+ if (!mDocumentURI) {
+ NS_NewURI(getter_AddRefs(mDocumentURI), "about:blank");
+ }
+
+ // NOTE: `cp` may be nullptr, but that's OK, we need to tell every other
+ // process in our group in that case.
+ IPCInitializer ipcinit = GetIPCInitializer();
+ Group()->EachOtherParent(cp, [&](ContentParent* otherContent) {
+ Unused << otherContent->SendCreateWindowContext(ipcinit);
+ });
+
+ if (!BrowsingContext()->IsDiscarded()) {
+ MOZ_ALWAYS_SUCCEEDS(
+ BrowsingContext()->SetCurrentInnerWindowId(InnerWindowId()));
+
+ Unused << SendSetContainerFeaturePolicy(
+ BrowsingContext()->GetContainerFeaturePolicy());
+ }
+
+ if (BrowsingContext()->IsTopContent()) {
+ // For top level sandboxed documents we need to create a new principal
+ // from URI + OriginAttributes, since the document principal will be a
+ // NullPrincipal. See Bug 1654546.
+ if (mSandboxFlags & SANDBOXED_ORIGIN) {
+ ContentBlockingAllowList::RecomputePrincipal(
+ mDocumentURI, mDocumentPrincipal->OriginAttributesRef(),
+ getter_AddRefs(mDocContentBlockingAllowListPrincipal));
+ } else {
+ ContentBlockingAllowList::ComputePrincipal(
+ mDocumentPrincipal,
+ getter_AddRefs(mDocContentBlockingAllowListPrincipal));
+ }
+ }
+
+ nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+ if (obs) {
+ obs->NotifyObservers(ToSupports(this), "window-global-created", nullptr);
+ }
+
+ if (!BrowsingContext()->IsDiscarded() && ShouldTrackSiteOriginTelemetry()) {
+ mOriginCounter.emplace();
+ mOriginCounter->UpdateSiteOriginsFrom(this, /* aIncrease = */ true);
+ }
+}
+
+void WindowGlobalParent::OriginCounter::UpdateSiteOriginsFrom(
+ WindowGlobalParent* aParent, bool aIncrease) {
+ MOZ_RELEASE_ASSERT(aParent);
+
+ if (aParent->DocumentPrincipal()->GetIsContentPrincipal()) {
+ nsAutoCString origin;
+ aParent->DocumentPrincipal()->GetSiteOrigin(origin);
+
+ if (aIncrease) {
+ int32_t& count = mOriginMap.LookupOrInsert(origin);
+ count += 1;
+ mMaxOrigins = std::max(mMaxOrigins, mOriginMap.Count());
+ } else if (auto entry = mOriginMap.Lookup(origin)) {
+ entry.Data() -= 1;
+
+ if (entry.Data() == 0) {
+ entry.Remove();
+ }
+ }
+ }
+}
+
+void WindowGlobalParent::OriginCounter::Accumulate() {
+ mozilla::glean::geckoview::per_document_site_origins.AccumulateSamples(
+ {mMaxOrigins});
+
+ mMaxOrigins = 0;
+ mOriginMap.Clear();
+}
+
+/* static */
+already_AddRefed<WindowGlobalParent> WindowGlobalParent::GetByInnerWindowId(
+ uint64_t aInnerWindowId) {
+ if (!XRE_IsParentProcess()) {
+ return nullptr;
+ }
+
+ return WindowContext::GetById(aInnerWindowId).downcast<WindowGlobalParent>();
+}
+
+already_AddRefed<WindowGlobalChild> WindowGlobalParent::GetChildActor() {
+ if (!CanSend()) {
+ return nullptr;
+ }
+ IProtocol* otherSide = InProcessParent::ChildActorFor(this);
+ return do_AddRef(static_cast<WindowGlobalChild*>(otherSide));
+}
+
+BrowserParent* WindowGlobalParent::GetBrowserParent() {
+ if (IsInProcess() || !CanSend()) {
+ return nullptr;
+ }
+ return static_cast<BrowserParent*>(Manager());
+}
+
+ContentParent* WindowGlobalParent::GetContentParent() {
+ if (IsInProcess() || !CanSend()) {
+ return nullptr;
+ }
+ return static_cast<ContentParent*>(Manager()->Manager());
+}
+
+already_AddRefed<nsFrameLoader> WindowGlobalParent::GetRootFrameLoader() {
+ dom::BrowsingContext* top = BrowsingContext()->Top();
+
+ RefPtr<nsFrameLoaderOwner> frameLoaderOwner =
+ do_QueryObject(top->GetEmbedderElement());
+ if (frameLoaderOwner) {
+ return frameLoaderOwner->GetFrameLoader();
+ }
+ return nullptr;
+}
+
+uint64_t WindowGlobalParent::ContentParentId() {
+ RefPtr<BrowserParent> browserParent = GetBrowserParent();
+ return browserParent ? browserParent->Manager()->ChildID() : 0;
+}
+
+int32_t WindowGlobalParent::OsPid() {
+ RefPtr<BrowserParent> browserParent = GetBrowserParent();
+ return browserParent ? browserParent->Manager()->Pid() : -1;
+}
+
+// A WindowGlobalPaernt is the root in its process if it has no parent, or its
+// embedder is in a different process.
+bool WindowGlobalParent::IsProcessRoot() {
+ if (!BrowsingContext()->GetParent()) {
+ return true;
+ }
+
+ RefPtr<WindowGlobalParent> embedder =
+ BrowsingContext()->GetEmbedderWindowGlobal();
+ if (NS_WARN_IF(!embedder)) {
+ return false;
+ }
+
+ return ContentParentId() != embedder->ContentParentId();
+}
+
+uint32_t WindowGlobalParent::ContentBlockingEvents() {
+ return GetContentBlockingLog()->GetContentBlockingEventsInLog();
+}
+
+void WindowGlobalParent::GetContentBlockingLog(nsAString& aLog) {
+ NS_ConvertUTF8toUTF16 log(GetContentBlockingLog()->Stringify());
+ aLog.Assign(std::move(log));
+}
+
+mozilla::ipc::IPCResult WindowGlobalParent::RecvLoadURI(
+ const MaybeDiscarded<dom::BrowsingContext>& aTargetBC,
+ nsDocShellLoadState* aLoadState, bool aSetNavigating) {
+ if (aTargetBC.IsNullOrDiscarded()) {
+ MOZ_LOG(
+ BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ParentIPC: Trying to send a message with dead or detached context"));
+ return IPC_OK();
+ }
+
+ if (net::SchemeIsJavascript(aLoadState->URI())) {
+ return IPC_FAIL(this, "Illegal cross-process javascript: load attempt");
+ }
+
+ RefPtr<CanonicalBrowsingContext> targetBC = aTargetBC.get_canonical();
+
+ // FIXME: For cross-process loads, we should double check CanAccess() for the
+ // source browsing context in the parent process.
+
+ if (targetBC->Group() != BrowsingContext()->Group()) {
+ return IPC_FAIL(this, "Illegal cross-group BrowsingContext load");
+ }
+
+ // FIXME: We should really initiate the load in the parent before bouncing
+ // back down to the child.
+
+ targetBC->LoadURI(aLoadState, aSetNavigating);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult WindowGlobalParent::RecvInternalLoad(
+ nsDocShellLoadState* aLoadState) {
+ if (!aLoadState->Target().IsEmpty() ||
+ aLoadState->TargetBrowsingContext().IsNull()) {
+ return IPC_FAIL(this, "must already be retargeted");
+ }
+ if (aLoadState->TargetBrowsingContext().IsDiscarded()) {
+ MOZ_LOG(
+ BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ParentIPC: Trying to send a message with dead or detached context"));
+ return IPC_OK();
+ }
+
+ if (net::SchemeIsJavascript(aLoadState->URI())) {
+ return IPC_FAIL(this, "Illegal cross-process javascript: load attempt");
+ }
+
+ RefPtr<CanonicalBrowsingContext> targetBC =
+ aLoadState->TargetBrowsingContext().get_canonical();
+
+ // FIXME: For cross-process loads, we should double check CanAccess() for the
+ // source browsing context in the parent process.
+
+ if (targetBC->Group() != BrowsingContext()->Group()) {
+ return IPC_FAIL(this, "Illegal cross-group BrowsingContext load");
+ }
+
+ // FIXME: We should really initiate the load in the parent before bouncing
+ // back down to the child.
+
+ targetBC->InternalLoad(aLoadState);
+ return IPC_OK();
+}
+
+IPCResult WindowGlobalParent::RecvUpdateDocumentURI(NotNull<nsIURI*> aURI) {
+ // XXX(nika): Assert that the URI change was one which makes sense (either
+ // about:blank -> a real URI, or a legal push/popstate URI change):
+ if (StaticPrefs::dom_security_setdocumenturi()) {
+ nsAutoCString scheme;
+ if (NS_FAILED(aURI->GetScheme(scheme))) {
+ return IPC_FAIL(this, "Setting DocumentURI without scheme.");
+ }
+
+ nsCOMPtr<nsIIOService> ios = do_GetIOService();
+ if (!ios) {
+ return IPC_FAIL(this, "Cannot get IOService");
+ }
+ nsCOMPtr<nsIProtocolHandler> handler;
+ ios->GetProtocolHandler(scheme.get(), getter_AddRefs(handler));
+ if (!handler) {
+ return IPC_FAIL(this, "Setting DocumentURI with unknown protocol.");
+ }
+
+ auto isLoadableViaInternet = [](nsIURI* uri) {
+ return (uri && (net::SchemeIsHTTP(uri) || net::SchemeIsHTTPS(uri)));
+ };
+
+ if (isLoadableViaInternet(aURI)) {
+ nsCOMPtr<nsIURI> principalURI = mDocumentPrincipal->GetURI();
+ if (mDocumentPrincipal->GetIsNullPrincipal()) {
+ nsCOMPtr<nsIPrincipal> precursor =
+ mDocumentPrincipal->GetPrecursorPrincipal();
+ if (precursor) {
+ principalURI = precursor->GetURI();
+ }
+ }
+
+ if (isLoadableViaInternet(principalURI) &&
+ !nsScriptSecurityManager::SecurityCompareURIs(principalURI, aURI)) {
+ return IPC_FAIL(this,
+ "Setting DocumentURI with a different Origin than "
+ "principal URI");
+ }
+ }
+ }
+
+ mDocumentURI = aURI;
+ return IPC_OK();
+}
+
+nsresult WindowGlobalParent::SetDocumentStoragePrincipal(
+ nsIPrincipal* aNewDocumentStoragePrincipal) {
+ if (mDocumentPrincipal->Equals(aNewDocumentStoragePrincipal)) {
+ mDocumentStoragePrincipal = mDocumentPrincipal;
+ return NS_OK;
+ }
+
+ // Compare originNoSuffix to ensure it's equal.
+ nsCString noSuffix;
+ nsresult rv = mDocumentPrincipal->GetOriginNoSuffix(noSuffix);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ nsCString storageNoSuffix;
+ rv = aNewDocumentStoragePrincipal->GetOriginNoSuffix(storageNoSuffix);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ if (noSuffix != storageNoSuffix) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (!mDocumentPrincipal->OriginAttributesRef().EqualsIgnoringPartitionKey(
+ aNewDocumentStoragePrincipal->OriginAttributesRef())) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mDocumentStoragePrincipal = aNewDocumentStoragePrincipal;
+ return NS_OK;
+}
+
+IPCResult WindowGlobalParent::RecvUpdateDocumentPrincipal(
+ nsIPrincipal* aNewDocumentPrincipal,
+ nsIPrincipal* aNewDocumentStoragePrincipal) {
+ if (!mDocumentPrincipal->Equals(aNewDocumentPrincipal)) {
+ return IPC_FAIL(this,
+ "Trying to reuse WindowGlobalParent but the principal of "
+ "the new document does not match the old one");
+ }
+ mDocumentPrincipal = aNewDocumentPrincipal;
+
+ if (NS_FAILED(SetDocumentStoragePrincipal(aNewDocumentStoragePrincipal))) {
+ return IPC_FAIL(this,
+ "Trying to reuse WindowGlobalParent but the principal of "
+ "the new document does not match the storage principal");
+ }
+
+ return IPC_OK();
+}
+mozilla::ipc::IPCResult WindowGlobalParent::RecvUpdateDocumentTitle(
+ const nsString& aTitle) {
+ if (mDocumentTitle.isSome() && mDocumentTitle.value() == aTitle) {
+ return IPC_OK();
+ }
+
+ mDocumentTitle = Some(aTitle);
+
+ // Send a pagetitlechanged event only for changes to the title
+ // for top-level frames.
+ if (!BrowsingContext()->IsTop()) {
+ return IPC_OK();
+ }
+
+ // Notify media controller in order to update its default metadata.
+ if (BrowsingContext()->HasCreatedMediaController()) {
+ BrowsingContext()->GetMediaController()->NotifyPageTitleChanged();
+ }
+
+ Element* frameElement = BrowsingContext()->GetEmbedderElement();
+ if (!frameElement) {
+ return IPC_OK();
+ }
+
+ AsyncEventDispatcher::RunDOMEventWhenSafe(
+ *frameElement, u"pagetitlechanged"_ns, CanBubble::eYes,
+ ChromeOnlyDispatch::eYes);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult WindowGlobalParent::RecvUpdateHttpsOnlyStatus(
+ uint32_t aHttpsOnlyStatus) {
+ mHttpsOnlyStatus = aHttpsOnlyStatus;
+ return IPC_OK();
+}
+
+IPCResult WindowGlobalParent::RecvUpdateDocumentHasLoaded(
+ bool aDocumentHasLoaded) {
+ mDocumentHasLoaded = aDocumentHasLoaded;
+ return IPC_OK();
+}
+
+IPCResult WindowGlobalParent::RecvUpdateDocumentHasUserInteracted(
+ bool aDocumentHasUserInteracted) {
+ mDocumentHasUserInteracted = aDocumentHasUserInteracted;
+ return IPC_OK();
+}
+
+IPCResult WindowGlobalParent::RecvUpdateSandboxFlags(uint32_t aSandboxFlags) {
+ mSandboxFlags = aSandboxFlags;
+ return IPC_OK();
+}
+
+IPCResult WindowGlobalParent::RecvUpdateDocumentCspSettings(
+ bool aBlockAllMixedContent, bool aUpgradeInsecureRequests) {
+ mBlockAllMixedContent = aBlockAllMixedContent;
+ mUpgradeInsecureRequests = aUpgradeInsecureRequests;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult WindowGlobalParent::RecvSetClientInfo(
+ const IPCClientInfo& aIPCClientInfo) {
+ mClientInfo = Some(ClientInfo(aIPCClientInfo));
+ return IPC_OK();
+}
+
+IPCResult WindowGlobalParent::RecvDestroy() {
+ // Make a copy so that we can avoid potential iterator invalidation when
+ // calling the user-provided Destroy() methods.
+ JSActorWillDestroy();
+
+ if (CanSend()) {
+ RefPtr<BrowserParent> browserParent = GetBrowserParent();
+ if (!browserParent || !browserParent->IsDestroyed()) {
+ Unused << Send__delete__(this);
+ }
+ }
+ return IPC_OK();
+}
+
+IPCResult WindowGlobalParent::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();
+}
+
+const nsACString& WindowGlobalParent::GetRemoteType() {
+ if (RefPtr<BrowserParent> browserParent = GetBrowserParent()) {
+ return browserParent->Manager()->GetRemoteType();
+ }
+
+ return NOT_REMOTE_TYPE;
+}
+
+void WindowGlobalParent::NotifyContentBlockingEvent(
+ uint32_t aEvent, nsIRequest* aRequest, bool aBlocked,
+ const nsACString& aTrackingOrigin,
+ const nsTArray<nsCString>& aTrackingFullHashes,
+ const Maybe<ContentBlockingNotifier::StorageAccessPermissionGrantedReason>&
+ aReason,
+ const Maybe<ContentBlockingNotifier::CanvasFingerprinter>&
+ aCanvasFingerprinter,
+ const Maybe<bool> aCanvasFingerprinterKnownText) {
+ MOZ_ASSERT(NS_IsMainThread());
+ DebugOnly<bool> isCookiesBlocked =
+ aEvent == nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER ||
+ aEvent == nsIWebProgressListener::STATE_COOKIES_BLOCKED_SOCIALTRACKER;
+ MOZ_ASSERT_IF(aBlocked, aReason.isNothing());
+ MOZ_ASSERT_IF(!isCookiesBlocked, aReason.isNothing());
+ MOZ_ASSERT_IF(isCookiesBlocked && !aBlocked, aReason.isSome());
+ MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
+ // TODO: temporarily remove this until we find the root case of Bug 1609144
+ // MOZ_DIAGNOSTIC_ASSERT_IF(XRE_IsE10sParentProcess(), !IsInProcess());
+
+ // Return early if this WindowGlobalParent is in process.
+ if (IsInProcess()) {
+ return;
+ }
+
+ Maybe<uint32_t> event = GetContentBlockingLog()->RecordLogParent(
+ aTrackingOrigin, aEvent, aBlocked, aReason, aTrackingFullHashes,
+ aCanvasFingerprinter, aCanvasFingerprinterKnownText);
+
+ // Notify the OnContentBlockingEvent if necessary.
+ if (event) {
+ if (auto* webProgress = GetBrowsingContext()->GetWebProgress()) {
+ webProgress->OnContentBlockingEvent(webProgress, aRequest, event.value());
+ }
+ }
+}
+
+already_AddRefed<JSWindowActorParent> WindowGlobalParent::GetActor(
+ JSContext* aCx, const nsACString& aName, ErrorResult& aRv) {
+ return JSActorManager::GetActor(aCx, aName, aRv)
+ .downcast<JSWindowActorParent>();
+}
+
+already_AddRefed<JSWindowActorParent> WindowGlobalParent::GetExistingActor(
+ const nsACString& aName) {
+ return JSActorManager::GetExistingActor(aName)
+ .downcast<JSWindowActorParent>();
+}
+
+already_AddRefed<JSActor> WindowGlobalParent::InitJSActor(
+ JS::Handle<JSObject*> aMaybeActor, const nsACString& aName,
+ ErrorResult& aRv) {
+ RefPtr<JSWindowActorParent> actor;
+ if (aMaybeActor.get()) {
+ aRv = UNWRAP_OBJECT(JSWindowActorParent, aMaybeActor.get(), actor);
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+ } else {
+ actor = new JSWindowActorParent();
+ }
+
+ MOZ_RELEASE_ASSERT(!actor->GetManager(),
+ "mManager was already initialized once!");
+ actor->Init(aName, this);
+ return actor.forget();
+}
+
+bool WindowGlobalParent::IsCurrentGlobal() {
+ if (mozilla::SessionHistoryInParent() && BrowsingContext() &&
+ BrowsingContext()->IsInBFCache()) {
+ return false;
+ }
+
+ return CanSend() && BrowsingContext()->GetCurrentWindowGlobal() == this;
+}
+
+bool WindowGlobalParent::IsActiveInTab() {
+ if (!CanSend()) {
+ return false;
+ }
+
+ CanonicalBrowsingContext* bc = BrowsingContext();
+ if (!bc || bc->GetCurrentWindowGlobal() != this) {
+ return false;
+ }
+
+ // We check the top BC so we don't need to worry about getting a stale value.
+ // That may not be necessary.
+ MOZ_ASSERT(bc->Top()->IsInBFCache() == bc->IsInBFCache(),
+ "BFCache bit out of sync?");
+ return bc->AncestorsAreCurrent() && !bc->Top()->IsInBFCache();
+}
+
+namespace {
+
+class ShareHandler final : public PromiseNativeHandler {
+ public:
+ explicit ShareHandler(
+ mozilla::dom::WindowGlobalParent::ShareResolver&& aResolver)
+ : mResolver(std::move(aResolver)) {}
+
+ NS_DECL_ISUPPORTS
+
+ public:
+ virtual void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
+ ErrorResult& aRv) override {
+ mResolver(NS_OK);
+ }
+
+ virtual void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
+ ErrorResult& aRv) override {
+ if (NS_WARN_IF(!aValue.isObject())) {
+ mResolver(NS_ERROR_FAILURE);
+ return;
+ }
+
+ // nsresult is stored as Exception internally in Promise
+ JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
+ RefPtr<DOMException> unwrapped;
+ nsresult rv = UNWRAP_OBJECT(DOMException, &obj, unwrapped);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ mResolver(NS_ERROR_FAILURE);
+ return;
+ }
+
+ mResolver(unwrapped->GetResult());
+ }
+
+ private:
+ ~ShareHandler() = default;
+
+ mozilla::dom::WindowGlobalParent::ShareResolver mResolver;
+};
+
+NS_IMPL_ISUPPORTS0(ShareHandler)
+
+} // namespace
+
+mozilla::ipc::IPCResult WindowGlobalParent::RecvGetContentBlockingEvents(
+ WindowGlobalParent::GetContentBlockingEventsResolver&& aResolver) {
+ uint32_t events = GetContentBlockingLog()->GetContentBlockingEventsInLog();
+ aResolver(events);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult WindowGlobalParent::RecvUpdateCookieJarSettings(
+ const CookieJarSettingsArgs& aCookieJarSettingsArgs) {
+ net::CookieJarSettings::Deserialize(aCookieJarSettingsArgs,
+ getter_AddRefs(mCookieJarSettings));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult WindowGlobalParent::RecvUpdateDocumentSecurityInfo(
+ nsITransportSecurityInfo* aSecurityInfo) {
+ mSecurityInfo = aSecurityInfo;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult WindowGlobalParent::RecvShare(
+ IPCWebShareData&& aData, WindowGlobalParent::ShareResolver&& aResolver) {
+ // Widget Layer handoff...
+ nsCOMPtr<nsISharePicker> sharePicker =
+ do_GetService("@mozilla.org/sharepicker;1");
+ if (!sharePicker) {
+ aResolver(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+ return IPC_OK();
+ }
+
+ // Initialize the ShareWidget
+ RefPtr<BrowserParent> parent = GetBrowserParent();
+ nsCOMPtr<mozIDOMWindowProxy> openerWindow;
+ if (parent) {
+ openerWindow = parent->GetParentWindowOuter();
+ if (!openerWindow) {
+ aResolver(NS_ERROR_FAILURE);
+ return IPC_OK();
+ }
+ }
+ sharePicker->Init(openerWindow);
+
+ // And finally share the data...
+ RefPtr<Promise> promise;
+ nsresult rv = sharePicker->Share(aData.title(), aData.text(), aData.url(),
+ getter_AddRefs(promise));
+ if (NS_FAILED(rv)) {
+ aResolver(rv);
+ return IPC_OK();
+ }
+
+ // Handler finally awaits response...
+ RefPtr<ShareHandler> handler = new ShareHandler(std::move(aResolver));
+ promise->AppendNativeHandler(handler);
+
+ return IPC_OK();
+}
+
+namespace {
+
+class CheckPermitUnloadRequest final : public PromiseNativeHandler,
+ public nsITimerCallback {
+ public:
+ CheckPermitUnloadRequest(WindowGlobalParent* aWGP, bool aHasInProcessBlocker,
+ nsIDocumentViewer::PermitUnloadAction aAction,
+ std::function<void(bool)>&& aResolver)
+ : mResolver(std::move(aResolver)),
+ mWGP(aWGP),
+ mAction(aAction),
+ mFoundBlocker(aHasInProcessBlocker) {}
+
+ void Run(ContentParent* aIgnoreProcess = nullptr, uint32_t aTimeout = 0) {
+ MOZ_ASSERT(mState == State::UNINITIALIZED);
+ mState = State::WAITING;
+
+ RefPtr<CheckPermitUnloadRequest> self(this);
+
+ AutoTArray<ContentParent*, 8> seen;
+ if (aIgnoreProcess) {
+ seen.AppendElement(aIgnoreProcess);
+ }
+
+ BrowsingContext* bc = mWGP->GetBrowsingContext();
+ bc->PreOrderWalk([&](dom::BrowsingContext* aBC) {
+ if (WindowGlobalParent* wgp =
+ aBC->Canonical()->GetCurrentWindowGlobal()) {
+ ContentParent* cp = wgp->GetContentParent();
+ if (wgp->HasBeforeUnload() && !seen.ContainsSorted(cp)) {
+ seen.InsertElementSorted(cp);
+ mPendingRequests++;
+ auto resolve = [self](bool blockNavigation) {
+ if (blockNavigation) {
+ self->mFoundBlocker = true;
+ }
+ self->ResolveRequest();
+ };
+ if (cp) {
+ cp->SendDispatchBeforeUnloadToSubtree(
+ bc, resolve, [self](auto) { self->ResolveRequest(); });
+ } else {
+ ContentChild::DispatchBeforeUnloadToSubtree(bc, resolve);
+ }
+ }
+ }
+ });
+
+ if (mPendingRequests && aTimeout) {
+ Unused << NS_NewTimerWithCallback(getter_AddRefs(mTimer), this, aTimeout,
+ nsITimer::TYPE_ONE_SHOT);
+ }
+
+ CheckDoneWaiting();
+ }
+
+ void ResolveRequest() {
+ mPendingRequests--;
+ CheckDoneWaiting();
+ }
+
+ NS_IMETHODIMP Notify(nsITimer* aTimer) override {
+ MOZ_ASSERT(aTimer == mTimer);
+ if (mState == State::WAITING) {
+ mState = State::TIMED_OUT;
+ CheckDoneWaiting();
+ }
+ return NS_OK;
+ }
+
+ void CheckDoneWaiting() {
+ // If we've found a blocker, we prompt immediately without waiting for
+ // further responses. The user's response applies to the entire navigation
+ // attempt, regardless of how many "beforeunload" listeners we call.
+ if (mState != State::TIMED_OUT &&
+ (mState != State::WAITING || (mPendingRequests && !mFoundBlocker))) {
+ return;
+ }
+
+ mState = State::PROMPTING;
+
+ // Clearing our reference to the timer will automatically cancel it if it's
+ // still running.
+ mTimer = nullptr;
+
+ if (!mFoundBlocker) {
+ SendReply(true);
+ return;
+ }
+
+ auto action = mAction;
+ if (StaticPrefs::dom_disable_beforeunload()) {
+ action = nsIDocumentViewer::eDontPromptAndUnload;
+ }
+ if (action != nsIDocumentViewer::ePrompt) {
+ SendReply(action == nsIDocumentViewer::eDontPromptAndUnload);
+ return;
+ }
+
+ // Handle any failure in prompting by aborting the navigation. See comment
+ // in nsContentViewer::PermitUnload for reasoning.
+ auto cleanup = MakeScopeExit([&]() { SendReply(false); });
+
+ if (nsCOMPtr<nsIPromptCollection> prompt =
+ do_GetService("@mozilla.org/embedcomp/prompt-collection;1")) {
+ RefPtr<Promise> promise;
+ prompt->AsyncBeforeUnloadCheck(mWGP->GetBrowsingContext(),
+ getter_AddRefs(promise));
+
+ if (!promise) {
+ return;
+ }
+
+ promise->AppendNativeHandler(this);
+ cleanup.release();
+ }
+ }
+
+ void SendReply(bool aAllow) {
+ MOZ_ASSERT(mState != State::REPLIED);
+ mResolver(aAllow);
+ mState = State::REPLIED;
+ }
+
+ void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
+ ErrorResult& aRv) override {
+ MOZ_ASSERT(mState == State::PROMPTING);
+
+ SendReply(JS::ToBoolean(aValue));
+ }
+
+ void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
+ ErrorResult& aRv) override {
+ MOZ_ASSERT(mState == State::PROMPTING);
+
+ SendReply(false);
+ }
+
+ NS_DECL_ISUPPORTS
+
+ private:
+ ~CheckPermitUnloadRequest() {
+ // We may get here without having sent a reply if the promise we're waiting
+ // on is destroyed without being resolved or rejected.
+ if (mState != State::REPLIED) {
+ SendReply(false);
+ }
+ }
+
+ enum class State : uint8_t {
+ UNINITIALIZED,
+ WAITING,
+ TIMED_OUT,
+ PROMPTING,
+ REPLIED,
+ };
+
+ std::function<void(bool)> mResolver;
+
+ RefPtr<WindowGlobalParent> mWGP;
+ nsCOMPtr<nsITimer> mTimer;
+
+ uint32_t mPendingRequests = 0;
+
+ nsIDocumentViewer::PermitUnloadAction mAction;
+
+ State mState = State::UNINITIALIZED;
+
+ bool mFoundBlocker = false;
+};
+
+NS_IMPL_ISUPPORTS(CheckPermitUnloadRequest, nsITimerCallback)
+
+} // namespace
+
+mozilla::ipc::IPCResult WindowGlobalParent::RecvCheckPermitUnload(
+ bool aHasInProcessBlocker, XPCOMPermitUnloadAction aAction,
+ CheckPermitUnloadResolver&& aResolver) {
+ if (!IsCurrentGlobal()) {
+ aResolver(false);
+ return IPC_OK();
+ }
+
+ auto request = MakeRefPtr<CheckPermitUnloadRequest>(
+ this, aHasInProcessBlocker, aAction, std::move(aResolver));
+ request->Run(/* aIgnoreProcess */ GetContentParent());
+
+ return IPC_OK();
+}
+
+already_AddRefed<Promise> WindowGlobalParent::PermitUnload(
+ PermitUnloadAction aAction, uint32_t aTimeout, mozilla::ErrorResult& aRv) {
+ nsIGlobalObject* global = GetParentObject();
+ RefPtr<Promise> promise = Promise::Create(global, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+
+ auto request = MakeRefPtr<CheckPermitUnloadRequest>(
+ this, /* aHasInProcessBlocker */ false,
+ nsIDocumentViewer::PermitUnloadAction(aAction),
+ [promise](bool aAllow) { promise->MaybeResolve(aAllow); });
+ request->Run(/* aIgnoreProcess */ nullptr, aTimeout);
+
+ return promise.forget();
+}
+
+void WindowGlobalParent::PermitUnload(std::function<void(bool)>&& aResolver) {
+ RefPtr<CheckPermitUnloadRequest> request = new CheckPermitUnloadRequest(
+ this, /* aHasInProcessBlocker */ false,
+ nsIDocumentViewer::PermitUnloadAction::ePrompt, std::move(aResolver));
+ request->Run();
+}
+
+already_AddRefed<mozilla::dom::Promise> WindowGlobalParent::DrawSnapshot(
+ const DOMRect* aRect, double aScale, const nsACString& aBackgroundColor,
+ bool aResetScrollPosition, mozilla::ErrorResult& aRv) {
+ nsIGlobalObject* global = GetParentObject();
+ RefPtr<Promise> promise = Promise::Create(global, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+
+ nscolor color;
+ if (NS_WARN_IF(!ServoCSSParser::ComputeColor(nullptr, NS_RGB(0, 0, 0),
+ aBackgroundColor, &color,
+ nullptr, nullptr))) {
+ aRv = NS_ERROR_FAILURE;
+ return nullptr;
+ }
+
+ gfx::CrossProcessPaintFlags flags =
+ gfx::CrossProcessPaintFlags::UseHighQualityScaling;
+ if (!aRect) {
+ // If no explicit Rect was passed, we want the currently visible viewport.
+ flags |= gfx::CrossProcessPaintFlags::DrawView;
+ } else if (aResetScrollPosition) {
+ flags |= gfx::CrossProcessPaintFlags::ResetScrollPosition;
+ }
+
+ if (!gfx::CrossProcessPaint::Start(this, aRect, (float)aScale, color, flags,
+ promise)) {
+ aRv = NS_ERROR_FAILURE;
+ return nullptr;
+ }
+ return promise.forget();
+}
+
+void WindowGlobalParent::DrawSnapshotInternal(gfx::CrossProcessPaint* aPaint,
+ const Maybe<IntRect>& aRect,
+ float aScale,
+ nscolor aBackgroundColor,
+ uint32_t aFlags) {
+ auto promise = SendDrawSnapshot(aRect, aScale, aBackgroundColor, aFlags);
+
+ RefPtr<gfx::CrossProcessPaint> paint(aPaint);
+ RefPtr<WindowGlobalParent> wgp(this);
+ promise->Then(
+ GetMainThreadSerialEventTarget(), __func__,
+ [paint, wgp](PaintFragment&& aFragment) {
+ paint->ReceiveFragment(wgp, std::move(aFragment));
+ },
+ [paint, wgp](ResponseRejectReason&& aReason) {
+ paint->LostFragment(wgp);
+ });
+}
+
+/**
+ * Accumulated page use counter data for a given top-level content document.
+ */
+struct PageUseCounters {
+ // The number of page use counter data messages we are still waiting for.
+ uint32_t mWaiting = 0;
+
+ // Whether we have received any page use counter data.
+ bool mReceivedAny = false;
+
+ // The accumulated page use counters.
+ UseCounters mUseCounters;
+};
+
+mozilla::ipc::IPCResult WindowGlobalParent::RecvExpectPageUseCounters(
+ const MaybeDiscarded<WindowContext>& aTop) {
+ if (aTop.IsNull()) {
+ return IPC_FAIL(this, "aTop must not be null");
+ }
+
+ MOZ_LOG(gUseCountersLog, LogLevel::Debug,
+ ("Expect page use counters: WindowContext %" PRIu64 " -> %" PRIu64,
+ InnerWindowId(), aTop.ContextId()));
+
+ // We've been called to indicate that the document in our window intends
+ // to send use counter data to accumulate towards the top-level document's
+ // page use counters. This causes us to wait for this window to go away
+ // (in WindowGlobalParent::ActorDestroy) before reporting the page use
+ // counters via Telemetry.
+ RefPtr<WindowGlobalParent> page =
+ static_cast<WindowGlobalParent*>(aTop.GetMaybeDiscarded());
+ if (!page || page->mSentPageUseCounters) {
+ MOZ_LOG(gUseCountersLog, LogLevel::Debug,
+ (" > too late, won't report page use counters for this straggler"));
+ return IPC_OK();
+ }
+
+ if (mPageUseCountersWindow) {
+ if (mPageUseCountersWindow != page) {
+ return IPC_FAIL(this,
+ "ExpectPageUseCounters called on the same "
+ "WindowContext with a different aTop value");
+ }
+
+ // We can get called with the same aTop value more than once, e.g. for
+ // initial about:blank documents and then subsequent "real" documents loaded
+ // into the same window. We must note each source window only once.
+ return IPC_OK();
+ }
+
+ // Note that the top-level document must wait for one more window's use
+ // counters before reporting via Telemetry.
+ mPageUseCountersWindow = page;
+ if (!page->mPageUseCounters) {
+ page->mPageUseCounters = MakeUnique<PageUseCounters>();
+ }
+ ++page->mPageUseCounters->mWaiting;
+
+ MOZ_LOG(
+ gUseCountersLog, LogLevel::Debug,
+ (" > top-level now waiting on %d\n", page->mPageUseCounters->mWaiting));
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult WindowGlobalParent::RecvAccumulatePageUseCounters(
+ const UseCounters& aUseCounters) {
+ // We've been called to accumulate use counter data into the page use counters
+ // for the document in mPageUseCountersWindow.
+
+ MOZ_LOG(
+ gUseCountersLog, LogLevel::Debug,
+ ("Accumulate page use counters: WindowContext %" PRIu64 " -> %" PRIu64,
+ InnerWindowId(),
+ mPageUseCountersWindow ? mPageUseCountersWindow->InnerWindowId() : 0));
+
+ if (!mPageUseCountersWindow || mPageUseCountersWindow->mSentPageUseCounters) {
+ MOZ_LOG(gUseCountersLog, LogLevel::Debug,
+ (" > too late, won't report page use counters for this straggler"));
+ return IPC_OK();
+ }
+
+ MOZ_ASSERT(mPageUseCountersWindow->mPageUseCounters);
+ MOZ_ASSERT(mPageUseCountersWindow->mPageUseCounters->mWaiting > 0);
+
+ mPageUseCountersWindow->mPageUseCounters->mUseCounters |= aUseCounters;
+ mPageUseCountersWindow->mPageUseCounters->mReceivedAny = true;
+ return IPC_OK();
+}
+
+// This is called on the top-level WindowGlobal, i.e. the one that is
+// accumulating the page use counters, not the (potentially descendant) window
+// that has finished providing use counter data.
+void WindowGlobalParent::FinishAccumulatingPageUseCounters() {
+ MOZ_LOG(gUseCountersLog, LogLevel::Debug,
+ ("Stop expecting page use counters: -> WindowContext %" PRIu64,
+ InnerWindowId()));
+
+ if (!mPageUseCounters) {
+ MOZ_ASSERT_UNREACHABLE("Not expecting page use counter data");
+ MOZ_LOG(gUseCountersLog, LogLevel::Debug,
+ (" > not expecting page use counter data"));
+ return;
+ }
+
+ MOZ_ASSERT(mPageUseCounters->mWaiting > 0);
+ --mPageUseCounters->mWaiting;
+
+ if (mPageUseCounters->mWaiting > 0) {
+ MOZ_LOG(gUseCountersLog, LogLevel::Debug,
+ (" > now waiting on %d", mPageUseCounters->mWaiting));
+ return;
+ }
+
+ if (mPageUseCounters->mReceivedAny) {
+ MOZ_LOG(gUseCountersLog, LogLevel::Debug,
+ (" > reporting [%s]",
+ nsContentUtils::TruncatedURLForDisplay(mDocumentURI).get()));
+
+ Maybe<nsCString> urlForLogging;
+ const bool dumpCounters = StaticPrefs::dom_use_counters_dump_page();
+ if (dumpCounters) {
+ urlForLogging.emplace(
+ nsContentUtils::TruncatedURLForDisplay(mDocumentURI));
+ }
+
+ glean::use_counter::top_level_content_documents_destroyed.Add();
+
+ bool any = false;
+ for (int32_t c = 0; c < eUseCounter_Count; ++c) {
+ auto uc = static_cast<UseCounter>(c);
+ if (!mPageUseCounters->mUseCounters[uc]) {
+ continue;
+ }
+ any = true;
+ const char* metricName = IncrementUseCounter(uc, /* aIsPage = */ true);
+ if (dumpCounters) {
+ printf_stderr("USE_COUNTER_PAGE: %s - %s\n", metricName,
+ urlForLogging->get());
+ }
+ }
+
+ if (!any) {
+ MOZ_LOG(gUseCountersLog, LogLevel::Debug,
+ (" > page use counter data was received, but was empty"));
+ }
+ } else {
+ MOZ_LOG(gUseCountersLog, LogLevel::Debug,
+ (" > no page use counter data was received"));
+ }
+
+ mSentPageUseCounters = true;
+ mPageUseCounters = nullptr;
+}
+
+Element* WindowGlobalParent::GetRootOwnerElement() {
+ WindowGlobalParent* top = TopWindowContext();
+ if (!top) {
+ return nullptr;
+ }
+
+ if (IsInProcess()) {
+ return top->BrowsingContext()->GetEmbedderElement();
+ }
+
+ if (BrowserParent* parent = top->GetBrowserParent()) {
+ return parent->GetOwnerElement();
+ }
+
+ return nullptr;
+}
+
+void WindowGlobalParent::NotifySessionStoreUpdatesComplete(Element* aEmbedder) {
+ if (!aEmbedder) {
+ aEmbedder = GetRootOwnerElement();
+ }
+ if (aEmbedder) {
+ if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
+ obs->NotifyWhenScriptSafe(ToSupports(aEmbedder),
+ "browser-shutdown-tabstate-updated", nullptr);
+ }
+ }
+}
+
+mozilla::ipc::IPCResult WindowGlobalParent::RecvRequestRestoreTabContent() {
+ CanonicalBrowsingContext* bc = BrowsingContext();
+ if (bc && bc->AncestorsAreCurrent()) {
+ bc->Top()->RequestRestoreTabContent(this);
+ }
+ return IPC_OK();
+}
+
+nsCString BFCacheStatusToString(uint32_t aFlags) {
+ if (aFlags == 0) {
+ return "0"_ns;
+ }
+
+ nsCString flags;
+#define ADD_BFCACHESTATUS_TO_STRING(_flag) \
+ if (aFlags & BFCacheStatus::_flag) { \
+ if (!flags.IsEmpty()) { \
+ flags.Append('|'); \
+ } \
+ flags.AppendLiteral(#_flag); \
+ aFlags &= ~BFCacheStatus::_flag; \
+ }
+
+ ADD_BFCACHESTATUS_TO_STRING(NOT_ALLOWED);
+ ADD_BFCACHESTATUS_TO_STRING(EVENT_HANDLING_SUPPRESSED);
+ ADD_BFCACHESTATUS_TO_STRING(SUSPENDED);
+ ADD_BFCACHESTATUS_TO_STRING(UNLOAD_LISTENER);
+ ADD_BFCACHESTATUS_TO_STRING(REQUEST);
+ ADD_BFCACHESTATUS_TO_STRING(ACTIVE_GET_USER_MEDIA);
+ ADD_BFCACHESTATUS_TO_STRING(ACTIVE_PEER_CONNECTION);
+ ADD_BFCACHESTATUS_TO_STRING(CONTAINS_EME_CONTENT);
+ ADD_BFCACHESTATUS_TO_STRING(CONTAINS_MSE_CONTENT);
+ ADD_BFCACHESTATUS_TO_STRING(HAS_ACTIVE_SPEECH_SYNTHESIS);
+ ADD_BFCACHESTATUS_TO_STRING(HAS_USED_VR);
+ ADD_BFCACHESTATUS_TO_STRING(CONTAINS_REMOTE_SUBFRAMES);
+ ADD_BFCACHESTATUS_TO_STRING(NOT_ONLY_TOPLEVEL_IN_BCG);
+ ADD_BFCACHESTATUS_TO_STRING(BEFOREUNLOAD_LISTENER);
+ ADD_BFCACHESTATUS_TO_STRING(ACTIVE_LOCK);
+
+#undef ADD_BFCACHESTATUS_TO_STRING
+
+ MOZ_ASSERT(aFlags == 0,
+ "Missing stringification for enum value in BFCacheStatus.");
+ return flags;
+}
+
+mozilla::ipc::IPCResult WindowGlobalParent::RecvUpdateBFCacheStatus(
+ const uint32_t& aOnFlags, const uint32_t& aOffFlags) {
+ if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Debug))) {
+ nsAutoCString uri("[no uri]");
+ if (mDocumentURI) {
+ uri = mDocumentURI->GetSpecOrDefault();
+ }
+ MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug,
+ ("Setting BFCache flags for %s +(%s) -(%s)", uri.get(),
+ BFCacheStatusToString(aOnFlags).get(),
+ BFCacheStatusToString(aOffFlags).get()));
+ }
+ mBFCacheStatus |= aOnFlags;
+ mBFCacheStatus &= ~aOffFlags;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+WindowGlobalParent::RecvUpdateActivePeerConnectionStatus(bool aIsAdded) {
+ if (aIsAdded) {
+ RecvUpdateBFCacheStatus(BFCacheStatus::ACTIVE_PEER_CONNECTION, 0);
+ } else {
+ RecvUpdateBFCacheStatus(0, BFCacheStatus::ACTIVE_PEER_CONNECTION);
+ }
+
+ if (WindowGlobalParent* top = TopWindowContext()) {
+ CheckedUint32 newValue(top->mNumOfProcessesWithActivePeerConnections);
+ if (aIsAdded) {
+ ++newValue;
+ } else {
+ --newValue;
+ }
+ if (!newValue.isValid()) {
+ return IPC_FAIL(this,
+ "mNumOfProcessesWithActivePeerConnections overflowed");
+ }
+
+ top->mNumOfProcessesWithActivePeerConnections = newValue.value();
+ Unused << top->SetHasActivePeerConnections(newValue.value() > 0);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult WindowGlobalParent::RecvSetSingleChannelId(
+ const Maybe<uint64_t>& aSingleChannelId) {
+ mSingleChannelId = aSingleChannelId;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult WindowGlobalParent::RecvSetDocumentDomain(
+ NotNull<nsIURI*> aDomain) {
+ if (mSandboxFlags & SANDBOXED_DOMAIN) {
+ // We're sandboxed; disallow setting domain
+ return IPC_FAIL(this, "Sandbox disallows domain setting.");
+ }
+
+ // Might need to do a featurepolicy check here, like we currently do in the
+ // child process?
+
+ nsCOMPtr<nsIURI> uri;
+ mDocumentPrincipal->GetDomain(getter_AddRefs(uri));
+ if (!uri) {
+ uri = mDocumentPrincipal->GetURI();
+ if (!uri) {
+ return IPC_OK();
+ }
+ }
+
+ if (!Document::IsValidDomain(uri, aDomain)) {
+ // Error: illegal domain
+ return IPC_FAIL(
+ this, "Setting domain that's not a suffix of existing domain value.");
+ }
+
+ if (Group()->IsPotentiallyCrossOriginIsolated()) {
+ return IPC_FAIL(this, "Setting domain in a cross-origin isolated BC.");
+ }
+
+ mDocumentPrincipal->SetDomain(aDomain);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult WindowGlobalParent::RecvReloadWithHttpsOnlyException() {
+ nsresult rv;
+ nsCOMPtr<nsIURI> currentUri = BrowsingContext()->Top()->GetCurrentURI();
+
+ if (!currentUri) {
+ return IPC_FAIL(this, "HTTPS-only mode: Failed to get current URI");
+ }
+
+ bool isViewSource = currentUri->SchemeIs("view-source");
+
+ nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(currentUri);
+ nsCOMPtr<nsIURI> innerURI;
+ if (isViewSource) {
+ nestedURI->GetInnerURI(getter_AddRefs(innerURI));
+ } else {
+ innerURI = currentUri;
+ }
+
+ if (!innerURI->SchemeIs("https") && !innerURI->SchemeIs("http")) {
+ return IPC_FAIL(this, "HTTPS-only mode: Illegal state");
+ }
+
+ // If the error page is within an iFrame, we create an exception for whatever
+ // scheme the top-level site is currently on, because the user wants to
+ // unbreak the iFrame and not the top-level page. When the error page shows up
+ // on a top-level request, then we replace the scheme with http, because the
+ // user wants to unbreak the whole page.
+ nsCOMPtr<nsIURI> newURI;
+ if (!BrowsingContext()->IsTop()) {
+ newURI = innerURI;
+ } else {
+ Unused << NS_MutateURI(innerURI).SetScheme("http"_ns).Finalize(
+ getter_AddRefs(newURI));
+ }
+
+ OriginAttributes originAttributes =
+ TopWindowContext()->DocumentPrincipal()->OriginAttributesRef();
+
+ originAttributes.SetFirstPartyDomain(true, newURI);
+
+ nsCOMPtr<nsIPermissionManager> permMgr =
+ components::PermissionManager::Service();
+ if (!permMgr) {
+ return IPC_FAIL(
+ this, "HTTPS-only mode: Failed to get Permission Manager service");
+ }
+
+ nsCOMPtr<nsIPrincipal> principal =
+ BasePrincipal::CreateContentPrincipal(newURI, originAttributes);
+
+ rv = permMgr->AddFromPrincipal(
+ principal, "https-only-load-insecure"_ns,
+ nsIHttpsOnlyModePermission::LOAD_INSECURE_ALLOW_SESSION,
+ nsIPermissionManager::EXPIRE_SESSION, 0);
+
+ if (NS_FAILED(rv)) {
+ return IPC_FAIL(
+ this, "HTTPS-only mode: Failed to add permission to the principal");
+ }
+
+ nsCOMPtr<nsIURI> insecureURI = newURI;
+ if (isViewSource) {
+ nsAutoCString spec;
+ MOZ_ALWAYS_SUCCEEDS(newURI->GetSpec(spec));
+ if (NS_FAILED(
+ NS_NewURI(getter_AddRefs(insecureURI), "view-source:"_ns + spec))) {
+ return IPC_FAIL(
+ this, "HTTPS-only mode: Failed to re-construct view-source URI");
+ }
+ }
+
+ RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(insecureURI);
+ loadState->SetTriggeringPrincipal(nsContentUtils::GetSystemPrincipal());
+ loadState->SetLoadType(LOAD_NORMAL_REPLACE);
+
+ RefPtr<CanonicalBrowsingContext> topBC = BrowsingContext()->Top();
+ topBC->LoadURI(loadState, /* setNavigating */ true);
+
+ return IPC_OK();
+}
+
+IPCResult WindowGlobalParent::RecvDiscoverIdentityCredentialFromExternalSource(
+ const IdentityCredentialRequestOptions& aOptions,
+ const DiscoverIdentityCredentialFromExternalSourceResolver& aResolver) {
+ IdentityCredential::DiscoverFromExternalSourceInMainProcess(
+ DocumentPrincipal(), this->BrowsingContext(), aOptions)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [aResolver](const IPCIdentityCredential& aResult) {
+ return aResolver(Some(aResult));
+ },
+ [aResolver](nsresult aErr) { aResolver(Nothing()); });
+ return IPC_OK();
+}
+
+IPCResult WindowGlobalParent::RecvGetStorageAccessPermission(
+ GetStorageAccessPermissionResolver&& aResolve) {
+ WindowGlobalParent* top = TopWindowContext();
+ if (!top) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ nsIPrincipal* topPrincipal = top->DocumentPrincipal();
+ nsIPrincipal* principal = DocumentPrincipal();
+ uint32_t result;
+ nsresult rv = AntiTrackingUtils::TestStoragePermissionInParent(
+ topPrincipal, principal, &result);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ aResolve(nsIPermissionManager::UNKNOWN_ACTION);
+ return IPC_OK();
+ }
+
+ aResolve(result);
+ return IPC_OK();
+}
+
+void WindowGlobalParent::ActorDestroy(ActorDestroyReason aWhy) {
+ if (GetBrowsingContext()->IsTopContent()) {
+ Telemetry::Accumulate(Telemetry::ORB_DID_EVER_BLOCK_RESPONSE,
+ mShouldReportHasBlockedOpaqueResponse);
+ }
+
+ if (mPageUseCountersWindow) {
+ mPageUseCountersWindow->FinishAccumulatingPageUseCounters();
+ mPageUseCountersWindow = nullptr;
+ }
+
+ if (GetBrowsingContext()->IsTopContent() &&
+ !mDocumentPrincipal->SchemeIs("about")) {
+ // Record the page load
+ uint32_t pageLoaded = 1;
+ Accumulate(Telemetry::MIXED_CONTENT_UNBLOCK_COUNTER, pageLoaded);
+
+ // Record the mixed content status of the docshell in Telemetry
+ enum {
+ NO_MIXED_CONTENT = 0, // There is no Mixed Content on the page
+ MIXED_DISPLAY_CONTENT =
+ 1, // The page attempted to load Mixed Display Content
+ MIXED_ACTIVE_CONTENT =
+ 2, // The page attempted to load Mixed Active Content
+ MIXED_DISPLAY_AND_ACTIVE_CONTENT = 3 // The page attempted to load Mixed
+ // Display & Mixed Active Content
+ };
+
+ bool hasMixedDisplay =
+ mSecurityState &
+ (nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT |
+ nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT);
+ bool hasMixedActive =
+ mSecurityState &
+ (nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT |
+ nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT);
+
+ uint32_t mixedContentLevel = NO_MIXED_CONTENT;
+ if (hasMixedDisplay && hasMixedActive) {
+ mixedContentLevel = MIXED_DISPLAY_AND_ACTIVE_CONTENT;
+ } else if (hasMixedActive) {
+ mixedContentLevel = MIXED_ACTIVE_CONTENT;
+ } else if (hasMixedDisplay) {
+ mixedContentLevel = MIXED_DISPLAY_CONTENT;
+ }
+ Accumulate(Telemetry::MIXED_CONTENT_PAGE_LOAD, mixedContentLevel);
+
+ if (GetDocTreeHadMedia()) {
+ ScalarAdd(Telemetry::ScalarID::MEDIA_ELEMENT_IN_PAGE_COUNT, 1);
+ }
+ }
+
+ ContentParent* cp = nullptr;
+ if (!IsInProcess()) {
+ cp = static_cast<ContentParent*>(Manager()->Manager());
+ }
+
+ Group()->EachOtherParent(cp, [&](ContentParent* otherContent) {
+ // Keep the WindowContext and our BrowsingContextGroup alive until other
+ // processes have acknowledged it has been discarded.
+ Group()->AddKeepAlive();
+ auto callback = [self = RefPtr{this}](auto) {
+ self->Group()->RemoveKeepAlive();
+ };
+ otherContent->SendDiscardWindowContext(InnerWindowId(), callback, callback);
+ });
+
+ // Note that our WindowContext has become discarded.
+ WindowContext::Discard();
+
+ // Report content blocking log when destroyed.
+ // There shouldn't have any content blocking log when a document is loaded in
+ // the parent process(See NotifyContentBlockingEvent), so we could skip
+ // reporting log when it is in-process.
+ if (!IsInProcess()) {
+ RefPtr<BrowserParent> browserParent =
+ static_cast<BrowserParent*>(Manager());
+ if (browserParent) {
+ nsCOMPtr<nsILoadContext> loadContext = browserParent->GetLoadContext();
+ if (loadContext && !loadContext->UsePrivateBrowsing() &&
+ BrowsingContext()->IsTopContent()) {
+ GetContentBlockingLog()->ReportLog(DocumentPrincipal());
+
+ if (mDocumentURI && (net::SchemeIsHTTP(mDocumentURI) ||
+ net::SchemeIsHTTPS(mDocumentURI))) {
+ GetContentBlockingLog()->ReportCanvasFingerprintingLog(
+ DocumentPrincipal());
+ GetContentBlockingLog()->ReportFontFingerprintingLog(
+ DocumentPrincipal());
+ GetContentBlockingLog()->ReportEmailTrackingLog(DocumentPrincipal());
+ }
+ }
+ }
+ }
+
+ // Destroy our JSWindowActors, and reject any pending queries.
+ JSActorDidDestroy();
+
+ nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+ if (obs) {
+ obs->NotifyObservers(ToSupports(this), "window-global-destroyed", nullptr);
+ }
+
+ if (mOriginCounter) {
+ mOriginCounter->Accumulate();
+ }
+}
+
+WindowGlobalParent::~WindowGlobalParent() = default;
+
+JSObject* WindowGlobalParent::WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) {
+ return WindowGlobalParent_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+nsIGlobalObject* WindowGlobalParent::GetParentObject() {
+ return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
+}
+
+nsIDOMProcessParent* WindowGlobalParent::GetDomProcess() {
+ if (RefPtr<BrowserParent> browserParent = GetBrowserParent()) {
+ return browserParent->Manager();
+ }
+ return InProcessParent::Singleton();
+}
+
+void WindowGlobalParent::DidBecomeCurrentWindowGlobal(bool aCurrent) {
+ WindowGlobalParent* top = BrowsingContext()->GetTopWindowContext();
+ if (top && top->mOriginCounter) {
+ top->mOriginCounter->UpdateSiteOriginsFrom(this,
+ /* aIncrease = */ aCurrent);
+ }
+
+ if (!aCurrent && Fullscreen()) {
+ ExitTopChromeDocumentFullscreen();
+ }
+}
+
+bool WindowGlobalParent::ShouldTrackSiteOriginTelemetry() {
+ CanonicalBrowsingContext* bc = BrowsingContext();
+
+ if (!bc->IsTopContent()) {
+ return false;
+ }
+
+ RefPtr<BrowserParent> browserParent = GetBrowserParent();
+ if (!browserParent ||
+ !IsWebRemoteType(browserParent->Manager()->GetRemoteType())) {
+ return false;
+ }
+
+ return DocumentPrincipal()->GetIsContentPrincipal();
+}
+
+void WindowGlobalParent::AddSecurityState(uint32_t aStateFlags) {
+ MOZ_ASSERT(TopWindowContext() == this);
+ MOZ_ASSERT((aStateFlags &
+ (nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT |
+ nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT |
+ nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT |
+ nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT |
+ nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADED |
+ nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADE_FAILED |
+ nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADED_FIRST)) ==
+ aStateFlags,
+ "Invalid flags specified!");
+
+ if ((mSecurityState & aStateFlags) == aStateFlags) {
+ return;
+ }
+
+ mSecurityState |= aStateFlags;
+
+ if (GetBrowsingContext()->GetCurrentWindowGlobal() == this) {
+ GetBrowsingContext()->UpdateSecurityState();
+ }
+}
+
+bool WindowGlobalParent::HasActivePeerConnections() {
+ MOZ_ASSERT(TopWindowContext() == this,
+ "mNumOfProcessesWithActivePeerConnections is set only "
+ "in the top window context");
+ return mNumOfProcessesWithActivePeerConnections > 0;
+}
+
+void WindowGlobalParent::ExitTopChromeDocumentFullscreen() {
+ RefPtr<CanonicalBrowsingContext> chromeTop =
+ BrowsingContext()->TopCrossChromeBoundary();
+ if (Document* chromeDoc = chromeTop->GetDocument()) {
+ Document::ClearPendingFullscreenRequests(chromeDoc);
+ if (chromeDoc->Fullscreen()) {
+ // This only clears the DOM fullscreen, will not exit from browser UI
+ // fullscreen mode.
+ Document::AsyncExitFullscreen(chromeDoc);
+ }
+ }
+}
+
+void WindowGlobalParent::SetShouldReportHasBlockedOpaqueResponse(
+ nsContentPolicyType aContentPolicy) {
+ // It's always okay to block TYPE_BEACON, TYPE_PING and TYPE_CSP_REPORT in
+ // the parent process because content processes can do nothing to their
+ // responses. Hence excluding them from the telemetry as blocking
+ // them have no webcompat concerns.
+ if (aContentPolicy != nsIContentPolicy::TYPE_BEACON &&
+ aContentPolicy != nsIContentPolicy::TYPE_PING &&
+ aContentPolicy != nsIContentPolicy::TYPE_CSP_REPORT) {
+ if (IsTop()) {
+ mShouldReportHasBlockedOpaqueResponse = true;
+ }
+ }
+}
+
+IPCResult WindowGlobalParent::RecvSetCookies(
+ const nsCString& aBaseDomain, const OriginAttributes& aOriginAttributes,
+ nsIURI* aHost, bool aFromHttp, const nsTArray<CookieStruct>& aCookies) {
+ // Get CookieServiceParent via
+ // ContentParent->NeckoParent->CookieServiceParent.
+ ContentParent* contentParent = GetContentParent();
+ NS_ENSURE_TRUE(contentParent, IPC_OK());
+
+ net::PNeckoParent* neckoParent =
+ LoneManagedOrNullAsserts(contentParent->ManagedPNeckoParent());
+ NS_ENSURE_TRUE(neckoParent, IPC_OK());
+ net::PCookieServiceParent* csParent =
+ LoneManagedOrNullAsserts(neckoParent->ManagedPCookieServiceParent());
+ NS_ENSURE_TRUE(csParent, IPC_OK());
+ auto* cs = static_cast<net::CookieServiceParent*>(csParent);
+
+ return cs->SetCookies(aBaseDomain, aOriginAttributes, aHost, aFromHttp,
+ aCookies, GetBrowsingContext());
+}
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(WindowGlobalParent, WindowContext,
+ mPageUseCountersWindow)
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(WindowGlobalParent,
+ WindowContext)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WindowGlobalParent)
+NS_INTERFACE_MAP_END_INHERITING(WindowContext)
+
+NS_IMPL_ADDREF_INHERITED(WindowGlobalParent, WindowContext)
+NS_IMPL_RELEASE_INHERITED(WindowGlobalParent, WindowContext)
+
+} // namespace mozilla::dom
diff --git a/dom/ipc/WindowGlobalParent.h b/dom/ipc/WindowGlobalParent.h
new file mode 100644
index 0000000000..be801de0d3
--- /dev/null
+++ b/dom/ipc/WindowGlobalParent.h
@@ -0,0 +1,444 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+/* 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/. */
+
+#ifndef mozilla_dom_WindowGlobalParent_h
+#define mozilla_dom_WindowGlobalParent_h
+
+#include "mozilla/ContentBlockingLog.h"
+#include "mozilla/ContentBlockingNotifier.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/dom/ClientInfo.h"
+#include "mozilla/dom/ClientIPCTypes.h"
+#include "mozilla/dom/DOMRect.h"
+#include "mozilla/dom/PWindowGlobalParent.h"
+#include "mozilla/dom/WindowContext.h"
+#include "mozilla/dom/WindowGlobalActorsBinding.h"
+#include "nsTHashMap.h"
+#include "nsRefPtrHashtable.h"
+#include "nsWrapperCache.h"
+#include "nsISupports.h"
+#include "nsIDOMProcessParent.h"
+#include "mozilla/dom/WindowGlobalActor.h"
+#include "mozilla/dom/CanonicalBrowsingContext.h"
+#include "mozilla/net/CookieJarSettings.h"
+
+class nsIPrincipal;
+class nsIURI;
+class nsFrameLoader;
+
+namespace mozilla {
+
+namespace gfx {
+class CrossProcessPaint;
+} // namespace gfx
+
+namespace dom {
+
+class BrowserParent;
+class WindowGlobalChild;
+class JSWindowActorParent;
+class JSActorMessageMeta;
+struct PageUseCounters;
+class WindowSessionStoreState;
+struct WindowSessionStoreUpdate;
+class SSCacheQueryResult;
+
+/**
+ * A handle in the parent process to a specific nsGlobalWindowInner object.
+ */
+class WindowGlobalParent final : public WindowContext,
+ public WindowGlobalActor,
+ public PWindowGlobalParent {
+ friend class gfx::CrossProcessPaint;
+ friend class PWindowGlobalParent;
+
+ public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(WindowGlobalParent,
+ WindowContext)
+
+ static already_AddRefed<WindowGlobalParent> GetByInnerWindowId(
+ uint64_t aInnerWindowId);
+
+ static already_AddRefed<WindowGlobalParent> GetByInnerWindowId(
+ const GlobalObject& aGlobal, uint64_t aInnerWindowId) {
+ return GetByInnerWindowId(aInnerWindowId);
+ }
+
+ // The same as the corresponding methods on `WindowContext`, except that the
+ // return types are already cast to their parent-process type variants, such
+ // as `WindowGlobalParent` or `CanonicalBrowsingContext`.
+ WindowGlobalParent* GetParentWindowContext() {
+ return static_cast<WindowGlobalParent*>(
+ WindowContext::GetParentWindowContext());
+ }
+ WindowGlobalParent* TopWindowContext() {
+ return static_cast<WindowGlobalParent*>(WindowContext::TopWindowContext());
+ }
+ CanonicalBrowsingContext* GetBrowsingContext() const {
+ return CanonicalBrowsingContext::Cast(WindowContext::GetBrowsingContext());
+ }
+
+ Element* GetRootOwnerElement();
+
+ // Has this actor been shut down
+ bool IsClosed() { return !CanSend(); }
+
+ // Get the other side of this actor if it is an in-process actor. Returns
+ // |nullptr| if the actor has been torn down, or is not in-process.
+ already_AddRefed<WindowGlobalChild> GetChildActor();
+
+ // Get a JS actor object by name.
+ already_AddRefed<JSWindowActorParent> GetActor(JSContext* aCx,
+ const nsACString& aName,
+ ErrorResult& aRv);
+ already_AddRefed<JSWindowActorParent> GetExistingActor(
+ const nsACString& aName);
+
+ // Get this actor's manager if it is not an in-process actor. Returns
+ // |nullptr| if the actor has been torn down, or is in-process.
+ BrowserParent* GetBrowserParent();
+
+ ContentParent* GetContentParent();
+
+ // The principal of this WindowGlobal. This value will not change over the
+ // lifetime of the WindowGlobal object, even to reflect changes in
+ // |document.domain|.
+ nsIPrincipal* DocumentPrincipal() { return mDocumentPrincipal; }
+
+ nsIPrincipal* DocumentStoragePrincipal() { return mDocumentStoragePrincipal; }
+
+ // The BrowsingContext which this WindowGlobal has been loaded into.
+ // FIXME: It's quite awkward that this method has a slightly different name
+ // than the one on WindowContext.
+ CanonicalBrowsingContext* BrowsingContext() override {
+ return GetBrowsingContext();
+ }
+
+ // Get the root nsFrameLoader object for the tree of BrowsingContext nodes
+ // which this WindowGlobal is a part of. This will be the nsFrameLoader
+ // holding the BrowserParent for remote tabs, and the root content frameloader
+ // for non-remote tabs.
+ already_AddRefed<nsFrameLoader> GetRootFrameLoader();
+
+ // The current URI which loaded in the document.
+ nsIURI* GetDocumentURI() override { return mDocumentURI; }
+
+ void GetDocumentTitle(nsAString& aTitle) const {
+ aTitle = mDocumentTitle.valueOr(nsString());
+ }
+
+ nsIPrincipal* GetContentBlockingAllowListPrincipal() const {
+ return mDocContentBlockingAllowListPrincipal;
+ }
+
+ Maybe<ClientInfo> GetClientInfo() { return mClientInfo; }
+
+ uint64_t ContentParentId();
+
+ int32_t OsPid();
+
+ bool IsCurrentGlobal();
+
+ bool IsActiveInTab();
+
+ bool IsProcessRoot();
+
+ uint32_t ContentBlockingEvents();
+
+ void GetContentBlockingLog(nsAString& aLog);
+
+ bool IsInitialDocument() {
+ return mIsInitialDocument.isSome() && mIsInitialDocument.value();
+ }
+
+ already_AddRefed<mozilla::dom::Promise> PermitUnload(
+ PermitUnloadAction aAction, uint32_t aTimeout, mozilla::ErrorResult& aRv);
+
+ void PermitUnload(std::function<void(bool)>&& aResolver);
+
+ already_AddRefed<mozilla::dom::Promise> DrawSnapshot(
+ const DOMRect* aRect, double aScale, const nsACString& aBackgroundColor,
+ bool aResetScrollPosition, mozilla::ErrorResult& aRv);
+
+ static already_AddRefed<WindowGlobalParent> CreateDisconnected(
+ const WindowGlobalInit& aInit);
+
+ // Initialize the mFrameLoader fields for a created WindowGlobalParent. Must
+ // be called after setting the Manager actor.
+ void Init() final;
+
+ nsIGlobalObject* GetParentObject();
+ JSObject* WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) override;
+
+ void NotifyContentBlockingEvent(
+ uint32_t aEvent, nsIRequest* aRequest, bool aBlocked,
+ const nsACString& aTrackingOrigin,
+ const nsTArray<nsCString>& aTrackingFullHashes,
+ const Maybe<
+ ContentBlockingNotifier::StorageAccessPermissionGrantedReason>&
+ aReason,
+ const Maybe<ContentBlockingNotifier::CanvasFingerprinter>&
+ aCanvasFingerprinter,
+ const Maybe<bool> aCanvasFingerprinterKnownText);
+
+ ContentBlockingLog* GetContentBlockingLog() { return &mContentBlockingLog; }
+
+ nsIDOMProcessParent* GetDomProcess();
+
+ nsICookieJarSettings* CookieJarSettings() { return mCookieJarSettings; }
+
+ nsICookieJarSettings* GetCookieJarSettings() const {
+ return mCookieJarSettings;
+ }
+
+ bool DocumentHasLoaded() { return mDocumentHasLoaded; }
+
+ bool DocumentHasUserInteracted() { return mDocumentHasUserInteracted; }
+
+ uint32_t SandboxFlags() { return mSandboxFlags; }
+
+ bool GetDocumentBlockAllMixedContent() { return mBlockAllMixedContent; }
+
+ bool GetDocumentUpgradeInsecureRequests() { return mUpgradeInsecureRequests; }
+
+ void DidBecomeCurrentWindowGlobal(bool aCurrent);
+
+ uint32_t HttpsOnlyStatus() { return mHttpsOnlyStatus; }
+
+ void AddSecurityState(uint32_t aStateFlags);
+ uint32_t GetSecurityFlags() { return mSecurityState; }
+
+ nsITransportSecurityInfo* GetSecurityInfo() { return mSecurityInfo; }
+
+ const nsACString& GetRemoteType() override;
+
+ void NotifySessionStoreUpdatesComplete(Element* aEmbedder);
+
+ Maybe<uint64_t> GetSingleChannelId() { return mSingleChannelId; }
+
+ uint32_t GetBFCacheStatus() { return mBFCacheStatus; }
+
+ bool HasActivePeerConnections();
+
+ bool Fullscreen() { return mFullscreen; }
+ void SetFullscreen(bool aFullscreen) { mFullscreen = aFullscreen; }
+
+ void ExitTopChromeDocumentFullscreen();
+
+ void SetShouldReportHasBlockedOpaqueResponse(
+ nsContentPolicyType aContentPolicy);
+
+ protected:
+ already_AddRefed<JSActor> InitJSActor(JS::Handle<JSObject*> aMaybeActor,
+ const nsACString& aName,
+ ErrorResult& aRv) override;
+ mozilla::ipc::IProtocol* AsNativeActor() override { return this; }
+
+ // IPC messages
+ mozilla::ipc::IPCResult RecvLoadURI(
+ const MaybeDiscarded<dom::BrowsingContext>& aTargetBC,
+ nsDocShellLoadState* aLoadState, bool aSetNavigating);
+ mozilla::ipc::IPCResult RecvInternalLoad(nsDocShellLoadState* aLoadState);
+ mozilla::ipc::IPCResult RecvUpdateDocumentURI(NotNull<nsIURI*> aURI);
+ mozilla::ipc::IPCResult RecvUpdateDocumentPrincipal(
+ nsIPrincipal* aNewDocumentPrincipal,
+ nsIPrincipal* aNewDocumentStoragePrincipal);
+ mozilla::ipc::IPCResult RecvUpdateDocumentHasLoaded(bool aDocumentHasLoaded);
+ mozilla::ipc::IPCResult RecvUpdateDocumentHasUserInteracted(
+ bool aDocumentHasUserInteracted);
+ mozilla::ipc::IPCResult RecvUpdateSandboxFlags(uint32_t aSandboxFlags);
+ mozilla::ipc::IPCResult RecvUpdateDocumentCspSettings(
+ bool aBlockAllMixedContent, bool aUpgradeInsecureRequests);
+ mozilla::ipc::IPCResult RecvUpdateDocumentTitle(const nsString& aTitle);
+ mozilla::ipc::IPCResult RecvUpdateHttpsOnlyStatus(uint32_t aHttpsOnlyStatus);
+ mozilla::ipc::IPCResult RecvSetIsInitialDocument(bool aIsInitialDocument) {
+ if (aIsInitialDocument && mIsInitialDocument.isSome() &&
+ (mIsInitialDocument.value() != aIsInitialDocument)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ mIsInitialDocument = Some(aIsInitialDocument);
+ return IPC_OK();
+ }
+ mozilla::ipc::IPCResult RecvUpdateDocumentSecurityInfo(
+ nsITransportSecurityInfo* aSecurityInfo);
+ mozilla::ipc::IPCResult RecvSetClientInfo(
+ const IPCClientInfo& aIPCClientInfo);
+ mozilla::ipc::IPCResult RecvDestroy();
+ mozilla::ipc::IPCResult RecvRawMessage(
+ const JSActorMessageMeta& aMeta, const Maybe<ClonedMessageData>& aData,
+ const Maybe<ClonedMessageData>& aStack);
+
+ mozilla::ipc::IPCResult RecvGetContentBlockingEvents(
+ GetContentBlockingEventsResolver&& aResolver);
+ mozilla::ipc::IPCResult RecvUpdateCookieJarSettings(
+ const CookieJarSettingsArgs& aCookieJarSettingsArgs);
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ void DrawSnapshotInternal(gfx::CrossProcessPaint* aPaint,
+ const Maybe<IntRect>& aRect, float aScale,
+ nscolor aBackgroundColor, uint32_t aFlags);
+
+ // WebShare API - try to share
+ mozilla::ipc::IPCResult RecvShare(IPCWebShareData&& aData,
+ ShareResolver&& aResolver);
+
+ mozilla::ipc::IPCResult RecvCheckPermitUnload(
+ bool aHasInProcessBlocker, XPCOMPermitUnloadAction aAction,
+ CheckPermitUnloadResolver&& aResolver);
+
+ mozilla::ipc::IPCResult RecvExpectPageUseCounters(
+ const MaybeDiscarded<dom::WindowContext>& aTop);
+ mozilla::ipc::IPCResult RecvAccumulatePageUseCounters(
+ const UseCounters& aUseCounters);
+
+ mozilla::ipc::IPCResult RecvRequestRestoreTabContent();
+
+ mozilla::ipc::IPCResult RecvUpdateBFCacheStatus(const uint32_t& aOnFlags,
+ const uint32_t& aOffFlags);
+
+ // This IPC method is to notify the parent process that the caller process
+ // creates the first active peer connection (aIsAdded = true) or closes the
+ // last active peer connection (aIsAdded = false).
+ mozilla::ipc::IPCResult RecvUpdateActivePeerConnectionStatus(bool aIsAdded);
+
+ public:
+ mozilla::ipc::IPCResult RecvSetSingleChannelId(
+ const Maybe<uint64_t>& aSingleChannelId);
+
+ mozilla::ipc::IPCResult RecvSetDocumentDomain(NotNull<nsIURI*> aDomain);
+
+ mozilla::ipc::IPCResult RecvReloadWithHttpsOnlyException();
+
+ mozilla::ipc::IPCResult RecvDiscoverIdentityCredentialFromExternalSource(
+ const IdentityCredentialRequestOptions& aOptions,
+ const DiscoverIdentityCredentialFromExternalSourceResolver& aResolver);
+
+ mozilla::ipc::IPCResult RecvGetStorageAccessPermission(
+ GetStorageAccessPermissionResolver&& aResolve);
+
+ mozilla::ipc::IPCResult RecvSetCookies(
+ const nsCString& aBaseDomain, const OriginAttributes& aOriginAttributes,
+ nsIURI* aHost, bool aFromHttp, const nsTArray<CookieStruct>& aCookies);
+
+ private:
+ WindowGlobalParent(CanonicalBrowsingContext* aBrowsingContext,
+ uint64_t aInnerWindowId, uint64_t aOuterWindowId,
+ FieldValues&& aInit);
+
+ ~WindowGlobalParent();
+
+ bool ShouldTrackSiteOriginTelemetry();
+ void FinishAccumulatingPageUseCounters();
+
+ // Returns failure if the new storage principal cannot be validated
+ // against the current document principle.
+ nsresult SetDocumentStoragePrincipal(
+ nsIPrincipal* aNewDocumentStoragePrincipal);
+
+ // NOTE: Neither this document principal nor the document storage
+ // principal doesn't reflect possible |document.domain| mutations
+ // which may have been made in the actual document.
+ nsCOMPtr<nsIPrincipal> mDocumentPrincipal;
+ nsCOMPtr<nsIPrincipal> mDocumentStoragePrincipal;
+
+ // The principal to use for the content blocking allow list.
+ nsCOMPtr<nsIPrincipal> mDocContentBlockingAllowListPrincipal;
+
+ nsCOMPtr<nsIURI> mDocumentURI;
+ Maybe<nsString> mDocumentTitle;
+
+ Maybe<bool> mIsInitialDocument;
+
+ // True if this window has a "beforeunload" event listener.
+ bool mHasBeforeUnload;
+
+ // The log of all content blocking actions taken on the document related to
+ // this WindowGlobalParent. This is only stored on top-level documents and
+ // includes the activity log for all of the nested subdocuments as well.
+ ContentBlockingLog mContentBlockingLog;
+
+ uint32_t mSecurityState = 0;
+
+ Maybe<ClientInfo> mClientInfo;
+ // Fields being mirrored from the corresponding document
+ nsCOMPtr<nsICookieJarSettings> mCookieJarSettings;
+ nsCOMPtr<nsITransportSecurityInfo> mSecurityInfo;
+
+ uint32_t mSandboxFlags;
+
+ struct OriginCounter {
+ void UpdateSiteOriginsFrom(WindowGlobalParent* aParent, bool aIncrease);
+ void Accumulate();
+
+ nsTHashMap<nsCStringHashKey, int32_t> mOriginMap;
+ uint32_t mMaxOrigins = 0;
+ };
+
+ // Used to collect unique site origin telemetry.
+ //
+ // Is only Some() on top-level content windows.
+ Maybe<OriginCounter> mOriginCounter;
+
+ bool mDocumentHasLoaded;
+ bool mDocumentHasUserInteracted;
+ bool mDocumentTreeWouldPreloadResources = false;
+ bool mBlockAllMixedContent;
+ bool mUpgradeInsecureRequests;
+
+ // HTTPS-Only Mode flags
+ uint32_t mHttpsOnlyStatus;
+
+ // The window of the document whose page use counters our document's use
+ // counters will contribute to. (If we are a top-level document, this
+ // will point to ourselves.)
+ RefPtr<WindowGlobalParent> mPageUseCountersWindow;
+
+ // Our page use counters, if we are a top-level document.
+ UniquePtr<PageUseCounters> mPageUseCounters;
+
+ // Whether we have sent our page use counters, and so should ignore any
+ // subsequent ExpectPageUseCounters calls.
+ bool mSentPageUseCounters = false;
+
+ uint32_t mBFCacheStatus = 0;
+
+ // If this WindowGlobalParent is a top window, this field indicates how many
+ // child processes have active peer connections for this window and its
+ // descendants.
+ uint32_t mNumOfProcessesWithActivePeerConnections = 0;
+
+ // mSingleChannelId records whether the loadgroup contains a single request
+ // with an id. If there is one channel in the loadgroup and it has an id then
+ // mSingleChannelId is set to Some(id) (ids are non-zero). If there is one
+ // request in the loadgroup and it's not a channel or it doesn't have an id,
+ // or there are multiple requests in the loadgroup, then mSingleChannelId is
+ // set to Some(0). If there are no requests in the loadgroup then
+ // mSingleChannelId is set to Nothing().
+ // Note: We ignore favicon loads when considering the requests in the
+ // loadgroup.
+ Maybe<uint64_t> mSingleChannelId;
+
+ // True if the current loaded document is in fullscreen.
+ bool mFullscreen = false;
+
+ bool mShouldReportHasBlockedOpaqueResponse = false;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+inline nsISupports* ToSupports(
+ mozilla::dom::WindowGlobalParent* aWindowGlobal) {
+ return static_cast<mozilla::dom::WindowContext*>(aWindowGlobal);
+}
+
+#endif // !defined(mozilla_dom_WindowGlobalParent_h)
diff --git a/dom/ipc/WindowGlobalTypes.ipdlh b/dom/ipc/WindowGlobalTypes.ipdlh
new file mode 100644
index 0000000000..d669aa44f6
--- /dev/null
+++ b/dom/ipc/WindowGlobalTypes.ipdlh
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include "mozilla/dom/PermissionMessageUtils.h";
+include "mozilla/ipc/TransportSecurityInfoUtils.h";
+include "mozilla/ipc/URIUtils.h";
+
+include NeckoChannelParams;
+include DOMTypes;
+
+using mozilla::dom::WindowContextInitializer from "mozilla/dom/WindowContext.h";
+[RefCounted] using class nsITransportSecurityInfo from "nsITransportSecurityInfo.h";
+
+namespace mozilla {
+namespace dom {
+
+struct WindowGlobalInit
+{
+ // Fields which are synchronized to other processes are found here.
+ WindowContextInitializer context;
+
+ // Private fields only shared with the parent process.
+ nullable nsIPrincipal principal;
+ nullable nsIPrincipal storagePrincipal;
+ nullable nsIURI documentURI;
+
+ bool isInitialDocument;
+ bool blockAllMixedContent;
+ bool upgradeInsecureRequests;
+ uint32_t sandboxFlags;
+ CookieJarSettingsArgs cookieJarSettings;
+ uint32_t httpsOnlyStatus;
+ nullable nsITransportSecurityInfo securityInfo;
+};
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/components.conf b/dom/ipc/components.conf
new file mode 100644
index 0000000000..c027392f4b
--- /dev/null
+++ b/dom/ipc/components.conf
@@ -0,0 +1,17 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+Classes = [
+ {
+ 'cid': '{91fdaa4e-eba4-4ed3-831c-ce05c142822d}',
+ 'contract_ids': ['@mozilla.org/login-detection-service;1'],
+ 'type': 'mozilla::dom::LoginDetectionService',
+ 'singleton': True,
+ 'headers': ['/dom/ipc/LoginDetectionService.h'],
+ 'constructor': 'mozilla::dom::LoginDetectionService::GetSingleton',
+ 'processes': ProcessSelector.MAIN_PROCESS_ONLY,
+ },
+]
diff --git a/dom/ipc/gtest/ProcessIsolationTest.cpp b/dom/ipc/gtest/ProcessIsolationTest.cpp
new file mode 100644
index 0000000000..197feabf8c
--- /dev/null
+++ b/dom/ipc/gtest/ProcessIsolationTest.cpp
@@ -0,0 +1,234 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/dom/ProcessIsolation.h"
+#include "mozilla/dom/WorkerPrivate.h"
+#include "mozilla/ExpandedPrincipal.h"
+#include "mozilla/ExtensionPolicyService.h"
+#include "mozilla/gtest/MozAssertions.h"
+#include "mozilla/gtest/MozHelpers.h"
+#include "mozilla/NullPrincipal.h"
+#include "mozilla/SystemPrincipal.h"
+#include "mozilla/StaticPrefs_browser.h"
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+static nsCOMPtr<nsIPrincipal> MakeTestPrincipal(const char* aURI) {
+ nsCOMPtr<nsIURI> uri;
+ MOZ_ALWAYS_SUCCEEDS(NS_NewURI(getter_AddRefs(uri), aURI));
+ return BasePrincipal::CreateContentPrincipal(uri, {});
+}
+
+namespace {
+struct RemoteTypes {
+ nsCString mIsolated;
+ nsCString mUnisolated;
+};
+
+struct WorkerExpectation {
+ nsCOMPtr<nsIPrincipal> mPrincipal;
+ WorkerKind mWorkerKind = WorkerKindShared;
+ Result<RemoteTypes, nsresult> mExpected = Err(NS_ERROR_FAILURE);
+ nsCString mCurrentRemoteType = "fakeRemoteType"_ns;
+
+ void Check(bool aUseRemoteSubframes) {
+ nsAutoCString origin;
+ ASSERT_NS_SUCCEEDED(mPrincipal->GetOrigin(origin));
+
+ nsPrintfCString describe(
+ "origin: %s, workerKind: %s, currentRemoteType: %s, "
+ "useRemoteSubframes: %d",
+ origin.get(), mWorkerKind == WorkerKindShared ? "shared" : "service",
+ mCurrentRemoteType.get(), aUseRemoteSubframes);
+
+ auto result = IsolationOptionsForWorker(
+ mPrincipal, mWorkerKind, mCurrentRemoteType, aUseRemoteSubframes);
+ ASSERT_EQ(result.isOk(), mExpected.isOk())
+ << "Unexpected status (expected " << (mExpected.isOk() ? "ok" : "err")
+ << ") for " << describe;
+ if (mExpected.isOk()) {
+ const nsCString& expected = aUseRemoteSubframes
+ ? mExpected.inspect().mIsolated
+ : mExpected.inspect().mUnisolated;
+ ASSERT_EQ(result.inspect().mRemoteType, expected)
+ << "Unexpected remote type (expected " << expected << ") for "
+ << describe;
+ }
+ }
+};
+} // namespace
+
+static nsCString WebIsolatedRemoteType(nsIPrincipal* aPrincipal) {
+ nsAutoCString origin;
+ MOZ_ALWAYS_SUCCEEDS(aPrincipal->GetSiteOrigin(origin));
+ return FISSION_WEB_REMOTE_TYPE + "="_ns + origin;
+}
+
+static nsCString CoopCoepRemoteType(nsIPrincipal* aPrincipal) {
+ nsAutoCString origin;
+ MOZ_ALWAYS_SUCCEEDS(aPrincipal->GetSiteOrigin(origin));
+ return WITH_COOP_COEP_REMOTE_TYPE + "="_ns + origin;
+}
+
+static nsCString ServiceWorkerIsolatedRemoteType(nsIPrincipal* aPrincipal) {
+ nsAutoCString origin;
+ MOZ_ALWAYS_SUCCEEDS(aPrincipal->GetSiteOrigin(origin));
+ return SERVICEWORKER_REMOTE_TYPE + "="_ns + origin;
+}
+
+TEST(ProcessIsolationTest, WorkerOptions)
+{
+ // Forcibly enable the privileged mozilla content process for the duration of
+ // the test.
+ MOZ_ALWAYS_SUCCEEDS(Preferences::SetCString(
+ "browser.tabs.remote.separatedMozillaDomains", "addons.mozilla.org"));
+ MOZ_ALWAYS_SUCCEEDS(Preferences::SetBool(
+ "browser.tabs.remote.separatePrivilegedMozillaWebContentProcess", true));
+ auto cleanup = MakeScopeExit([&] {
+ MOZ_ALWAYS_SUCCEEDS(
+ Preferences::ClearUser("browser.tabs.remote.separatedMozillaDomains"));
+ MOZ_ALWAYS_SUCCEEDS(Preferences::ClearUser(
+ "browser.tabs.remote.separatePrivilegedMozillaWebContentProcess"));
+ });
+
+ nsCOMPtr<nsIPrincipal> systemPrincipal = SystemPrincipal::Get();
+ nsCOMPtr<nsIPrincipal> nullPrincipal =
+ NullPrincipal::CreateWithoutOriginAttributes();
+ nsCOMPtr<nsIPrincipal> secureComPrincipal =
+ MakeTestPrincipal("https://example.com");
+ nsCOMPtr<nsIPrincipal> secureOrgPrincipal =
+ MakeTestPrincipal("https://example.org");
+ nsCOMPtr<nsIPrincipal> insecureOrgPrincipal =
+ MakeTestPrincipal("http://example.org");
+ nsCOMPtr<nsIPrincipal> filePrincipal =
+ MakeTestPrincipal("file:///path/to/dir");
+ nsCOMPtr<nsIPrincipal> extensionPrincipal =
+ MakeTestPrincipal("moz-extension://fake-uuid");
+ nsCOMPtr<nsIPrincipal> privilegedMozillaPrincipal =
+ MakeTestPrincipal("https://addons.mozilla.org");
+ nsCOMPtr<nsIPrincipal> expandedPrincipal = ExpandedPrincipal::Create(
+ nsTArray{secureComPrincipal, extensionPrincipal}, {});
+ nsCOMPtr<nsIPrincipal> nullSecureComPrecursorPrincipal =
+ NullPrincipal::CreateWithInheritedAttributes(secureComPrincipal);
+
+ nsCString extensionRemoteType =
+ ExtensionPolicyService::GetSingleton().UseRemoteExtensions()
+ ? EXTENSION_REMOTE_TYPE
+ : NOT_REMOTE_TYPE;
+ nsCString fileRemoteType =
+ StaticPrefs::browser_tabs_remote_separateFileUriProcess()
+ ? FILE_REMOTE_TYPE
+ : WEB_REMOTE_TYPE;
+
+ WorkerExpectation expectations[] = {
+ // Neither service not shared workers can have expanded principals
+ {.mPrincipal = expandedPrincipal,
+ .mWorkerKind = WorkerKindService,
+ .mExpected = Err(NS_ERROR_UNEXPECTED)},
+ {.mPrincipal = expandedPrincipal,
+ .mWorkerKind = WorkerKindShared,
+ .mExpected = Err(NS_ERROR_UNEXPECTED)},
+
+ // Service workers cannot have system or null principals
+ {.mPrincipal = systemPrincipal,
+ .mWorkerKind = WorkerKindService,
+ .mExpected = Err(NS_ERROR_UNEXPECTED)},
+ {.mPrincipal = nullPrincipal,
+ .mWorkerKind = WorkerKindService,
+ .mExpected = Err(NS_ERROR_UNEXPECTED)},
+ {.mPrincipal = nullSecureComPrecursorPrincipal,
+ .mWorkerKind = WorkerKindService,
+ .mExpected = Err(NS_ERROR_UNEXPECTED)},
+
+ // Service workers with various content principals
+ {.mPrincipal = secureComPrincipal,
+ .mWorkerKind = WorkerKindService,
+ .mExpected =
+ RemoteTypes{ServiceWorkerIsolatedRemoteType(secureComPrincipal),
+ WEB_REMOTE_TYPE}},
+ {.mPrincipal = secureOrgPrincipal,
+ .mWorkerKind = WorkerKindService,
+ .mExpected =
+ RemoteTypes{ServiceWorkerIsolatedRemoteType(secureOrgPrincipal),
+ WEB_REMOTE_TYPE}},
+ {.mPrincipal = extensionPrincipal,
+ .mWorkerKind = WorkerKindService,
+ .mExpected = RemoteTypes{extensionRemoteType, extensionRemoteType}},
+ {.mPrincipal = privilegedMozillaPrincipal,
+ .mWorkerKind = WorkerKindService,
+ .mExpected = RemoteTypes{PRIVILEGEDMOZILLA_REMOTE_TYPE,
+ PRIVILEGEDMOZILLA_REMOTE_TYPE}},
+
+ // Shared Worker loaded from within a webCOOP+COEP remote type process,
+ // should load elsewhere.
+ {.mPrincipal = secureComPrincipal,
+ .mWorkerKind = WorkerKindShared,
+ .mExpected = RemoteTypes{WebIsolatedRemoteType(secureComPrincipal),
+ WEB_REMOTE_TYPE},
+ .mCurrentRemoteType = CoopCoepRemoteType(secureComPrincipal)},
+
+ // Even precursorless null principal should load elsewhere.
+ {.mPrincipal = nullPrincipal,
+ .mWorkerKind = WorkerKindShared,
+ .mExpected = RemoteTypes{WEB_REMOTE_TYPE, WEB_REMOTE_TYPE},
+ .mCurrentRemoteType = CoopCoepRemoteType(secureComPrincipal)},
+
+ // System principal shared workers can only load in the parent process or
+ // the privilegedabout remote type.
+ {.mPrincipal = systemPrincipal,
+ .mWorkerKind = WorkerKindShared,
+ .mExpected = RemoteTypes{NOT_REMOTE_TYPE, NOT_REMOTE_TYPE},
+ .mCurrentRemoteType = NOT_REMOTE_TYPE},
+ {.mPrincipal = systemPrincipal,
+ .mWorkerKind = WorkerKindShared,
+ .mExpected = RemoteTypes{PRIVILEGEDABOUT_REMOTE_TYPE,
+ PRIVILEGEDABOUT_REMOTE_TYPE},
+ .mCurrentRemoteType = PRIVILEGEDABOUT_REMOTE_TYPE},
+ {.mPrincipal = systemPrincipal,
+ .mWorkerKind = WorkerKindShared,
+ .mExpected = Err(NS_ERROR_UNEXPECTED)},
+ {.mPrincipal = systemPrincipal,
+ .mWorkerKind = WorkerKindShared,
+ .mExpected = Err(NS_ERROR_UNEXPECTED)},
+
+ // Content principals should load in the appropriate remote types,
+ // ignoring the current remote type.
+ {.mPrincipal = secureComPrincipal,
+ .mWorkerKind = WorkerKindShared,
+ .mExpected = RemoteTypes{WebIsolatedRemoteType(secureComPrincipal),
+ WEB_REMOTE_TYPE}},
+ {.mPrincipal = secureOrgPrincipal,
+ .mWorkerKind = WorkerKindShared,
+ .mExpected = RemoteTypes{WebIsolatedRemoteType(secureOrgPrincipal),
+ WEB_REMOTE_TYPE}},
+ {.mPrincipal = insecureOrgPrincipal,
+ .mWorkerKind = WorkerKindShared,
+ .mExpected = RemoteTypes{WebIsolatedRemoteType(insecureOrgPrincipal),
+ WEB_REMOTE_TYPE}},
+ {.mPrincipal = filePrincipal,
+ .mWorkerKind = WorkerKindShared,
+ .mExpected = RemoteTypes{fileRemoteType, fileRemoteType}},
+ {.mPrincipal = extensionPrincipal,
+ .mWorkerKind = WorkerKindShared,
+ .mExpected = RemoteTypes{extensionRemoteType, extensionRemoteType}},
+ {.mPrincipal = privilegedMozillaPrincipal,
+ .mWorkerKind = WorkerKindShared,
+ .mExpected = RemoteTypes{PRIVILEGEDMOZILLA_REMOTE_TYPE,
+ PRIVILEGEDMOZILLA_REMOTE_TYPE}},
+ {.mPrincipal = nullSecureComPrecursorPrincipal,
+ .mWorkerKind = WorkerKindShared,
+ .mExpected = RemoteTypes{WebIsolatedRemoteType(secureComPrincipal),
+ WEB_REMOTE_TYPE}},
+ };
+
+ for (auto& expectation : expectations) {
+ expectation.Check(true);
+ expectation.Check(false);
+ }
+}
diff --git a/dom/ipc/gtest/moz.build b/dom/ipc/gtest/moz.build
new file mode 100644
index 0000000000..c064a751ef
--- /dev/null
+++ b/dom/ipc/gtest/moz.build
@@ -0,0 +1,13 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+UNIFIED_SOURCES += [
+ "ProcessIsolationTest.cpp",
+]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+FINAL_LIBRARY = "xul-gtest"
diff --git a/dom/ipc/jar.mn b/dom/ipc/jar.mn
new file mode 100644
index 0000000000..6c43857aea
--- /dev/null
+++ b/dom/ipc/jar.mn
@@ -0,0 +1,7 @@
+# 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/.
+
+toolkit.jar:
+ content/global/BrowserElementChild.js (../browser-element/BrowserElementChild.js)
+ content/global/BrowserElementChildPreload.js (../browser-element/BrowserElementChildPreload.js)
diff --git a/dom/ipc/jsactor/JSActor.cpp b/dom/ipc/jsactor/JSActor.cpp
new file mode 100644
index 0000000000..03926fee04
--- /dev/null
+++ b/dom/ipc/jsactor/JSActor.cpp
@@ -0,0 +1,503 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/JSActor.h"
+#include "mozilla/dom/JSActorBinding.h"
+
+#include "chrome/common/ipc_channel.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/FunctionRef.h"
+#include "mozilla/dom/AutoEntryScript.h"
+#include "mozilla/dom/ClonedErrorHolder.h"
+#include "mozilla/dom/DOMException.h"
+#include "mozilla/dom/DOMExceptionBinding.h"
+#include "mozilla/dom/JSActorManager.h"
+#include "mozilla/dom/MessageManagerBinding.h"
+#include "mozilla/dom/PWindowGlobal.h"
+#include "mozilla/dom/Promise.h"
+#include "mozilla/dom/RootedDictionary.h"
+#include "mozilla/dom/ipc/StructuredCloneData.h"
+#include "mozilla/ProfilerMarkers.h"
+#include "js/Promise.h"
+#include "xpcprivate.h"
+#include "nsFrameMessageManager.h"
+#include "nsICrashReporter.h"
+
+namespace mozilla::dom {
+
+struct JSActorMessageMarker {
+ static constexpr Span<const char> MarkerTypeName() {
+ return MakeStringSpan("JSActorMessage");
+ }
+ static void StreamJSONMarkerData(baseprofiler::SpliceableJSONWriter& aWriter,
+ const ProfilerString8View& aActorName,
+ const ProfilerString16View& aMessageName) {
+ aWriter.StringProperty("actor", aActorName);
+ aWriter.StringProperty("name", NS_ConvertUTF16toUTF8(aMessageName));
+ }
+ static MarkerSchema MarkerTypeDisplay() {
+ using MS = MarkerSchema;
+ MS schema{MS::Location::MarkerChart, MS::Location::MarkerTable};
+ schema.AddKeyLabelFormatSearchable(
+ "actor", "Actor Name", MS::Format::String, MS::Searchable::Searchable);
+ schema.AddKeyLabelFormatSearchable(
+ "name", "Message Name", MS::Format::String, MS::Searchable::Searchable);
+ schema.SetTooltipLabel("JSActor - {marker.name}");
+ schema.SetTableLabel(
+ "{marker.name} - [{marker.data.actor}] {marker.data.name}");
+ return schema;
+ }
+};
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(JSActor)
+ NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(JSActor)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(JSActor)
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(JSActor)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(JSActor)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mWrappedJS)
+ tmp->mPendingQueries.Clear();
+ NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(JSActor)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWrappedJS)
+ for (const auto& query : tmp->mPendingQueries.Values()) {
+ CycleCollectionNoteChild(cb, query.mPromise.get(), "Pending Query Promise");
+ }
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+JSActor::JSActor(nsISupports* aGlobal) {
+ mGlobal = do_QueryInterface(aGlobal);
+ if (!mGlobal) {
+ mGlobal = xpc::NativeGlobal(xpc::PrivilegedJunkScope());
+ }
+}
+
+void JSActor::StartDestroy() { mCanSend = false; }
+
+void JSActor::AfterDestroy() {
+ mCanSend = false;
+
+ // Take our queries out, in case somehow rejecting promises can trigger
+ // additions or removals.
+ const nsTHashMap<nsUint64HashKey, PendingQuery> pendingQueries =
+ std::move(mPendingQueries);
+ for (const auto& entry : pendingQueries.Values()) {
+ nsPrintfCString message(
+ "Actor '%s' destroyed before query '%s' was resolved", mName.get(),
+ NS_LossyConvertUTF16toASCII(entry.mMessageName).get());
+ entry.mPromise->MaybeRejectWithAbortError(message);
+ }
+
+ InvokeCallback(CallbackFunction::DidDestroy);
+ ClearManager();
+}
+
+void JSActor::InvokeCallback(CallbackFunction callback) {
+ MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
+
+ AutoEntryScript aes(GetParentObject(), "JSActor destroy callback");
+ JSContext* cx = aes.cx();
+ MozJSActorCallbacks callbacksHolder;
+ JS::Rooted<JS::Value> val(cx, JS::ObjectOrNullValue(GetWrapper()));
+ if (NS_WARN_IF(!callbacksHolder.Init(cx, val))) {
+ return;
+ }
+
+ // Destroy callback is optional.
+ if (callback == CallbackFunction::DidDestroy) {
+ if (callbacksHolder.mDidDestroy.WasPassed()) {
+ callbacksHolder.mDidDestroy.Value()->Call(this);
+ }
+ } else {
+ if (callbacksHolder.mActorCreated.WasPassed()) {
+ callbacksHolder.mActorCreated.Value()->Call(this);
+ }
+ }
+}
+
+nsresult JSActor::QueryInterfaceActor(const nsIID& aIID, void** aPtr) {
+ MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
+ if (!GetWrapperPreserveColor()) {
+ // If we have no preserved wrapper, we won't implement any interfaces.
+ return NS_NOINTERFACE;
+ }
+
+ if (!mWrappedJS) {
+ AutoEntryScript aes(GetParentObject(), "JSActor query interface");
+ JSContext* cx = aes.cx();
+
+ JS::Rooted<JSObject*> self(cx, GetWrapper());
+ JSAutoRealm ar(cx, self);
+
+ RefPtr<nsXPCWrappedJS> wrappedJS;
+ nsresult rv = nsXPCWrappedJS::GetNewOrUsed(
+ cx, self, NS_GET_IID(nsISupports), getter_AddRefs(wrappedJS));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mWrappedJS = do_QueryInterface(wrappedJS);
+ MOZ_ASSERT(mWrappedJS);
+ }
+
+ return mWrappedJS->QueryInterface(aIID, aPtr);
+}
+
+void JSActor::SetName(const nsACString& aName) {
+ MOZ_ASSERT(mName.IsEmpty(), "Cannot set name twice!");
+ mName = aName;
+}
+
+void JSActor::ThrowStateErrorForGetter(const char* aName,
+ ErrorResult& aRv) const {
+ if (mName.IsEmpty()) {
+ aRv.ThrowInvalidStateError(nsPrintfCString(
+ "Cannot access property '%s' before actor is initialized", aName));
+ } else {
+ aRv.ThrowInvalidStateError(nsPrintfCString(
+ "Cannot access property '%s' after actor '%s' has been destroyed",
+ aName, mName.get()));
+ }
+}
+
+static Maybe<ipc::StructuredCloneData> TryClone(JSContext* aCx,
+ JS::Handle<JS::Value> aValue) {
+ Maybe<ipc::StructuredCloneData> data{std::in_place};
+
+ // Try to directly serialize the passed-in data, and return it to our caller.
+ IgnoredErrorResult rv;
+ data->Write(aCx, aValue, rv);
+ if (rv.Failed()) {
+ // Serialization failed, return `Nothing()` instead.
+ JS_ClearPendingException(aCx);
+ data.reset();
+ }
+ return data;
+}
+
+static Maybe<ipc::StructuredCloneData> CloneJSStack(
+ JSContext* aCx, JS::Handle<JSObject*> aStack) {
+ JS::Rooted<JS::Value> stackVal(aCx, JS::ObjectOrNullValue(aStack));
+ return TryClone(aCx, stackVal);
+}
+
+static Maybe<ipc::StructuredCloneData> CaptureJSStack(JSContext* aCx) {
+ JS::Rooted<JSObject*> stack(aCx, nullptr);
+ if (JS::IsAsyncStackCaptureEnabledForRealm(aCx) &&
+ !JS::CaptureCurrentStack(aCx, &stack)) {
+ JS_ClearPendingException(aCx);
+ }
+
+ return CloneJSStack(aCx, stack);
+}
+
+void JSActor::SendAsyncMessage(JSContext* aCx, const nsAString& aMessageName,
+ JS::Handle<JS::Value> aObj,
+ JS::Handle<JS::Value> aTransfers,
+ ErrorResult& aRv) {
+ profiler_add_marker("SendAsyncMessage", geckoprofiler::category::IPC, {},
+ JSActorMessageMarker{}, mName, aMessageName);
+ Maybe<ipc::StructuredCloneData> data{std::in_place};
+ if (!nsFrameMessageManager::GetParamsForMessage(aCx, aObj, aTransfers,
+ *data)) {
+ aRv.ThrowDataCloneError(nsPrintfCString(
+ "Failed to serialize message '%s::%s'",
+ NS_LossyConvertUTF16toASCII(aMessageName).get(), mName.get()));
+ return;
+ }
+
+ JSActorMessageMeta meta;
+ meta.actorName() = mName;
+ meta.messageName() = aMessageName;
+ meta.kind() = JSActorMessageKind::Message;
+
+ SendRawMessage(meta, std::move(data), CaptureJSStack(aCx), aRv);
+}
+
+already_AddRefed<Promise> JSActor::SendQuery(JSContext* aCx,
+ const nsAString& aMessageName,
+ JS::Handle<JS::Value> aObj,
+ ErrorResult& aRv) {
+ profiler_add_marker("SendQuery", geckoprofiler::category::IPC, {},
+ JSActorMessageMarker{}, mName, aMessageName);
+ Maybe<ipc::StructuredCloneData> data{std::in_place};
+ if (!nsFrameMessageManager::GetParamsForMessage(
+ aCx, aObj, JS::UndefinedHandleValue, *data)) {
+ aRv.ThrowDataCloneError(nsPrintfCString(
+ "Failed to serialize message '%s::%s'",
+ NS_LossyConvertUTF16toASCII(aMessageName).get(), mName.get()));
+ return nullptr;
+ }
+
+ nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx);
+ if (NS_WARN_IF(!global)) {
+ aRv.ThrowUnknownError("Unable to get current native global");
+ return nullptr;
+ }
+
+ RefPtr<Promise> promise = Promise::Create(global, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+
+ JSActorMessageMeta meta;
+ meta.actorName() = mName;
+ meta.messageName() = aMessageName;
+ meta.queryId() = mNextQueryId++;
+ meta.kind() = JSActorMessageKind::Query;
+
+ SendRawMessage(meta, std::move(data), CaptureJSStack(aCx), aRv);
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+
+ mPendingQueries.InsertOrUpdate(meta.queryId(),
+ PendingQuery{promise, meta.messageName()});
+
+ return promise.forget();
+}
+
+void JSActor::CallReceiveMessage(JSContext* aCx,
+ const JSActorMessageMeta& aMetadata,
+ JS::Handle<JS::Value> aData,
+ JS::MutableHandle<JS::Value> aRetVal,
+ ErrorResult& aRv) {
+ // The argument which we want to pass to IPC.
+ RootedDictionary<ReceiveMessageArgument> argument(aCx);
+ argument.mTarget = this;
+ argument.mName = aMetadata.messageName();
+ argument.mData = aData;
+ argument.mJson = aData;
+ argument.mSync = false;
+
+ if (GetWrapperPreserveColor()) {
+ // Invoke the actual callback.
+ JS::Rooted<JSObject*> global(aCx, JS::GetNonCCWObjectGlobal(GetWrapper()));
+ RefPtr<MessageListener> messageListener =
+ new MessageListener(GetWrapper(), global, nullptr, nullptr);
+ messageListener->ReceiveMessage(argument, aRetVal, aRv,
+ "JSActor receive message",
+ MessageListener::eRethrowExceptions);
+ } else {
+ aRv.ThrowTypeError<MSG_NOT_CALLABLE>("Property 'receiveMessage'");
+ }
+}
+
+void JSActor::ReceiveMessage(JSContext* aCx,
+ const JSActorMessageMeta& aMetadata,
+ JS::Handle<JS::Value> aData, ErrorResult& aRv) {
+ MOZ_ASSERT(aMetadata.kind() == JSActorMessageKind::Message);
+ profiler_add_marker("ReceiveMessage", geckoprofiler::category::IPC, {},
+ JSActorMessageMarker{}, mName, aMetadata.messageName());
+
+ JS::Rooted<JS::Value> retval(aCx);
+ CallReceiveMessage(aCx, aMetadata, aData, &retval, aRv);
+}
+
+void JSActor::ReceiveQuery(JSContext* aCx, const JSActorMessageMeta& aMetadata,
+ JS::Handle<JS::Value> aData, ErrorResult& aRv) {
+ MOZ_ASSERT(aMetadata.kind() == JSActorMessageKind::Query);
+ profiler_add_marker("ReceiveQuery", geckoprofiler::category::IPC, {},
+ JSActorMessageMarker{}, mName, aMetadata.messageName());
+
+ // This promise will be resolved or rejected once the listener has been
+ // called. Our listener on this promise will then send the reply.
+ RefPtr<Promise> promise = Promise::Create(GetParentObject(), aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
+
+ RefPtr<QueryHandler> handler = new QueryHandler(this, aMetadata, promise);
+ promise->AppendNativeHandler(handler);
+
+ ErrorResult error;
+ JS::Rooted<JS::Value> retval(aCx);
+ CallReceiveMessage(aCx, aMetadata, aData, &retval, error);
+
+ // If we have a promise, resolve or reject it respectively.
+ if (error.Failed()) {
+ if (error.IsUncatchableException()) {
+ promise->MaybeRejectWithTimeoutError(
+ "Message handler threw uncatchable exception");
+ } else {
+ promise->MaybeReject(std::move(error));
+ }
+ } else {
+ promise->MaybeResolve(retval);
+ }
+ error.SuppressException();
+}
+
+void JSActor::ReceiveQueryReply(JSContext* aCx,
+ const JSActorMessageMeta& aMetadata,
+ JS::Handle<JS::Value> aData, ErrorResult& aRv) {
+ if (NS_WARN_IF(aMetadata.actorName() != mName)) {
+ aRv.ThrowUnknownError("Mismatched actor name for query reply");
+ return;
+ }
+
+ Maybe<PendingQuery> query = mPendingQueries.Extract(aMetadata.queryId());
+ if (NS_WARN_IF(!query)) {
+ aRv.ThrowUnknownError("Received reply for non-pending query");
+ return;
+ }
+
+ profiler_add_marker("ReceiveQueryReply", geckoprofiler::category::IPC, {},
+ JSActorMessageMarker{}, mName, aMetadata.messageName());
+
+ Promise* promise = query->mPromise;
+ JSAutoRealm ar(aCx, promise->PromiseObj());
+ JS::RootedValue data(aCx, aData);
+ if (NS_WARN_IF(!JS_WrapValue(aCx, &data))) {
+ aRv.NoteJSContextException(aCx);
+ return;
+ }
+
+ if (aMetadata.kind() == JSActorMessageKind::QueryResolve) {
+ promise->MaybeResolve(data);
+ } else {
+ promise->MaybeReject(data);
+ }
+}
+
+void JSActor::SendRawMessageInProcess(const JSActorMessageMeta& aMeta,
+ Maybe<ipc::StructuredCloneData>&& aData,
+ Maybe<ipc::StructuredCloneData>&& aStack,
+ OtherSideCallback&& aGetOtherSide) {
+ MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "JSActor Async Message",
+ [aMeta, data{std::move(aData)}, stack{std::move(aStack)},
+ getOtherSide{std::move(aGetOtherSide)}]() mutable {
+ if (RefPtr<JSActorManager> otherSide = getOtherSide()) {
+ otherSide->ReceiveRawMessage(aMeta, std::move(data),
+ std::move(stack));
+ }
+ }));
+}
+
+// Native handler for our generated promise which is used to handle Queries and
+// send the reply when their promises have been resolved.
+JSActor::QueryHandler::QueryHandler(JSActor* aActor,
+ const JSActorMessageMeta& aMetadata,
+ Promise* aPromise)
+ : mActor(aActor),
+ mPromise(aPromise),
+ mMessageName(aMetadata.messageName()),
+ mQueryId(aMetadata.queryId()) {}
+
+void JSActor::QueryHandler::RejectedCallback(JSContext* aCx,
+ JS::Handle<JS::Value> aValue,
+ ErrorResult& aRv) {
+ if (!mActor) {
+ // Make sure that this rejection is reported. See comment below.
+ if (!JS::CallOriginalPromiseReject(aCx, aValue)) {
+ JS_ClearPendingException(aCx);
+ }
+ return;
+ }
+
+ JS::Rooted<JS::Value> value(aCx, aValue);
+ if (value.isObject()) {
+ JS::Rooted<JSObject*> error(aCx, &value.toObject());
+ if (UniquePtr<ClonedErrorHolder> ceh =
+ ClonedErrorHolder::Create(aCx, error, IgnoreErrors())) {
+ if (!ToJSValue(aCx, std::move(ceh), &value)) {
+ JS_ClearPendingException(aCx);
+ }
+ } else {
+ JS_ClearPendingException(aCx);
+ }
+ }
+
+ Maybe<ipc::StructuredCloneData> data = TryClone(aCx, value);
+ if (!data) {
+ // Failed to clone the rejection value. Make sure that this
+ // rejection is reported, despite being "handled". This is done by
+ // creating a new promise in the rejected state, and throwing it
+ // away. This will be reported as an unhandled rejected promise.
+ if (!JS::CallOriginalPromiseReject(aCx, aValue)) {
+ JS_ClearPendingException(aCx);
+ }
+ }
+
+ SendReply(aCx, JSActorMessageKind::QueryReject, std::move(data));
+}
+
+void JSActor::QueryHandler::ResolvedCallback(JSContext* aCx,
+ JS::Handle<JS::Value> aValue,
+ ErrorResult& aRv) {
+ if (!mActor) {
+ return;
+ }
+
+ Maybe<ipc::StructuredCloneData> data{std::in_place};
+ data->InitScope(JS::StructuredCloneScope::DifferentProcess);
+
+ IgnoredErrorResult error;
+ data->Write(aCx, aValue, error);
+ if (NS_WARN_IF(error.Failed())) {
+ JS_ClearPendingException(aCx);
+
+ nsAutoCString msg;
+ msg.Append(mActor->Name());
+ msg.Append(':');
+ msg.Append(NS_LossyConvertUTF16toASCII(mMessageName));
+ msg.AppendLiteral(": message reply cannot be cloned.");
+
+ auto exc = MakeRefPtr<Exception>(msg, NS_ERROR_FAILURE, "DataCloneError"_ns,
+ nullptr, nullptr);
+
+ JS::Rooted<JS::Value> val(aCx);
+ if (ToJSValue(aCx, exc, &val)) {
+ RejectedCallback(aCx, val, aRv);
+ } else {
+ JS_ClearPendingException(aCx);
+ }
+ return;
+ }
+
+ SendReply(aCx, JSActorMessageKind::QueryResolve, std::move(data));
+}
+
+void JSActor::QueryHandler::SendReply(JSContext* aCx, JSActorMessageKind aKind,
+ Maybe<ipc::StructuredCloneData>&& aData) {
+ MOZ_ASSERT(mActor);
+ profiler_add_marker("SendQueryReply", geckoprofiler::category::IPC, {},
+ JSActorMessageMarker{}, mActor->Name(), mMessageName);
+
+ JSActorMessageMeta meta;
+ meta.actorName() = mActor->Name();
+ meta.messageName() = mMessageName;
+ meta.queryId() = mQueryId;
+ meta.kind() = aKind;
+
+ JS::Rooted<JSObject*> promise(aCx, mPromise->PromiseObj());
+ JS::Rooted<JSObject*> stack(aCx, JS::GetPromiseResolutionSite(promise));
+
+ mActor->SendRawMessage(meta, std::move(aData), CloneJSStack(aCx, stack),
+ IgnoreErrors());
+ mActor = nullptr;
+ mPromise = nullptr;
+}
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(JSActor::QueryHandler)
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(JSActor::QueryHandler)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(JSActor::QueryHandler)
+
+NS_IMPL_CYCLE_COLLECTION(JSActor::QueryHandler, mActor, mPromise)
+
+} // namespace mozilla::dom
diff --git a/dom/ipc/jsactor/JSActor.h b/dom/ipc/jsactor/JSActor.h
new file mode 100644
index 0000000000..6c6678a7ff
--- /dev/null
+++ b/dom/ipc/jsactor/JSActor.h
@@ -0,0 +1,174 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/. */
+
+#ifndef mozilla_dom_JSActor_h
+#define mozilla_dom_JSActor_h
+
+#include "js/TypeDecls.h"
+#include "ipc/EnumSerializer.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/dom/PromiseNativeHandler.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsTHashMap.h"
+#include "nsWrapperCache.h"
+
+class nsIGlobalObject;
+class nsQueryJSActor;
+
+namespace mozilla {
+class ErrorResult;
+
+namespace dom {
+
+namespace ipc {
+class StructuredCloneData;
+}
+
+class JSActorManager;
+class JSActorMessageMeta;
+class QueryPromiseHandler;
+
+enum class JSActorMessageKind {
+ Message,
+ Query,
+ QueryResolve,
+ QueryReject,
+ EndGuard_,
+};
+
+// Common base class for JSWindowActor{Parent,Child}.
+class JSActor : public nsISupports, public nsWrapperCache {
+ public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(JSActor)
+
+ explicit JSActor(nsISupports* aGlobal = nullptr);
+
+ const nsCString& Name() const { return mName; }
+ void GetName(nsCString& aName) { aName = Name(); }
+
+ void SendAsyncMessage(JSContext* aCx, const nsAString& aMessageName,
+ JS::Handle<JS::Value> aObj,
+ JS::Handle<JS::Value> aTransfers, ErrorResult& aRv);
+
+ already_AddRefed<Promise> SendQuery(JSContext* aCx,
+ const nsAString& aMessageName,
+ JS::Handle<JS::Value> aObj,
+ ErrorResult& aRv);
+
+ nsIGlobalObject* GetParentObject() const { return mGlobal; };
+
+ protected:
+ // Send the message described by the structured clone data |aData|, and the
+ // message metadata |aMetadata|. The underlying transport should call the
+ // |ReceiveMessage| method on the other side asynchronously.
+ virtual void SendRawMessage(const JSActorMessageMeta& aMetadata,
+ Maybe<ipc::StructuredCloneData>&& aData,
+ Maybe<ipc::StructuredCloneData>&& aStack,
+ ErrorResult& aRv) = 0;
+
+ // Helper method to send an in-process raw message.
+ using OtherSideCallback = std::function<already_AddRefed<JSActorManager>()>;
+ static void SendRawMessageInProcess(const JSActorMessageMeta& aMeta,
+ Maybe<ipc::StructuredCloneData>&& aData,
+ Maybe<ipc::StructuredCloneData>&& aStack,
+ OtherSideCallback&& aGetOtherSide);
+
+ virtual ~JSActor() = default;
+
+ void SetName(const nsACString& aName);
+
+ bool CanSend() const { return mCanSend; }
+
+ void ThrowStateErrorForGetter(const char* aName, ErrorResult& aRv) const;
+
+ void StartDestroy();
+ void AfterDestroy();
+
+ enum class CallbackFunction { DidDestroy, ActorCreated };
+ void InvokeCallback(CallbackFunction callback);
+
+ virtual void ClearManager() = 0;
+
+ private:
+ friend class JSActorManager;
+ friend class ::nsQueryJSActor; // for QueryInterfaceActor
+
+ nsresult QueryInterfaceActor(const nsIID& aIID, void** aPtr);
+
+ // Called by JSActorManager when they receive raw message data destined for
+ // this actor.
+ void ReceiveMessage(JSContext* aCx, const JSActorMessageMeta& aMetadata,
+ JS::Handle<JS::Value> aData, ErrorResult& aRv);
+ void ReceiveQuery(JSContext* aCx, const JSActorMessageMeta& aMetadata,
+ JS::Handle<JS::Value> aData, ErrorResult& aRv);
+ void ReceiveQueryReply(JSContext* aCx, const JSActorMessageMeta& aMetadata,
+ JS::Handle<JS::Value> aData, ErrorResult& aRv);
+
+ // Call the actual `ReceiveMessage` method, and get the return value.
+ void CallReceiveMessage(JSContext* aCx, const JSActorMessageMeta& aMetadata,
+ JS::Handle<JS::Value> aData,
+ JS::MutableHandle<JS::Value> aRetVal,
+ ErrorResult& aRv);
+
+ // Helper object used while processing query messages to send the final reply
+ // message.
+ class QueryHandler final : public PromiseNativeHandler {
+ public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_CLASS(QueryHandler)
+
+ QueryHandler(JSActor* aActor, const JSActorMessageMeta& aMetadata,
+ Promise* aPromise);
+
+ void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
+ ErrorResult& aRv) override;
+
+ void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
+ ErrorResult& aRv) override;
+
+ private:
+ ~QueryHandler() = default;
+
+ void SendReply(JSContext* aCx, JSActorMessageKind aKind,
+ Maybe<ipc::StructuredCloneData>&& aData);
+
+ RefPtr<JSActor> mActor;
+ RefPtr<Promise> mPromise;
+ nsString mMessageName;
+ uint64_t mQueryId;
+ };
+
+ // A query which hasn't been resolved yet, along with metadata about what
+ // query the promise is for.
+ struct PendingQuery {
+ RefPtr<Promise> mPromise;
+ nsString mMessageName;
+ };
+
+ nsCOMPtr<nsIGlobalObject> mGlobal;
+ nsCOMPtr<nsISupports> mWrappedJS;
+ nsCString mName;
+ nsTHashMap<nsUint64HashKey, PendingQuery> mPendingQueries;
+ uint64_t mNextQueryId = 0;
+ bool mCanSend = true;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::dom::JSActorMessageKind>
+ : public ContiguousEnumSerializer<
+ mozilla::dom::JSActorMessageKind,
+ mozilla::dom::JSActorMessageKind::Message,
+ mozilla::dom::JSActorMessageKind::EndGuard_> {};
+
+} // namespace IPC
+
+#endif // !defined(mozilla_dom_JSActor_h)
diff --git a/dom/ipc/jsactor/JSActorManager.cpp b/dom/ipc/jsactor/JSActorManager.cpp
new file mode 100644
index 0000000000..b8791570d6
--- /dev/null
+++ b/dom/ipc/jsactor/JSActorManager.cpp
@@ -0,0 +1,266 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/JSActorManager.h"
+
+#include "mozilla/dom/AutoEntryScript.h"
+#include "mozilla/dom/JSActorService.h"
+#include "mozilla/dom/MessagePort.h"
+#include "mozilla/dom/PWindowGlobal.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/AppShutdown.h"
+#include "mozilla/CycleCollectedJSRuntime.h"
+#include "mozilla/ScopeExit.h"
+#include "mozJSModuleLoader.h"
+#include "jsapi.h"
+#include "js/CallAndConstruct.h" // JS::Construct
+#include "js/PropertyAndElement.h" // JS_GetProperty
+#include "nsContentUtils.h"
+
+namespace mozilla::dom {
+
+already_AddRefed<JSActor> JSActorManager::GetActor(JSContext* aCx,
+ const nsACString& aName,
+ ErrorResult& aRv) {
+ MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
+
+ // If our connection has been closed, return an error.
+ mozilla::ipc::IProtocol* nativeActor = AsNativeActor();
+ if (!nativeActor->CanSend()) {
+ aRv.ThrowInvalidStateError(nsPrintfCString(
+ "Cannot get actor '%s'. Native '%s' actor is destroyed.",
+ PromiseFlatCString(aName).get(), nativeActor->GetProtocolName()));
+ return nullptr;
+ }
+
+ // Check if this actor has already been created, and return it if it has.
+ if (RefPtr<JSActor> actor = mJSActors.Get(aName)) {
+ return actor.forget();
+ }
+
+ RefPtr<JSActorService> actorSvc = JSActorService::GetSingleton();
+ if (!actorSvc) {
+ aRv.ThrowInvalidStateError("JSActorService hasn't been initialized");
+ return nullptr;
+ }
+
+ // Check if this actor satisfies the requirements of the protocol
+ // corresponding to `aName`, and get the module which implements it.
+ RefPtr<JSActorProtocol> protocol =
+ MatchingJSActorProtocol(actorSvc, aName, aRv);
+ if (!protocol) {
+ return nullptr;
+ }
+
+ auto& side = nativeActor->GetSide() == mozilla::ipc::ParentSide
+ ? protocol->Parent()
+ : protocol->Child();
+
+ // Load the module using mozJSModuleLoader.
+ // If the JSActor uses `loadInDevToolsLoader`, force loading in the DevTools
+ // specific's loader.
+ RefPtr loader = protocol->mLoadInDevToolsLoader
+ ? mozJSModuleLoader::GetOrCreateDevToolsLoader()
+ : mozJSModuleLoader::Get();
+ MOZ_ASSERT(loader);
+
+ // We're about to construct the actor, so make sure we're in the loader realm
+ // while importing etc.
+ JSAutoRealm ar(aCx, loader->GetSharedGlobal(aCx));
+
+ // If a module URI was provided, use it to construct an instance of the actor.
+ JS::Rooted<JSObject*> actorObj(aCx);
+ if (side.mModuleURI || side.mESModuleURI) {
+ JS::Rooted<JSObject*> exports(aCx);
+ if (side.mModuleURI) {
+ JS::Rooted<JSObject*> global(aCx);
+ aRv = loader->Import(aCx, side.mModuleURI.ref(), &global, &exports);
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+ } else {
+ aRv = loader->ImportESModule(aCx, side.mESModuleURI.ref(), &exports);
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+ }
+ MOZ_ASSERT(exports, "null exports!");
+
+ // Load the specific property from our module.
+ JS::Rooted<JS::Value> ctor(aCx);
+ nsAutoCString ctorName(aName);
+ ctorName.Append(StringFromIPCSide(nativeActor->GetSide()));
+ if (!JS_GetProperty(aCx, exports, ctorName.get(), &ctor)) {
+ aRv.NoteJSContextException(aCx);
+ return nullptr;
+ }
+
+ if (NS_WARN_IF(!ctor.isObject())) {
+ aRv.ThrowNotFoundError(nsPrintfCString(
+ "Could not find actor constructor '%s'", ctorName.get()));
+ return nullptr;
+ }
+
+ // Invoke the constructor loaded from the module.
+ if (!JS::Construct(aCx, ctor, JS::HandleValueArray::empty(), &actorObj)) {
+ aRv.NoteJSContextException(aCx);
+ return nullptr;
+ }
+ }
+
+ // Initialize our newly-constructed actor, and return it.
+ RefPtr<JSActor> actor = InitJSActor(actorObj, aName, aRv);
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+ mJSActors.InsertOrUpdate(aName, RefPtr{actor});
+ return actor.forget();
+}
+
+already_AddRefed<JSActor> JSActorManager::GetExistingActor(
+ const nsACString& aName) {
+ if (!AsNativeActor()->CanSend()) {
+ return nullptr;
+ }
+ return mJSActors.Get(aName);
+}
+
+#define CHILD_DIAGNOSTIC_ASSERT(test, msg) \
+ do { \
+ if (XRE_IsParentProcess()) { \
+ MOZ_ASSERT(test, msg); \
+ } else { \
+ MOZ_DIAGNOSTIC_ASSERT(test, msg); \
+ } \
+ } while (0)
+
+void JSActorManager::ReceiveRawMessage(
+ const JSActorMessageMeta& aMetadata,
+ Maybe<ipc::StructuredCloneData>&& aData,
+ Maybe<ipc::StructuredCloneData>&& aStack) {
+ MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
+
+ CrashReporter::AutoAnnotateCrashReport autoActorName(
+ CrashReporter::Annotation::JSActorName, aMetadata.actorName());
+ CrashReporter::AutoAnnotateCrashReport autoMessageName(
+ CrashReporter::Annotation::JSActorMessage,
+ NS_LossyConvertUTF16toASCII(aMetadata.messageName()));
+
+ // We're going to be running JS. Enter the privileged junk realm so we can set
+ // up our JS state correctly.
+ AutoEntryScript aes(xpc::PrivilegedJunkScope(), "JSActor message handler");
+ JSContext* cx = aes.cx();
+
+ // Ensure any errors reported to `error` are set on the scope, so they're
+ // reported.
+ ErrorResult error;
+ auto autoSetException =
+ MakeScopeExit([&] { Unused << error.MaybeSetPendingException(cx); });
+
+ // If an async stack was provided, set up our async stack state.
+ JS::Rooted<JSObject*> stack(cx);
+ Maybe<JS::AutoSetAsyncStackForNewCalls> stackSetter;
+ {
+ JS::Rooted<JS::Value> stackVal(cx);
+ if (aStack) {
+ aStack->Read(cx, &stackVal, error);
+ if (error.Failed()) {
+ error.SuppressException();
+ JS_ClearPendingException(cx);
+ stackVal.setUndefined();
+ }
+ }
+
+ if (stackVal.isObject()) {
+ stack = &stackVal.toObject();
+ if (!js::IsSavedFrame(stack)) {
+ CHILD_DIAGNOSTIC_ASSERT(false, "Stack must be a SavedFrame object");
+ error.ThrowDataError("Actor async stack must be a SavedFrame object");
+ return;
+ }
+ stackSetter.emplace(cx, stack, "JSActor query");
+ }
+ }
+
+ RefPtr<JSActor> actor = GetActor(cx, aMetadata.actorName(), error);
+ if (error.Failed()) {
+ return;
+ }
+
+ JS::Rooted<JS::Value> data(cx);
+ if (aData) {
+ aData->Read(cx, &data, error);
+ // StructuredCloneHolder populates an array of ports for MessageEvent.ports
+ // which we don't need, but which StructuredCloneHolder's destructor will
+ // assert on for thread safety reasons (that do not apply in this case) if
+ // we do not consume the array. It's possible for the Read call above to
+ // populate this array even in event of an error, so we must consume the
+ // array before processing the error.
+ nsTArray<RefPtr<MessagePort>> ports = aData->TakeTransferredPorts();
+ // Cast to void so that the ports will actually be moved, and then
+ // discarded.
+ (void)ports;
+ if (error.Failed()) {
+ CHILD_DIAGNOSTIC_ASSERT(CycleCollectedJSRuntime::Get()->OOMReported(),
+ "Should not receive non-decodable data");
+ return;
+ }
+ }
+
+ switch (aMetadata.kind()) {
+ case JSActorMessageKind::QueryResolve:
+ case JSActorMessageKind::QueryReject:
+ actor->ReceiveQueryReply(cx, aMetadata, data, error);
+ break;
+
+ case JSActorMessageKind::Message:
+ actor->ReceiveMessage(cx, aMetadata, data, error);
+ break;
+
+ case JSActorMessageKind::Query:
+ actor->ReceiveQuery(cx, aMetadata, data, error);
+ break;
+
+ default:
+ MOZ_ASSERT_UNREACHABLE();
+ }
+}
+
+void JSActorManager::JSActorWillDestroy() {
+ for (const auto& entry : mJSActors.Values()) {
+ entry->StartDestroy();
+ }
+}
+
+void JSActorManager::JSActorDidDestroy() {
+ MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
+ CrashReporter::AutoAnnotateCrashReport autoMessageName(
+ CrashReporter::Annotation::JSActorMessage, "<DidDestroy>"_ns);
+
+ // Swap the table with `mJSActors` so that we don't invalidate it while
+ // iterating.
+ const nsRefPtrHashtable<nsCStringHashKey, JSActor> actors =
+ std::move(mJSActors);
+ for (const auto& entry : actors.Values()) {
+ CrashReporter::AutoAnnotateCrashReport autoActorName(
+ CrashReporter::Annotation::JSActorName, entry->Name());
+ // Do not risk to run script very late in shutdown
+ if (!AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdownFinal)) {
+ entry->AfterDestroy();
+ }
+ }
+}
+
+void JSActorManager::JSActorUnregister(const nsACString& aName) {
+ MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
+
+ RefPtr<JSActor> actor;
+ if (mJSActors.Remove(aName, getter_AddRefs(actor))) {
+ actor->AfterDestroy();
+ }
+}
+
+} // namespace mozilla::dom
diff --git a/dom/ipc/jsactor/JSActorManager.h b/dom/ipc/jsactor/JSActorManager.h
new file mode 100644
index 0000000000..86efa9d7af
--- /dev/null
+++ b/dom/ipc/jsactor/JSActorManager.h
@@ -0,0 +1,100 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_JSActorManager_h
+#define mozilla_dom_JSActorManager_h
+
+#include "js/TypeDecls.h"
+#include "mozilla/dom/JSActor.h"
+#include "nsRefPtrHashtable.h"
+#include "nsString.h"
+
+namespace mozilla {
+class ErrorResult;
+
+namespace ipc {
+class IProtocol;
+}
+
+namespace dom {
+
+class JSActorProtocol;
+class JSActorService;
+
+class JSActorManager : public nsISupports {
+ public:
+ /**
+ * Get or create an actor by its name.
+ *
+ * Will set an error on |aRv| if the actor fails to be constructed.
+ */
+ already_AddRefed<JSActor> GetActor(JSContext* aCx, const nsACString& aName,
+ ErrorResult& aRv);
+
+ /**
+ * Look up an existing actor by its name, returning nullptr if it doesn't
+ * already exist. Will not attempt to create the actor.
+ */
+ already_AddRefed<JSActor> GetExistingActor(const nsACString& aName);
+
+ /**
+ * Handle receiving a raw message from the other side.
+ */
+ void ReceiveRawMessage(const JSActorMessageMeta& aMetadata,
+ Maybe<ipc::StructuredCloneData>&& aData,
+ Maybe<ipc::StructuredCloneData>&& aStack);
+
+ protected:
+ /**
+ * The actor is about to be destroyed so prevent it from sending any
+ * more messages.
+ */
+ void JSActorWillDestroy();
+
+ /**
+ * Lifecycle method which will fire the `didDestroy` methods on relevant
+ * actors.
+ */
+ void JSActorDidDestroy();
+
+ /**
+ * Return the protocol with the given name, if it is supported by the current
+ * actor.
+ */
+ virtual already_AddRefed<JSActorProtocol> MatchingJSActorProtocol(
+ JSActorService* aActorSvc, const nsACString& aName, ErrorResult& aRv) = 0;
+
+ /**
+ * Initialize a JSActor instance given the constructed JS object.
+ * `aMaybeActor` may be `nullptr`, which should construct the default empty
+ * actor.
+ */
+ virtual already_AddRefed<JSActor> InitJSActor(
+ JS::Handle<JSObject*> aMaybeActor, const nsACString& aName,
+ ErrorResult& aRv) = 0;
+
+ /**
+ * Return this native actor. This should be the same object which is
+ * implementing `JSActorManager`.
+ */
+ virtual mozilla::ipc::IProtocol* AsNativeActor() = 0;
+
+ private:
+ friend class JSActorService;
+
+ /**
+ * Note that a particular actor name has been unregistered, and fire the
+ * `didDestroy` method on the actor, if it's been initialized.
+ */
+ void JSActorUnregister(const nsACString& aName);
+
+ nsRefPtrHashtable<nsCStringHashKey, JSActor> mJSActors;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_JSActorManager_h
diff --git a/dom/ipc/jsactor/JSActorProtocolUtils.h b/dom/ipc/jsactor/JSActorProtocolUtils.h
new file mode 100644
index 0000000000..20c0440d63
--- /dev/null
+++ b/dom/ipc/jsactor/JSActorProtocolUtils.h
@@ -0,0 +1,121 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_JSActorProtocolUtils_h
+#define mozilla_dom_JSActorProtocolUtils_h
+
+#include "mozilla/Assertions.h" // MOZ_ASSERT
+#include "mozilla/ErrorResult.h" // ErrorResult
+
+namespace mozilla {
+
+namespace dom {
+
+class JSActorProtocolUtils {
+ public:
+ template <typename ProtoT, typename ActorInfoT>
+ static void FromIPCShared(ProtoT& aProto, const ActorInfoT& aInfo) {
+ aProto->mRemoteTypes = aInfo.remoteTypes().Clone();
+
+ if (aInfo.isESModule()) {
+ aProto->mChild.mESModuleURI = aInfo.url();
+ } else {
+ aProto->mChild.mModuleURI = aInfo.url();
+ }
+
+ aProto->mLoadInDevToolsLoader = aInfo.loadInDevToolsLoader();
+
+ aProto->mChild.mObservers = aInfo.observers().Clone();
+ }
+
+ template <typename ProtoT, typename ActorInfoT>
+ static void ToIPCShared(ActorInfoT& aInfo, const ProtoT& aProto) {
+ aInfo.name() = aProto->mName;
+
+ aInfo.remoteTypes() = aProto->mRemoteTypes.Clone();
+
+ if (aProto->mChild.mModuleURI) {
+ aInfo.url() = aProto->mChild.mModuleURI;
+ aInfo.isESModule() = false;
+ } else {
+ aInfo.url() = aProto->mChild.mESModuleURI;
+ aInfo.isESModule() = true;
+ }
+
+ aInfo.loadInDevToolsLoader() = aProto->mLoadInDevToolsLoader;
+
+ aInfo.observers() = aProto->mChild.mObservers.Clone();
+ }
+
+ template <typename ProtoT, typename ActorOptionsT>
+ static bool FromWebIDLOptionsShared(ProtoT& aProto,
+ const ActorOptionsT& aOptions,
+ ErrorResult& aRv) {
+ if (aOptions.mRemoteTypes.WasPassed()) {
+ MOZ_ASSERT(aOptions.mRemoteTypes.Value().Length());
+ aProto->mRemoteTypes = aOptions.mRemoteTypes.Value();
+ }
+
+ if (aOptions.mParent.WasPassed()) {
+ const auto& parentOptions = aOptions.mParent.Value();
+
+ if (parentOptions.mModuleURI.WasPassed()) {
+ if (parentOptions.mEsModuleURI.WasPassed()) {
+ aRv.ThrowNotSupportedError(
+ "moduleURI and esModuleURI are mutually exclusive.");
+ return false;
+ }
+
+ aProto->mParent.mModuleURI.emplace(parentOptions.mModuleURI.Value());
+ } else if (parentOptions.mEsModuleURI.WasPassed()) {
+ aProto->mParent.mESModuleURI.emplace(
+ parentOptions.mEsModuleURI.Value());
+ } else {
+ aRv.ThrowNotSupportedError(
+ "Either moduleURI or esModuleURI is required.");
+ return false;
+ }
+ }
+ if (aOptions.mChild.WasPassed()) {
+ const auto& childOptions = aOptions.mChild.Value();
+
+ if (childOptions.mModuleURI.WasPassed()) {
+ if (childOptions.mEsModuleURI.WasPassed()) {
+ aRv.ThrowNotSupportedError(
+ "moduleURI and esModuleURI are exclusive.");
+ return false;
+ }
+
+ aProto->mChild.mModuleURI.emplace(childOptions.mModuleURI.Value());
+ } else if (childOptions.mEsModuleURI.WasPassed()) {
+ aProto->mChild.mESModuleURI.emplace(childOptions.mEsModuleURI.Value());
+ } else {
+ aRv.ThrowNotSupportedError(
+ "Either moduleURI or esModuleURI is required.");
+ return false;
+ }
+ }
+
+ if (!aOptions.mChild.WasPassed() && !aOptions.mParent.WasPassed()) {
+ aRv.ThrowNotSupportedError(
+ "No point registering an actor with neither child nor parent "
+ "specifications.");
+ return false;
+ }
+
+ if (aOptions.mChild.WasPassed() &&
+ aOptions.mChild.Value().mObservers.WasPassed()) {
+ aProto->mChild.mObservers = aOptions.mChild.Value().mObservers.Value();
+ }
+
+ return true;
+ }
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_JSActorProtocolUtils_h
diff --git a/dom/ipc/jsactor/JSActorService.cpp b/dom/ipc/jsactor/JSActorService.cpp
new file mode 100644
index 0000000000..3fde76a206
--- /dev/null
+++ b/dom/ipc/jsactor/JSActorService.cpp
@@ -0,0 +1,322 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/JSActorService.h"
+#include "mozilla/dom/ChromeUtilsBinding.h"
+#include "mozilla/dom/BrowserChild.h"
+#include "mozilla/dom/BrowserParent.h"
+#include "mozilla/dom/EventListenerBinding.h"
+#include "mozilla/dom/Event.h"
+#include "mozilla/dom/EventTargetBinding.h"
+#include "mozilla/dom/EventTarget.h"
+#include "mozilla/dom/InProcessChild.h"
+#include "mozilla/dom/InProcessParent.h"
+#include "mozilla/dom/JSActorManager.h"
+#include "mozilla/dom/JSProcessActorBinding.h"
+#include "mozilla/dom/JSProcessActorChild.h"
+#include "mozilla/dom/JSProcessActorProtocol.h"
+#include "mozilla/dom/JSWindowActorBinding.h"
+#include "mozilla/dom/JSWindowActorChild.h"
+#include "mozilla/dom/JSWindowActorProtocol.h"
+#include "mozilla/dom/MessageManagerBinding.h"
+#include "mozilla/dom/PContent.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/WindowGlobalChild.h"
+#include "mozilla/dom/WindowGlobalParent.h"
+#include "mozilla/ArrayAlgorithm.h"
+#include "mozilla/Services.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/Logging.h"
+#include "nsIObserverService.h"
+
+namespace mozilla::dom {
+namespace {
+StaticRefPtr<JSActorService> gJSActorService;
+}
+
+JSActorService::JSActorService() { MOZ_ASSERT(NS_IsMainThread()); }
+
+JSActorService::~JSActorService() { MOZ_ASSERT(NS_IsMainThread()); }
+
+/* static */
+already_AddRefed<JSActorService> JSActorService::GetSingleton() {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!gJSActorService) {
+ gJSActorService = new JSActorService();
+ ClearOnShutdown(&gJSActorService);
+ }
+
+ RefPtr<JSActorService> service = gJSActorService.get();
+ return service.forget();
+}
+
+void JSActorService::RegisterWindowActor(const nsACString& aName,
+ const WindowActorOptions& aOptions,
+ ErrorResult& aRv) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ const auto proto = mWindowActorDescriptors.WithEntryHandle(
+ aName, [&](auto&& entry) -> RefPtr<JSWindowActorProtocol> {
+ if (entry) {
+ aRv.ThrowNotSupportedError(
+ nsPrintfCString("'%s' actor is already registered.",
+ PromiseFlatCString(aName).get()));
+ return nullptr;
+ }
+
+ // Insert a new entry for the protocol.
+ RefPtr<JSWindowActorProtocol> protocol =
+ JSWindowActorProtocol::FromWebIDLOptions(aName, aOptions, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+
+ entry.Insert(protocol);
+
+ return protocol;
+ });
+
+ if (!proto) {
+ MOZ_ASSERT(aRv.Failed());
+ return;
+ }
+
+ // Send information about the newly added entry to every existing content
+ // process.
+ AutoTArray<JSWindowActorInfo, 1> windowInfos{proto->ToIPC()};
+ nsTArray<JSProcessActorInfo> contentInfos{};
+ for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) {
+ Unused << cp->SendInitJSActorInfos(contentInfos, windowInfos);
+ }
+
+ // Register event listeners for any existing chrome targets.
+ for (EventTarget* target : mChromeEventTargets) {
+ proto->RegisterListenersFor(target);
+ }
+
+ // Add observers to the protocol.
+ proto->AddObservers();
+}
+
+void JSActorService::UnregisterWindowActor(const nsACString& aName) {
+ MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
+ CrashReporter::AutoAnnotateCrashReport autoActorName(
+ CrashReporter::Annotation::JSActorName, aName);
+ CrashReporter::AutoAnnotateCrashReport autoMessageName(
+ CrashReporter::Annotation::JSActorMessage, "<Unregister>"_ns);
+
+ nsAutoCString name(aName);
+ RefPtr<JSWindowActorProtocol> proto;
+ if (mWindowActorDescriptors.Remove(name, getter_AddRefs(proto))) {
+ // Remove listeners for this actor from each of our chrome targets.
+ for (EventTarget* target : mChromeEventTargets) {
+ proto->UnregisterListenersFor(target);
+ }
+
+ // Remove observers for this actor from observer serivce.
+ proto->RemoveObservers();
+
+ // Tell every content process to also unregister, and accumulate the set of
+ // potential managers, to have the actor disabled.
+ nsTArray<RefPtr<JSActorManager>> managers;
+ if (XRE_IsParentProcess()) {
+ for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) {
+ Unused << cp->SendUnregisterJSWindowActor(name);
+ for (const auto& bp : cp->ManagedPBrowserParent()) {
+ for (const auto& wgp : bp->ManagedPWindowGlobalParent()) {
+ managers.AppendElement(static_cast<WindowGlobalParent*>(wgp));
+ }
+ }
+ }
+
+ for (const auto& wgp :
+ InProcessParent::Singleton()->ManagedPWindowGlobalParent()) {
+ managers.AppendElement(static_cast<WindowGlobalParent*>(wgp));
+ }
+ for (const auto& wgc :
+ InProcessChild::Singleton()->ManagedPWindowGlobalChild()) {
+ managers.AppendElement(static_cast<WindowGlobalChild*>(wgc));
+ }
+ } else {
+ for (const auto& bc :
+ ContentChild::GetSingleton()->ManagedPBrowserChild()) {
+ for (const auto& wgc : bc->ManagedPWindowGlobalChild()) {
+ managers.AppendElement(static_cast<WindowGlobalChild*>(wgc));
+ }
+ }
+ }
+
+ for (auto& mgr : managers) {
+ mgr->JSActorUnregister(name);
+ }
+ }
+}
+
+void JSActorService::LoadJSActorInfos(nsTArray<JSProcessActorInfo>& aProcess,
+ nsTArray<JSWindowActorInfo>& aWindow) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(XRE_IsContentProcess());
+
+ for (auto& info : aProcess) {
+ // Create our JSProcessActorProtocol, register it in
+ // mProcessActorDescriptors.
+ auto name = info.name();
+ RefPtr<JSProcessActorProtocol> proto =
+ JSProcessActorProtocol::FromIPC(std::move(info));
+ mProcessActorDescriptors.InsertOrUpdate(std::move(name), RefPtr{proto});
+
+ // Add observers for each actor.
+ proto->AddObservers();
+ }
+
+ for (auto& info : aWindow) {
+ auto name = info.name();
+ RefPtr<JSWindowActorProtocol> proto =
+ JSWindowActorProtocol::FromIPC(std::move(info));
+ mWindowActorDescriptors.InsertOrUpdate(std::move(name), RefPtr{proto});
+
+ // Register listeners for each chrome target.
+ for (EventTarget* target : mChromeEventTargets) {
+ proto->RegisterListenersFor(target);
+ }
+
+ // Add observers for each actor.
+ proto->AddObservers();
+ }
+}
+
+void JSActorService::GetJSWindowActorInfos(
+ nsTArray<JSWindowActorInfo>& aInfos) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ for (const auto& data : mWindowActorDescriptors.Values()) {
+ aInfos.AppendElement(data->ToIPC());
+ }
+}
+
+void JSActorService::RegisterChromeEventTarget(EventTarget* aTarget) {
+ MOZ_ASSERT(!mChromeEventTargets.Contains(aTarget));
+ mChromeEventTargets.AppendElement(aTarget);
+
+ // Register event listeners on the newly added Window Root.
+ for (const auto& data : mWindowActorDescriptors.Values()) {
+ data->RegisterListenersFor(aTarget);
+ }
+
+ nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
+ obs->NotifyObservers(aTarget, "chrome-event-target-created", nullptr);
+}
+
+/* static */
+void JSActorService::UnregisterChromeEventTarget(EventTarget* aTarget) {
+ if (gJSActorService) {
+ // NOTE: No need to unregister listeners here, as the target is going away.
+ gJSActorService->mChromeEventTargets.RemoveElement(aTarget);
+ }
+}
+
+void JSActorService::RegisterProcessActor(const nsACString& aName,
+ const ProcessActorOptions& aOptions,
+ ErrorResult& aRv) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ const auto proto = mProcessActorDescriptors.WithEntryHandle(
+ aName, [&](auto&& entry) -> RefPtr<JSProcessActorProtocol> {
+ if (entry) {
+ aRv.ThrowNotSupportedError(
+ nsPrintfCString("'%s' actor is already registered.",
+ PromiseFlatCString(aName).get()));
+ return nullptr;
+ }
+
+ // Insert a new entry for the protocol.
+ RefPtr<JSProcessActorProtocol> protocol =
+ JSProcessActorProtocol::FromWebIDLOptions(aName, aOptions, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nullptr;
+ }
+
+ entry.Insert(protocol);
+
+ return protocol;
+ });
+
+ if (!proto) {
+ MOZ_ASSERT(aRv.Failed());
+ return;
+ }
+
+ // Send information about the newly added entry to every existing content
+ // process.
+ AutoTArray<JSProcessActorInfo, 1> contentInfos{proto->ToIPC()};
+ nsTArray<JSWindowActorInfo> windowInfos{};
+ for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) {
+ Unused << cp->SendInitJSActorInfos(contentInfos, windowInfos);
+ }
+
+ // Add observers to the protocol.
+ proto->AddObservers();
+}
+
+void JSActorService::UnregisterProcessActor(const nsACString& aName) {
+ MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
+ CrashReporter::AutoAnnotateCrashReport autoActorName(
+ CrashReporter::Annotation::JSActorName, aName);
+ CrashReporter::AutoAnnotateCrashReport autoMessageName(
+ CrashReporter::Annotation::JSActorMessage, "<Unregister>"_ns);
+
+ nsAutoCString name(aName);
+ RefPtr<JSProcessActorProtocol> proto;
+ if (mProcessActorDescriptors.Remove(name, getter_AddRefs(proto))) {
+ // Remove observers for this actor from observer serivce.
+ proto->RemoveObservers();
+
+ // Tell every content process to also unregister, and accumulate the set of
+ // potential managers, to have the actor disabled.
+ nsTArray<RefPtr<JSActorManager>> managers;
+ if (XRE_IsParentProcess()) {
+ for (auto* cp : ContentParent::AllProcesses(ContentParent::eLive)) {
+ Unused << cp->SendUnregisterJSProcessActor(name);
+ managers.AppendElement(cp);
+ }
+ managers.AppendElement(InProcessChild::Singleton());
+ managers.AppendElement(InProcessParent::Singleton());
+ } else {
+ managers.AppendElement(ContentChild::GetSingleton());
+ }
+
+ for (auto& mgr : managers) {
+ mgr->JSActorUnregister(name);
+ }
+ }
+}
+
+void JSActorService::GetJSProcessActorInfos(
+ nsTArray<JSProcessActorInfo>& aInfos) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ for (const auto& data : mProcessActorDescriptors.Values()) {
+ aInfos.AppendElement(data->ToIPC());
+ }
+}
+
+already_AddRefed<JSProcessActorProtocol>
+JSActorService::GetJSProcessActorProtocol(const nsACString& aName) {
+ return mProcessActorDescriptors.Get(aName);
+}
+
+already_AddRefed<JSWindowActorProtocol>
+JSActorService::GetJSWindowActorProtocol(const nsACString& aName) {
+ return mWindowActorDescriptors.Get(aName);
+}
+
+} // namespace mozilla::dom
diff --git a/dom/ipc/jsactor/JSActorService.h b/dom/ipc/jsactor/JSActorService.h
new file mode 100644
index 0000000000..1e7a5fcdc7
--- /dev/null
+++ b/dom/ipc/jsactor/JSActorService.h
@@ -0,0 +1,113 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_JSActorService_h
+#define mozilla_dom_JSActorService_h
+
+#include "mozilla/dom/BrowsingContext.h"
+#include "nsIURI.h"
+#include "nsRefPtrHashtable.h"
+#include "nsString.h"
+#include "nsTArray.h"
+#include "mozilla/dom/JSActor.h"
+#include "nsIObserver.h"
+#include "nsIDOMEventListener.h"
+#include "mozilla/EventListenerManager.h"
+
+namespace mozilla {
+class ErrorResult;
+
+namespace dom {
+
+struct ProcessActorOptions;
+struct WindowActorOptions;
+class JSProcessActorInfo;
+class JSWindowActorInfo;
+class EventTarget;
+class JSWindowActorProtocol;
+class JSProcessActorProtocol;
+
+class JSActorService final {
+ public:
+ NS_INLINE_DECL_REFCOUNTING(JSActorService)
+
+ static already_AddRefed<JSActorService> GetSingleton();
+
+ // Register or unregister a chrome event target.
+ void RegisterChromeEventTarget(EventTarget* aTarget);
+
+ // NOTE: This method is static, as it may be called during shutdown.
+ static void UnregisterChromeEventTarget(EventTarget* aTarget);
+
+ // Register child's Actor for content process.
+ void LoadJSActorInfos(nsTArray<JSProcessActorInfo>& aProcess,
+ nsTArray<JSWindowActorInfo>& aWindow);
+
+ // --- Window Actor
+
+ void RegisterWindowActor(const nsACString& aName,
+ const WindowActorOptions& aOptions,
+ ErrorResult& aRv);
+
+ void UnregisterWindowActor(const nsACString& aName);
+
+ // Get the named of Window Actor and the child's WindowActorOptions
+ // from mDescriptors to JSWindowActorInfos.
+ void GetJSWindowActorInfos(nsTArray<JSWindowActorInfo>& aInfos);
+
+ already_AddRefed<JSWindowActorProtocol> GetJSWindowActorProtocol(
+ const nsACString& aName);
+
+ // -- Content Actor
+
+ void RegisterProcessActor(const nsACString& aName,
+ const ProcessActorOptions& aOptions,
+ ErrorResult& aRv);
+
+ void UnregisterProcessActor(const nsACString& aName);
+
+ // Get the named of Content Actor and the child's ProcessActorOptions
+ // from mDescriptors to JSProcessActorInfos.
+ void GetJSProcessActorInfos(nsTArray<JSProcessActorInfo>& aInfos);
+
+ already_AddRefed<JSProcessActorProtocol> GetJSProcessActorProtocol(
+ const nsACString& aName);
+
+ private:
+ JSActorService();
+ ~JSActorService();
+
+ nsTArray<EventTarget*> mChromeEventTargets;
+
+ // -- Window Actor
+ nsRefPtrHashtable<nsCStringHashKey, JSWindowActorProtocol>
+ mWindowActorDescriptors;
+
+ // -- Process Actor
+ nsRefPtrHashtable<nsCStringHashKey, JSProcessActorProtocol>
+ mProcessActorDescriptors;
+};
+
+/**
+ * Base class for both `JSWindowActorProtocol` and `JSProcessActorProtocol`
+ * which can be used by generic code.
+ */
+class JSActorProtocol : public nsISupports {
+ public:
+ struct Sided {
+ Maybe<nsCString> mModuleURI;
+ Maybe<nsCString> mESModuleURI;
+ };
+
+ virtual const Sided& Parent() const = 0;
+ virtual const Sided& Child() const = 0;
+ bool mLoadInDevToolsLoader = false;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_JSActorService_h
diff --git a/dom/ipc/jsactor/JSProcessActorChild.cpp b/dom/ipc/jsactor/JSProcessActorChild.cpp
new file mode 100644
index 0000000000..a9379838cf
--- /dev/null
+++ b/dom/ipc/jsactor/JSProcessActorChild.cpp
@@ -0,0 +1,86 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/JSProcessActorBinding.h"
+#include "mozilla/dom/JSProcessActorChild.h"
+#include "mozilla/dom/InProcessChild.h"
+#include "mozilla/dom/InProcessParent.h"
+
+namespace mozilla::dom {
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(JSProcessActorChild, JSActor, mManager)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(JSProcessActorChild)
+NS_INTERFACE_MAP_END_INHERITING(JSActor)
+
+NS_IMPL_ADDREF_INHERITED(JSProcessActorChild, JSActor)
+NS_IMPL_RELEASE_INHERITED(JSProcessActorChild, JSActor)
+
+JSObject* JSProcessActorChild::WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) {
+ return JSProcessActorChild_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+void JSProcessActorChild::SendRawMessage(
+ const JSActorMessageMeta& aMeta, Maybe<ipc::StructuredCloneData>&& aData,
+ Maybe<ipc::StructuredCloneData>&& aStack, ErrorResult& aRv) {
+ if (NS_WARN_IF(!CanSend() || !mManager || !mManager->GetCanSend())) {
+ aRv.ThrowInvalidStateError("JSProcessActorChild cannot send at the moment");
+ return;
+ }
+
+ // If the parent side is in the same process, we have a PInProcess manager,
+ // and can dispatch the message directly to the event loop.
+ ContentChild* contentChild = mManager->AsContentChild();
+ if (!contentChild) {
+ SendRawMessageInProcess(aMeta, std::move(aData), std::move(aStack), []() {
+ return do_AddRef(InProcessParent::Singleton());
+ });
+ return;
+ }
+
+ // Cross-process case - send data over ContentChild to other side.
+ Maybe<ClonedMessageData> msgData;
+ if (aData) {
+ msgData.emplace();
+ if (NS_WARN_IF(!aData->BuildClonedMessageData(*msgData))) {
+ aRv.ThrowDataCloneError(
+ nsPrintfCString("JSProcessActorChild serialization error: cannot "
+ "clone, in actor '%s'",
+ PromiseFlatCString(aMeta.actorName()).get()));
+ return;
+ }
+ }
+
+ Maybe<ClonedMessageData> stackData;
+ if (aStack) {
+ stackData.emplace();
+ if (!aStack->BuildClonedMessageData(*stackData)) {
+ stackData.reset();
+ }
+ }
+
+ if (NS_WARN_IF(!contentChild->SendRawMessage(aMeta, msgData, stackData))) {
+ aRv.ThrowOperationError(
+ nsPrintfCString("JSProcessActorChild send error in actor '%s'",
+ PromiseFlatCString(aMeta.actorName()).get()));
+ return;
+ }
+}
+
+void JSProcessActorChild::Init(const nsACString& aName,
+ nsIDOMProcessChild* aManager) {
+ MOZ_ASSERT(!mManager, "Cannot Init() a JSProcessActorChild twice!");
+ SetName(aName);
+ mManager = aManager;
+
+ InvokeCallback(CallbackFunction::ActorCreated);
+}
+
+void JSProcessActorChild::ClearManager() { mManager = nullptr; }
+
+} // namespace mozilla::dom
diff --git a/dom/ipc/jsactor/JSProcessActorChild.h b/dom/ipc/jsactor/JSProcessActorChild.h
new file mode 100644
index 0000000000..a8be65211c
--- /dev/null
+++ b/dom/ipc/jsactor/JSProcessActorChild.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_JSProcessActorChild_h
+#define mozilla_dom_JSProcessActorChild_h
+
+#include "mozilla/dom/JSActor.h"
+#include "nsIDOMProcessChild.h"
+
+namespace mozilla::dom {
+
+// Placeholder implementation.
+class JSProcessActorChild final : public JSActor {
+ public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(JSProcessActorChild, JSActor)
+
+ explicit JSProcessActorChild(nsISupports* aGlobal = nullptr)
+ : JSActor(aGlobal) {}
+
+ JSObject* WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) override;
+
+ static already_AddRefed<JSProcessActorChild> Constructor(
+ GlobalObject& aGlobal) {
+ return MakeAndAddRef<JSProcessActorChild>(aGlobal.GetAsSupports());
+ }
+
+ nsIDOMProcessChild* Manager() const { return mManager; }
+
+ void Init(const nsACString& aName, nsIDOMProcessChild* aManager);
+ void ClearManager() override;
+
+ protected:
+ // Send the message described by the structured clone data |aData|, and the
+ // message metadata |aMetadata|. The underlying transport should call the
+ // |ReceiveMessage| method on the other side asynchronously.
+ virtual void SendRawMessage(const JSActorMessageMeta& aMetadata,
+ Maybe<ipc::StructuredCloneData>&& aData,
+ Maybe<ipc::StructuredCloneData>&& aStack,
+ ErrorResult& aRv) override;
+
+ private:
+ ~JSProcessActorChild() { MOZ_ASSERT(!mManager); }
+
+ nsCOMPtr<nsIDOMProcessChild> mManager;
+};
+
+} // namespace mozilla::dom
+
+#endif // mozilla_dom_JSProcessActorChild_h
diff --git a/dom/ipc/jsactor/JSProcessActorParent.cpp b/dom/ipc/jsactor/JSProcessActorParent.cpp
new file mode 100644
index 0000000000..eec8ad17c7
--- /dev/null
+++ b/dom/ipc/jsactor/JSProcessActorParent.cpp
@@ -0,0 +1,90 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/JSProcessActorBinding.h"
+#include "mozilla/dom/JSProcessActorParent.h"
+#include "mozilla/dom/InProcessChild.h"
+#include "mozilla/dom/InProcessParent.h"
+
+namespace mozilla::dom {
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(JSProcessActorParent, JSActor, mManager)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(JSProcessActorParent)
+NS_INTERFACE_MAP_END_INHERITING(JSActor)
+
+NS_IMPL_ADDREF_INHERITED(JSProcessActorParent, JSActor)
+NS_IMPL_RELEASE_INHERITED(JSProcessActorParent, JSActor)
+
+JSObject* JSProcessActorParent::WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) {
+ return JSProcessActorParent_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+void JSProcessActorParent::Init(const nsACString& aName,
+ nsIDOMProcessParent* aManager) {
+ MOZ_ASSERT(!mManager, "Cannot Init() a JSProcessActorParent twice!");
+ SetName(aName);
+ mManager = aManager;
+
+ InvokeCallback(CallbackFunction::ActorCreated);
+}
+
+JSProcessActorParent::~JSProcessActorParent() { MOZ_ASSERT(!mManager); }
+
+void JSProcessActorParent::SendRawMessage(
+ const JSActorMessageMeta& aMeta, Maybe<ipc::StructuredCloneData>&& aData,
+ Maybe<ipc::StructuredCloneData>&& aStack, ErrorResult& aRv) {
+ if (NS_WARN_IF(!CanSend() || !mManager || !mManager->GetCanSend())) {
+ aRv.ThrowInvalidStateError(
+ nsPrintfCString("Actor '%s' cannot send message '%s' during shutdown.",
+ PromiseFlatCString(aMeta.actorName()).get(),
+ NS_ConvertUTF16toUTF8(aMeta.messageName()).get()));
+ return;
+ }
+
+ // If the parent side is in the same process, we have a PInProcess manager,
+ // and can dispatch the message directly to the event loop.
+ ContentParent* contentParent = mManager->AsContentParent();
+ if (!contentParent) {
+ SendRawMessageInProcess(aMeta, std::move(aData), std::move(aStack), []() {
+ return do_AddRef(InProcessChild::Singleton());
+ });
+ return;
+ }
+
+ // Cross-process case - send data over ContentParent to other side.
+ Maybe<ClonedMessageData> msgData;
+ if (aData) {
+ msgData.emplace();
+ if (NS_WARN_IF(!aData->BuildClonedMessageData(*msgData))) {
+ aRv.ThrowDataCloneError(
+ nsPrintfCString("Actor '%s' cannot send message '%s': cannot clone.",
+ PromiseFlatCString(aMeta.actorName()).get(),
+ NS_ConvertUTF16toUTF8(aMeta.messageName()).get()));
+ return;
+ }
+ }
+
+ Maybe<ClonedMessageData> stackData;
+ if (aStack) {
+ stackData.emplace();
+ if (!aStack->BuildClonedMessageData(*stackData)) {
+ stackData.reset();
+ }
+ }
+
+ if (NS_WARN_IF(!contentParent->SendRawMessage(aMeta, msgData, stackData))) {
+ aRv.ThrowOperationError(
+ nsPrintfCString("JSProcessActorParent send error in actor '%s'",
+ PromiseFlatCString(aMeta.actorName()).get()));
+ return;
+ }
+}
+
+void JSProcessActorParent::ClearManager() { mManager = nullptr; }
+
+} // namespace mozilla::dom
diff --git a/dom/ipc/jsactor/JSProcessActorParent.h b/dom/ipc/jsactor/JSProcessActorParent.h
new file mode 100644
index 0000000000..7b2a1535f9
--- /dev/null
+++ b/dom/ipc/jsactor/JSProcessActorParent.h
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_JSProcessActorParent_h
+#define mozilla_dom_JSProcessActorParent_h
+
+#include "js/TypeDecls.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/JSActor.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsIDOMProcessParent.h"
+#include "nsWrapperCache.h"
+
+namespace mozilla {
+class ErrorResult;
+
+namespace dom {
+
+class JSProcessActorParent final : public JSActor {
+ public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(JSProcessActorParent, JSActor)
+
+ explicit JSProcessActorParent(nsISupports* aGlobal = nullptr)
+ : JSActor(aGlobal) {}
+
+ JSObject* WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) override;
+
+ static already_AddRefed<JSProcessActorParent> Constructor(
+ GlobalObject& aGlobal) {
+ return MakeAndAddRef<JSProcessActorParent>(aGlobal.GetAsSupports());
+ }
+
+ nsIDOMProcessParent* Manager() const { return mManager; }
+
+ void Init(const nsACString& aName, nsIDOMProcessParent* aManager);
+ void ClearManager() override;
+
+ protected:
+ // Send the message described by the structured clone data |aData|, and the
+ // message metadata |aMetadata|. The underlying transport should call the
+ // |ReceiveMessage| method on the other side asynchronously.
+ virtual void SendRawMessage(const JSActorMessageMeta& aMetadata,
+ Maybe<ipc::StructuredCloneData>&& aData,
+ Maybe<ipc::StructuredCloneData>&& aStack,
+ ErrorResult& aRv) override;
+
+ private:
+ ~JSProcessActorParent();
+
+ nsCOMPtr<nsIDOMProcessParent> mManager;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_JSProcessActorParent_h
diff --git a/dom/ipc/jsactor/JSProcessActorProtocol.cpp b/dom/ipc/jsactor/JSProcessActorProtocol.cpp
new file mode 100644
index 0000000000..e973cdec44
--- /dev/null
+++ b/dom/ipc/jsactor/JSProcessActorProtocol.cpp
@@ -0,0 +1,149 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/JSProcessActorProtocol.h"
+#include "mozilla/dom/InProcessChild.h"
+#include "mozilla/dom/JSProcessActorBinding.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/JSActorBinding.h"
+#include "mozilla/dom/PContent.h"
+
+#include "nsContentUtils.h"
+#include "JSActorProtocolUtils.h"
+
+namespace mozilla::dom {
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(JSProcessActorProtocol)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(JSProcessActorProtocol)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(JSProcessActorProtocol)
+ NS_INTERFACE_MAP_ENTRY(nsIObserver)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTION(JSProcessActorProtocol)
+
+/* static */ already_AddRefed<JSProcessActorProtocol>
+JSProcessActorProtocol::FromIPC(const JSProcessActorInfo& aInfo) {
+ MOZ_DIAGNOSTIC_ASSERT(XRE_IsContentProcess());
+
+ RefPtr<JSProcessActorProtocol> proto =
+ new JSProcessActorProtocol(aInfo.name());
+ JSActorProtocolUtils::FromIPCShared(proto, aInfo);
+
+ // Content processes aren't the parent process, so this flag is irrelevant and
+ // not propagated.
+ proto->mIncludeParent = false;
+
+ return proto.forget();
+}
+
+JSProcessActorInfo JSProcessActorProtocol::ToIPC() {
+ MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
+
+ JSProcessActorInfo info;
+ JSActorProtocolUtils::ToIPCShared(info, this);
+
+ return info;
+}
+
+already_AddRefed<JSProcessActorProtocol>
+JSProcessActorProtocol::FromWebIDLOptions(const nsACString& aName,
+ const ProcessActorOptions& aOptions,
+ ErrorResult& aRv) {
+ MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
+
+ RefPtr<JSProcessActorProtocol> proto = new JSProcessActorProtocol(aName);
+ if (!JSActorProtocolUtils::FromWebIDLOptionsShared(proto, aOptions, aRv)) {
+ return nullptr;
+ }
+
+ proto->mIncludeParent = aOptions.mIncludeParent;
+
+ proto->mLoadInDevToolsLoader = aOptions.mLoadInDevToolsLoader;
+
+ return proto.forget();
+}
+
+NS_IMETHODIMP JSProcessActorProtocol::Observe(nsISupports* aSubject,
+ const char* aTopic,
+ const char16_t* aData) {
+ MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
+
+ RefPtr<JSActorManager> manager;
+ if (XRE_IsParentProcess()) {
+ manager = InProcessChild::Singleton();
+ } else {
+ manager = ContentChild::GetSingleton();
+ }
+
+ // Ensure our actor is present.
+ AutoJSAPI jsapi;
+ jsapi.Init();
+ RefPtr<JSActor> actor = manager->GetActor(jsapi.cx(), mName, IgnoreErrors());
+ if (!actor || NS_WARN_IF(!actor->GetWrapperPreserveColor())) {
+ return NS_OK;
+ }
+
+ // Build a observer callback.
+ JS::Rooted<JSObject*> global(jsapi.cx(),
+ JS::GetNonCCWObjectGlobal(actor->GetWrapper()));
+ RefPtr<MozObserverCallback> observerCallback =
+ new MozObserverCallback(actor->GetWrapper(), global, nullptr, nullptr);
+ observerCallback->Observe(aSubject, nsDependentCString(aTopic),
+ aData ? nsDependentString(aData) : VoidString());
+ return NS_OK;
+}
+
+void JSProcessActorProtocol::AddObservers() {
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ for (auto& topic : mChild.mObservers) {
+ // This makes the observer service hold an owning reference to the
+ // JSProcessActorProtocol. The JSWindowActorProtocol objects will be living
+ // for the full lifetime of the content process, thus the extra strong
+ // referencec doesn't have a negative impact.
+ os->AddObserver(this, topic.get(), false);
+ }
+}
+
+void JSProcessActorProtocol::RemoveObservers() {
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ for (auto& topic : mChild.mObservers) {
+ os->RemoveObserver(this, topic.get());
+ }
+}
+
+bool JSProcessActorProtocol::RemoteTypePrefixMatches(
+ const nsDependentCSubstring& aRemoteType) {
+ for (auto& remoteType : mRemoteTypes) {
+ if (StringBeginsWith(aRemoteType, remoteType)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool JSProcessActorProtocol::Matches(const nsACString& aRemoteType,
+ ErrorResult& aRv) {
+ if (!mIncludeParent && aRemoteType.IsEmpty()) {
+ aRv.ThrowNotSupportedError(nsPrintfCString(
+ "Process protocol '%s' doesn't match the parent process", mName.get()));
+ return false;
+ }
+
+ if (!mRemoteTypes.IsEmpty() &&
+ !RemoteTypePrefixMatches(RemoteTypePrefix(aRemoteType))) {
+ aRv.ThrowNotSupportedError(nsPrintfCString(
+ "Process protocol '%s' doesn't support remote type '%s'", mName.get(),
+ PromiseFlatCString(aRemoteType).get()));
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace mozilla::dom
diff --git a/dom/ipc/jsactor/JSProcessActorProtocol.h b/dom/ipc/jsactor/JSProcessActorProtocol.h
new file mode 100644
index 0000000000..eab8876c35
--- /dev/null
+++ b/dom/ipc/jsactor/JSProcessActorProtocol.h
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_JSProcessActorProtocol_h
+#define mozilla_dom_JSProcessActorProtocol_h
+
+#include "mozilla/dom/BrowsingContext.h"
+#include "mozilla/dom/JSActorService.h"
+#include "nsIURI.h"
+#include "nsString.h"
+#include "nsTArray.h"
+#include "nsIObserver.h"
+
+namespace mozilla {
+class ErrorResult;
+
+namespace dom {
+
+struct ProcessActorOptions;
+class JSProcessActorInfo;
+class EventTarget;
+class JSActorProtocolUtils;
+
+/**
+ * Object corresponding to a single process actor protocol
+ *
+ * This object also can act as a carrier for methods and other state related to
+ * a single protocol managed by the JSActorService.
+ */
+class JSProcessActorProtocol final : public JSActorProtocol,
+ public nsIObserver {
+ public:
+ NS_DECL_NSIOBSERVER
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(JSProcessActorProtocol, nsIObserver)
+
+ static already_AddRefed<JSProcessActorProtocol> FromIPC(
+ const JSProcessActorInfo& aInfo);
+ JSProcessActorInfo ToIPC();
+
+ static already_AddRefed<JSProcessActorProtocol> FromWebIDLOptions(
+ const nsACString& aName, const ProcessActorOptions& aOptions,
+ ErrorResult& aRv);
+
+ struct ParentSide : public Sided {};
+
+ struct ChildSide : public Sided {
+ nsTArray<nsCString> mObservers;
+ };
+
+ const ParentSide& Parent() const override { return mParent; }
+ const ChildSide& Child() const override { return mChild; }
+
+ void AddObservers();
+ void RemoveObservers();
+ bool Matches(const nsACString& aRemoteType, ErrorResult& aRv);
+
+ private:
+ explicit JSProcessActorProtocol(const nsACString& aName) : mName(aName) {}
+ bool RemoteTypePrefixMatches(const nsDependentCSubstring& aRemoteType);
+ ~JSProcessActorProtocol() = default;
+
+ nsCString mName;
+ nsTArray<nsCString> mRemoteTypes;
+ bool mIncludeParent = false;
+
+ friend class JSActorProtocolUtils;
+
+ ParentSide mParent;
+ ChildSide mChild;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_JSProcessActorProtocol_h
diff --git a/dom/ipc/jsactor/JSWindowActorChild.cpp b/dom/ipc/jsactor/JSWindowActorChild.cpp
new file mode 100644
index 0000000000..4d028a0789
--- /dev/null
+++ b/dom/ipc/jsactor/JSWindowActorChild.cpp
@@ -0,0 +1,160 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/JSWindowActorBinding.h"
+#include "mozilla/dom/JSWindowActorChild.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/WindowGlobalChild.h"
+#include "mozilla/dom/WindowGlobalParent.h"
+#include "mozilla/dom/WindowProxyHolder.h"
+#include "mozilla/dom/MessageManagerBinding.h"
+#include "mozilla/dom/BrowsingContext.h"
+#include "nsGlobalWindowInner.h"
+
+namespace mozilla::dom {
+
+JSWindowActorChild::~JSWindowActorChild() { MOZ_ASSERT(!mManager); }
+
+JSObject* JSWindowActorChild::WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) {
+ return JSWindowActorChild_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+WindowGlobalChild* JSWindowActorChild::GetManager() const { return mManager; }
+
+WindowContext* JSWindowActorChild::GetWindowContext() const {
+ return mManager ? mManager->WindowContext() : nullptr;
+}
+
+void JSWindowActorChild::Init(const nsACString& aName,
+ WindowGlobalChild* aManager) {
+ MOZ_ASSERT(!mManager, "Cannot Init() a JSWindowActorChild twice!");
+ SetName(aName);
+ mManager = aManager;
+
+ InvokeCallback(CallbackFunction::ActorCreated);
+}
+
+#ifdef DEBUG
+# define DEBUG_WARN_MESSAGE_UNSENT(aMeta, aWarning) \
+ NS_DebugBreak( \
+ NS_DEBUG_WARNING, \
+ nsPrintfCString( \
+ "JSWindowActorChild::SendRawMessage (%s, %s) not sent: %s", \
+ (aMeta).actorName().get(), \
+ NS_LossyConvertUTF16toASCII((aMeta).messageName()).get(), \
+ (aWarning)) \
+ .get(), \
+ nullptr, __FILE__, __LINE__)
+#else
+# define DEBUG_WARN_MESSAGE_UNSENT(aMeta, aWarning)
+#endif
+
+void JSWindowActorChild::SendRawMessage(
+ const JSActorMessageMeta& aMeta, Maybe<ipc::StructuredCloneData>&& aData,
+ Maybe<ipc::StructuredCloneData>&& aStack, ErrorResult& aRv) {
+ if (!CanSend() || !mManager || !mManager->CanSend()) {
+ DEBUG_WARN_MESSAGE_UNSENT(
+ aMeta, "!CanSend() || !mManager || !mManager->CanSend()");
+ aRv.ThrowInvalidStateError("JSWindowActorChild cannot send at the moment");
+ return;
+ }
+
+ if (mManager->IsInProcess()) {
+ SendRawMessageInProcess(
+ aMeta, std::move(aData), std::move(aStack),
+ [manager{mManager}]() { return manager->GetParentActor(); });
+ return;
+ }
+
+ // Cross-process case - send data over WindowGlobalChild to other side.
+ Maybe<ClonedMessageData> msgData;
+ if (aData) {
+ msgData.emplace();
+ if (!aData->BuildClonedMessageData(*msgData)) {
+ DEBUG_WARN_MESSAGE_UNSENT(aMeta,
+ "!aData->BuildClonedMessageData(*msgData)");
+ aRv.ThrowDataCloneError(
+ nsPrintfCString("JSWindowActorChild serialization error: cannot "
+ "clone, in actor '%s'",
+ PromiseFlatCString(aMeta.actorName()).get()));
+ return;
+ }
+ }
+
+ Maybe<ClonedMessageData> stackData;
+ if (aStack) {
+ stackData.emplace();
+ if (!aStack->BuildClonedMessageData(*stackData)) {
+ stackData.reset();
+ }
+ }
+
+ if (!mManager->SendRawMessage(aMeta, msgData, stackData)) {
+ DEBUG_WARN_MESSAGE_UNSENT(
+ aMeta, "!mManager->SendRawMessage(aMeta, msgData, stackData)");
+ aRv.ThrowOperationError(
+ nsPrintfCString("JSWindowActorChild send error in actor '%s'",
+ PromiseFlatCString(aMeta.actorName()).get()));
+ return;
+ }
+}
+
+Document* JSWindowActorChild::GetDocument(ErrorResult& aRv) {
+ if (!mManager) {
+ ThrowStateErrorForGetter("document", aRv);
+ return nullptr;
+ }
+
+ nsGlobalWindowInner* window = mManager->GetWindowGlobal();
+ return window ? window->GetDocument() : nullptr;
+}
+
+BrowsingContext* JSWindowActorChild::GetBrowsingContext(ErrorResult& aRv) {
+ if (!mManager) {
+ ThrowStateErrorForGetter("browsingContext", aRv);
+ return nullptr;
+ }
+
+ return mManager->BrowsingContext();
+}
+
+nsIDocShell* JSWindowActorChild::GetDocShell(ErrorResult& aRv) {
+ if (!mManager) {
+ ThrowStateErrorForGetter("docShell", aRv);
+ return nullptr;
+ }
+
+ return mManager->BrowsingContext()->GetDocShell();
+}
+
+Nullable<WindowProxyHolder> JSWindowActorChild::GetContentWindow(
+ ErrorResult& aRv) {
+ if (!mManager) {
+ ThrowStateErrorForGetter("contentWindow", aRv);
+ return nullptr;
+ }
+
+ if (nsGlobalWindowInner* window = mManager->GetWindowGlobal()) {
+ if (window->IsCurrentInnerWindow()) {
+ return WindowProxyHolder(window->GetBrowsingContext());
+ }
+ }
+
+ return nullptr;
+}
+
+void JSWindowActorChild::ClearManager() { mManager = nullptr; }
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(JSWindowActorChild, JSActor, mManager)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(JSWindowActorChild)
+NS_INTERFACE_MAP_END_INHERITING(JSActor)
+
+NS_IMPL_ADDREF_INHERITED(JSWindowActorChild, JSActor)
+NS_IMPL_RELEASE_INHERITED(JSWindowActorChild, JSActor)
+
+} // namespace mozilla::dom
diff --git a/dom/ipc/jsactor/JSWindowActorChild.h b/dom/ipc/jsactor/JSWindowActorChild.h
new file mode 100644
index 0000000000..8e43d6a6c7
--- /dev/null
+++ b/dom/ipc/jsactor/JSWindowActorChild.h
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_JSWindowActorChild_h
+#define mozilla_dom_JSWindowActorChild_h
+
+#include "js/RootingAPI.h"
+#include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/JSActor.h"
+#include "mozilla/dom/WindowGlobalChild.h"
+#include "nsCOMPtr.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsIGlobalObject.h"
+#include "nsISupports.h"
+#include "nsStringFwd.h"
+
+class nsIDocShell;
+
+namespace mozilla {
+class ErrorResult;
+
+namespace dom {
+
+template <typename>
+struct Nullable;
+
+class BrowsingContext;
+class Document;
+class WindowProxyHolder;
+
+} // namespace dom
+} // namespace mozilla
+
+namespace mozilla::dom {
+
+class JSWindowActorChild final : public JSActor {
+ public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(JSWindowActorChild, JSActor)
+
+ explicit JSWindowActorChild(nsISupports* aGlobal = nullptr)
+ : JSActor(aGlobal) {}
+
+ JSObject* WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) override;
+
+ static already_AddRefed<JSWindowActorChild> Constructor(
+ GlobalObject& aGlobal) {
+ return MakeAndAddRef<JSWindowActorChild>(aGlobal.GetAsSupports());
+ }
+
+ WindowGlobalChild* GetManager() const;
+ WindowContext* GetWindowContext() const;
+ void Init(const nsACString& aName, WindowGlobalChild* aManager);
+ void ClearManager() override;
+ Document* GetDocument(ErrorResult& aRv);
+ BrowsingContext* GetBrowsingContext(ErrorResult& aRv);
+ nsIDocShell* GetDocShell(ErrorResult& aRv);
+ Nullable<WindowProxyHolder> GetContentWindow(ErrorResult& aRv);
+
+ protected:
+ void SendRawMessage(const JSActorMessageMeta& aMeta,
+ Maybe<ipc::StructuredCloneData>&& aData,
+ Maybe<ipc::StructuredCloneData>&& aStack,
+ ErrorResult& aRv) override;
+
+ private:
+ ~JSWindowActorChild();
+
+ RefPtr<WindowGlobalChild> mManager;
+
+ nsCOMPtr<nsIGlobalObject> mGlobal;
+};
+
+} // namespace mozilla::dom
+
+#endif // mozilla_dom_JSWindowActorChild_h
diff --git a/dom/ipc/jsactor/JSWindowActorParent.cpp b/dom/ipc/jsactor/JSWindowActorParent.cpp
new file mode 100644
index 0000000000..0bb82a9b8e
--- /dev/null
+++ b/dom/ipc/jsactor/JSWindowActorParent.cpp
@@ -0,0 +1,102 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/JSWindowActorBinding.h"
+#include "mozilla/dom/JSWindowActorParent.h"
+#include "mozilla/dom/BrowserParent.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/WindowGlobalChild.h"
+#include "mozilla/dom/WindowGlobalParent.h"
+#include "mozilla/dom/MessageManagerBinding.h"
+
+namespace mozilla::dom {
+
+JSWindowActorParent::~JSWindowActorParent() { MOZ_ASSERT(!mManager); }
+
+JSObject* JSWindowActorParent::WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) {
+ return JSWindowActorParent_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+WindowGlobalParent* JSWindowActorParent::GetManager() const { return mManager; }
+
+WindowContext* JSWindowActorParent::GetWindowContext() const {
+ return mManager;
+}
+
+void JSWindowActorParent::Init(const nsACString& aName,
+ WindowGlobalParent* aManager) {
+ MOZ_ASSERT(!mManager, "Cannot Init() a JSWindowActorParent twice!");
+ SetName(aName);
+ mManager = aManager;
+
+ InvokeCallback(CallbackFunction::ActorCreated);
+}
+
+void JSWindowActorParent::SendRawMessage(
+ const JSActorMessageMeta& aMeta, Maybe<ipc::StructuredCloneData>&& aData,
+ Maybe<ipc::StructuredCloneData>&& aStack, ErrorResult& aRv) {
+ if (NS_WARN_IF(!CanSend() || !mManager || !mManager->CanSend())) {
+ aRv.ThrowInvalidStateError("JSWindowActorParent cannot send at the moment");
+ return;
+ }
+
+ if (mManager->IsInProcess()) {
+ SendRawMessageInProcess(
+ aMeta, std::move(aData), std::move(aStack),
+ [manager{mManager}]() { return manager->GetChildActor(); });
+ return;
+ }
+
+ Maybe<ClonedMessageData> msgData;
+ if (aData) {
+ msgData.emplace();
+ if (NS_WARN_IF(!aData->BuildClonedMessageData(*msgData))) {
+ aRv.ThrowDataCloneError(
+ nsPrintfCString("JSWindowActorParent serialization error: cannot "
+ "clone, in actor '%s'",
+ PromiseFlatCString(aMeta.actorName()).get()));
+ return;
+ }
+ }
+
+ Maybe<ClonedMessageData> stackData;
+ if (aStack) {
+ stackData.emplace();
+ if (!aStack->BuildClonedMessageData(*stackData)) {
+ stackData.reset();
+ }
+ }
+
+ if (NS_WARN_IF(!mManager->SendRawMessage(aMeta, msgData, stackData))) {
+ aRv.ThrowOperationError(
+ nsPrintfCString("JSWindowActorParent send error in actor '%s'",
+ PromiseFlatCString(aMeta.actorName()).get()));
+ return;
+ }
+}
+
+CanonicalBrowsingContext* JSWindowActorParent::GetBrowsingContext(
+ ErrorResult& aRv) {
+ if (!mManager) {
+ aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+ return nullptr;
+ }
+
+ return mManager->BrowsingContext();
+}
+
+void JSWindowActorParent::ClearManager() { mManager = nullptr; }
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(JSWindowActorParent, JSActor, mManager)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(JSWindowActorParent)
+NS_INTERFACE_MAP_END_INHERITING(JSActor)
+
+NS_IMPL_ADDREF_INHERITED(JSWindowActorParent, JSActor)
+NS_IMPL_RELEASE_INHERITED(JSWindowActorParent, JSActor)
+
+} // namespace mozilla::dom
diff --git a/dom/ipc/jsactor/JSWindowActorParent.h b/dom/ipc/jsactor/JSWindowActorParent.h
new file mode 100644
index 0000000000..05fb88f8ac
--- /dev/null
+++ b/dom/ipc/jsactor/JSWindowActorParent.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/. */
+
+#ifndef mozilla_dom_JSWindowActorParent_h
+#define mozilla_dom_JSWindowActorParent_h
+
+#include "js/TypeDecls.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/JSActor.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsWrapperCache.h"
+
+namespace mozilla {
+class ErrorResult;
+
+namespace dom {
+
+class WindowGlobalParent;
+
+} // namespace dom
+} // namespace mozilla
+
+namespace mozilla::dom {
+
+class JSWindowActorParent final : public JSActor {
+ public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(JSWindowActorParent, JSActor)
+
+ explicit JSWindowActorParent(nsISupports* aGlobal = nullptr)
+ : JSActor(aGlobal) {}
+
+ JSObject* WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) override;
+
+ static already_AddRefed<JSWindowActorParent> Constructor(
+ GlobalObject& aGlobal) {
+ return MakeAndAddRef<JSWindowActorParent>(aGlobal.GetAsSupports());
+ }
+
+ WindowGlobalParent* GetManager() const;
+ WindowContext* GetWindowContext() const;
+ void Init(const nsACString& aName, WindowGlobalParent* aManager);
+ void ClearManager() override;
+ CanonicalBrowsingContext* GetBrowsingContext(ErrorResult& aRv);
+
+ protected:
+ void SendRawMessage(const JSActorMessageMeta& aMeta,
+ Maybe<ipc::StructuredCloneData>&& aData,
+ Maybe<ipc::StructuredCloneData>&& aStack,
+ ErrorResult& aRv) override;
+
+ private:
+ ~JSWindowActorParent();
+
+ RefPtr<WindowGlobalParent> mManager;
+};
+
+} // namespace mozilla::dom
+
+#endif // mozilla_dom_JSWindowActorParent_h
diff --git a/dom/ipc/jsactor/JSWindowActorProtocol.cpp b/dom/ipc/jsactor/JSWindowActorProtocol.cpp
new file mode 100644
index 0000000000..a459d94204
--- /dev/null
+++ b/dom/ipc/jsactor/JSWindowActorProtocol.cpp
@@ -0,0 +1,380 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/Event.h"
+#include "mozilla/dom/JSActorBinding.h"
+#include "mozilla/dom/JSActorService.h"
+#include "mozilla/dom/JSWindowActorBinding.h"
+#include "mozilla/dom/JSWindowActorChild.h"
+#include "mozilla/dom/JSWindowActorProtocol.h"
+#include "mozilla/dom/PContent.h"
+#include "mozilla/dom/WindowGlobalChild.h"
+
+#include "mozilla/extensions/MatchPattern.h"
+#include "nsContentUtils.h"
+#include "JSActorProtocolUtils.h"
+
+namespace mozilla::dom {
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(JSWindowActorProtocol)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(JSWindowActorProtocol)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(JSWindowActorProtocol)
+ NS_INTERFACE_MAP_ENTRY(nsIObserver)
+ NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTION(JSWindowActorProtocol)
+
+/* static */ already_AddRefed<JSWindowActorProtocol>
+JSWindowActorProtocol::FromIPC(const JSWindowActorInfo& aInfo) {
+ MOZ_DIAGNOSTIC_ASSERT(XRE_IsContentProcess());
+
+ RefPtr<JSWindowActorProtocol> proto = new JSWindowActorProtocol(aInfo.name());
+ JSActorProtocolUtils::FromIPCShared(proto, aInfo);
+
+ // Content processes cannot load chrome browsing contexts, so this flag is
+ // irrelevant and not propagated.
+ proto->mIncludeChrome = false;
+ proto->mAllFrames = aInfo.allFrames();
+ proto->mMatches = aInfo.matches().Clone();
+ proto->mMessageManagerGroups = aInfo.messageManagerGroups().Clone();
+
+ proto->mChild.mEvents.SetCapacity(aInfo.events().Length());
+ for (auto& ipc : aInfo.events()) {
+ auto event = proto->mChild.mEvents.AppendElement();
+ event->mName.Assign(ipc.name());
+ event->mFlags.mCapture = ipc.capture();
+ event->mFlags.mInSystemGroup = ipc.systemGroup();
+ event->mFlags.mAllowUntrustedEvents = ipc.allowUntrusted();
+ if (ipc.passive()) {
+ event->mPassive.Construct(ipc.passive().value());
+ }
+ event->mCreateActor = ipc.createActor();
+ }
+
+ return proto.forget();
+}
+
+JSWindowActorInfo JSWindowActorProtocol::ToIPC() {
+ MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
+
+ JSWindowActorInfo info;
+ JSActorProtocolUtils::ToIPCShared(info, this);
+
+ info.allFrames() = mAllFrames;
+ info.matches() = mMatches.Clone();
+ info.messageManagerGroups() = mMessageManagerGroups.Clone();
+
+ info.events().SetCapacity(mChild.mEvents.Length());
+ for (auto& event : mChild.mEvents) {
+ auto ipc = info.events().AppendElement();
+ ipc->name().Assign(event.mName);
+ ipc->capture() = event.mFlags.mCapture;
+ ipc->systemGroup() = event.mFlags.mInSystemGroup;
+ ipc->allowUntrusted() = event.mFlags.mAllowUntrustedEvents;
+ if (event.mPassive.WasPassed()) {
+ ipc->passive() = Some(event.mPassive.Value());
+ }
+ ipc->createActor() = event.mCreateActor;
+ }
+
+ return info;
+}
+
+already_AddRefed<JSWindowActorProtocol>
+JSWindowActorProtocol::FromWebIDLOptions(const nsACString& aName,
+ const WindowActorOptions& aOptions,
+ ErrorResult& aRv) {
+ MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
+
+ RefPtr<JSWindowActorProtocol> proto = new JSWindowActorProtocol(aName);
+ if (!JSActorProtocolUtils::FromWebIDLOptionsShared(proto, aOptions, aRv)) {
+ return nullptr;
+ }
+
+ proto->mAllFrames = aOptions.mAllFrames;
+ proto->mIncludeChrome = aOptions.mIncludeChrome;
+
+ if (aOptions.mMatches.WasPassed()) {
+ MOZ_ASSERT(aOptions.mMatches.Value().Length());
+ proto->mMatches = aOptions.mMatches.Value();
+ }
+
+ if (aOptions.mMessageManagerGroups.WasPassed()) {
+ proto->mMessageManagerGroups = aOptions.mMessageManagerGroups.Value();
+ }
+
+ // For each event declared in the source dictionary, initialize the
+ // corresponding event declaration entry in the JSWindowActorProtocol.
+ if (aOptions.mChild.WasPassed() &&
+ aOptions.mChild.Value().mEvents.WasPassed()) {
+ auto& entries = aOptions.mChild.Value().mEvents.Value().Entries();
+ proto->mChild.mEvents.SetCapacity(entries.Length());
+
+ for (auto& entry : entries) {
+ // We don't support the mOnce field, as it doesn't work well in this
+ // environment. For now, throw an error in that case.
+ if (entry.mValue.mOnce) {
+ aRv.ThrowNotSupportedError("mOnce is not supported");
+ return nullptr;
+ }
+
+ // Add the EventDecl to our list of events.
+ EventDecl* evt = proto->mChild.mEvents.AppendElement();
+ evt->mName = entry.mKey;
+ evt->mFlags.mCapture = entry.mValue.mCapture;
+ evt->mFlags.mInSystemGroup = entry.mValue.mMozSystemGroup;
+ evt->mFlags.mAllowUntrustedEvents =
+ entry.mValue.mWantUntrusted.WasPassed()
+ ? entry.mValue.mWantUntrusted.Value()
+ : false;
+ if (entry.mValue.mPassive.WasPassed()) {
+ evt->mPassive.Construct(entry.mValue.mPassive.Value());
+ }
+ evt->mCreateActor = entry.mValue.mCreateActor;
+ }
+ }
+
+ return proto.forget();
+}
+
+/**
+ * This listener only listens for events for the child side of the protocol.
+ * This will work in both content and parent processes.
+ */
+NS_IMETHODIMP JSWindowActorProtocol::HandleEvent(Event* aEvent) {
+ MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
+
+ // Determine which inner window we're associated with, and get its
+ // WindowGlobalChild actor.
+ EventTarget* target = aEvent->GetOriginalTarget();
+ if (NS_WARN_IF(!target)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsPIDOMWindowInner> inner =
+ do_QueryInterface(target->GetOwnerGlobal());
+ if (NS_WARN_IF(!inner)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ RefPtr<WindowGlobalChild> wgc = inner->GetWindowGlobalChild();
+ if (NS_WARN_IF(!wgc)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (aEvent->ShouldIgnoreChromeEventTargetListener()) {
+ return NS_OK;
+ }
+
+ // Ensure our actor is present.
+ RefPtr<JSActor> actor = wgc->GetExistingActor(mName);
+ if (!actor) {
+ // Check if we're supposed to create the actor when this event is fired.
+ bool createActor = true;
+ nsAutoString typeStr;
+ aEvent->GetType(typeStr);
+ for (auto& event : mChild.mEvents) {
+ if (event.mName == typeStr) {
+ createActor = event.mCreateActor;
+ break;
+ }
+ }
+
+ // If we're supposed to create the actor, call GetActor to cause it to be
+ // created.
+ if (createActor) {
+ AutoJSAPI jsapi;
+ jsapi.Init();
+ actor = wgc->GetActor(jsapi.cx(), mName, IgnoreErrors());
+ }
+ }
+ if (!actor || NS_WARN_IF(!actor->GetWrapperPreserveColor())) {
+ return NS_OK;
+ }
+
+ // Build our event listener & call it.
+ JS::Rooted<JSObject*> global(RootingCx(),
+ JS::GetNonCCWObjectGlobal(actor->GetWrapper()));
+ RefPtr<EventListener> eventListener =
+ new EventListener(actor->GetWrapper(), global, nullptr, nullptr);
+ eventListener->HandleEvent(*aEvent, "JSWindowActorProtocol::HandleEvent");
+ return NS_OK;
+}
+
+NS_IMETHODIMP JSWindowActorProtocol::Observe(nsISupports* aSubject,
+ const char* aTopic,
+ const char16_t* aData) {
+ MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
+
+ nsCOMPtr<nsPIDOMWindowInner> inner = do_QueryInterface(aSubject);
+ RefPtr<WindowGlobalChild> wgc;
+
+ if (!inner) {
+ nsCOMPtr<nsPIDOMWindowOuter> outer = do_QueryInterface(aSubject);
+ if (NS_WARN_IF(!outer)) {
+ nsContentUtils::LogSimpleConsoleError(
+ NS_ConvertUTF8toUTF16(nsPrintfCString(
+ "JSWindowActor %s: expected window subject for topic '%s'.",
+ mName.get(), aTopic)),
+ "JSActor"_ns,
+ /* aFromPrivateWindow */ false,
+ /* aFromChromeContext */ true);
+ return NS_ERROR_FAILURE;
+ }
+ if (NS_WARN_IF(!outer->GetCurrentInnerWindow())) {
+ return NS_ERROR_FAILURE;
+ }
+ wgc = outer->GetCurrentInnerWindow()->GetWindowGlobalChild();
+ } else {
+ wgc = inner->GetWindowGlobalChild();
+ }
+
+ if (NS_WARN_IF(!wgc)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // Ensure our actor is present.
+ AutoJSAPI jsapi;
+ jsapi.Init();
+ RefPtr<JSActor> actor = wgc->GetActor(jsapi.cx(), mName, IgnoreErrors());
+ if (!actor || NS_WARN_IF(!actor->GetWrapperPreserveColor())) {
+ return NS_OK;
+ }
+
+ // Build a observer callback.
+ JS::Rooted<JSObject*> global(jsapi.cx(),
+ JS::GetNonCCWObjectGlobal(actor->GetWrapper()));
+ RefPtr<MozObserverCallback> observerCallback =
+ new MozObserverCallback(actor->GetWrapper(), global, nullptr, nullptr);
+ observerCallback->Observe(aSubject, nsDependentCString(aTopic),
+ aData ? nsDependentString(aData) : VoidString());
+ return NS_OK;
+}
+
+void JSWindowActorProtocol::RegisterListenersFor(EventTarget* aTarget) {
+ EventListenerManager* elm = aTarget->GetOrCreateListenerManager();
+
+ for (auto& event : mChild.mEvents) {
+ elm->AddEventListenerByType(EventListenerHolder(this), event.mName,
+ event.mFlags, event.mPassive);
+ }
+}
+
+void JSWindowActorProtocol::UnregisterListenersFor(EventTarget* aTarget) {
+ EventListenerManager* elm = aTarget->GetOrCreateListenerManager();
+
+ for (auto& event : mChild.mEvents) {
+ elm->RemoveEventListenerByType(EventListenerHolder(this), event.mName,
+ event.mFlags);
+ }
+}
+
+void JSWindowActorProtocol::AddObservers() {
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ for (auto& topic : mChild.mObservers) {
+ // This makes the observer service hold an owning reference to the
+ // JSWindowActorProtocol. The JSWindowActorProtocol objects will be living
+ // for the full lifetime of the content process, thus the extra strong
+ // referencec doesn't have a negative impact.
+ os->AddObserver(this, topic.get(), false);
+ }
+}
+
+void JSWindowActorProtocol::RemoveObservers() {
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ for (auto& topic : mChild.mObservers) {
+ os->RemoveObserver(this, topic.get());
+ }
+}
+
+extensions::MatchPatternSetCore* JSWindowActorProtocol::GetURIMatcher() {
+ // If we've already created the pattern set, return it.
+ if (mURIMatcher || mMatches.IsEmpty()) {
+ return mURIMatcher;
+ }
+
+ nsTArray<RefPtr<extensions::MatchPatternCore>> patterns(mMatches.Length());
+ for (const nsString& pattern : mMatches) {
+ patterns.AppendElement(new extensions::MatchPatternCore(
+ pattern, false, false, IgnoreErrors()));
+ }
+ mURIMatcher = new extensions::MatchPatternSetCore(std::move(patterns));
+ return mURIMatcher;
+}
+
+bool JSWindowActorProtocol::RemoteTypePrefixMatches(
+ const nsDependentCSubstring& aRemoteType) {
+ for (auto& remoteType : mRemoteTypes) {
+ if (StringBeginsWith(aRemoteType, remoteType)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool JSWindowActorProtocol::MessageManagerGroupMatches(
+ BrowsingContext* aBrowsingContext) {
+ BrowsingContext* top = aBrowsingContext->Top();
+ for (auto& group : mMessageManagerGroups) {
+ if (group == top->GetMessageManagerGroup()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool JSWindowActorProtocol::Matches(BrowsingContext* aBrowsingContext,
+ nsIURI* aURI, const nsACString& aRemoteType,
+ ErrorResult& aRv) {
+ MOZ_ASSERT(aBrowsingContext, "DocShell without a BrowsingContext!");
+ MOZ_ASSERT(aURI, "Must have URI!");
+
+ if (!mAllFrames && aBrowsingContext->GetParent()) {
+ aRv.ThrowNotSupportedError(nsPrintfCString(
+ "Window protocol '%s' doesn't match subframes", mName.get()));
+ return false;
+ }
+
+ if (!mIncludeChrome && !aBrowsingContext->IsContent()) {
+ aRv.ThrowNotSupportedError(nsPrintfCString(
+ "Window protocol '%s' doesn't match chrome browsing contexts",
+ mName.get()));
+ return false;
+ }
+
+ if (!mRemoteTypes.IsEmpty() &&
+ !RemoteTypePrefixMatches(RemoteTypePrefix(aRemoteType))) {
+ aRv.ThrowNotSupportedError(
+ nsPrintfCString("Window protocol '%s' doesn't match remote type '%s'",
+ mName.get(), PromiseFlatCString(aRemoteType).get()));
+ return false;
+ }
+
+ if (!mMessageManagerGroups.IsEmpty() &&
+ !MessageManagerGroupMatches(aBrowsingContext)) {
+ aRv.ThrowNotSupportedError(nsPrintfCString(
+ "Window protocol '%s' doesn't match message manager group",
+ mName.get()));
+ return false;
+ }
+
+ if (extensions::MatchPatternSetCore* uriMatcher = GetURIMatcher()) {
+ if (!uriMatcher->Matches(aURI)) {
+ aRv.ThrowNotSupportedError(nsPrintfCString(
+ "Window protocol '%s' doesn't match uri %s", mName.get(),
+ nsContentUtils::TruncatedURLForDisplay(aURI).get()));
+ return false;
+ }
+ }
+
+ return true;
+}
+
+} // namespace mozilla::dom
diff --git a/dom/ipc/jsactor/JSWindowActorProtocol.h b/dom/ipc/jsactor/JSWindowActorProtocol.h
new file mode 100644
index 0000000000..b0128d9307
--- /dev/null
+++ b/dom/ipc/jsactor/JSWindowActorProtocol.h
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=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/. */
+
+#ifndef mozilla_dom_JSWindowActorProtocol_h
+#define mozilla_dom_JSWindowActorProtocol_h
+
+#include "mozilla/dom/BrowsingContext.h"
+#include "mozilla/dom/JSActorService.h"
+#include "mozilla/extensions/MatchPattern.h"
+#include "nsIURI.h"
+#include "nsString.h"
+#include "nsTArray.h"
+#include "nsIObserver.h"
+#include "nsIDOMEventListener.h"
+
+namespace mozilla {
+class ErrorResult;
+
+namespace dom {
+
+struct WindowActorOptions;
+class JSWindowActorInfo;
+class EventTarget;
+class JSActorProtocolUtils;
+
+/**
+ * Object corresponding to a single window actor protocol. This object acts as
+ * an Event listener for the actor which is called for events which would
+ * trigger actor creation.
+ *
+ * This object also can act as a carrier for methods and other state related to
+ * a single protocol managed by the JSActorService.
+ */
+class JSWindowActorProtocol final : public JSActorProtocol,
+ public nsIObserver,
+ public nsIDOMEventListener {
+ public:
+ NS_DECL_NSIOBSERVER
+ NS_DECL_NSIDOMEVENTLISTENER
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(JSWindowActorProtocol, nsIObserver)
+
+ static already_AddRefed<JSWindowActorProtocol> FromIPC(
+ const JSWindowActorInfo& aInfo);
+ JSWindowActorInfo ToIPC();
+
+ static already_AddRefed<JSWindowActorProtocol> FromWebIDLOptions(
+ const nsACString& aName, const WindowActorOptions& aOptions,
+ ErrorResult& aRv);
+
+ struct ParentSide : public Sided {};
+
+ struct EventDecl {
+ nsString mName;
+ EventListenerFlags mFlags;
+ Optional<bool> mPassive;
+ bool mCreateActor = true;
+ };
+
+ struct ChildSide : public Sided {
+ nsTArray<EventDecl> mEvents;
+ nsTArray<nsCString> mObservers;
+ };
+
+ const ParentSide& Parent() const override { return mParent; }
+ const ChildSide& Child() const override { return mChild; }
+
+ void RegisterListenersFor(EventTarget* aTarget);
+ void UnregisterListenersFor(EventTarget* aTarget);
+ void AddObservers();
+ void RemoveObservers();
+ bool Matches(BrowsingContext* aBrowsingContext, nsIURI* aURI,
+ const nsACString& aRemoteType, ErrorResult& aRv);
+
+ private:
+ explicit JSWindowActorProtocol(const nsACString& aName) : mName(aName) {}
+ extensions::MatchPatternSetCore* GetURIMatcher();
+ bool RemoteTypePrefixMatches(const nsDependentCSubstring& aRemoteType);
+ bool MessageManagerGroupMatches(BrowsingContext* aBrowsingContext);
+ ~JSWindowActorProtocol() = default;
+
+ nsCString mName;
+ bool mAllFrames = false;
+ bool mIncludeChrome = false;
+ nsTArray<nsString> mMatches;
+ nsTArray<nsCString> mRemoteTypes;
+ nsTArray<nsString> mMessageManagerGroups;
+
+ friend class JSActorProtocolUtils;
+
+ ParentSide mParent;
+ ChildSide mChild;
+
+ RefPtr<extensions::MatchPatternSetCore> mURIMatcher;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_JSWindowActorProtocol_h
diff --git a/dom/ipc/jsactor/moz.build b/dom/ipc/jsactor/moz.build
new file mode 100644
index 0000000000..6bc66df923
--- /dev/null
+++ b/dom/ipc/jsactor/moz.build
@@ -0,0 +1,42 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+EXPORTS.mozilla.dom += [
+ "JSActor.h",
+ "JSActorManager.h",
+ "JSActorService.h",
+ "JSProcessActorChild.h",
+ "JSProcessActorParent.h",
+ "JSProcessActorProtocol.h",
+ "JSWindowActorChild.h",
+ "JSWindowActorParent.h",
+ "JSWindowActorProtocol.h",
+]
+
+EXPORTS += [
+ "nsQueryActor.h",
+]
+
+UNIFIED_SOURCES += [
+ "JSActor.cpp",
+ "JSActorManager.cpp",
+ "JSActorService.cpp",
+ "JSProcessActorChild.cpp",
+ "JSProcessActorParent.cpp",
+ "JSProcessActorProtocol.cpp",
+ "JSWindowActorChild.cpp",
+ "JSWindowActorParent.cpp",
+ "JSWindowActorProtocol.cpp",
+]
+
+LOCAL_INCLUDES += [
+ "/js/xpconnect/loader",
+ "/js/xpconnect/src",
+]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+FINAL_LIBRARY = "xul"
diff --git a/dom/ipc/jsactor/nsQueryActor.h b/dom/ipc/jsactor/nsQueryActor.h
new file mode 100644
index 0000000000..cbbda7e473
--- /dev/null
+++ b/dom/ipc/jsactor/nsQueryActor.h
@@ -0,0 +1,90 @@
+/* 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/. */
+
+#ifndef nsQueryActor_h
+#define nsQueryActor_h
+
+#include <type_traits>
+
+#include "nsCOMPtr.h"
+#include "nsPIDOMWindow.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/dom/Document.h"
+#include "mozilla/dom/JSActor.h"
+#include "mozilla/dom/JSActorManager.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/dom/WindowGlobalChild.h"
+
+class nsIDOMProcessChild;
+class nsIDOMProcessParent;
+
+// This type is used to get an XPCOM interface implemented by a JSActor from its
+// native manager.
+class MOZ_STACK_CLASS nsQueryJSActor final : public nsCOMPtr_helper {
+ public:
+ nsQueryJSActor(const nsLiteralCString aActorName,
+ mozilla::dom::JSActorManager* aManager)
+ : mActorName(aActorName), mManager(aManager) {}
+
+ nsresult NS_FASTCALL operator()(const nsIID& aIID,
+ void** aResult) const override {
+ if (!mManager) {
+ return NS_ERROR_NO_INTERFACE;
+ }
+
+ mozilla::dom::AutoJSAPI jsapi;
+ jsapi.Init();
+
+ RefPtr<mozilla::dom::JSActor> actor =
+ mManager->GetActor(jsapi.cx(), mActorName, mozilla::IgnoreErrors());
+ if (!actor) {
+ return NS_ERROR_NO_INTERFACE;
+ }
+
+ return actor->QueryInterfaceActor(aIID, aResult);
+ }
+
+ private:
+ const nsLiteralCString mActorName;
+ mozilla::dom::JSActorManager* mManager;
+};
+
+// Request an XPCOM interface from a JSActor managed by `aManager`.
+//
+// These APIs will work with both JSWindowActors and JSProcessActors.
+template <size_t length>
+inline nsQueryJSActor do_QueryActor(const char (&aActorName)[length],
+ mozilla::dom::JSActorManager* aManager) {
+ return nsQueryJSActor(nsLiteralCString(aActorName), aManager);
+}
+
+// Overload for directly querying a JSWindowActorChild from an inner window.
+template <size_t length>
+inline nsQueryJSActor do_QueryActor(const char (&aActorName)[length],
+ nsPIDOMWindowInner* aWindow) {
+ return nsQueryJSActor(nsLiteralCString(aActorName),
+ aWindow ? aWindow->GetWindowGlobalChild() : nullptr);
+}
+
+// Overload for directly querying a JSWindowActorChild from a document.
+template <size_t length>
+inline nsQueryJSActor do_QueryActor(const char (&aActorName)[length],
+ mozilla::dom::Document* aDoc) {
+ return nsQueryJSActor(nsLiteralCString(aActorName),
+ aDoc ? aDoc->GetWindowGlobalChild() : nullptr);
+}
+
+// Overload for directly querying from a nsIDOMProcess{Parent,Child} without
+// confusing overload selection for types inheriting from both
+// nsIDOMProcess{Parent,Child} and JSActorManager.
+template <size_t length, typename T,
+ typename = std::enable_if_t<std::is_same_v<T, nsIDOMProcessParent> ||
+ std::is_same_v<T, nsIDOMProcessChild>>>
+inline nsQueryJSActor do_QueryActor(const char (&aActorName)[length],
+ T* aManager) {
+ return nsQueryJSActor(nsLiteralCString(aActorName),
+ aManager ? aManager->AsJSActorManager() : nullptr);
+}
+
+#endif // defined nsQueryActor_h
diff --git a/dom/ipc/moz.build b/dom/ipc/moz.build
new file mode 100644
index 0000000000..68f82f5a38
--- /dev/null
+++ b/dom/ipc/moz.build
@@ -0,0 +1,276 @@
+# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# 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/.
+
+with Files("**"):
+ BUG_COMPONENT = ("Core", "DOM: Content Processes")
+
+DIRS += ["jsactor"]
+
+TEST_DIRS += ["gtest"]
+
+XPIDL_SOURCES += [
+ "nsIDOMProcessChild.idl",
+ "nsIDOMProcessParent.idl",
+ "nsIHangReport.idl",
+ "nsILoginDetectionService.idl",
+]
+
+XPIDL_MODULE = "dom"
+
+XPCOM_MANIFESTS += [
+ "components.conf",
+]
+
+EXTRA_JS_MODULES += [
+ "ManifestMessagesChild.sys.mjs",
+]
+
+EXPORTS.mozilla.dom.ipc += [
+ "IdType.h",
+ "MemMapSnapshot.h",
+ "SharedMap.h",
+ "SharedMapChangeEvent.h",
+ "SharedStringMap.h",
+ "StringTable.h",
+ "StructuredCloneData.h",
+]
+
+EXPORTS.mozilla.dom += [
+ "BrowserBridgeChild.h",
+ "BrowserBridgeHost.h",
+ "BrowserBridgeParent.h",
+ "BrowserChild.h",
+ "BrowserHost.h",
+ "BrowserParent.h",
+ "ClonedErrorHolder.h",
+ "CoalescedInputData.h",
+ "CoalescedMouseData.h",
+ "CoalescedTouchData.h",
+ "CoalescedWheelData.h",
+ "ContentChild.h",
+ "ContentParent.h",
+ "ContentProcess.h",
+ "ContentProcessManager.h",
+ "CSPMessageUtils.h",
+ "DocShellMessageUtils.h",
+ "EffectsInfo.h",
+ "FilePickerMessageUtils.h",
+ "FilePickerParent.h",
+ "InProcessChild.h",
+ "InProcessParent.h",
+ "JSOracleChild.h",
+ "JSOracleParent.h",
+ "JSValidatorChild.h",
+ "JSValidatorParent.h",
+ "JSValidatorUtils.h",
+ "LoginDetectionService.h",
+ "MaybeDiscarded.h",
+ "MemoryReportRequest.h",
+ "NativeThreadId.h",
+ "PageLoadEventUtils.h",
+ "PermissionMessageUtils.h",
+ "ProcessActor.h",
+ "ProcessIsolation.h",
+ "PropertyBagUtils.h",
+ "ReferrerInfoUtils.h",
+ "RefMessageBodyService.h",
+ "RemoteBrowser.h",
+ "RemoteType.h",
+ "RemoteWebProgressRequest.h",
+ "SharedMessageBody.h",
+ "TabContext.h",
+ "TabMessageTypes.h",
+ "TabMessageUtils.h",
+ "URLClassifierChild.h",
+ "URLClassifierParent.h",
+ "UserActivationIPCUtils.h",
+ "VsyncChild.h",
+ "VsyncMainChild.h",
+ "VsyncParent.h",
+ "VsyncWorkerChild.h",
+ "WindowGlobalActor.h",
+ "WindowGlobalChild.h",
+ "WindowGlobalParent.h",
+]
+
+EXPORTS.mozilla += [
+ "PreallocatedProcessManager.h",
+ "ProcessHangMonitor.h",
+ "ProcessHangMonitorIPC.h",
+ "ProcessPriorityManager.h",
+]
+
+UNIFIED_SOURCES += [
+ "BrowserBridgeChild.cpp",
+ "BrowserBridgeHost.cpp",
+ "BrowserBridgeParent.cpp",
+ "BrowserChild.cpp",
+ "BrowserHost.cpp",
+ "BrowserParent.cpp",
+ "ClonedErrorHolder.cpp",
+ "CoalescedInputData.cpp",
+ "CoalescedMouseData.cpp",
+ "CoalescedTouchData.cpp",
+ "CoalescedWheelData.cpp",
+ "ColorPickerParent.cpp",
+ "ContentParent.cpp",
+ "ContentProcess.cpp",
+ "ContentProcessManager.cpp",
+ "CSPMessageUtils.cpp",
+ "DocShellMessageUtils.cpp",
+ "FilePickerParent.cpp",
+ "InProcessImpl.cpp",
+ "JSOracleChild.cpp",
+ "JSOracleParent.cpp",
+ "JSValidatorChild.cpp",
+ "JSValidatorParent.cpp",
+ "JSValidatorUtils.cpp",
+ "LoginDetectionService.cpp",
+ "MemMapSnapshot.cpp",
+ "MemoryReportRequest.cpp",
+ "MMPrinter.cpp",
+ "PermissionMessageUtils.cpp",
+ "PreallocatedProcessManager.cpp",
+ "ProcessActor.cpp",
+ "ProcessIsolation.cpp",
+ "ProcessPriorityManager.cpp",
+ "PropertyBagUtils.cpp",
+ "ReferrerInfoUtils.cpp",
+ "RefMessageBodyService.cpp",
+ "RemoteBrowser.cpp",
+ "RemoteWebProgressRequest.cpp",
+ "SharedMap.cpp",
+ "SharedMessageBody.cpp",
+ "SharedStringMap.cpp",
+ "StructuredCloneData.cpp",
+ "TabContext.cpp",
+ "URLClassifierParent.cpp",
+ "WindowGlobalActor.cpp",
+ "WindowGlobalChild.cpp",
+ "WindowGlobalParent.cpp",
+]
+
+# ContentChild.cpp cannot be compiled in unified mode on linux due to Time conflict
+SOURCES += [
+ "ContentChild.cpp",
+ "ProcessHangMonitor.cpp",
+ "VsyncMainChild.cpp",
+ "VsyncParent.cpp",
+ "VsyncWorkerChild.cpp",
+]
+
+PREPROCESSED_IPDL_SOURCES += [
+ "PBrowser.ipdl",
+ "PBrowserBridge.ipdl",
+ "PContent.ipdl",
+]
+
+IPDL_SOURCES += [
+ "CustomElementTypes.ipdlh",
+ "DOMTypes.ipdlh",
+ "IPCTransferable.ipdlh",
+ "MemoryReportTypes.ipdlh",
+ "PColorPicker.ipdl",
+ "PContentPermission.ipdlh",
+ "PContentPermissionRequest.ipdl",
+ "PCycleCollectWithLogs.ipdl",
+ "PFilePicker.ipdl",
+ "PInProcess.ipdl",
+ "PJSOracle.ipdl",
+ "PJSValidator.ipdl",
+ "PProcessHangMonitor.ipdl",
+ "PrefsTypes.ipdlh",
+ "PTabContext.ipdlh",
+ "PURLClassifier.ipdl",
+ "PURLClassifierInfo.ipdlh",
+ "PURLClassifierLocal.ipdl",
+ "PVsync.ipdl",
+ "PWindowGlobal.ipdl",
+ "ServiceWorkerConfiguration.ipdlh",
+ "WindowGlobalTypes.ipdlh",
+]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+FINAL_LIBRARY = "xul"
+
+if CONFIG["MOZ_SANDBOX"] and (CONFIG["OS_TARGET"] in ["Darwin", "Linux"]):
+ USE_LIBS += [
+ "mozsandbox",
+ ]
+
+LOCAL_INCLUDES += [
+ "/caps",
+ "/chrome",
+ "/docshell/base",
+ "/dom/base",
+ "/dom/bindings",
+ "/dom/events",
+ "/dom/filesystem",
+ "/dom/geolocation",
+ "/dom/media/webrtc",
+ "/dom/media/webspeech/synth/ipc",
+ "/dom/security",
+ "/dom/storage",
+ "/extensions/spellcheck/src",
+ "/gfx/2d",
+ "/hal/sandbox",
+ "/js/xpconnect/loader",
+ "/js/xpconnect/src",
+ "/layout/base",
+ "/media/webrtc",
+ "/netwerk/base",
+ "/netwerk/dns",
+ "/netwerk/protocol/http",
+ "/toolkit/components/printingui/ipc",
+ "/toolkit/crashreporter",
+ "/toolkit/xre",
+ "/uriloader/exthandler",
+ "/widget",
+ "/xpcom/base",
+ "/xpcom/threads",
+]
+
+if CONFIG["MOZ_SANDBOX"] and CONFIG["OS_ARCH"] == "WINNT":
+ LOCAL_INCLUDES += [
+ "/security/sandbox/chromium",
+ "/security/sandbox/chromium-shim",
+ ]
+
+if CONFIG["MOZ_SANDBOX"] and CONFIG["OS_ARCH"] == "OpenBSD":
+ LOCAL_INCLUDES += [
+ "/xpcom/build",
+ ]
+
+if CONFIG["OS_ARCH"] != "WINNT":
+ LOCAL_INCLUDES += [
+ "/modules/libjar",
+ ]
+
+DEFINES["BIN_SUFFIX"] = '"%s"' % CONFIG["BIN_SUFFIX"]
+
+DEFINES["MOZ_APP_NAME"] = '"%s"' % CONFIG["MOZ_APP_NAME"]
+
+if CONFIG["MOZ_WIDGET_TOOLKIT"] == "android":
+ DEFINES["MOZ_ENABLE_FREETYPE"] = True
+
+JAR_MANIFESTS += ["jar.mn"]
+
+BROWSER_CHROME_MANIFESTS += [
+ "tests/browser.toml",
+ "tests/JSProcessActor/browser.toml",
+ "tests/JSWindowActor/browser.toml",
+]
+
+MOCHITEST_CHROME_MANIFESTS += ["tests/chrome.toml"]
+MOCHITEST_MANIFESTS += ["tests/mochitest.toml"]
+XPCSHELL_TESTS_MANIFESTS += ["tests/xpcshell.toml"]
+
+if CONFIG["MOZ_WIDGET_TOOLKIT"] == "gtk":
+ CXXFLAGS += CONFIG["MOZ_GTK3_CFLAGS"]
+
+# Add libFuzzer configuration directives
+include("/tools/fuzzing/libfuzzer-config.mozbuild")
diff --git a/dom/ipc/nsIDOMProcessChild.idl b/dom/ipc/nsIDOMProcessChild.idl
new file mode 100644
index 0000000000..ff6875f395
--- /dev/null
+++ b/dom/ipc/nsIDOMProcessChild.idl
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+%{C++
+namespace mozilla {
+namespace dom {
+class ContentChild;
+class JSActorManager;
+} // namespace dom
+} // namespace mozilla
+%}
+[ptr] native ContentChildPtr(mozilla::dom::ContentChild);
+[ptr] native JSActorManagerPtr(mozilla::dom::JSActorManager);
+
+webidl JSProcessActorChild;
+
+/**
+ * Child actor interface for a process which can host DOM content.
+ *
+ * Implemented by either `InProcessChild` for the parent process, or
+ * `ContentChild` for a content process.
+ */
+[scriptable, builtinclass, uuid(b0c6e5f3-02f1-4f11-a0af-336fc231f3bf)]
+interface nsIDOMProcessChild: nsISupports {
+ %{C++
+ /**
+ * Get the nsIDOMProcessChild singleton for this content process. This will
+ * either be an InProcessChild in the parent process, or ContentChild in the
+ * child process.
+ *
+ * Implemented in ContentChild.cpp
+ */
+ static nsIDOMProcessChild* GetSingleton();
+ %}
+
+ /**
+ * Internal child process ID. `0` is reserved for the parent process.
+ */
+ [infallible] readonly attribute unsigned long long childID;
+
+ /**
+ * Lookup a JSProcessActorChild managed by this interface by name.
+ */
+ [implicit_jscontext] JSProcessActorChild getActor(in ACString name);
+ JSProcessActorChild getExistingActor(in ACString name);
+
+ /** Can the actor still send messages? */
+ [infallible] readonly attribute boolean canSend;
+
+ [notxpcom, nostdcall] ContentChildPtr AsContentChild();
+
+ /** Cast this nsIDOMProcessChild to a JSActorManager */
+ [notxpcom, nostdcall] JSActorManagerPtr AsJSActorManager();
+};
diff --git a/dom/ipc/nsIDOMProcessParent.idl b/dom/ipc/nsIDOMProcessParent.idl
new file mode 100644
index 0000000000..daa11deddf
--- /dev/null
+++ b/dom/ipc/nsIDOMProcessParent.idl
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+%{C++
+namespace mozilla {
+namespace dom {
+class ContentParent;
+class JSActorManager;
+} // namespace dom
+} // namespace mozilla
+%}
+[ptr] native ContentParentPtr(mozilla::dom::ContentParent);
+[ptr] native JSActorManagerPtr(mozilla::dom::JSActorManager);
+
+webidl JSProcessActorParent;
+
+/**
+ * Parent actor interface for a process which can host DOM content.
+ *
+ * Implemented by either `InProcessParent` for the parent process, or
+ * `ContentParent` for a content process.
+ */
+[scriptable, builtinclass, uuid(81fc08b9-c901-471f-ab0d-876362eba770)]
+interface nsIDOMProcessParent: nsISupports {
+ /**
+ * Internal child process ID. `0` is reserved for the parent process.
+ */
+ [infallible] readonly attribute unsigned long long childID;
+
+ /**
+ * OS ID of the process.
+ */
+ [infallible] readonly attribute long osPid;
+
+ /**
+ * Lookup a JSProcessActorParent managed by this interface by name.
+ */
+ [implicit_jscontext] JSProcessActorParent getActor(in ACString name);
+ JSProcessActorParent getExistingActor(in ACString name);
+
+ /** Can the actor still send messages? */
+ [infallible] readonly attribute boolean canSend;
+
+ [notxpcom, nostdcall] ContentParentPtr AsContentParent();
+
+ /** Cast this nsIDOMProcessParent to a JSActorManager */
+ [notxpcom, nostdcall] JSActorManagerPtr AsJSActorManager();
+
+ /**
+ * Remote type of the process.
+ */
+ readonly attribute ACString remoteType;
+};
diff --git a/dom/ipc/nsIHangReport.idl b/dom/ipc/nsIHangReport.idl
new file mode 100644
index 0000000000..ff01d7f51a
--- /dev/null
+++ b/dom/ipc/nsIHangReport.idl
@@ -0,0 +1,51 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+webidl FrameLoader;
+webidl Element;
+
+/**
+ * When a content process hangs, Gecko notifies "process-hang-report" observers
+ * and passes an nsIHangReport for the subject parameter. There is at most one
+ * nsIHangReport associated with a given content process. As long as the content
+ * process stays stuck, the "process-hang-report" observer will continue to be
+ * notified at regular intervals (approximately once per second). The content
+ * process will continue to run uninhibitedly during this time.
+ */
+
+[scriptable, uuid(5fcffbb9-be62-49b1-b8a1-36e820787a74)]
+interface nsIHangReport : nsISupports
+{
+ readonly attribute Element scriptBrowser;
+ readonly attribute ACString scriptFileName;
+ // Duration of the hang so far.
+ readonly attribute double hangDuration;
+ readonly attribute AString addonId;
+
+ // The child id of the process in which the hang happened.
+ readonly attribute unsigned long long childID;
+
+ // Called by front end code when user ignores or cancels
+ // the notification.
+ void userCanceled();
+
+ // Terminate the slow script if it is still running.
+ void terminateScript();
+
+ // Ask the content process to start up the slow script debugger.
+ void beginStartingDebugger();
+
+ // Inform the content process that the slow script debugger has finished
+ // spinning up. The content process will run a nested event loop until this
+ // method is called.
+ void endStartingDebugger();
+
+ // Inquire whether the report is for a content process loaded by the given
+ // frameloader, or any descendents in its BrowsingContext tree.
+ bool isReportForBrowserOrChildren(in FrameLoader aFrameLoader);
+};
diff --git a/dom/ipc/nsILoginDetectionService.idl b/dom/ipc/nsILoginDetectionService.idl
new file mode 100644
index 0000000000..a0b1ae90a7
--- /dev/null
+++ b/dom/ipc/nsILoginDetectionService.idl
@@ -0,0 +1,22 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIURI;
+
+[scriptable, uuid(4c3c9a82-722a-4b0b-9c7d-36ef90135537)]
+interface nsILoginDetectionService : nsISupports
+{
+ /**
+ * called to initialize the login detection service.
+ */
+ void init();
+
+ /**
+ * Returns true if we have loaded logins from the password manager.
+ * This is now used by testcase only.
+ */
+ bool isLoginsLoaded();
+};
diff --git a/dom/ipc/tests/JSProcessActor/browser.toml b/dom/ipc/tests/JSProcessActor/browser.toml
new file mode 100644
index 0000000000..807b54dd81
--- /dev/null
+++ b/dom/ipc/tests/JSProcessActor/browser.toml
@@ -0,0 +1,18 @@
+[DEFAULT]
+support-files = ["head.js"]
+
+["browser_devtools_loader.js"]
+
+["browser_getActor.js"]
+
+["browser_getActor_filter.js"]
+
+["browser_observer_notification.js"]
+
+["browser_registerProcessActor.js"]
+
+["browser_sendAsyncMessage.js"]
+
+["browser_sendQuery.js"]
+
+["browser_uri_combination.js"]
diff --git a/dom/ipc/tests/JSProcessActor/browser_devtools_loader.js b/dom/ipc/tests/JSProcessActor/browser_devtools_loader.js
new file mode 100644
index 0000000000..0d2699c5a9
--- /dev/null
+++ b/dom/ipc/tests/JSProcessActor/browser_devtools_loader.js
@@ -0,0 +1,57 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+declTest("getActor in the regular shared loader", {
+ loadInDevToolsLoader: false,
+
+ async test(browser) {
+ let parent = browser.browsingContext.currentWindowGlobal.domProcess;
+ let parentActor = parent.getActor("TestProcessActor");
+ ok(parentActor, "JSProcessActorParent should have value.");
+ is(
+ Cu.getRealmLocation(Cu.getGlobalForObject(parentActor)),
+ "shared JSM global",
+ "The JSActor module in the parent process should be loaded in the shared global"
+ );
+
+ await SpecialPowers.spawn(browser, [], async function () {
+ let child = ChromeUtils.domProcessChild;
+ ok(child, "DOMProcessChild should have value.");
+ let childActor = child.getActor("TestProcessActor");
+ ok(childActor, "JSProcessActorChild should have value.");
+ is(
+ Cu.getRealmLocation(Cu.getGlobalForObject(childActor)),
+ "shared JSM global",
+ "The JSActor module in the child process should be loaded in the shared global"
+ );
+ });
+ },
+});
+
+declTest("getActor in the distinct DevTools loader", {
+ loadInDevToolsLoader: true,
+
+ async test(browser) {
+ let parent = browser.browsingContext.currentWindowGlobal.domProcess;
+ let parentActor = parent.getActor("TestProcessActor");
+ ok(parentActor, "JSProcessActorParent should have value.");
+ is(
+ Cu.getRealmLocation(Cu.getGlobalForObject(parentActor)),
+ "DevTools global",
+ "The JSActor module in the parent process should be loaded in the distinct DevTools global"
+ );
+
+ await SpecialPowers.spawn(browser, [], async function () {
+ let child = ChromeUtils.domProcessChild;
+ ok(child, "DOMProcessChild should have value.");
+ let childActor = child.getActor("TestProcessActor");
+ ok(childActor, "JSProcessActorChild should have value.");
+ is(
+ Cu.getRealmLocation(Cu.getGlobalForObject(childActor)),
+ "DevTools global",
+ "The JSActor module in the child process should be loaded in the distinct DevTools global"
+ );
+ });
+ },
+});
diff --git a/dom/ipc/tests/JSProcessActor/browser_getActor.js b/dom/ipc/tests/JSProcessActor/browser_getActor.js
new file mode 100644
index 0000000000..e111f1a274
--- /dev/null
+++ b/dom/ipc/tests/JSProcessActor/browser_getActor.js
@@ -0,0 +1,47 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+declTest("getActor on both sides", {
+ async test(browser) {
+ let parent = browser.browsingContext.currentWindowGlobal.domProcess;
+ ok(parent, "WindowGlobalParent should have value.");
+ let actorParent = parent.getActor("TestProcessActor");
+ is(
+ actorParent.show(),
+ "TestProcessActorParent",
+ "actor `show` should have value."
+ );
+ is(
+ actorParent.manager,
+ parent,
+ "manager should match WindowGlobalParent.domProcess"
+ );
+
+ ok(
+ actorParent.sawActorCreated,
+ "Checking that we can observe parent creation"
+ );
+
+ await SpecialPowers.spawn(browser, [], async function () {
+ let child = ChromeUtils.domProcessChild;
+ ok(child, "WindowGlobalChild should have value.");
+ let actorChild = child.getActor("TestProcessActor");
+ is(
+ actorChild.show(),
+ "TestProcessActorChild",
+ "actor show should have vaule."
+ );
+ is(
+ actorChild.manager,
+ child,
+ "manager should match ChromeUtils.domProcessChild."
+ );
+
+ ok(
+ actorChild.sawActorCreated,
+ "Checking that we can observe child creation"
+ );
+ });
+ },
+});
diff --git a/dom/ipc/tests/JSProcessActor/browser_getActor_filter.js b/dom/ipc/tests/JSProcessActor/browser_getActor_filter.js
new file mode 100644
index 0000000000..f79340fd89
--- /dev/null
+++ b/dom/ipc/tests/JSProcessActor/browser_getActor_filter.js
@@ -0,0 +1,80 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+declTest("getActor with remoteType match", {
+ remoteTypes: ["web"],
+
+ async test(browser) {
+ let parent = browser.browsingContext.currentWindowGlobal.domProcess;
+ ok(
+ parent.getActor("TestProcessActor"),
+ "JSProcessActorParent should have value."
+ );
+
+ await SpecialPowers.spawn(browser, [], async function () {
+ let child = ChromeUtils.domProcessChild;
+ ok(child, "DOMProcessChild should have value.");
+ ok(
+ child.getActor("TestProcessActor"),
+ "JSProcessActorChild should have value."
+ );
+ });
+ },
+});
+
+declTest("getActor with remoteType mismatch", {
+ remoteTypes: ["privilegedabout"],
+ url: TEST_URL,
+
+ async test(browser) {
+ let parent = browser.browsingContext.currentWindowGlobal.domProcess;
+ Assert.throws(
+ () => parent.getActor("TestProcessActor"),
+ /NotSupportedError/,
+ "Should throw if its remoteTypes don't match."
+ );
+
+ await SpecialPowers.spawn(browser, [], async function () {
+ let child = ChromeUtils.domProcessChild;
+ ok(child, "DOMProcessChild should have value.");
+ Assert.throws(
+ () => child.getActor("TestProcessActor"),
+ /NotSupportedError/,
+ "Should throw if its remoteTypes don't match."
+ );
+ });
+ },
+});
+
+declTest("getActor without includeParent", {
+ includeParent: false,
+
+ async test(_browser, win) {
+ let parent = win.docShell.browsingContext.currentWindowGlobal.domProcess;
+ SimpleTest.doesThrow(
+ () => parent.getActor("TestProcessActor"),
+ "Should throw if includeParent is false."
+ );
+
+ let child = ChromeUtils.domProcessChild;
+ SimpleTest.doesThrow(
+ () => child.getActor("TestProcessActor"),
+ "Should throw if includeParent is false."
+ );
+ },
+});
+
+declTest("getActor with includeParent", {
+ includeParent: true,
+
+ async test(_browser, win) {
+ let parent = win.docShell.browsingContext.currentWindowGlobal.domProcess;
+ let actorParent = parent.getActor("TestProcessActor");
+ ok(actorParent, "JSProcessActorParent should have value.");
+
+ let child = ChromeUtils.domProcessChild;
+ let actorChild = child.getActor("TestProcessActor");
+ ok(actorChild, "JSProcessActorChild should have value.");
+ },
+});
diff --git a/dom/ipc/tests/JSProcessActor/browser_observer_notification.js b/dom/ipc/tests/JSProcessActor/browser_observer_notification.js
new file mode 100644
index 0000000000..28dfa16481
--- /dev/null
+++ b/dom/ipc/tests/JSProcessActor/browser_observer_notification.js
@@ -0,0 +1,41 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+/* eslint-disable no-unused-vars */
+declTest("test observer triggering actor creation", {
+ async test(browser) {
+ await SpecialPowers.spawn(browser, [], async function () {
+ const TOPIC = "test-js-content-actor-child-observer";
+ Services.obs.notifyObservers(content.window, TOPIC, "dataString");
+
+ let child = ChromeUtils.domProcessChild;
+ let actorChild = child.getActor("TestProcessActor");
+ ok(actorChild, "JSProcessActorChild should have value.");
+ ok(
+ actorChild.lastObserved,
+ "JSProcessActorChild lastObserved should have value."
+ );
+ let { subject, topic, data } = actorChild.lastObserved;
+ is(topic, TOPIC, "Topic matches");
+ is(data, "dataString", "Data matches");
+ });
+ },
+});
+
+declTest("test observers with null data", {
+ async test(browser) {
+ await SpecialPowers.spawn(browser, [], async function () {
+ const TOPIC = "test-js-content-actor-child-observer";
+ Services.obs.notifyObservers(content.window, TOPIC);
+
+ let child = ChromeUtils.domProcessChild;
+ let actorChild = child.getActor("TestProcessActor");
+ ok(actorChild, "JSProcessActorChild should have value.");
+ let { subject, topic, data } = actorChild.lastObserved;
+
+ is(topic, TOPIC, "Topic matches");
+ is(data, null, "Data matches");
+ });
+ },
+});
diff --git a/dom/ipc/tests/JSProcessActor/browser_registerProcessActor.js b/dom/ipc/tests/JSProcessActor/browser_registerProcessActor.js
new file mode 100644
index 0000000000..1fa4e1c17c
--- /dev/null
+++ b/dom/ipc/tests/JSProcessActor/browser_registerProcessActor.js
@@ -0,0 +1,16 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+declTest("double register", {
+ async test(_browser, _window, fileExt) {
+ SimpleTest.doesThrow(
+ () =>
+ ChromeUtils.registerContentActor(
+ "TestProcessActor",
+ processActorOptions[fileExt]
+ ),
+ "Should throw if register has duplicate name."
+ );
+ },
+});
diff --git a/dom/ipc/tests/JSProcessActor/browser_sendAsyncMessage.js b/dom/ipc/tests/JSProcessActor/browser_sendAsyncMessage.js
new file mode 100644
index 0000000000..02ad64ee8b
--- /dev/null
+++ b/dom/ipc/tests/JSProcessActor/browser_sendAsyncMessage.js
@@ -0,0 +1,51 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+declTest("asyncMessage testing", {
+ async test(browser) {
+ let parent = browser.browsingContext.currentWindowGlobal.domProcess;
+ let actorParent = parent.getActor("TestProcessActor");
+ ok(actorParent, "JSProcessActorParent should have value.");
+
+ await ContentTask.spawn(browser, {}, async function () {
+ let child = ChromeUtils.domProcessChild;
+ let actorChild = child.getActor("TestProcessActor");
+ ok(actorChild, "JSProcessActorChild should have value.");
+
+ let promise = new Promise(resolve => {
+ actorChild.sendAsyncMessage("init", {});
+ actorChild.done = data => resolve(data);
+ }).then(data => {
+ ok(data.initial, "Initial should be true.");
+ ok(data.toParent, "ToParent should be true.");
+ ok(data.toChild, "ToChild should be true.");
+ });
+
+ await promise;
+ });
+ },
+});
+
+declTest("asyncMessage without both sides", {
+ async test(browser) {
+ // If we don't create a parent actor, make sure the parent actor
+ // gets created by having sent the message.
+ await ContentTask.spawn(browser, {}, async function () {
+ let child = ChromeUtils.domProcessChild;
+ let actorChild = child.getActor("TestProcessActor");
+ ok(actorChild, "JSProcessActorChild should have value.");
+
+ let promise = new Promise(resolve => {
+ actorChild.sendAsyncMessage("init", {});
+ actorChild.done = data => resolve(data);
+ }).then(data => {
+ ok(data.initial, "Initial should be true.");
+ ok(data.toParent, "ToParent should be true.");
+ ok(data.toChild, "ToChild should be true.");
+ });
+
+ await promise;
+ });
+ },
+});
diff --git a/dom/ipc/tests/JSProcessActor/browser_sendQuery.js b/dom/ipc/tests/JSProcessActor/browser_sendQuery.js
new file mode 100644
index 0000000000..5a9767bf3a
--- /dev/null
+++ b/dom/ipc/tests/JSProcessActor/browser_sendQuery.js
@@ -0,0 +1,99 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+const ERROR_LINE_NUMBERS = {
+ jsm: 31,
+ "sys.mjs": 28,
+};
+const EXCEPTION_LINE_NUMBERS = {
+ jsm: ERROR_LINE_NUMBERS.jsm + 3,
+ "sys.mjs": ERROR_LINE_NUMBERS["sys.mjs"] + 3,
+};
+const ERROR_COLUMN_NUMBERS = {
+ jsm: 31,
+ "sys.mjs": 31,
+};
+const EXCEPTION_COLUMN_NUMBERS = {
+ jsm: 22,
+ "sys.mjs": 22,
+};
+
+function maybeAsyncStack(offset, column) {
+ if (
+ Services.prefs.getBoolPref(
+ "javascript.options.asyncstack_capture_debuggee_only"
+ )
+ ) {
+ return "";
+ }
+
+ let stack = Error().stack.replace(/^.*?\n/, "");
+ return (
+ "JSActor query*" +
+ stack.replace(
+ /^([^\n]+?):(\d+):\d+/,
+ (m0, m1, m2) => `${m1}:${+m2 + offset}:${column}`
+ )
+ );
+}
+
+declTest("sendQuery Error", {
+ async test(browser, _window, fileExt) {
+ let parent = browser.browsingContext.currentWindowGlobal.domProcess;
+ let actorParent = parent.getActor("TestProcessActor");
+
+ let asyncStack = maybeAsyncStack(2, 8);
+ let error = await actorParent
+ .sendQuery("error", { message: "foo" })
+ .catch(e => e);
+
+ is(error.message, "foo", "Error should have the correct message");
+ is(error.name, "SyntaxError", "Error should have the correct name");
+ is(
+ error.stack,
+ `receiveMessage@resource://testing-common/TestProcessActorChild.${fileExt}:${ERROR_LINE_NUMBERS[fileExt]}:${ERROR_COLUMN_NUMBERS[fileExt]}\n` +
+ asyncStack,
+ "Error should have the correct stack"
+ );
+ },
+});
+
+declTest("sendQuery Exception", {
+ async test(browser, _window, fileExt) {
+ let parent = browser.browsingContext.currentWindowGlobal.domProcess;
+ let actorParent = parent.getActor("TestProcessActor");
+
+ let asyncStack = maybeAsyncStack(2, 8);
+ let error = await actorParent
+ .sendQuery("exception", {
+ message: "foo",
+ result: Cr.NS_ERROR_INVALID_ARG,
+ })
+ .catch(e => e);
+
+ is(error.message, "foo", "Error should have the correct message");
+ is(
+ error.result,
+ Cr.NS_ERROR_INVALID_ARG,
+ "Error should have the correct result code"
+ );
+ is(
+ error.stack,
+ `receiveMessage@resource://testing-common/TestProcessActorChild.${fileExt}:${EXCEPTION_LINE_NUMBERS[fileExt]}:${EXCEPTION_COLUMN_NUMBERS[fileExt]}\n` +
+ asyncStack,
+ "Error should have the correct stack"
+ );
+ },
+});
+
+declTest("sendQuery testing", {
+ async test(browser) {
+ let parent = browser.browsingContext.currentWindowGlobal.domProcess;
+ let actorParent = parent.getActor("TestProcessActor");
+ ok(actorParent, "JSWindowActorParent should have value.");
+
+ let { result } = await actorParent.sendQuery("asyncAdd", { a: 10, b: 20 });
+ is(result, 30);
+ },
+});
diff --git a/dom/ipc/tests/JSProcessActor/browser_uri_combination.js b/dom/ipc/tests/JSProcessActor/browser_uri_combination.js
new file mode 100644
index 0000000000..33394cf54a
--- /dev/null
+++ b/dom/ipc/tests/JSProcessActor/browser_uri_combination.js
@@ -0,0 +1,81 @@
+add_task(function specify_both() {
+ // Specifying both should throw.
+
+ SimpleTest.doesThrow(() => {
+ ChromeUtils.registerProcessActor("TestProcessActor", {
+ parent: {
+ moduleURI: "resource://testing-common/TestProcessActorParent.jsm",
+ },
+ child: {
+ moduleURI: "resource://testing-common/TestProcessActorChild.jsm",
+ esModuleURI: "resource://testing-common/TestProcessActorChild.sys.mjs",
+ },
+ });
+ }, "Should throw if both moduleURI and esModuleURI are specified.");
+
+ SimpleTest.doesThrow(() => {
+ ChromeUtils.registerProcessActor("TestProcessActor", {
+ parent: {
+ esModuleURI: "resource://testing-common/TestProcessActorParent.sys.mjs",
+ },
+ child: {
+ moduleURI: "resource://testing-common/TestProcessActorChild.jsm",
+ esModuleURI: "resource://testing-common/TestProcessActorChild.sys.mjs",
+ },
+ });
+ }, "Should throw if both moduleURI and esModuleURI are specified.");
+
+ SimpleTest.doesThrow(() => {
+ ChromeUtils.registerProcessActor("TestProcessActor", {
+ parent: {
+ moduleURI: "resource://testing-common/TestProcessActorParent.jsm",
+ esModuleURI: "resource://testing-common/TestProcessActorParent.sys.mjs",
+ },
+ child: {
+ moduleURI: "resource://testing-common/TestProcessActorChild.jsm",
+ },
+ });
+ }, "Should throw if both moduleURI and esModuleURI are specified.");
+
+ SimpleTest.doesThrow(() => {
+ ChromeUtils.registerProcessActor("TestProcessActor", {
+ parent: {
+ moduleURI: "resource://testing-common/TestProcessActorParent.jsm",
+ esModuleURI: "resource://testing-common/TestProcessActorParent.sys.mjs",
+ },
+ child: {
+ esModuleURI: "resource://testing-common/TestProcessActorChild.sys.mjs",
+ },
+ });
+ }, "Should throw if both moduleURI and esModuleURI are specified.");
+});
+
+add_task(function specify_mixed() {
+ // Mixing JSM and ESM should work.
+
+ try {
+ ChromeUtils.registerProcessActor("TestProcessActor", {
+ parent: {
+ moduleURI: "resource://testing-common/TestProcessActorParent.jsm",
+ },
+ child: {
+ esModuleURI: "resource://testing-common/TestProcessActorChild.sys.mjs",
+ },
+ });
+ } finally {
+ ChromeUtils.unregisterProcessActor("TestProcessActor");
+ }
+
+ try {
+ ChromeUtils.registerProcessActor("TestProcessActor", {
+ parent: {
+ esModuleURI: "resource://testing-common/TestProcessActorParent.sys.mjs",
+ },
+ child: {
+ moduleURI: "resource://testing-common/TestProcessActorChild.jsm",
+ },
+ });
+ } finally {
+ ChromeUtils.unregisterProcessActor("TestProcessActor");
+ }
+});
diff --git a/dom/ipc/tests/JSProcessActor/head.js b/dom/ipc/tests/JSProcessActor/head.js
new file mode 100644
index 0000000000..cde193ca2e
--- /dev/null
+++ b/dom/ipc/tests/JSProcessActor/head.js
@@ -0,0 +1,88 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Provide infrastructure for JSProcessActor tests.
+ */
+
+const URL = "about:blank";
+const TEST_URL = "http://test2.example.org/";
+let processActorOptions = {
+ jsm: {
+ parent: {
+ moduleURI: "resource://testing-common/TestProcessActorParent.jsm",
+ },
+ child: {
+ moduleURI: "resource://testing-common/TestProcessActorChild.jsm",
+ observers: ["test-js-content-actor-child-observer"],
+ },
+ },
+ "sys.mjs": {
+ parent: {
+ esModuleURI: "resource://testing-common/TestProcessActorParent.sys.mjs",
+ },
+ child: {
+ esModuleURI: "resource://testing-common/TestProcessActorChild.sys.mjs",
+ observers: ["test-js-content-actor-child-observer"],
+ },
+ },
+};
+
+function promiseNotification(aNotification) {
+ let notificationResolve;
+ let notificationObserver = function observer() {
+ notificationResolve();
+ Services.obs.removeObserver(notificationObserver, aNotification);
+ };
+ return new Promise(resolve => {
+ notificationResolve = resolve;
+ Services.obs.addObserver(notificationObserver, aNotification);
+ });
+}
+
+function declTest(name, cfg) {
+ declTestWithOptions(name, cfg, "jsm");
+ declTestWithOptions(name, cfg, "sys.mjs");
+}
+
+function declTestWithOptions(name, cfg, fileExt) {
+ let {
+ url = "about:blank",
+ includeParent = false,
+ remoteTypes,
+ loadInDevToolsLoader = false,
+ test,
+ } = cfg;
+
+ // Build the actor options object which will be used to register & unregister
+ // our process actor.
+ let actorOptions = {
+ parent: Object.assign({}, processActorOptions[fileExt].parent),
+ child: Object.assign({}, processActorOptions[fileExt].child),
+ };
+ actorOptions.includeParent = includeParent;
+ if (remoteTypes !== undefined) {
+ actorOptions.remoteTypes = remoteTypes;
+ }
+ if (loadInDevToolsLoader) {
+ actorOptions.loadInDevToolsLoader = true;
+ }
+
+ // Add a new task for the actor test declared here.
+ add_task(async function () {
+ info("Entering test: " + name);
+
+ // Register our actor, and load a new tab with the provided URL
+ ChromeUtils.registerProcessActor("TestProcessActor", actorOptions);
+ try {
+ await BrowserTestUtils.withNewTab(url, async browser => {
+ info("browser ready");
+ await Promise.resolve(test(browser, window, fileExt));
+ });
+ } finally {
+ // Unregister the actor after the test is complete.
+ ChromeUtils.unregisterProcessActor("TestProcessActor");
+ info("Exiting test: " + name);
+ }
+ });
+}
diff --git a/dom/ipc/tests/JSWindowActor/audio.ogg b/dom/ipc/tests/JSWindowActor/audio.ogg
new file mode 100644
index 0000000000..bed764fbf1
--- /dev/null
+++ b/dom/ipc/tests/JSWindowActor/audio.ogg
Binary files differ
diff --git a/dom/ipc/tests/JSWindowActor/browser.toml b/dom/ipc/tests/JSWindowActor/browser.toml
new file mode 100644
index 0000000000..a9dc7e8b8f
--- /dev/null
+++ b/dom/ipc/tests/JSWindowActor/browser.toml
@@ -0,0 +1,32 @@
+[DEFAULT]
+support-files = ["head.js"]
+
+["browser_contentWindow.js"]
+
+["browser_crash_report.js"]
+
+["browser_destroy_callbacks.js"]
+skip-if = ["!debug && os == 'mac'"] #Bug 1604538
+
+["browser_event_listener.js"]
+support-files = ["file_dummyChromePage.html"]
+
+["browser_getActor.js"]
+
+["browser_getActor_filter.js"]
+
+["browser_observer_notification.js"]
+support-files = [
+ "file_mediaPlayback.html",
+ "audio.ogg",
+]
+
+["browser_process_childid.js"]
+
+["browser_registerWindowActor.js"]
+
+["browser_sendAsyncMessage.js"]
+
+["browser_sendQuery.js"]
+
+["browser_uri_combination.js"]
diff --git a/dom/ipc/tests/JSWindowActor/browser_contentWindow.js b/dom/ipc/tests/JSWindowActor/browser_contentWindow.js
new file mode 100644
index 0000000000..e061fd895c
--- /dev/null
+++ b/dom/ipc/tests/JSWindowActor/browser_contentWindow.js
@@ -0,0 +1,35 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+const CONTENT_WINDOW_URL = "https://example.com/";
+
+declTest("contentWindow null when inner window inactive", {
+ matches: [CONTENT_WINDOW_URL + "*"],
+ url: CONTENT_WINDOW_URL + "?1",
+
+ async test(browser) {
+ // If Fission is disabled, the pref is no-op.
+ await SpecialPowers.pushPrefEnv({
+ set: [["fission.bfcacheInParent", true]],
+ });
+
+ let parent = browser.browsingContext.currentWindowGlobal;
+ let actorParent = parent.getActor("TestWindow");
+
+ await actorParent.sendQuery("storeActor");
+
+ let url = CONTENT_WINDOW_URL + "?2";
+ let loaded = BrowserTestUtils.browserLoaded(browser, false, url);
+ BrowserTestUtils.startLoadingURIString(browser, url);
+ await loaded;
+
+ let result = await actorParent.sendQuery("checkActor");
+
+ is(result.status, "success", "Should succeed when bfcache is enabled");
+ ok(
+ result.valueIsNull,
+ "Should get a null contentWindow when inner window is inactive"
+ );
+ },
+});
diff --git a/dom/ipc/tests/JSWindowActor/browser_crash_report.js b/dom/ipc/tests/JSWindowActor/browser_crash_report.js
new file mode 100644
index 0000000000..f029f1a85a
--- /dev/null
+++ b/dom/ipc/tests/JSWindowActor/browser_crash_report.js
@@ -0,0 +1,114 @@
+/* Any copyright is dedicated to the Public Domain.
+http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+declTest("crash actor", {
+ allFrames: true,
+
+ async test(browser) {
+ if (!("@mozilla.org/toolkit/crash-reporter;1" in Cc)) {
+ ok(true, "Cannot test crash annotations without a crash reporter");
+ return;
+ }
+
+ {
+ info("Creating a new tab.");
+ let newTab = await BrowserTestUtils.openNewForegroundTab(gBrowser, URL);
+ let newTabBrowser = newTab.linkedBrowser;
+
+ let parent =
+ newTabBrowser.browsingContext.currentWindowGlobal.getActor(
+ "TestWindow"
+ );
+ ok(parent, "JSWindowActorParent should have value.");
+
+ await SpecialPowers.spawn(newTabBrowser, [], async function () {
+ let child = content.windowGlobalChild;
+ ok(child, "WindowGlobalChild should have value.");
+ is(
+ child.isInProcess,
+ false,
+ "Actor should be loaded in the content process."
+ );
+ // Make sure that the actor is loaded.
+ let actorChild = child.getActor("TestWindow");
+ is(
+ actorChild.show(),
+ "TestWindowChild",
+ "actor show should have value."
+ );
+ is(
+ actorChild.manager,
+ child,
+ "manager should match WindowGlobalChild."
+ );
+ });
+
+ info(
+ "Crashing from withing an actor. We should have an actor name and a message name."
+ );
+ let report = await BrowserTestUtils.crashFrame(
+ newTabBrowser,
+ /* shouldShowTabCrashPage = */ false,
+ /* shouldClearMinidumps = */ true,
+ /* browsingContext = */ null,
+ { asyncCrash: false }
+ );
+
+ is(report.JSActorName, "BrowserTestUtils");
+ is(report.JSActorMessage, "BrowserTestUtils:CrashFrame");
+
+ BrowserTestUtils.removeTab(newTab);
+ }
+
+ {
+ info("Creating a new tab for async crash");
+ let newTab = await BrowserTestUtils.openNewForegroundTab(gBrowser, URL);
+ let newTabBrowser = newTab.linkedBrowser;
+
+ let parent =
+ newTabBrowser.browsingContext.currentWindowGlobal.getActor(
+ "TestWindow"
+ );
+ ok(parent, "JSWindowActorParent should have value.");
+
+ await SpecialPowers.spawn(newTabBrowser, [], async function () {
+ let child = content.windowGlobalChild;
+ ok(child, "WindowGlobalChild should have value.");
+ is(
+ child.isInProcess,
+ false,
+ "Actor should be loaded in the content process."
+ );
+ // Make sure that the actor is loaded.
+ let actorChild = child.getActor("TestWindow");
+ is(
+ actorChild.show(),
+ "TestWindowChild",
+ "actor show should have value."
+ );
+ is(
+ actorChild.manager,
+ child,
+ "manager should match WindowGlobalChild."
+ );
+ });
+
+ info(
+ "Crashing from without an actor. We should have neither an actor name nor a message name."
+ );
+ let report = await BrowserTestUtils.crashFrame(
+ newTabBrowser,
+ /* shouldShowTabCrashPage = */ false,
+ /* shouldClearMinidumps = */ true,
+ /* browsingContext = */ null,
+ { asyncCrash: true }
+ );
+
+ ok(!report.JSActorName);
+ ok(!report.JSActorMessage);
+
+ BrowserTestUtils.removeTab(newTab);
+ }
+ },
+});
diff --git a/dom/ipc/tests/JSWindowActor/browser_destroy_callbacks.js b/dom/ipc/tests/JSWindowActor/browser_destroy_callbacks.js
new file mode 100644
index 0000000000..74cbae9415
--- /dev/null
+++ b/dom/ipc/tests/JSWindowActor/browser_destroy_callbacks.js
@@ -0,0 +1,193 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+declTest("destroy actor by iframe remove", {
+ allFrames: true,
+
+ async test(browser) {
+ await SpecialPowers.spawn(browser, [], async function () {
+ // Create and append an iframe into the window's document.
+ let frame = content.document.createElement("iframe");
+ frame.id = "frame";
+ content.document.body.appendChild(frame);
+ await ContentTaskUtils.waitForEvent(frame, "load");
+ is(content.window.frames.length, 1, "There should be an iframe.");
+ let child = frame.contentWindow.windowGlobalChild;
+ let actorChild = child.getActor("TestWindow");
+ ok(actorChild, "JSWindowActorChild should have value.");
+
+ {
+ let error = actorChild.uninitializedGetterError;
+ const prop = "contentWindow";
+ Assert.ok(
+ error,
+ `Should get error accessing '${prop}' before actor initialization`
+ );
+ if (error) {
+ Assert.equal(
+ error.name,
+ "InvalidStateError",
+ "Error should be an InvalidStateError"
+ );
+ Assert.equal(
+ error.message,
+ `JSWindowActorChild.${prop} getter: Cannot access property '${prop}' before actor is initialized`,
+ "Error should have informative message"
+ );
+ }
+ }
+
+ let didDestroyPromise = new Promise(resolve => {
+ const TOPIC = "test-js-window-actor-diddestroy";
+ Services.obs.addObserver(function obs(subject, topic, data) {
+ ok(data, "didDestroyCallback data should be true.");
+ is(subject, actorChild, "Should have this value");
+
+ Services.obs.removeObserver(obs, TOPIC);
+ // Make a trip through the event loop to ensure that the
+ // actor's manager has been cleared before running remaining
+ // checks.
+ Services.tm.dispatchToMainThread(resolve);
+ }, TOPIC);
+ });
+
+ info("Remove frame");
+ content.document.getElementById("frame").remove();
+ await didDestroyPromise;
+
+ Assert.throws(
+ () => child.getActor("TestWindow"),
+ /InvalidStateError/,
+ "Should throw if frame destroy."
+ );
+
+ for (let prop of [
+ "document",
+ "browsingContext",
+ "docShell",
+ "contentWindow",
+ ]) {
+ let error;
+ try {
+ void actorChild[prop];
+ } catch (e) {
+ error = e;
+ }
+ Assert.ok(
+ error,
+ `Should get error accessing '${prop}' after actor destruction`
+ );
+ if (error) {
+ Assert.equal(
+ error.name,
+ "InvalidStateError",
+ "Error should be an InvalidStateError"
+ );
+ Assert.equal(
+ error.message,
+ `JSWindowActorChild.${prop} getter: Cannot access property '${prop}' after actor 'TestWindow' has been destroyed`,
+ "Error should have informative message"
+ );
+ }
+ }
+ });
+ },
+});
+
+declTest("destroy actor by page navigates", {
+ allFrames: true,
+
+ async test(browser) {
+ info("creating an in-process frame");
+ await SpecialPowers.spawn(browser, [URL], async function (url) {
+ let frame = content.document.createElement("iframe");
+ frame.src = url;
+ content.document.body.appendChild(frame);
+ });
+
+ info("navigating page");
+ await SpecialPowers.spawn(browser, [TEST_URL], async function (url) {
+ let frame = content.document.querySelector("iframe");
+ frame.contentWindow.location = url;
+ let child = frame.contentWindow.windowGlobalChild;
+ let actorChild = child.getActor("TestWindow");
+ ok(actorChild, "JSWindowActorChild should have value.");
+
+ let didDestroyPromise = new Promise(resolve => {
+ const TOPIC = "test-js-window-actor-diddestroy";
+ Services.obs.addObserver(function obs(subject, topic, data) {
+ ok(data, "didDestroyCallback data should be true.");
+ is(subject, actorChild, "Should have this value");
+
+ Services.obs.removeObserver(obs, TOPIC);
+ resolve();
+ }, TOPIC);
+ });
+
+ await Promise.all([
+ didDestroyPromise,
+ ContentTaskUtils.waitForEvent(frame, "load"),
+ ]);
+
+ Assert.throws(
+ () => child.getActor("TestWindow"),
+ /InvalidStateError/,
+ "Should throw if frame destroy."
+ );
+ });
+ },
+});
+
+declTest("destroy actor by tab being closed", {
+ allFrames: true,
+
+ async test(browser) {
+ info("creating a new tab");
+ let newTab = await BrowserTestUtils.openNewForegroundTab(gBrowser, URL);
+ let newTabBrowser = newTab.linkedBrowser;
+
+ let parent =
+ newTabBrowser.browsingContext.currentWindowGlobal.getActor("TestWindow");
+ ok(parent, "JSWindowActorParent should have value.");
+
+ // We can't depend on `SpecialPowers.spawn` to resolve our promise, as the
+ // frame message manager will be being shut down at the same time. Instead
+ // send messages over the per-process message manager which should still be
+ // active.
+ let didDestroyPromise = new Promise(resolve => {
+ Services.ppmm.addMessageListener(
+ "test-jswindowactor-diddestroy",
+ function onmessage(msg) {
+ Services.ppmm.removeMessageListener(
+ "test-jswindowactor-diddestroy",
+ onmessage
+ );
+ resolve();
+ }
+ );
+ });
+
+ info("setting up destroy listeners");
+ await SpecialPowers.spawn(newTabBrowser, [], () => {
+ let child = content.windowGlobalChild;
+ let actorChild = child.getActor("TestWindow");
+ ok(actorChild, "JSWindowActorChild should have value.");
+
+ Services.obs.addObserver(function obs(subject, topic, data) {
+ if (subject != actorChild) {
+ return;
+ }
+ dump("DidDestroy called\n");
+ Services.obs.removeObserver(obs, "test-js-window-actor-diddestroy");
+ Services.cpmm.sendAsyncMessage("test-jswindowactor-diddestroy", data);
+ }, "test-js-window-actor-diddestroy");
+ });
+
+ info("removing new tab");
+ await BrowserTestUtils.removeTab(newTab);
+ info("waiting for destroy callbacks to fire");
+ await didDestroyPromise;
+ info("got didDestroy callback");
+ },
+});
diff --git a/dom/ipc/tests/JSWindowActor/browser_event_listener.js b/dom/ipc/tests/JSWindowActor/browser_event_listener.js
new file mode 100644
index 0000000000..725c2c3753
--- /dev/null
+++ b/dom/ipc/tests/JSWindowActor/browser_event_listener.js
@@ -0,0 +1,171 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+async function createAndShowDropdown(browser) {
+ // Add a select element to the DOM of the loaded document.
+ await SpecialPowers.spawn(browser, [], async function () {
+ content.document.body.innerHTML += `
+ <select id="testSelect">
+ <option>A</option>
+ <option>B</option>
+ </select>`;
+ });
+
+ // Click on the select to show the dropdown.
+ await BrowserTestUtils.synthesizeMouseAtCenter("#testSelect", {}, browser);
+}
+
+declTest("test event triggering actor creation", {
+ events: { mozshowdropdown: {} },
+
+ async test(browser) {
+ // Wait for the observer notification.
+ let observePromise = TestUtils.topicObserved(
+ "test-js-window-actor-parent-event"
+ );
+
+ await createAndShowDropdown(browser);
+
+ // Wait for the observer notification to fire, and inspect the results.
+ let [subject, data] = await observePromise;
+ is(data, "mozshowdropdown");
+
+ let parent = browser.browsingContext.currentWindowGlobal;
+ let actorParent = parent.getActor("TestWindow");
+ ok(actorParent, "JSWindowActorParent should have value.");
+ is(
+ subject.wrappedJSObject,
+ actorParent,
+ "Should have been recieved on the right actor"
+ );
+ },
+});
+
+declTest("test createActor:false not triggering actor creation", {
+ events: { mozshowdropdown: { createActor: false } },
+
+ async test(browser) {
+ info("before actor has been created");
+ const TOPIC = "test-js-window-actor-parent-event";
+ function obs() {
+ ok(false, "actor should not be created");
+ }
+ Services.obs.addObserver(obs, TOPIC);
+
+ await createAndShowDropdown(browser);
+
+ // Bounce into the content process & create the actor after showing the
+ // dropdown, in order to be sure that if an event was going to be
+ // delivered, it already would've been.
+ await SpecialPowers.spawn(browser, [], async () => {
+ content.windowGlobalChild.getActor("TestWindow");
+ });
+ ok(true, "observer notification should not have fired");
+ Services.obs.removeObserver(obs, TOPIC);
+
+ // ----------------------------------------------------
+ info("after actor has been created");
+ let observePromise = TestUtils.topicObserved(
+ "test-js-window-actor-parent-event"
+ );
+ await createAndShowDropdown(browser);
+
+ // Wait for the observer notification to fire, and inspect the results.
+ let [subject, data] = await observePromise;
+ is(data, "mozshowdropdown");
+
+ let parent = browser.browsingContext.currentWindowGlobal;
+ let actorParent = parent.getActor("TestWindow");
+ ok(actorParent, "JSWindowActorParent should have value.");
+ is(
+ subject.wrappedJSObject,
+ actorParent,
+ "Should have been recieved on the right actor"
+ );
+ },
+});
+
+async function testEventProcessedOnce(browser, waitForUrl) {
+ let notificationCount = 0;
+ let firstNotificationResolve;
+ let firstNotification = new Promise(resolve => {
+ firstNotificationResolve = resolve;
+ });
+
+ const TOPIC = "test-js-window-actor-parent-event";
+ function obs(subject, topic, data) {
+ is(data, "mozwindowactortestevent");
+ notificationCount++;
+ if (firstNotificationResolve) {
+ firstNotificationResolve();
+ firstNotificationResolve = null;
+ }
+ }
+ Services.obs.addObserver(obs, TOPIC);
+
+ if (waitForUrl) {
+ info("Waiting for URI to be alright");
+ await TestUtils.waitForCondition(() => {
+ if (!browser.browsingContext?.currentWindowGlobal) {
+ info("No CWG yet");
+ return false;
+ }
+ return SpecialPowers.spawn(browser, [waitForUrl], async function (url) {
+ info(content.document.documentURI);
+ return content.document.documentURI.includes(url);
+ });
+ });
+ }
+
+ info("Dispatching event");
+ await SpecialPowers.spawn(browser, [], async function () {
+ content.document.dispatchEvent(
+ new content.CustomEvent("mozwindowactortestevent", { bubbles: true })
+ );
+ });
+
+ info("Waiting for notification");
+ await firstNotification;
+
+ await new Promise(r => setTimeout(r, 0));
+
+ is(notificationCount, 1, "Should get only one notification");
+
+ Services.obs.removeObserver(obs, TOPIC);
+}
+
+declTest("test in-process content events are not processed twice", {
+ url: "about:preferences",
+ events: { mozwindowactortestevent: { wantUntrusted: true } },
+ async test(browser) {
+ is(
+ browser.getAttribute("type"),
+ "content",
+ "Should be a content <browser>"
+ );
+ is(browser.getAttribute("remotetype"), "", "Should not be remote");
+ await testEventProcessedOnce(browser);
+ },
+});
+
+declTest("test in-process chrome events are processed correctly", {
+ url: "about:blank",
+ events: { mozwindowactortestevent: { wantUntrusted: true } },
+ allFrames: true,
+ includeChrome: true,
+ async test(browser) {
+ let dialogBox = gBrowser.getTabDialogBox(browser);
+ let { dialogClosed, dialog } = dialogBox.open(
+ "chrome://mochitests/content/browser/dom/ipc/tests/JSWindowActor/file_dummyChromePage.html"
+ );
+ let chromeBrowser = dialog._frame;
+ is(chromeBrowser.getAttribute("type"), "", "Should be a chrome <browser>");
+ is(chromeBrowser.getAttribute("remotetype"), "", "Should not be remote");
+
+ await testEventProcessedOnce(chromeBrowser, "dummyChromePage.html");
+
+ dialog.close();
+ await dialogClosed;
+ },
+});
diff --git a/dom/ipc/tests/JSWindowActor/browser_getActor.js b/dom/ipc/tests/JSWindowActor/browser_getActor.js
new file mode 100644
index 0000000000..8d002daf99
--- /dev/null
+++ b/dom/ipc/tests/JSWindowActor/browser_getActor.js
@@ -0,0 +1,36 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+declTest("getActor on both sides", {
+ async test(browser) {
+ let parent = browser.browsingContext.currentWindowGlobal;
+ ok(parent, "WindowGlobalParent should have value.");
+ let actorParent = parent.getActor("TestWindow");
+ is(actorParent.show(), "TestWindowParent", "actor show should have vaule.");
+ is(actorParent.manager, parent, "manager should match WindowGlobalParent.");
+
+ ok(
+ actorParent.sawActorCreated,
+ "Checking that we can observe parent creation"
+ );
+
+ await SpecialPowers.spawn(browser, [], async function () {
+ let child = content.windowGlobalChild;
+ ok(child, "WindowGlobalChild should have value.");
+ is(
+ child.isInProcess,
+ false,
+ "Actor should be loaded in the content process."
+ );
+ let actorChild = child.getActor("TestWindow");
+ is(actorChild.show(), "TestWindowChild", "actor show should have vaule.");
+ is(actorChild.manager, child, "manager should match WindowGlobalChild.");
+
+ ok(
+ actorChild.sawActorCreated,
+ "Checking that we can observe child creation"
+ );
+ });
+ },
+});
diff --git a/dom/ipc/tests/JSWindowActor/browser_getActor_filter.js b/dom/ipc/tests/JSWindowActor/browser_getActor_filter.js
new file mode 100644
index 0000000000..a10697c989
--- /dev/null
+++ b/dom/ipc/tests/JSWindowActor/browser_getActor_filter.js
@@ -0,0 +1,259 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+requestLongerTimeout(2);
+
+declTest("getActor with mismatch", {
+ matches: ["*://*/*"],
+
+ async test(browser) {
+ let parent = browser.browsingContext.currentWindowGlobal;
+ ok(parent, "WindowGlobalParent should have value.");
+ Assert.throws(
+ () => parent.getActor("TestWindow"),
+ /NotSupportedError/,
+ "Should throw if it doesn't match."
+ );
+
+ await SpecialPowers.spawn(browser, [], async function () {
+ let child = content.windowGlobalChild;
+ ok(child, "WindowGlobalChild should have value.");
+
+ Assert.throws(
+ () => child.getActor("TestWindow"),
+ /NotSupportedError/,
+ "Should throw if it doesn't match."
+ );
+ });
+ },
+});
+
+declTest("getActor with matches", {
+ matches: ["*://*/*"],
+ url: TEST_URL,
+
+ async test(browser) {
+ let parent = browser.browsingContext.currentWindowGlobal;
+ ok(parent.getActor("TestWindow"), "JSWindowActorParent should have value.");
+
+ await SpecialPowers.spawn(browser, [], async function () {
+ let child = content.windowGlobalChild;
+ ok(child, "WindowGlobalChild should have value.");
+ ok(child.getActor("TestWindow"), "JSWindowActorChild should have value.");
+ });
+ },
+});
+
+declTest("getActor with iframe matches", {
+ allFrames: true,
+ matches: ["*://*/*"],
+
+ async test(browser) {
+ await SpecialPowers.spawn(browser, [TEST_URL], async function (url) {
+ // Create and append an iframe into the window's document.
+ let frame = content.document.createElement("iframe");
+ frame.src = url;
+ content.document.body.appendChild(frame);
+ await ContentTaskUtils.waitForEvent(frame, "load");
+
+ is(content.frames.length, 1, "There should be an iframe.");
+ await content.SpecialPowers.spawn(frame, [], () => {
+ let child = content.windowGlobalChild;
+ Assert.ok(
+ child.getActor("TestWindow"),
+ "JSWindowActorChild should have value."
+ );
+ });
+ });
+ },
+});
+
+declTest("getActor with iframe mismatch", {
+ allFrames: true,
+ matches: ["about:home"],
+
+ async test(browser) {
+ await SpecialPowers.spawn(browser, [TEST_URL], async function (url) {
+ // Create and append an iframe into the window's document.
+ let frame = content.document.createElement("iframe");
+ frame.src = url;
+ content.document.body.appendChild(frame);
+ await ContentTaskUtils.waitForEvent(frame, "load");
+
+ is(content.frames.length, 1, "There should be an iframe.");
+ await content.SpecialPowers.spawn(frame, [], () => {
+ let child = content.windowGlobalChild;
+ Assert.throws(
+ () => child.getActor("TestWindow"),
+ /NotSupportedError/,
+ "Should throw if it doesn't match."
+ );
+ });
+ });
+ },
+});
+
+declTest("getActor with remoteType match", {
+ remoteTypes: ["web"],
+
+ async test(browser) {
+ let parent = browser.browsingContext.currentWindowGlobal;
+ ok(parent.getActor("TestWindow"), "JSWindowActorParent should have value.");
+
+ await SpecialPowers.spawn(browser, [], async function () {
+ let child = content.windowGlobalChild;
+ ok(child, "WindowGlobalChild should have value.");
+ ok(child.getActor("TestWindow"), "JSWindowActorChild should have value.");
+ });
+ },
+});
+
+declTest("getActor with iframe remoteType match", {
+ allFrames: true,
+ remoteTypes: ["web"],
+
+ async test(browser) {
+ await SpecialPowers.spawn(browser, [TEST_URL], async function (url) {
+ let child = content.windowGlobalChild;
+ ok(child, "WindowGlobalChild should have value.");
+ ok(child.getActor("TestWindow"), "JSWindowActorChild should have value.");
+
+ // Create and append an iframe into the window's document.
+ let frame = content.document.createElement("iframe");
+ frame.src = url;
+ content.document.body.appendChild(frame);
+ await ContentTaskUtils.waitForEvent(frame, "load");
+
+ is(content.frames.length, 1, "There should be an iframe.");
+ await content.SpecialPowers.spawn(frame, [], () => {
+ child = content.windowGlobalChild;
+ Assert.ok(
+ child.getActor("TestWindow"),
+ "JSWindowActorChild should have value."
+ );
+ });
+ });
+ },
+});
+
+declTest("getActor with remoteType mismatch", {
+ remoteTypes: ["privilegedabout"],
+ url: TEST_URL,
+
+ async test(browser) {
+ let parent = browser.browsingContext.currentWindowGlobal;
+ Assert.throws(
+ () => parent.getActor("TestWindow"),
+ /NotSupportedError/,
+ "Should throw if its remoteTypes don't match."
+ );
+
+ await SpecialPowers.spawn(browser, [], async function () {
+ let child = content.windowGlobalChild;
+ ok(child, "WindowGlobalChild should have value.");
+ Assert.throws(
+ () => child.getActor("TestWindow"),
+ /NotSupportedError/,
+ "Should throw if its remoteTypes don't match."
+ );
+ });
+ },
+});
+
+declTest("getActor with iframe messageManagerGroups match", {
+ allFrames: true,
+ messageManagerGroups: ["browsers"],
+
+ async test(browser) {
+ let parent = browser.browsingContext.currentWindowGlobal;
+ ok(parent.getActor("TestWindow"), "JSWindowActorParent should have value.");
+
+ await SpecialPowers.spawn(browser, [TEST_URL], async function (url) {
+ let child = content.windowGlobalChild;
+ ok(child, "WindowGlobalChild should have value.");
+ ok(child.getActor("TestWindow"), "JSWindowActorChild should have value.");
+ });
+ },
+});
+
+declTest("getActor with iframe messageManagerGroups mismatch", {
+ allFrames: true,
+ messageManagerGroups: ["sidebars"],
+
+ async test(browser) {
+ let parent = browser.browsingContext.currentWindowGlobal;
+ Assert.throws(
+ () => parent.getActor("TestWindow"),
+ /NotSupportedError/,
+ "Should throw if its messageManagerGroups doesn't match."
+ );
+
+ await SpecialPowers.spawn(browser, [TEST_URL], async function (url) {
+ let child = content.windowGlobalChild;
+ ok(child, "WindowGlobalChild should have value.");
+ Assert.throws(
+ () => child.getActor("TestWindow"),
+ /NotSupportedError/,
+ "Should throw if its messageManagerGroups doesn't match."
+ );
+ });
+ },
+});
+
+declTest("getActor without allFrames", {
+ allFrames: false,
+
+ async test(browser) {
+ await SpecialPowers.spawn(browser, [], async function () {
+ // Create and append an iframe into the window's document.
+ let frame = content.document.createElement("iframe");
+ content.document.body.appendChild(frame);
+ is(content.frames.length, 1, "There should be an iframe.");
+ let child = frame.contentWindow.windowGlobalChild;
+ Assert.throws(
+ () => child.getActor("TestWindow"),
+ /NotSupportedError/,
+ "Should throw if allFrames is false."
+ );
+ });
+ },
+});
+
+declTest("getActor with allFrames", {
+ allFrames: true,
+
+ async test(browser) {
+ await SpecialPowers.spawn(browser, [], async function () {
+ // Create and append an iframe into the window's document.
+ let frame = content.document.createElement("iframe");
+ content.document.body.appendChild(frame);
+ is(content.frames.length, 1, "There should be an iframe.");
+ let child = frame.contentWindow.windowGlobalChild;
+ let actorChild = child.getActor("TestWindow");
+ ok(actorChild, "JSWindowActorChild should have value.");
+ });
+ },
+});
+
+declTest("getActor without includeChrome", {
+ includeChrome: false,
+
+ async test(_browser, win) {
+ let parent = win.docShell.browsingContext.currentWindowGlobal;
+ SimpleTest.doesThrow(
+ () => parent.getActor("TestWindow"),
+ "Should throw if includeChrome is false."
+ );
+ },
+});
+
+declTest("getActor with includeChrome", {
+ includeChrome: true,
+
+ async test(_browser, win) {
+ let parent = win.docShell.browsingContext.currentWindowGlobal;
+ let actorParent = parent.getActor("TestWindow");
+ ok(actorParent, "JSWindowActorParent should have value.");
+ },
+});
diff --git a/dom/ipc/tests/JSWindowActor/browser_observer_notification.js b/dom/ipc/tests/JSWindowActor/browser_observer_notification.js
new file mode 100644
index 0000000000..c0fe2249e8
--- /dev/null
+++ b/dom/ipc/tests/JSWindowActor/browser_observer_notification.js
@@ -0,0 +1,118 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+/* eslint-disable no-unused-vars */
+declTest("test observer triggering actor creation", {
+ observers: ["test-js-window-actor-child-observer"],
+
+ async test(browser) {
+ await SpecialPowers.spawn(browser, [], async function () {
+ const TOPIC = "test-js-window-actor-child-observer";
+ Services.obs.notifyObservers(content.window, TOPIC, "dataString");
+
+ let child = content.windowGlobalChild;
+ let actorChild = child.getActor("TestWindow");
+ ok(actorChild, "JSWindowActorChild should have value.");
+ let { subject, topic, data } = actorChild.lastObserved;
+
+ is(
+ subject.windowGlobalChild.getActor("TestWindow"),
+ actorChild,
+ "Should have been recieved on the right actor"
+ );
+ is(topic, TOPIC, "Topic matches");
+ is(data, "dataString", "Data matches");
+ });
+ },
+});
+
+declTest("test observers with null data", {
+ observers: ["test-js-window-actor-child-observer"],
+
+ async test(browser) {
+ await SpecialPowers.spawn(browser, [], async function () {
+ const TOPIC = "test-js-window-actor-child-observer";
+ Services.obs.notifyObservers(content.window, TOPIC);
+
+ let child = content.windowGlobalChild;
+ let actorChild = child.getActor("TestWindow");
+ ok(actorChild, "JSWindowActorChild should have value.");
+ let { subject, topic, data } = actorChild.lastObserved;
+
+ is(
+ subject.windowGlobalChild.getActor("TestWindow"),
+ actorChild,
+ "Should have been recieved on the right actor"
+ );
+ is(topic, TOPIC, "Topic matches");
+ is(data, null, "Data matches");
+ });
+ },
+});
+
+declTest("observers don't notify with wrong window", {
+ observers: ["test-js-window-actor-child-observer"],
+
+ async test(browser) {
+ const MSG_RE =
+ /JSWindowActor TestWindow: expected window subject for topic 'test-js-window-actor-child-observer'/;
+ let expectMessage = new Promise(resolve => {
+ Services.console.registerListener(function consoleListener(msg) {
+ // Run everything async in order to avoid logging messages from the
+ // console listener.
+ Cu.dispatch(() => {
+ if (!MSG_RE.test(msg.message)) {
+ info("ignoring non-matching console message: " + msg.message);
+ return;
+ }
+ info("received console message: " + msg.message);
+ is(msg.logLevel, Ci.nsIConsoleMessage.error, "should be an error");
+
+ Services.console.unregisterListener(consoleListener);
+ resolve();
+ });
+ });
+ });
+
+ await SpecialPowers.spawn(browser, [], async function () {
+ const TOPIC = "test-js-window-actor-child-observer";
+ Services.obs.notifyObservers(null, TOPIC);
+ let child = content.windowGlobalChild;
+ let actorChild = child.getActor("TestWindow");
+ ok(actorChild, "JSWindowActorChild should have value.");
+ is(
+ actorChild.lastObserved,
+ undefined,
+ "Should not receive wrong window's observer notification!"
+ );
+ });
+
+ await expectMessage;
+ },
+});
+
+declTest("observers notify with audio-playback", {
+ observers: ["audio-playback"],
+ url: "http://example.com/browser/dom/ipc/tests/JSWindowActor/file_mediaPlayback.html",
+
+ async test(browser) {
+ await SpecialPowers.spawn(browser, [], async function () {
+ let audio = content.document.querySelector("audio");
+ audio.play();
+
+ let child = content.windowGlobalChild;
+ let actorChild = child.getActor("TestWindow");
+ ok(actorChild, "JSWindowActorChild should have value.");
+
+ let observePromise = new Promise(resolve => {
+ actorChild.done = ({ subject, topic, data }) =>
+ resolve({ subject, topic, data });
+ });
+
+ let { subject, topic, data } = await observePromise;
+ is(topic, "audio-playback", "Topic matches");
+ is(data, "active", "Data matches");
+ });
+ },
+});
diff --git a/dom/ipc/tests/JSWindowActor/browser_process_childid.js b/dom/ipc/tests/JSWindowActor/browser_process_childid.js
new file mode 100644
index 0000000000..ba6db1dabb
--- /dev/null
+++ b/dom/ipc/tests/JSWindowActor/browser_process_childid.js
@@ -0,0 +1,27 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// Test that `process.childID` is defined.
+
+declTest("test childid", {
+ async test(browser) {
+ let parent = browser.browsingContext.currentWindowGlobal;
+ ok(
+ parent.domProcess.childID,
+ "parent domProcess.childID should have a value."
+ );
+ await SpecialPowers.spawn(
+ browser,
+ [parent.domProcess.childID],
+ async function (parentChildID) {
+ ok(
+ ChromeUtils.domProcessChild.childID,
+ "child process.childID should have a value."
+ );
+ let childID = ChromeUtils.domProcessChild.childID;
+ is(parentChildID, childID);
+ }
+ );
+ },
+});
diff --git a/dom/ipc/tests/JSWindowActor/browser_registerWindowActor.js b/dom/ipc/tests/JSWindowActor/browser_registerWindowActor.js
new file mode 100644
index 0000000000..edaa99d2ed
--- /dev/null
+++ b/dom/ipc/tests/JSWindowActor/browser_registerWindowActor.js
@@ -0,0 +1,16 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+declTest("double register", {
+ async test(_browser, _window, fileExt) {
+ SimpleTest.doesThrow(
+ () =>
+ ChromeUtils.registerWindowActor(
+ "TestWindow",
+ windowActorOptions[fileExt]
+ ),
+ "Should throw if register has duplicate name."
+ );
+ },
+});
diff --git a/dom/ipc/tests/JSWindowActor/browser_sendAsyncMessage.js b/dom/ipc/tests/JSWindowActor/browser_sendAsyncMessage.js
new file mode 100644
index 0000000000..9ba33a3936
--- /dev/null
+++ b/dom/ipc/tests/JSWindowActor/browser_sendAsyncMessage.js
@@ -0,0 +1,71 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+declTest("asyncMessage testing", {
+ async test(browser) {
+ let parent = browser.browsingContext.currentWindowGlobal;
+ let actorParent = parent.getActor("TestWindow");
+ ok(actorParent, "JSWindowActorParent should have value.");
+
+ await ContentTask.spawn(browser, {}, async function () {
+ let child = content.windowGlobalChild;
+ let actorChild = child.getActor("TestWindow");
+ ok(actorChild, "JSWindowActorChild should have value.");
+
+ let promise = new Promise(resolve => {
+ actorChild.sendAsyncMessage("init", {});
+ actorChild.done = data => resolve(data);
+ }).then(data => {
+ ok(data.initial, "Initial should be true.");
+ ok(data.toParent, "ToParent should be true.");
+ ok(data.toChild, "ToChild should be true.");
+ });
+
+ await promise;
+ });
+ },
+});
+
+declTest("asyncMessage without both sides", {
+ async test(browser) {
+ // If we don't create a parent actor, make sure the parent actor
+ // gets created by having sent the message.
+ await ContentTask.spawn(browser, {}, async function () {
+ let child = content.windowGlobalChild;
+ let actorChild = child.getActor("TestWindow");
+ ok(actorChild, "JSWindowActorChild should have value.");
+
+ let promise = new Promise(resolve => {
+ actorChild.sendAsyncMessage("init", {});
+ actorChild.done = data => resolve(data);
+ }).then(data => {
+ ok(data.initial, "Initial should be true.");
+ ok(data.toParent, "ToParent should be true.");
+ ok(data.toChild, "ToChild should be true.");
+ });
+
+ await promise;
+ });
+ },
+});
+
+declTest("asyncMessage can transfer MessagePorts", {
+ async test(browser) {
+ await ContentTask.spawn(browser, {}, async function () {
+ let child = content.windowGlobalChild;
+ let actorChild = child.getActor("TestWindow");
+
+ let { port1, port2 } = new MessageChannel();
+ actorChild.sendAsyncMessage("messagePort", { port: port2 }, [port2]);
+ let reply = await new Promise(resolve => {
+ port1.onmessage = message => {
+ resolve(message.data);
+ port1.close();
+ };
+ });
+
+ is(reply, "Message sent from parent over a MessagePort.");
+ });
+ },
+});
diff --git a/dom/ipc/tests/JSWindowActor/browser_sendQuery.js b/dom/ipc/tests/JSWindowActor/browser_sendQuery.js
new file mode 100644
index 0000000000..0d1a845949
--- /dev/null
+++ b/dom/ipc/tests/JSWindowActor/browser_sendQuery.js
@@ -0,0 +1,131 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+const ERROR_LINE_NUMBERS = {
+ jsm: 39,
+ "sys.mjs": 36,
+};
+const EXCEPTION_LINE_NUMBERS = {
+ jsm: ERROR_LINE_NUMBERS.jsm + 3,
+ "sys.mjs": ERROR_LINE_NUMBERS["sys.mjs"] + 3,
+};
+const ERROR_COLUMN_NUMBERS = {
+ jsm: 31,
+ "sys.mjs": 31,
+};
+const EXCEPTION_COLUMN_NUMBERS = {
+ jsm: 22,
+ "sys.mjs": 22,
+};
+
+function maybeAsyncStack(offset, column) {
+ if (
+ Services.prefs.getBoolPref(
+ "javascript.options.asyncstack_capture_debuggee_only"
+ )
+ ) {
+ return "";
+ }
+
+ let stack = Error().stack.replace(/^.*?\n/, "");
+ return (
+ "JSActor query*" +
+ stack.replace(
+ /^([^\n]+?):(\d+):\d+/,
+ (m0, m1, m2) => `${m1}:${+m2 + offset}:${column}`
+ )
+ );
+}
+
+declTest("sendQuery Error", {
+ async test(browser, _window, fileExt) {
+ let parent = browser.browsingContext.currentWindowGlobal;
+ let actorParent = parent.getActor("TestWindow");
+
+ let asyncStack = maybeAsyncStack(2, 8);
+ let error = await actorParent
+ .sendQuery("error", { message: "foo" })
+ .catch(e => e);
+
+ is(error.message, "foo", "Error should have the correct message");
+ is(error.name, "SyntaxError", "Error should have the correct name");
+ is(
+ error.stack,
+ `receiveMessage@resource://testing-common/TestWindowChild.${fileExt}:${ERROR_LINE_NUMBERS[fileExt]}:${ERROR_COLUMN_NUMBERS[fileExt]}\n` +
+ asyncStack,
+ "Error should have the correct stack"
+ );
+ },
+});
+
+declTest("sendQuery Exception", {
+ async test(browser, _window, fileExt) {
+ let parent = browser.browsingContext.currentWindowGlobal;
+ let actorParent = parent.getActor("TestWindow");
+
+ let asyncStack = maybeAsyncStack(2, 8);
+ let error = await actorParent
+ .sendQuery("exception", {
+ message: "foo",
+ result: Cr.NS_ERROR_INVALID_ARG,
+ })
+ .catch(e => e);
+
+ is(error.message, "foo", "Error should have the correct message");
+ is(
+ error.result,
+ Cr.NS_ERROR_INVALID_ARG,
+ "Error should have the correct result code"
+ );
+ is(
+ error.stack,
+ `receiveMessage@resource://testing-common/TestWindowChild.${fileExt}:${EXCEPTION_LINE_NUMBERS[fileExt]}:${EXCEPTION_COLUMN_NUMBERS[fileExt]}\n` +
+ asyncStack,
+ "Error should have the correct stack"
+ );
+ },
+});
+
+declTest("sendQuery testing", {
+ async test(browser) {
+ let parent = browser.browsingContext.currentWindowGlobal;
+ let actorParent = parent.getActor("TestWindow");
+ ok(actorParent, "JSWindowActorParent should have value.");
+
+ let { result } = await actorParent.sendQuery("asyncAdd", { a: 10, b: 20 });
+ is(result, 30);
+ },
+});
+
+declTest("sendQuery in-process early lifetime", {
+ url: "about:mozilla",
+ allFrames: true,
+
+ async test(browser) {
+ let iframe = browser.contentDocument.createElement("iframe");
+ browser.contentDocument.body.appendChild(iframe);
+ let wgc = iframe.contentWindow.windowGlobalChild;
+ let actorChild = wgc.getActor("TestWindow");
+ let { result } = await actorChild.sendQuery("asyncMul", { a: 10, b: 20 });
+ is(result, 200);
+ },
+});
+
+declTest("sendQuery unserializable reply", {
+ async test(browser) {
+ let parent = browser.browsingContext.currentWindowGlobal;
+ let actorParent = parent.getActor("TestWindow");
+ ok(actorParent, "JSWindowActorParent should have value");
+
+ try {
+ await actorParent.sendQuery("noncloneReply", {});
+ ok(false, "expected noncloneReply to be rejected");
+ } catch (error) {
+ ok(
+ error.message.includes("message reply cannot be cloned"),
+ "Error should have the correct message"
+ );
+ }
+ },
+});
diff --git a/dom/ipc/tests/JSWindowActor/browser_uri_combination.js b/dom/ipc/tests/JSWindowActor/browser_uri_combination.js
new file mode 100644
index 0000000000..ce46a3e3b6
--- /dev/null
+++ b/dom/ipc/tests/JSWindowActor/browser_uri_combination.js
@@ -0,0 +1,81 @@
+add_task(function specify_both() {
+ // Specifying both should throw.
+
+ SimpleTest.doesThrow(() => {
+ ChromeUtils.registerWindowActor("TestWindow", {
+ parent: {
+ moduleURI: "resource://testing-common/TestWindowParent.jsm",
+ },
+ child: {
+ moduleURI: "resource://testing-common/TestWindowChild.jsm",
+ esModuleURI: "resource://testing-common/TestWindowChild.sys.mjs",
+ },
+ });
+ }, "Should throw if both moduleURI and esModuleURI are specified.");
+
+ SimpleTest.doesThrow(() => {
+ ChromeUtils.registerWindowActor("TestWindow", {
+ parent: {
+ esModuleURI: "resource://testing-common/TestWindowParent.sys.mjs",
+ },
+ child: {
+ moduleURI: "resource://testing-common/TestWindowChild.jsm",
+ esModuleURI: "resource://testing-common/TestWindowChild.sys.mjs",
+ },
+ });
+ }, "Should throw if both moduleURI and esModuleURI are specified.");
+
+ SimpleTest.doesThrow(() => {
+ ChromeUtils.registerWindowActor("TestWindow", {
+ parent: {
+ moduleURI: "resource://testing-common/TestWindowParent.jsm",
+ esModuleURI: "resource://testing-common/TestWindowParent.sys.mjs",
+ },
+ child: {
+ moduleURI: "resource://testing-common/TestWindowChild.jsm",
+ },
+ });
+ }, "Should throw if both moduleURI and esModuleURI are specified.");
+
+ SimpleTest.doesThrow(() => {
+ ChromeUtils.registerWindowActor("TestWindow", {
+ parent: {
+ moduleURI: "resource://testing-common/TestWindowParent.jsm",
+ esModuleURI: "resource://testing-common/TestWindowParent.sys.mjs",
+ },
+ child: {
+ esModuleURI: "resource://testing-common/TestWindowChild.sys.mjs",
+ },
+ });
+ }, "Should throw if both moduleURI and esModuleURI are specified.");
+});
+
+add_task(function specify_mixed() {
+ // Mixing JSM and ESM should work.
+
+ try {
+ ChromeUtils.registerWindowActor("TestWindow", {
+ parent: {
+ moduleURI: "resource://testing-common/TestWindowParent.jsm",
+ },
+ child: {
+ esModuleURI: "resource://testing-common/TestWindowChild.sys.mjs",
+ },
+ });
+ } finally {
+ ChromeUtils.unregisterWindowActor("TestWindow");
+ }
+
+ try {
+ ChromeUtils.registerWindowActor("TestWindow", {
+ parent: {
+ esModuleURI: "resource://testing-common/TestWindowParent.sys.mjs",
+ },
+ child: {
+ moduleURI: "resource://testing-common/TestWindowChild.jsm",
+ },
+ });
+ } finally {
+ ChromeUtils.unregisterWindowActor("TestWindow");
+ }
+});
diff --git a/dom/ipc/tests/JSWindowActor/file_dummyChromePage.html b/dom/ipc/tests/JSWindowActor/file_dummyChromePage.html
new file mode 100644
index 0000000000..c50eddd41f
--- /dev/null
+++ b/dom/ipc/tests/JSWindowActor/file_dummyChromePage.html
@@ -0,0 +1 @@
+<!doctype html>
diff --git a/dom/ipc/tests/JSWindowActor/file_mediaPlayback.html b/dom/ipc/tests/JSWindowActor/file_mediaPlayback.html
new file mode 100644
index 0000000000..a6979287e2
--- /dev/null
+++ b/dom/ipc/tests/JSWindowActor/file_mediaPlayback.html
@@ -0,0 +1,2 @@
+<!DOCTYPE html>
+<audio src="audio.ogg" controls loop>
diff --git a/dom/ipc/tests/JSWindowActor/head.js b/dom/ipc/tests/JSWindowActor/head.js
new file mode 100644
index 0000000000..cfabd40aac
--- /dev/null
+++ b/dom/ipc/tests/JSWindowActor/head.js
@@ -0,0 +1,82 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Provide infrastructure for JSWindowActor tests.
+ */
+
+const URL = "about:blank";
+const TEST_URL = "http://test2.example.org/";
+let windowActorOptions = {
+ jsm: {
+ parent: {
+ moduleURI: "resource://testing-common/TestWindowParent.jsm",
+ },
+ child: {
+ moduleURI: "resource://testing-common/TestWindowChild.jsm",
+ },
+ },
+ "sys.mjs": {
+ parent: {
+ esModuleURI: "resource://testing-common/TestWindowParent.sys.mjs",
+ },
+ child: {
+ esModuleURI: "resource://testing-common/TestWindowChild.sys.mjs",
+ },
+ },
+};
+
+function declTest(name, cfg) {
+ declTestWithOptions(name, cfg, "jsm");
+ declTestWithOptions(name, cfg, "sys.mjs");
+}
+
+function declTestWithOptions(name, cfg, fileExt) {
+ let {
+ url = "about:blank",
+ allFrames = false,
+ includeChrome = false,
+ matches,
+ remoteTypes,
+ messageManagerGroups,
+ events,
+ observers,
+ test,
+ } = cfg;
+
+ // Build the actor options object which will be used to register & unregister
+ // our window actor.
+ let actorOptions = {
+ parent: { ...windowActorOptions[fileExt].parent },
+ child: { ...windowActorOptions[fileExt].child, events, observers },
+ };
+ actorOptions.allFrames = allFrames;
+ actorOptions.includeChrome = includeChrome;
+ if (matches !== undefined) {
+ actorOptions.matches = matches;
+ }
+ if (remoteTypes !== undefined) {
+ actorOptions.remoteTypes = remoteTypes;
+ }
+ if (messageManagerGroups !== undefined) {
+ actorOptions.messageManagerGroups = messageManagerGroups;
+ }
+
+ // Add a new task for the actor test declared here.
+ add_task(async function () {
+ info("Entering test: " + name);
+
+ // Register our actor, and load a new tab with the relevant URL
+ ChromeUtils.registerWindowActor("TestWindow", actorOptions);
+ try {
+ await BrowserTestUtils.withNewTab(url, async browser => {
+ info("browser ready");
+ await Promise.resolve(test(browser, window, fileExt));
+ });
+ } finally {
+ // Unregister the actor after the test is complete.
+ ChromeUtils.unregisterWindowActor("TestWindow");
+ info("Exiting test: " + name);
+ }
+ });
+}
diff --git a/dom/ipc/tests/blob_verify.sjs b/dom/ipc/tests/blob_verify.sjs
new file mode 100644
index 0000000000..c979192bf0
--- /dev/null
+++ b/dom/ipc/tests/blob_verify.sjs
@@ -0,0 +1,26 @@
+const CC = Components.Constructor;
+const BinaryInputStream = CC(
+ "@mozilla.org/binaryinputstream;1",
+ "nsIBinaryInputStream",
+ "setInputStream"
+);
+const BinaryOutputStream = CC(
+ "@mozilla.org/binaryoutputstream;1",
+ "nsIBinaryOutputStream",
+ "setOutputStream"
+);
+
+function handleRequest(request, response) {
+ var bodyStream = new BinaryInputStream(request.bodyInputStream);
+ var bodyBytes = [];
+ let bodyAvail;
+ while ((bodyAvail = bodyStream.available()) > 0) {
+ Array.prototype.push.apply(bodyBytes, bodyStream.readByteArray(bodyAvail));
+ }
+
+ var bos = new BinaryOutputStream(response.bodyOutputStream);
+
+ response.processAsync();
+ bos.writeByteArray(bodyBytes);
+ response.finish();
+}
diff --git a/dom/ipc/tests/browser.toml b/dom/ipc/tests/browser.toml
new file mode 100644
index 0000000000..cd2129e9c1
--- /dev/null
+++ b/dom/ipc/tests/browser.toml
@@ -0,0 +1,90 @@
+[DEFAULT]
+support-files = [
+ "file_disableScript.html",
+ "file_domainPolicy_base.html",
+ "file_cancel_content_js.html",
+ "../../media/test/short.mp4",
+ "../../media/test/owl.mp3",
+]
+
+["browser_CrashService_crash.js"]
+skip-if = ["!crashreporter"]
+
+["browser_ProcessPriorityManager.js"]
+# The Process Priority Manager is only enabled for Windows, Linux, and MacOS so far.
+# Bug 1522879.
+# However, you can still run browser_ProcessPriorityManager.js locally on other
+# OSes. This will test the priority manager infrastructure but not actually
+# change the priority.
+skip-if = ["os == 'android'"]
+support-files = [
+ "file_cross_frame.html",
+ "file_dummy.html",
+ "../../tests/browser/file_coop_coep.html",
+ "../../tests/browser/file_coop_coep.html^headers^",
+]
+
+["browser_bug1646088.js"]
+support-files = ["file_dummy.html"]
+
+["browser_bug1686194.js"]
+support-files = ["file_dummy.html"]
+
+["browser_cancel_content_js.js"]
+
+["browser_child_clipboard_restricted.js"]
+
+["browser_content_shutdown_with_endless_js.js"]
+support-files = [
+ "file_endless_js.html",
+ "file_dummy.html",
+]
+
+["browser_crash_oopiframe.js"]
+skip-if = [
+ "!crashreporter",
+ "verify",
+ "os == 'win'", # Bug 1775837
+ "os == 'linux'", # Bug 1775837
+]
+
+["browser_domainPolicy.js"]
+
+["browser_gc_schedule.js"]
+# This test is timing sensitive, timing changes due to asan/tsan/debugging
+# can upset it.
+skip-if = [
+ "verify",
+ "asan",
+ "tsan",
+ "debug",
+ "os != 'linux'",
+ "bits != 64",
+]
+
+["browser_hide_tooltip.js"]
+
+["browser_layers_unloaded_while_interruptingJS.js"]
+
+["browser_memory_distribution_telemetry.js"]
+skip-if = ["true"]
+
+["browser_pbrowser_creation_failure.js"]
+
+["browser_subframesPreferUsed.js"]
+
+["browser_very_fission.js"]
+support-files = ["file_dummy.html"]
+run-if = ["widget == 'gtk'"]
+
+["browser_wpi_isolate_everything.js"]
+support-files = ["browser_wpi_base.js"]
+
+["browser_wpi_isolate_high_value.js"]
+skip-if = ["!fission"] # Only relevant for fission
+support-files = ["browser_wpi_base.js"]
+
+["browser_wpi_isolate_nothing.js"]
+skip-if = ["apple_catalina && debug"] # Bug 1741763; high frequency intermittent; leaked 2 windows
+
+support-files = ["browser_wpi_base.js"]
diff --git a/dom/ipc/tests/browser_CrashService_crash.js b/dom/ipc/tests/browser_CrashService_crash.js
new file mode 100644
index 0000000000..0bcbf95410
--- /dev/null
+++ b/dom/ipc/tests/browser_CrashService_crash.js
@@ -0,0 +1,72 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+// Ensures that content crashes are reported to the crash service
+// (nsICrashService and CrashManager.sys.mjs).
+
+/* eslint-disable mozilla/no-arbitrary-setTimeout */
+SimpleTest.requestFlakyTimeout("untriaged");
+SimpleTest.requestCompleteLog();
+
+add_task(async function () {
+ let tab = await BrowserTestUtils.openNewForegroundTab({
+ gBrowser,
+ forceNewProcess: true,
+ });
+
+ SimpleTest.expectChildProcessCrash();
+
+ let crashMan = Services.crashmanager;
+
+ // First, clear the crash record store.
+ info("Waiting for pruneOldCrashes");
+ var future = new Date(Date.now() + 1000 * 60 * 60 * 24);
+ await crashMan.pruneOldCrashes(future);
+
+ var crashDateMS = Date.now();
+
+ let crashPromise = BrowserTestUtils.crashFrame(tab.linkedBrowser);
+
+ // Finally, poll for the new crash record.
+ await new Promise((resolve, reject) => {
+ function tryGetCrash() {
+ info("Waiting for getCrashes");
+ crashMan.getCrashes().then(
+ function (crashes) {
+ if (crashes.length) {
+ is(crashes.length, 1, "There should be only one record");
+ var crash = crashes[0];
+ ok(
+ crash.isOfType(
+ crashMan.processTypes[Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT],
+ crashMan.CRASH_TYPE_CRASH
+ ),
+ "Record should be a content crash"
+ );
+ ok(!!crash.id, "Record should have an ID");
+ ok(!!crash.crashDate, "Record should have a crash date");
+ var dateMS = crash.crashDate.valueOf();
+ var twoMin = 1000 * 60 * 2;
+ ok(
+ crashDateMS - twoMin <= dateMS && dateMS <= crashDateMS + twoMin,
+ `Record's crash date should be nowish: ` +
+ `now=${crashDateMS} recordDate=${dateMS}`
+ );
+ resolve();
+ } else {
+ setTimeout(tryGetCrash, 1000);
+ }
+ },
+ function (err) {
+ reject(err);
+ }
+ );
+ }
+ setTimeout(tryGetCrash, 1000);
+ });
+
+ await crashPromise;
+
+ await BrowserTestUtils.removeTab(tab);
+});
diff --git a/dom/ipc/tests/browser_ProcessPriorityManager.js b/dom/ipc/tests/browser_ProcessPriorityManager.js
new file mode 100644
index 0000000000..e1532a53ca
--- /dev/null
+++ b/dom/ipc/tests/browser_ProcessPriorityManager.js
@@ -0,0 +1,963 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+const PRIORITY_SET_TOPIC =
+ "process-priority-manager:TEST-ONLY:process-priority-set";
+
+// Copied from Hal.cpp
+const PROCESS_PRIORITY_FOREGROUND = "FOREGROUND";
+const PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE = "BACKGROUND_PERCEIVABLE";
+const PROCESS_PRIORITY_BACKGROUND = "BACKGROUND";
+
+// This is how many milliseconds we'll wait for a process priority
+// change before we assume that it's just not happening.
+const WAIT_FOR_CHANGE_TIME_MS = 2000;
+
+// A convenience function for getting the child ID from a browsing context.
+function browsingContextChildID(bc) {
+ return bc.currentWindowGlobal?.domProcess.childID;
+}
+
+/**
+ * This class is responsible for watching process priority changes, and
+ * mapping them to tabs in a single window.
+ */
+class TabPriorityWatcher {
+ /**
+ * Constructing a TabPriorityWatcher should happen before any tests
+ * start when there's only a single tab in the window.
+ *
+ * Callers must call `destroy()` on any instance that is constructed
+ * when the test is completed.
+ *
+ * @param tabbrowser (<tabbrowser>)
+ * The tabbrowser (gBrowser) for the window to be tested.
+ */
+ constructor(tabbrowser) {
+ this.tabbrowser = tabbrowser;
+ Assert.equal(
+ tabbrowser.tabs.length,
+ 1,
+ "TabPriorityWatcher must be constructed in a window " +
+ "with a single tab to start."
+ );
+
+ // This maps from childIDs to process priorities.
+ this.priorityMap = new Map();
+
+ // The keys in this map are childIDs we're not expecting to change.
+ // Each value is an array of priorities we've seen the childID changed
+ // to since it was added to the map. If the array is empty, there
+ // have been no changes.
+ this.noChangeChildIDs = new Map();
+
+ Services.obs.addObserver(this, PRIORITY_SET_TOPIC);
+ }
+
+ /**
+ * Cleans up lingering references for an instance of
+ * TabPriorityWatcher to avoid leaks. This should be called when
+ * finishing the test.
+ */
+ destroy() {
+ Services.obs.removeObserver(this, PRIORITY_SET_TOPIC);
+ }
+
+ /**
+ * This returns a Promise that resolves when the process with
+ * the given childID reaches the given priority.
+ * This will eventually time out if that priority is never reached.
+ *
+ * @param childID
+ * The childID of the process to wait on.
+ * @param expectedPriority (String)
+ * One of the PROCESS_PRIORITY_ constants defined at the
+ * top of this file.
+ * @return Promise
+ * @resolves undefined
+ * Once the browser reaches the expected priority.
+ */
+ async waitForPriorityChange(childID, expectedPriority) {
+ await TestUtils.waitForCondition(() => {
+ let currentPriority = this.priorityMap.get(childID);
+ if (currentPriority == expectedPriority) {
+ Assert.ok(
+ true,
+ `Process with child ID ${childID} reached expected ` +
+ `priority: ${currentPriority}`
+ );
+ return true;
+ }
+ return false;
+ }, `Waiting for process with child ID ${childID} to reach priority ${expectedPriority}`);
+ }
+
+ /**
+ * Returns a Promise that resolves after a duration of
+ * WAIT_FOR_CHANGE_TIME_MS. During that time, if the process
+ * with the passed in child ID changes priority, a test
+ * failure will be registered.
+ *
+ * @param childID
+ * The childID of the process that we expect to not change priority.
+ * @return Promise
+ * @resolves undefined
+ * Once the WAIT_FOR_CHANGE_TIME_MS duration has passed.
+ */
+ async ensureNoPriorityChange(childID) {
+ this.noChangeChildIDs.set(childID, []);
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ await new Promise(resolve => setTimeout(resolve, WAIT_FOR_CHANGE_TIME_MS));
+ let priorities = this.noChangeChildIDs.get(childID);
+ Assert.deepEqual(
+ priorities,
+ [],
+ `Should have seen no process priority changes for child ID ${childID}`
+ );
+ this.noChangeChildIDs.delete(childID);
+ }
+
+ /**
+ * This returns a Promise that resolves when all of the processes
+ * of the browsing contexts in the browsing context tree
+ * of a particular <browser> have reached a particular priority.
+ * This will eventually time out if that priority is never reached.
+ *
+ * @param browser (<browser>)
+ * The <browser> to get the BC tree from.
+ * @param expectedPriority (String)
+ * One of the PROCESS_PRIORITY_ constants defined at the
+ * top of this file.
+ * @return Promise
+ * @resolves undefined
+ * Once the browser reaches the expected priority.
+ */
+ async waitForBrowserTreePriority(browser, expectedPriority) {
+ let childIDs = new Set(
+ browser.browsingContext
+ .getAllBrowsingContextsInSubtree()
+ .map(browsingContextChildID)
+ );
+ let promises = [];
+ for (let childID of childIDs) {
+ let currentPriority = this.priorityMap.get(childID);
+
+ promises.push(
+ currentPriority == expectedPriority
+ ? this.ensureNoPriorityChange(childID)
+ : this.waitForPriorityChange(childID, expectedPriority)
+ );
+ }
+
+ await Promise.all(promises);
+ }
+
+ /**
+ * Synchronously returns the priority of a particular child ID.
+ *
+ * @param childID
+ * The childID to get the content process priority for.
+ * @return String
+ * The priority of the child ID's process.
+ */
+ currentPriority(childID) {
+ return this.priorityMap.get(childID);
+ }
+
+ /**
+ * A utility function that takes a string passed via the
+ * PRIORITY_SET_TOPIC observer notification and extracts the
+ * childID and priority string.
+ *
+ * @param ppmDataString (String)
+ * The string data passed through the PRIORITY_SET_TOPIC observer
+ * notification.
+ * @return Object
+ * An object with the following properties:
+ *
+ * childID (Number)
+ * The ID of the content process that changed priority.
+ *
+ * priority (String)
+ * The priority that the content process was set to.
+ */
+ parsePPMData(ppmDataString) {
+ let [childIDStr, priority] = ppmDataString.split(":");
+ return {
+ childID: parseInt(childIDStr, 10),
+ priority,
+ };
+ }
+
+ /** nsIObserver **/
+ observe(subject, topic, data) {
+ if (topic != PRIORITY_SET_TOPIC) {
+ Assert.ok(false, "TabPriorityWatcher is observing the wrong topic");
+ return;
+ }
+
+ let { childID, priority } = this.parsePPMData(data);
+ if (this.noChangeChildIDs.has(childID)) {
+ this.noChangeChildIDs.get(childID).push(priority);
+ }
+ this.priorityMap.set(childID, priority);
+ }
+}
+
+let gTabPriorityWatcher;
+
+add_setup(async function () {
+ // We need to turn on testMode for the process priority manager in
+ // order to receive the observer notifications that this test relies on.
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.ipc.processPriorityManager.testMode", true],
+ ["dom.ipc.processPriorityManager.enabled", true],
+ ],
+ });
+ gTabPriorityWatcher = new TabPriorityWatcher(gBrowser);
+});
+
+registerCleanupFunction(() => {
+ gTabPriorityWatcher.destroy();
+ gTabPriorityWatcher = null;
+});
+
+/**
+ * Utility function that switches the current tabbrowser from one
+ * tab to another, and ensures that the tab that goes into the background
+ * has (or reaches) a particular content process priority.
+ *
+ * It is expected that the fromTab and toTab belong to two separate content
+ * processes.
+ *
+ * @param Object
+ * An object with the following properties:
+ *
+ * fromTab (<tab>)
+ * The tab that will be switched from to the toTab. The fromTab
+ * is the one that will be going into the background.
+ *
+ * toTab (<tab>)
+ * The tab that will be switched to from the fromTab. The toTab
+ * is presumed to start in the background, and will enter the
+ * foreground.
+ *
+ * fromTabExpectedPriority (String)
+ * The priority that the content process for the fromTab is
+ * expected to be (or reach) after the tab goes into the background.
+ * This should be one of the PROCESS_PRIORITY_ strings defined at the
+ * top of the file.
+ *
+ * @return Promise
+ * @resolves undefined
+ * Once the tab switch is complete, and the two content processes for the
+ * tabs have reached the expected priority levels.
+ */
+async function assertPriorityChangeOnBackground({
+ fromTab,
+ toTab,
+ fromTabExpectedPriority,
+}) {
+ let fromBrowser = fromTab.linkedBrowser;
+ let toBrowser = toTab.linkedBrowser;
+
+ // If the tabs aren't running in separate processes, none of the
+ // rest of this is going to work.
+ Assert.notEqual(
+ toBrowser.frameLoader.remoteTab.osPid,
+ fromBrowser.frameLoader.remoteTab.osPid,
+ "Tabs should be running in separate processes."
+ );
+
+ let fromPromise = gTabPriorityWatcher.waitForBrowserTreePriority(
+ fromBrowser,
+ fromTabExpectedPriority
+ );
+ let toPromise = gTabPriorityWatcher.waitForBrowserTreePriority(
+ toBrowser,
+ PROCESS_PRIORITY_FOREGROUND
+ );
+
+ await BrowserTestUtils.switchTab(gBrowser, toTab);
+ await Promise.all([fromPromise, toPromise]);
+}
+
+/**
+ * Test that if a normal tab goes into the background,
+ * it has its process priority lowered to PROCESS_PRIORITY_BACKGROUND.
+ * Additionally, test priorityHint flag sets the process priority
+ * appropriately to PROCESS_PRIORITY_BACKGROUND and PROCESS_PRIORITY_FOREGROUND.
+ */
+add_task(async function test_normal_background_tab() {
+ let originalTab = gBrowser.selectedTab;
+
+ await BrowserTestUtils.withNewTab(
+ "https://example.com/browser/dom/ipc/tests/file_cross_frame.html",
+ async browser => {
+ let tab = gBrowser.getTabForBrowser(browser);
+ await assertPriorityChangeOnBackground({
+ fromTab: tab,
+ toTab: originalTab,
+ fromTabExpectedPriority: PROCESS_PRIORITY_BACKGROUND,
+ });
+
+ await assertPriorityChangeOnBackground({
+ fromTab: originalTab,
+ toTab: tab,
+ fromTabExpectedPriority: PROCESS_PRIORITY_BACKGROUND,
+ });
+
+ let origtabID = browsingContextChildID(
+ originalTab.linkedBrowser.browsingContext
+ );
+
+ Assert.equal(
+ originalTab.linkedBrowser.frameLoader.remoteTab.priorityHint,
+ false,
+ "PriorityHint of the original tab should be false by default"
+ );
+
+ // Changing renderLayers doesn't change priority of the background tab.
+ originalTab.linkedBrowser.preserveLayers(true);
+ originalTab.linkedBrowser.renderLayers = true;
+ await new Promise(resolve =>
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ setTimeout(resolve, WAIT_FOR_CHANGE_TIME_MS)
+ );
+ Assert.equal(
+ gTabPriorityWatcher.currentPriority(origtabID),
+ PROCESS_PRIORITY_BACKGROUND,
+ "Tab didn't get prioritized only due to renderLayers"
+ );
+
+ // Test when priorityHint is true, the original tab priority
+ // becomes PROCESS_PRIORITY_FOREGROUND.
+ originalTab.linkedBrowser.frameLoader.remoteTab.priorityHint = true;
+ Assert.equal(
+ gTabPriorityWatcher.currentPriority(origtabID),
+ PROCESS_PRIORITY_FOREGROUND,
+ "Setting priorityHint to true should set the original tab to foreground priority"
+ );
+
+ // Test when priorityHint is false, the original tab priority
+ // becomes PROCESS_PRIORITY_BACKGROUND.
+ originalTab.linkedBrowser.frameLoader.remoteTab.priorityHint = false;
+ await new Promise(resolve =>
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ setTimeout(resolve, WAIT_FOR_CHANGE_TIME_MS)
+ );
+ Assert.equal(
+ gTabPriorityWatcher.currentPriority(origtabID),
+ PROCESS_PRIORITY_BACKGROUND,
+ "Setting priorityHint to false should set the original tab to background priority"
+ );
+
+ let tabID = browsingContextChildID(tab.linkedBrowser.browsingContext);
+
+ // Test when priorityHint is true, the process priority of the
+ // active tab remains PROCESS_PRIORITY_FOREGROUND.
+ tab.linkedBrowser.frameLoader.remoteTab.priorityHint = true;
+ Assert.equal(
+ gTabPriorityWatcher.currentPriority(tabID),
+ PROCESS_PRIORITY_FOREGROUND,
+ "Setting priorityHint to true should maintain the new tab priority as foreground"
+ );
+
+ // Test when priorityHint is false, the process priority of the
+ // active tab remains PROCESS_PRIORITY_FOREGROUND.
+ tab.linkedBrowser.frameLoader.remoteTab.priorityHint = false;
+ Assert.equal(
+ gTabPriorityWatcher.currentPriority(tabID),
+ PROCESS_PRIORITY_FOREGROUND,
+ "Setting priorityHint to false should maintain the new tab priority as foreground"
+ );
+
+ originalTab.linkedBrowser.preserveLayers(false);
+ originalTab.linkedBrowser.renderLayers = false;
+ }
+ );
+});
+
+// Load a simple page on the given host into a new tab.
+async function loadKeepAliveTab(host) {
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ host + "/browser/dom/ipc/tests/file_dummy.html"
+ );
+ let childID = browsingContextChildID(
+ gBrowser.selectedBrowser.browsingContext
+ );
+
+ Assert.equal(
+ gTabPriorityWatcher.currentPriority(childID),
+ PROCESS_PRIORITY_FOREGROUND,
+ "Loading a new tab should make it prioritized"
+ );
+
+ if (SpecialPowers.useRemoteSubframes) {
+ // There must be only one process with a remote type for the tab we loaded
+ // to ensure that when we load a new page into the iframe with that host
+ // that it will end up in the same process as the initial tab.
+ let remoteType = gBrowser.selectedBrowser.remoteType;
+ await TestUtils.waitForCondition(() => {
+ return (
+ ChromeUtils.getAllDOMProcesses().filter(
+ process => process.remoteType == remoteType
+ ).length == 1
+ );
+ }, `Waiting for there to be only one process with remote type ${remoteType}`);
+ }
+
+ return { tab, childID };
+}
+
+const AUDIO_WAKELOCK_NAME = "audio-playing";
+const VIDEO_WAKELOCK_NAME = "video-playing";
+
+// This function was copied from toolkit/content/tests/browser/head.js
+function wakeLockObserved(powerManager, observeTopic, checkFn) {
+ return new Promise(resolve => {
+ function wakeLockListener() {}
+ wakeLockListener.prototype = {
+ QueryInterface: ChromeUtils.generateQI(["nsIDOMMozWakeLockListener"]),
+ callback(topic, state) {
+ if (topic == observeTopic && checkFn(state)) {
+ powerManager.removeWakeLockListener(wakeLockListener.prototype);
+ resolve();
+ }
+ },
+ };
+ powerManager.addWakeLockListener(wakeLockListener.prototype);
+ });
+}
+
+// This function was copied from toolkit/content/tests/browser/head.js
+async function waitForExpectedWakeLockState(
+ topic,
+ { needLock, isForegroundLock }
+) {
+ const powerManagerService = Cc["@mozilla.org/power/powermanagerservice;1"];
+ const powerManager = powerManagerService.getService(
+ Ci.nsIPowerManagerService
+ );
+ const wakelockState = powerManager.getWakeLockState(topic);
+ let expectedLockState = "unlocked";
+ if (needLock) {
+ expectedLockState = isForegroundLock
+ ? "locked-foreground"
+ : "locked-background";
+ }
+ if (wakelockState != expectedLockState) {
+ info(`wait until wakelock becomes ${expectedLockState}`);
+ await wakeLockObserved(
+ powerManager,
+ topic,
+ state => state == expectedLockState
+ );
+ }
+ is(
+ powerManager.getWakeLockState(topic),
+ expectedLockState,
+ `the wakelock state for '${topic}' is equal to '${expectedLockState}'`
+ );
+}
+
+/**
+ * If an iframe in a foreground tab is navigated to a new page for
+ * a different site, then the process of the new iframe page should
+ * have priority PROCESS_PRIORITY_FOREGROUND. Additionally, if Fission
+ * is enabled, then the old iframe page's process's priority should be
+ * lowered to PROCESS_PRIORITY_BACKGROUND.
+ */
+add_task(async function test_iframe_navigate() {
+ // This test (eventually) loads a page from the host topHost that has an
+ // iframe from iframe1Host. It then navigates the iframe to iframe2Host.
+ let topHost = "https://example.com";
+ let iframe1Host = "https://example.org";
+ let iframe2Host = "https://example.net";
+
+ // Before we load the final test page into a tab, we need to load pages
+ // from both iframe hosts into tabs. This is needed so that we are testing
+ // the "load a new page" part of prioritization and not the "initial
+ // process load" part. Additionally, it ensures that the process for the
+ // initial iframe page doesn't shut down once we navigate away from it,
+ // which will also affect its prioritization.
+ let { tab: iframe1Tab, childID: iframe1TabChildID } = await loadKeepAliveTab(
+ iframe1Host
+ );
+ let { tab: iframe2Tab, childID: iframe2TabChildID } = await loadKeepAliveTab(
+ iframe2Host
+ );
+
+ await BrowserTestUtils.withNewTab(
+ topHost + "/browser/dom/ipc/tests/file_cross_frame.html",
+ async browser => {
+ Assert.equal(
+ gTabPriorityWatcher.currentPriority(iframe2TabChildID),
+ PROCESS_PRIORITY_BACKGROUND,
+ "Switching to another new tab should deprioritize the old one"
+ );
+
+ let topChildID = browsingContextChildID(browser.browsingContext);
+ let iframe = browser.browsingContext.children[0];
+ let iframe1ChildID = browsingContextChildID(iframe);
+
+ Assert.equal(
+ gTabPriorityWatcher.currentPriority(topChildID),
+ PROCESS_PRIORITY_FOREGROUND,
+ "The top level page in the new tab should be prioritized"
+ );
+
+ Assert.equal(
+ gTabPriorityWatcher.currentPriority(iframe1ChildID),
+ PROCESS_PRIORITY_FOREGROUND,
+ "The iframe in the new tab should be prioritized"
+ );
+
+ if (SpecialPowers.useRemoteSubframes) {
+ // Basic process uniqueness checks for the state after all three tabs
+ // are initially loaded.
+ Assert.notEqual(
+ topChildID,
+ iframe1ChildID,
+ "file_cross_frame.html should be loaded into a different process " +
+ "than its initial iframe"
+ );
+
+ Assert.notEqual(
+ topChildID,
+ iframe2TabChildID,
+ "file_cross_frame.html should be loaded into a different process " +
+ "than the tab containing iframe2Host"
+ );
+
+ Assert.notEqual(
+ iframe1ChildID,
+ iframe2TabChildID,
+ "The initial iframe loaded by file_cross_frame.html should be " +
+ "loaded into a different process than the tab containing " +
+ "iframe2Host"
+ );
+
+ // Note: this assertion depends on our process selection logic.
+ // Specifically, that we reuse an existing process for an iframe if
+ // possible.
+ Assert.equal(
+ iframe1TabChildID,
+ iframe1ChildID,
+ "Both pages loaded in iframe1Host should be in the same process"
+ );
+ }
+
+ // Do a cross-origin navigation in the iframe in the foreground tab.
+ let iframe2URI = iframe2Host + "/browser/dom/ipc/tests/file_dummy.html";
+ let loaded = BrowserTestUtils.browserLoaded(browser, true, iframe2URI);
+ await SpecialPowers.spawn(
+ iframe,
+ [iframe2URI],
+ async function (_iframe2URI) {
+ content.location = _iframe2URI;
+ }
+ );
+ await loaded;
+
+ let iframe2ChildID = browsingContextChildID(iframe);
+ let iframe1Priority = gTabPriorityWatcher.currentPriority(iframe1ChildID);
+ let iframe2Priority = gTabPriorityWatcher.currentPriority(iframe2ChildID);
+
+ if (SpecialPowers.useRemoteSubframes) {
+ // Basic process uniqueness check for the state after navigating the
+ // iframe. There's no need to check the top level pages because they
+ // have not navigated.
+ //
+ // iframe1ChildID != iframe2ChildID is implied by:
+ // iframe1ChildID != iframe2TabChildID
+ // iframe2TabChildID == iframe2ChildID
+ //
+ // iframe2ChildID != topChildID is implied by:
+ // topChildID != iframe2TabChildID
+ // iframe2TabChildID == iframe2ChildID
+
+ // Note: this assertion depends on our process selection logic.
+ // Specifically, that we reuse an existing process for an iframe if
+ // possible. If that changes, this test may need to be carefully
+ // rewritten, as the whole point of the test is to check what happens
+ // with the priority manager when an iframe shares a process with
+ // a page in another tab.
+ Assert.equal(
+ iframe2TabChildID,
+ iframe2ChildID,
+ "Both pages loaded in iframe2Host should be in the same process"
+ );
+
+ // Now that we've established the relationship between the various
+ // processes, we can finally check that the priority manager is doing
+ // the right thing.
+ Assert.equal(
+ iframe1Priority,
+ PROCESS_PRIORITY_BACKGROUND,
+ "The old iframe process should have been deprioritized"
+ );
+ } else {
+ Assert.equal(
+ iframe1ChildID,
+ iframe2ChildID,
+ "Navigation should not have switched processes"
+ );
+ }
+
+ Assert.equal(
+ iframe2Priority,
+ PROCESS_PRIORITY_FOREGROUND,
+ "The new iframe process should be prioritized"
+ );
+ }
+ );
+
+ await BrowserTestUtils.removeTab(iframe2Tab);
+ await BrowserTestUtils.removeTab(iframe1Tab);
+});
+
+/**
+ * Test that a cross-group navigation properly preserves the process priority.
+ * The goal of this test is to check that the code related to mPriorityActive in
+ * CanonicalBrowsingContext::ReplacedBy works correctly, but in practice the
+ * prioritization code in SetRenderLayers will also make this test pass, though
+ * that prioritization happens slightly later.
+ */
+add_task(async function test_cross_group_navigate() {
+ // This page is same-site with the page we're going to cross-group navigate to.
+ let coopPage =
+ "https://example.com/browser/dom/tests/browser/file_coop_coep.html";
+
+ // Load it as a top level tab so that we don't accidentally get the initial
+ // load prioritization.
+ let backgroundTab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ coopPage
+ );
+ let backgroundTabChildID = browsingContextChildID(
+ gBrowser.selectedBrowser.browsingContext
+ );
+
+ Assert.equal(
+ gTabPriorityWatcher.currentPriority(backgroundTabChildID),
+ PROCESS_PRIORITY_FOREGROUND,
+ "Loading a new tab should make it prioritized"
+ );
+
+ await BrowserTestUtils.withNewTab(
+ "https://example.org/browser/dom/ipc/tests/file_cross_frame.html",
+ async browser => {
+ Assert.equal(
+ gTabPriorityWatcher.currentPriority(backgroundTabChildID),
+ PROCESS_PRIORITY_BACKGROUND,
+ "Switching to a new tab should deprioritize the old one"
+ );
+
+ let dotOrgChildID = browsingContextChildID(browser.browsingContext);
+
+ // Do a cross-group navigation.
+ BrowserTestUtils.startLoadingURIString(browser, coopPage);
+ await BrowserTestUtils.browserLoaded(browser);
+
+ let coopChildID = browsingContextChildID(browser.browsingContext);
+ let coopPriority = gTabPriorityWatcher.currentPriority(coopChildID);
+ let dotOrgPriority = gTabPriorityWatcher.currentPriority(dotOrgChildID);
+
+ Assert.equal(
+ backgroundTabChildID,
+ coopChildID,
+ "The same site should get loaded into the same process"
+ );
+ Assert.notEqual(
+ dotOrgChildID,
+ coopChildID,
+ "Navigation should have switched processes"
+ );
+ Assert.equal(
+ dotOrgPriority,
+ PROCESS_PRIORITY_BACKGROUND,
+ "The old page process should have been deprioritized"
+ );
+ Assert.equal(
+ coopPriority,
+ PROCESS_PRIORITY_FOREGROUND,
+ "The new page process should be prioritized"
+ );
+ }
+ );
+
+ await BrowserTestUtils.removeTab(backgroundTab);
+});
+
+/**
+ * Test that if a tab with video goes into the background,
+ * it has its process priority lowered to
+ * PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE if it has no audio,
+ * and that it has its priority remain at
+ * PROCESS_PRIORITY_FOREGROUND if it does have audio.
+ */
+add_task(async function test_video_background_tab() {
+ let originalTab = gBrowser.selectedTab;
+
+ await BrowserTestUtils.withNewTab("https://example.com", async browser => {
+ // Let's load up a video in the tab, but mute it, so that this tab should
+ // reach PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE. We need to wait for the
+ // wakelock changes from the unmuting to get back up to the parent.
+ await SpecialPowers.spawn(browser, [], async () => {
+ let video = content.document.createElement("video");
+ video.src = "https://example.net/browser/dom/ipc/tests/short.mp4";
+ video.muted = true;
+ content.document.body.appendChild(video);
+ // We'll loop the video to avoid it ending before the test is done.
+ video.loop = true;
+ await video.play();
+ });
+ await Promise.all([
+ waitForExpectedWakeLockState(AUDIO_WAKELOCK_NAME, {
+ needLock: false,
+ }),
+ waitForExpectedWakeLockState(VIDEO_WAKELOCK_NAME, {
+ needLock: true,
+ isForegroundLock: true,
+ }),
+ ]);
+
+ let tab = gBrowser.getTabForBrowser(browser);
+
+ // The tab with the muted video should reach
+ // PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE when backgrounded.
+ await assertPriorityChangeOnBackground({
+ fromTab: tab,
+ toTab: originalTab,
+ fromTabExpectedPriority: PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE,
+ });
+
+ // Now switch back. The initial blank tab should reach
+ // PROCESS_PRIORITY_BACKGROUND when backgrounded.
+ await assertPriorityChangeOnBackground({
+ fromTab: originalTab,
+ toTab: tab,
+ fromTabExpectedPriority: PROCESS_PRIORITY_BACKGROUND,
+ });
+
+ // Let's unmute the video now. We need to wait for the wakelock change from
+ // the unmuting to get back up to the parent.
+ await Promise.all([
+ waitForExpectedWakeLockState(AUDIO_WAKELOCK_NAME, {
+ needLock: true,
+ isForegroundLock: true,
+ }),
+ SpecialPowers.spawn(browser, [], async () => {
+ let video = content.document.querySelector("video");
+ video.muted = false;
+ }),
+ ]);
+
+ // The tab with the unmuted video should stay at
+ // PROCESS_PRIORITY_FOREGROUND when backgrounded.
+ await assertPriorityChangeOnBackground({
+ fromTab: tab,
+ toTab: originalTab,
+ fromTabExpectedPriority: PROCESS_PRIORITY_FOREGROUND,
+ });
+
+ // Now switch back. The initial blank tab should reach
+ // PROCESS_PRIORITY_BACKGROUND when backgrounded.
+ await assertPriorityChangeOnBackground({
+ fromTab: originalTab,
+ toTab: tab,
+ fromTabExpectedPriority: PROCESS_PRIORITY_BACKGROUND,
+ });
+ });
+});
+
+/**
+ * Test that if a tab with a playing <audio> element goes into
+ * the background, the process priority does not change, unless
+ * that audio is muted (in which case, it reaches
+ * PROCESS_PRIORITY_BACKGROUND).
+ */
+add_task(async function test_audio_background_tab() {
+ let originalTab = gBrowser.selectedTab;
+
+ await BrowserTestUtils.withNewTab("https://example.com", async browser => {
+ // Let's load up some audio in the tab, but mute it, so that this tab should
+ // reach PROCESS_PRIORITY_BACKGROUND.
+ await SpecialPowers.spawn(browser, [], async () => {
+ let audio = content.document.createElement("audio");
+ audio.src = "https://example.net/browser/dom/ipc/tests/owl.mp3";
+ audio.muted = true;
+ content.document.body.appendChild(audio);
+ // We'll loop the audio to avoid it ending before the test is done.
+ audio.loop = true;
+ await audio.play();
+ });
+
+ let tab = gBrowser.getTabForBrowser(browser);
+
+ // The tab with the muted audio should reach
+ // PROCESS_PRIORITY_BACKGROUND when backgrounded.
+ await assertPriorityChangeOnBackground({
+ fromTab: tab,
+ toTab: originalTab,
+ fromTabExpectedPriority: PROCESS_PRIORITY_BACKGROUND,
+ });
+
+ // Now switch back. The initial blank tab should reach
+ // PROCESS_PRIORITY_BACKGROUND when backgrounded.
+ await assertPriorityChangeOnBackground({
+ fromTab: originalTab,
+ toTab: tab,
+ fromTabExpectedPriority: PROCESS_PRIORITY_BACKGROUND,
+ });
+
+ // Now unmute the audio. Unfortuntely, there's a bit of a race here,
+ // since the wakelock on the audio element is released and then
+ // re-acquired if the audio reaches its end and loops around. This
+ // will cause an unexpected priority change on the content process.
+ //
+ // To avoid this race, we'll seek the audio back to the beginning,
+ // and lower its playback rate to the minimum to increase the
+ // likelihood that the check completes before the audio loops around.
+ await SpecialPowers.spawn(browser, [], async () => {
+ let audio = content.document.querySelector("audio");
+ let seeked = ContentTaskUtils.waitForEvent(audio, "seeked");
+ audio.muted = false;
+ // 0.25 is the minimum playback rate that still keeps the audio audible.
+ audio.playbackRate = 0.25;
+ audio.currentTime = 0;
+ await seeked;
+ });
+
+ // The tab with the unmuted audio should stay at
+ // PROCESS_PRIORITY_FOREGROUND when backgrounded.
+ await assertPriorityChangeOnBackground({
+ fromTab: tab,
+ toTab: originalTab,
+ fromTabExpectedPriority: PROCESS_PRIORITY_FOREGROUND,
+ });
+
+ // Now switch back. The initial blank tab should reach
+ // PROCESS_PRIORITY_BACKGROUND when backgrounded.
+ await assertPriorityChangeOnBackground({
+ fromTab: originalTab,
+ toTab: tab,
+ fromTabExpectedPriority: PROCESS_PRIORITY_BACKGROUND,
+ });
+ });
+});
+
+/**
+ * Test that if a tab with a WebAudio playing goes into the background,
+ * the process priority does not change, unless that WebAudio context is
+ * suspended.
+ */
+add_task(async function test_web_audio_background_tab() {
+ let originalTab = gBrowser.selectedTab;
+
+ await BrowserTestUtils.withNewTab("https://example.com", async browser => {
+ // Let's synthesize a basic square wave as WebAudio.
+ await SpecialPowers.spawn(browser, [], async () => {
+ let audioCtx = new content.AudioContext();
+ let oscillator = audioCtx.createOscillator();
+ oscillator.type = "square";
+ oscillator.frequency.setValueAtTime(440, audioCtx.currentTime);
+ oscillator.connect(audioCtx.destination);
+ oscillator.start();
+ while (audioCtx.state != "running") {
+ info(`wait until AudioContext starts running`);
+ await new Promise(r => (audioCtx.onstatechange = r));
+ }
+ // we'll stash the AudioContext away so that it's easier to access
+ // in the next SpecialPowers.spawn.
+ content.audioCtx = audioCtx;
+ });
+
+ let tab = gBrowser.getTabForBrowser(browser);
+
+ // The tab with the WebAudio should stay at
+ // PROCESS_PRIORITY_FOREGROUND when backgrounded.
+ await assertPriorityChangeOnBackground({
+ fromTab: tab,
+ toTab: originalTab,
+ fromTabExpectedPriority: PROCESS_PRIORITY_FOREGROUND,
+ });
+
+ // Now switch back. The initial blank tab should reach
+ // PROCESS_PRIORITY_BACKGROUND when backgrounded.
+ await assertPriorityChangeOnBackground({
+ fromTab: originalTab,
+ toTab: tab,
+ fromTabExpectedPriority: PROCESS_PRIORITY_BACKGROUND,
+ });
+
+ // Now suspend the WebAudio. This will cause it to stop
+ // playing.
+ await SpecialPowers.spawn(browser, [], async () => {
+ content.audioCtx.suspend();
+ });
+
+ // The tab with the suspended WebAudio should reach
+ // PROCESS_PRIORITY_BACKGROUND when backgrounded.
+ await assertPriorityChangeOnBackground({
+ fromTab: tab,
+ toTab: originalTab,
+ fromTabExpectedPriority: PROCESS_PRIORITY_BACKGROUND,
+ });
+
+ // Now switch back. The initial blank tab should reach
+ // PROCESS_PRIORITY_BACKGROUND when backgrounded.
+ await assertPriorityChangeOnBackground({
+ fromTab: originalTab,
+ toTab: tab,
+ fromTabExpectedPriority: PROCESS_PRIORITY_BACKGROUND,
+ });
+ });
+});
+
+/**
+ * Test that foreground tab's process priority isn't changed when going back to
+ * a bfcached session history entry.
+ */
+add_task(async function test_audio_background_tab() {
+ let page1 = "https://example.com";
+ let page2 = page1 + "/?2";
+
+ await BrowserTestUtils.withNewTab(page1, async browser => {
+ let childID = browsingContextChildID(browser.browsingContext);
+ Assert.equal(
+ gTabPriorityWatcher.currentPriority(childID),
+ PROCESS_PRIORITY_FOREGROUND,
+ "Loading a new tab should make it prioritized."
+ );
+ let loaded = BrowserTestUtils.browserLoaded(browser, false, page2);
+ BrowserTestUtils.startLoadingURIString(browser, page2);
+ await loaded;
+
+ childID = browsingContextChildID(browser.browsingContext);
+ Assert.equal(
+ gTabPriorityWatcher.currentPriority(childID),
+ PROCESS_PRIORITY_FOREGROUND,
+ "Loading a new page should keep the tab prioritized."
+ );
+
+ let pageShowPromise = BrowserTestUtils.waitForContentEvent(
+ browser,
+ "pageshow"
+ );
+ browser.goBack();
+ await pageShowPromise;
+
+ childID = browsingContextChildID(browser.browsingContext);
+ Assert.equal(
+ gTabPriorityWatcher.currentPriority(childID),
+ PROCESS_PRIORITY_FOREGROUND,
+ "Loading a page from the bfcache should keep the tab prioritized."
+ );
+ });
+});
diff --git a/dom/ipc/tests/browser_bug1646088.js b/dom/ipc/tests/browser_bug1646088.js
new file mode 100644
index 0000000000..18e9afb49e
--- /dev/null
+++ b/dom/ipc/tests/browser_bug1646088.js
@@ -0,0 +1,71 @@
+let dir = getChromeDir(getResolvedURI(gTestPath));
+dir.append("file_dummy.html");
+const uriString = Services.io.newFileURI(dir).spec;
+
+add_task(async function () {
+ await BrowserTestUtils.withNewTab(
+ "https://example.com",
+ async function (browser) {
+ // Override the browser's `prepareToChangeRemoteness` so that we can delay
+ // the process switch for an indefinite amount of time. This will allow us
+ // to control the timing of the resolve call to trigger the bug.
+ let prepareToChangeCalled = Promise.withResolvers();
+ let finishSwitch = Promise.withResolvers();
+ let oldPrepare = browser.prepareToChangeRemoteness;
+ browser.prepareToChangeRemoteness = async () => {
+ prepareToChangeCalled.resolve();
+ await oldPrepare.call(browser);
+ await finishSwitch.promise;
+ };
+
+ // Begin a process switch, which should cause `prepareToChangeRemoteness` to
+ // be called.
+ // NOTE: This used to avoid BrowserTestUtils.loadURI, as that call would
+ // previously eagerly perform a process switch meaning that the interesting
+ // codepath wouldn't be triggered. Nowadays the process switch codepath
+ // always happens during navigation as required by this test.
+ info("Beginning process switch into file URI process");
+ let browserLoaded = BrowserTestUtils.browserLoaded(browser);
+ BrowserTestUtils.startLoadingURIString(browser, uriString);
+ await prepareToChangeCalled.promise;
+
+ // The tab we opened is now midway through process switching. Open another
+ // browser within the same tab, and immediately close it after the load
+ // finishes.
+ info("Creating new tab loaded in file URI process");
+ let fileProcess;
+ let browserParentDestroyed = Promise.withResolvers();
+ await BrowserTestUtils.withNewTab(
+ uriString,
+ async function (otherBrowser) {
+ let remoteTab = otherBrowser.frameLoader.remoteTab;
+ fileProcess = remoteTab.contentProcessId;
+ info("Loaded test URI in pid: " + fileProcess);
+
+ browserParentDestroyed.resolve(
+ TestUtils.topicObserved(
+ "ipc:browser-destroyed",
+ subject => subject === remoteTab
+ )
+ );
+ }
+ );
+ await browserParentDestroyed.promise;
+
+ // This browser has now been closed, which could cause the file content
+ // process to begin shutting down, despite us process switching into it.
+ // We can now allow the process switch to finish, and wait for the load to
+ // finish as well.
+ info("BrowserParent has been destroyed, finishing process switch");
+ finishSwitch.resolve();
+ await browserLoaded;
+
+ info("Load complete");
+ is(
+ browser.frameLoader.remoteTab.contentProcessId,
+ fileProcess,
+ "Should have loaded in the same file URI process"
+ );
+ }
+ );
+});
diff --git a/dom/ipc/tests/browser_bug1686194.js b/dom/ipc/tests/browser_bug1686194.js
new file mode 100644
index 0000000000..f6acefa2f2
--- /dev/null
+++ b/dom/ipc/tests/browser_bug1686194.js
@@ -0,0 +1,47 @@
+/* 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/. */
+
+"use strict";
+
+const TEST_PAGE =
+ "http://mochi.test:8888/browser/dom/ipc/tests/file_dummy.html";
+
+function untilPageTitleChanged() {
+ return new Promise(resolve =>
+ gBrowser.addEventListener("pagetitlechanged", resolve, { once: true })
+ );
+}
+
+add_task(async () => {
+ const tab = await BrowserTestUtils.openNewForegroundTab({
+ gBrowser,
+ opening: TEST_PAGE,
+ });
+
+ const { linkedBrowser } = tab;
+ ok(
+ tab.getAttribute("label").includes("file_dummy.html"),
+ "The title should be the raw path"
+ );
+
+ await Promise.all([
+ SpecialPowers.spawn(linkedBrowser, [], function () {
+ content.document.title = "Title";
+ }),
+ untilPageTitleChanged(),
+ ]);
+
+ is(tab.getAttribute("label"), "Title", "The title should change");
+
+ linkedBrowser.reload();
+
+ await untilPageTitleChanged();
+
+ ok(
+ tab.getAttribute("label").includes("file_dummy.html"),
+ "The title should be the raw path again"
+ );
+
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/dom/ipc/tests/browser_cancel_content_js.js b/dom/ipc/tests/browser_cancel_content_js.js
new file mode 100644
index 0000000000..4cb00cd468
--- /dev/null
+++ b/dom/ipc/tests/browser_cancel_content_js.js
@@ -0,0 +1,65 @@
+/* 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/. */
+
+"use strict";
+
+requestLongerTimeout(10);
+
+const TEST_PAGE =
+ "http://mochi.test:8888/browser/dom/ipc/tests/file_cancel_content_js.html";
+const NEXT_PAGE = "http://mochi.test:8888/browser/dom/ipc/tests/";
+const JS_URI = "javascript:void(document.title = 'foo')";
+
+async function test_navigation(nextPage, shouldCancel) {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.max_script_run_time", 20],
+ // Force a single process so that the navigation will complete in the same
+ // process as the previous page which is running the long-running script.
+ ["dom.ipc.processCount", 1],
+ ["dom.ipc.processCount.webIsolated", 1],
+ ],
+ });
+ let tab = await BrowserTestUtils.openNewForegroundTab({
+ gBrowser,
+ opening: TEST_PAGE,
+ });
+
+ const loopEnded = ContentTask.spawn(tab.linkedBrowser, [], async function () {
+ await new Promise(resolve => {
+ content.addEventListener("LongLoopEnded", resolve, {
+ once: true,
+ });
+ });
+ });
+
+ // Wait for the test page's long-running JS loop to start.
+ await ContentTask.spawn(tab.linkedBrowser, [], function () {
+ content.dispatchEvent(new content.Event("StartLongLoop"));
+ });
+
+ info(`navigating to ${nextPage}`);
+ const nextPageLoaded = BrowserTestUtils.waitForContentEvent(
+ tab.linkedBrowser,
+ "DOMTitleChanged"
+ );
+ BrowserTestUtils.startLoadingURIString(gBrowser, nextPage);
+
+ const result = await Promise.race([
+ nextPageLoaded,
+ loopEnded.then(() => "timeout"),
+ ]);
+
+ const timedOut = result === "timeout";
+ if (shouldCancel) {
+ Assert.strictEqual(timedOut, false, "expected next page to be loaded");
+ } else {
+ Assert.strictEqual(timedOut, true, "expected timeout");
+ }
+
+ BrowserTestUtils.removeTab(tab);
+}
+
+add_task(async () => test_navigation(NEXT_PAGE, true));
+add_task(async () => test_navigation(JS_URI, false));
diff --git a/dom/ipc/tests/browser_child_clipboard_restricted.js b/dom/ipc/tests/browser_child_clipboard_restricted.js
new file mode 100644
index 0000000000..be2d1bca9c
--- /dev/null
+++ b/dom/ipc/tests/browser_child_clipboard_restricted.js
@@ -0,0 +1,93 @@
+/* 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/. */
+add_task(async function () {
+ // Create a new content tab to make sure the paste is cross-process.
+ let tab = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ "data:text/html,<br>"
+ );
+ let browser = tab.linkedBrowser;
+
+ await SpecialPowers.spawn(browser, [], async function (arg) {
+ const trans = Cc["@mozilla.org/widget/transferable;1"].createInstance(
+ Ci.nsITransferable
+ );
+ trans.init(null);
+
+ const string = Cc["@mozilla.org/supports-string;1"].createInstance(
+ Ci.nsISupportsString
+ );
+ string.data = "blablabla";
+
+ trans.addDataFlavor("text/unknown");
+ trans.setTransferData("text/unknown", string);
+
+ trans.addDataFlavor("text/plain");
+ trans.setTransferData("text/plain", string);
+
+ // Write to clipboard.
+ Services.clipboard.setData(trans, null, Ci.nsIClipboard.kGlobalClipboard);
+ });
+
+ // Wait for known.
+ for (var i = 0; i < 20; i++) {
+ if (
+ Services.clipboard.hasDataMatchingFlavors(
+ ["text/plain"],
+ Services.clipboard.kGlobalClipboard
+ )
+ ) {
+ break;
+ }
+ }
+
+ function readClipboard(flavor) {
+ const trans = Cc["@mozilla.org/widget/transferable;1"].createInstance(
+ Ci.nsITransferable
+ );
+ trans.init(null);
+ trans.addDataFlavor(flavor);
+ Services.clipboard.getData(
+ trans,
+ Services.clipboard.kGlobalClipboard,
+ SpecialPowers.wrap(window).browsingContext.currentWindowContext
+ );
+
+ let data = {};
+ trans.getTransferData(flavor, data);
+ return data.value.QueryInterface(Ci.nsISupportsString).data;
+ }
+
+ ok(
+ Services.clipboard.hasDataMatchingFlavors(
+ ["text/plain"],
+ Services.clipboard.kGlobalClipboard
+ ),
+ "clipboard should have text/plain"
+ );
+
+ is(
+ readClipboard("text/plain"),
+ "blablabla",
+ "matching string for text/plain"
+ );
+
+ ok(
+ !Services.clipboard.hasDataMatchingFlavors(
+ ["text/unknown"],
+ Services.clipboard.kGlobalClipboard
+ ),
+ "clipboard should not have text/unknown"
+ );
+
+ let error = undefined;
+ try {
+ readClipboard("text/unknown");
+ } catch (e) {
+ error = e;
+ }
+ is(typeof error, "object", "reading text/unknown should fail");
+
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/dom/ipc/tests/browser_content_shutdown_with_endless_js.js b/dom/ipc/tests/browser_content_shutdown_with_endless_js.js
new file mode 100644
index 0000000000..bdec55a12c
--- /dev/null
+++ b/dom/ipc/tests/browser_content_shutdown_with_endless_js.js
@@ -0,0 +1,86 @@
+/* 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/. */
+
+"use strict";
+
+const EMPTY_PAGE =
+ "http://mochi.test:8888/browser/dom/ipc/tests/file_dummy.html";
+
+const HANG_PAGE =
+ "http://mochi.test:8888/browser/dom/ipc/tests/file_endless_js.html";
+
+function pushPref(name, val) {
+ return SpecialPowers.pushPrefEnv({ set: [[name, val]] });
+}
+
+async function createAndShutdownContentProcess(url) {
+ info("Create and shutdown a content process for " + url);
+
+ // Launch a new process and load url. Sets up a promise that will resolve
+ // on shutdown.
+ let browserDestroyed = Promise.withResolvers();
+ await BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ opening: url,
+ waitForLoad: true,
+ forceNewProcess: true,
+ },
+ async function (otherBrowser) {
+ let remoteTab = otherBrowser.frameLoader.remoteTab;
+
+ ok(true, "Content process created.");
+
+ browserDestroyed.resolve(
+ TestUtils.topicObserved(
+ "ipc:browser-destroyed",
+ subject => subject === remoteTab
+ )
+ );
+
+ // Trigger onmessage in the content browser
+ await SpecialPowers.spawn(otherBrowser, [], function () {
+ content.postMessage("LoadedMessage", "*");
+ });
+
+ // Give the content process some extra time before we start its shutdown.
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ await new Promise(resolve => setTimeout(resolve, 50));
+
+ // withNewTab will start the shutdown of the child process for us
+ }
+ );
+
+ // Now wait for it to really shut down.
+ // If the HANG_PAGE JS is not canceled we will hang here.
+ await browserDestroyed.promise;
+
+ // If we do not hang and get here, we are fine.
+ ok(true, "Shutdown of content process.");
+}
+
+add_task(async () => {
+ // This test is only relevant in e10s.
+ if (!gMultiProcessBrowser) {
+ ok(true, "We are not in multiprocess mode, skipping test.");
+ return;
+ }
+
+ await pushPref("dom.abort_script_on_child_shutdown", true);
+
+ // Ensure the process cache cannot interfere.
+ pushPref("dom.ipc.processPreload.enabled", false);
+ // Ensure we have no cached processes from previous tests.
+ Services.ppmm.releaseCachedProcesses();
+
+ // First let's do a dry run that should always succeed.
+ await createAndShutdownContentProcess(EMPTY_PAGE);
+
+ // Now we will start a shutdown of our content process while our content
+ // script is running an endless loop.
+ //
+ // If the JS does not get interrupted on shutdown, it will cause this test
+ // to hang.
+ await createAndShutdownContentProcess(HANG_PAGE);
+});
diff --git a/dom/ipc/tests/browser_crash_oopiframe.js b/dom/ipc/tests/browser_crash_oopiframe.js
new file mode 100644
index 0000000000..9161b874a9
--- /dev/null
+++ b/dom/ipc/tests/browser_crash_oopiframe.js
@@ -0,0 +1,245 @@
+"use strict";
+
+/**
+ * Opens a number of tabs containing an out-of-process iframe.
+ *
+ * @param numTabs the number of tabs to open.
+ * @returns the browsing context of the iframe in the last tab opened.
+ */
+async function openTestTabs(numTabs) {
+ let iframeBC = null;
+
+ for (let count = 0; count < numTabs; count++) {
+ let tab = await BrowserTestUtils.openNewForegroundTab({
+ gBrowser,
+ url: "about:blank",
+ });
+
+ // If we load example.com in an injected subframe, we assume that this
+ // will load in its own subprocess, which we can then crash.
+ iframeBC = await SpecialPowers.spawn(tab.linkedBrowser, [], async () => {
+ let iframe = content.document.createElement("iframe");
+ iframe.setAttribute("src", "http://example.com");
+
+ content.document.body.appendChild(iframe);
+ await ContentTaskUtils.waitForEvent(iframe, "load");
+ return iframe.frameLoader.browsingContext;
+ });
+ }
+
+ return iframeBC;
+}
+
+/**
+ * Helper function for testing frame crashing. Some tabs are opened
+ * containing frames from example.com and then the process for
+ * example.com is crashed. Notifications should apply to each tab
+ * and all should close when one of the notifications is closed.
+ *
+ * @param numTabs the number of tabs to open.
+ */
+async function testFrameCrash(numTabs) {
+ let iframeBC = await openTestTabs(numTabs);
+ let browser = gBrowser.selectedBrowser;
+ let rootBC = browser.browsingContext;
+
+ is(iframeBC.parent, rootBC, "oop frame has root as parent");
+
+ let eventFiredPromise = BrowserTestUtils.waitForEvent(
+ browser,
+ "oop-browser-crashed"
+ );
+
+ BrowserTestUtils.crashFrame(
+ browser,
+ true /* shouldShowTabCrashPage */,
+ true /* shouldClearMinidumps */,
+ iframeBC
+ );
+
+ let notificationPromise = BrowserTestUtils.waitForNotificationBar(
+ gBrowser,
+ browser,
+ "subframe-crashed"
+ );
+
+ info("Waiting for oop-browser-crashed event.");
+ await eventFiredPromise.then(event => {
+ ok(!event.isTopFrame, "should not be reporting top-level frame crash");
+ Assert.notEqual(event.childID, 0, "childID is non-zero");
+
+ isnot(
+ event.browsingContextId,
+ rootBC,
+ "top frame browsing context id not expected."
+ );
+
+ is(
+ event.browsingContextId,
+ iframeBC.id,
+ "oop frame browsing context id expected."
+ );
+ });
+
+ if (numTabs == 1) {
+ // The BrowsingContext is re-used, but the window global might still be
+ // getting set up at this point, so wait until it's been initialized.
+ let { subject: windowGlobal } = await BrowserUtils.promiseObserved(
+ "window-global-created",
+ wgp => wgp.documentURI.spec.startsWith("about:framecrashed")
+ );
+
+ is(
+ windowGlobal,
+ iframeBC.currentWindowGlobal,
+ "Resolved on expected window global"
+ );
+
+ let newIframeURI = await SpecialPowers.spawn(iframeBC, [], async () => {
+ return content.document.documentURI;
+ });
+
+ ok(
+ newIframeURI.startsWith("about:framecrashed"),
+ "The iframe is now pointing at about:framecrashed"
+ );
+
+ let title = await SpecialPowers.spawn(iframeBC, [], async () => {
+ await content.document.l10n.ready;
+ return content.document.documentElement.getAttribute("title");
+ });
+ ok(title, "The iframe has a non-empty tooltip.");
+ }
+
+ // Next, check that the crash notification bar has appeared.
+ await notificationPromise;
+
+ for (let count = 1; count <= numTabs; count++) {
+ let notificationBox = gBrowser.getNotificationBox(gBrowser.browsers[count]);
+ let notification = notificationBox.currentNotification;
+ ok(notification, "Notification " + count + " should be visible");
+ is(
+ notification.getAttribute("value"),
+ "subframe-crashed",
+ "Should be showing the right notification" + count
+ );
+
+ let buttons = notification.buttonContainer.querySelectorAll(
+ ".notification-button"
+ );
+ is(
+ buttons.length,
+ 1,
+ "Notification " + count + " should have only one button."
+ );
+ let links = notification.supportLinkEls;
+ is(
+ links.length,
+ 1,
+ "Notification " + count + " should have only one link."
+ );
+ ok(
+ notification.messageText.textContent.length,
+ "Notification " + count + " should have a crash msg."
+ );
+ }
+
+ // Press the ignore button on the visible notification.
+ let notificationBox = gBrowser.getNotificationBox(gBrowser.selectedBrowser);
+ let notification = notificationBox.currentNotification;
+
+ // Make sure all of the notifications were closed when one of them was closed.
+ let closedPromises = [];
+ for (let count = 1; count <= numTabs; count++) {
+ let nb = gBrowser.getNotificationBox(gBrowser.browsers[count]);
+ closedPromises.push(
+ BrowserTestUtils.waitForMutationCondition(
+ nb.stack,
+ { childList: true },
+ () => !nb.currentNotification
+ )
+ );
+ }
+
+ notification.dismiss();
+ await Promise.all(closedPromises);
+
+ for (let count = 1; count <= numTabs; count++) {
+ BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ }
+}
+
+/**
+ * In this test, we crash an out-of-process iframe and
+ * verify that :
+ * 1. the "oop-browser-crashed" event is dispatched with
+ * the browsing context of the crashed oop subframe.
+ * 2. the crashed subframe is now pointing at "about:framecrashed"
+ * page.
+ */
+add_task(async function test_crashframe() {
+ // Open a new window with fission enabled.
+ ok(
+ SpecialPowers.useRemoteSubframes,
+ "This test only makes sense of we can use OOP iframes."
+ );
+
+ // Create the crash reporting directory if it doesn't yet exist, otherwise, a failure
+ // sometimes occurs. See bug 1687855 for fixing this.
+ const uAppDataPath = Services.dirsvc.get("UAppData", Ci.nsIFile).path;
+ let path = PathUtils.join(uAppDataPath, "Crash Reports", "pending");
+ await IOUtils.makeDirectory(path, { ignoreExisting: true });
+
+ // Test both one tab and when four tabs are opened.
+ await testFrameCrash(1);
+ await testFrameCrash(4);
+});
+
+// This test checks that no notification shows when there is no minidump available. It
+// simulates the steps that occur during a crash, once with a dumpID and once without.
+add_task(async function test_nominidump() {
+ for (let dumpID of [null, "8888"]) {
+ let iframeBC = await openTestTabs(1);
+
+ let childID = iframeBC.currentWindowGlobal.domProcess.childID;
+
+ let notificationPromise;
+ if (dumpID) {
+ notificationPromise = BrowserTestUtils.waitForNotificationBar(
+ gBrowser,
+ gBrowser.selectedBrowser,
+ "subframe-crashed"
+ );
+ }
+
+ gBrowser.selectedBrowser.dispatchEvent(
+ new FrameCrashedEvent("oop-browser-crashed", {
+ browsingContextID: iframeBC,
+ childID,
+ isTopFrame: false,
+ bubbles: true,
+ })
+ );
+
+ let bag = Cc["@mozilla.org/hash-property-bag;1"].createInstance(
+ Ci.nsIWritablePropertyBag
+ );
+ bag.setProperty("abnormal", "true");
+ bag.setProperty("childID", iframeBC.currentWindowGlobal.domProcess.childID);
+ if (dumpID) {
+ bag.setProperty("dumpID", dumpID);
+ }
+
+ Services.obs.notifyObservers(bag, "ipc:content-shutdown");
+
+ await notificationPromise;
+ let notificationBox = gBrowser.getNotificationBox(gBrowser.selectedBrowser);
+ let notification = notificationBox.currentNotification;
+ ok(
+ dumpID ? notification : !notification,
+ "notification shown for browser with no minidump"
+ );
+
+ BrowserTestUtils.removeTab(gBrowser.selectedTab);
+ }
+});
diff --git a/dom/ipc/tests/browser_domainPolicy.js b/dom/ipc/tests/browser_domainPolicy.js
new file mode 100644
index 0000000000..988288f950
--- /dev/null
+++ b/dom/ipc/tests/browser_domainPolicy.js
@@ -0,0 +1,187 @@
+// This test waits for a lot of subframe loads, causing it to take a long time,
+// especially with Fission enabled.
+requestLongerTimeout(2);
+
+const BASE_FILE =
+ "http://mochi.test:8888/browser/dom/ipc/tests/file_domainPolicy_base.html";
+const SCRIPT_PATH = "/browser/dom/ipc/tests/file_disableScript.html";
+
+const TEST_POLICY = {
+ exceptions: ["http://test1.example.com", "http://example.com"],
+ superExceptions: ["http://test2.example.org", "https://test1.example.com"],
+ exempt: [
+ "http://test1.example.com",
+ "http://example.com",
+ "http://test2.example.org",
+ "http://sub1.test2.example.org",
+ "https://sub1.test1.example.com",
+ ],
+ notExempt: [
+ "http://test2.example.com",
+ "http://sub1.test1.example.com",
+ "http://www.example.com",
+ "https://test2.example.com",
+ "https://example.com",
+ "http://test1.example.org",
+ ],
+};
+
+// To make sure we never leave up an activated domain policy after a failed
+// test, let's make this global.
+var policy;
+
+function activateDomainPolicy(isBlock) {
+ policy = Services.scriptSecurityManager.activateDomainPolicy();
+
+ if (isBlock === undefined) {
+ return;
+ }
+
+ let set = isBlock ? policy.blocklist : policy.allowlist;
+ for (let e of TEST_POLICY.exceptions) {
+ set.add(makeURI(e));
+ }
+
+ let superSet = isBlock ? policy.superBlocklist : policy.superAllowlist;
+ for (let e of TEST_POLICY.superExceptions) {
+ superSet.add(makeURI(e));
+ }
+}
+
+function deactivateDomainPolicy() {
+ if (policy) {
+ policy.deactivate();
+ policy = null;
+ }
+}
+
+add_setup(async function () {
+ await SpecialPowers.pushPrefEnv({
+ set: [["browser.pagethumbnails.capturing_disabled", false]],
+ });
+
+ registerCleanupFunction(() => {
+ deactivateDomainPolicy();
+ });
+});
+
+add_task(async function test_domainPolicy() {
+ function test(testFunc, { activateFirst, isBlock }) {
+ if (activateFirst) {
+ activateDomainPolicy(isBlock);
+ }
+ return BrowserTestUtils.withNewTab(
+ {
+ gBrowser,
+ opening: BASE_FILE,
+ forceNewProcess: true,
+ },
+ async browser => {
+ if (!activateFirst) {
+ activateDomainPolicy(isBlock);
+ }
+ await testFunc(browser);
+ deactivateDomainPolicy();
+ }
+ );
+ }
+
+ async function testDomain(browser, domain, expectEnabled = false) {
+ function navigateFrame() {
+ let url = domain + SCRIPT_PATH;
+ return SpecialPowers.spawn(browser, [url], async src => {
+ let iframe = content.document.getElementById("root");
+ await new Promise(resolve => {
+ iframe.addEventListener("load", resolve, { once: true });
+ iframe.src = src;
+ });
+ return iframe.browsingContext;
+ });
+ }
+
+ function checkScriptEnabled(bc) {
+ return SpecialPowers.spawn(bc, [expectEnabled], enabled => {
+ content.wrappedJSObject.gFiredOnclick = false;
+ content.document.body.dispatchEvent(new content.Event("click"));
+ Assert.equal(
+ content.wrappedJSObject.gFiredOnclick,
+ enabled,
+ `Checking script-enabled for ${content.name} (${content.location})`
+ );
+ });
+ }
+
+ let browsingContext = await navigateFrame();
+ return checkScriptEnabled(browsingContext);
+ }
+
+ async function testList(browser, list, expectEnabled) {
+ // Run these sequentially to avoid navigating multiple domains at once.
+ for (let domain of list) {
+ await testDomain(browser, domain, expectEnabled);
+ }
+ }
+
+ info("1. Testing simple blocklist policy");
+
+ info("1A. Creating child process first, activating domainPolicy after");
+ await test(
+ async browser => {
+ policy.blocklist.add(Services.io.newURI("http://example.com"));
+ await testDomain(browser, "http://example.com");
+ },
+ { activateFirst: false }
+ );
+
+ info("1B. Activating domainPolicy first, creating child process after");
+ await test(
+ async browser => {
+ policy.blocklist.add(Services.io.newURI("http://example.com"));
+ await testDomain(browser, "http://example.com");
+ },
+ { activateFirst: true }
+ );
+
+ info("2. Testing Blocklist-style Domain Policy");
+
+ info("2A. Activating domainPolicy first, creating child process after");
+ await test(
+ async browser => {
+ await testList(browser, TEST_POLICY.notExempt, true);
+ await testList(browser, TEST_POLICY.exempt, false);
+ },
+ { activateFirst: true, isBlock: true }
+ );
+
+ info("2B. Creating child process first, activating domainPolicy after");
+ await test(
+ async browser => {
+ await testList(browser, TEST_POLICY.notExempt, true);
+ await testList(browser, TEST_POLICY.exempt, false);
+ },
+ { activateFirst: false, isBlock: true }
+ );
+
+ info("3. Testing Allowlist-style Domain Policy");
+ await SpecialPowers.pushPrefEnv({ set: [["javascript.enabled", false]] });
+
+ info("3A. Activating domainPolicy first, creating child process after");
+ await test(
+ async browser => {
+ await testList(browser, TEST_POLICY.notExempt, false);
+ await testList(browser, TEST_POLICY.exempt, true);
+ },
+ { activateFirst: true, isBlock: false }
+ );
+
+ info("3B. Creating child process first, activating domainPolicy after");
+ await test(
+ async browser => {
+ await testList(browser, TEST_POLICY.notExempt, false);
+ await testList(browser, TEST_POLICY.exempt, true);
+ },
+ { activateFirst: false, isBlock: false }
+ );
+
+ finish();
+});
diff --git a/dom/ipc/tests/browser_gc_schedule.js b/dom/ipc/tests/browser_gc_schedule.js
new file mode 100644
index 0000000000..8b44c98eae
--- /dev/null
+++ b/dom/ipc/tests/browser_gc_schedule.js
@@ -0,0 +1,379 @@
+/* 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/. */
+
+"use strict";
+
+const TEST_PAGE =
+ "http://mochi.test:8888/browser/dom/ipc/tests/file_dummy.html";
+
+async function waitForGCBegin() {
+ var waitTopic = "garbage-collector-begin";
+ var observer = {};
+
+ info("Waiting for " + waitTopic);
+ // This fixes a ReferenceError for Date, it's weird.
+ ok(Date.now(), "Date.now()");
+ var when = await new Promise(resolve => {
+ observer.observe = function (subject, topic, data) {
+ resolve(Date.now());
+ };
+
+ Services.obs.addObserver(observer, waitTopic);
+ });
+
+ Services.obs.removeObserver(observer, waitTopic);
+
+ // This delay attempts to make the time stamps unique.
+ do {
+ var now = Date.now();
+ } while (when + 5 > now);
+
+ return when;
+}
+
+async function waitForGCEnd() {
+ var waitTopic = "garbage-collector-end";
+ var observer = {};
+
+ info("Waiting for " + waitTopic);
+ // This fixes a ReferenceError for Date, it's weird.
+ ok(Date.now(), "Date.now()");
+ let when = await new Promise(resolve => {
+ observer.observe = function (subject, topic, data) {
+ resolve(Date.now());
+ };
+
+ Services.obs.addObserver(observer, waitTopic);
+ });
+
+ Services.obs.removeObserver(observer, waitTopic);
+
+ do {
+ var now = Date.now();
+ } while (when + 5 > now);
+
+ return when;
+}
+
+function getProcessID() {
+ return Services.appinfo.processID;
+}
+
+async function resolveInOrder(promisesAndStates) {
+ var order = [];
+ var promises = [];
+
+ for (let p of promisesAndStates) {
+ promises.push(
+ p.promise.then(when => {
+ info(`Tab: ${p.tab} did ${p.state}`);
+ order.push({ tab: p.tab, state: p.state, when });
+ })
+ );
+ }
+
+ await Promise.all(promises);
+
+ return order;
+}
+
+// Check that the list of events returned by resolveInOrder are in a
+// sensible order.
+function checkOneAtATime(events) {
+ var cur = null;
+ var lastWhen = null;
+
+ info("Checking order of events");
+ for (const e of events) {
+ ok(e.state === "begin" || e.state === "end", "event.state is good");
+ Assert.notStrictEqual(e.tab, undefined, "event.tab exists");
+
+ if (lastWhen) {
+ // We need these in sorted order so that the other checks here make
+ // sense.
+ Assert.lessOrEqual(
+ lastWhen,
+ e.when,
+ `Unsorted events, last: ${lastWhen}, this: ${e.when}`
+ );
+ }
+ lastWhen = e.when;
+
+ if (e.state === "begin") {
+ is(cur, null, `GC can begin on tab ${e.tab}`);
+ cur = e.tab;
+ } else {
+ is(e.tab, cur, `GC can end on tab ${e.tab}`);
+ cur = null;
+ }
+ }
+
+ is(cur, null, "No GC left running");
+}
+
+function checkAllCompleted(events, expectTabsCompleted) {
+ var tabsCompleted = events.filter(e => e.state === "end").map(e => e.tab);
+
+ for (var t of expectTabsCompleted) {
+ ok(tabsCompleted.includes(t), `Tab ${t} did a GC`);
+ }
+}
+
+async function setupTabsAndOneForForeground(num_tabs) {
+ ++num_tabs;
+ var pids = [];
+
+ const parent_pid = getProcessID();
+ info("Parent process PID is " + parent_pid);
+
+ const tabs = await Promise.all(
+ Array(num_tabs)
+ .fill()
+ .map(_ => {
+ return BrowserTestUtils.openNewForegroundTab({
+ gBrowser,
+ opening: TEST_PAGE,
+ forceNewProcess: true,
+ });
+ })
+ );
+
+ for (const [i, tab] of Object.entries(tabs)) {
+ const tab_pid = await SpecialPowers.spawn(
+ tab.linkedBrowser,
+ [],
+ getProcessID
+ );
+
+ info(`Tab ${i} pid is ${tab_pid}`);
+ isnot(parent_pid, tab_pid, `Tab ${i} is in content process`);
+ ok(!pids.includes(tab_pid), `Tab ${i} is in a distinct process`);
+
+ pids.push(tab_pid);
+ }
+
+ // Since calling openNewForegroundTab several times in a row doesn't update
+ // process priorities correctly, we need to explicitly switch tabs.
+ for (let tab of tabs) {
+ await BrowserTestUtils.switchTab(gBrowser, tab);
+ }
+
+ return tabs;
+}
+
+function doContentRunNextCollectionTimer() {
+ content.windowUtils.pokeGC("PAGE_HIDE");
+ content.windowUtils.runNextCollectorTimer("PAGE_HIDE");
+}
+
+function startNextCollection(
+ tab,
+ tab_num,
+ waits,
+ fn = doContentRunNextCollectionTimer
+) {
+ var browser = tab.linkedBrowser;
+
+ // Finish any currently running GC.
+ SpecialPowers.spawn(browser, [], () => {
+ SpecialPowers.Cu.getJSTestingFunctions().finishgc();
+ });
+
+ if (tab.selected) {
+ // One isn't expected to use the return value with foreground tab!
+ return {};
+ }
+
+ var waitBegin = SpecialPowers.spawn(browser, [], waitForGCBegin);
+ var waitEnd = SpecialPowers.spawn(browser, [], waitForGCEnd);
+ waits.push({ promise: waitBegin, tab: tab_num, state: "begin" });
+ waits.push({ promise: waitEnd, tab: tab_num, state: "end" });
+
+ SpecialPowers.spawn(browser, [], fn);
+
+ // Return these so that the abort GC test can wait for the begin.
+ return { waitBegin, waitEnd };
+}
+
+add_task(async function gcOneAtATime() {
+ SpecialPowers.pushPrefEnv({
+ set: [["javascript.options.concurrent_multiprocess_gcs.max", 1]],
+ });
+
+ const num_tabs = 12;
+ var tabs = await setupTabsAndOneForForeground(num_tabs);
+
+ info("Tabs ready, Asking for GCs");
+ var waits = [];
+ for (var i = 0; i < num_tabs; i++) {
+ startNextCollection(tabs[i], i, waits);
+ }
+
+ let order = await resolveInOrder(waits);
+ // We need these in the order they actually occurred, so far that's how
+ // they're returned, but we'll sort them to be sure.
+ order.sort((e1, e2) => e1.when - e2.when);
+ checkOneAtATime(order);
+ checkAllCompleted(
+ order,
+ Array.from({ length: num_tabs }, (_, n) => n)
+ );
+
+ for (var tab of tabs) {
+ BrowserTestUtils.removeTab(tab);
+ }
+
+ SpecialPowers.popPrefEnv();
+});
+
+add_task(async function gcAbort() {
+ SpecialPowers.pushPrefEnv({
+ set: [["javascript.options.concurrent_multiprocess_gcs.max", 1]],
+ });
+
+ const num_tabs = 2;
+ var tabs = await setupTabsAndOneForForeground(num_tabs);
+
+ info("Tabs ready, Asking for GCs");
+ var waits = [];
+
+ var tab0Waits = startNextCollection(tabs[0], 0, waits, () => {
+ SpecialPowers.Cu.getJSTestingFunctions().gcslice(1);
+ });
+ await tab0Waits.waitBegin;
+
+ // Tab 0 has started a GC. Now we schedule a GC in tab one. It must not
+ // begin yet (but we don't check that, gcOneAtATime is assumed to check
+ // this.
+ startNextCollection(tabs[1], 1, waits);
+
+ // Request that tab 0 abort, this test checks that tab 1 can now begin.
+ SpecialPowers.spawn(tabs[0].linkedBrowser, [], () => {
+ SpecialPowers.Cu.getJSTestingFunctions().abortgc();
+ });
+
+ let order = await resolveInOrder(waits);
+ // We need these in the order they actually occurred, so far that's how
+ // they're returned, but we'll sort them to be sure.
+ order.sort((e1, e2) => e1.when - e2.when);
+ checkOneAtATime(order);
+ checkAllCompleted(
+ order,
+ Array.from({ length: num_tabs }, (_, n) => n)
+ );
+
+ for (var tab of tabs) {
+ BrowserTestUtils.removeTab(tab);
+ }
+
+ SpecialPowers.popPrefEnv();
+});
+
+add_task(async function gcJSInitiatedDuring() {
+ SpecialPowers.pushPrefEnv({
+ set: [["javascript.options.concurrent_multiprocess_gcs.max", 1]],
+ });
+
+ const num_tabs = 3;
+ var tabs = await setupTabsAndOneForForeground(num_tabs);
+
+ info("Tabs ready, Asking for GCs");
+ var waits = [];
+
+ // Start a GC on tab 0 to consume the scheduler's "token". Zeal mode 10
+ // will cause it to run in many slices.
+ var tab0Waits = startNextCollection(tabs[0], 0, waits, () => {
+ if (SpecialPowers.Cu.getJSTestingFunctions().gczeal) {
+ SpecialPowers.Cu.getJSTestingFunctions().gczeal(10);
+ }
+ SpecialPowers.Cu.getJSTestingFunctions().gcslice(1);
+ });
+ await tab0Waits.waitBegin;
+ info("GC on tab 0 has begun");
+
+ // Request a GC in tab 1, this will be blocked by the ongoing GC in tab 0.
+ var tab1Waits = startNextCollection(tabs[1], 1, waits);
+
+ // Force a GC to start in tab 1. This won't wait for tab 0.
+ SpecialPowers.spawn(tabs[1].linkedBrowser, [], () => {
+ SpecialPowers.Cu.getJSTestingFunctions().gcslice(1);
+ });
+
+ await tab1Waits.waitBegin;
+ info("GC on tab 1 has begun");
+
+ // The GC in tab 0 should still be running.
+ var state = await SpecialPowers.spawn(tabs[0].linkedBrowser, [], () => {
+ return SpecialPowers.Cu.getJSTestingFunctions().gcstate();
+ });
+ info("State of Tab 0 GC is " + state);
+ isnot(state, "NotActive", "GC is active in tab 0");
+
+ // Let the GCs complete, verify that a GC in a 3rd tab can acquire a token.
+ startNextCollection(tabs[2], 2, waits);
+
+ let order = await resolveInOrder(waits);
+ info("All GCs finished");
+ checkAllCompleted(
+ order,
+ Array.from({ length: num_tabs }, (_, n) => n)
+ );
+
+ for (var tab of tabs) {
+ BrowserTestUtils.removeTab(tab);
+ }
+
+ SpecialPowers.popPrefEnv();
+});
+
+add_task(async function gcJSInitiatedBefore() {
+ SpecialPowers.pushPrefEnv({
+ set: [["javascript.options.concurrent_multiprocess_gcs.max", 1]],
+ });
+
+ const num_tabs = 8;
+ var tabs = await setupTabsAndOneForForeground(num_tabs);
+
+ info("Tabs ready");
+ var waits = [];
+
+ // Start a GC on tab 0 to consume the scheduler's first "token". Zeal mode 10
+ // will cause it to run in many slices.
+ info("Force a JS-initiated GC in tab 0");
+ var tab0Waits = startNextCollection(tabs[0], 0, waits, () => {
+ if (SpecialPowers.Cu.getJSTestingFunctions().gczeal) {
+ SpecialPowers.Cu.getJSTestingFunctions().gczeal(10);
+ }
+ SpecialPowers.Cu.getJSTestingFunctions().gcslice(1);
+ });
+ await tab0Waits.waitBegin;
+
+ info("Request GCs in remaining tabs");
+ for (var i = 1; i < num_tabs; i++) {
+ startNextCollection(tabs[i], i, waits);
+ }
+
+ // The GC in tab 0 should still be running.
+ var state = await SpecialPowers.spawn(tabs[0].linkedBrowser, [], () => {
+ return SpecialPowers.Cu.getJSTestingFunctions().gcstate();
+ });
+ info("State is " + state);
+ isnot(state, "NotActive", "GC is active in tab 0");
+
+ let order = await resolveInOrder(waits);
+ // We need these in the order they actually occurred, so far that's how
+ // they're returned, but we'll sort them to be sure.
+ order.sort((e1, e2) => e1.when - e2.when);
+ checkOneAtATime(order);
+ checkAllCompleted(
+ order,
+ Array.from({ length: num_tabs }, (_, n) => n)
+ );
+
+ for (var tab of tabs) {
+ BrowserTestUtils.removeTab(tab);
+ }
+
+ SpecialPowers.popPrefEnv();
+});
diff --git a/dom/ipc/tests/browser_hide_tooltip.js b/dom/ipc/tests/browser_hide_tooltip.js
new file mode 100644
index 0000000000..1b7f7c56b9
--- /dev/null
+++ b/dom/ipc/tests/browser_hide_tooltip.js
@@ -0,0 +1,38 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function test_hiding_tooltip() {
+ let page1 = "data:text/html,<html title='title'><body>page 1<body></html>";
+ let page2 = "data:text/html,<html><body>page 2</body></html>";
+
+ let tab = await BrowserTestUtils.openNewForegroundTab({
+ gBrowser,
+ opening: page1,
+ });
+
+ let popup = new Promise(function (resolve) {
+ window.addEventListener("popupshown", resolve, { once: true });
+ });
+ // Fire a mousemove to trigger the tooltip.
+ EventUtils.synthesizeMouseAtCenter(gBrowser.selectedBrowser, {
+ type: "mousemove",
+ });
+ await popup;
+
+ let hiding = new Promise(function (resolve) {
+ window.addEventListener("popuphiding", resolve, { once: true });
+ });
+ let loaded = BrowserTestUtils.browserLoaded(
+ gBrowser.selectedBrowser,
+ false,
+ page2
+ );
+ BrowserTestUtils.startLoadingURIString(gBrowser, page2);
+ await loaded;
+ await hiding;
+
+ ok(true, "Should have hidden the tooltip");
+ BrowserTestUtils.removeTab(tab);
+});
diff --git a/dom/ipc/tests/browser_layers_unloaded_while_interruptingJS.js b/dom/ipc/tests/browser_layers_unloaded_while_interruptingJS.js
new file mode 100644
index 0000000000..9903055858
--- /dev/null
+++ b/dom/ipc/tests/browser_layers_unloaded_while_interruptingJS.js
@@ -0,0 +1,32 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function test_check_layers_cleared() {
+ let initialTab = gBrowser.selectedTab;
+ await BrowserTestUtils.withNewTab("about:blank", async browser => {
+ await ContentTask.spawn(browser, null, () => {
+ return new Promise(resolve => {
+ content.requestAnimationFrame(() => {
+ content.setTimeout(
+ "let start = performance.now(); while (performance.now() < start + 5000);"
+ );
+ resolve();
+ });
+ });
+ });
+ let layersCleared = BrowserTestUtils.waitForEvent(
+ window,
+ "MozLayerTreeCleared"
+ );
+ let startWaiting = performance.now();
+ await BrowserTestUtils.switchTab(gBrowser, initialTab);
+ await layersCleared;
+ Assert.less(
+ performance.now(),
+ startWaiting + 2000,
+ "MozLayerTreeCleared should be dispatched while the script is still running"
+ );
+ });
+});
diff --git a/dom/ipc/tests/browser_memory_distribution_telemetry.js b/dom/ipc/tests/browser_memory_distribution_telemetry.js
new file mode 100644
index 0000000000..f4568fe05f
--- /dev/null
+++ b/dom/ipc/tests/browser_memory_distribution_telemetry.js
@@ -0,0 +1,93 @@
+"use strict";
+
+const { TelemetrySession } = ChromeUtils.importESModule(
+ "resource://gre/modules/TelemetrySession.sys.mjs"
+);
+
+const DUMMY_PAGE_DATA_URI = `data:text/html,
+ <html>
+ <head>
+ <meta charset="utf-8"/>
+ <title>Dummy</title>
+ </head>
+ <body>
+ <h1 id='header'>Just a regular everyday normal page.</h1>
+ </body>
+ </html>`;
+
+/**
+ * Tests the MEMORY_DISTRIBUTION_AMONG_CONTENT probe by opening a few tabs, then triggering
+ * the memory probes and waiting for the "gather-memory-telemetry-finished" notification.
+ */
+add_task(async function test_memory_distribution() {
+ waitForExplicitFinish();
+
+ if (SpecialPowers.getIntPref("dom.ipc.processCount", 1) < 2) {
+ ok(true, "Skip this test if e10s-multi is disabled.");
+ finish();
+ return;
+ }
+
+ Services.telemetry.canRecordExtended = true;
+
+ let histogram = Services.telemetry.getKeyedHistogramById(
+ "MEMORY_DISTRIBUTION_AMONG_CONTENT"
+ );
+ histogram.clear();
+
+ let tab1 = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ DUMMY_PAGE_DATA_URI
+ );
+ let tab2 = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ DUMMY_PAGE_DATA_URI
+ );
+ let tab3 = await BrowserTestUtils.openNewForegroundTab(
+ gBrowser,
+ DUMMY_PAGE_DATA_URI
+ );
+
+ let finishedGathering = new Promise(resolve => {
+ let obs = function () {
+ Services.obs.removeObserver(obs, "gather-memory-telemetry-finished");
+ resolve();
+ };
+ Services.obs.addObserver(obs, "gather-memory-telemetry-finished");
+ });
+
+ TelemetrySession.getPayload();
+
+ await finishedGathering;
+
+ let s = histogram.snapshot();
+ ok("0 - 10 tabs" in s, "We should have some samples by now in this bucket.");
+ for (var key in s) {
+ is(key, "0 - 10 tabs");
+ let fewTabsSnapshot = s[key];
+ Assert.greater(
+ fewTabsSnapshot.sum,
+ 0,
+ "Zero difference between all the content processes is unlikely, what happened?"
+ );
+ Assert.less(
+ fewTabsSnapshot.sum,
+ 80,
+ "20 percentage difference on average is unlikely, what happened?"
+ );
+ let values = fewTabsSnapshot.values;
+ for (let [bucket, value] of Object.entries(values)) {
+ if (bucket >= 10) {
+ // If this check fails it means that one of the content processes uses at least 20% more or 20% less than the mean.
+ is(value, 0, "All the buckets above 10 should be empty");
+ }
+ }
+ }
+
+ histogram.clear();
+
+ BrowserTestUtils.removeTab(tab3);
+ BrowserTestUtils.removeTab(tab2);
+ BrowserTestUtils.removeTab(tab1);
+ finish();
+});
diff --git a/dom/ipc/tests/browser_pbrowser_creation_failure.js b/dom/ipc/tests/browser_pbrowser_creation_failure.js
new file mode 100644
index 0000000000..d4f3b8fdd5
--- /dev/null
+++ b/dom/ipc/tests/browser_pbrowser_creation_failure.js
@@ -0,0 +1,57 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+add_task(async function test_subframe_pbrowser_creation_failure() {
+ await BrowserTestUtils.withNewTab(
+ "https://example.com/document-builder.sjs?html=<iframe></iframe>",
+ async browser => {
+ let bcid = await SpecialPowers.spawn(browser, [], () => {
+ return content.document.body.querySelector("iframe").browsingContext.id;
+ });
+
+ // We currently have no known way to trigger PBrowser creation failure,
+ // other than to use this custom pref for the purpose.
+ info(`enabling failPBrowserCreation for browsingContext: ${bcid}`);
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.tabs.remote.testOnly.failPBrowserCreation.enabled", true],
+ [
+ "browser.tabs.remote.testOnly.failPBrowserCreation.browsingContext",
+ `${bcid}`,
+ ],
+ ],
+ });
+
+ let eventFiredPromise = BrowserTestUtils.waitForEvent(
+ browser,
+ "oop-browser-crashed"
+ );
+
+ info("triggering navigation which will fail pbrowser creation");
+ await SpecialPowers.spawn(browser, [], () => {
+ content.document.body.querySelector("iframe").src =
+ "https://example.org/document-builder.sjs?html=frame";
+ });
+
+ info("Waiting for oop-browser-crashed event.");
+ let event = await eventFiredPromise;
+ ok(!event.isTopFrame, "should be reporting subframe crash");
+ Assert.equal(
+ event.childID,
+ 0,
+ "childID should be zero, as no process actually crashed"
+ );
+ is(event.browsingContextId, bcid, "bcid should match");
+
+ let { subject: windowGlobal } = await BrowserUtils.promiseObserved(
+ "window-global-created",
+ wgp => wgp.documentURI.spec.startsWith("about:framecrashed")
+ );
+ is(windowGlobal.browsingContext.id, bcid, "bcid is correct");
+
+ await SpecialPowers.popPrefEnv();
+ }
+ );
+});
diff --git a/dom/ipc/tests/browser_subframesPreferUsed.js b/dom/ipc/tests/browser_subframesPreferUsed.js
new file mode 100644
index 0000000000..f2f9ed2593
--- /dev/null
+++ b/dom/ipc/tests/browser_subframesPreferUsed.js
@@ -0,0 +1,82 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+ok(
+ Services.appinfo.fissionAutostart,
+ "this test requires fission to function!"
+);
+
+function documentURL(origin, html) {
+ let params = new URLSearchParams();
+ params.append("html", html.trim());
+ return `${origin}/document-builder.sjs?${params.toString()}`;
+}
+
+async function singleTest(preferUsed) {
+ info(`running test with preferUsed=${preferUsed}`);
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.ipc.processCount.webIsolated", 4],
+ ["browser.tabs.remote.subframesPreferUsed", preferUsed],
+ ],
+ });
+
+ const TEST_URL = documentURL(
+ "https://example.com",
+ `<iframe src=${JSON.stringify(
+ documentURL("https://example.org", `<h1>iframe</h1>`)
+ )}></iframe>`
+ );
+
+ await BrowserTestUtils.withNewTab(TEST_URL, async browser1 => {
+ is(browser1.browsingContext.children.length, 1);
+ let topProc1 = browser1.browsingContext.currentWindowGlobal.domProcess;
+ let frameProc1 =
+ browser1.browsingContext.children[0].currentWindowGlobal.domProcess;
+ isnot(
+ topProc1.childID,
+ frameProc1.childID,
+ "the frame should be in a separate process"
+ );
+
+ await BrowserTestUtils.withNewTab(TEST_URL, async browser2 => {
+ is(browser2.browsingContext.children.length, 1);
+ let topProc2 = browser2.browsingContext.currentWindowGlobal.domProcess;
+ let frameProc2 =
+ browser2.browsingContext.children[0].currentWindowGlobal.domProcess;
+ isnot(
+ topProc2.childID,
+ frameProc2.childID,
+ "the frame should be in a separate process"
+ );
+
+ // Compare processes used for the two tabs.
+ isnot(
+ topProc1.childID,
+ topProc2.childID,
+ "the toplevel windows should be loaded in separate processes"
+ );
+ if (preferUsed) {
+ is(
+ frameProc1.childID,
+ frameProc2.childID,
+ "the iframes should load in the same process with subframesPreferUsed"
+ );
+ } else {
+ isnot(
+ frameProc1.childID,
+ frameProc2.childID,
+ "the iframes should load in different processes without subframesPreferUsed"
+ );
+ }
+ });
+ });
+}
+
+add_task(async function test_preferUsed() {
+ await singleTest(true);
+});
+
+add_task(async function test_noPreferUsed() {
+ await singleTest(false);
+});
diff --git a/dom/ipc/tests/browser_very_fission.js b/dom/ipc/tests/browser_very_fission.js
new file mode 100644
index 0000000000..582fe00133
--- /dev/null
+++ b/dom/ipc/tests/browser_very_fission.js
@@ -0,0 +1,38 @@
+/* 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/. */
+
+"use strict";
+
+// This test creates a large number of content processes as a
+// regression test for bug 1635451.
+
+const TEST_PAGE =
+ "http://mochi.test:8888/browser/dom/ipc/tests/file_dummy.html";
+
+const NUM_TABS = 256;
+
+add_task(async () => {
+ let promises = [];
+ for (let i = 0; i < NUM_TABS; ++i) {
+ promises.push(
+ BrowserTestUtils.openNewForegroundTab({
+ gBrowser,
+ opening: TEST_PAGE,
+ waitForLoad: true,
+ forceNewProcess: true,
+ })
+ );
+ }
+
+ let tabs = [];
+ for (const p of promises) {
+ tabs.push(await p);
+ }
+
+ ok(true, "All of the tabs loaded");
+
+ for (const t of tabs) {
+ BrowserTestUtils.removeTab(t);
+ }
+});
diff --git a/dom/ipc/tests/browser_wpi_base.js b/dom/ipc/tests/browser_wpi_base.js
new file mode 100644
index 0000000000..7a01c9a161
--- /dev/null
+++ b/dom/ipc/tests/browser_wpi_base.js
@@ -0,0 +1,305 @@
+// This test is fission-only! Make that clear before continuing, to avoid
+// confusing failures.
+ok(
+ Services.appinfo.fissionAutostart,
+ "this test requires fission to function!"
+);
+
+requestLongerTimeout(2);
+
+const WebContentIsolationStrategy = {
+ IsolateNothing: 0,
+ IsolateEverything: 1,
+ IsolateHighValue: 2,
+};
+
+const COM_ORIGIN = "https://example.com";
+const ORG_ORIGIN = "https://example.org";
+const MOZ_ORIGIN = "https://www.mozilla.org";
+
+// Helper for building document-builder.sjs URLs which have specific headers &
+// HTML content.
+function documentURL(origin, headers, html) {
+ let params = new URLSearchParams();
+ params.append("html", html.trim());
+ for (const [key, value] of Object.entries(headers)) {
+ params.append("headers", `${key}:${value}`);
+ }
+ return `${origin}/document-builder.sjs?${params.toString()}`;
+}
+
+async function testTreeRemoteTypes(name, testpage) {
+ // Use document-builder.sjs to build up the expected document tree.
+ function buildURL(path, page) {
+ let html = `<h1>${path}</h1>`;
+ for (let i = 0; i < page.children.length; ++i) {
+ const inner = buildURL(`${path}[${i}]`, page.children[i]);
+ html += `<iframe src=${JSON.stringify(inner)}></iframe>`;
+ }
+ return documentURL(page.origin, page.headers, html);
+ }
+ const url = buildURL(name, testpage);
+
+ // Load the tab and confirm that properties of the loaded documents match
+ // expectation.
+ await BrowserTestUtils.withNewTab(url, async browser => {
+ let stack = [
+ {
+ path: name,
+ bc: browser.browsingContext,
+ ...testpage,
+ },
+ ];
+
+ while (stack.length) {
+ const { path, bc, remoteType, children, origin } = stack.pop();
+ is(
+ Services.scriptSecurityManager.createContentPrincipal(
+ bc.currentWindowGlobal.documentURI,
+ {}
+ ).originNoSuffix,
+ origin,
+ `Frame ${path} has expected originNoSuffix`
+ );
+ is(
+ bc.currentWindowGlobal.domProcess.remoteType,
+ remoteType,
+ `Frame ${path} has expected remote type`
+ );
+ is(
+ bc.children.length,
+ children.length,
+ `Frame ${path} has the expected number of children`
+ );
+ for (let i = 0; i < bc.children.length; ++i) {
+ stack.push({
+ path: `${path}[${i}]`,
+ bc: bc.children[i],
+ ...children[i],
+ });
+ }
+ }
+ });
+}
+
+function mkTestPage({
+ comRemoteType,
+ orgRemoteType,
+ mozRemoteType,
+ topOrigin,
+ topHeaders = {},
+ frameHeaders = {},
+}) {
+ const topRemoteType = {
+ [COM_ORIGIN]: comRemoteType,
+ [ORG_ORIGIN]: orgRemoteType,
+ [MOZ_ORIGIN]: mozRemoteType,
+ }[topOrigin];
+
+ const innerChildren = [
+ {
+ origin: COM_ORIGIN,
+ headers: frameHeaders,
+ remoteType: comRemoteType,
+ children: [],
+ },
+ {
+ origin: ORG_ORIGIN,
+ headers: frameHeaders,
+ remoteType: orgRemoteType,
+ children: [],
+ },
+ {
+ origin: MOZ_ORIGIN,
+ headers: frameHeaders,
+ remoteType: mozRemoteType,
+ children: [],
+ },
+ ];
+
+ return {
+ origin: topOrigin,
+ headers: topHeaders,
+ remoteType: topRemoteType,
+ children: [
+ {
+ origin: COM_ORIGIN,
+ headers: frameHeaders,
+ remoteType: comRemoteType,
+ children: [...innerChildren],
+ },
+ {
+ origin: ORG_ORIGIN,
+ headers: frameHeaders,
+ remoteType: orgRemoteType,
+ children: [...innerChildren],
+ },
+ {
+ origin: MOZ_ORIGIN,
+ headers: frameHeaders,
+ remoteType: mozRemoteType,
+ children: [...innerChildren],
+ },
+ ],
+ };
+}
+
+const heuristics = [
+ {
+ name: "coop",
+ setup_com: async expected => {
+ // Set the COOP header, and load
+ await testTreeRemoteTypes(
+ "com_set_coop",
+ mkTestPage({
+ topOrigin: COM_ORIGIN,
+ topHeaders: { "Cross-Origin-Opener-Policy": "same-origin" },
+ comRemoteType: expected.com_high,
+ orgRemoteType: expected.org_normal,
+ mozRemoteType: expected.moz_normal,
+ })
+ );
+ },
+ run_extra_test: async expected => {
+ // Load with both the COOP and COEP headers set.
+ await testTreeRemoteTypes(
+ "com_coop_coep",
+ mkTestPage({
+ topOrigin: COM_ORIGIN,
+ topHeaders: {
+ "Cross-Origin-Opener-Policy": "same-origin",
+ "Cross-Origin-Embedder-Policy": "require-corp",
+ },
+ frameHeaders: {
+ "Cross-Origin-Embedder-Policy": "require-corp",
+ "Cross-Origin-Resource-Policy": "cross-origin",
+ },
+ comRemoteType: expected.com_coop_coep,
+ orgRemoteType: expected.org_coop_coep,
+ mozRemoteType: expected.moz_coop_coep,
+ })
+ );
+ },
+ },
+ {
+ name: "hasSavedLogin",
+ setup_com: async expected => {
+ // add .com to the password manager
+ let LoginInfo = new Components.Constructor(
+ "@mozilla.org/login-manager/loginInfo;1",
+ Ci.nsILoginInfo,
+ "init"
+ );
+ await Services.logins.addLoginAsync(
+ new LoginInfo(COM_ORIGIN, "", null, "username", "password", "", "")
+ );
+
+ // Init login detection service to trigger fetching logins
+ let loginDetection = Cc[
+ "@mozilla.org/login-detection-service;1"
+ ].createInstance(Ci.nsILoginDetectionService);
+ loginDetection.init();
+
+ await TestUtils.waitForCondition(() => {
+ let x = loginDetection.isLoginsLoaded();
+ return x;
+ }, "waiting for loading logins from the password manager");
+ },
+ },
+ {
+ name: "isLoggedIn",
+ setup_com: async expected => {
+ let p = new Promise(resolve => {
+ Services.obs.addObserver(function obs() {
+ Services.obs.removeObserver(
+ obs,
+ "passwordmgr-form-submission-detected"
+ );
+ resolve();
+ }, "passwordmgr-form-submission-detected");
+ });
+
+ const TEST_URL = documentURL(
+ COM_ORIGIN,
+ {},
+ `<form>
+ <input value="username">
+ <input type="password" value="password">
+ <input type="submit">
+ </form>`
+ );
+
+ // submit the form to simulate the login behavior
+ await BrowserTestUtils.withNewTab(TEST_URL, async browser => {
+ await SpecialPowers.spawn(browser, [], async () => {
+ content.document.querySelector("form").submit();
+ });
+ });
+ await p;
+ },
+ },
+];
+
+async function do_tests(expected) {
+ for (let heuristic of heuristics) {
+ info(`Starting ${heuristic.name} test`);
+ // Clear all site-specific data, as we don't want to have any high-value site
+ // permissions from any previous iterations.
+ await new Promise(resolve =>
+ Services.clearData.deleteData(Ci.nsIClearDataService.CLEAR_ALL, resolve)
+ );
+
+ // Loads for basic URLs with no special headers set.
+ await testTreeRemoteTypes(
+ "basic_com",
+ mkTestPage({
+ topOrigin: COM_ORIGIN,
+ comRemoteType: expected.com_normal,
+ orgRemoteType: expected.org_normal,
+ mozRemoteType: expected.moz_normal,
+ })
+ );
+
+ await testTreeRemoteTypes(
+ "basic_org",
+ mkTestPage({
+ topOrigin: ORG_ORIGIN,
+ comRemoteType: expected.com_normal,
+ orgRemoteType: expected.org_normal,
+ mozRemoteType: expected.moz_normal,
+ })
+ );
+
+ info(`Setting up ${heuristic.name} test`);
+ await heuristic.setup_com(expected);
+
+ // Load again after the heuristic is triggered
+ info(`Running ${heuristic.name} tests after setup`);
+ await testTreeRemoteTypes(
+ `com_after_${heuristic.name}`,
+ mkTestPage({
+ topOrigin: COM_ORIGIN,
+ comRemoteType: expected.com_high,
+ orgRemoteType: expected.org_normal,
+ mozRemoteType: expected.moz_normal,
+ })
+ );
+
+ // Load again with a .org toplevel
+ await testTreeRemoteTypes(
+ `org_after_${heuristic.name}`,
+ mkTestPage({
+ topOrigin: ORG_ORIGIN,
+ comRemoteType: expected.com_high,
+ orgRemoteType: expected.org_normal,
+ mozRemoteType: expected.moz_normal,
+ })
+ );
+
+ // Run heuristic dependent tests
+ if (heuristic.run_extra_test) {
+ info(`Running extra tests for ${heuristic.name}`);
+ await heuristic.run_extra_test(expected);
+ }
+ }
+}
diff --git a/dom/ipc/tests/browser_wpi_isolate_everything.js b/dom/ipc/tests/browser_wpi_isolate_everything.js
new file mode 100644
index 0000000000..e902fec9d0
--- /dev/null
+++ b/dom/ipc/tests/browser_wpi_isolate_everything.js
@@ -0,0 +1,27 @@
+// Import this in order to use `do_tests()`.
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/dom/ipc/tests/browser_wpi_base.js",
+ this
+);
+
+add_task(async function test_isolate_everything() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.tabs.remote.separatedMozillaDomains", "mozilla.org"],
+ [
+ "fission.webContentIsolationStrategy",
+ WebContentIsolationStrategy.IsolateEverything,
+ ],
+ ],
+ });
+
+ await do_tests({
+ com_normal: "webIsolated=https://example.com",
+ org_normal: "webIsolated=https://example.org",
+ moz_normal: "privilegedmozilla",
+ com_high: "webIsolated=https://example.com",
+ com_coop_coep: "webCOOP+COEP=https://example.com",
+ org_coop_coep: "webCOOP+COEP=https://example.org",
+ moz_coop_coep: "privilegedmozilla",
+ });
+});
diff --git a/dom/ipc/tests/browser_wpi_isolate_high_value.js b/dom/ipc/tests/browser_wpi_isolate_high_value.js
new file mode 100644
index 0000000000..bf6b99d5f5
--- /dev/null
+++ b/dom/ipc/tests/browser_wpi_isolate_high_value.js
@@ -0,0 +1,27 @@
+// Import this in order to use `do_tests()`.
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/dom/ipc/tests/browser_wpi_base.js",
+ this
+);
+
+add_task(async function test_isolate_high_value() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.tabs.remote.separatedMozillaDomains", "mozilla.org"],
+ [
+ "fission.webContentIsolationStrategy",
+ WebContentIsolationStrategy.IsolateHighValue,
+ ],
+ ],
+ });
+
+ await do_tests({
+ com_normal: "web",
+ org_normal: "web",
+ moz_normal: "privilegedmozilla",
+ com_high: "webIsolated=https://example.com",
+ com_coop_coep: "webCOOP+COEP=https://example.com",
+ org_coop_coep: "webCOOP+COEP=https://example.org",
+ moz_coop_coep: "privilegedmozilla",
+ });
+});
diff --git a/dom/ipc/tests/browser_wpi_isolate_nothing.js b/dom/ipc/tests/browser_wpi_isolate_nothing.js
new file mode 100644
index 0000000000..afd5e51640
--- /dev/null
+++ b/dom/ipc/tests/browser_wpi_isolate_nothing.js
@@ -0,0 +1,27 @@
+// Import this in order to use `do_tests()`.
+Services.scriptloader.loadSubScript(
+ "chrome://mochitests/content/browser/dom/ipc/tests/browser_wpi_base.js",
+ this
+);
+
+add_task(async function test_isolate_nothing() {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["browser.tabs.remote.separatedMozillaDomains", "mozilla.org"],
+ [
+ "fission.webContentIsolationStrategy",
+ WebContentIsolationStrategy.IsolateNothing,
+ ],
+ ],
+ });
+
+ await do_tests({
+ com_normal: "web",
+ org_normal: "web",
+ moz_normal: "privilegedmozilla",
+ com_high: "web",
+ com_coop_coep: "webCOOP+COEP=https://example.com",
+ org_coop_coep: "webCOOP+COEP=https://example.org",
+ moz_coop_coep: "privilegedmozilla",
+ });
+});
diff --git a/dom/ipc/tests/chrome.toml b/dom/ipc/tests/chrome.toml
new file mode 100644
index 0000000000..79f3e2fb22
--- /dev/null
+++ b/dom/ipc/tests/chrome.toml
@@ -0,0 +1,6 @@
+[DEFAULT]
+skip-if = ["os == 'android'"]
+support-files = ["process_error.xhtml"]
+
+["test_process_error.xhtml"]
+skip-if = ["!crashreporter"]
diff --git a/dom/ipc/tests/file_broadcast_currenturi_onload.html b/dom/ipc/tests/file_broadcast_currenturi_onload.html
new file mode 100644
index 0000000000..b92c46c944
--- /dev/null
+++ b/dom/ipc/tests/file_broadcast_currenturi_onload.html
@@ -0,0 +1,66 @@
+
+<!doctype html>
+<body>
+<script>
+
+const url = new URL(location.href);
+
+// Create a popup to broadcast the load's completion to the test document.
+//
+// NOTE: We're using a popup to ensure that the new document has the same origin
+// (http://mochi.test:8888/) as the original test document, so that we can use a
+// BroadcastChannel to communicate with the test page. We can't use an iframe as
+// the mixed content blocker will prevent embedding this URL in https://
+// documents.
+function sendPayload(payload) {
+ let broadcastURL = new URL(url.pathname, "http://mochi.test:8888/");
+ broadcastURL.search = "?payload=" + encodeURIComponent(JSON.stringify(payload));
+ window.open(broadcastURL.href);
+}
+
+async function getURIs() {
+ // Run the test and fetch the relevant information.
+ const browsingContext = SpecialPowers.wrap(window).browsingContext;
+ let [docURI, curURI] = await SpecialPowers.spawnChrome(
+ [browsingContext.id], async id => {
+ let bc = BrowsingContext.get(id);
+ return [
+ bc.currentWindowGlobal.documentURI.spec,
+ bc.currentURI.spec,
+ ];
+ }
+ );
+ return { location: location.href, docURI, curURI };
+}
+
+addEventListener("load", async e => {
+ // If a payload parameter was included, just send the message.
+ const payloadStr = url.searchParams.get("payload");
+ if (payloadStr) {
+ const chan = new BroadcastChannel("test_broadcast_onload");
+ chan.postMessage(JSON.parse(payloadStr));
+ window.close();
+ return;
+ }
+
+ // collect the initial set of URIs
+ const result1 = await getURIs();
+
+ const pushstateURL = new URL("after_pushstate", url.href);
+ history.pushState({}, "After PushState!", pushstateURL.href);
+ await new Promise(resolve => setTimeout(resolve, 0));
+
+ // Collect the set of URIs after pushstate
+ const result2 = await getURIs();
+
+ window.location.hash = "#after_hashchange";
+ await new Promise(resolve => setTimeout(resolve, 0));
+
+ // Collect the set of URIs after a hash change
+ const result3 = await getURIs();
+
+ sendPayload([result1, result2, result3]);
+ window.close();
+});
+</script>
+</body>
diff --git a/dom/ipc/tests/file_cancel_content_js.html b/dom/ipc/tests/file_cancel_content_js.html
new file mode 100644
index 0000000000..d2caf03c6a
--- /dev/null
+++ b/dom/ipc/tests/file_cancel_content_js.html
@@ -0,0 +1,18 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Wait for it...</title>
+ </head>
+ <body>
+ Try to go to another page.
+ <script>
+ addEventListener("StartLongLoop", function() {
+ setTimeout(() => {
+ const start = Date.now();
+ while (Date.now() - start < 7500);
+ window.dispatchEvent(new CustomEvent("LongLoopEnded"));
+ });
+ });
+ </script>
+ </body>
+</html>
diff --git a/dom/ipc/tests/file_cross_frame.html b/dom/ipc/tests/file_cross_frame.html
new file mode 100644
index 0000000000..b52d920dd0
--- /dev/null
+++ b/dom/ipc/tests/file_cross_frame.html
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <meta charset="utf-8">
+ <title>Different-origin iframe</title>
+</head>
+<body>
+<iframe id="testIFrame" src="https://example.org/browser/dom/ipc/tests/file_dummy.html"></iframe>
+<i>I am a web page</i>
+</div>
+</body>
+</html>
diff --git a/dom/ipc/tests/file_disableScript.html b/dom/ipc/tests/file_disableScript.html
new file mode 100644
index 0000000000..f4888cd586
--- /dev/null
+++ b/dom/ipc/tests/file_disableScript.html
@@ -0,0 +1,11 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script>
+var gFiredOnload = false;
+var gFiredOnclick = false;
+</script>
+</head>
+<body onload="gFiredOnload = true;" onclick="gFiredOnclick = true;">
+</body>
+</html>
diff --git a/dom/ipc/tests/file_domainPolicy_base.html b/dom/ipc/tests/file_domainPolicy_base.html
new file mode 100644
index 0000000000..6e3ec7aec4
--- /dev/null
+++ b/dom/ipc/tests/file_domainPolicy_base.html
@@ -0,0 +1,8 @@
+<!DOCTYPE html>
+<html>
+<head>
+</head>
+<body>
+<iframe id="root" name="root"/>
+</body>
+</html>
diff --git a/dom/ipc/tests/file_dummy.html b/dom/ipc/tests/file_dummy.html
new file mode 100644
index 0000000000..c8701dae7d
--- /dev/null
+++ b/dom/ipc/tests/file_dummy.html
@@ -0,0 +1,7 @@
+<!doctype html>
+<html>
+<head><meta charset="utf-8"></head>
+<body>
+ <h1>This is a dummy file</h1>
+</body>
+</html>
diff --git a/dom/ipc/tests/file_endless_js.html b/dom/ipc/tests/file_endless_js.html
new file mode 100644
index 0000000000..926fb1d8ab
--- /dev/null
+++ b/dom/ipc/tests/file_endless_js.html
@@ -0,0 +1,17 @@
+<!doctype html>
+<html>
+<head><meta charset="utf-8"></head>
+<script>
+ function hang(m) {
+ let i = 1;
+ while (i > 0) {
+ i = Date.now();
+ }
+ }
+
+ onmessage = hang;
+</script>
+<body>
+ <h1>This is an endless JS loop</h1>
+</body>
+</html>
diff --git a/dom/ipc/tests/mochitest.toml b/dom/ipc/tests/mochitest.toml
new file mode 100644
index 0000000000..b1b347408d
--- /dev/null
+++ b/dom/ipc/tests/mochitest.toml
@@ -0,0 +1,30 @@
+[DEFAULT]
+
+["test_Preallocated.html"]
+skip-if = [
+ "os == 'android'",
+ "tsan", # Bug 1525959. tsan: Bug 1683730
+]
+
+["test_bcg_processes.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_browsingcontext_currenturi.html"]
+support-files = ["file_broadcast_currenturi_onload.html"]
+skip-if = [
+ "http3",
+ "http2",
+]
+
+["test_temporaryfile_stream.html"]
+skip-if = ["os == 'android'"]
+support-files = [
+ "blob_verify.sjs",
+ "!/dom/canvas/test/captureStream_common.js",
+]
+
+["test_window_open_discarded_bc.html"]
+skip-if = ["os == 'android'"]
diff --git a/dom/ipc/tests/process_error.xhtml b/dom/ipc/tests/process_error.xhtml
new file mode 100644
index 0000000000..3d57a3f456
--- /dev/null
+++ b/dom/ipc/tests/process_error.xhtml
@@ -0,0 +1,60 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ orient="vertical">
+
+ <browser id="thebrowser" type="content" remote="true" />
+ <script type="application/javascript"><![CDATA[
+ const {BrowserTestUtils} = ChromeUtils.importESModule(
+ "resource://testing-common/BrowserTestUtils.sys.mjs"
+ );
+
+ const ok = window.arguments[0].ok;
+ const is = window.arguments[0].is;
+ const done = window.arguments[0].done;
+ const SimpleTest = window.arguments[0].SimpleTest;
+
+ // Parse test options.
+ const url = new URL(document.location);
+ const crashType = url.searchParams.get("crashType");
+
+ // Allow the browser to get connected before using the messageManager to cause
+ // a crash:
+ addEventListener("DOMContentLoaded", () => {
+ let browser = document.getElementById('thebrowser');
+
+ let observerPromise = new Promise(resolve => {
+ let crashObserver = (subject, topic, data) => {
+ is(topic, 'ipc:content-shutdown', 'Received correct observer topic.');
+ ok(subject instanceof Ci.nsIPropertyBag2,
+ 'Subject implements nsIPropertyBag2.');
+
+ var dumpID;
+ if ('nsICrashReporter' in Ci) {
+ dumpID = subject.getPropertyAsAString('dumpID');
+ ok(dumpID, "dumpID is present and not an empty string");
+ }
+
+ Services.obs.removeObserver(crashObserver, 'ipc:content-shutdown');
+ resolve();
+ }
+
+ Services.obs.addObserver(crashObserver, 'ipc:content-shutdown');
+ });
+
+ let browsingContextId = browser.frameLoader.browsingContext.id;
+
+ let eventFiredPromise = BrowserTestUtils.waitForEvent(browser, "oop-browser-crashed");
+ let eventPromise = eventFiredPromise.then(event => {
+ is(event.browsingContextId, browsingContextId,
+ "Expected the right browsing context id on the oop-browser-crashed event.");
+ })
+
+ BrowserTestUtils.crashFrame(browser, true, false, /* Default browsing context */ null, { crashType });
+
+ Promise.all([observerPromise, eventPromise]).then(done);
+ });
+ ]]></script>
+
+</window>
diff --git a/dom/ipc/tests/test_Preallocated.html b/dom/ipc/tests/test_Preallocated.html
new file mode 100644
index 0000000000..fb719995f5
--- /dev/null
+++ b/dom/ipc/tests/test_Preallocated.html
@@ -0,0 +1,51 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Test that the preallocated process starts up.
+-->
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script type="application/javascript" src="../browserElementTestHelpers.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+
+<script type="application/javascript">
+"use strict";
+
+SimpleTest.waitForExplicitFinish();
+
+function expectProcessCreated() {
+ /* eslint-env mozilla/chrome-script */
+ return new Promise(resolve => {
+ function parentExpectProcessCreated() {
+ let topic = "ipc:content-initializing";
+ let obs = { observe() {
+ Services.obs.removeObserver(obs, topic);
+ sendAsyncMessage("process-created");
+ }};
+ Services.obs.addObserver(obs, topic);
+ }
+
+ let helper = SpecialPowers.loadChromeScript(parentExpectProcessCreated);
+ SimpleTest.registerCleanupFunction(function() { helper.destroy(); });
+ helper.addMessageListener("process-created", resolve);
+ });
+}
+
+expectProcessCreated().then(() => {
+ ok(true, "Process creation detected.");
+ SimpleTest.finish();
+});
+
+// Kill existing preallocated process.
+SpecialPowers.pushPrefEnv({"set": [["dom.ipc.processPrelaunch.enabled", false]]}).then(() => {
+ // Make sure we have the capacity to launch preallocated process.
+ SpecialPowers.pushPrefEnv({"set": [["dom.ipc.processCount", 100]]}).then(() => {
+ // Enable preallocated process and run the test.
+ SpecialPowers.pushPrefEnv({"set": [["dom.ipc.processPrelaunch.enabled", true]]});
+ });
+});
+</script>
+</body>
+</html>
diff --git a/dom/ipc/tests/test_bcg_processes.html b/dom/ipc/tests/test_bcg_processes.html
new file mode 100644
index 0000000000..8f68aa4a89
--- /dev/null
+++ b/dom/ipc/tests/test_bcg_processes.html
@@ -0,0 +1,45 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+
+<script type="application/javascript">
+"use strict";
+
+add_task(async function main_test() {
+ await SpecialPowers.pushPrefEnv({
+ set: [["dom.ipc.processCount.webIsolated", 10]],
+ });
+
+ let frame1 = document.createElement("iframe");
+ frame1.src = "http://example.com";
+ document.body.appendChild(frame1);
+ await new Promise(resolve => {
+ frame1.addEventListener("load", resolve, { once: true })
+ });
+ info("frame 1 loaded");
+
+ let frame2 = document.createElement("iframe");
+ frame2.src = "http://example.com";
+ document.body.appendChild(frame2);
+ await new Promise(resolve => {
+ frame2.addEventListener("load", resolve, { once: true })
+ });
+ info("frame 2 loaded");
+
+ let id1 = await SpecialPowers.spawn(frame1, [], () => {
+ return ChromeUtils.domProcessChild.childId;
+ });
+ let id2 = await SpecialPowers.spawn(frame2, [], () => {
+ return ChromeUtils.domProcessChild.childId;
+ });
+
+ is(id1, id2, "childID for example.com subframes should match");
+});
+
+</script>
+</body>
+</html>
diff --git a/dom/ipc/tests/test_blob_sliced_from_child_process.js b/dom/ipc/tests/test_blob_sliced_from_child_process.js
new file mode 100644
index 0000000000..8a7a56088d
--- /dev/null
+++ b/dom/ipc/tests/test_blob_sliced_from_child_process.js
@@ -0,0 +1,140 @@
+"use strict";
+
+const { XPCShellContentUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/XPCShellContentUtils.sys.mjs"
+);
+
+XPCShellContentUtils.init(this);
+
+function childFrameScript() {
+ /* eslint-env mozilla/frame-script */
+ "use strict";
+
+ const messageName = "test:blob-slice-test";
+ const blobData = ["So", " ", "many", " ", "blobs!"];
+ const blobType = "text/plain";
+
+ let blob = new Blob(blobData, { type: blobType });
+
+ let firstSliceStart = blobData[0].length + blobData[1].length;
+ let firstSliceEnd = firstSliceStart + blobData[2].length;
+
+ let slice = blob.slice(firstSliceStart, firstSliceEnd, blobType);
+
+ let secondSliceStart = blobData[2].indexOf("a");
+ let secondSliceEnd = secondSliceStart + 2;
+
+ slice = slice.slice(secondSliceStart, secondSliceEnd, blobType);
+
+ sendAsyncMessage(messageName, { blob });
+ sendAsyncMessage(messageName, { slice });
+}
+
+add_task(async function test() {
+ let page = await XPCShellContentUtils.loadContentPage(
+ "data:text/html,<!DOCTYPE HTML><html><body></body></html>",
+ {
+ remote: true,
+ }
+ );
+
+ page.loadFrameScript(childFrameScript);
+
+ const messageName = "test:blob-slice-test";
+ const blobData = ["So", " ", "many", " ", "blobs!"];
+ const blobText = blobData.join("");
+ const blobType = "text/plain";
+
+ const sliceText = "an";
+
+ let receivedBlob = false;
+ let receivedSlice = false;
+
+ let resolveBlob, resolveSlice;
+ let blobPromise = new Promise(resolve => {
+ resolveBlob = resolve;
+ });
+ let slicePromise = new Promise(resolve => {
+ resolveSlice = resolve;
+ });
+
+ let mm = page.browser.messageManager;
+ mm.addMessageListener(messageName, function (message) {
+ if ("blob" in message.data) {
+ equal(receivedBlob, false, "Have not yet received Blob");
+ equal(receivedSlice, false, "Have not yet received Slice");
+
+ receivedBlob = true;
+
+ let blob = message.data.blob;
+
+ ok(Blob.isInstance(blob), "Received a Blob");
+ equal(blob.size, blobText.length, "Blob has correct size");
+ equal(blob.type, blobType, "Blob has correct type");
+
+ let slice = blob.slice(
+ blobText.length - blobData[blobData.length - 1].length,
+ blob.size,
+ blobType
+ );
+
+ ok(Blob.isInstance(slice), "Slice returned a Blob");
+ equal(
+ slice.size,
+ blobData[blobData.length - 1].length,
+ "Slice has correct size"
+ );
+ equal(slice.type, blobType, "Slice has correct type");
+
+ let reader = new FileReader();
+ reader.onload = function () {
+ equal(
+ reader.result,
+ blobData[blobData.length - 1],
+ "Slice has correct data"
+ );
+
+ resolveBlob();
+ };
+ reader.readAsText(slice);
+ } else if ("slice" in message.data) {
+ equal(receivedBlob, true, "Already received Blob");
+ equal(receivedSlice, false, "Have not yet received Slice");
+
+ receivedSlice = true;
+
+ let slice = message.data.slice;
+
+ ok(Blob.isInstance(slice), "Received a Blob for slice");
+ equal(slice.size, sliceText.length, "Slice has correct size");
+ equal(slice.type, blobType, "Slice has correct type");
+
+ let reader = new FileReader();
+ reader.onload = function () {
+ equal(reader.result, sliceText, "Slice has correct data");
+
+ let slice2 = slice.slice(1, 2, blobType);
+
+ ok(Blob.isInstance(slice2), "Slice returned a Blob");
+ equal(slice2.size, 1, "Slice has correct size");
+ equal(slice2.type, blobType, "Slice has correct type");
+
+ let reader2 = new FileReader();
+ reader2.onload = function () {
+ equal(reader2.result, sliceText[1], "Slice has correct data");
+
+ resolveSlice();
+ };
+ reader2.readAsText(slice2);
+ };
+ reader.readAsText(slice);
+ } else {
+ ok(false, "Received a bad message: " + JSON.stringify(message.data));
+ }
+ });
+
+ await blobPromise;
+ await slicePromise;
+
+ await page.close();
+});
diff --git a/dom/ipc/tests/test_blob_sliced_from_parent_process.js b/dom/ipc/tests/test_blob_sliced_from_parent_process.js
new file mode 100644
index 0000000000..e196a6986c
--- /dev/null
+++ b/dom/ipc/tests/test_blob_sliced_from_parent_process.js
@@ -0,0 +1,167 @@
+"use strict";
+
+const { XPCShellContentUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/XPCShellContentUtils.sys.mjs"
+);
+
+XPCShellContentUtils.init(this);
+
+function childFrameScript() {
+ /* eslint-env mozilla/frame-script */
+ const messageName = "test:blob-slice-test";
+ const blobData = ["So", " ", "many", " ", "blobs!"];
+ const blobText = blobData.join("");
+ const blobType = "text/plain";
+
+ const sliceText = "an";
+
+ function info(msg) {
+ sendAsyncMessage(messageName, { op: "info", msg });
+ }
+
+ function ok(condition, name, diag) {
+ sendAsyncMessage(messageName, { op: "ok", condition, name, diag });
+ }
+
+ function is(a, b, name) {
+ let pass = a == b;
+ let diag = pass ? "" : "got " + a + ", expected " + b;
+ ok(pass, name, diag);
+ }
+
+ function finish(result) {
+ sendAsyncMessage(messageName, { op: "done", result });
+ }
+
+ function grabAndContinue(arg) {
+ testGenerator.next(arg);
+ }
+
+ function* testSteps() {
+ addMessageListener(messageName, grabAndContinue);
+ let message = yield undefined;
+
+ let blob = message.data;
+
+ ok(Blob.isInstance(blob), "Received a Blob");
+ is(blob.size, blobText.length, "Blob has correct length");
+ is(blob.type, blobType, "Blob has correct type");
+
+ info("Reading blob");
+
+ let reader = new FileReader();
+ reader.addEventListener("load", grabAndContinue);
+ reader.readAsText(blob);
+
+ yield undefined;
+
+ is(reader.result, blobText, "Blob has correct data");
+
+ let firstSliceStart = blobData[0].length + blobData[1].length;
+ let firstSliceEnd = firstSliceStart + blobData[2].length;
+
+ let slice = blob.slice(firstSliceStart, firstSliceEnd, blobType);
+
+ ok(Blob.isInstance(slice), "Slice returned a Blob");
+ is(slice.size, blobData[2].length, "Slice has correct length");
+ is(slice.type, blobType, "Slice has correct type");
+
+ info("Reading slice");
+
+ reader = new FileReader();
+ reader.addEventListener("load", grabAndContinue);
+ reader.readAsText(slice);
+
+ yield undefined;
+
+ is(reader.result, blobData[2], "Slice has correct data");
+
+ let secondSliceStart = blobData[2].indexOf("a");
+ let secondSliceEnd = secondSliceStart + sliceText.length;
+
+ slice = slice.slice(secondSliceStart, secondSliceEnd, blobType);
+
+ ok(Blob.isInstance(slice), "Second slice returned a Blob");
+ is(slice.size, sliceText.length, "Second slice has correct length");
+ is(slice.type, blobType, "Second slice has correct type");
+
+ info("Sending second slice");
+ finish(slice);
+ }
+
+ let testGenerator = testSteps();
+ testGenerator.next();
+}
+
+add_task(async function test() {
+ let page = await XPCShellContentUtils.loadContentPage(
+ "data:text/html,<!DOCTYPE HTML><html><body></body></html>",
+ {
+ remote: true,
+ }
+ );
+
+ page.loadFrameScript(childFrameScript);
+
+ const messageName = "test:blob-slice-test";
+ const blobData = ["So", " ", "many", " ", "blobs!"];
+ const blobType = "text/plain";
+
+ const sliceText = "an";
+
+ await new Promise(resolve => {
+ function grabAndContinue(arg) {
+ testGenerator.next(arg);
+ }
+
+ function* testSteps() {
+ let slice = yield undefined;
+
+ ok(Blob.isInstance(slice), "Received a Blob");
+ equal(slice.size, sliceText.length, "Slice has correct size");
+ equal(slice.type, blobType, "Slice has correct type");
+
+ let reader = new FileReader();
+ reader.onload = grabAndContinue;
+ reader.readAsText(slice);
+ yield undefined;
+
+ equal(reader.result, sliceText, "Slice has correct data");
+ resolve();
+ }
+
+ let testGenerator = testSteps();
+ testGenerator.next();
+
+ let mm = page.browser.messageManager;
+ mm.addMessageListener(messageName, function (message) {
+ let data = message.data;
+ switch (data.op) {
+ case "info": {
+ info(data.msg);
+ break;
+ }
+
+ case "ok": {
+ ok(data.condition, data.name + " - " + data.diag);
+ break;
+ }
+
+ case "done": {
+ testGenerator.next(data.result);
+ break;
+ }
+
+ default: {
+ ok(false, "Unknown op: " + data.op);
+ resolve();
+ }
+ }
+ });
+
+ let blob = new Blob(blobData, { type: blobType });
+ mm.sendAsyncMessage(messageName, blob);
+ });
+
+ await page.close();
+});
diff --git a/dom/ipc/tests/test_browsingcontext_currenturi.html b/dom/ipc/tests/test_browsingcontext_currenturi.html
new file mode 100644
index 0000000000..b03af96b20
--- /dev/null
+++ b/dom/ipc/tests/test_browsingcontext_currenturi.html
@@ -0,0 +1,131 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+
+<iframe id="tls1frame" src="https://tls1.example.com/"></iframe>
+
+<script>
+"use strict";
+
+add_task(async function test_frame() {
+ let win = SpecialPowers.wrap(window);
+ info(`id=${win.browsingContext.id}`);
+ let [docURI, curURI] = await SpecialPowers.spawnChrome([win.browsingContext.id], async id => {
+ let bc = BrowsingContext.get(id);
+ return [
+ bc.currentWindowGlobal.documentURI.spec,
+ bc.currentURI.spec,
+ ];
+ });
+ info(`docURI=${docURI}, curURI=${curURI}`);
+ is(window.location.href, curURI, "curURI has the expected value");
+ is(window.location.href, docURI, "documentURI has the expected value");
+});
+
+add_task(async function test_tls1_frame() {
+ let expframe = SpecialPowers.wrap(document.getElementById("tls1frame"));
+ let [docURI, curURI] = await SpecialPowers.spawnChrome(
+ [expframe.browsingContext.id], async id => {
+ const { TestUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/TestUtils.sys.mjs"
+ );
+
+ let bc = BrowsingContext.get(id);
+
+ // awkwardly wait for the current window global to update to the error page.
+ // would be nice to do just about anything else here...
+ await TestUtils.waitForCondition(
+ () =>
+ bc.currentURI && bc.currentURI.spec != "about:blank" &&
+ bc.currentWindowGlobal && bc.currentWindowGlobal.documentURI.spec != "about:blank",
+ "waiting for current window global to be non-initial");
+
+ info(`currentWindowGlobal has updated in the parent!`);
+ return [
+ bc.currentWindowGlobal.documentURI.spec,
+ bc.currentURI.spec,
+ ];
+ });
+
+ info(`docURI=${docURI}, curURI=${curURI}`);
+ is(curURI, "https://tls1.example.com/", "curURI has expected value");
+ ok(docURI.startsWith("about:neterror"), "documentURI starts with about:neterror");
+});
+
+let BROADCAST_ONLOAD_URL =
+ new URL("file_broadcast_currenturi_onload.html", location.href);
+
+async function broadcastLoadTest(baseURI, callback) {
+ // Bug 1746646: Make mochitests work with TCP enabled (cookieBehavior = 5)
+ // Acquire storage access permission here so that the BroadcastChannel used to
+ // communicate with the opened windows works in xorigin tests. Otherwise,
+ // the iframe containing this page is isolated from first-party storage access,
+ // which isolates BroadcastChannel communication.
+ if (isXOrigin) {
+ await SpecialPowers.pushPrefEnv({
+ set: [["privacy.partition.always_partition_third_party_non_cookie_storage", false]],
+ });
+ SpecialPowers.wrap(document).notifyUserGestureActivation();
+ await SpecialPowers.addPermission(
+ "storageAccessAPI",
+ true,
+ window.location.href
+ );
+ await SpecialPowers.wrap(document).requestStorageAccess();
+ }
+ let loaded = new Promise(resolve => {
+ let chan = new BroadcastChannel("test_broadcast_onload");
+ chan.onmessage = event => {
+ resolve(event.data);
+ };
+ });
+ let srcURL = new URL(BROADCAST_ONLOAD_URL.pathname, baseURI);
+ callback(srcURL.href);
+
+ let results = await loaded;
+ for (let { location, curURI, docURI } of results) {
+ info(`location=${location}, docURI=${docURI}, curURI=${curURI}`);
+ is(location, curURI, "curURI has expected value");
+ is(location, docURI, "documentURI has expected value");
+ }
+}
+
+async function normalFrameLoadTest(base) {
+ await broadcastLoadTest(base, src => {
+ let frame = document.createElement("iframe");
+ frame.src = src;
+ document.body.appendChild(frame);
+ });
+}
+
+async function normalPopupLoadTest(base, flags = "") {
+ await broadcastLoadTest(base, src => {
+ window.open(src, null, flags);
+ });
+}
+
+add_task(async function test_sameorigin_frame() {
+ await normalFrameLoadTest(location.href);
+})
+
+add_task(async function test_crossorigin_frame() {
+ await normalFrameLoadTest("https://example.com");
+});
+
+add_task(async function test_sameorigin_popup() {
+ await normalPopupLoadTest(location.href);
+ await normalPopupLoadTest(location.href, "noopener");
+});
+
+add_task(async function test_crossorigin_popup() {
+ await normalPopupLoadTest("https://example.com");
+ await normalPopupLoadTest("https://example.com", "noopener");
+});
+
+</script>
+</body>
+</html>
diff --git a/dom/ipc/tests/test_bug1086684.js b/dom/ipc/tests/test_bug1086684.js
new file mode 100644
index 0000000000..8a34906686
--- /dev/null
+++ b/dom/ipc/tests/test_bug1086684.js
@@ -0,0 +1,99 @@
+"use strict";
+
+const { XPCShellContentUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/XPCShellContentUtils.sys.mjs"
+);
+
+XPCShellContentUtils.init(this);
+
+const childFramePath = "/file_bug1086684.html";
+const childFrameURL = "http://example.com" + childFramePath;
+
+const childFrameContents = `<!DOCTYPE html>
+<html>
+<head>
+ <meta charset="UTF-8">
+</head>
+<body>
+<div id="content">
+ <input type="file" id="f">
+</div>
+</body>
+</html>`;
+
+const server = XPCShellContentUtils.createHttpServer({
+ hosts: ["example.com"],
+});
+server.registerPathHandler(childFramePath, (request, response) => {
+ response.write(childFrameContents);
+});
+
+function childFrameScript() {
+ /* eslint-env mozilla/frame-script */
+ "use strict";
+
+ let { MockFilePicker } = ChromeUtils.importESModule(
+ "resource://testing-common/MockFilePicker.sys.mjs"
+ );
+
+ function parentReady(message) {
+ MockFilePicker.init(content);
+ MockFilePicker.setFiles([message.data.file]);
+ MockFilePicker.returnValue = MockFilePicker.returnOK;
+
+ let input = content.document.getElementById("f");
+ input.addEventListener("change", () => {
+ MockFilePicker.cleanup();
+ let value = input.value;
+ message.target.sendAsyncMessage("testBug1086684:childDone", { value });
+ });
+
+ input.focus();
+ input.click();
+ }
+
+ addMessageListener("testBug1086684:parentReady", function (message) {
+ parentReady(message);
+ });
+}
+
+add_task(async function () {
+ Services.prefs.setBoolPref("dom.security.https_first", false);
+ let page = await XPCShellContentUtils.loadContentPage(childFrameURL, {
+ remote: true,
+ });
+
+ page.loadFrameScript(childFrameScript);
+
+ await new Promise(resolve => {
+ let test;
+ function* testStructure(mm) {
+ let value;
+
+ function testDone(msg) {
+ test.next(msg.data.value);
+ }
+
+ mm.addMessageListener("testBug1086684:childDone", testDone);
+
+ let blob = new Blob([]);
+ let file = new File([blob], "helloworld.txt", { type: "text/plain" });
+
+ mm.sendAsyncMessage("testBug1086684:parentReady", { file });
+ value = yield;
+
+ // Note that the "helloworld.txt" passed in above doesn't affect the
+ // 'value' getter. Because we're mocking a file using a blob, we ask the
+ // blob for its path, which is the empty string.
+ equal(value, "", "got the right answer and didn't crash");
+
+ resolve();
+ }
+
+ test = testStructure(page.browser.messageManager);
+ test.next();
+ });
+
+ await page.close();
+ Services.prefs.clearUserPref("dom.security.https_first");
+});
diff --git a/dom/ipc/tests/test_child_docshell.js b/dom/ipc/tests/test_child_docshell.js
new file mode 100644
index 0000000000..ee79a509dc
--- /dev/null
+++ b/dom/ipc/tests/test_child_docshell.js
@@ -0,0 +1,90 @@
+"use strict";
+
+const { XPCShellContentUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/XPCShellContentUtils.sys.mjs"
+);
+
+XPCShellContentUtils.init(this);
+
+add_task(async function test() {
+ let page = await XPCShellContentUtils.loadContentPage("about:blank", {
+ remote: true,
+ });
+
+ await new Promise(resolve => {
+ let mm = page.browser.messageManager;
+ mm.addMessageListener("chromeEventHandler", function (msg) {
+ var result = msg.json;
+ equal(
+ result.processType,
+ Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT,
+ "The frame script is running in a real distinct child process"
+ );
+ ok(
+ result.hasCorrectInterface,
+ "docshell.chromeEventHandler has EventTarget interface"
+ );
+ });
+
+ mm.addMessageListener("DOMWindowCreatedReceived", function (msg) {
+ ok(true, "the chrome event handler looks functional");
+ var result = msg.json;
+ ok(
+ result.stableChromeEventHandler,
+ "docShell.chromeEventHandler is stable"
+ );
+ ok(result.iframeHasNewDocShell, "iframe spawns a new docShell");
+ ok(
+ result.iframeHasSameChromeEventHandler,
+ "but iframe has the same chrome event handler"
+ );
+ resolve();
+ });
+
+ // Inject a frame script in the child process:
+ page.loadFrameScript(async function () {
+ /* eslint-env mozilla/frame-script */
+ var chromeEventHandler = docShell.chromeEventHandler;
+ sendAsyncMessage("chromeEventHandler", {
+ processType: Services.appinfo.processType,
+ hasCorrectInterface:
+ chromeEventHandler && EventTarget.isInstance(chromeEventHandler),
+ });
+
+ /*
+ Ensure that this chromeEventHandler actually works,
+ by creating a new window and listening for its DOMWindowCreated event
+ */
+ chromeEventHandler.addEventListener(
+ "DOMWindowCreated",
+ function listener(evt) {
+ if (evt.target == content.document) {
+ return;
+ }
+ chromeEventHandler.removeEventListener("DOMWindowCreated", listener);
+ let new_win = evt.target.defaultView;
+ let new_docShell = new_win.docShell;
+ sendAsyncMessage("DOMWindowCreatedReceived", {
+ stableChromeEventHandler:
+ chromeEventHandler === docShell.chromeEventHandler,
+ iframeHasNewDocShell: new_docShell !== docShell,
+ iframeHasSameChromeEventHandler:
+ new_docShell.chromeEventHandler === chromeEventHandler,
+ });
+ }
+ );
+
+ if (content.document.readyState != "complete") {
+ await new Promise(res =>
+ addEventListener("load", res, { once: true, capture: true })
+ );
+ }
+
+ let iframe = content.document.createElement("iframe");
+ iframe.setAttribute("src", "data:text/html,foo");
+ content.document.documentElement.appendChild(iframe);
+ });
+ });
+
+ await page.close();
+});
diff --git a/dom/ipc/tests/test_process_error.xhtml b/dom/ipc/tests/test_process_error.xhtml
new file mode 100644
index 0000000000..d122e7fedd
--- /dev/null
+++ b/dom/ipc/tests/test_process_error.xhtml
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin/global.css" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script>
+ SimpleTest.waitForExplicitFinish();
+ SimpleTest.expectChildProcessCrash();
+
+ var w = window.browsingContext.topChromeWindow.openDialog('process_error.xhtml', '_blank', 'chrome,resizable=yes,width=400,height=600', window);
+
+ function done()
+ {
+ w.close();
+ SimpleTest.finish();
+ }
+ </script>
+
+ <body xmlns="http://www.w3.org/1999/xhtml" style="height: 300px; overflow: auto;" />
+</window>
diff --git a/dom/ipc/tests/test_sharedMap.js b/dom/ipc/tests/test_sharedMap.js
new file mode 100644
index 0000000000..2a6c3a7142
--- /dev/null
+++ b/dom/ipc/tests/test_sharedMap.js
@@ -0,0 +1,377 @@
+"use strict";
+
+const { AppConstants } = ChromeUtils.importESModule(
+ "resource://gre/modules/AppConstants.sys.mjs"
+);
+const { XPCShellContentUtils } = ChromeUtils.importESModule(
+ "resource://testing-common/XPCShellContentUtils.sys.mjs"
+);
+
+const PROCESS_COUNT_PREF = "dom.ipc.processCount";
+
+const remote = AppConstants.platform !== "android";
+
+XPCShellContentUtils.init(this);
+
+let contentPage;
+
+async function readBlob(key, sharedData = Services.cpmm.sharedData) {
+ const { ExtensionUtils } = ChromeUtils.importESModule(
+ "resource://gre/modules/ExtensionUtils.sys.mjs"
+ );
+
+ let reader = new FileReader();
+ reader.readAsText(sharedData.get(key));
+ await ExtensionUtils.promiseEvent(reader, "loadend");
+ return reader.result;
+}
+
+function getKey(key, sharedData = Services.cpmm.sharedData) {
+ return sharedData.get(key);
+}
+
+function hasKey(key, sharedData = Services.cpmm.sharedData) {
+ return sharedData.has(key);
+}
+
+function getContents(sharedMap = Services.cpmm.sharedData) {
+ return {
+ keys: Array.from(sharedMap.keys()),
+ values: Array.from(sharedMap.values()),
+ entries: Array.from(sharedMap.entries()),
+ getValues: Array.from(sharedMap.keys(), key => sharedMap.get(key)),
+ };
+}
+
+function checkMap(contents, expected) {
+ expected = Array.from(expected);
+
+ equal(contents.keys.length, expected.length, "Got correct number of keys");
+ equal(
+ contents.values.length,
+ expected.length,
+ "Got correct number of values"
+ );
+ equal(
+ contents.entries.length,
+ expected.length,
+ "Got correct number of entries"
+ );
+
+ for (let [i, [key, val]] of contents.entries.entries()) {
+ equal(key, contents.keys[i], `keys()[${i}] matches entries()[${i}]`);
+ deepEqual(
+ val,
+ contents.values[i],
+ `values()[${i}] matches entries()[${i}]`
+ );
+ }
+
+ expected.sort(([a], [b]) => a.localeCompare(b));
+ contents.entries.sort(([a], [b]) => a.localeCompare(b));
+
+ for (let [i, [key, val]] of contents.entries.entries()) {
+ equal(
+ key,
+ expected[i][0],
+ `expected[${i}].key matches entries()[${i}].key`
+ );
+ deepEqual(
+ val,
+ expected[i][1],
+ `expected[${i}].value matches entries()[${i}].value`
+ );
+ }
+}
+
+function checkParentMap(expected) {
+ info("Checking parent map");
+ checkMap(getContents(Services.ppmm.sharedData), expected);
+}
+
+async function checkContentMaps(expected, parentOnly = false) {
+ info("Checking in-process content map");
+ checkMap(getContents(Services.cpmm.sharedData), expected);
+
+ if (!parentOnly) {
+ info("Checking out-of-process content map");
+ let contents = await contentPage.spawn([], getContents);
+ checkMap(contents, expected);
+ }
+}
+
+async function loadContentPage() {
+ let page = await XPCShellContentUtils.loadContentPage("data:text/html,", {
+ remote,
+ });
+ registerCleanupFunction(() => page.close());
+ return page;
+}
+
+add_setup(async function () {
+ // Start with one content process so that we can increase the number
+ // later and test the behavior of a fresh content process.
+ Services.prefs.setIntPref(PROCESS_COUNT_PREF, 1);
+
+ contentPage = await loadContentPage();
+});
+
+add_task(async function test_sharedMap() {
+ let { sharedData } = Services.ppmm;
+
+ info("Check that parent and child maps are both initially empty");
+
+ checkParentMap([]);
+ await checkContentMaps([]);
+
+ let expected = [
+ ["foo-a", { foo: "a" }],
+ ["foo-b", { foo: "b" }],
+ ["bar-c", null],
+ ["bar-d", 42],
+ ];
+
+ function setKey(key, val) {
+ sharedData.set(key, val);
+ expected = expected.filter(([k]) => k != key);
+ expected.push([key, val]);
+ }
+ function deleteKey(key) {
+ sharedData.delete(key);
+ expected = expected.filter(([k]) => k != key);
+ }
+
+ for (let [key, val] of expected) {
+ sharedData.set(key, val);
+ }
+
+ info(
+ "Add some entries, test that they are initially only available in the parent"
+ );
+
+ checkParentMap(expected);
+ await checkContentMaps([]);
+
+ info("Flush. Check that changes are visible in both parent and children");
+
+ sharedData.flush();
+
+ checkParentMap(expected);
+ await checkContentMaps(expected);
+
+ info(
+ "Add another entry. Check that it is initially only available in the parent"
+ );
+
+ let oldExpected = Array.from(expected);
+
+ setKey("baz-a", { meh: "meh" });
+
+ // When we do several checks in a row, we can't check the values in
+ // the content process, since the async checks may allow the idle
+ // flush task to run, and update it before we're ready.
+
+ checkParentMap(expected);
+ checkContentMaps(oldExpected, true);
+
+ info(
+ "Add another entry. Check that both new entries are only available in the parent"
+ );
+
+ setKey("baz-a", { meh: 12 });
+
+ checkParentMap(expected);
+ checkContentMaps(oldExpected, true);
+
+ info(
+ "Delete an entry. Check that all changes are only visible in the parent"
+ );
+
+ deleteKey("foo-b");
+
+ checkParentMap(expected);
+ checkContentMaps(oldExpected, true);
+
+ info(
+ "Flush. Check that all entries are available in both parent and children"
+ );
+
+ sharedData.flush();
+
+ checkParentMap(expected);
+ await checkContentMaps(expected);
+
+ info("Test that entries are automatically flushed on idle:");
+
+ info(
+ "Add a new entry. Check that it is initially only available in the parent"
+ );
+
+ // Test the idle flush task.
+ oldExpected = Array.from(expected);
+
+ setKey("thing", "stuff");
+
+ checkParentMap(expected);
+ checkContentMaps(oldExpected, true);
+
+ info(
+ "Wait for an idle timeout. Check that changes are now visible in all children"
+ );
+
+ await new Promise(resolve => ChromeUtils.idleDispatch(resolve));
+
+ checkParentMap(expected);
+ await checkContentMaps(expected);
+
+ // Test that has() rebuilds map after a flush.
+ sharedData.set("grick", true);
+ sharedData.flush();
+ equal(
+ await contentPage.spawn(["grick"], hasKey),
+ true,
+ "has() should see key after flush"
+ );
+
+ sharedData.set("grack", true);
+ sharedData.flush();
+ equal(
+ await contentPage.spawn(["gruck"], hasKey),
+ false,
+ "has() should return false for nonexistent key"
+ );
+});
+
+add_task(async function test_blobs() {
+ let { sharedData } = Services.ppmm;
+
+ let text = [
+ "The quick brown fox jumps over the lazy dog",
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit",
+ "sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.",
+ ];
+ let blobs = text.map(str => new Blob([str]));
+
+ let data = { foo: { bar: "baz" } };
+
+ sharedData.set("blob0", blobs[0]);
+ sharedData.set("blob1", blobs[1]);
+ sharedData.set("data", data);
+
+ equal(
+ await readBlob("blob0", sharedData),
+ text[0],
+ "Expected text for blob0 in parent ppmm"
+ );
+
+ sharedData.flush();
+
+ equal(
+ await readBlob("blob0", sharedData),
+ text[0],
+ "Expected text for blob0 in parent ppmm"
+ );
+ equal(
+ await readBlob("blob1", sharedData),
+ text[1],
+ "Expected text for blob1 in parent ppmm"
+ );
+
+ equal(
+ await readBlob("blob0"),
+ text[0],
+ "Expected text for blob0 in parent cpmm"
+ );
+ equal(
+ await readBlob("blob1"),
+ text[1],
+ "Expected text for blob1 in parent cpmm"
+ );
+
+ equal(
+ await contentPage.spawn(["blob0"], readBlob),
+ text[0],
+ "Expected text for blob0 in child 1 cpmm"
+ );
+ equal(
+ await contentPage.spawn(["blob1"], readBlob),
+ text[1],
+ "Expected text for blob1 in child 1 cpmm"
+ );
+
+ // Start a second child process
+ Services.prefs.setIntPref(PROCESS_COUNT_PREF, 2);
+
+ let page2 = await loadContentPage();
+
+ equal(
+ await page2.spawn(["blob0"], readBlob),
+ text[0],
+ "Expected text for blob0 in child 2 cpmm"
+ );
+ equal(
+ await page2.spawn(["blob1"], readBlob),
+ text[1],
+ "Expected text for blob1 in child 2 cpmm"
+ );
+
+ sharedData.set("blob0", blobs[2]);
+
+ equal(
+ await readBlob("blob0", sharedData),
+ text[2],
+ "Expected text for blob0 in parent ppmm"
+ );
+
+ sharedData.flush();
+
+ equal(
+ await readBlob("blob0", sharedData),
+ text[2],
+ "Expected text for blob0 in parent ppmm"
+ );
+ equal(
+ await readBlob("blob1", sharedData),
+ text[1],
+ "Expected text for blob1 in parent ppmm"
+ );
+
+ equal(
+ await readBlob("blob0"),
+ text[2],
+ "Expected text for blob0 in parent cpmm"
+ );
+ equal(
+ await readBlob("blob1"),
+ text[1],
+ "Expected text for blob1 in parent cpmm"
+ );
+
+ equal(
+ await contentPage.spawn(["blob0"], readBlob),
+ text[2],
+ "Expected text for blob0 in child 1 cpmm"
+ );
+ equal(
+ await contentPage.spawn(["blob1"], readBlob),
+ text[1],
+ "Expected text for blob1 in child 1 cpmm"
+ );
+
+ equal(
+ await page2.spawn(["blob0"], readBlob),
+ text[2],
+ "Expected text for blob0 in child 2 cpmm"
+ );
+ equal(
+ await page2.spawn(["blob1"], readBlob),
+ text[1],
+ "Expected text for blob1 in child 2 cpmm"
+ );
+
+ deepEqual(
+ await page2.spawn(["data"], getKey),
+ data,
+ "Expected data for data key in child 2 cpmm"
+ );
+});
diff --git a/dom/ipc/tests/test_temporaryfile_stream.html b/dom/ipc/tests/test_temporaryfile_stream.html
new file mode 100644
index 0000000000..9fa76a2155
--- /dev/null
+++ b/dom/ipc/tests/test_temporaryfile_stream.html
@@ -0,0 +1,84 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Send an nsTemporaryFileInputStream cross-process</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <script src="/tests/dom/canvas/test/captureStream_common.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<div id="content">
+</div>
+<script class="testbody" type="text/javascript">
+function startTest() {
+ var canvas = document.createElement("canvas");
+ canvas.width = canvas.height = 100;
+ document.getElementById("content").appendChild(canvas);
+
+
+ // eslint-disable-next-line no-undef
+ var helper = new CaptureStreamTestHelper2D(100, 100);
+ helper.drawColor(canvas, helper.red);
+
+ var stream = canvas.captureStream(0);
+
+ var blob;
+
+ let mediaRecorder = new MediaRecorder(stream);
+ is(mediaRecorder.stream, stream,
+ "Media recorder stream = canvas stream at the start of recording");
+
+ mediaRecorder.onwarning = () => ok(false, "warning unexpectedly fired");
+
+ mediaRecorder.onerror = () => ok(false, "Recording failed");
+
+ mediaRecorder.ondataavailable = ev => {
+ is(blob, undefined, "Should only get one dataavailable event");
+ blob = ev.data;
+ };
+
+ mediaRecorder.onstart = () => {
+ info("Got 'start' event");
+ // We just want one frame encoded, to see that the recorder produces something readable.
+ mediaRecorder.stop();
+ };
+
+ mediaRecorder.onstop = () => {
+ info("Got 'stop' event");
+ ok(blob, "Should have gotten a data blob");
+ var xhr = new XMLHttpRequest();
+ xhr.open("POST", "blob_verify.sjs", true);
+ xhr.onload = () => {
+ var video = document.createElement("video");
+ video.id = "recorded-video";
+ video.src = URL.createObjectURL(xhr.response);
+ video.play();
+ video.onerror = err => {
+ ok(false, "Should be able to play the recording. Got error. code=" + video.error.code);
+ SimpleTest.finish();
+ };
+ document.getElementById("content").appendChild(video);
+ helper.pixelMustBecome(video, helper.red, {
+ threshold: 128,
+ infoString: "Should become red",
+ }).then(SimpleTest.finish);
+ };
+ xhr.onerror = () => {
+ ok(false, "XHR error");
+ SimpleTest.finish();
+ };
+ xhr.responseType = "blob";
+ xhr.send(blob);
+ };
+
+ mediaRecorder.start();
+ is(mediaRecorder.state, "recording", "Media recorder should be recording");
+}
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.pushPrefEnv({set: [["media.recorder.max_memory", 1]]}, startTest);
+</script>
+</pre>
+</body>
+</html>
diff --git a/dom/ipc/tests/test_window_open_discarded_bc.html b/dom/ipc/tests/test_window_open_discarded_bc.html
new file mode 100644
index 0000000000..4cd81463e0
--- /dev/null
+++ b/dom/ipc/tests/test_window_open_discarded_bc.html
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>Discard a new BrowsingContext during window.open nested event loop</title>
+ <script src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<script>
+add_task(async function() {
+ const TOPIC = "dangerous:test-only:new-browser-child-ready";
+
+ let found = false;
+ function observer(subject, topic, data) {
+ let win = SpecialPowers.wrap(subject);
+ if (SpecialPowers.compare(win.opener, window)) {
+ found = true;
+ SpecialPowers.removeObserver(observer, TOPIC);
+
+ win.close();
+ // window.close() is not synchronous, so we need to wait for the
+ // BrowsingContext to actually become discarded after we call it, to
+ // make sure that the window provider actually has a discarded BC at the
+ // end of its own nested event loop.
+ SpecialPowers.Services.tm.spinEventLoopUntil(
+ "Test(test_window_open_discarded_bc.html:add_task)",
+ () => !win.opener
+ );
+ }
+ }
+ SpecialPowers.addObserver(observer, TOPIC);
+
+ let win = window.open();
+
+ is(found, true, "Our observer should have fired for the new window");
+ is(win, null, "window.open() should return null when new window is already closed");
+});
+</script>
+</body>
+</html>
diff --git a/dom/ipc/tests/xpcshell.toml b/dom/ipc/tests/xpcshell.toml
new file mode 100644
index 0000000000..bc4c75e4b0
--- /dev/null
+++ b/dom/ipc/tests/xpcshell.toml
@@ -0,0 +1,16 @@
+[DEFAULT]
+
+["test_blob_sliced_from_child_process.js"]
+skip-if = ["os == 'android' && processor == 'x86_64'"]
+
+["test_blob_sliced_from_parent_process.js"]
+skip-if = ["os == 'android' && processor == 'x86_64'"]
+
+["test_bug1086684.js"]
+skip-if = ["os == 'android' && processor == 'x86_64'"]
+
+["test_child_docshell.js"]
+skip-if = ["os == 'android' && processor == 'x86_64'"]
+
+["test_sharedMap.js"]
+skip-if = ["os == 'android' && processor == 'x86_64'"]