summaryrefslogtreecommitdiffstats
path: root/dom/ipc
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /dom/ipc
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dom/ipc')
-rw-r--r--dom/ipc/BrowserBridgeChild.cpp261
-rw-r--r--dom/ipc/BrowserBridgeChild.h121
-rw-r--r--dom/ipc/BrowserBridgeHost.cpp88
-rw-r--r--dom/ipc/BrowserBridgeHost.h70
-rw-r--r--dom/ipc/BrowserBridgeParent.cpp247
-rw-r--r--dom/ipc/BrowserBridgeParent.h111
-rw-r--r--dom/ipc/BrowserChild.cpp4020
-rw-r--r--dom/ipc/BrowserChild.h936
-rw-r--r--dom/ipc/BrowserHost.cpp263
-rw-r--r--dom/ipc/BrowserHost.h108
-rw-r--r--dom/ipc/BrowserParent.cpp4125
-rw-r--r--dom/ipc/BrowserParent.h1018
-rw-r--r--dom/ipc/CSPMessageUtils.cpp51
-rw-r--r--dom/ipc/CSPMessageUtils.h25
-rw-r--r--dom/ipc/ClonedErrorHolder.cpp352
-rw-r--r--dom/ipc/ClonedErrorHolder.h104
-rw-r--r--dom/ipc/CoalescedInputData.h52
-rw-r--r--dom/ipc/CoalescedMouseData.cpp116
-rw-r--r--dom/ipc/CoalescedMouseData.h57
-rw-r--r--dom/ipc/CoalescedWheelData.cpp46
-rw-r--r--dom/ipc/CoalescedWheelData.h30
-rw-r--r--dom/ipc/ColorPickerParent.cpp76
-rw-r--r--dom/ipc/ColorPickerParent.h54
-rw-r--r--dom/ipc/ContentChild.cpp4721
-rw-r--r--dom/ipc/ContentChild.h945
-rw-r--r--dom/ipc/ContentParent.cpp7516
-rw-r--r--dom/ipc/ContentParent.h1671
-rw-r--r--dom/ipc/ContentProcess.cpp204
-rw-r--r--dom/ipc/ContentProcess.h53
-rw-r--r--dom/ipc/ContentProcessManager.cpp135
-rw-r--r--dom/ipc/ContentProcessManager.h92
-rw-r--r--dom/ipc/DOMTypes.ipdlh396
-rw-r--r--dom/ipc/DocShellMessageUtils.cpp40
-rw-r--r--dom/ipc/DocShellMessageUtils.h54
-rw-r--r--dom/ipc/EffectsInfo.h55
-rw-r--r--dom/ipc/FilePickerParent.cpp291
-rw-r--r--dom/ipc/FilePickerParent.h97
-rw-r--r--dom/ipc/IdType.h67
-rw-r--r--dom/ipc/InProcessChild.h71
-rw-r--r--dom/ipc/InProcessImpl.cpp304
-rw-r--r--dom/ipc/InProcessParent.h76
-rw-r--r--dom/ipc/MMPrinter.cpp81
-rw-r--r--dom/ipc/MMPrinter.h34
-rw-r--r--dom/ipc/ManifestMessagesChild.jsm121
-rw-r--r--dom/ipc/MaybeDiscarded.h135
-rw-r--r--dom/ipc/MemMapSnapshot.cpp44
-rw-r--r--dom/ipc/MemMapSnapshot.h53
-rw-r--r--dom/ipc/MemoryReportRequest.cpp170
-rw-r--r--dom/ipc/MemoryReportRequest.h75
-rw-r--r--dom/ipc/MemoryReportTypes.ipdlh22
-rw-r--r--dom/ipc/NativeThreadId.h16
-rw-r--r--dom/ipc/PBrowser.ipdl1048
-rw-r--r--dom/ipc/PBrowserBridge.ipdl129
-rw-r--r--dom/ipc/PColorPicker.ipdl27
-rw-r--r--dom/ipc/PContent.ipdl1865
-rw-r--r--dom/ipc/PContentPermission.ipdlh19
-rw-r--r--dom/ipc/PContentPermissionRequest.ipdl26
-rw-r--r--dom/ipc/PCycleCollectWithLogs.ipdl22
-rw-r--r--dom/ipc/PFilePicker.ipdl53
-rw-r--r--dom/ipc/PInProcess.ipdl28
-rw-r--r--dom/ipc/PLoginReputation.ipdl26
-rw-r--r--dom/ipc/PPluginWidget.ipdl63
-rw-r--r--dom/ipc/PProcessHangMonitor.ipdl57
-rw-r--r--dom/ipc/PTabContext.ipdlh62
-rw-r--r--dom/ipc/PURLClassifier.ipdl23
-rw-r--r--dom/ipc/PURLClassifierInfo.ipdlh15
-rw-r--r--dom/ipc/PURLClassifierLocal.ipdl35
-rw-r--r--dom/ipc/PVsync.ipdl40
-rw-r--r--dom/ipc/PWindowGlobal.ipdl203
-rw-r--r--dom/ipc/PermissionMessageUtils.cpp54
-rw-r--r--dom/ipc/PermissionMessageUtils.h76
-rw-r--r--dom/ipc/PreallocatedProcessManager.cpp428
-rw-r--r--dom/ipc/PreallocatedProcessManager.h73
-rw-r--r--dom/ipc/PrefsTypes.ipdlh31
-rw-r--r--dom/ipc/ProcessActor.cpp38
-rw-r--r--dom/ipc/ProcessActor.h36
-rw-r--r--dom/ipc/ProcessHangMonitor.cpp1460
-rw-r--r--dom/ipc/ProcessHangMonitor.h96
-rw-r--r--dom/ipc/ProcessHangMonitorIPC.h28
-rw-r--r--dom/ipc/ProcessPriorityManager.cpp978
-rw-r--r--dom/ipc/ProcessPriorityManager.h84
-rw-r--r--dom/ipc/PropertyBagUtils.cpp261
-rw-r--r--dom/ipc/PropertyBagUtils.h39
-rw-r--r--dom/ipc/RefMessageBodyService.cpp159
-rw-r--r--dom/ipc/RefMessageBodyService.h137
-rw-r--r--dom/ipc/ReferrerInfoUtils.cpp55
-rw-r--r--dom/ipc/ReferrerInfoUtils.h25
-rw-r--r--dom/ipc/RemoteBrowser.cpp31
-rw-r--r--dom/ipc/RemoteBrowser.h74
-rw-r--r--dom/ipc/RemoteType.h32
-rw-r--r--dom/ipc/RemoteWebProgress.cpp58
-rw-r--r--dom/ipc/RemoteWebProgress.h39
-rw-r--r--dom/ipc/RemoteWebProgressRequest.cpp256
-rw-r--r--dom/ipc/RemoteWebProgressRequest.h38
-rw-r--r--dom/ipc/ServiceWorkerConfiguration.ipdlh18
-rw-r--r--dom/ipc/SharedMap.cpp463
-rw-r--r--dom/ipc/SharedMap.h360
-rw-r--r--dom/ipc/SharedMapChangeEvent.h50
-rw-r--r--dom/ipc/SharedMessageBody.cpp297
-rw-r--r--dom/ipc/SharedMessageBody.h112
-rw-r--r--dom/ipc/SharedStringMap.cpp143
-rw-r--r--dom/ipc/SharedStringMap.h224
-rw-r--r--dom/ipc/StringTable.h117
-rw-r--r--dom/ipc/StructuredCloneData.cpp427
-rw-r--r--dom/ipc/StructuredCloneData.h301
-rw-r--r--dom/ipc/TabContext.cpp154
-rw-r--r--dom/ipc/TabContext.h217
-rw-r--r--dom/ipc/TabMessageTypes.h29
-rw-r--r--dom/ipc/TabMessageUtils.cpp26
-rw-r--r--dom/ipc/TabMessageUtils.h133
-rw-r--r--dom/ipc/URLClassifierChild.h92
-rw-r--r--dom/ipc/URLClassifierParent.cpp186
-rw-r--r--dom/ipc/URLClassifierParent.h90
-rw-r--r--dom/ipc/UserActivationIPCUtils.h28
-rw-r--r--dom/ipc/VsyncChild.cpp75
-rw-r--r--dom/ipc/VsyncChild.h53
-rw-r--r--dom/ipc/VsyncParent.cpp107
-rw-r--r--dom/ipc/VsyncParent.h54
-rw-r--r--dom/ipc/WindowGlobalActor.cpp169
-rw-r--r--dom/ipc/WindowGlobalActor.h55
-rw-r--r--dom/ipc/WindowGlobalChild.cpp700
-rw-r--r--dom/ipc/WindowGlobalChild.h195
-rw-r--r--dom/ipc/WindowGlobalParent.cpp1322
-rw-r--r--dom/ipc/WindowGlobalParent.h351
-rw-r--r--dom/ipc/WindowGlobalTypes.ipdlh38
-rw-r--r--dom/ipc/fuzztest/content_parent_ipc_libfuzz.cpp33
-rw-r--r--dom/ipc/fuzztest/moz.build20
-rw-r--r--dom/ipc/jar.mn9
-rw-r--r--dom/ipc/jsactor/JSActor.cpp479
-rw-r--r--dom/ipc/jsactor/JSActor.h178
-rw-r--r--dom/ipc/jsactor/JSActorManager.cpp225
-rw-r--r--dom/ipc/jsactor/JSActorManager.h94
-rw-r--r--dom/ipc/jsactor/JSActorService.cpp306
-rw-r--r--dom/ipc/jsactor/JSActorService.h111
-rw-r--r--dom/ipc/jsactor/JSProcessActorChild.cpp105
-rw-r--r--dom/ipc/jsactor/JSProcessActorChild.h57
-rw-r--r--dom/ipc/jsactor/JSProcessActorParent.cpp109
-rw-r--r--dom/ipc/jsactor/JSProcessActorParent.h64
-rw-r--r--dom/ipc/jsactor/JSProcessActorProtocol.cpp169
-rw-r--r--dom/ipc/jsactor/JSProcessActorProtocol.h76
-rw-r--r--dom/ipc/jsactor/JSWindowActorChild.cpp155
-rw-r--r--dom/ipc/jsactor/JSWindowActorChild.h84
-rw-r--r--dom/ipc/jsactor/JSWindowActorParent.cpp121
-rw-r--r--dom/ipc/jsactor/JSWindowActorParent.h68
-rw-r--r--dom/ipc/jsactor/JSWindowActorProtocol.cpp388
-rw-r--r--dom/ipc/jsactor/JSWindowActorProtocol.h99
-rw-r--r--dom/ipc/jsactor/moz.build42
-rw-r--r--dom/ipc/jsactor/nsQueryActor.h90
-rw-r--r--dom/ipc/moz.build247
-rw-r--r--dom/ipc/nsIDOMProcessChild.idl58
-rw-r--r--dom/ipc/nsIDOMProcessParent.idl57
-rw-r--r--dom/ipc/nsIHangReport.idl76
-rw-r--r--dom/ipc/remote-test.js55
-rw-r--r--dom/ipc/test.xhtml259
-rw-r--r--dom/ipc/tests/.eslintrc.js9
-rw-r--r--dom/ipc/tests/JSProcessActor/browser.ini11
-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.js82
-rw-r--r--dom/ipc/tests/JSProcessActor/head.js66
-rw-r--r--dom/ipc/tests/JSWindowActor/audio.oggbin0 -> 14293 bytes
-rw-r--r--dom/ipc/tests/JSWindowActor/browser.ini19
-rw-r--r--dom/ipc/tests/JSWindowActor/browser_contentWindow.js47
-rw-r--r--dom/ipc/tests/JSWindowActor/browser_crash_report.js112
-rw-r--r--dom/ipc/tests/JSWindowActor/browser_destroy_callbacks.js194
-rw-r--r--dom/ipc/tests/JSWindowActor/browser_event_listener.js43
-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.js111
-rw-r--r--dom/ipc/tests/JSWindowActor/browser_process_childid.js27
-rw-r--r--dom/ipc/tests/JSWindowActor/browser_registerWindowActor.js12
-rw-r--r--dom/ipc/tests/JSWindowActor/browser_sendAsyncMessage.js51
-rw-r--r--dom/ipc/tests/JSWindowActor/browser_sendQuery.js117
-rw-r--r--dom/ipc/tests/JSWindowActor/file_mediaPlayback.html2
-rw-r--r--dom/ipc/tests/JSWindowActor/head.js71
-rw-r--r--dom/ipc/tests/blob_verify.sjs20
-rw-r--r--dom/ipc/tests/browser.ini22
-rw-r--r--dom/ipc/tests/browser_CrashService_crash.js72
-rw-r--r--dom/ipc/tests/browser_ProcessPriorityManager.js520
-rw-r--r--dom/ipc/tests/browser_bug1646088.js70
-rw-r--r--dom/ipc/tests/browser_cancel_content_js.js68
-rw-r--r--dom/ipc/tests/browser_crash_oopiframe.js167
-rw-r--r--dom/ipc/tests/browser_domainPolicy.js187
-rw-r--r--dom/ipc/tests/browser_memory_distribution_telemetry.js92
-rw-r--r--dom/ipc/tests/chrome.ini11
-rw-r--r--dom/ipc/tests/file_cancel_content_js.html18
-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.html4
-rw-r--r--dom/ipc/tests/mochitest.ini12
-rw-r--r--dom/ipc/tests/process_error.xhtml63
-rw-r--r--dom/ipc/tests/test_Preallocated.html52
-rw-r--r--dom/ipc/tests/test_bcg_processes.html46
-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_bug1086684.js99
-rw-r--r--dom/ipc/tests/test_child_docshell.js93
-rw-r--r--dom/ipc/tests/test_process_error.xhtml22
-rw-r--r--dom/ipc/tests/test_process_error_oom.xhtml22
-rw-r--r--dom/ipc/tests/test_sharedMap.js382
-rw-r--r--dom/ipc/tests/test_temporaryfile_stream.html84
-rw-r--r--dom/ipc/tests/test_window_open_discarded_bc.html37
-rw-r--r--dom/ipc/tests/xpcshell.ini10
206 files changed, 52620 insertions, 0 deletions
diff --git a/dom/ipc/BrowserBridgeChild.cpp b/dom/ipc/BrowserBridgeChild.cpp
new file mode 100644
index 0000000000..3e3be33388
--- /dev/null
+++ b/dom/ipc/BrowserBridgeChild.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/. */
+
+#ifdef ACCESSIBILITY
+# ifdef XP_WIN
+# include "mozilla/a11y/ProxyAccessible.h"
+# include "mozilla/a11y/ProxyWrappers.h"
+# endif
+# 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;
+
+namespace mozilla::dom {
+
+BrowserBridgeChild::BrowserBridgeChild(BrowsingContext* aBrowsingContext,
+ TabId aId, const LayersId& aLayersId)
+ : mId{aId}, mLayersId{aLayersId}, mBrowsingContext(aBrowsingContext) {}
+
+BrowserBridgeChild::~BrowserBridgeChild() {
+#if defined(ACCESSIBILITY) && defined(XP_WIN)
+ if (mEmbeddedDocAccessible) {
+ mEmbeddedDocAccessible->Shutdown();
+ }
+#endif
+}
+
+already_AddRefed<BrowserBridgeHost> BrowserBridgeChild::FinishInit(
+ nsFrameLoader* aFrameLoader) {
+ MOZ_DIAGNOSTIC_ASSERT(!mFrameLoader);
+ mFrameLoader = aFrameLoader;
+
+ RefPtr<Element> owner = mFrameLoader->GetOwnerContent();
+ nsCOMPtr<nsIDocShell> docShell = do_GetInterface(owner->GetOwnerGlobal());
+ MOZ_DIAGNOSTIC_ASSERT(docShell);
+
+ nsDocShell::Cast(docShell)->OOPChildLoadStarted(this);
+
+#if defined(ACCESSIBILITY)
+ if (a11y::DocAccessible* docAcc =
+ a11y::GetExistingDocAccessible(owner->OwnerDoc())) {
+ if (a11y::Accessible* 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) {
+ Unused << SendActivate(aActionId);
+}
+
+void BrowserBridgeChild::Deactivate(bool aWindowLowering, uint64_t aActionId) {
+ Unused << SendDeactivate(aWindowLowering, aActionId);
+}
+
+void BrowserBridgeChild::SetIsUnderHiddenEmbedderElement(
+ bool aIsUnderHiddenEmbedderElement) {
+ Unused << SendSetIsUnderHiddenEmbedderElement(aIsUnderHiddenEmbedderElement);
+}
+
+/*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::RecvSetEmbeddedDocAccessibleCOMProxy(
+ const a11y::IDispatchHolder& aCOMProxy) {
+#if defined(ACCESSIBILITY) && defined(XP_WIN)
+ MOZ_ASSERT(!aCOMProxy.IsNull());
+ if (mEmbeddedDocAccessible) {
+ mEmbeddedDocAccessible->Shutdown();
+ }
+ RefPtr<IDispatch> comProxy(aCOMProxy.Get());
+ mEmbeddedDocAccessible =
+ new a11y::RemoteIframeDocProxyAccessibleWrap(comProxy);
+#endif
+ 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->ScrollFrameRectIntoView(frame, 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 (!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 (auto* docShell =
+ nsDocShell::Cast(mBrowsingContext->GetParent()->GetDocShell())) {
+ docShell->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();
+}
+
+} // namespace mozilla::dom
diff --git a/dom/ipc/BrowserBridgeChild.h b/dom/ipc/BrowserBridgeChild.h
new file mode 100644
index 0000000000..c6e33300e8
--- /dev/null
+++ b/dom/ipc/BrowserBridgeChild.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_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 {
+
+namespace a11y {
+class RemoteIframeDocProxyAccessibleWrap;
+}
+
+namespace 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);
+
+ void SetIsUnderHiddenEmbedderElement(bool aIsUnderHiddenEmbedderElement);
+
+ already_AddRefed<BrowserBridgeHost> FinishInit(nsFrameLoader* aFrameLoader);
+
+#if defined(ACCESSIBILITY) && defined(XP_WIN)
+ a11y::RemoteIframeDocProxyAccessibleWrap* GetEmbeddedDocAccessible() {
+ return mEmbeddedDocAccessible;
+ }
+#endif
+
+ 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);
+
+ mozilla::ipc::IPCResult RecvSetEmbeddedDocAccessibleCOMProxy(
+ const IDispatchHolder& aCOMProxy);
+
+ mozilla::ipc::IPCResult RecvMaybeFireEmbedderLoadEvents(
+ EmbedderElementEventType aFireEventAtEmbeddingElement);
+
+ mozilla::ipc::IPCResult RecvIntrinsicSizeOrRatioChanged(
+ const Maybe<IntrinsicSize>& aIntrinsicSize,
+ const Maybe<AspectRatio>& aIntrinsicRatio);
+
+ 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) && defined(XP_WIN)
+ RefPtr<a11y::RemoteIframeDocProxyAccessibleWrap> mEmbeddedDocAccessible;
+#endif
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // !defined(mozilla_dom_BrowserBridgeParent_h)
diff --git a/dom/ipc/BrowserBridgeHost.cpp b/dom/ipc/BrowserBridgeHost.cpp
new file mode 100644
index 0000000000..975deb344a
--- /dev/null
+++ b/dom/ipc/BrowserBridgeHost.cpp
@@ -0,0 +1,88 @@
+/* -*- 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();
+}
+
+void BrowserBridgeHost::LoadURL(nsDocShellLoadState* aLoadState) {
+ MOZ_ASSERT(aLoadState);
+ Unused << mBridge->SendLoadURL(aLoadState);
+}
+
+void BrowserBridgeHost::ResumeLoad(uint64_t aPendingSwitchId) {
+ Unused << mBridge->SendResumeLoad(aPendingSwitchId);
+}
+
+void BrowserBridgeHost::DestroyStart() { DestroyComplete(); }
+
+void BrowserBridgeHost::DestroyComplete() {
+ if (!mBridge) {
+ return;
+ }
+
+ Unused << mBridge->Send__delete__(mBridge);
+ 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..0c93100f12
--- /dev/null
+++ b/dom/ipc/BrowserBridgeHost.h
@@ -0,0 +1,70 @@
+/* -*- 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 {
+
+namespace 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;
+
+ 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 dom
+} // namespace mozilla
+
+#endif // mozilla_dom_BrowserBridgeHost_h
diff --git a/dom/ipc/BrowserBridgeParent.cpp b/dom/ipc/BrowserBridgeParent.cpp
new file mode 100644
index 0000000000..4639db64e4
--- /dev/null
+++ b/dom/ipc/BrowserBridgeParent.cpp
@@ -0,0 +1,247 @@
+/* -*- 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"
+#endif
+
+#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");
+
+ RefPtr<CanonicalBrowsingContext> browsingContext =
+ CanonicalBrowsingContext::Get(aWindowInit.context().mBrowsingContextId);
+ if (!browsingContext || browsingContext->IsDiscarded()) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // 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)
+ CanonicalBrowsingContext* ancestor = browsingContext->GetParent();
+ while (ancestor) {
+ if (NS_WARN_IF(ancestor->IsDiscarded())) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ ancestor = ancestor->GetParent();
+ }
+
+ // 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();
+ 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;
+ }
+
+ // 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();
+
+ 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) {
+ mBrowserParent->Destroy();
+ mBrowserParent->SetBrowserBridgeParent(nullptr);
+ mBrowserParent = nullptr;
+ }
+}
+
+IPCResult BrowserBridgeParent::RecvShow(const OwnerShowInfo& aOwnerInfo) {
+ mBrowserParent->AttachLayerManager();
+ 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(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::RecvRenderLayers(
+ const bool& aEnabled, const layers::LayersObserverEpoch& aEpoch) {
+ Unused << mBrowserParent->SendRenderLayers(aEnabled, aEpoch);
+ return IPC_OK();
+}
+
+IPCResult BrowserBridgeParent::RecvNavigateByKey(
+ const bool& aForward, const bool& aForDocumentNavigation) {
+ Unused << mBrowserParent->SendNavigateByKey(aForward, aForDocumentNavigation);
+ return IPC_OK();
+}
+
+IPCResult BrowserBridgeParent::RecvDispatchSynthesizedMouseEvent(
+ const WidgetMouseEvent& aEvent) {
+ if (aEvent.mMessage != eMouseMove ||
+ aEvent.mReason != WidgetMouseEvent::eSynthesized) {
+ return IPC_FAIL(this, "Unexpected event type");
+ }
+
+ WidgetMouseEvent event = aEvent;
+ // 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();
+}
+
+IPCResult BrowserBridgeParent::RecvSetIsUnderHiddenEmbedderElement(
+ const bool& aIsUnderHiddenEmbedderElement) {
+ Unused << mBrowserParent->SendSetIsUnderHiddenEmbedderElement(
+ aIsUnderHiddenEmbedderElement);
+ return IPC_OK();
+}
+
+#ifdef ACCESSIBILITY
+IPCResult BrowserBridgeParent::RecvSetEmbedderAccessible(
+ PDocAccessibleParent* aDoc, uint64_t aID) {
+ mEmbedderAccessibleDoc = static_cast<a11y::DocAccessibleParent*>(aDoc);
+ mEmbedderAccessibleID = aID;
+ if (auto embeddedBrowser = GetBrowserParent()) {
+ a11y::DocAccessibleParent* childDocAcc =
+ embeddedBrowser->GetTopLevelDocAccessible();
+ if (childDocAcc && !childDocAcc->IsShutdown()) {
+ // 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(childDocAcc, aID,
+ /* aCreating */ false);
+ }
+ }
+ return IPC_OK();
+}
+#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..9140e2a60f
--- /dev/null
+++ b/dom/ipc/BrowserBridgeParent.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_BrowserBridgeParent_h
+#define mozilla_dom_BrowserBridgeParent_h
+
+#include "mozilla/dom/PBrowserBridgeParent.h"
+#include "mozilla/Tuple.h"
+#include "mozilla/dom/ipc/IdType.h"
+#include "mozilla/dom/WindowGlobalTypes.h"
+
+namespace mozilla {
+
+namespace a11y {
+class DocAccessibleParent;
+}
+
+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 accessible for this iframe's embedder OuterDocAccessible.
+ * This returns the actor for the containing document and the unique id of
+ * the embedder accessible within that document.
+ */
+ Tuple<a11y::DocAccessibleParent*, uint64_t> GetEmbedderAccessible() {
+ return Tuple<a11y::DocAccessibleParent*, uint64_t>(mEmbedderAccessibleDoc,
+ mEmbedderAccessibleID);
+ }
+#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 RecvRenderLayers(const bool& aEnabled,
+ const LayersObserverEpoch& aEpoch);
+
+ mozilla::ipc::IPCResult RecvNavigateByKey(const bool& aForward,
+ const bool& aForDocumentNavigation);
+
+ 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 RecvSetIsUnderHiddenEmbedderElement(
+ const bool& aIsUnderHiddenEmbedderElement);
+
+#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..9438f34689
--- /dev/null
+++ b/dom/ipc/BrowserChild.cpp
@@ -0,0 +1,4020 @@
+/* -*- 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 "BrowserChild.h"
+
+#ifdef ACCESSIBILITY
+# include "mozilla/a11y/DocAccessibleChild.h"
+#endif
+#include <algorithm>
+#include <utility>
+
+#include "BackgroundChild.h"
+#include "BrowserParent.h"
+#include "ClientLayerManager.h"
+#include "ContentChild.h"
+#include "DocumentInlines.h"
+#include "EventStateManager.h"
+#include "FrameLayerBuilder.h"
+#include "GeckoProfiler.h"
+#include "Layers.h"
+#include "MMPrinter.h"
+#include "PermissionMessageUtils.h"
+#include "PuppetWidget.h"
+#include "StructuredCloneData.h"
+#include "UnitTransforms.h"
+#include "Units.h"
+#include "VRManagerChild.h"
+#include "ipc/nsGUIEventIPC.h"
+#include "js/JSON.h"
+#include "mozilla/AsyncEventDispatcher.h"
+#include "mozilla/ClearOnShutdown.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/Preferences.h"
+#include "mozilla/PresShell.h"
+#include "mozilla/ProcessHangMonitor.h"
+#include "mozilla/ResultExtensions.h"
+#include "mozilla/ScopeExit.h"
+#include "mozilla/Services.h"
+#include "mozilla/StaticPrefs_dom.h"
+#include "mozilla/StaticPrefs_fission.h"
+#include "mozilla/StaticPrefs_layout.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/TextEvents.h"
+#include "mozilla/TouchEvents.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/DocGroup.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/Event.h"
+#include "mozilla/dom/JSWindowActorChild.h"
+#include "mozilla/dom/LoadURIOptionsBinding.h"
+#include "mozilla/dom/MessageManagerBinding.h"
+#include "mozilla/dom/MouseEventBinding.h"
+#include "mozilla/dom/Nullable.h"
+#include "mozilla/dom/PBrowser.h"
+#include "mozilla/dom/PaymentRequestChild.h"
+#include "mozilla/dom/PointerEventHandler.h"
+#include "mozilla/dom/SessionStoreListener.h"
+#include "mozilla/dom/WindowGlobalChild.h"
+#include "mozilla/dom/WindowProxyHolder.h"
+#include "mozilla/gfx/CrossProcessPaint.h"
+#include "mozilla/gfx/Matrix.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/ipc/BackgroundUtils.h"
+#include "mozilla/ipc/PBackgroundChild.h"
+#include "mozilla/ipc/URIUtils.h"
+#include "mozilla/layers/APZCCallbackHelper.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/LayerTransactionChild.h"
+#include "mozilla/layers/ShadowLayers.h"
+#include "mozilla/layers/WebRenderLayerManager.h"
+#include "mozilla/plugins/PPluginWidgetChild.h"
+#include "nsBrowserStatusFilter.h"
+#include "nsColorPickerProxy.h"
+#include "nsCommandParams.h"
+#include "nsContentPermissionHelper.h"
+#include "nsContentUtils.h"
+#include "nsDeviceContext.h"
+#include "nsDocShell.h"
+#include "nsDocShellLoadState.h"
+#include "nsEmbedCID.h"
+#include "nsExceptionHandler.h"
+#include "nsFilePickerProxy.h"
+#include "nsFocusManager.h"
+#include "nsGlobalWindow.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 "nsIScriptError.h"
+#include "nsISecureBrowserUI.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 "nsPointerHashKeys.h"
+#include "nsPrintfCString.h"
+#include "nsQueryActor.h"
+#include "nsQueryObject.h"
+#include "nsRefreshDriver.h"
+#include "nsSandboxFlags.h"
+#include "nsString.h"
+#include "nsTHashtable.h"
+#include "nsThreadManager.h"
+#include "nsThreadUtils.h"
+#include "nsViewManager.h"
+#include "nsViewportInfo.h"
+#include "nsWebBrowser.h"
+#include "nsWindowWatcher.h"
+#include "nsIXULRuntime.h"
+
+#ifdef XP_WIN
+# include "mozilla/plugins/PluginWidgetChild.h"
+#endif
+
+#ifdef MOZ_WAYLAND
+# include "nsAppRunner.h"
+#endif
+
+#ifdef NS_PRINTING
+# include "nsIPrintSession.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::docshell;
+using namespace mozilla::widget;
+using mozilla::layers::GeckoContentController;
+
+NS_IMPL_ISUPPORTS(ContentListener, nsIDOMEventListener)
+
+static const char BEFORE_FIRST_PAINT[] = "before-first-paint";
+
+typedef nsDataHashtable<nsUint64HashKey, BrowserChild*> BrowserChildMap;
+static BrowserChildMap* sBrowserChildren;
+StaticMutex sBrowserChildrenMutex;
+
+already_AddRefed<Document> BrowserChild::GetTopLevelDocument() const {
+ nsCOMPtr<Document> doc;
+ WebNavigation()->GetDocument(getter_AddRefs(doc));
+ 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;
+}
+
+NS_IMETHODIMP
+ContentListener::HandleEvent(Event* aEvent) {
+ RemoteDOMEvent remoteEvent;
+ remoteEvent.mEvent = aEvent;
+ NS_ENSURE_STATE(remoteEvent.mEvent);
+ mBrowserChild->SendEvent(remoteEvent);
+ return NS_OK;
+}
+
+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()},
+ mDidFakeShow(false),
+ mTriedBrowserInit(false),
+ mOrientation(hal::eScreenOrientation_PortraitPrimary),
+ mVsyncChild(nullptr),
+ mIgnoreKeyPressEvent(false),
+ mHasValidInnerSize(false),
+ mDestroyed(false),
+ mDynamicToolbarMaxHeight(0),
+ mUniqueId(aTabId),
+ mIsTopLevel(aIsTopLevel),
+ mHasSiblings(false),
+ mIsTransparent(false),
+ mIPCOpen(false),
+ mDidSetRealShowInfo(false),
+ mDidLoadURLInit(false),
+ mSkipKeyPress(false),
+ mLayersObserverEpoch{1},
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+ mNativeWindowHandle(0),
+#endif
+#if defined(ACCESSIBILITY)
+ mTopLevelDocAccessibleChild(nullptr),
+#endif
+ mShouldSendWebProgressEventsToParent(false),
+ mRenderLayers(true),
+ mPendingDocShellIsActive(false),
+ mPendingDocShellReceivedMessage(false),
+ mPendingRenderLayers(false),
+ mPendingRenderLayersReceivedMessage(false),
+ mPendingLayersObserverEpoch{0},
+ mPendingDocShellBlockers(0),
+ mCancelContentJSEpoch(0),
+ mWidgetNativeData(0) {
+ mozilla::HoldJSObjects(this);
+
+ nsWeakPtr weakPtrThis(do_GetWeakReference(
+ static_cast<nsIBrowserChild*>(this))); // for capture by the lambda
+ mSetAllowedTouchBehaviorCallback =
+ [weakPtrThis](uint64_t aInputBlockId,
+ const nsTArray<TouchBehaviorFlags>& aFlags) {
+ if (nsCOMPtr<nsIBrowserChild> browserChild =
+ do_QueryReferent(weakPtrThis)) {
+ static_cast<BrowserChild*>(browserChild.get())
+ ->SetAllowedTouchBehavior(aInputBlockId, aFlags);
+ }
+ };
+
+ // preloaded BrowserChild should not be added to child map
+ if (mUniqueId) {
+ MOZ_ASSERT(NestedBrowserChildMap().find(mUniqueId) ==
+ NestedBrowserChildMap().end());
+ NestedBrowserChildMap()[mUniqueId] = this;
+ }
+ mCoalesceMouseMoveEvents =
+ Preferences::GetBool("dom.event.coalesce_mouse_move");
+ if (mCoalesceMouseMoveEvents) {
+ mCoalescedMouseEventFlusher = new CoalescedMouseMoveFlusher(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 && doc->IsTopLevelContentDocument()) {
+ 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);
+ }
+}
+
+void BrowserChild::SetAllowedTouchBehavior(
+ uint64_t aInputBlockId,
+ const nsTArray<TouchBehaviorFlags>& aTargets) const {
+ if (mApzcTreeManager) {
+ mApzcTreeManager->SetAllowedTouchBehavior(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?");
+
+ // Set the tab context attributes then pass to docShell
+ NotifyTabContextUpdated();
+
+ // 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);
+ }
+
+ docShell->SetAffectPrivateSessionLifetime(
+ mBrowsingContext->UsePrivateBrowsing() ||
+ mChromeFlags & nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME);
+
+#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 nsGlobalWindow::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);
+
+ if (window->GetCurrentInnerWindow()) {
+ window->SetKeyboardIndicators(ShowFocusRings());
+ } else {
+ // Skip ShouldShowFocusRing check if no inner window is available
+ window->SetInitialKeyboardIndicators(ShowFocusRings());
+ }
+
+ // 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 !defined(MOZ_WIDGET_ANDROID) && !defined(MOZ_THUNDERBIRD) && \
+ !defined(MOZ_SUITE)
+ mSessionStoreListener = new TabListener(docShell, nullptr);
+ rv = mSessionStoreListener->Init();
+ NS_ENSURE_SUCCESS(rv, rv);
+#endif
+
+ // 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;
+}
+
+void BrowserChild::NotifyTabContextUpdated() {
+ nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
+ MOZ_ASSERT(docShell);
+
+ if (!docShell) {
+ return;
+ }
+
+ // Set SANDBOXED_AUXILIARY_NAVIGATION flag if this is a receiver page.
+ if (!PresentationURL().IsEmpty()) {
+ // Return value of setting synced field should be checked. See bug 1656492.
+ Unused << mBrowsingContext->SetSandboxFlags(SANDBOXED_AUXILIARY_NAVIGATION);
+ }
+}
+
+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(mWebBrowserChrome)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mStatusFilter)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mWebNav)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowsingContext)
+ 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(mWebBrowserChrome)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStatusFilter)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWebNav)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowsingContext)
+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(nsIEmbeddingSiteWindow)
+ 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::RemoteSizeShellTo(int32_t aWidth, int32_t aHeight,
+ int32_t aShellItemWidth,
+ int32_t aShellItemHeight) {
+ nsCOMPtr<nsIDocShell> ourDocShell = do_GetInterface(WebNavigation());
+ nsCOMPtr<nsIBaseWindow> docShellAsWin(do_QueryInterface(ourDocShell));
+ NS_ENSURE_STATE(docShellAsWin);
+
+ int32_t width, height;
+ docShellAsWin->GetSize(&width, &height);
+
+ uint32_t flags = 0;
+ if (width == aWidth) {
+ flags |= nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_CX;
+ }
+
+ if (height == aHeight) {
+ flags |= nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_CY;
+ }
+
+ bool sent = SendSizeShellTo(flags, aWidth, aHeight, aShellItemWidth,
+ aShellItemHeight);
+
+ return sent ? NS_OK : NS_ERROR_FAILURE;
+}
+
+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(nsString(aStatusText));
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BrowserChild::SetDimensions(uint32_t aFlags, int32_t aX, int32_t aY,
+ int32_t aCx, int32_t aCy) {
+ // 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 the current value for the non-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 ignore its value.
+ int32_t x, y, cx, cy;
+ GetDimensions(aFlags, &x, &y, &cx, &cy);
+
+ if (x == aX) {
+ aFlags |= nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_X;
+ }
+
+ if (y == aY) {
+ aFlags |= nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_Y;
+ }
+
+ if (cx == aCx) {
+ aFlags |= nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_CX;
+ }
+
+ if (cy == aCy) {
+ aFlags |= nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_CY;
+ }
+
+ double scale = mPuppetWidget ? mPuppetWidget->GetDefaultScale().scale : 1.0;
+
+ Unused << SendSetDimensions(aFlags, aX, aY, aCx, aCy, scale);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BrowserChild::GetDimensions(uint32_t aFlags, int32_t* aX, int32_t* aY,
+ int32_t* aCx, int32_t* aCy) {
+ ScreenIntRect rect = GetOuterRect();
+ 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::SetFocus() { return NS_ERROR_NOT_IMPLEMENTED; }
+
+NS_IMETHODIMP
+BrowserChild::GetVisibility(bool* aVisibility) {
+ *aVisibility = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BrowserChild::SetVisibility(bool aVisibility) {
+ // should the platform support this? Bug 666365
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BrowserChild::GetTitle(nsAString& aTitle) {
+ NS_WARNING("BrowserChild::GetTitle not supported in BrowserChild");
+
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+BrowserChild::SetTitle(const nsAString& aTitle) {
+ // JavaScript sends the "DOMTitleChanged" event to the parent
+ // via the message manager.
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BrowserChild::GetSiteWindow(void** aSiteWindow) {
+ NS_WARNING("BrowserChild::GetSiteWindow not supported in BrowserChild");
+
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+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) {
+ if (aIID.Equals(NS_GET_IID(nsIWebBrowserChrome3))) {
+ return GetWebBrowserChrome(reinterpret_cast<nsIWebBrowserChrome3**>(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,
+ bool aWidthSpecified, nsIURI* aURI,
+ const nsAString& aName, const nsACString& aFeatures,
+ bool aForceNoOpener, bool aForceNoReferrer,
+ nsDocShellLoadState* aLoadState, bool* aWindowIsNew,
+ BrowsingContext** aReturn) {
+ *aReturn = nullptr;
+
+ RefPtr<BrowsingContext> parent = aOpenWindowInfo->GetParent();
+
+ int32_t openLocation = nsWindowWatcher::GetWindowOpenLocation(
+ parent->GetDOMWindow(), aChromeFlags, aCalledFromJS, aWidthSpecified,
+ 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(this, aOpenWindowInfo, aChromeFlags,
+ aCalledFromJS, aWidthSpecified, aURI, aName,
+ aFeatures, aForceNoOpener, aForceNoReferrer,
+ 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 (mSessionStoreListener) {
+ mSessionStoreListener->RemoveListeners();
+ mSessionStoreListener = 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};
+ }
+}
+
+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();
+ }
+ }
+
+ CompositorBridgeChild* compositorChild = CompositorBridgeChild::Get();
+ if (compositorChild) {
+ compositorChild->CancelNotifyAfterRemotePaint(this);
+ }
+
+ 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(
+ WillChangeProcessResolver&& aResolve) {
+ if (mWebBrowser) {
+ mWebBrowser->SetWillChangeProcess();
+ }
+ aResolve(true);
+ 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());
+ MOZ_ASSERT(docShell);
+ if (!docShell) {
+ NS_WARNING("WebNavigation does not have a docshell");
+ return IPC_OK();
+ }
+ docShell->LoadURI(aLoadState, true);
+
+ nsDocShell::Cast(docShell)->MaybeClearStorageAccessFlag();
+
+ CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::URL, spec);
+ 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();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvCloneDocumentTreeIntoSelf(
+ const MaybeDiscarded<BrowsingContext>& aSourceBC) {
+ if (NS_WARN_IF(aSourceBC.IsNullOrDiscarded())) {
+ return IPC_OK();
+ }
+ nsCOMPtr<Document> sourceDocument = aSourceBC.get()->GetDocument();
+ if (NS_WARN_IF(!sourceDocument)) {
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsIDocShell> ourDocShell = do_GetInterface(WebNavigation());
+ if (NS_WARN_IF(!ourDocShell)) {
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsIContentViewer> cv;
+ ourDocShell->GetContentViewer(getter_AddRefs(cv));
+ if (NS_WARN_IF(!cv)) {
+ return IPC_OK();
+ }
+
+ RefPtr<Document> clone;
+ {
+ AutoPrintEventDispatcher dispatcher(*sourceDocument);
+ nsAutoScriptBlocker scriptBlocker;
+ bool hasInProcessCallbacks = false;
+ clone = sourceDocument->CreateStaticClone(ourDocShell, cv,
+ &hasInProcessCallbacks);
+ if (NS_WARN_IF(!clone)) {
+ return IPC_OK();
+ }
+ }
+
+ // Since the clone document is not parsed-created, we need to initialize
+ // layout manually. This is usually done in ReflowPrintObject for non-remote
+ // documents.
+ if (RefPtr<PresShell> ps = clone->GetPresShell()) {
+ if (!ps->DidInitialize()) {
+ nsresult rv = ps->Initialize();
+ Unused << NS_WARN_IF(NS_FAILED(rv));
+ }
+ }
+
+ 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);
+ 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());
+
+ mOrientation = aDimensionInfo.orientation();
+ 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;
+
+ // 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::RecvSetIsUnderHiddenEmbedderElement(
+ const bool& aIsUnderHiddenEmbedderElement) {
+ if (RefPtr<PresShell> presShell = GetTopLevelPresShell()) {
+ presShell->SetIsUnderHiddenEmbedderElement(aIsUnderHiddenEmbedderElement);
+ }
+ 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) {
+ 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();
+ CSSRect zoomToRect = CalculateRectToZoomTo(document, aPoint);
+ // 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, zoomToRect, DEFAULT_BEHAVIOR);
+ }
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvHandleTap(
+ const GeckoContentController::TapType& aType,
+ const LayoutDevicePoint& aPoint, const Modifiers& aModifiers,
+ const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId) {
+ // 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) {
+ return IPC_OK();
+ }
+ if (!presShell->GetPresContext()) {
+ 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) {
+ mAPZEventState->ProcessSingleTap(point, scale, aModifiers, 1);
+ }
+ break;
+ case GeckoContentController::TapType::eDoubleTap:
+ HandleDoubleTap(point, aModifiers, aGuid);
+ break;
+ case GeckoContentController::TapType::eSecondTap:
+ if (mBrowserChildMessageManager) {
+ mAPZEventState->ProcessSingleTap(point, scale, aModifiers, 2);
+ }
+ 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) {
+ // 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);
+}
+
+bool BrowserChild::NotifyAPZStateChange(
+ const ViewID& aViewId,
+ const layers::GeckoContentController::APZStateChange& aChange,
+ const int& aArg) {
+ mAPZEventState->ProcessAPZStateChange(aViewId, aChange, aArg);
+ 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.
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ observerService->NotifyObservers(nullptr, "APZ:TransformEnd", nullptr);
+ }
+ return true;
+}
+
+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, 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::RecvSetKeyboardIndicators(
+ const UIStateChangeType& aShowFocusRings) {
+ nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation());
+ NS_ENSURE_TRUE(window, IPC_OK());
+ window->SetKeyboardIndicators(aShowFocusRings);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvStopIMEStateManagement() {
+ IMEStateManager::StopIMEStateManagement();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvMouseEvent(
+ const nsString& aType, const float& aX, const float& aY,
+ const int32_t& aButton, const int32_t& aClickCount,
+ const int32_t& aModifiers) {
+ // 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();
+ APZCCallbackHelper::DispatchMouseEvent(presShell, aType, CSSPoint(aX, aY),
+ aButton, aClickCount, aModifiers,
+ MouseEvent_Binding::MOZ_SOURCE_UNKNOWN,
+ 0 /* Use the default value here. */);
+ return IPC_OK();
+}
+
+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);
+}
+
+ScreenRect BrowserChild::GetTopLevelViewportVisibleRectInBrowserCoords() const {
+ return mTopLevelViewportVisibleRectInBrowserCoords;
+}
+
+void BrowserChild::FlushAllCoalescedMouseData() {
+ MOZ_ASSERT(mCoalesceMouseMoveEvents);
+
+ // Move all entries from mCoalescedMouseData to mToBeDispatchedMouseData.
+ for (auto iter = mCoalescedMouseData.Iter(); !iter.Done(); iter.Next()) {
+ CoalescedMouseData* data = iter.UserData();
+ 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.LookupOrAdd(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 = new CoalescedMouseData();
+ mCoalescedMouseData.Put(aEvent.pointerId, newData);
+ 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.
+ UniquePtr<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->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()) {
+ Unused << postLayerization.release();
+ }
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvNormalPriorityRealMouseButtonEvent(
+ const WidgetMouseEvent& aEvent, const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId) {
+ return RecvRealMouseButtonEvent(aEvent, aGuid, aInputBlockId);
+}
+
+// In case handling repeated mouse wheel takes much time, we skip firing current
+// wheel event if it may be coalesced to the next one.
+bool BrowserChild::MaybeCoalesceWheelEvent(const WidgetWheelEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId,
+ bool* aIsNextWheelEvent) {
+ MOZ_ASSERT(aIsNextWheelEvent);
+ if (aEvent.mMessage == eWheel) {
+ GetIPCChannel()->PeekMessages(
+ [aIsNextWheelEvent](const IPC::Message& aMsg) -> bool {
+ if (aMsg.type() == mozilla::dom::PBrowser::Msg_MouseWheelEvent__ID) {
+ *aIsNextWheelEvent = true;
+ }
+ return false; // Stop peeking.
+ });
+ // We only coalesce the current event when
+ // 1. It's eWheel (we don't coalesce eOperationStart and eWheelOperationEnd)
+ // 2. It's not the first wheel event.
+ // 3. It's not the last wheel event.
+ // 4. It's dispatched before the last wheel event was processed +
+ // the processing time of the last event.
+ // This way pages spending lots of time in wheel listeners get wheel
+ // events coalesced more aggressively.
+ // 5. It has same attributes as the coalesced wheel event which is not yet
+ // fired.
+ if (!mLastWheelProcessedTimeFromParent.IsNull() && *aIsNextWheelEvent &&
+ aEvent.mTimeStamp < (mLastWheelProcessedTimeFromParent +
+ mLastWheelProcessingDuration) &&
+ (mCoalescedWheelData.IsEmpty() ||
+ mCoalescedWheelData.CanCoalesce(aEvent, aGuid, aInputBlockId))) {
+ mCoalescedWheelData.Coalesce(aEvent, aGuid, aInputBlockId);
+ return true;
+ }
+ }
+ return false;
+}
+
+nsEventStatus BrowserChild::DispatchWidgetEventViaAPZ(WidgetGUIEvent& aEvent) {
+ aEvent.ResetWaitingReplyFromRemoteProcessState();
+ return APZCCallbackHelper::DispatchWidgetEvent(aEvent);
+}
+
+void BrowserChild::MaybeDispatchCoalescedWheelEvent() {
+ if (mCoalescedWheelData.IsEmpty()) {
+ return;
+ }
+ 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());
+ UniquePtr<DisplayportSetListener> postLayerization =
+ APZCCallbackHelper::SendSetTargetAPZCNotification(
+ mPuppetWidget, document, aEvent, aGuid.mLayersId, aInputBlockId);
+ if (postLayerization && postLayerization->Register()) {
+ Unused << postLayerization.release();
+ }
+ }
+
+ 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->ProcessWheelEvent(localEvent, aInputBlockId);
+ }
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvMouseWheelEvent(
+ const WidgetWheelEvent& aEvent, const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId) {
+ bool isNextWheelEvent = false;
+ if (MaybeCoalesceWheelEvent(aEvent, aGuid, aInputBlockId,
+ &isNextWheelEvent)) {
+ return IPC_OK();
+ }
+ if (isNextWheelEvent) {
+ // Update mLastWheelProcessedTimeFromParent so that we can compare the end
+ // time of the current event with the dispatched time of the next event.
+ mLastWheelProcessedTimeFromParent = aEvent.mTimeStamp;
+ mozilla::TimeStamp beforeDispatchingTime = TimeStamp::Now();
+ MaybeDispatchCoalescedWheelEvent();
+ DispatchWheelEvent(aEvent, aGuid, aInputBlockId);
+ mLastWheelProcessingDuration = (TimeStamp::Now() - beforeDispatchingTime);
+ mLastWheelProcessedTimeFromParent += mLastWheelProcessingDuration;
+ } else {
+ // This is the last wheel event. Set mLastWheelProcessedTimeFromParent to
+ // null moment to avoid coalesce the next incoming wheel event.
+ mLastWheelProcessedTimeFromParent = TimeStamp();
+ MaybeDispatchCoalescedWheelEvent();
+ 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));
+
+ 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();
+ if (StaticPrefs::layout_css_touch_action_enabled()) {
+ allowedTouchBehaviors =
+ APZCCallbackHelper::SendSetAllowedTouchBehaviorNotification(
+ mPuppetWidget, document, localEvent, aInputBlockId,
+ mSetAllowedTouchBehaviorCallback);
+ }
+ UniquePtr<DisplayportSetListener> postLayerization =
+ APZCCallbackHelper::SendSetTargetAPZCNotification(
+ mPuppetWidget, document, localEvent, aGuid.mLayersId,
+ aInputBlockId);
+ if (postLayerization && postLayerization->Register()) {
+ Unused << postLayerization.release();
+ }
+ }
+
+ // 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();
+ }
+
+ 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 (!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(nsIWidget::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 nsIWidget::NativeKeyBindingsForSingleLineEditor:
+ case nsIWidget::NativeKeyBindingsForMultiLineEditor:
+ case nsIWidget::NativeKeyBindingsForRichTextEditor:
+ 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(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::RecvFlushTabState(
+ const uint32_t& aFlushId, const bool& aIsFinal) {
+ UpdateSessionStore(aFlushId, aIsFinal);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvUpdateEpoch(const uint32_t& aEpoch) {
+ mSessionStoreListener->SetEpoch(aEpoch);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvUpdateSHistory(
+ const bool& aImmediately) {
+ if (mSessionStoreListener) {
+ mSessionStoreListener->UpdateSHistoryChanges(aImmediately);
+ }
+ 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) {
+ if (SkipRepeatedKeyEvent(aEvent)) {
+ return IPC_OK();
+ }
+
+ MOZ_ASSERT(
+ aEvent.mMessage != eKeyPress || aEvent.AreAllEditCommandsInitialized(),
+ "eKeyPress event should have native key binding information");
+
+ // If content code called preventDefault() on a keydown event, then we don't
+ // want to process any following keypress events.
+ if (aEvent.mMessage == eKeyPress && mIgnoreKeyPressEvent) {
+ return IPC_OK();
+ }
+
+ WidgetKeyboardEvent localEvent(aEvent);
+ localEvent.mWidget = mPuppetWidget;
+ localEvent.mUniqueId = aEvent.mUniqueId;
+ 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 a response is desired from the content process, resend the key event.
+ if (aEvent.WantReplyFromContentProcess()) {
+ // 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, peventDefault() 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();
+ }
+ // 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);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvNormalPriorityRealKeyEvent(
+ const WidgetKeyboardEvent& aEvent) {
+ return RecvRealKeyEvent(aEvent);
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvCompositionEvent(
+ const WidgetCompositionEvent& aEvent) {
+ WidgetCompositionEvent localEvent(aEvent);
+ localEvent.mWidget = mPuppetWidget;
+ DispatchWidgetEventViaAPZ(localEvent);
+ Unused << SendOnEventNeedingAckHandled(aEvent.mMessage);
+ 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);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvNormalPrioritySelectionEvent(
+ const WidgetSelectionEvent& aEvent) {
+ return RecvSelectionEvent(aEvent);
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvPasteTransferable(
+ const IPCDataTransfer& aDataTransfer, const bool& aIsPrivateData,
+ nsIPrincipal* aRequestingPrincipal,
+ const nsContentPolicyType& aContentPolicyType) {
+ 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(
+ aDataTransfer, aIsPrivateData, aRequestingPrincipal, aContentPolicyType,
+ trans, nullptr, this);
+ 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 uint32_t&,
+ const IAccessibleHolder&) {
+ 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
+
+PColorPickerChild* BrowserChild::AllocPColorPickerChild(const nsString&,
+ const nsString&) {
+ MOZ_CRASH("unused");
+ return nullptr;
+}
+
+bool BrowserChild::DeallocPColorPickerChild(PColorPickerChild* aColorPicker) {
+ nsColorPickerProxy* picker = static_cast<nsColorPickerProxy*>(aColorPicker);
+ NS_RELEASE(picker);
+ return true;
+}
+
+PFilePickerChild* BrowserChild::AllocPFilePickerChild(const nsString&,
+ const int16_t&) {
+ MOZ_CRASH("unused");
+ return nullptr;
+}
+
+bool BrowserChild::DeallocPFilePickerChild(PFilePickerChild* actor) {
+ nsFilePickerProxy* filePicker = static_cast<nsFilePickerProxy*>(actor);
+ NS_RELEASE(filePicker);
+ return true;
+}
+
+PVsyncChild* BrowserChild::AllocPVsyncChild() {
+ RefPtr<dom::VsyncChild> actor = new VsyncChild();
+ // There still has one ref-count after return, and it will be released in
+ // DeallocPVsyncChild().
+ return actor.forget().take();
+}
+
+bool BrowserChild::DeallocPVsyncChild(PVsyncChild* aActor) {
+ MOZ_ASSERT(aActor);
+
+ // This actor already has one ref-count. Please check AllocPVsyncChild().
+ RefPtr<VsyncChild> actor = dont_AddRef(static_cast<VsyncChild*>(aActor));
+ return true;
+}
+
+RefPtr<VsyncChild> 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 (!IsWaylandDisabled() && !mVsyncChild) {
+ PVsyncChild* actor = SendPVsyncConstructor();
+ mVsyncChild = static_cast<VsyncChild*>(actor);
+ }
+#endif
+ return mVsyncChild;
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvActivateFrameEvent(
+ const nsString& aType, const bool& capture) {
+ nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation());
+ NS_ENSURE_TRUE(window, IPC_OK());
+ nsCOMPtr<EventTarget> chromeHandler = window->GetChromeEventHandler();
+ NS_ENSURE_TRUE(chromeHandler, IPC_OK());
+ RefPtr<ContentListener> listener = new ContentListener(this);
+ chromeHandler->AddEventListener(aType, listener, capture);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvLoadRemoteScript(
+ const nsString& 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 nsString& 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;
+ UnpackClonedMessageDataForChild(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 mozilla::Maybe<uint64_t>& aSourceOuterWindowID,
+ 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) {
+ aCallback(
+ PrintPreviewResultInfo(0, 0, false, false, false)); // signal error
+ }
+ });
+
+ RefPtr<nsGlobalWindowOuter> sourceWindow;
+ if (aSourceOuterWindowID) {
+ sourceWindow =
+ nsGlobalWindowOuter::GetOuterWindowWithId(aSourceOuterWindowID.value());
+ 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->GetNewPrintSettings(getter_AddRefs(printSettings));
+ if (NS_WARN_IF(!printSettings)) {
+ return IPC_OK();
+ }
+ printSettingsSvc->DeserializeToPrintSettings(aPrintData, printSettings);
+
+ nsCOMPtr<nsIDocShell> docShellToCloneInto;
+ if (aSourceOuterWindowID) {
+ docShellToCloneInto = do_GetInterface(WebNavigation());
+ if (NS_WARN_IF(!docShellToCloneInto)) {
+ return IPC_OK();
+ }
+ }
+
+ sourceWindow->Print(printSettings,
+ /* 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 uint64_t& aOuterWindowID,
+ const PrintData& aPrintData) {
+#ifdef NS_PRINTING
+ RefPtr<nsGlobalWindowOuter> outerWindow =
+ nsGlobalWindowOuter::GetOuterWindowWithId(aOuterWindowID);
+ 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->GetNewPrintSettings(getter_AddRefs(printSettings));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsIPrintSession> printSession =
+ do_CreateInstance("@mozilla.org/gfx/printsession;1", &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return IPC_OK();
+ }
+
+ printSettings->SetPrintSession(printSession);
+ printSettingsSvc->DeserializeToPrintSettings(aPrintData, printSettings);
+ {
+ IgnoredErrorResult rv;
+ outerWindow->Print(printSettings,
+ /* 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 == false);
+ 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, const layers::LayersObserverEpoch& aEpoch) {
+ if (mPendingDocShellBlockers > 0) {
+ mPendingRenderLayersReceivedMessage = true;
+ mPendingRenderLayers = aEnabled;
+ mPendingLayersObserverEpoch = aEpoch;
+ return IPC_OK();
+ }
+
+ // Since requests to change the rendering state come in from both the hang
+ // monitor channel and the PContent channel, we have an ordering problem. This
+ // code ensures that we respect the order in which the requests were made and
+ // ignore stale requests.
+ if (mLayersObserverEpoch >= aEpoch) {
+ return IPC_OK();
+ }
+ mLayersObserverEpoch = aEpoch;
+
+ 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(mLayersObserverEpoch);
+ }
+ });
+
+ if (aEnabled) {
+ ProcessHangMonitor::MaybeStartPaintWhileInterruptingJS();
+ }
+
+ if (mCompositorOptions) {
+ MOZ_ASSERT(mPuppetWidget);
+ RefPtr<LayerManager> lm = mPuppetWidget->GetLayerManager();
+ MOZ_ASSERT(lm);
+
+ // We send the current layer observer epoch to the compositor so that
+ // BrowserParent knows whether a layer update notification corresponds to
+ // the latest RecvRenderLayers request that was made.
+ lm->SetLayersObserverEpoch(mLayersObserverEpoch);
+ }
+
+ mRenderLayers = aEnabled;
+
+ if (aEnabled && IsVisible()) {
+ // This request is a no-op.
+ // In this case, we still want a MozLayerTreeReady notification to fire
+ // in the parent (so that it knows that the child has updated its epoch).
+ // PaintWhileInterruptingJSNoOp does that.
+ if (IPCOpen()) {
+ Unused << SendPaintWhileInterruptingJSNoOp(mLayersObserverEpoch);
+ }
+ return IPC_OK();
+ }
+
+ // FIXME(emilio): Probably / maybe this shouldn't be needed? See the comment
+ // in MakeVisible(), having the two separate states is not great.
+ UpdateVisibility();
+
+ if (!aEnabled) {
+ 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()) {
+ FrameLayerBuilder::InvalidateAllLayersForFrame(
+ nsLayoutUtils::GetDisplayRootFrame(root));
+ 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->Paint(view, view->GetBounds(), PaintFlags::PaintLayers);
+ }
+ }
+ 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();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvHandledWindowedPluginKeyEvent(
+ const NativeEventData& aKeyEventData, const bool& aIsConsumed) {
+ if (NS_WARN_IF(!mPuppetWidget)) {
+ return IPC_OK();
+ }
+ mPuppetWidget->HandledWindowedPluginKeyEvent(aKeyEventData, aIsConsumed);
+ return IPC_OK();
+}
+
+bool BrowserChild::InitBrowserChildMessageManager() {
+ 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->Get(uint64_t(aLayersId)));
+ sBrowserChildren->Put(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->HasLayerManager() ||
+ mPuppetWidget->GetLayerManager()->GetBackendType() ==
+ layers::LayersBackend::LAYERS_BASIC);
+ 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();
+ RefPtr<LayerManager> lm = mPuppetWidget->GetLayerManager();
+ MOZ_ASSERT(lm);
+ lm->SetLayersObserverEpoch(mLayersObserverEpoch);
+ } else {
+ NS_WARNING("Fallback to BasicLayerManager");
+ 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);
+
+ bool success = false;
+ if (mCompositorOptions->UseWebRender()) {
+ success = mPuppetWidget->CreateRemoteLayerManager(
+ [&](LayerManager* aLayerManager) -> bool {
+ MOZ_ASSERT(aLayerManager->AsWebRenderLayerManager());
+ nsCString error;
+ return aLayerManager->AsWebRenderLayerManager()->Initialize(
+ aCompositorChild, wr::AsPipelineId(mLayersId),
+ &mTextureFactoryIdentifier, error);
+ });
+ } else {
+ nsTArray<LayersBackend> ignored;
+ PLayerTransactionChild* shadowManager =
+ aCompositorChild->SendPLayerTransactionConstructor(ignored,
+ GetLayersId());
+ if (shadowManager &&
+ shadowManager->SendGetTextureFactoryIdentifier(
+ &mTextureFactoryIdentifier) &&
+ mTextureFactoryIdentifier.mParentBackend !=
+ LayersBackend::LAYERS_NONE) {
+ success = true;
+ }
+ if (!success) {
+ // Since no LayerManager is associated with the tab's widget, we will
+ // never have an opportunity to destroy the PLayerTransaction on the next
+ // device or compositor reset. Therefore, we make sure to forcefully close
+ // it here. Failure to do so will cause the next layer tree to fail to
+ // attach due since the compositor requires the old layer tree to be
+ // disassociated.
+ if (shadowManager) {
+ static_cast<LayerTransactionChild*>(shadowManager)->Destroy();
+ shadowManager = nullptr;
+ }
+ NS_WARNING("failed to allocate layer transaction");
+ } else {
+ success = mPuppetWidget->CreateRemoteLayerManager(
+ [&](LayerManager* aLayerManager) -> bool {
+ ShadowLayerForwarder* lf = aLayerManager->AsShadowForwarder();
+ lf->SetShadowManager(shadowManager);
+ lf->IdentifyTextureHost(mTextureFactoryIdentifier);
+ return true;
+ });
+ }
+ }
+ return success;
+}
+
+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);
+ 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) {
+ nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
+ if (docShell) {
+ // 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) {
+ if (nsIFrame* root = presShell->GetRootFrame()) {
+ root->InvalidateFrame();
+ }
+ }
+ }
+ }
+
+ return IPC_OK();
+}
+
+bool BrowserChild::IsVisible() {
+ return mPuppetWidget && mPuppetWidget->IsVisible();
+}
+
+void BrowserChild::UpdateVisibility() {
+ bool shouldBeVisible = mIsTopLevel ? mRenderLayers : mEffectsInfo.IsVisible();
+ bool isVisible = IsVisible();
+
+ if (shouldBeVisible != isVisible) {
+ if (shouldBeVisible) {
+ MakeVisible();
+ } else {
+ MakeHidden();
+ }
+ }
+}
+
+void BrowserChild::MakeVisible() {
+ if (IsVisible()) {
+ return;
+ }
+
+ if (mPuppetWidget) {
+ mPuppetWidget->Show(true);
+ }
+
+ nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
+ if (!docShell) {
+ return;
+ }
+
+ // The browser / tab-switcher is responsible of fixing the browsingContext
+ // state up explicitly via SetDocShellIsActive, which propagates to children
+ // automatically.
+ //
+ // We need it not to be observable, as this used via RecvRenderLayers and co.,
+ // for stuff like async tab warming.
+ //
+ // We don't want to go through the docshell 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...
+ if (RefPtr<PresShell> presShell = docShell->GetPresShell()) {
+ presShell->SetIsActive(true);
+ }
+}
+
+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 && mPuppetWidget->HasLayerManager()) {
+ ClearCachedResources();
+ }
+
+ if (nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation())) {
+ // Hide all plugins in this tab. 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 (nsPresContext* presContext = presShell->GetPresContext()) {
+ nsRootPresContext* rootPresContext = presContext->GetRootPresContext();
+ nsIFrame* rootFrame = presShell->GetRootFrame();
+ rootPresContext->ComputePluginGeometryUpdates(rootFrame, nullptr,
+ nullptr);
+ rootPresContext->ApplyPluginGeometryUpdates();
+ }
+ presShell->SetIsActive(false);
+ }
+ }
+
+ if (mPuppetWidget) {
+ mPuppetWidget->Show(false);
+ }
+}
+
+NS_IMETHODIMP
+BrowserChild::GetMessageManager(ContentFrameMessageManager** aResult) {
+ RefPtr<ContentFrameMessageManager> mm(mBrowserChildMessageManager);
+ mm.forget(aResult);
+ return *aResult ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+BrowserChild::GetWebBrowserChrome(nsIWebBrowserChrome3** aWebBrowserChrome) {
+ nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
+ if (nsCOMPtr<nsIWebBrowserChrome3> chrome =
+ do_QueryActor("WebBrowserChrome", docShell->GetDocument())) {
+ chrome.forget(aWebBrowserChrome);
+ } else {
+ // TODO: remove fallback
+ NS_IF_ADDREF(*aWebBrowserChrome = mWebBrowserChrome);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+BrowserChild::SetWebBrowserChrome(nsIWebBrowserChrome3* aWebBrowserChrome) {
+ mWebBrowserChrome = aWebBrowserChrome;
+ return NS_OK;
+}
+
+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;
+}
+
+void BrowserChild::SetTabId(const TabId& aTabId) {
+ MOZ_ASSERT(mUniqueId == 0);
+
+ mUniqueId = aTabId;
+ NestedBrowserChildMap()[mUniqueId] = this;
+}
+
+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 (!BuildClonedMessageDataForChild(Manager(), aData, data)) {
+ return false;
+ }
+ return SendSyncMessage(PromiseFlatString(aMessage), data, aRetVal);
+}
+
+nsresult BrowserChild::DoSendAsyncMessage(const nsAString& aMessage,
+ StructuredCloneData& aData) {
+ ClonedMessageData data;
+ if (!BuildClonedMessageDataForChild(Manager(), 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);
+
+ nsTArray<RefPtr<BrowserChild>> list;
+ if (!sBrowserChildren) {
+ return list;
+ }
+
+ for (auto iter = sBrowserChildren->Iter(); !iter.Done(); iter.Next()) {
+ list.AppendElement(iter.Data());
+ }
+
+ return list;
+}
+
+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<LayerManager> lm = mPuppetWidget->GetLayerManager();
+ MOZ_ASSERT(lm);
+
+ lm->DidComposite(aTransactionId, aCompositeStart, aCompositeEnd);
+}
+
+void BrowserChild::DidRequestComposite(const TimeStamp& aCompositeReqStart,
+ const TimeStamp& aCompositeReqEnd) {
+ nsCOMPtr<nsIDocShell> docShellComPtr = do_GetInterface(WebNavigation());
+ if (!docShellComPtr) {
+ return;
+ }
+
+ nsDocShell* docShell = static_cast<nsDocShell*>(docShellComPtr.get());
+ RefPtr<TimelineConsumers> timelines = TimelineConsumers::Get();
+
+ if (timelines && timelines->HasConsumer(docShell)) {
+ // Since we're assuming that it's impossible for content JS to directly
+ // trigger a synchronous paint, we can avoid capturing a stack trace here,
+ // which means we won't run into JS engine reentrancy issues like bug
+ // 1310014.
+ timelines->AddMarkerForDocShell(
+ docShell, "CompositeForwardTransaction", aCompositeReqStart,
+ MarkerTracingType::START, MarkerStackRequest::NO_STACK);
+ timelines->AddMarkerForDocShell(docShell, "CompositeForwardTransaction",
+ aCompositeReqEnd, MarkerTracingType::END,
+ MarkerStackRequest::NO_STACK);
+ }
+}
+
+void BrowserChild::ClearCachedResources() {
+ MOZ_ASSERT(mPuppetWidget);
+ RefPtr<LayerManager> lm = mPuppetWidget->GetLayerManager();
+ MOZ_ASSERT(lm);
+
+ lm->ClearCachedResources();
+
+ if (nsCOMPtr<Document> document = GetTopLevelDocument()) {
+ nsPresContext* presContext = document->GetPresContext();
+ if (presContext) {
+ presContext->NotifyPaintStatusReset();
+ }
+ }
+}
+
+void BrowserChild::InvalidateLayers() {
+ MOZ_ASSERT(mPuppetWidget);
+ RefPtr<LayerManager> lm = mPuppetWidget->GetLayerManager();
+ MOZ_ASSERT(lm);
+
+ FrameLayerBuilder::InvalidateAllLayers(lm);
+}
+
+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());
+
+ // 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();
+ RefPtr<LayerManager> lm = mPuppetWidget->GetLayerManager();
+ MOZ_ASSERT(lm);
+ lm->SetLayersObserverEpoch(mLayersObserverEpoch);
+
+ nsCOMPtr<Document> doc(GetTopLevelDocument());
+ doc->NotifyLayerManagerRecreated();
+}
+
+void BrowserChild::ReinitRenderingForDeviceReset() {
+ InvalidateLayers();
+
+ RefPtr<LayerManager> lm = mPuppetWidget->GetLayerManager();
+ if (WebRenderLayerManager* wlm = lm->AsWebRenderLayerManager()) {
+ wlm->DoDestroy(/* aIsSync */ true);
+ } else if (ClientLayerManager* clm = lm->AsClientLayerManager()) {
+ if (ShadowLayerForwarder* fwd = clm->AsShadowForwarder()) {
+ // Force the LayerTransactionChild to synchronously shutdown. It is
+ // okay to do this early, we'll simply stop sending messages. This
+ // step is necessary since otherwise the compositor will think we
+ // are trying to attach two layer trees to the same ID.
+ fwd->SynchronouslyShutdown();
+ }
+ } else {
+ if (mLayersConnected.isNothing()) {
+ return;
+ }
+ }
+
+ // 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<LayerManager> lm = mPuppetWidget->GetLayerManager();
+ MOZ_ASSERT(lm);
+ lm->UpdatePartialPrerenderedAnimations(aJankedAnimations);
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvRequestNotifyAfterRemotePaint() {
+ // Get the CompositorBridgeChild instance for this content thread.
+ CompositorBridgeChild* compositor = CompositorBridgeChild::Get();
+
+ // Tell the CompositorBridgeChild that, when it gets a RemotePaintIsReady
+ // message that it should forward it us so that we can bounce it to our
+ // BrowserParent.
+ compositor->RequestNotifyAfterRemotePaint(this);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvUIResolutionChanged(
+ const float& aDpi, const int32_t& aRounding, const double& aScale) {
+ nsCOMPtr<Document> document(GetTopLevelDocument());
+ if (!document) {
+ return IPC_OK();
+ }
+
+ ScreenIntSize oldScreenSize = GetInnerSize();
+ if (aDpi > 0) {
+ mPuppetWidget->UpdateBackingScaleCache(aDpi, aRounding, aScale);
+ }
+ RefPtr<nsPresContext> presContext = document->GetPresContext();
+ if (presContext) {
+ presContext->UIResolutionChangedSync();
+ }
+
+ 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);
+ }
+
+ 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(0, &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::RecvSetWidgetNativeData(
+ const WindowsHandle& aWidgetNativeData) {
+ mWidgetNativeData = aWidgetNativeData;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserChild::RecvReleaseAllPointerCapture() {
+ PointerEventHandler::ReleaseAllPointerCapture();
+ return IPC_OK();
+}
+
+mozilla::plugins::PPluginWidgetChild* BrowserChild::AllocPPluginWidgetChild() {
+#ifdef XP_WIN
+ return new mozilla::plugins::PluginWidgetChild();
+#else
+ MOZ_ASSERT_UNREACHABLE("AllocPPluginWidgetChild only supports Windows");
+ return nullptr;
+#endif
+}
+
+bool BrowserChild::DeallocPPluginWidgetChild(
+ mozilla::plugins::PPluginWidgetChild* aActor) {
+ delete aActor;
+ return true;
+}
+
+#ifdef XP_WIN
+nsresult BrowserChild::CreatePluginWidget(nsIWidget* aParent,
+ nsIWidget** aOut) {
+ *aOut = nullptr;
+ mozilla::plugins::PluginWidgetChild* child =
+ static_cast<mozilla::plugins::PluginWidgetChild*>(
+ SendPPluginWidgetConstructor());
+ if (!child) {
+ NS_ERROR("couldn't create PluginWidgetChild");
+ return NS_ERROR_UNEXPECTED;
+ }
+ nsCOMPtr<nsIWidget> pluginWidget =
+ nsIWidget::CreatePluginProxyWidget(this, child);
+ if (!pluginWidget) {
+ NS_ERROR("couldn't create PluginWidgetProxy");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ nsWidgetInitData initData;
+ initData.mWindowType = eWindowType_plugin_ipc_content;
+ initData.clipChildren = true;
+ initData.clipSiblings = true;
+ nsresult rv = pluginWidget->Create(
+ aParent, nullptr, LayoutDeviceIntRect(0, 0, 0, 0), &initData);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Creating native plugin widget on the chrome side failed.");
+ }
+ pluginWidget.forget(aOut);
+ return rv;
+}
+#endif // XP_WIN
+
+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 Some(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(
+ const layers::LayersObserverEpoch& aEpoch) {
+ if (!IPCOpen() || !mPuppetWidget || !mPuppetWidget->HasLayerManager()) {
+ // 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(true /* aEnabled */, aEpoch);
+}
+
+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::BeginSendingWebProgressEventsToParent() {
+ mShouldSendWebProgressEventsToParent = true;
+ return NS_OK;
+}
+
+nsresult BrowserChild::GetHasSiblings(bool* aHasSiblings) {
+ *aHasSiblings = mHasSiblings;
+ return NS_OK;
+}
+
+nsresult BrowserChild::SetHasSiblings(bool aHasSiblings) {
+ mHasSiblings = aHasSiblings;
+ return NS_OK;
+}
+
+NS_IMETHODIMP BrowserChild::OnStateChange(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest,
+ uint32_t aStateFlags,
+ nsresult aStatus) {
+ if (!IPCOpen() || !mShouldSendWebProgressEventsToParent) {
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
+ if (!docShell) {
+ 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;
+ }
+
+ RefPtr<Document> document;
+ if (nsCOMPtr<nsPIDOMWindowOuter> outerWindow = do_GetInterface(docShell)) {
+ document = outerWindow->GetExtantDoc();
+ } else {
+ return NS_OK;
+ }
+
+ Maybe<WebProgressData> webProgressData;
+ Maybe<WebProgressStateChangeData> stateChangeData;
+ RequestData requestData;
+
+ MOZ_TRY(PrepareProgressListenerData(aWebProgress, aRequest, webProgressData,
+ requestData));
+
+ if (webProgressData->isTopLevel()) {
+ stateChangeData.emplace();
+
+ stateChangeData->isNavigating() = docShell->GetIsNavigating();
+ stateChangeData->mayEnableCharacterEncodingMenu() =
+ docShell->GetMayEnableCharacterEncodingMenu();
+ stateChangeData->charsetAutodetected() = docShell->GetCharsetAutodetected();
+
+ 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;
+ }
+
+ Maybe<WebProgressData> webProgressData;
+ RequestData requestData;
+
+ nsresult rv = PrepareProgressListenerData(aWebProgress, aRequest,
+ webProgressData, requestData);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ Unused << SendOnProgressChange(webProgressData, requestData, aCurSelfProgress,
+ aMaxSelfProgress, 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<nsIWebNavigation> webNav = WebNavigation();
+ nsCOMPtr<nsIDocShell> docShell = do_GetInterface(webNav);
+ if (!docShell) {
+ return NS_OK;
+ }
+
+ RefPtr<Document> document;
+ if (nsCOMPtr<nsPIDOMWindowOuter> outerWindow = do_GetInterface(docShell)) {
+ document = outerWindow->GetExtantDoc();
+ } else {
+ return NS_OK;
+ }
+
+ if (!document) {
+ return NS_OK;
+ }
+
+ Maybe<WebProgressData> webProgressData;
+ RequestData requestData;
+
+ MOZ_TRY(PrepareProgressListenerData(aWebProgress, aRequest, webProgressData,
+ requestData));
+
+ Maybe<WebProgressLocationChangeData> locationChangeData;
+
+ bool canGoBack = false;
+ bool canGoForward = false;
+
+ MOZ_TRY(webNav->GetCanGoBack(&canGoBack));
+ MOZ_TRY(webNav->GetCanGoForward(&canGoForward));
+
+ if (aWebProgress && webProgressData->isTopLevel()) {
+ 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->charsetAutodetected() =
+ docShell->GetCharsetAutodetected();
+
+ 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;
+ }
+
+ Maybe<WebProgressData> webProgressData;
+ RequestData requestData;
+
+ nsresult rv = PrepareProgressListenerData(aWebProgress, aRequest,
+ webProgressData, requestData);
+
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ const nsString message(aMessage);
+
+ Unused << SendOnStatusChange(webProgressData, requestData, aStatus, message);
+
+ 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,
+ int32_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::PrepareProgressListenerData(
+ nsIWebProgress* aWebProgress, nsIRequest* aRequest,
+ Maybe<WebProgressData>& aWebProgressData, RequestData& aRequestData) {
+ if (aWebProgress) {
+ aWebProgressData.emplace();
+
+ bool isTopLevel = false;
+ nsresult rv = aWebProgress->GetIsTopLevel(&isTopLevel);
+ NS_ENSURE_SUCCESS(rv, rv);
+ aWebProgressData->isTopLevel() = isTopLevel;
+
+ bool isLoadingDocument = false;
+ rv = aWebProgress->GetIsLoadingDocument(&isLoadingDocument);
+ NS_ENSURE_SUCCESS(rv, rv);
+ aWebProgressData->isLoadingDocument() = isLoadingDocument;
+
+ uint32_t loadType = 0;
+ rv = aWebProgress->GetLoadType(&loadType);
+ NS_ENSURE_SUCCESS(rv, rv);
+ aWebProgressData->loadType() = loadType;
+ }
+
+ nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
+ if (channel) {
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = channel->GetURI(getter_AddRefs(uri));
+ NS_ENSURE_SUCCESS(rv, rv);
+ aRequestData.requestURI() = uri;
+
+ rv = channel->GetOriginalURI(getter_AddRefs(uri));
+ NS_ENSURE_SUCCESS(rv, rv);
+ aRequestData.originalRequestURI() = uri;
+
+ nsCOMPtr<nsIClassifiedChannel> classifiedChannel =
+ do_QueryInterface(channel);
+ if (classifiedChannel) {
+ nsAutoCString matchedList;
+ rv = classifiedChannel->GetMatchedList(matchedList);
+ NS_ENSURE_SUCCESS(rv, rv);
+ aRequestData.matchedList() = std::move(matchedList);
+ }
+ }
+ return NS_OK;
+}
+
+bool BrowserChild::UpdateSessionStore(uint32_t aFlushId, bool aIsFinal) {
+ if (!mSessionStoreListener) {
+ return false;
+ }
+ RefPtr<ContentSessionStore> store = mSessionStoreListener->GetSessionStore();
+
+ Maybe<nsCString> docShellCaps;
+ if (store->IsDocCapChanged()) {
+ docShellCaps.emplace(store->GetDocShellCaps());
+ }
+
+ Maybe<bool> privatedMode;
+ if (store->IsPrivateChanged()) {
+ privatedMode.emplace(store->GetPrivateModeEnabled());
+ }
+
+ nsTArray<int32_t> positionDescendants;
+ nsTArray<nsCString> positions;
+ if (store->IsScrollPositionChanged()) {
+ store->GetScrollPositions(positions, positionDescendants);
+ }
+
+ nsTArray<InputFormData> inputs;
+ nsTArray<CollectedInputDataValue> idVals, xPathVals;
+ if (store->IsFormDataChanged()) {
+ inputs = store->GetInputs(idVals, xPathVals);
+ }
+
+ nsTArray<nsCString> origins;
+ nsTArray<nsString> keys, values;
+ bool isFullStorage = false;
+ if (store->IsStorageUpdated()) {
+ isFullStorage = store->GetAndClearStorageChanges(origins, keys, values);
+ }
+
+ Unused << SendSessionStoreUpdate(
+ docShellCaps, privatedMode, positions, positionDescendants, inputs,
+ idVals, xPathVals, origins, keys, values, isFullStorage,
+ store->GetAndClearSHistoryChanged(), aFlushId, aIsFinal,
+ mSessionStoreListener->GetEpoch());
+ return true;
+}
+
+#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) {
+ if (!IPCOpen()) {
+ return;
+ }
+
+ Maybe<WebProgressData> webProgressData;
+ RequestData requestData;
+ nsresult rv = PrepareProgressListenerData(nullptr, aChannel, webProgressData,
+ requestData);
+ NS_ENSURE_SUCCESS_VOID(rv);
+
+ Unused << SendNotifyContentBlockingEvent(aEvent, requestData, aBlocked,
+ PromiseFlatCString(aTrackingOrigin),
+ aTrackingFullHashes, aReason);
+}
+
+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(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() {
+ nsCOMPtr<nsIEventTarget> target = EventTargetFor(TaskCategory::Other);
+ return target.forget();
+}
+
+nsresult BrowserChildMessageManager::Dispatch(
+ TaskCategory aCategory, already_AddRefed<nsIRunnable>&& aRunnable) {
+ return DispatcherTrait::Dispatch(aCategory, std::move(aRunnable));
+}
+
+nsISerialEventTarget* BrowserChildMessageManager::EventTargetFor(
+ TaskCategory aCategory) const {
+ return DispatcherTrait::EventTargetFor(aCategory);
+}
+
+AbstractThread* BrowserChildMessageManager::AbstractMainThreadFor(
+ TaskCategory aCategory) {
+ return DispatcherTrait::AbstractMainThreadFor(aCategory);
+}
diff --git a/dom/ipc/BrowserChild.h b/dom/ipc/BrowserChild.h
new file mode 100644
index 0000000000..5b948499cf
--- /dev/null
+++ b/dom/ipc/BrowserChild.h
@@ -0,0 +1,936 @@
+/* -*- 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 "nsIEmbeddingSiteWindow.h"
+#include "nsIWebBrowserChromeFocus.h"
+#include "nsIDOMEventListener.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/CoalescedWheelData.h"
+#include "mozilla/dom/MessageManagerCallback.h"
+#include "mozilla/dom/VsyncChild.h"
+#include "mozilla/DOMEventTargetHelper.h"
+#include "mozilla/EventDispatcher.h"
+#include "mozilla/EventForwards.h"
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/APZCCallbackHelper.h"
+#include "mozilla/layers/CompositorOptions.h"
+#include "mozilla/layers/GeckoContentControllerTypes.h"
+#include "nsIWebBrowserChrome3.h"
+#include "mozilla/dom/ipc/IdType.h"
+#include "AudioChannelService.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 {
+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 ContentSessionStore;
+class TabListener;
+class RequestData;
+class WebProgressData;
+
+class BrowserChildMessageManager : public ContentFrameMessageManager,
+ public nsIMessageSender,
+ public DispatcherTrait,
+ 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;
+
+ virtual Nullable<WindowProxyHolder> GetContent(ErrorResult& aError) override;
+ virtual already_AddRefed<nsIDocShell> GetDocShell(
+ ErrorResult& aError) override;
+ virtual 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.
+ virtual nsresult Dispatch(mozilla::TaskCategory aCategory,
+ already_AddRefed<nsIRunnable>&& aRunnable) override;
+
+ virtual nsISerialEventTarget* EventTargetFor(
+ mozilla::TaskCategory aCategory) const override;
+
+ virtual AbstractThread* AbstractMainThreadFor(
+ mozilla::TaskCategory aCategory) override;
+
+ RefPtr<BrowserChild> mBrowserChild;
+
+ protected:
+ ~BrowserChildMessageManager();
+};
+
+class ContentListener final : public nsIDOMEventListener {
+ public:
+ explicit ContentListener(BrowserChild* aBrowserChild)
+ : mBrowserChild(aBrowserChild) {}
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIDOMEVENTLISTENER
+ protected:
+ ~ContentListener() = default;
+ BrowserChild* mBrowserChild;
+};
+
+/**
+ * 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 nsIEmbeddingSiteWindow,
+ public nsIWebBrowserChromeFocus,
+ public nsIInterfaceRequestor,
+ public nsIWindowProvider,
+ public nsSupportsWeakReference,
+ public nsIBrowserChild,
+ public nsIObserver,
+ public nsIWebProgressListener2,
+ public TabContext,
+ public nsITooltipListener,
+ public mozilla::ipc::IShmemAllocator {
+ typedef mozilla::widget::PuppetWidget PuppetWidget;
+ typedef mozilla::dom::ClonedMessageData ClonedMessageData;
+ typedef mozilla::dom::CoalescedMouseData CoalescedMouseData;
+ typedef mozilla::dom::CoalescedWheelData CoalescedWheelData;
+ typedef mozilla::layers::APZEventState APZEventState;
+ typedef mozilla::layers::SetAllowedTouchBehaviorCallback
+ SetAllowedTouchBehaviorCallback;
+ typedef mozilla::layers::TouchBehaviorFlags 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; }
+
+ const TabId GetTabId() const {
+ MOZ_ASSERT(mUniqueId != 0);
+ return mUniqueId;
+ }
+
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_NSIWEBBROWSERCHROME
+ NS_DECL_NSIEMBEDDINGSITEWINDOW
+ 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; }
+
+ /**
+ * 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 RecvResumeLoad(const uint64_t& aPendingSwitchID,
+ const ParentShowInfo&);
+
+ mozilla::ipc::IPCResult RecvCloneDocumentTreeIntoSelf(
+ const MaybeDiscarded<BrowsingContext>& aSourceBC);
+
+ 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);
+
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ mozilla::ipc::IPCResult RecvMouseEvent(const nsString& aType, const float& aX,
+ const float& aY,
+ const int32_t& aButton,
+ const int32_t& aClickCount,
+ const int32_t& aModifiers);
+
+ 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);
+
+ 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);
+
+ mozilla::ipc::IPCResult RecvNormalPriorityRealKeyEvent(
+ const mozilla::WidgetKeyboardEvent& aEvent);
+
+ 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 RecvFlushTabState(const uint32_t& aFlushId,
+ const bool& aIsFinal);
+
+ mozilla::ipc::IPCResult RecvUpdateEpoch(const uint32_t& aEpoch);
+
+ mozilla::ipc::IPCResult RecvUpdateSHistory(const bool& aImmediately);
+
+ 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 RecvSetIsUnderHiddenEmbedderElement(
+ const bool& aIsUnderHiddenEmbedderElement);
+
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ mozilla::ipc::IPCResult RecvPasteTransferable(
+ const IPCDataTransfer& aDataTransfer, const bool& aIsPrivateData,
+ nsIPrincipal* aRequestingPrincipal,
+ const nsContentPolicyType& aContentPolicyType);
+
+ mozilla::ipc::IPCResult RecvActivateFrameEvent(const nsString& aType,
+ const bool& aCapture);
+
+ mozilla::ipc::IPCResult RecvLoadRemoteScript(const nsString& aURL,
+ const bool& aRunInGlobalScope);
+
+ mozilla::ipc::IPCResult RecvAsyncMessage(const nsString& 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 uint32_t&,
+ const IAccessibleHolder&);
+ bool DeallocPDocAccessibleChild(PDocAccessibleChild*);
+#endif
+
+ PColorPickerChild* AllocPColorPickerChild(const nsString& aTitle,
+ const nsString& aInitialColor);
+
+ bool DeallocPColorPickerChild(PColorPickerChild* aActor);
+
+ PFilePickerChild* AllocPFilePickerChild(const nsString& aTitle,
+ const int16_t& aMode);
+
+ virtual PVsyncChild* AllocPVsyncChild();
+
+ virtual bool DeallocPVsyncChild(PVsyncChild* aActor);
+
+ RefPtr<VsyncChild> GetVsyncChild();
+
+ bool DeallocPFilePickerChild(PFilePickerChild* aActor);
+
+ nsIWebNavigation* WebNavigation() const { return mWebNav; }
+
+ PuppetWidget* WebWidget() { return mPuppetWidget; }
+
+ bool IsTransparent() const { return mIsTransparent; }
+
+ const EffectsInfo& GetEffectsInfo() const { return mEffectsInfo; }
+
+ hal::ScreenOrientation GetOrientation() const { return mOrientation; }
+
+ void SetBackgroundColor(const nscolor& aColor);
+
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY virtual mozilla::ipc::IPCResult RecvUpdateEffects(
+ const EffectsInfo& aEffects);
+
+ void RequestEditCommands(nsIWidget::NativeKeyBindingsType aType,
+ const WidgetKeyboardEvent& aEvent,
+ nsTArray<CommandInt>& aCommands);
+
+ bool IsVisible();
+
+ /**
+ * Signal to this BrowserChild that it should be made visible:
+ * activated widget, retained layer tree, etc. (Respectively,
+ * made not visible.)
+ */
+ MOZ_CAN_RUN_SCRIPT void UpdateVisibility();
+ MOZ_CAN_RUN_SCRIPT void MakeVisible();
+ void MakeHidden();
+
+ 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 DidRequestComposite(const TimeStamp& aCompositeReqStart,
+ const TimeStamp& aCompositeReqEnd);
+
+ void ClearCachedResources();
+ void InvalidateLayers();
+ 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);
+
+ mozilla::ipc::IPCResult RecvHandledWindowedPluginKeyEvent(
+ const mozilla::NativeEventData& aKeyEventData, const bool& aIsConsumed);
+
+ mozilla::ipc::IPCResult RecvPrintPreview(
+ const PrintData& aPrintData,
+ const mozilla::Maybe<uint64_t>& aSourceOuterWindowID,
+ PrintPreviewResolver&& aCallback);
+
+ mozilla::ipc::IPCResult RecvExitPrintPreview();
+
+ mozilla::ipc::IPCResult RecvPrint(const uint64_t& aOuterWindowID,
+ const PrintData& aPrintData);
+
+ mozilla::ipc::IPCResult RecvUpdateNativeWindowHandle(
+ const uintptr_t& aNewHandle);
+
+ mozilla::ipc::IPCResult RecvWillChangeProcess(
+ WillChangeProcessResolver&& aResolve);
+ /**
+ * Native widget remoting protocol for use with windowed plugins with e10s.
+ */
+ PPluginWidgetChild* AllocPPluginWidgetChild();
+
+ bool DeallocPPluginWidgetChild(PPluginWidgetChild* aActor);
+
+#ifdef XP_WIN
+ nsresult CreatePluginWidget(nsIWidget* aParent, nsIWidget** aOut);
+#endif
+
+ 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);
+
+ 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);
+
+ void SetAllowedTouchBehavior(
+ uint64_t aInputBlockId, const nsTArray<TouchBehaviorFlags>& aFlags) const;
+
+ bool UpdateFrame(const layers::RepaintRequest& aRequest);
+ bool NotifyAPZStateChange(
+ const ViewID& aViewId,
+ const layers::GeckoContentController_APZStateChange& aChange,
+ const int& aArg);
+ 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(const layers::LayersObserverEpoch& aEpoch);
+
+ nsresult CanCancelContentJS(nsIRemoteTab::NavigationType aNavigationType,
+ int32_t aNavigationIndex, nsIURI* aNavigationURI,
+ int32_t aEpoch, bool* aCanCancel);
+
+ layers::LayersObserverEpoch LayersObserverEpoch() const {
+ return mLayersObserverEpoch;
+ }
+
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+ uintptr_t GetNativeWindowHandle() const { return mNativeWindowHandle; }
+#endif
+
+ BrowsingContext* GetBrowsingContext() const { return mBrowsingContext; }
+
+#if defined(ACCESSIBILITY)
+ void SetTopLevelDocAccessibleChild(PDocAccessibleChild* aTopLevelChild) {
+ mTopLevelDocAccessibleChild = aTopLevelChild;
+ }
+
+ PDocAccessibleChild* GetTopLevelDocAccessibleChild() {
+ return mTopLevelDocAccessibleChild;
+ }
+#endif
+
+ // The HANDLE object for the widget this BrowserChild in.
+ WindowsHandle WidgetNativeData() { return mWidgetNativeData; }
+
+ // 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.
+ mozilla::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 HandleRealMouseButtonEvent(const WidgetMouseEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId);
+
+ void SetCancelContentJSEpoch(int32_t aEpoch) {
+ mCancelContentJSEpoch = aEpoch;
+ }
+
+ bool UpdateSessionStore(uint32_t aFlushId, bool aIsFinal = false);
+
+#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);
+
+ protected:
+ virtual ~BrowserChild();
+
+ mozilla::ipc::IPCResult RecvDestroy();
+
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ mozilla::ipc::IPCResult RecvRenderLayers(
+ const bool& aEnabled, const layers::LayersObserverEpoch& aEpoch);
+
+ mozilla::ipc::IPCResult RecvNavigateByKey(const bool& aForward,
+ const bool& aForDocumentNavigation);
+
+ mozilla::ipc::IPCResult RecvRequestNotifyAfterRemotePaint();
+
+ mozilla::ipc::IPCResult RecvSuppressDisplayport(const bool& aEnabled);
+
+ mozilla::ipc::IPCResult RecvScrollbarPreferenceChanged(ScrollbarPreference);
+
+ mozilla::ipc::IPCResult RecvSetKeyboardIndicators(
+ const UIStateChangeType& aShowFocusRings);
+
+ mozilla::ipc::IPCResult RecvStopIMEStateManagement();
+
+ mozilla::ipc::IPCResult RecvAllowScriptsToClose();
+
+ mozilla::ipc::IPCResult RecvSetWidgetNativeData(
+ const WindowsHandle& aWidgetNativeData);
+
+ mozilla::ipc::IPCResult RecvReleaseAllPointerCapture();
+
+ private:
+ void HandleDoubleTap(const CSSPoint& aPoint, const Modifiers& aModifiers,
+ const ScrollableLayerGuid& aGuid);
+
+ // Notify others that our TabContext has been updated.
+ //
+ // You should call this after calling TabContext::SetTabContext(). We also
+ // call this during Init().
+ void NotifyTabContextUpdated();
+
+ 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();
+
+ void SetTabId(const TabId& aTabId);
+
+ ScreenIntRect GetOuterRect();
+
+ void SetUnscaledInnerSize(const CSSSize& aSize) {
+ mUnscaledInnerSize = aSize;
+ }
+
+ bool SkipRepeatedKeyEvent(const WidgetKeyboardEvent& aEvent);
+
+ void UpdateRepeatedKeyEventEndTime(const WidgetKeyboardEvent& aEvent);
+
+ bool MaybeCoalesceWheelEvent(const WidgetWheelEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId,
+ bool* aIsNextWheelEvent);
+
+ void MaybeDispatchCoalescedWheelEvent();
+
+ /**
+ * 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 PrepareProgressListenerData(nsIWebProgress* aWebProgress,
+ nsIRequest* aRequest,
+ Maybe<WebProgressData>& aWebProgressData,
+ RequestData& aRequestData);
+ already_AddRefed<nsIWebBrowserChrome3> GetWebBrowserChromeActor();
+
+ class DelayedDeleteRunnable;
+
+ RefPtr<BrowserChildMessageManager> mBrowserChildMessageManager;
+ nsCOMPtr<nsIWebBrowserChrome3> mWebBrowserChrome;
+ 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;
+ EffectsInfo mEffectsInfo;
+ bool mDidFakeShow;
+ bool mTriedBrowserInit;
+ hal::ScreenOrientation mOrientation;
+
+ RefPtr<VsyncChild> mVsyncChild;
+
+ bool mIgnoreKeyPressEvent;
+ RefPtr<APZEventState> mAPZEventState;
+ SetAllowedTouchBehaviorCallback mSetAllowedTouchBehaviorCallback;
+ bool mHasValidInnerSize;
+ bool mDestroyed;
+
+ // 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;
+
+ // Whether or not this browser is the child part of the top level PBrowser
+ // actor in a remote browser.
+ bool mIsTopLevel;
+
+ // Whether or not this tab has siblings (other tabs in the same window).
+ // This is one factor used when choosing to allow or deny a non-system
+ // script's attempt to resize the window.
+ bool mHasSiblings;
+
+ // 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;
+
+ bool mIsTransparent;
+
+ bool mIPCOpen;
+ CSSSize mUnscaledInnerSize;
+ bool mDidSetRealShowInfo;
+ bool mDidLoadURLInit;
+
+ bool mSkipKeyPress;
+
+ // 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;
+ RefPtr<CoalescedMouseMoveFlusher> mCoalescedMouseEventFlusher;
+
+ RefPtr<layers::IAPZCTreeManager> mApzcTreeManager;
+ RefPtr<TabListener> mSessionStoreListener;
+
+ // The most recently seen layer observer epoch in RecvSetDocShellIsActive.
+ layers::LayersObserverEpoch mLayersObserverEpoch;
+
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+ // The handle associated with the native window that contains this tab
+ uintptr_t mNativeWindowHandle;
+#endif // defined(XP_WIN)
+
+#if defined(ACCESSIBILITY)
+ PDocAccessibleChild* mTopLevelDocAccessibleChild;
+#endif
+ bool mCoalesceMouseMoveEvents;
+
+ bool mShouldSendWebProgressEventsToParent;
+
+ // Whether we are rendering to the compositor or not.
+ bool mRenderLayers;
+
+ // In some circumstances, a DocShell might be in a state where it is
+ // "blocked", and we should not attempt to change its active state or
+ // the underlying PresShell state until the DocShell becomes unblocked.
+ // It is possible, however, for the parent process to send commands to
+ // change those states while the DocShell is blocked. We store those
+ // states temporarily as "pending", and only apply them once the DocShell
+ // is no longer blocked.
+ bool mPendingDocShellIsActive;
+ bool mPendingDocShellReceivedMessage;
+ bool mPendingRenderLayers;
+ bool mPendingRenderLayersReceivedMessage;
+ layers::LayersObserverEpoch mPendingLayersObserverEpoch;
+ // When mPendingDocShellBlockers is greater than 0, the DocShell is blocked,
+ // and once it reaches 0, it is no longer blocked.
+ uint32_t mPendingDocShellBlockers;
+ int32_t mCancelContentJSEpoch;
+
+ WindowsHandle mWidgetNativeData;
+
+ Maybe<LayoutDeviceToLayoutDeviceMatrix4x4> mChildToParentConversionMatrix;
+ ScreenRect mTopLevelViewportVisibleRectInBrowserCoords;
+
+#ifdef XP_WIN
+ // Should only be accessed on main thread.
+ Maybe<bool> mWindowSupportsProtectedMedia;
+#endif
+
+ 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..6815775f21
--- /dev/null
+++ b/dom/ipc/BrowserHost.cpp
@@ -0,0 +1,263 @@
+/* -*- 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 "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;
+}
+
+a11y::DocAccessibleParent* BrowserHost::GetTopLevelDocAccessible() const {
+ return mRoot->GetTopLevelDocAccessible();
+}
+
+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) {
+ if (!mRoot) {
+ *aHasLayers = false;
+ return NS_OK;
+ }
+ *aHasLayers = mRoot->GetHasLayers();
+ return NS_OK;
+}
+
+/* void resolutionChanged (); */
+NS_IMETHODIMP
+BrowserHost::NotifyResolutionChanged(void) {
+ if (!mRoot) {
+ return NS_OK;
+ }
+ VisitAll([](BrowserParent* aBrowserParent) {
+ aBrowserParent->NotifyResolutionChanged();
+ });
+ return NS_OK;
+}
+
+/* void deprioritize (); */
+NS_IMETHODIMP
+BrowserHost::Deprioritize(void) {
+ if (!mRoot) {
+ return NS_OK;
+ }
+ VisitAll(
+ [](BrowserParent* aBrowserParent) { aBrowserParent->Deprioritize(); });
+ 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 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);
+}
+
+/* boolean startApzAutoscroll (in float aAnchorX, in float aAnchorY, in nsViewID
+ * aScrollId, in uint32_t aPresShellId); */
+NS_IMETHODIMP
+BrowserHost::StartApzAutoscroll(float aAnchorX, float aAnchorY,
+ nsViewID aScrollId, uint32_t aPresShellId,
+ bool* _retval) {
+ if (!mRoot) {
+ return NS_OK;
+ }
+ *_retval =
+ mRoot->StartApzAutoscroll(aAnchorX, aAnchorY, aScrollId, aPresShellId);
+ return NS_OK;
+}
+
+/* void stopApzAutoscroll (in nsViewID aScrollId, in uint32_t aPresShellId); */
+NS_IMETHODIMP
+BrowserHost::StopApzAutoscroll(nsViewID aScrollId, uint32_t aPresShellId) {
+ if (!mRoot) {
+ return NS_OK;
+ }
+ mRoot->StopApzAutoscroll(aScrollId, aPresShellId);
+ 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;
+ }
+ if (StaticPrefs::dom_ipc_cancel_content_js_when_navigating()) {
+ 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..fd3a6a4973
--- /dev/null
+++ b/dom/ipc/BrowserHost.h
@@ -0,0 +1,108 @@
+/* -*- 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;
+
+ 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..5ede68022d
--- /dev/null
+++ b/dom/ipc/BrowserParent.cpp
@@ -0,0 +1,4125 @@
+/* -*- 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"
+
+#ifdef ACCESSIBILITY
+# include "mozilla/a11y/DocAccessibleParent.h"
+# include "mozilla/a11y/Platform.h"
+# include "mozilla/a11y/ProxyAccessibleBase.h"
+# include "nsAccessibilityService.h"
+#endif
+#include "mozilla/dom/BrowserHost.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/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/RemoteWebProgress.h"
+#include "mozilla/dom/RemoteWebProgressRequest.h"
+#include "mozilla/dom/SessionHistoryEntry.h"
+#include "mozilla/dom/SessionStoreUtils.h"
+#include "mozilla/dom/SessionStoreUtilsBinding.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/Hal.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/plugins/PPluginWidgetParent.h"
+#include "mozilla/LookAndFeel.h"
+#include "mozilla/MiscEvents.h"
+#include "mozilla/MouseEvents.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/StaticPrefs_dom.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 "GeckoProfiler.h"
+#include "MMPrinter.h"
+#include "SessionStoreFunctions.h"
+#include "mozilla/dom/CrashReport.h"
+#include "nsISecureBrowserUI.h"
+#include "nsIXULRuntime.h"
+#include "VsyncSource.h"
+
+#ifdef XP_WIN
+# include "mozilla/plugins/PluginWidgetParent.h"
+# 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
+
+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;
+using mozilla::StaticAutoPtr;
+using mozilla::Unused;
+
+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;
+/* static */
+BrowserParent* BrowserParent::sPointerLockedRemoteTarget = 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::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),
+ mRemoteLayerTreeOwner{},
+ mLayerTreeEpoch{1},
+ mChildToParentConversionMatrix{},
+ mRect(0, 0, 0, 0),
+ mDimensions(0, 0),
+ mOrientation(0),
+ mDPI(0),
+ mRounding(0),
+ mDefaultScale(0),
+ mUpdatedDimensions(false),
+ mSizeMode(nsSizeMode_Normal),
+ mClientOffset{},
+ mChromeOffset{},
+ mCreatingWindow(false),
+ mDelayedFrameScripts{},
+ mCursor(eCursorInvalid),
+ mCustomCursor{},
+ mCustomCursorHotspotX(0),
+ mCustomCursorHotspotY(0),
+ mVerifyDropLinks{},
+ mVsyncParent(nullptr),
+ mMarkedDestroying(false),
+ mIsDestroyed(false),
+ mRemoteTargetSetsCursor(false),
+ mPreserveLayers(false),
+ mRenderLayers(true),
+ mActiveInPriorityManager(false),
+ mHasLayers(false),
+ mHasPresented(false),
+ mIsReadyToHandleInputEvents(false),
+ mIsMouseEnterIntoWidgetEventSuppressed(false),
+ mSuspendedProgressEvents(false) {
+ MOZ_ASSERT(aManager);
+ // 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();
+}
+
+BrowserParent::~BrowserParent() = default;
+
+/* static */
+BrowserParent* BrowserParent::GetFocused() { return sFocus; }
+
+/* static */
+BrowserParent* BrowserParent::GetLastMouseRemoteTarget() {
+ return sLastMouseRemoteTarget;
+}
+
+/* static */
+BrowserParent* BrowserParent::GetPointerLockedRemoteTarget() {
+ return sPointerLockedRemoteTarget;
+}
+
+/*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->Put(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()) {
+ nsViewManager* vm = presShell->GetViewManager();
+ nsCOMPtr<nsIWidget> widget;
+ vm->GetRootWidget(getter_AddRefs(widget));
+ return widget.forget();
+ }
+ }
+ 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 iter = docs.ConstIter(); !iter.Done(); iter.Next()) {
+ auto doc = static_cast<a11y::DocAccessibleParent*>(iter.Get()->GetKey());
+ // 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()) {
+ return doc;
+ }
+ }
+
+ MOZ_ASSERT(docs.Count() == 0,
+ "If there isn't a top level accessible doc "
+ "there shouldn't be an accessible doc at all!");
+#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(kNameSpaceID_None, nsGkAtoms::name, name);
+ bool isTransparent =
+ nsContentUtils::IsChromeDoc(mFrameElement->OwnerDoc()) &&
+ mFrameElement->HasAttr(kNameSpaceID_None, 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();
+ TryCacheDPIAndScale();
+
+ // Try to send down WidgetNativeData, now that this BrowserParent is
+ // associated with a widget.
+ nsCOMPtr<nsIWidget> widget = GetTopLevelWidget();
+ if (widget) {
+ WindowsHandle widgetNativeData = reinterpret_cast<WindowsHandle>(
+ widget->GetNativeData(NS_NATIVE_SHAREABLE_WINDOW));
+ if (widgetNativeData) {
+ Unused << SendSetWidgetNativeData(widgetNativeData);
+ }
+ }
+
+ 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);
+ }
+
+ UpdateVsyncParentVsyncSource();
+
+ 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::DestroyInternal() {
+ UnsetTopLevelWebFocus(this);
+ UnsetLastMouseRemoteTarget(this);
+ UnsetPointerLockedRemoteTarget(this);
+ PointerEventHandler::ReleasePointerCaptureRemoteTarget(this);
+ PresShell::ReleaseCapturingRemoteTarget(this);
+
+ RemoveWindowListeners();
+
+#ifdef ACCESSIBILITY
+ if (a11y::DocAccessibleParent* tabDoc = GetTopLevelDocAccessible()) {
+ tabDoc->Destroy();
+ }
+#endif
+
+ // 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__().
+ Unused << SendDestroy();
+
+#ifdef XP_WIN
+ // Let all PluginWidgets know we are tearing down. Prevents
+ // these objects from sending async events after the child side
+ // is shut down.
+ const ManagedContainer<PPluginWidgetParent>& kids =
+ ManagedPPluginWidgetParent();
+ for (auto iter = kids.ConstIter(); !iter.Done(); iter.Next()) {
+ static_cast<mozilla::plugins::PluginWidgetParent*>(iter.Get()->GetKey())
+ ->ParentDestroy();
+ }
+#endif
+}
+
+void BrowserParent::Destroy() {
+ // Aggressively release the window to avoid leaking the world in shutdown
+ // corner cases.
+ mBrowserDOMWindow = nullptr;
+
+ if (mIsDestroyed) {
+ return;
+ }
+
+ DestroyInternal();
+
+ 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::RecvEnsureLayersConnected(
+ CompositorOptions* aCompositorOptions) {
+ if (mRemoteLayerTreeOwner.IsInitialized()) {
+ mRemoteLayerTreeOwner.EnsureLayersConnected(aCompositorOptions);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::Recv__delete__() {
+ Manager()->NotifyTabDestroyed(mTabId, mMarkedDestroying);
+ return IPC_OK();
+}
+
+void BrowserParent::ActorDestroy(ActorDestroyReason why) {
+ ContentProcessManager::GetSingleton()->UnregisterRemoteFrame(mTabId);
+
+ if (mRemoteLayerTreeOwner.IsInitialized()) {
+ // 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(mRemoteLayerTreeOwner.GetLayersId());
+ mRemoteLayerTreeOwner.Destroy();
+ }
+
+ // Even though BrowserParent::Destroy calls this, we need to do it here too in
+ // case of a crash.
+ BrowserParent::UnsetTopLevelWebFocus(this);
+ BrowserParent::UnsetLastMouseRemoteTarget(this);
+ BrowserParent::UnsetPointerLockedRemoteTarget(this);
+ PointerEventHandler::ReleasePointerCaptureRemoteTarget(this);
+ PresShell::ReleaseCapturingRemoteTarget(this);
+
+ 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());
+
+ auto* bridge = GetBrowserBridgeParent();
+ if (bridge && bridge->CanSend() && !mBrowsingContext->IsDiscarded()) {
+ MOZ_ASSERT(!mBrowsingContext->IsTop());
+
+ // Set the owner process of the root context belonging to a crashed
+ // process to the embedding process, since we'll be showing the crashed
+ // page in that process.
+ mBrowsingContext->SetOwnerProcessId(
+ bridge->Manager()->Manager()->ChildID());
+ MOZ_ALWAYS_SUCCEEDS(mBrowsingContext->SetCurrentInnerWindowId(0));
+
+ // Tell the browser bridge to show the subframe crashed page.
+ Unused << bridge->SendSubFrameCrashed();
+ }
+ }
+ }
+
+ mFrameLoader = nullptr;
+}
+
+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::RecvSizeShellTo(
+ const uint32_t& aFlags, const int32_t& aWidth, const int32_t& aHeight,
+ const int32_t& aShellItemWidth, const int32_t& aShellItemHeight) {
+ NS_ENSURE_TRUE(mFrameElement, IPC_OK());
+
+ nsCOMPtr<nsIDocShell> docShell = mFrameElement->OwnerDoc()->GetDocShell();
+ NS_ENSURE_TRUE(docShell, IPC_OK());
+
+ nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
+ nsresult rv = docShell->GetTreeOwner(getter_AddRefs(treeOwner));
+ NS_ENSURE_SUCCESS(rv, IPC_OK());
+
+ int32_t width = aWidth;
+ int32_t height = aHeight;
+
+ if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_CX) {
+ width = mDimensions.width;
+ }
+
+ if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_CY) {
+ height = mDimensions.height;
+ }
+
+ nsCOMPtr<nsIAppWindow> appWin(do_GetInterface(treeOwner));
+ NS_ENSURE_TRUE(appWin, IPC_OK());
+ appWin->SizeShellToWithLimit(width, height, aShellItemWidth,
+ aShellItemHeight);
+
+ 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();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvEvent(const RemoteDOMEvent& aEvent) {
+ RefPtr<Event> event = aEvent.mEvent;
+ NS_ENSURE_TRUE(event, IPC_OK());
+
+ RefPtr<EventTarget> target = mFrameElement;
+ NS_ENSURE_TRUE(target, IPC_OK());
+
+ event->SetOwner(target);
+
+ target->DispatchEvent(*event);
+ return IPC_OK();
+}
+
+bool BrowserParent::SendLoadRemoteScript(const nsString& aURL,
+ const bool& aRunInGlobalScope) {
+ if (mCreatingWindow) {
+ mDelayedFrameScripts.AppendElement(
+ FrameScriptInfo(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;
+ }
+ nsCString spec;
+ aLoadState->URI()->GetSpec(spec);
+ if (mCreatingWindow) {
+ // Don't send the message if the child wants to load its own URL.
+ return;
+ }
+
+ Unused << SendLoadURL(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);
+
+ Unused << SendDynamicToolbarMaxHeightChanged(
+ widget->GetDynamicToolbarMaxHeight());
+#endif
+}
+
+bool BrowserParent::AttachLayerManager() {
+ return !!mRemoteLayerTreeOwner.AttachLayerManager();
+}
+
+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.AttachLayerManager()) {
+ return false;
+ }
+
+ mSizeMode = aOwnerInfo.sizeMode();
+ Unused << SendShow(GetShowInfo(), aOwnerInfo);
+ return true;
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvSetDimensions(
+ const uint32_t& aFlags, const int32_t& aX, const int32_t& aY,
+ const int32_t& aCx, const int32_t& aCy, const double& aScale) {
+ MOZ_ASSERT(!(aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_SIZE_INNER),
+ "We should never see DIM_FLAGS_SIZE_INNER here!");
+
+ 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());
+
+ // We only care about the parameters that actually changed, see more details
+ // in `BrowserChild::SetDimensions()`.
+ // Note that `BrowserChild::SetDimensions()` may be called before receiving
+ // our `SendUIResolutionChanged()` call. Therefore, if given each cordinate
+ // 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 `GetUnscaledDevicePixelsPerCSSPixel()`.
+ double currentScale;
+ treeOwnerAsWin->GetUnscaledDevicePixelsPerCSSPixel(&currentScale);
+
+ int32_t x = aX;
+ int32_t y = aY;
+ if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_POSITION) {
+ if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_X) {
+ int32_t unused;
+ treeOwnerAsWin->GetPosition(&x, &unused);
+ } else if (aScale != currentScale) {
+ x = x * currentScale / aScale;
+ }
+
+ if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_Y) {
+ int32_t unused;
+ treeOwnerAsWin->GetPosition(&unused, &y);
+ } else if (aScale != currentScale) {
+ y = y * currentScale / aScale;
+ }
+ }
+
+ int32_t cx = aCx;
+ int32_t cy = aCy;
+ if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_SIZE_OUTER) {
+ if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_CX) {
+ int32_t unused;
+ treeOwnerAsWin->GetSize(&cx, &unused);
+ } else if (aScale != currentScale) {
+ cx = cx * currentScale / aScale;
+ }
+
+ if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_CY) {
+ int32_t unused;
+ treeOwnerAsWin->GetSize(&unused, &cy);
+ } else if (aScale != currentScale) {
+ cy = cy * currentScale / aScale;
+ }
+ }
+
+ if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_POSITION &&
+ aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_SIZE_OUTER) {
+ treeOwnerAsWin->SetPositionAndSize(x, y, cx, cy, nsIBaseWindow::eRepaint);
+ return IPC_OK();
+ }
+
+ if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_POSITION) {
+ treeOwnerAsWin->SetPosition(x, y);
+ mUpdatedDimensions = false;
+ UpdatePosition();
+ return IPC_OK();
+ }
+
+ if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_SIZE_OUTER) {
+ treeOwnerAsWin->SetSize(cx, cy, true);
+ return IPC_OK();
+ }
+
+ MOZ_ASSERT(false, "Unknown flags!");
+ return IPC_FAIL_NO_REASON(this);
+}
+
+nsresult BrowserParent::UpdatePosition() {
+ RefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
+ if (!frameLoader) {
+ return NS_OK;
+ }
+ nsIntRect windowDims;
+ NS_ENSURE_SUCCESS(frameLoader->GetWindowDimensions(windowDims),
+ NS_ERROR_FAILURE);
+ UpdateDimensions(windowDims, mDimensions);
+ return NS_OK;
+}
+
+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;
+ }
+
+ hal::ScreenConfiguration config;
+ hal::GetCurrentScreenConfiguration(&config);
+ hal::ScreenOrientation orientation = config.orientation();
+ LayoutDeviceIntPoint clientOffset = GetClientOffset();
+ LayoutDeviceIntPoint chromeOffset = !GetBrowserBridgeParent()
+ ? -GetChildProcessOffset()
+ : LayoutDeviceIntPoint();
+
+ if (!mUpdatedDimensions || mOrientation != orientation ||
+ mDimensions != size || !mRect.IsEqualEdges(rect) ||
+ clientOffset != mClientOffset || chromeOffset != mChromeOffset) {
+ mUpdatedDimensions = true;
+ mRect = rect;
+ mDimensions = size;
+ mOrientation = orientation;
+ mClientOffset = clientOffset;
+ mChromeOffset = chromeOffset;
+
+ Unused << SendUpdateDimensions(GetDimensionInfo());
+ }
+}
+
+DimensionInfo BrowserParent::GetDimensionInfo() {
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ MOZ_ASSERT(widget);
+ CSSToLayoutDeviceScale widgetScale = widget->GetDefaultScale();
+
+ LayoutDeviceIntRect devicePixelRect = ViewAs<LayoutDevicePixel>(
+ mRect, PixelCastJustification::LayoutDeviceIsScreenForTabDims);
+ LayoutDeviceIntSize devicePixelSize = ViewAs<LayoutDevicePixel>(
+ mDimensions, PixelCastJustification::LayoutDeviceIsScreenForTabDims);
+
+ CSSRect unscaledRect = devicePixelRect / widgetScale;
+ CSSSize unscaledSize = devicePixelSize / widgetScale;
+ DimensionInfo di(unscaledRect, unscaledSize, mOrientation, mClientOffset,
+ mChromeOffset);
+ return di;
+}
+
+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);
+ Unused << SendHandleAccessKey(localEvent, aCharCodes);
+ }
+}
+
+void BrowserParent::Activate(uint64_t aActionId) {
+ LOGBROWSERFOCUS(("Activate %p", this));
+ if (!mIsDestroyed) {
+ SetTopLevelWebFocus(this); // Intentionally inside "if"
+ Unused << SendActivate(aActionId);
+ }
+}
+
+void BrowserParent::Deactivate(bool aWindowLowering, uint64_t aActionId) {
+ LOGBROWSERFOCUS(("Deactivate %p", this));
+ 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 uint32_t&,
+ const IAccessibleHolder&) {
+ // Reference freed in DeallocPDocAccessibleParent.
+ return do_AddRef(new a11y::DocAccessibleParent()).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 uint32_t& aMsaaID,
+ const IAccessibleHolder& aDocCOMProxy) {
+ 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) {
+ // 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);
+ mozilla::ipc::IPCResult added = parentDoc->AddChildDoc(doc, aParentID);
+ if (!added) {
+# ifdef DEBUG
+ return added;
+# else
+ return IPC_OK();
+# endif
+ }
+
+# ifdef XP_WIN
+ MOZ_ASSERT(aDocCOMProxy.IsNull());
+ a11y::WrapperFor(doc)->SetID(aMsaaID);
+ if (a11y::nsWinUtils::IsWindowEmulationStarted()) {
+ doc->SetEmulatedWindowHandle(parentDoc->GetEmulatedWindowHandle());
+ }
+# else
+ Unused << aDoc->SendConstructedInParentProcess();
+# endif
+
+ return IPC_OK();
+ }
+
+ if (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();
+# ifdef XP_WIN
+ MOZ_ASSERT(!aDocCOMProxy.IsNull());
+ RefPtr<IAccessible> proxy(aDocCOMProxy.Get());
+ doc->SetCOMInterface(proxy);
+# endif
+ a11y::ProxyCreated(
+ doc, a11y::Interfaces::DOCUMENT | a11y::Interfaces::HYPERTEXT);
+# ifdef XP_WIN
+ // This *must* be called after ProxyCreated because WrapperFor will fail
+ // before that.
+ a11y::AccessibleWrap* wrapper = a11y::WrapperFor(doc);
+ MOZ_ASSERT(wrapper);
+ wrapper->SetID(aMsaaID);
+# endif
+ a11y::DocAccessibleParent* embedderDoc;
+ uint64_t embedderID;
+ Tie(embedderDoc, embedderID) = doc->GetRemoteEmbedder();
+ // 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 (embedderDoc) {
+ MOZ_ASSERT(embedderID);
+ mozilla::ipc::IPCResult added =
+ embedderDoc->AddChildDoc(doc, embedderID,
+ /* aCreating */ false);
+ 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);
+ }
+
+ doc->SetTopLevel();
+ a11y::DocManager::RemoteDocAdded(doc);
+# ifdef XP_WIN
+ a11y::WrapperFor(doc)->SetID(aMsaaID);
+ MOZ_ASSERT(!aDocCOMProxy.IsNull());
+
+ RefPtr<IAccessible> proxy(aDocCOMProxy.Get());
+ doc->SetCOMInterface(proxy);
+ doc->MaybeInitWindowEmulation();
+ if (a11y::Accessible* outerDoc = doc->OuterDocOfRemoteBrowser()) {
+ doc->SendParentCOMProxy(outerDoc);
+ }
+# endif
+ }
+ return IPC_OK();
+}
+#endif
+
+PFilePickerParent* BrowserParent::AllocPFilePickerParent(const nsString& aTitle,
+ const int16_t& aMode) {
+ return new FilePickerParent(aTitle, aMode);
+}
+
+bool BrowserParent::DeallocPFilePickerParent(PFilePickerParent* actor) {
+ delete actor;
+ return true;
+}
+
+IPCResult BrowserParent::RecvIndexedDBPermissionRequest(
+ nsIPrincipal* aPrincipal, IndexedDBPermissionRequestResolver&& aResolve) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsCOMPtr<nsIPrincipal> principal(aPrincipal);
+ if (!principal) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ if (NS_WARN_IF(!mFrameElement)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ RefPtr<indexedDB::PermissionRequestHelper> actor =
+ new indexedDB::PermissionRequestHelper(mFrameElement, principal,
+ aResolve);
+
+ mozilla::Result permissionOrErr = actor->PromptIfNeeded();
+ if (permissionOrErr.isErr()) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ if (permissionOrErr.inspect() !=
+ indexedDB::PermissionRequestBase::kPermissionPrompt) {
+ aResolve(permissionOrErr.inspect());
+ }
+
+ return IPC_OK();
+}
+
+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");
+ }
+
+ // 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();
+ UpdateVsyncParentVsyncSource();
+ return mVsyncParent.get();
+}
+
+bool BrowserParent::DeallocPVsyncParent(PVsyncParent* aActor) {
+ MOZ_ASSERT(aActor);
+ mVsyncParent = nullptr;
+ return true;
+}
+
+void BrowserParent::UpdateVsyncParentVsyncSource() {
+ if (!mVsyncParent) {
+ return;
+ }
+
+ if (nsCOMPtr<nsIWidget> widget = GetWidget()) {
+ mVsyncParent->UpdateVsyncSource(widget->GetVsyncSource());
+ }
+}
+
+void BrowserParent::SendMouseEvent(const nsAString& aType, float aX, float aY,
+ int32_t aButton, int32_t aClickCount,
+ int32_t aModifiers) {
+ if (!mIsDestroyed) {
+ Unused << PBrowserParent::SendMouseEvent(nsString(aType), aX, aY, aButton,
+ aClickCount, aModifiers);
+ }
+}
+
+void BrowserParent::MouseEnterIntoWidget() {
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (widget) {
+ // When we mouseenter the remote target, the remote target's cursor should
+ // become the current cursor. When we mouseexit, we stop.
+ mRemoteTargetSetsCursor = true;
+ if (mCursor != eCursorInvalid) {
+ widget->SetCursor(mCursor, mCustomCursor, mCustomCursorHotspotX,
+ mCustomCursorHotspotY);
+ }
+ }
+
+ // 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.mRefPoint);
+
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (widget) {
+ // 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;
+ if (mCursor != eCursorInvalid) {
+ widget->SetCursor(mCursor, mCustomCursor, mCustomCursorHotspotX,
+ mCustomCursorHotspotY);
+ }
+ } 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
+ ? SendRealMouseButtonEvent(localEvent, guid, blockId)
+ : SendNormalPriorityRealMouseButtonEvent(localEvent, guid, blockId);
+ NS_WARNING_ASSERTION(
+ ret, "SendRealMouseButtonEvent(eMouseEnterIntoWidget) 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;
+ }
+
+ 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) {
+ 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) {
+ 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) {
+ 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::RecvRequestNativeKeyBindings(
+ const uint32_t& aType, const WidgetKeyboardEvent& aEvent,
+ nsTArray<CommandInt>* aCommands) {
+ MOZ_ASSERT(aCommands);
+ MOZ_ASSERT(aCommands->IsEmpty());
+
+ nsIWidget::NativeKeyBindingsType keyBindingsType =
+ static_cast<nsIWidget::NativeKeyBindingsType>(aType);
+ switch (keyBindingsType) {
+ case nsIWidget::NativeKeyBindingsForSingleLineEditor:
+ case nsIWidget::NativeKeyBindingsForMultiLineEditor:
+ case nsIWidget::NativeKeyBindingsForRichTextEditor:
+ 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();
+ }
+
+ if (localEvent.InitEditCommandsFor(keyBindingsType)) {
+ *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) {
+ 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 uint32_t& aModifierFlags, const uint64_t& aObserverId) {
+ AutoSynthesizedEventResponder responder(this, aObserverId, "mouseevent");
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (widget) {
+ widget->SynthesizeNativeMouseEvent(aPoint, aNativeMessage, aModifierFlags,
+ responder.GetObserver());
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvSynthesizeNativeMouseMove(
+ const LayoutDeviceIntPoint& aPoint, const uint64_t& aObserverId) {
+ 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) {
+ 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) {
+ 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::RecvSynthesizeNativeTouchTap(
+ const LayoutDeviceIntPoint& aPoint, const bool& aLongTap,
+ const uint64_t& aObserverId) {
+ 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) {
+ AutoSynthesizedEventResponder responder(this, aObserverId, "cleartouch");
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (widget) {
+ widget->ClearNativeTouchSequence(responder.GetObserver());
+ }
+ return IPC_OK();
+}
+
+void BrowserParent::SendRealKeyEvent(WidgetKeyboardEvent& aEvent) {
+ if (mIsDestroyed || !mIsReadyToHandleInputEvents) {
+ return;
+ }
+ aEvent.mRefPoint = TransformParentToChild(aEvent.mRefPoint);
+
+ if (aEvent.mMessage == eKeyPress) {
+ // XXX Should we do this only when input context indicates an editor having
+ // focus and the key event won't cause inputting text?
+ aEvent.InitAllEditCommands();
+ } else {
+ aEvent.PreventNativeKeyBindings();
+ }
+ DebugOnly<bool> ret =
+ Manager()->IsInputPriorityEventEnabled()
+ ? PBrowserParent::SendRealKeyEvent(aEvent)
+ : PBrowserParent::SendNormalPriorityRealKeyEvent(aEvent);
+
+ NS_WARNING_ASSERTION(ret, "PBrowserParent::SendRealKeyEvent() failed");
+ MOZ_ASSERT(!ret || aEvent.HasBeenPostedToRemoteProcess());
+}
+
+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) {
+ 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)
+ : PBrowserParent::SendNormalPriorityHandleTap(
+ aType, TransformParentToChild(aPoint), aModifiers, aGuid,
+ aInputBlockId);
+}
+
+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::UnpackClonedMessageDataForParent(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::UnpackClonedMessageDataForParent(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,
+ const nsCString& aCursorData, const uint32_t& aWidth,
+ const uint32_t& aHeight, 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) {
+ if (aHeight * aStride != aCursorData.Length() ||
+ aStride < aWidth * gfx::BytesPerPixel(aFormat)) {
+ return IPC_FAIL(this, "Invalid custom cursor data");
+ }
+ const gfx::IntSize size(aWidth, aHeight);
+ RefPtr<gfx::DataSourceSurface> customCursor =
+ gfx::CreateDataSourceSurfaceFromData(
+ size, aFormat,
+ reinterpret_cast<const uint8_t*>(aCursorData.BeginReading()),
+ aStride);
+
+ RefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(customCursor, size);
+ cursorImage = image::ImageOps::CreateFromDrawable(drawable);
+ }
+
+ mCursor = aCursor;
+ mCustomCursor = cursorImage;
+ mCustomCursorHotspotX = aHotspotX;
+ mCustomCursorHotspotY = aHotspotY;
+
+ if (!mRemoteTargetSetsCursor) {
+ return IPC_OK();
+ }
+
+ widget->SetCursor(aCursor, cursorImage, aHotspotX, aHotspotY);
+ 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();
+
+ xulBrowserWindow->ShowTooltip(aX, aY, aTooltip, aDirection, el);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvHideTooltip() {
+ 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();
+ }
+
+ 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();
+ }
+ 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();
+ }
+ 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();
+ }
+ 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();
+ }
+
+ 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();
+ }
+ mContentCache.AssignContent(aContentCache, widget, &aIMENotification);
+ mContentCache.MaybeNotifyIME(widget, aIMENotification);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvOnEventNeedingAckHandled(
+ const EventMessage& aMessage) {
+ // 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);
+ return IPC_OK();
+}
+
+void BrowserParent::HandledWindowedPluginKeyEvent(
+ const NativeEventData& aKeyEventData, bool aIsConsumed) {
+ DebugOnly<bool> ok =
+ SendHandledWindowedPluginKeyEvent(aKeyEventData, aIsConsumed);
+ NS_WARNING_ASSERTION(ok, "SendHandledWindowedPluginKeyEvent failed");
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvOnWindowedPluginKeyEvent(
+ const NativeEventData& aKeyEventData) {
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (NS_WARN_IF(!widget)) {
+ // Notifies the plugin process of the key event being not consumed by us.
+ HandledWindowedPluginKeyEvent(aKeyEventData, false);
+ return IPC_OK();
+ }
+ nsresult rv = widget->OnWindowedPluginKeyEvent(aKeyEventData, this);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ // Notifies the plugin process of the key event being not consumed by us.
+ HandledWindowedPluginKeyEvent(aKeyEventData, false);
+ return IPC_OK();
+ }
+
+ // If the key event is posted to another process, we need to wait a call
+ // of HandledWindowedPluginKeyEvent(). So, nothing to do here in this case.
+ if (rv == NS_SUCCESS_EVENT_HANDLED_ASYNCHRONOUSLY) {
+ return IPC_OK();
+ }
+
+ // Otherwise, the key event is handled synchronously. Let's notify the
+ // plugin process of the key event's result.
+ bool consumed = (rv == NS_SUCCESS_EVENT_CONSUMED);
+ HandledWindowedPluginKeyEvent(aKeyEventData, consumed);
+
+ 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 LayoutDeviceIntPoint& aPoint) {
+ LayoutDeviceToLayoutDeviceMatrix4x4 matrix =
+ GetChildToParentConversionMatrix();
+ if (!matrix.Invert()) {
+ return LayoutDeviceIntPoint(0, 0);
+ }
+ return TransformPoint(aPoint, matrix);
+}
+
+LayoutDevicePoint BrowserParent::TransformParentToChild(
+ const LayoutDevicePoint& aPoint) {
+ LayoutDeviceToLayoutDeviceMatrix4x4 matrix =
+ GetChildToParentConversionMatrix();
+ if (!matrix.Invert()) {
+ return LayoutDevicePoint(0.0, 0.0);
+ }
+ return TransformPoint(aPoint, matrix);
+}
+
+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) {
+ mChildToParentConversionMatrix = aMatrix;
+ 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) {
+ NS_ENSURE_TRUE(mFrameElement, 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.
+ Document* doc = mFrameElement->OwnerDoc();
+ nsPresContext* presContext = doc->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;
+ }
+ }
+
+ EventDispatcher::Dispatch(mFrameElement, 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.
+ // XXX If there were two or more remote processes, this may be called
+ // twice or more for a keyboard event, that must be a bug. But how to
+ // detect if received event has already been handled?
+
+ MOZ_ASSERT(aEvent.mMessage == eKeyPress);
+ 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()) {
+ nsPresContext* presContext = presShell->GetPresContext();
+ NS_ENSURE_TRUE(presContext, IPC_OK());
+
+ EventDispatcher::Dispatch(mFrameElement, 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 Maybe<WebProgressData>& aWebProgressData,
+ const RequestData& aRequestData, const uint32_t aStateFlags,
+ const nsresult aStatus,
+ const Maybe<WebProgressStateChangeData>& aStateChangeData) {
+ if (mSuspendedProgressEvents) {
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsIBrowser> browser = GetBrowser();
+ if (!GetBrowsingContext()->GetWebProgress() || !browser) {
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsIWebProgress> webProgress;
+ nsCOMPtr<nsIRequest> request;
+ ReconstructWebProgressAndRequest(aWebProgressData, aRequestData,
+ getter_AddRefs(webProgress),
+ getter_AddRefs(request));
+
+ if (aWebProgressData && aWebProgressData->isTopLevel() &&
+ aStateChangeData.isSome()) {
+ Unused << browser->SetIsNavigating(aStateChangeData->isNavigating());
+ Unused << browser->SetMayEnableCharacterEncodingMenu(
+ aStateChangeData->mayEnableCharacterEncodingMenu());
+ Unused << browser->SetCharsetAutodetected(
+ aStateChangeData->charsetAutodetected());
+ Unused << browser->UpdateForStateChange(aStateChangeData->charset(),
+ aStateChangeData->documentURI(),
+ aStateChangeData->contentType());
+ } else if (aStateChangeData.isSome()) {
+ return IPC_FAIL(
+ this,
+ "Unexpected WebProgressStateChangeData for non-top-level WebProgress");
+ }
+
+ GetBrowsingContext()->Top()->GetWebProgress()->OnStateChange(
+ webProgress, request, aStateFlags, aStatus);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvOnProgressChange(
+ const Maybe<WebProgressData>& aWebProgressData,
+ const RequestData& aRequestData, const int32_t aCurSelfProgress,
+ const int32_t aMaxSelfProgress, const int32_t aCurTotalProgress,
+ const int32_t aMaxTotalProgress) {
+ if (mSuspendedProgressEvents) {
+ return IPC_OK();
+ }
+
+ if (!GetBrowsingContext()->GetWebProgress()) {
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsIWebProgress> webProgress;
+ nsCOMPtr<nsIRequest> request;
+ ReconstructWebProgressAndRequest(aWebProgressData, aRequestData,
+ getter_AddRefs(webProgress),
+ getter_AddRefs(request));
+
+ GetBrowsingContext()->Top()->GetWebProgress()->OnProgressChange(
+ webProgress, request, aCurSelfProgress, aMaxSelfProgress,
+ aCurTotalProgress, aMaxTotalProgress);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvOnLocationChange(
+ const Maybe<WebProgressData>& aWebProgressData,
+ const RequestData& aRequestData, nsIURI* aLocation, const uint32_t aFlags,
+ const bool aCanGoBack, const bool aCanGoForward,
+ const Maybe<WebProgressLocationChangeData>& aLocationChangeData) {
+ if (mSuspendedProgressEvents) {
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsIBrowser> browser = GetBrowser();
+ if (!GetBrowsingContext()->GetWebProgress() || !browser) {
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsIWebProgress> webProgress;
+ nsCOMPtr<nsIRequest> request;
+ ReconstructWebProgressAndRequest(aWebProgressData, aRequestData,
+ getter_AddRefs(webProgress),
+ getter_AddRefs(request));
+
+ Unused << browser->UpdateWebNavigationForLocationChange(aCanGoBack,
+ aCanGoForward);
+
+ if (aWebProgressData && aWebProgressData->isTopLevel() &&
+ aLocationChangeData.isSome()) {
+ Unused << browser->SetIsNavigating(aLocationChangeData->isNavigating());
+ Unused << browser->UpdateForLocationChange(
+ aLocation, aLocationChangeData->charset(),
+ aLocationChangeData->mayEnableCharacterEncodingMenu(),
+ aLocationChangeData->charsetAutodetected(),
+ aLocationChangeData->documentURI(), aLocationChangeData->title(),
+ aLocationChangeData->contentPrincipal(),
+ aLocationChangeData->contentPartitionedPrincipal(),
+ aLocationChangeData->csp(), aLocationChangeData->referrerInfo(),
+ aLocationChangeData->isSyntheticDocument(),
+ aLocationChangeData->requestContextID().isSome(),
+ aLocationChangeData->requestContextID().valueOr(0),
+ aLocationChangeData->contentType());
+ }
+
+ GetBrowsingContext()->Top()->GetWebProgress()->OnLocationChange(
+ webProgress, 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 (!(aFlags & nsIWebProgressListener::LOCATION_CHANGE_SAME_DOCUMENT)) {
+ GetBrowsingContext()->UpdateSecurityState();
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvOnStatusChange(
+ const Maybe<WebProgressData>& aWebProgressData,
+ const RequestData& aRequestData, const nsresult aStatus,
+ const nsString& aMessage) {
+ if (mSuspendedProgressEvents) {
+ return IPC_OK();
+ }
+
+ if (!GetBrowsingContext()->GetWebProgress()) {
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsIWebProgress> webProgress;
+ nsCOMPtr<nsIRequest> request;
+ ReconstructWebProgressAndRequest(aWebProgressData, aRequestData,
+ getter_AddRefs(webProgress),
+ getter_AddRefs(request));
+
+ GetBrowsingContext()->Top()->GetWebProgress()->OnStatusChange(
+ webProgress, request, aStatus, 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) {
+ 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);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvSetAllowDeprecatedTls(bool value) {
+ Preferences::SetBool("security.tls.version.enable-deprecated", value);
+ 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();
+}
+
+void BrowserParent::ReconstructWebProgressAndRequest(
+ const Maybe<WebProgressData>& aWebProgressData,
+ const RequestData& aRequestData, nsIWebProgress** aOutWebProgress,
+ nsIRequest** aOutRequest) {
+ MOZ_DIAGNOSTIC_ASSERT(aOutWebProgress,
+ "aOutWebProgress should never be null");
+ MOZ_DIAGNOSTIC_ASSERT(aOutRequest, "aOutRequest should never be null");
+
+ nsCOMPtr<nsIWebProgress> webProgress;
+ if (aWebProgressData) {
+ webProgress = new RemoteWebProgress(aWebProgressData->loadType(),
+ aWebProgressData->isLoadingDocument(),
+ aWebProgressData->isTopLevel());
+ } else {
+ webProgress = new RemoteWebProgress(0, false, false);
+ }
+ webProgress.forget(aOutWebProgress);
+
+ if (aRequestData.requestURI()) {
+ nsCOMPtr<nsIRequest> request = MakeAndAddRef<RemoteWebProgressRequest>(
+ aRequestData.requestURI(), aRequestData.originalRequestURI(),
+ aRequestData.matchedList());
+ request.forget(aOutRequest);
+ } else {
+ *aOutRequest = nullptr;
+ }
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvSessionStoreUpdate(
+ const Maybe<nsCString>& aDocShellCaps, const Maybe<bool>& aPrivatedMode,
+ nsTArray<nsCString>&& aPositions, nsTArray<int32_t>&& aPositionDescendants,
+ const nsTArray<InputFormData>& aInputs,
+ const nsTArray<CollectedInputDataValue>& aIdVals,
+ const nsTArray<CollectedInputDataValue>& aXPathVals,
+ nsTArray<nsCString>&& aOrigins, nsTArray<nsString>&& aKeys,
+ nsTArray<nsString>&& aValues, const bool aIsFullStorage,
+ const bool aNeedCollectSHistory, const uint32_t& aFlushId,
+ const bool& aIsFinal, const uint32_t& aEpoch) {
+ UpdateSessionStoreData data;
+ if (aDocShellCaps.isSome()) {
+ data.mDocShellCaps.Construct() = aDocShellCaps.value();
+ }
+ if (aPrivatedMode.isSome()) {
+ data.mIsPrivate.Construct() = aPrivatedMode.value();
+ }
+ if (aPositions.Length() != 0) {
+ data.mPositions.Construct(std::move(aPositions));
+ data.mPositionDescendants.Construct(std::move(aPositionDescendants));
+ }
+ if (aIdVals.Length() != 0) {
+ SessionStoreUtils::ComposeInputData(aIdVals, data.mId.Construct());
+ }
+ if (aXPathVals.Length() != 0) {
+ SessionStoreUtils::ComposeInputData(aXPathVals, data.mXpath.Construct());
+ }
+ if (aInputs.Length() != 0) {
+ nsTArray<int> descendants, numId, numXPath;
+ nsTArray<nsString> innerHTML;
+ nsTArray<nsCString> url;
+ for (const InputFormData& input : aInputs) {
+ descendants.AppendElement(input.descendants);
+ numId.AppendElement(input.numId);
+ numXPath.AppendElement(input.numXPath);
+ innerHTML.AppendElement(input.innerHTML);
+ url.AppendElement(input.url);
+ }
+
+ data.mInputDescendants.Construct(std::move(descendants));
+ data.mNumId.Construct(std::move(numId));
+ data.mNumXPath.Construct(std::move(numXPath));
+ data.mInnerHTML.Construct(std::move(innerHTML));
+ data.mUrl.Construct(std::move(url));
+ }
+ // In normal case, we only update the storage when needed.
+ // However, we need to reset the session storage(aOrigins.Length() will be 0)
+ // if the usage is over the "browser_sessionstore_dom_storage_limit".
+ // In this case, aIsFullStorage is true.
+ if (aOrigins.Length() != 0 || aIsFullStorage) {
+ data.mStorageOrigins.Construct(std::move(aOrigins));
+ data.mStorageKeys.Construct(std::move(aKeys));
+ data.mStorageValues.Construct(std::move(aValues));
+ data.mIsFullStorage.Construct() = aIsFullStorage;
+ }
+
+ nsCOMPtr<nsISessionStoreFunctions> funcs =
+ do_ImportModule("resource://gre/modules/SessionStoreFunctions.jsm");
+ NS_ENSURE_TRUE(funcs, IPC_OK());
+ nsCOMPtr<nsIXPConnectWrappedJS> wrapped = do_QueryInterface(funcs);
+ AutoJSAPI jsapi;
+ MOZ_ALWAYS_TRUE(jsapi.Init(wrapped->GetJSObjectGlobal()));
+ JS::Rooted<JS::Value> dataVal(jsapi.cx());
+ bool ok = ToJSValue(jsapi.cx(), data, &dataVal);
+ NS_ENSURE_TRUE(ok, IPC_OK());
+
+ nsresult rv = funcs->UpdateSessionStore(mFrameElement, mBrowsingContext,
+ aFlushId, aIsFinal, aEpoch, dataVal,
+ aNeedCollectSHistory);
+ NS_ENSURE_SUCCESS(rv, IPC_OK());
+
+ return IPC_OK();
+}
+
+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();
+}
+
+bool BrowserParent::HandleQueryContentEvent(WidgetQueryContentEvent& aEvent) {
+ nsCOMPtr<nsIWidget> textInputHandlingWidget = GetTextInputHandlingWidget();
+ if (!textInputHandlingWidget) {
+ return true;
+ }
+ if (NS_WARN_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) {
+ if (mIsDestroyed) {
+ return false;
+ }
+
+ 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::SendPasteTransferable(
+ const IPCDataTransfer& aDataTransfer, const bool& aIsPrivateData,
+ nsIPrincipal* aRequestingPrincipal,
+ const nsContentPolicyType& aContentPolicyType) {
+ return PBrowserParent::SendPasteTransferable(
+ aDataTransfer, aIsPrivateData, aRequestingPrincipal, aContentPolicyType);
+}
+
+/* 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;
+ }
+}
+
+/* static */
+void BrowserParent::UnsetPointerLockedRemoteTarget(
+ BrowserParent* aBrowserParent) {
+ if (sPointerLockedRemoteTarget == aBrowserParent) {
+ sPointerLockedRemoteTarget = nullptr;
+ }
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvRequestIMEToCommitComposition(
+ const bool& aCancel, bool* aIsCommitted, nsString* aCommittedString) {
+ nsCOMPtr<nsIWidget> widget = GetTextInputHandlingWidget();
+ if (!widget) {
+ *aIsCommitted = false;
+ return IPC_OK();
+ }
+
+ *aIsCommitted = mContentCache.RequestIMEToCommitComposition(
+ widget, aCancel, *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();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvSetNativeChildOfShareableWindow(
+ const uintptr_t& aChildWindow) {
+#if defined(XP_WIN)
+ nsCOMPtr<nsIWidget> widget = GetTopLevelWidget();
+ if (widget) {
+ // Note that this call will probably cause a sync native message to the
+ // process that owns the child window.
+ widget->SetNativeData(NS_NATIVE_CHILD_OF_SHAREABLE_WINDOW, aChildWindow);
+ }
+ return IPC_OK();
+#else
+ MOZ_ASSERT_UNREACHABLE(
+ "BrowserParent::RecvSetNativeChildOfShareableWindow not implemented!");
+ return IPC_FAIL_NO_REASON(this);
+#endif
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvDispatchFocusToTopLevelWindow() {
+ if (nsCOMPtr<nsIWidget> widget = GetTopLevelWidget()) {
+ widget->SetFocus(nsIWidget::Raise::No, CallerType::System);
+ }
+ 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;
+}
+
+PColorPickerParent* BrowserParent::AllocPColorPickerParent(
+ const nsString& aTitle, const nsString& aInitialColor) {
+ return new ColorPickerParent(aTitle, aInitialColor);
+}
+
+bool BrowserParent::DeallocPColorPickerParent(PColorPickerParent* actor) {
+ delete actor;
+ return true;
+}
+
+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;
+ }
+
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+
+ if (widget) {
+ mDPI = widget->GetDPI();
+ mRounding = widget->RoundsWidgetCoordinatesTo();
+ mDefaultScale = widget->GetDefaultScale();
+ }
+}
+
+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 (mActiveInPriorityManager != aEnabled) {
+ mActiveInPriorityManager = aEnabled;
+ // Let's inform the priority manager. This operation can end up with the
+ // changing of the process priority.
+ ProcessPriorityManager::TabActivityChanged(this, aEnabled);
+ }
+
+ if (aEnabled == mRenderLayers) {
+ if (aEnabled && mHasLayers && mPreserveLayers) {
+ // RenderLayers might be called when we've been preserving layers,
+ // and already had layers uploaded. In that case, the MozLayerTreeReady
+ // event will not naturally arrive, which can confuse the front-end
+ // layer. So we fire the event here.
+ RefPtr<BrowserParent> self = this;
+ LayersObserverEpoch epoch = mLayerTreeEpoch;
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "dom::BrowserParent::RenderLayers", [self, epoch]() {
+ MOZ_ASSERT(NS_IsMainThread());
+ self->LayerTreeUpdate(epoch, true);
+ }));
+ }
+
+ return;
+ }
+
+ // Preserve layers means that attempts to stop rendering layers
+ // will be ignored.
+ if (!aEnabled && mPreserveLayers) {
+ return;
+ }
+
+ mRenderLayers = aEnabled;
+
+ SetRenderLayersInternal(aEnabled);
+}
+
+void BrowserParent::SetRenderLayersInternal(bool aEnabled) {
+ // Increment the epoch so that layer tree updates from previous
+ // RenderLayers requests are ignored.
+ mLayerTreeEpoch = mLayerTreeEpoch.Next();
+
+ Unused << SendRenderLayers(aEnabled, mLayerTreeEpoch);
+
+ // Ask the child to repaint using the PHangMonitor channel/thread (which may
+ // be less congested).
+ if (aEnabled) {
+ Manager()->PaintTabWhileInterruptingJS(this, mLayerTreeEpoch);
+ }
+}
+
+void BrowserParent::PreserveLayers(bool aPreserveLayers) {
+ mPreserveLayers = aPreserveLayers;
+}
+
+void BrowserParent::NotifyResolutionChanged() {
+ if (!mIsDestroyed) {
+ // 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);
+ }
+}
+
+void BrowserParent::Deprioritize() {
+ if (mActiveInPriorityManager) {
+ ProcessPriorityManager::TabActivityChanged(this, false);
+ mActiveInPriorityManager = false;
+ }
+}
+
+bool BrowserParent::StartApzAutoscroll(float aAnchorX, float aAnchorY,
+ nsViewID aScrollId,
+ uint32_t aPresShellId) {
+ if (!AsyncPanZoomEnabled()) {
+ return false;
+ }
+
+ bool success = false;
+ if (mRemoteLayerTreeOwner.IsInitialized()) {
+ layers::LayersId layersId = mRemoteLayerTreeOwner.GetLayersId();
+ if (nsCOMPtr<nsIWidget> widget = GetWidget()) {
+ ScrollableLayerGuid guid(layersId, aPresShellId, aScrollId);
+
+ // The anchor coordinates that are passed in are relative to the origin
+ // of the screen, but we are sending them to APZ which only knows about
+ // coordinates relative to the widget, so convert them accordingly.
+ CSSPoint anchorCss{aAnchorX, aAnchorY};
+ LayoutDeviceIntPoint anchor =
+ RoundedToInt(anchorCss * widget->GetDefaultScale());
+ anchor -= widget->WidgetToScreenOffset();
+
+ success = widget->StartAsyncAutoscroll(
+ ViewAs<ScreenPixel>(
+ anchor, PixelCastJustification::LayoutDeviceIsScreenForBounds),
+ guid);
+ }
+ }
+ return success;
+}
+
+void BrowserParent::StopApzAutoscroll(nsViewID aScrollId,
+ uint32_t aPresShellId) {
+ if (!AsyncPanZoomEnabled()) {
+ return;
+ }
+
+ if (mRemoteLayerTreeOwner.IsInitialized()) {
+ layers::LayersId layersId = mRemoteLayerTreeOwner.GetLayersId();
+ if (nsCOMPtr<nsIWidget> widget = GetWidget()) {
+ ScrollableLayerGuid guid(layersId, aPresShellId, aScrollId);
+
+ widget->StopAsyncAutoscroll(guid);
+ }
+ }
+}
+
+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(const LayersObserverEpoch& aEpoch,
+ bool aActive) {
+ // 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.
+ //
+ // XXX: Should we still be updating |mHasLayers|?
+ if (GetBrowserBridgeParent()) {
+ return;
+ }
+
+ // Ignore updates from old epochs. They might tell us that layers are
+ // available when we've already sent a message to clear them. We can't trust
+ // the update in that case since layers could disappear anytime after that.
+ if (aEpoch != mLayerTreeEpoch || mIsDestroyed) {
+ return;
+ }
+
+ RefPtr<EventTarget> target = mFrameElement;
+ if (!target) {
+ NS_WARNING("Could not locate target for layer tree message.");
+ return;
+ }
+
+ mHasLayers = aActive;
+
+ RefPtr<Event> event = NS_NewDOMEvent(mFrameElement, nullptr, nullptr);
+ if (aActive) {
+ mHasPresented = true;
+ event->InitEvent(u"MozLayerTreeReady"_ns, true, false);
+ } else {
+ event->InitEvent(u"MozLayerTreeCleared"_ns, true, false);
+ }
+ event->SetTrusted(true);
+ event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
+ mFrameElement->DispatchEvent(*event);
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvPaintWhileInterruptingJSNoOp(
+ const LayersObserverEpoch& aEpoch) {
+ // We sent a PaintWhileInterruptingJS message when layers were already
+ // visible. In this case, we should act as if an update occurred even though
+ // we already have the layers.
+ LayerTreeUpdate(aEpoch, true);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvRemotePaintIsReady() {
+ RefPtr<EventTarget> target = mFrameElement;
+ if (!target) {
+ NS_WARNING("Could not locate target for MozAfterRemotePaint message.");
+ return IPC_OK();
+ }
+
+ RefPtr<Event> event = NS_NewDOMEvent(mFrameElement, nullptr, nullptr);
+ event->InitEvent(u"MozAfterRemotePaint"_ns, false, false);
+ event->SetTrusted(true);
+ event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
+ mFrameElement->DispatchEvent(*event);
+ return IPC_OK();
+}
+
+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();
+}
+
+mozilla::plugins::PPluginWidgetParent*
+BrowserParent::AllocPPluginWidgetParent() {
+#ifdef XP_WIN
+ return new mozilla::plugins::PluginWidgetParent();
+#else
+ MOZ_ASSERT_UNREACHABLE("AllocPPluginWidgetParent only supports Windows");
+ return nullptr;
+#endif
+}
+
+bool BrowserParent::DeallocPPluginWidgetParent(
+ mozilla::plugins::PPluginWidgetParent* aActor) {
+ delete aActor;
+ return true;
+}
+
+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;
+}
+
+class FakeChannel final : public nsIChannel,
+ public nsIAuthPromptCallback,
+ public nsIInterfaceRequestor,
+ public nsILoadContext {
+ public:
+ FakeChannel(const nsCString& aUri, uint64_t aCallbackId, Element* aElement)
+ : mCallbackId(aCallbackId), mElement(aElement) {
+ NS_NewURI(getter_AddRefs(mUri), aUri);
+ }
+
+ NS_DECL_ISUPPORTS
+
+#define NO_IMPL \
+ override { return NS_ERROR_NOT_IMPLEMENTED; }
+ NS_IMETHOD GetName(nsACString&) NO_IMPL;
+ NS_IMETHOD IsPending(bool*) NO_IMPL;
+ NS_IMETHOD GetStatus(nsresult*) NO_IMPL;
+ NS_IMETHOD Cancel(nsresult) NO_IMPL;
+ NS_IMETHOD GetCanceled(bool* aCanceled) NO_IMPL;
+ NS_IMETHOD Suspend() NO_IMPL;
+ NS_IMETHOD Resume() NO_IMPL;
+ NS_IMETHOD GetLoadGroup(nsILoadGroup**) NO_IMPL;
+ NS_IMETHOD SetLoadGroup(nsILoadGroup*) NO_IMPL;
+ NS_IMETHOD SetLoadFlags(nsLoadFlags) NO_IMPL;
+ NS_IMETHOD GetLoadFlags(nsLoadFlags*) NO_IMPL;
+ NS_IMETHOD GetTRRMode(nsIRequest::TRRMode* aTRRMode) NO_IMPL;
+ NS_IMETHOD SetTRRMode(nsIRequest::TRRMode aMode) NO_IMPL;
+ NS_IMETHOD GetIsDocument(bool*) NO_IMPL;
+ NS_IMETHOD GetOriginalURI(nsIURI**) NO_IMPL;
+ NS_IMETHOD SetOriginalURI(nsIURI*) NO_IMPL;
+ NS_IMETHOD GetURI(nsIURI** aUri) override {
+ nsCOMPtr<nsIURI> copy = mUri;
+ copy.forget(aUri);
+ return NS_OK;
+ }
+ NS_IMETHOD GetOwner(nsISupports**) NO_IMPL;
+ NS_IMETHOD SetOwner(nsISupports*) NO_IMPL;
+ NS_IMETHOD GetLoadInfo(nsILoadInfo** aLoadInfo) override {
+ nsCOMPtr<nsILoadInfo> copy = mLoadInfo;
+ copy.forget(aLoadInfo);
+ return NS_OK;
+ }
+ NS_IMETHOD SetLoadInfo(nsILoadInfo* aLoadInfo) override {
+ mLoadInfo = aLoadInfo;
+ return NS_OK;
+ }
+ NS_IMETHOD GetNotificationCallbacks(
+ nsIInterfaceRequestor** aRequestor) override {
+ NS_ADDREF(*aRequestor = this);
+ return NS_OK;
+ }
+ NS_IMETHOD SetNotificationCallbacks(nsIInterfaceRequestor*) NO_IMPL;
+ NS_IMETHOD GetSecurityInfo(nsISupports**) NO_IMPL;
+ NS_IMETHOD GetContentType(nsACString&) NO_IMPL;
+ NS_IMETHOD SetContentType(const nsACString&) NO_IMPL;
+ NS_IMETHOD GetContentCharset(nsACString&) NO_IMPL;
+ NS_IMETHOD SetContentCharset(const nsACString&) NO_IMPL;
+ NS_IMETHOD GetContentLength(int64_t*) NO_IMPL;
+ NS_IMETHOD SetContentLength(int64_t) NO_IMPL;
+ NS_IMETHOD Open(nsIInputStream**) NO_IMPL;
+ NS_IMETHOD AsyncOpen(nsIStreamListener*) NO_IMPL;
+ NS_IMETHOD GetContentDisposition(uint32_t*) NO_IMPL;
+ NS_IMETHOD SetContentDisposition(uint32_t) NO_IMPL;
+ NS_IMETHOD GetContentDispositionFilename(nsAString&) NO_IMPL;
+ NS_IMETHOD SetContentDispositionFilename(const nsAString&) NO_IMPL;
+ NS_IMETHOD GetContentDispositionHeader(nsACString&) NO_IMPL;
+ NS_IMETHOD OnAuthAvailable(nsISupports* aContext,
+ nsIAuthInformation* aAuthInfo) override;
+ NS_IMETHOD OnAuthCancelled(nsISupports* aContext, bool userCancel) override;
+ NS_IMETHOD GetInterface(const nsIID& uuid, void** result) override {
+ return QueryInterface(uuid, result);
+ }
+ NS_IMETHOD GetAssociatedWindow(mozIDOMWindowProxy**) NO_IMPL;
+ NS_IMETHOD GetTopWindow(mozIDOMWindowProxy**) NO_IMPL;
+ NS_IMETHOD GetTopFrameElement(Element** aElement) override {
+ RefPtr<Element> elem = mElement;
+ elem.forget(aElement);
+ return NS_OK;
+ }
+ NS_IMETHOD GetIsContent(bool*) NO_IMPL;
+ NS_IMETHOD GetUsePrivateBrowsing(bool*) NO_IMPL;
+ NS_IMETHOD SetUsePrivateBrowsing(bool) NO_IMPL;
+ NS_IMETHOD SetPrivateBrowsing(bool) NO_IMPL;
+ NS_IMETHOD GetScriptableOriginAttributes(JSContext*,
+ JS::MutableHandleValue) NO_IMPL;
+ NS_IMETHOD_(void)
+ GetOriginAttributes(mozilla::OriginAttributes& aAttrs) override {}
+ NS_IMETHOD GetUseRemoteTabs(bool*) NO_IMPL;
+ NS_IMETHOD SetRemoteTabs(bool) NO_IMPL;
+ NS_IMETHOD GetUseRemoteSubframes(bool*) NO_IMPL;
+ NS_IMETHOD SetRemoteSubframes(bool) NO_IMPL;
+ NS_IMETHOD GetUseTrackingProtection(bool*) NO_IMPL;
+ NS_IMETHOD SetUseTrackingProtection(bool) NO_IMPL;
+#undef NO_IMPL
+
+ protected:
+ ~FakeChannel() = default;
+
+ nsCOMPtr<nsIURI> mUri;
+ uint64_t mCallbackId;
+ RefPtr<Element> mElement;
+ nsCOMPtr<nsILoadInfo> mLoadInfo;
+};
+
+NS_IMPL_ISUPPORTS(FakeChannel, nsIChannel, nsIAuthPromptCallback, nsIRequest,
+ nsIInterfaceRequestor, nsILoadContext);
+
+mozilla::ipc::IPCResult BrowserParent::RecvAsyncAuthPrompt(
+ const nsCString& aUri, const nsString& aRealm,
+ const uint64_t& aCallbackId) {
+ nsCOMPtr<nsIAuthPrompt2> authPrompt;
+ GetAuthPrompt(nsIAuthPromptProvider::PROMPT_NORMAL,
+ NS_GET_IID(nsIAuthPrompt2), getter_AddRefs(authPrompt));
+ RefPtr<FakeChannel> channel =
+ new FakeChannel(aUri, aCallbackId, mFrameElement);
+ uint32_t promptFlags = nsIAuthInformation::AUTH_HOST;
+
+ RefPtr<nsAuthInformationHolder> holder =
+ new nsAuthInformationHolder(promptFlags, aRealm, ""_ns);
+
+ uint32_t level = nsIAuthPrompt2::LEVEL_NONE;
+ nsCOMPtr<nsICancelable> dummy;
+ nsresult rv = authPrompt->AsyncPromptAuth(channel, channel, nullptr, level,
+ holder, getter_AddRefs(dummy));
+
+ if (NS_FAILED(rv)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvInvokeDragSession(
+ nsTArray<IPCDataTransfer>&& aTransfers, const uint32_t& aAction,
+ Maybe<Shmem>&& aVisualDnDData, const uint32_t& aStride,
+ const gfx::SurfaceFormat& aFormat, const LayoutDeviceIntRect& aDragRect,
+ nsIPrincipal* aPrincipal, nsIContentSecurityPolicy* aCsp,
+ const CookieJarSettingsArgs& aCookieJarSettingsArgs) {
+ PresShell* presShell = mFrameElement->OwnerDoc()->GetPresShell();
+ if (!presShell) {
+ Unused << Manager()->SendEndDragSession(true, true, LayoutDeviceIntPoint(),
+ 0);
+ // 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(aTransfers), aDragRect,
+ aPrincipal, aCsp, cookieJarSettings);
+
+ if (!aVisualDnDData.isNothing() && aVisualDnDData.ref().IsReadable() &&
+ aVisualDnDData.ref().Size<char>() >= aDragRect.height * aStride) {
+ dragStartData->SetVisualization(gfx::CreateDataSourceSurfaceFromData(
+ gfx::IntSize(aDragRect.width, aDragRect.height), aFormat,
+ aVisualDnDData.ref().get<uint8_t>(), aStride));
+ }
+
+ nsCOMPtr<nsIDragService> dragService =
+ do_GetService("@mozilla.org/widget/dragservice;1");
+ if (dragService) {
+ dragService->MaybeAddChildProcess(Manager());
+ }
+
+ presShell->GetPresContext()
+ ->EventStateManager()
+ ->BeginTrackingRemoteDragGesture(mFrameElement, dragStartData);
+
+ if (aVisualDnDData.isSome()) {
+ Unused << DeallocShmem(aVisualDnDData.ref());
+ }
+
+ return IPC_OK();
+}
+
+bool BrowserParent::AsyncPanZoomEnabled() const {
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ return widget && widget->AsyncPanZoomEnabled();
+}
+
+void BrowserParent::StartPersistence(
+ CanonicalBrowsingContext* aContext,
+ nsIWebBrowserPersistDocumentReceiver* aRecv, ErrorResult& aRv) {
+ auto* 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) {
+ if (!aURI) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ RefPtr<nsIWidget> widget = GetWidget();
+ if (NS_WARN_IF(!widget)) {
+ return IPC_OK();
+ }
+ nsCOMPtr<IHistory> history = services::GetHistory();
+ if (history) {
+ Unused << history->VisitURI(widget, aURI, aLastVisitedURI, aFlags);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvQueryVisitedState(
+ const nsTArray<RefPtr<nsIURI>>&& aURIs) {
+#ifdef MOZ_ANDROID_HISTORY
+ nsCOMPtr<IHistory> history = services::GetHistory();
+ if (NS_WARN_IF(!history)) {
+ return IPC_OK();
+ }
+ RefPtr<nsIWidget> widget = GetWidget();
+ if (NS_WARN_IF(!widget)) {
+ return IPC_OK();
+ }
+
+ for (size_t i = 0; i < aURIs.Length(); ++i) {
+ if (!aURIs[i]) {
+ return IPC_FAIL(this, "Received null URI");
+ }
+ }
+
+ GeckoViewHistory* gvHistory = static_cast<GeckoViewHistory*>(history.get());
+ gvHistory->QueryVisitedState(widget, 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();
+}
+
+NS_IMETHODIMP
+FakeChannel::OnAuthAvailable(nsISupports* aContext,
+ nsIAuthInformation* aAuthInfo) {
+ nsAuthInformationHolder* holder =
+ static_cast<nsAuthInformationHolder*>(aAuthInfo);
+
+ if (!net::gNeckoChild->SendOnAuthAvailable(
+ mCallbackId, holder->User(), holder->Password(), holder->Domain())) {
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+FakeChannel::OnAuthCancelled(nsISupports* aContext, bool userCancel) {
+ if (!net::gNeckoChild->SendOnAuthCancelled(mCallbackId, userCancel)) {
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvIsWindowSupportingProtectedMedia(
+ const uint64_t& aOuterWindowID,
+ IsWindowSupportingProtectedMediaResolver&& aResolve) {
+#ifdef XP_WIN
+ bool isFxrWindow =
+ FxRWindowManager::GetInstance()->IsFxRWindow(aOuterWindowID);
+ aResolve(!isFxrWindow);
+#else
+ 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();
+}
+
+bool BrowserParent::SetPointerLock() {
+ if (sPointerLockedRemoteTarget) {
+ return sPointerLockedRemoteTarget == this;
+ }
+
+ sPointerLockedRemoteTarget = this;
+ return true;
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvRequestPointerLock(
+ RequestPointerLockResolver&& aResolve) {
+ nsCString error;
+ if (!SetPointerLock()) {
+ error = "PointerLockDeniedInUse";
+ } else {
+ PointerEventHandler::ReleaseAllPointerCaptureRemoteTarget();
+ }
+ aResolve(error);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult BrowserParent::RecvReleasePointerLock() {
+ MOZ_ASSERT_IF(sPointerLockedRemoteTarget, sPointerLockedRemoteTarget == this);
+ UnsetPointerLockedRemoteTarget(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();
+}
+
+} // namespace mozilla::dom
diff --git a/dom/ipc/BrowserParent.h b/dom/ipc/BrowserParent.h
new file mode 100644
index 0000000000..db4ace2dc5
--- /dev/null
+++ b/dom/ipc/BrowserParent.h
@@ -0,0 +1,1018 @@
+/* -*- 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/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 "nsIKeyEventInPluginCallback.h"
+#include "nsIRemoteTab.h"
+#include "nsIWidget.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 nsIKeyEventInPluginCallback,
+ 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* GetPointerLockedRemoteTarget();
+
+ 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; }
+
+ 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 (auto iter = browserBridges.ConstIter(); !iter.Done(); iter.Next()) {
+ BrowserBridgeParent* browserBridge =
+ static_cast<BrowserBridgeParent*>(iter.Get()->GetKey());
+ 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 (auto iter = browserBridges.ConstIter(); !iter.Done(); iter.Next()) {
+ BrowserBridgeParent* browserBridge =
+ static_cast<BrowserBridgeParent*>(iter.Get()->GetKey());
+ 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 RecvMoveFocus(const bool& aForward,
+ const bool& aForDocumentNavigation);
+
+ mozilla::ipc::IPCResult RecvSizeShellTo(const uint32_t& aFlags,
+ const int32_t& aWidth,
+ const int32_t& aHeight,
+ const int32_t& aShellItemWidth,
+ const int32_t& aShellItemHeight);
+
+ mozilla::ipc::IPCResult RecvDropLinks(nsTArray<nsString>&& aLinks);
+
+ mozilla::ipc::IPCResult RecvEvent(const RemoteDOMEvent& aEvent);
+
+ mozilla::ipc::IPCResult RecvReplyKeyEvent(const WidgetKeyboardEvent& aEvent);
+
+ 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 Maybe<WebProgressData>& awebProgressData,
+ const RequestData& aRequestData, const uint32_t aStateFlags,
+ const nsresult aStatus,
+ const Maybe<WebProgressStateChangeData>& aStateChangeData);
+
+ mozilla::ipc::IPCResult RecvOnProgressChange(
+ const Maybe<WebProgressData>& aWebProgressData,
+ const RequestData& aRequestData, const int32_t aCurSelfProgress,
+ const int32_t aMaxSelfProgress, const int32_t aCurTotalProgres,
+ const int32_t aMaxTotalProgress);
+
+ mozilla::ipc::IPCResult RecvOnLocationChange(
+ const Maybe<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 Maybe<WebProgressData>& aWebProgressData,
+ const RequestData& aRequestData, const nsresult aStatus,
+ 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);
+
+ mozilla::ipc::IPCResult RecvSetAllowDeprecatedTls(bool value);
+
+ mozilla::ipc::IPCResult RecvNavigationFinished();
+
+ already_AddRefed<nsIBrowser> GetBrowser();
+
+ void ReconstructWebProgressAndRequest(
+ const Maybe<WebProgressData>& aWebProgressData,
+ const RequestData& aRequestData, nsIWebProgress** aOutWebProgress,
+ nsIRequest** aOutRequest);
+
+ mozilla::ipc::IPCResult RecvSessionStoreUpdate(
+ const Maybe<nsCString>& aDocShellCaps, const Maybe<bool>& aPrivatedMode,
+ nsTArray<nsCString>&& aPositions,
+ nsTArray<int32_t>&& aPositionDescendants,
+ const nsTArray<InputFormData>& aInputs,
+ const nsTArray<CollectedInputDataValue>& aIdVals,
+ const nsTArray<CollectedInputDataValue>& aXPathVals,
+ nsTArray<nsCString>&& aOrigins, nsTArray<nsString>&& aKeys,
+ nsTArray<nsString>&& aValues, const bool aIsFullStorage,
+ const bool aNeedCollectSHistory, const uint32_t& aFlushId,
+ const bool& aIsFinal, const uint32_t& aEpoch);
+
+ mozilla::ipc::IPCResult RecvIntrinsicSizeOrRatioChanged(
+ const Maybe<IntrinsicSize>& aIntrinsicSize,
+ const Maybe<AspectRatio>& aIntrinsicRatio);
+
+ 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);
+
+ mozilla::ipc::IPCResult RecvRequestIMEToCommitComposition(
+ const bool& aCancel, bool* aIsCommitted, nsString* aCommittedString);
+
+ mozilla::ipc::IPCResult RecvGetInputContext(widget::IMEState* aIMEState);
+
+ mozilla::ipc::IPCResult RecvSetInputContext(
+ const widget::InputContext& aContext,
+ const widget::InputContextAction& aAction);
+
+ // See nsIKeyEventInPluginCallback
+ virtual void HandledWindowedPluginKeyEvent(
+ const NativeEventData& aKeyEventData, bool aIsConsumed) override;
+
+ mozilla::ipc::IPCResult RecvOnWindowedPluginKeyEvent(
+ const NativeEventData& aKeyEventData);
+
+ 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,
+ const nsCString& aUri, const uint32_t& aWidth, const uint32_t& aHeight,
+ 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 RecvSetNativeChildOfShareableWindow(
+ const uintptr_t& childWindow);
+
+ mozilla::ipc::IPCResult RecvDispatchFocusToTopLevelWindow();
+
+ 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 RecvScrollRectIntoView(
+ const nsRect& aRect, const ScrollAxis& aVertical,
+ const ScrollAxis& aHorizontal, const ScrollFlags& aScrollFlags,
+ const int32_t& aAppUnitsPerDevPixel);
+
+ PColorPickerParent* AllocPColorPickerParent(const nsString& aTitle,
+ const nsString& aInitialColor);
+
+ bool DeallocPColorPickerParent(PColorPickerParent* aColorPicker);
+
+ PVsyncParent* AllocPVsyncParent();
+
+ bool DeallocPVsyncParent(PVsyncParent* aActor);
+
+#ifdef ACCESSIBILITY
+ PDocAccessibleParent* AllocPDocAccessibleParent(PDocAccessibleParent*,
+ const uint64_t&,
+ const uint32_t&,
+ const IAccessibleHolder&);
+ bool DeallocPDocAccessibleParent(PDocAccessibleParent*);
+ virtual mozilla::ipc::IPCResult RecvPDocAccessibleConstructor(
+ PDocAccessibleParent* aDoc, PDocAccessibleParent* aParentDoc,
+ const uint64_t& aParentID, const uint32_t& aMsaaID,
+ const IAccessibleHolder& aDocCOMProxy) override;
+#endif
+
+ 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 AttachLayerManager();
+ void MaybeShowFrame();
+
+ bool Show(const OwnerShowInfo&);
+
+ void UpdateDimensions(const nsIntRect& aRect, const ScreenIntSize& aSize);
+
+ DimensionInfo GetDimensionInfo();
+
+ nsresult UpdatePosition();
+
+ 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();
+
+ 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 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 RecvSynthesizeNativeTouchTap(
+ const LayoutDeviceIntPoint& aPoint, const bool& aLongTap,
+ const uint64_t& aObserverId);
+
+ mozilla::ipc::IPCResult RecvClearNativeTouchSequence(
+ const uint64_t& aObserverId);
+
+ void SendMouseEvent(const nsAString& aType, float aX, float aY,
+ int32_t aButton, int32_t aClickCount, int32_t aModifiers);
+
+ /**
+ * 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);
+
+ 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);
+
+ 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);
+
+ PFilePickerParent* AllocPFilePickerParent(const nsString& aTitle,
+ const int16_t& aMode);
+
+ bool DeallocPFilePickerParent(PFilePickerParent* actor);
+
+ mozilla::ipc::IPCResult RecvIndexedDBPermissionRequest(
+ nsIPrincipal* aPrincipal, IndexedDBPermissionRequestResolver&& aResolve);
+
+ bool GetGlobalJSObject(JSContext* cx, JSObject** globalp);
+
+ void StartPersistence(CanonicalBrowsingContext* aContext,
+ nsIWebBrowserPersistDocumentReceiver* aRecv,
+ ErrorResult& aRv);
+
+ bool HandleQueryContentEvent(mozilla::WidgetQueryContentEvent& aEvent);
+
+ bool SendPasteTransferable(const IPCDataTransfer& aDataTransfer,
+ const bool& aIsPrivateData,
+ nsIPrincipal* aRequestingPrincipal,
+ const nsContentPolicyType& aContentPolicyType);
+
+ // 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 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();
+
+ /**
+ * Native widget remoting protocol for use with windowed plugins with e10s.
+ */
+ PPluginWidgetParent* AllocPPluginWidgetParent();
+
+ bool DeallocPPluginWidgetParent(PPluginWidgetParent* aActor);
+
+ PPaymentRequestParent* AllocPPaymentRequestParent();
+
+ bool DeallocPPaymentRequestParent(PPaymentRequestParent* aActor);
+
+ bool SendLoadRemoteScript(const nsString& aURL,
+ const bool& aRunInGlobalScope);
+
+ void LayerTreeUpdate(const LayersObserverEpoch& aEpoch, bool aActive);
+
+ mozilla::ipc::IPCResult RecvInvokeDragSession(
+ nsTArray<IPCDataTransfer>&& aTransfers, const uint32_t& aAction,
+ Maybe<Shmem>&& aVisualDnDData, const uint32_t& aStride,
+ const gfx::SurfaceFormat& aFormat, const LayoutDeviceIntRect& aDragRect,
+ nsIPrincipal* aPrincipal, nsIContentSecurityPolicy* aCsp,
+ const CookieJarSettingsArgs& aCookieJarSettingsArgs);
+
+ void AddInitialDnDDataTo(DataTransfer* aDataTransfer,
+ 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);
+ void PreserveLayers(bool aPreserveLayers);
+ void NotifyResolutionChanged();
+
+ void Deprioritize();
+
+ bool StartApzAutoscroll(float aAnchorX, float aAnchorY, nsViewID aScrollId,
+ uint32_t aPresShellId);
+ void StopApzAutoscroll(nsViewID aScrollId, uint32_t aPresShellId);
+
+ // Suspend nsIWebProgressListener events. This is used to block any further
+ // progress events from the old process when process switching away.
+ void SuspendProgressEvents() { mSuspendedProgressEvents = true; }
+
+ bool CanCancelContentJS(nsIRemoteTab::NavigationType aNavigationType,
+ int32_t aNavigationIndex,
+ nsIURI* aNavigationURI) const;
+
+ 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);
+
+ mozilla::ipc::IPCResult RecvAsyncAuthPrompt(const nsCString& aUri,
+ const nsString& aRealm,
+ const uint64_t& aCallbackId);
+
+ virtual mozilla::ipc::IPCResult Recv__delete__() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ mozilla::ipc::IPCResult RecvRemotePaintIsReady();
+
+ mozilla::ipc::IPCResult RecvRemoteIsReadyToHandleInputEvents();
+
+ mozilla::ipc::IPCResult RecvPaintWhileInterruptingJSNoOp(
+ const LayersObserverEpoch& aEpoch);
+
+ mozilla::ipc::IPCResult RecvSetDimensions(
+ const uint32_t& aFlags, const int32_t& aX, const int32_t& aY,
+ const int32_t& aCx, const int32_t& aCy, 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);
+
+ mozilla::ipc::IPCResult RecvQueryVisitedState(
+ const nsTArray<RefPtr<nsIURI>>&& aURIs);
+
+ mozilla::ipc::IPCResult RecvMaybeFireEmbedderLoadEvents(
+ EmbedderElementEventType aFireEventAtEmbeddingElement);
+
+ bool SetPointerLock();
+ 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);
+
+ private:
+ void SuppressDisplayport(bool aEnabled);
+
+ void DestroyInternal();
+
+ 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();
+
+ private:
+ // This is used when APZ needs to find the BrowserParent associated with a
+ // layer to dispatch events.
+ typedef nsDataHashtable<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);
+
+ // Keeps track of which BrowserParent requested pointer lock.
+ static BrowserParent* sPointerLockedRemoteTarget;
+
+ // Unsetter for sPointerLockedRemoteTarget; only unsets if argument matches
+ // current sPointerLockedRemoteTarget.
+ static void UnsetPointerLockedRemoteTarget(BrowserParent* aBrowserParent);
+
+ struct APZData {
+ bool operator==(const APZData& aOther) {
+ return aOther.guid == guid && aOther.blockId == blockId &&
+ aOther.apzResponse == apzResponse;
+ }
+
+ bool operator!=(const APZData& aOther) { return !(*this == aOther); }
+
+ ScrollableLayerGuid guid;
+ uint64_t blockId;
+ nsEventStatus apzResponse;
+ };
+ void SendRealTouchMoveEvent(WidgetTouchEvent& aEvent, APZData& aAPZData,
+ uint32_t aConsecutiveTouchMoveCount);
+
+ void UpdateVsyncParentVsyncSource();
+
+ 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;
+ LayersObserverEpoch mLayerTreeEpoch;
+
+ Maybe<LayoutDeviceToLayoutDeviceMatrix4x4> mChildToParentConversionMatrix;
+
+ nsIntRect mRect;
+ ScreenIntSize mDimensions;
+ hal::ScreenOrientation mOrientation;
+ 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.
+ nsCursor mCursor;
+ nsCOMPtr<imgIContainer> mCustomCursor;
+ uint32_t mCustomCursorHotspotX, mCustomCursorHotspotY;
+
+ 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 mPreserveLayers : 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;
+
+ // Whether this is active for the ProcessPriorityManager or not.
+ bool mActiveInPriorityManager : 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;
+
+ // Set to true if we're currently suspending nsIWebProgress events.
+ // We do this when we are process switching and want to suspend events
+ // from the old BrowserParent after it sent STATE_START event.
+ bool mSuspendedProgressEvents : 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..dd5572d0fc
--- /dev/null
+++ b/dom/ipc/CSPMessageUtils.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/CSPMessageUtils.h"
+#include "mozilla/ipc/PBackgroundSharedTypes.h"
+#include "nsSerializationHelper.h"
+#include "BackgroundUtils.h"
+
+namespace IPC {
+
+using namespace mozilla::ipc;
+
+void ParamTraits<nsIContentSecurityPolicy*>::Write(
+ Message* aMsg, nsIContentSecurityPolicy* aParam) {
+ bool isNull = !aParam;
+ WriteParam(aMsg, isNull);
+ if (isNull) {
+ return;
+ }
+
+ CSPInfo csp;
+ mozilla::Unused << NS_WARN_IF(NS_FAILED(CSPToCSPInfo(aParam, &csp)));
+ IPDLParamTraits<CSPInfo>::Write(aMsg, nullptr, csp);
+}
+
+bool ParamTraits<nsIContentSecurityPolicy*>::Read(
+ const Message* aMsg, PickleIterator* aIter,
+ RefPtr<nsIContentSecurityPolicy>* aResult) {
+ bool isNull;
+ if (!ReadParam(aMsg, aIter, &isNull)) {
+ return false;
+ }
+
+ if (isNull) {
+ *aResult = nullptr;
+ return true;
+ }
+
+ CSPInfo csp;
+ if (!IPDLParamTraits<CSPInfo>::Read(aMsg, aIter, nullptr, &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..3877e66402
--- /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(Message* aMsg, nsIContentSecurityPolicy* aParam);
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ 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..df33669cca
--- /dev/null
+++ b/dom/ipc/ClonedErrorHolder.cpp
@@ -0,0 +1,352 @@
+/* -*- 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
+already_AddRefed<ClonedErrorHolder> ClonedErrorHolder::Constructor(
+ const GlobalObject& aGlobal, JS::Handle<JSObject*> aError,
+ ErrorResult& aRv) {
+ return Create(aGlobal.Context(), aError, aRv);
+}
+
+// static
+already_AddRefed<ClonedErrorHolder> ClonedErrorHolder::Create(
+ JSContext* aCx, JS::Handle<JSObject*> aError, ErrorResult& aRv) {
+ RefPtr<ClonedErrorHolder> ceh = new ClonedErrorHolder();
+ ceh->Init(aCx, aError, aRv);
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+ return ceh.forget();
+}
+
+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;
+ }
+ 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::RootedValue 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) {
+ MOZ_DIAGNOSTIC_ASSERT(uint32_t(aStr.Length()) != kVoidStringLength,
+ "We should not be serializing a 4GiB string");
+ if (aStr.IsVoid()) {
+ return kVoidStringLength;
+ }
+ return aStr.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) &&
+ 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) &&
+ 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);
+ {
+ RefPtr<ClonedErrorHolder> ceh = new ClonedErrorHolder();
+ if (!ceh->Init(aCx, aReader) || !ceh->ToErrorValue(aCx, &errorVal)) {
+ return nullptr;
+ }
+ }
+ return &errorVal.toObject();
+}
+
+static JS::UniqueTwoByteChars ToJSStringBuffer(JSContext* aCx,
+ const nsString& aStr) {
+ size_t nbytes = aStr.Length() * 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::MutableHandleValue 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);
+ }
+
+ if (!ToJSString(aCx, mFilename, &filename) ||
+ !ToJSString(aCx, mMessage, &message)) {
+ return false;
+ }
+ if (!JS::CreateError(aCx, mExnType, stack, filename, mLineNumber, mColumn,
+ nullptr, message, aResult)) {
+ return false;
+ }
+
+ if (!mSourceLine.IsVoid()) {
+ JS::Rooted<JSObject*> errObj(aCx, &aResult.toObject());
+ if (JSErrorReport* err = JS_ErrorFromException(aCx, errObj)) {
+ NS_ConvertUTF8toUTF16 sourceLine(mSourceLine);
+ if (JS::UniqueTwoByteChars buffer = ToJSStringBuffer(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;
+ }
+
+ 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..1eb6af4b27
--- /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 "nsISupportsImpl.h"
+#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 {
+ NS_INLINE_DECL_REFCOUNTING(ClonedErrorHolder)
+
+ public:
+ static already_AddRefed<ClonedErrorHolder> Constructor(
+ const GlobalObject& aGlobal, JS::Handle<JSObject*> aError,
+ ErrorResult& aRv);
+
+ static already_AddRefed<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();
+ ~ClonedErrorHolder() = default;
+
+ 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::MutableHandleValue 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
+ uint32_t mColumn = 0; // 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.h b/dom/ipc/CoalescedInputData.h
new file mode 100644
index 0000000000..7d1dd86e2c
--- /dev/null
+++ b/dom/ipc/CoalescedInputData.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_CoalescedInputData_h
+#define mozilla_dom_CoalescedInputData_h
+
+#include "mozilla/UniquePtr.h"
+#include "mozilla/layers/ScrollableLayerGuid.h"
+
+namespace mozilla {
+namespace dom {
+
+template <class InputEventType>
+class CoalescedInputData {
+ protected:
+ typedef mozilla::layers::ScrollableLayerGuid 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; }
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_CoalescedInputData_h
diff --git a/dom/ipc/CoalescedMouseData.cpp b/dom/ipc/CoalescedMouseData.cpp
new file mode 100644
index 0000000000..5e3083c85b
--- /dev/null
+++ b/dom/ipc/CoalescedMouseData.cpp
@@ -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/. */
+#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 &&
+ StaticPrefs::dom_w3c_pointer_events_enabled()) {
+ // 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->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);
+}
+
+void CoalescedMouseMoveFlusher::WillRefresh(mozilla::TimeStamp aTime) {
+ MOZ_ASSERT(mRefreshDriver);
+ mBrowserChild->FlushAllCoalescedMouseData();
+ mBrowserChild->ProcessPendingCoalescedMouseDataAndDispatchEvents();
+}
+
+void CoalescedMouseMoveFlusher::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 mouse move flusher");
+ }
+}
+
+void CoalescedMouseMoveFlusher::RemoveObserver() {
+ if (mRefreshDriver) {
+ mRefreshDriver->RemoveRefreshObserver(this, FlushType::Event);
+ mRefreshDriver = nullptr;
+ }
+}
+
+CoalescedMouseMoveFlusher::CoalescedMouseMoveFlusher(
+ BrowserChild* aBrowserChild)
+ : mBrowserChild(aBrowserChild) {
+ MOZ_ASSERT(mBrowserChild);
+}
+
+CoalescedMouseMoveFlusher::~CoalescedMouseMoveFlusher() { RemoveObserver(); }
+
+nsRefreshDriver* CoalescedMouseMoveFlusher::GetRefreshDriver() {
+ PresShell* presShell = mBrowserChild->GetTopLevelPresShell();
+ if (!presShell || !presShell->GetPresContext() ||
+ !presShell->GetPresContext()->RefreshDriver()) {
+ return nullptr;
+ }
+ return presShell->GetPresContext()->RefreshDriver();
+}
diff --git a/dom/ipc/CoalescedMouseData.h b/dom/ipc/CoalescedMouseData.h
new file mode 100644
index 0000000000..01ff886a1e
--- /dev/null
+++ b/dom/ipc/CoalescedMouseData.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_CoalescedMouseData_h
+#define mozilla_dom_CoalescedMouseData_h
+
+#include "CoalescedInputData.h"
+#include "mozilla/MouseEvents.h"
+#include "nsRefreshObservers.h"
+
+class nsRefreshDriver;
+
+namespace mozilla {
+namespace 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 nsARefreshObserver {
+ public:
+ explicit CoalescedMouseMoveFlusher(BrowserChild* aBrowserChild);
+
+ virtual void WillRefresh(mozilla::TimeStamp aTime) override;
+
+ NS_INLINE_DECL_REFCOUNTING(CoalescedMouseMoveFlusher, override)
+
+ void StartObserver();
+ void RemoveObserver();
+
+ private:
+ ~CoalescedMouseMoveFlusher();
+
+ nsRefreshDriver* GetRefreshDriver();
+
+ BrowserChild* mBrowserChild;
+ RefPtr<nsRefreshDriver> mRefreshDriver;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_CoalescedMouseData_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..21f27cd9f6
--- /dev/null
+++ b/dom/ipc/CoalescedWheelData.h
@@ -0,0 +1,30 @@
+/* -*- 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 {
+namespace 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 dom
+} // namespace mozilla
+
+#endif // mozilla_dom_CoalescedWheelData_h
diff --git a/dom/ipc/ColorPickerParent.cpp b/dom/ipc/ColorPickerParent.cpp
new file mode 100644
index 0000000000..deca898633
--- /dev/null
+++ b/dom/ipc/ColorPickerParent.cpp
@@ -0,0 +1,76 @@
+/* -*- 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(nsString(aColor));
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ColorPickerParent::ColorPickerShownCallback::Done(const nsAString& aColor) {
+ if (mColorPickerParent) {
+ Unused << ColorPickerParent::Send__delete__(mColorPickerParent,
+ nsString(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));
+}
+
+mozilla::ipc::IPCResult ColorPickerParent::RecvOpen() {
+ if (!CreateColorPicker()) {
+ Unused << Send__delete__(this, mInitialColor);
+ return IPC_OK();
+ }
+
+ 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..560784af15
--- /dev/null
+++ b/dom/ipc/ColorPickerParent.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_ColorPickerParent_h
+#define mozilla_dom_ColorPickerParent_h
+
+#include "mozilla/dom/PColorPickerParent.h"
+#include "nsIColorPicker.h"
+
+namespace mozilla {
+namespace dom {
+
+class ColorPickerParent : public PColorPickerParent {
+ public:
+ ColorPickerParent(const nsString& aTitle, const nsString& aInitialColor)
+ : mTitle(aTitle), mInitialColor(aInitialColor) {}
+
+ 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;
+ ColorPickerParent* mColorPickerParent;
+ };
+
+ private:
+ virtual ~ColorPickerParent() = default;
+
+ bool CreateColorPicker();
+
+ RefPtr<ColorPickerShownCallback> mCallback;
+ nsCOMPtr<nsIColorPicker> mPicker;
+
+ nsString mTitle;
+ nsString mInitialColor;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_ColorPickerParent_h
diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp
new file mode 100644
index 0000000000..f68d9a147d
--- /dev/null
+++ b/dom/ipc/ContentChild.cpp
@@ -0,0 +1,4721 @@
+/* -*- 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 MOZ_WIDGET_GTK
+# include <gdk/gdkx.h>
+# include <gtk/gtk.h>
+#endif
+
+#include "BrowserChild.h"
+#include "ContentChild.h"
+#include "GeckoProfiler.h"
+#include "HandlerServiceChild.h"
+#include "nsXPLookAndFeel.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/BackgroundHangMonitor.h"
+#include "mozilla/BenchmarkStorageChild.h"
+#include "mozilla/ContentBlocking.h"
+#ifdef MOZ_GLEAN
+# include "mozilla/FOGIPC.h"
+#endif
+#include "GMPServiceChild.h"
+#include "Geolocation.h"
+#include "imgLoader.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/HangDetails.h"
+#include "mozilla/LoadInfo.h"
+#include "mozilla/LookAndFeel.h"
+#include "mozilla/MemoryTelemetry.h"
+#include "mozilla/NullPrincipal.h"
+#include "mozilla/PerfStats.h"
+#include "mozilla/PerformanceMetricsCollector.h"
+#include "mozilla/PerformanceUtils.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/SpinEventLoopUntil.h"
+#include "mozilla/StaticPrefs_dom.h"
+#include "mozilla/StaticPrefs_fission.h"
+#include "mozilla/StaticPrefs_media.h"
+#include "mozilla/TelemetryIPC.h"
+#include "mozilla/Unused.h"
+#include "mozilla/WebBrowserPersistDocumentChild.h"
+#include "mozilla/devtools/HeapSnapshotTempFileHelperChild.h"
+#include "mozilla/docshell/OfflineCacheUpdateChild.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/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/PLoginReputationChild.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/WindowGlobalChild.h"
+#include "mozilla/dom/WorkerDebugger.h"
+#include "mozilla/dom/WorkerDebuggerManager.h"
+#include "mozilla/dom/ipc/SharedMap.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/LocaleService.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/ipc/FileDescriptorSetChild.h"
+#include "mozilla/ipc/FileDescriptorUtils.h"
+#include "mozilla/ipc/GeckoChildProcessHost.h"
+#include "mozilla/ipc/LibrarySandboxPreload.h"
+#include "mozilla/ipc/PChildToParentStreamChild.h"
+#include "mozilla/ipc/PParentToChildStreamChild.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"
+#include "mozilla/loader/ScriptCacheActors.h"
+#include "mozilla/media/MediaChild.h"
+#include "mozilla/net/CaptivePortalService.h"
+#include "mozilla/net/CookieServiceChild.h"
+#include "mozilla/net/HttpChannelChild.h"
+#include "mozilla/net/NeckoChild.h"
+#include "mozilla/plugins/PluginInstanceParent.h"
+#include "mozilla/plugins/PluginModuleParent.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 "nsIConsoleService.h"
+#include "nsIInputStreamChannel.h"
+#include "nsIOpenWindowInfo.h"
+#include "nsIStringBundle.h"
+#include "nsIURIMutator.h"
+#include "nsQueryObject.h"
+#include "nsSandboxFlags.h"
+
+#if !defined(XP_WIN)
+# include "mozilla/Omnijar.h"
+#endif
+
+#ifdef MOZ_GECKO_PROFILER
+# include "ChildProfilerController.h"
+#endif
+
+#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 "mozilla/Sandbox.h"
+# elif defined(__OpenBSD__)
+# include <err.h>
+# include <sys/stat.h>
+# include <unistd.h>
+
+# include <fstream>
+
+# include "SpecialSystemDirectory.h"
+# include "nsILineInputStream.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 "mozilla/Unused.h"
+#include "nsAnonymousTemporaryFile.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 "nsIConsoleService.h"
+#include "nsIContentViewer.h"
+#include "nsICycleCollectorListener.h"
+#include "nsIDocShellTreeOwner.h"
+#include "nsIDragService.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIMemoryInfoDumper.h"
+#include "nsIMemoryReporter.h"
+#include "nsIObserverService.h"
+#include "nsIScriptError.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsJSEnvironment.h"
+#include "nsMemoryInfoDumper.h"
+#include "nsPluginHost.h"
+#include "nsServiceManagerUtils.h"
+#include "nsStyleSheetService.h"
+#include "nsThreadManager.h"
+#include "nsVariant.h"
+#include "nsXULAppAPI.h"
+#ifdef NS_PRINTING
+# include "nsPrintingProxy.h"
+#endif
+#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 "nsIScriptSecurityManager.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"
+#endif
+
+#ifdef XP_WIN
+# include <process.h>
+# define getpid _getpid
+# include "mozilla/WinDllServices.h"
+# include "mozilla/audio/AudioNotificationReceiver.h"
+# include "mozilla/widget/AudioSession.h"
+# include "mozilla/widget/WinContentSystemParameters.h"
+#endif
+
+#if defined(XP_MACOSX)
+# include "nsMacUtilsImpl.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"
+#include "mozilla/dom/PPresentationChild.h"
+#include "mozilla/dom/PresentationIPCService.h"
+#include "mozilla/ipc/IPCStreamAlloc.h"
+#include "mozilla/ipc/IPCStreamDestination.h"
+#include "mozilla/ipc/IPCStreamSource.h"
+
+#ifdef MOZ_WEBSPEECH
+# include "mozilla/dom/PSpeechSynthesisChild.h"
+#endif
+
+#include "ClearOnShutdown.h"
+#include "DomainPolicy.h"
+#include "GMPServiceChild.h"
+#include "GfxInfoBase.h"
+#include "MMPrinter.h"
+#include "ProcessUtils.h"
+#include "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 "nsAppRunner.h"
+#endif
+
+#ifdef MOZ_CODE_COVERAGE
+# include "mozilla/CodeCoverageHandler.h"
+#endif
+
+using namespace mozilla;
+using namespace mozilla::docshell;
+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 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::RootedValue 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::RootedValue 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.BuildClonedMessageDataForChild(mChild, 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))
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+ ,
+ mMainChromeTid(0),
+ mMsaaID(0)
+#endif
+ ,
+ mIsForBrowser(false),
+ mIsAlive(true),
+ mShuttingDown(false) {
+ // This process is a content process, so it's clearly running in
+ // multiprocess mode!
+ nsDebugImpl::SetMultiprocessMode("Child");
+
+ // 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::Shutdown);
+ }
+}
+
+#ifdef _MSC_VER
+# pragma warning(push)
+# pragma warning( \
+ disable : 4722) /* Silence "destructor never returns" warning \
+ */
+#endif
+
+ContentChild::~ContentChild() {
+#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,
+ LookAndFeelData&& aLookAndFeelData,
+ nsTArray<SystemFontListEntry>&& aFontList,
+ const Maybe<SharedMemoryHandle>& aSharedUASheetHandle,
+ const uintptr_t& aSharedUASheetAddress,
+ nsTArray<SharedMemoryHandle>&& aSharedFontListBlocks) {
+ if (!sShutdownCanary) {
+ return IPC_OK();
+ }
+
+ mLookAndFeelData = std::move(aLookAndFeelData);
+ mFontList = std::move(aFontList);
+ mSharedFontListBlocks = std::move(aSharedFontListBlocks);
+#ifdef XP_WIN
+ widget::WinContentSystemParameters::GetSingleton()->SetContentValues(
+ aXPCOMInit.systemParameters());
+#endif
+
+ gfx::gfxVars::SetValuesForInitialize(aXPCOMInit.gfxNonDefaultVarUpdates());
+ InitSharedUASheets(aSharedUASheetHandle, aSharedUASheetAddress);
+ InitXPCOM(std::move(aXPCOMInit), aInitialData);
+ InitGraphicsDeviceData(aXPCOMInit.contentDeviceData());
+
+ return IPC_OK();
+}
+
+class nsGtkNativeInitRunnable : public Runnable {
+ public:
+ nsGtkNativeInitRunnable() : Runnable("nsGtkNativeInitRunnable") {}
+
+ NS_IMETHOD Run() override {
+ LookAndFeel::NativeInit();
+ return NS_OK;
+ }
+};
+
+bool ContentChild::Init(MessageLoop* aIOLoop, base::ProcessId aParentPid,
+ const char* aParentBuildID,
+ UniquePtr<IPC::Channel> aChannel, 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 waylandDisabled = true;
+# ifdef MOZ_WAYLAND
+ waylandDisabled = IsWaylandDisabled();
+# endif
+ if (waylandDisabled) {
+ 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
+
+ NS_ASSERTION(!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))) {
+ return false;
+ }
+
+ if (!Open(std::move(aChannel), aParentPid, aIOLoop)) {
+ return false;
+ }
+ sSingleton = this;
+
+ // If communications with the parent have broken down, take the process
+ // down so it's not hanging around.
+ GetIPCChannel()->SetAbortOnError(true);
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+ GetIPCChannel()->SetChannelFlags(MessageChannel::REQUIRE_A11Y_REENTRY);
+#endif
+
+ // 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 (GDK_IS_X11_DISPLAY(gdk_display_get_default()) &&
+ !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;
+
+#ifdef NS_PRINTING
+ // Force the creation of the nsPrintingProxy so that it's IPC counterpart,
+ // PrintingParent, is always available for printing initiated from the parent.
+ RefPtr<nsPrintingProxy> printingProxy = nsPrintingProxy::GetInstance();
+#endif
+
+ 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(
+ TaskCategory::Other,
+ NS_NewRunnableFunction("RegisterPendingInputEventHangAnnotator", [] {
+ BackgroundHangMonitor::RegisterAnnotator(
+ PendingInputEventHangAnnotator::sSingleton);
+ }));
+#endif
+
+ return true;
+}
+
+void ContentChild::SetProcessName(const nsACString& aName,
+ const nsACString* aETLDplus1) {
+ char* name;
+ if ((name = PR_GetEnv("MOZ_DEBUG_APP_PROCESS")) && aName.EqualsASCII(name)) {
+#ifdef OS_POSIX
+ printf_stderr("\n\nCHILDCHILDCHILDCHILD\n [%s] debug me @%d\n\n", name,
+ getpid());
+ sleep(30);
+#elif defined(OS_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
+ }
+
+ mProcessName = aName;
+#ifdef MOZ_GECKO_PROFILER
+ if (aETLDplus1) {
+ profiler_set_process_name(mProcessName, aETLDplus1);
+ } else {
+ profiler_set_process_name(mProcessName);
+ }
+#endif
+ mozilla::ipc::SetThisProcessName(PromiseFlatCString(mProcessName).get());
+}
+
+static nsresult GetCreateWindowParams(nsIOpenWindowInfo* aOpenWindowInfo,
+ nsDocShellLoadState* aLoadState,
+ bool aForceNoReferrer, float* aFullZoom,
+ nsIReferrerInfo** aReferrerInfo,
+ nsIPrincipal** aTriggeringPrincipal,
+ nsIContentSecurityPolicy** aCsp) {
+ *aFullZoom = 1.0f;
+ 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);
+
+ *aFullZoom = parent->FullZoom();
+ return NS_OK;
+}
+
+nsresult ContentChild::ProvideWindowCommon(
+ BrowserChild* aTabOpener, nsIOpenWindowInfo* aOpenWindowInfo,
+ uint32_t aChromeFlags, bool aCalledFromJS, bool aWidthSpecified,
+ nsIURI* aURI, const nsAString& aName, const nsACString& aFeatures,
+ bool aForceNoOpener, bool aForceNoReferrer, nsDocShellLoadState* aLoadState,
+ bool* aWindowIsNew, BrowsingContext** aReturn) {
+ MOZ_DIAGNOSTIC_ASSERT(aTabOpener, "We must have a tab opener");
+
+ *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();
+ }
+
+ bool sandboxFlagsPropagate =
+ parentSandboxFlags & SANDBOX_PROPAGATES_TO_AUXILIARY_BROWSING_CONTEXTS;
+
+ // Check if we should load in a different process. Under Fission, we never
+ // want to do this, since the Fission process selection logic will handle
+ // everything for us. Outside of Fission, we always want to load in a
+ // different process if we have noopener set, but we also might if we can't
+ // load in the current process.
+ bool loadInDifferentProcess =
+ aForceNoOpener && StaticPrefs::dom_noopener_newprocess_enabled() &&
+ !useRemoteSubframes && !sandboxFlagsPropagate &&
+ !aOpenWindowInfo->GetIsForPrinting();
+ if (!loadInDifferentProcess && aURI) {
+ // Only special-case cross-process loads if Fission is disabled. With
+ // Fission enabled, the initial in-process load will automatically be
+ // retargeted to the correct process.
+ if (!(parent && parent->UseRemoteSubframes())) {
+ nsCOMPtr<nsIWebBrowserChrome3> browserChrome3;
+ rv = aTabOpener->GetWebBrowserChrome(getter_AddRefs(browserChrome3));
+ if (NS_SUCCEEDED(rv) && browserChrome3) {
+ bool shouldLoad;
+ rv = browserChrome3->ShouldLoadURIInThisProcess(aURI, &shouldLoad);
+ loadInDifferentProcess = NS_SUCCEEDED(rv) && !shouldLoad;
+ }
+ }
+ }
+
+ // 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!
+ if (loadInDifferentProcess && !sandboxFlagsPropagate) {
+ float fullZoom;
+ nsCOMPtr<nsIPrincipal> triggeringPrincipal;
+ nsCOMPtr<nsIContentSecurityPolicy> csp;
+ nsCOMPtr<nsIReferrerInfo> referrerInfo;
+ rv = GetCreateWindowParams(aOpenWindowInfo, aLoadState, aForceNoReferrer,
+ &fullZoom, 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, aWidthSpecified, aURI,
+ features, fullZoom, 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);
+ 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 = MakeRefPtr<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;
+ ipcContext.openerChild() = aTabOpener;
+ 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.
+ RefPtr<nsPIDOMWindowOuter> parentWindow =
+ parent ? parent->GetDOMWindow() : nullptr;
+ if (NS_FAILED(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());
+ bool hasSiblings = info.hasSiblings();
+
+ // 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,
+ aTabOpener->WebWidget()->GetDPI(),
+ aTabOpener->WebWidget()->RoundsWidgetCoordinatesTo(),
+ aTabOpener->WebWidget()->GetDefaultScale().scale);
+
+ newChild->SetMaxTouchPoints(maxTouchPoints);
+ newChild->SetHasSiblings(hasSiblings);
+
+ 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.
+ float fullZoom;
+ nsCOMPtr<nsIPrincipal> triggeringPrincipal;
+ nsCOMPtr<nsIContentSecurityPolicy> csp;
+ nsCOMPtr<nsIReferrerInfo> referrerInfo;
+ rv = GetCreateWindowParams(aOpenWindowInfo, aLoadState, aForceNoReferrer,
+ &fullZoom, getter_AddRefs(referrerInfo),
+ getter_AddRefs(triggeringPrincipal),
+ getter_AddRefs(csp));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ SendCreateWindow(aTabOpener, parent, newChild, aChromeFlags, aCalledFromJS,
+ aWidthSpecified, aOpenWindowInfo->GetIsForPrinting(),
+ aOpenWindowInfo->GetIsForWindowDotPrint(), aURI, features,
+ fullZoom, Principal(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([&]() { 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(const 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(*aHandle, aAddress);
+}
+
+void ContentChild::InitXPCOM(
+ XPCOMInitData&& aXPCOMInit,
+ const mozilla::dom::ipc::StructuredCloneData& aInitialData) {
+ // 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.
+ BackgroundChild::Startup();
+
+#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();
+#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;
+ }
+
+ LSObject::Initialize();
+
+ 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());
+ // XXX(Bug 1633675) The LocaleService calls could also move the arguments.
+ LocaleService::GetInstance()->AssignAppLocales(aXPCOMInit.appLocales());
+ LocaleService::GetInstance()->AssignRequestedLocales(
+ aXPCOMInit.requestedLocales());
+
+ 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::RootedValue 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()));
+
+ DataStorage::SetCachedStorageEntries(aXPCOMInit.dataStorage());
+
+ // Initialize the RemoteDecoderManager thread and its associated PBackground
+ // channel.
+ RemoteDecoderManagerChild::Init();
+
+ // 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();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvGetUntrustedModulesData(
+ GetUntrustedModulesDataResolver&& aResolver) {
+#if defined(XP_WIN)
+ 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();
+#else
+ return IPC_FAIL(this, "Unsupported on this platform");
+#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) {
+#ifdef MOZ_GECKO_PROFILER
+ mProfilerController = ChildProfilerController::Create(std::move(aEndpoint));
+#endif
+ 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();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvRequestPerformanceMetrics(
+ const nsID& aID) {
+ RefPtr<ContentChild> self = this;
+ RefPtr<AbstractThread> mainThread = AbstractThread::MainThread();
+ nsTArray<RefPtr<PerformanceInfoPromise>> promises = CollectPerformanceInfo();
+
+ PerformanceInfoPromise::All(mainThread, promises)
+ ->Then(
+ mainThread, __func__,
+ [self, aID](const nsTArray<mozilla::dom::PerformanceInfo>& aResult) {
+ self->SendAddPerformanceMetrics(aID, aResult);
+ },
+ []() { /* silently fails -- the parent times out
+ and proceeds when the data is not coming back */
+ });
+
+ 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();
+
+ // Zap all the old layer managers we have lying around.
+ for (const auto& browserChild : tabs) {
+ if (browserChild->GetLayersId().IsValid()) {
+ browserChild->InvalidateLayers();
+ }
+ }
+
+ // 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();
+ }
+ }
+
+ RemoteDecoderManagerChild::InitForGPUProcess(std::move(aVideoManager));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvAudioDefaultDeviceChange() {
+#ifdef XP_WIN
+ audio::AudioNotificationReceiver::NotifyDefaultDeviceChanged();
+#endif
+ 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 acheived when we additionally deny
+ // future connections, however this currently breaks WebGL so it's not done
+ // by default.
+ if (aIsSandboxEnabled &&
+ Preferences::GetBool(
+ "security.sandbox.content.mac.disconnect-windowserver")) {
+ 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)
+
+# ifdef MOZ_USING_WASM_SANDBOXING
+ mozilla::ipc::PreloadSandboxedDynamicLibraries();
+# endif
+
+ 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::GetCubebContext();
+ }
+
+ 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(OS_ANDROID)
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::ContentSandboxCapabilities,
+ static_cast<int>(SandboxInfo::Get().AsInteger()));
+# endif /* XP_LINUX && !OS_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_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()) {
+ return IPC_FAIL(this, "Null or discarded initial BrowsingContext");
+ }
+
+ // 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();
+}
+
+PFileDescriptorSetChild* ContentChild::SendPFileDescriptorSetConstructor(
+ const FileDescriptor& aFD) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (IsShuttingDown()) {
+ return nullptr;
+ }
+
+ return PContentChild::SendPFileDescriptorSetConstructor(aFD);
+}
+
+PFileDescriptorSetChild* ContentChild::AllocPFileDescriptorSetChild(
+ const FileDescriptor& aFD) {
+ return new FileDescriptorSetChild(aFD);
+}
+
+bool ContentChild::DeallocPFileDescriptorSetChild(
+ PFileDescriptorSetChild* aActor) {
+ delete static_cast<FileDescriptorSetChild*>(aActor);
+ return true;
+}
+
+already_AddRefed<PRemoteLazyInputStreamChild>
+ContentChild::AllocPRemoteLazyInputStreamChild(const nsID& aID,
+ const uint64_t& aSize) {
+ RefPtr<RemoteLazyInputStreamChild> actor =
+ new RemoteLazyInputStreamChild(aID, aSize);
+ return actor.forget();
+}
+
+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;
+}
+
+PPresentationChild* ContentChild::AllocPPresentationChild() {
+ MOZ_CRASH("We should never be manually allocating PPresentationChild actors");
+ return nullptr;
+}
+
+bool ContentChild::DeallocPPresentationChild(PPresentationChild* aActor) {
+ delete aActor;
+ return true;
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvNotifyPresentationReceiverLaunched(
+ PBrowserChild* aIframe, const nsString& aSessionId) {
+ nsCOMPtr<nsIDocShell> docShell =
+ do_GetInterface(static_cast<BrowserChild*>(aIframe)->WebNavigation());
+ NS_WARNING_ASSERTION(docShell, "WebNavigation failed");
+
+ nsCOMPtr<nsIPresentationService> service =
+ do_GetService(PRESENTATION_SERVICE_CONTRACTID);
+ NS_WARNING_ASSERTION(service, "presentation service is missing");
+
+ Unused << NS_WARN_IF(
+ NS_FAILED(static_cast<PresentationIPCService*>(service.get())
+ ->MonitorResponderLoading(aSessionId, docShell)));
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvNotifyPresentationReceiverCleanUp(
+ const nsString& aSessionId) {
+ nsCOMPtr<nsIPresentationService> service =
+ do_GetService(PRESENTATION_SERVICE_CONTRACTID);
+ NS_WARNING_ASSERTION(service, "presentation service is missing");
+
+ Unused << NS_WARN_IF(NS_FAILED(service->UntrackSessionInfo(
+ aSessionId, nsIPresentationService::ROLE_RECEIVER)));
+
+ return IPC_OK();
+}
+
+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;
+}
+
+PTestShellChild* ContentChild::AllocPTestShellChild() {
+ return new TestShellChild();
+}
+
+bool ContentChild::DeallocPTestShellChild(PTestShellChild* shell) {
+ delete shell;
+ return true;
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvPTestShellConstructor(
+ PTestShellChild* actor) {
+ return IPC_OK();
+}
+
+void ContentChild::UpdateCookieStatus(nsIChannel* aChannel) {
+ RefPtr<CookieServiceChild> csChild = CookieServiceChild::GetSingleton();
+ NS_ASSERTION(csChild, "Couldn't get CookieServiceChild");
+
+ 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);
+ return IPC_OK();
+}
+
+PNeckoChild* ContentChild::AllocPNeckoChild() { return new NeckoChild(); }
+
+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();
+}
+
+bool ContentChild::DeallocPNeckoChild(PNeckoChild* necko) {
+ delete necko;
+ return true;
+}
+
+PPrintingChild* ContentChild::AllocPPrintingChild() {
+ // The ContentParent should never attempt to allocate the nsPrintingProxy,
+ // which implements PPrintingChild. Instead, the nsPrintingProxy service is
+ // requested and instantiated via XPCOM, and the constructor of
+ // nsPrintingProxy sets up the IPC connection.
+ MOZ_CRASH("Should never get here!");
+ return nullptr;
+}
+
+bool ContentChild::DeallocPPrintingChild(PPrintingChild* printing) {
+ return true;
+}
+
+PChildToParentStreamChild* ContentChild::SendPChildToParentStreamConstructor(
+ PChildToParentStreamChild* aActor) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (IsShuttingDown()) {
+ return nullptr;
+ }
+
+ return PContentChild::SendPChildToParentStreamConstructor(aActor);
+}
+
+PChildToParentStreamChild* ContentChild::AllocPChildToParentStreamChild() {
+ MOZ_CRASH("PChildToParentStreamChild actors should be manually constructed!");
+}
+
+bool ContentChild::DeallocPChildToParentStreamChild(
+ PChildToParentStreamChild* aActor) {
+ delete aActor;
+ return true;
+}
+
+PParentToChildStreamChild* ContentChild::AllocPParentToChildStreamChild() {
+ return mozilla::ipc::AllocPParentToChildStreamChild();
+}
+
+bool ContentChild::DeallocPParentToChildStreamChild(
+ PParentToChildStreamChild* aActor) {
+ delete aActor;
+ return true;
+}
+
+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_WEBSPEECH
+PSpeechSynthesisChild* ContentChild::AllocPSpeechSynthesisChild() {
+ MOZ_CRASH("No one should be allocating PSpeechSynthesisChild actors");
+}
+
+bool ContentChild::DeallocPSpeechSynthesisChild(PSpeechSynthesisChild* aActor) {
+ delete aActor;
+ return true;
+}
+#endif
+
+PWebrtcGlobalChild* ContentChild::AllocPWebrtcGlobalChild() {
+#ifdef MOZ_WEBRTC
+ auto* child = new WebrtcGlobalChild();
+ return child;
+#else
+ return nullptr;
+#endif
+}
+
+bool ContentChild::DeallocPWebrtcGlobalChild(PWebrtcGlobalChild* aActor) {
+#ifdef MOZ_WEBRTC
+ delete static_cast<WebrtcGlobalChild*>(aActor);
+ return true;
+#else
+ return false;
+#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) {
+ nsIPrincipal* prin = aForPrincipal ? aForPrincipal.value().get() : nullptr;
+ SharedStyleSheetCache::Clear(prin);
+ 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();
+}
+
+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();
+
+ 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::RecvDataStoragePut(
+ const nsString& aFilename, const DataStorageItem& aItem) {
+ RefPtr<DataStorage> storage = DataStorage::GetFromRawFileName(aFilename);
+ if (storage) {
+ storage->Put(aItem.key(), aItem.value(), aItem.type());
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvDataStorageRemove(
+ const nsString& aFilename, const nsCString& aKey,
+ const DataStorageType& aType) {
+ RefPtr<DataStorage> storage = DataStorage::GetFromRawFileName(aFilename);
+ if (storage) {
+ storage->Remove(aKey, aType);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvDataStorageClear(
+ const nsString& aFilename) {
+ RefPtr<DataStorage> storage = DataStorage::GetFromRawFileName(aFilename);
+ if (storage) {
+ storage->Clear();
+ }
+ 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();
+}
+
+// NOTE: This method is being run in the SystemGroup, and thus cannot directly
+// touch pages. See GetSpecificMessageEventTarget.
+mozilla::ipc::IPCResult ContentChild::RecvNotifyVisited(
+ nsTArray<VisitedQueryResult>&& aURIs) {
+ nsCOMPtr<IHistory> history = services::GetHistory();
+ 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(
+ LookAndFeelData&& aLookAndFeelData, widget::ThemeChangeKind aKind) {
+ switch (aLookAndFeelData.type()) {
+ case LookAndFeelData::TLookAndFeelCache:
+ LookAndFeel::SetCache(aLookAndFeelData.get_LookAndFeelCache());
+ break;
+ case LookAndFeelData::TFullLookAndFeel:
+ LookAndFeel::SetData(std::move(aLookAndFeelData.get_FullLookAndFeel()));
+ break;
+ default:
+ MOZ_ASSERT(false, "unreachable");
+ }
+ LookAndFeel::NotifyChangedAllWindows(aKind);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvUpdateSystemParameters(
+ nsTArray<SystemParameterKVPair>&& aUpdates) {
+#ifdef XP_WIN
+ widget::WinContentSystemParameters::GetSingleton()->SetContentValues(
+ aUpdates);
+#endif
+
+ 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::UnpackClonedMessageDataForChild(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 =
+ services::GetStringBundleService();
+
+ for (auto& descriptor : aDescriptors) {
+ stringBundleService->RegisterContentBundle(
+ descriptor.bundleURL(), descriptor.mapFile(), descriptor.mapSize());
+ }
+
+ 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::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(
+ nsTArray<SystemFontListEntry>&& aFontList) {
+ mFontList = std::move(aFontList);
+ gfxPlatform::GetPlatform()->UpdateFontList(true);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvRebuildFontList(
+ const bool& aFullRebuild) {
+ gfxPlatform::GetPlatform()->UpdateFontList(aFullRebuild);
+ 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::RecvAddPermission(
+ const IPC::Permission& permission) {
+ nsCOMPtr<nsIPermissionManager> permissionManagerIface =
+ services::GetPermissionManager();
+ 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 =
+ services::GetPermissionManager();
+ 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(
+ const uint32_t& aMainChromeTid, const uint32_t& aMsaaID) {
+#ifdef ACCESSIBILITY
+# ifdef XP_WIN
+ MOZ_ASSERT(aMainChromeTid != 0);
+ mMainChromeTid = aMainChromeTid;
+
+ MOZ_ASSERT(aMsaaID != 0);
+ mMsaaID = aMsaaID;
+# endif // XP_WIN
+
+ // 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();
+ 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) {
+ if (!mRemoteType.IsVoid()) {
+ // Preallocated processes are type PREALLOC_REMOTE_TYPE; they can become
+ // anything except a File: 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
+ MOZ_RELEASE_ASSERT(aRemoteType != FILE_REMOTE_TYPE &&
+ (mRemoteType == PREALLOC_REMOTE_TYPE ||
+ (mRemoteType == DEFAULT_REMOTE_TYPE &&
+ aRemoteType == DEFAULT_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()));
+ }
+
+ auto remoteTypePrefix = RemoteTypePrefix(aRemoteType);
+
+ // Update the process name so about:memory's process names are more obvious.
+ if (aRemoteType == FILE_REMOTE_TYPE) {
+ SetProcessName("file:// Content"_ns);
+ } else if (aRemoteType == EXTENSION_REMOTE_TYPE) {
+ SetProcessName("WebExtensions"_ns);
+ } else if (aRemoteType == PRIVILEGEDABOUT_REMOTE_TYPE) {
+ SetProcessName("Privileged Content"_ns);
+ } else if (aRemoteType == LARGE_ALLOCATION_REMOTE_TYPE) {
+ SetProcessName("Large Allocation Web Content"_ns);
+ } else if (RemoteTypePrefix(aRemoteType) == FISSION_WEB_REMOTE_TYPE) {
+ SetProcessName("Isolated Web Content"_ns);
+#ifdef NIGHTLY_BUILD
+ // for Nightly only, and requires pref flip
+ if (StaticPrefs::fission_processOriginNames()) {
+ // Sets profiler process name
+ SetProcessName(
+ Substring(aRemoteType, FISSION_WEB_REMOTE_TYPE.Length() + 1));
+
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("Changed name of process %d from %s to %s", getpid(),
+ PromiseFlatCString(mRemoteType).get(),
+ PromiseFlatCString(
+ Substring(aRemoteType, FISSION_WEB_REMOTE_TYPE.Length() + 1))
+ .get()));
+ } else
+#endif
+ {
+ // The profiler can sanitize out the eTLD+1
+ nsCString etld(
+ Substring(aRemoteType, FISSION_WEB_REMOTE_TYPE.Length() + 1));
+ SetProcessName("Isolated Web Content"_ns, &etld);
+ }
+ }
+ // else "prealloc", "web" or "webCOOP+COEP" type -> "Web Content" already set
+
+ mRemoteType.Assign(aRemoteType);
+
+ // 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();
+}
+
+// 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::RecvInitServiceWorkers(
+ const ServiceWorkerConfiguration& aConfig) {
+ RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+ if (!swm) {
+ // browser shutdown began
+ return IPC_OK();
+ }
+ swm->LoadRegistrations(aConfig.serviceWorkerRegistrations());
+ return IPC_OK();
+}
+
+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.agentClusterId(), 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));
+
+ os->NotifyObservers(static_cast<nsIPropertyBag2*>(props),
+ "ipc:process-priority-changed", nullptr);
+ 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.PutEntry(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.RemoveEntry(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::RecvShutdown() {
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ if (os) {
+ os->NotifyObservers(ToSupports(this), "content-child-will-shutdown",
+ nullptr);
+ }
+
+ ShutdownInternal();
+ return IPC_OK();
+}
+
+void ContentChild::ShutdownInternal() {
+ // 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).
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::IPCShutdownState, "RecvShutdown"_ns);
+
+ 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) {
+ os->NotifyObservers(ToSupports(this), "content-child-shutdown", nullptr);
+ }
+
+#if defined(XP_WIN)
+ mozilla::widget::StopAudioSession();
+#endif
+
+ GetIPCChannel()->SetAbortOnError(false);
+
+#ifdef MOZ_GECKO_PROFILER
+ if (mProfilerController) {
+ nsCString shutdownProfile =
+ mProfilerController->GrabShutdownProfileAndShutdown();
+ mProfilerController = nullptr;
+ // Send the shutdown profile to the parent process through our own
+ // message channel, which we know will survive for long enough.
+ Unused << SendShutdownProfile(shutdownProfile);
+ }
+#endif
+
+ // Start a timer that will insure we quickly exit after a reasonable
+ // period of time. Prevents shutdown hangs after our connection to the
+ // parent closes.
+ StartForceKillTimer();
+
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::IPCShutdownState,
+ "SendFinishShutdown (sending)"_ns);
+ bool sent = SendFinishShutdown();
+ CrashReporter::AnnotateCrashReport(
+ CrashReporter::Annotation::IPCShutdownState,
+ sent ? "SendFinishShutdown (sent)"_ns : "SendFinishShutdown (failed)"_ns);
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvUpdateWindow(
+ const uintptr_t& aChildId) {
+#if defined(XP_WIN)
+ NS_ASSERTION(aChildId,
+ "Expected child hwnd value for remote plugin instance.");
+ mozilla::plugins::PluginInstanceParent* parentInstance =
+ mozilla::plugins::PluginInstanceParent::LookupPluginInstanceByID(
+ aChildId);
+ if (parentInstance) {
+ // sync! update call to the plugin instance that forces the
+ // plugin to paint its child window.
+ if (!parentInstance->CallUpdateWindow()) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ }
+ return IPC_OK();
+#else
+ MOZ_ASSERT(
+ false,
+ "ContentChild::RecvUpdateWindow calls unexpected on this platform.");
+ return IPC_FAIL_NO_REASON(this);
+#endif
+}
+
+PContentPermissionRequestChild*
+ContentChild::AllocPContentPermissionRequestChild(
+ const nsTArray<PermissionRequest>& aRequests,
+ const IPC::Principal& aPrincipal, const IPC::Principal& 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;
+}
+
+PWebBrowserPersistDocumentChild*
+ContentChild::AllocPWebBrowserPersistDocumentChild(
+ PBrowserChild* aBrowser, const MaybeDiscarded<BrowsingContext>& aContext) {
+ return new 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();
+}
+
+bool ContentChild::DeallocPWebBrowserPersistDocumentChild(
+ PWebBrowserPersistDocumentChild* aActor) {
+ delete aActor;
+ return true;
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvInvokeDragSession(
+ nsTArray<IPCDataTransfer>&& aTransfers, const uint32_t& aAction) {
+ nsCOMPtr<nsIDragService> dragService =
+ do_GetService("@mozilla.org/widget/dragservice;1");
+ if (dragService) {
+ dragService->StartDragSession();
+ nsCOMPtr<nsIDragSession> session;
+ dragService->GetCurrentSession(getter_AddRefs(session));
+ if (session) {
+ session->SetDragAction(aAction);
+ // 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 < aTransfers.Length() && !hasFiles; ++i) {
+ auto& items = aTransfers[i].items();
+ for (uint32_t j = 0; j < items.Length() && !hasFiles; ++j) {
+ if (items[j].data().type() == IPCDataTransferData::TIPCBlob) {
+ hasFiles = true;
+ }
+ }
+ }
+
+ // Add the entries from the IPC to the new DataTransfer
+ nsCOMPtr<DataTransfer> dataTransfer =
+ new DataTransfer(nullptr, eDragStart, false, -1);
+ for (uint32_t i = 0; i < aTransfers.Length(); ++i) {
+ auto& items = aTransfers[i].items();
+ for (uint32_t j = 0; j < items.Length(); ++j) {
+ const IPCDataTransferItem& item = items[j];
+ RefPtr<nsVariantCC> variant = new nsVariantCC();
+ if (item.data().type() == IPCDataTransferData::TnsString) {
+ const nsString& data = item.data().get_nsString();
+ variant->SetAsAString(data);
+ } else if (item.data().type() == IPCDataTransferData::TShmem) {
+ Shmem data = item.data().get_Shmem();
+ variant->SetAsACString(
+ nsDependentCSubstring(data.get<char>(), data.Size<char>()));
+ Unused << DeallocShmem(data);
+ } else if (item.data().type() == IPCDataTransferData::TIPCBlob) {
+ RefPtr<BlobImpl> blobImpl =
+ IPCBlobUtils::Deserialize(item.data().get_IPCBlob());
+ variant->SetAsISupports(blobImpl);
+ } else {
+ 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() != IPCDataTransferData::TIPCBlob;
+ dataTransfer->SetDataWithPrincipalFromOtherProcess(
+ NS_ConvertUTF8toUTF16(item.flavor()), variant, i,
+ nsContentUtils::GetSystemPrincipal(), hidden);
+ }
+ }
+ session->SetDataTransfer(dataTransfer);
+ }
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvEndDragSession(
+ const bool& aDoneDrag, const bool& aUserCancelled,
+ const LayoutDeviceIntPoint& aDragEndPoint, const uint32_t& aKeyModifiers) {
+ nsCOMPtr<nsIDragService> dragService =
+ do_GetService("@mozilla.org/widget/dragservice;1");
+ if (dragService) {
+ if (aUserCancelled) {
+ nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
+ if (dragSession) {
+ dragSession->UserCancelled();
+ }
+ }
+ static_cast<nsBaseDragService*>(dragService.get())
+ ->SetDragEndPoint(aDragEndPoint);
+ dragService->EndDragSession(aDoneDrag, aKeyModifiers);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvPush(const nsCString& aScope,
+ const IPC::Principal& 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, const IPC::Principal& 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, const IPC::Principal& aPrincipal) {
+ PushSubscriptionChangeDispatcher dispatcher(aScope, aPrincipal);
+ Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers()));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvPushError(
+ const nsCString& aScope, const IPC::Principal& 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, const IPC::Principal& 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,
+ const IPC::Principal& aPrincipal, const Maybe<nsID>& aAgentClusterId) {
+ RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(aBlob);
+ MOZ_ASSERT(blobImpl);
+
+ BlobURLProtocolHandler::AddDataEntry(aURI, aPrincipal, aAgentClusterId,
+ blobImpl);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvBlobURLUnregistration(
+ const nsCString& aURI) {
+ BlobURLProtocolHandler::RemoveDataEntry(
+ aURI,
+ /* aBroadcastToOtherProcesses = */ false);
+ return IPC_OK();
+}
+
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+bool ContentChild::SendGetA11yContentId() {
+ return PContentChild::SendGetA11yContentId(&mMsaaID);
+}
+#endif // defined(XP_WIN) && defined(ACCESSIBILITY)
+
+void ContentChild::CreateGetFilesRequest(const nsAString& aDirectoryPath,
+ bool aRecursiveFlag, nsID& aUUID,
+ GetFilesHelperChild* aChild) {
+ MOZ_ASSERT(aChild);
+ MOZ_ASSERT(!mGetFilesPendingRequests.GetWeak(aUUID));
+
+ Unused << SendGetFilesRequest(aUUID, nsString(aDirectoryPath),
+ aRecursiveFlag);
+ mGetFilesPendingRequests.Put(aUUID, RefPtr{aChild});
+}
+
+void ContentChild::DeleteGetFilesRequest(nsID& aUUID,
+ GetFilesHelperChild* aChild) {
+ MOZ_ASSERT(aChild);
+ MOZ_ASSERT(mGetFilesPendingRequests.GetWeak(aUUID));
+
+ Unused << SendDeleteGetFilesRequest(aUUID);
+ mGetFilesPendingRequests.Remove(aUUID);
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvGetFilesResponse(
+ const nsID& aUUID, const GetFilesResponseResult& aResult) {
+ GetFilesHelperChild* child = mGetFilesPendingRequests.GetWeak(aUUID);
+ // This object can already been deleted in case DeleteGetFilesRequest has
+ // been called when the response was sending by the parent.
+ if (!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);
+ }
+
+ mGetFilesPendingRequests.Remove(aUUID);
+ 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(
+ const Principal& 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, const nsTArray<IPCURLClassifierFeature>& aFeatures) {
+ return new URLClassifierLocalChild();
+}
+
+bool ContentChild::DeallocPURLClassifierLocalChild(
+ PURLClassifierLocalChild* aActor) {
+ MOZ_ASSERT(aActor);
+ delete aActor;
+ return true;
+}
+
+PLoginReputationChild* ContentChild::AllocPLoginReputationChild(nsIURI* aUri) {
+ return new PLoginReputationChild();
+}
+
+bool ContentChild::DeallocPLoginReputationChild(PLoginReputationChild* 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.LookupOrAdd(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::RecvSetPluginList(
+ const uint32_t& aPluginEpoch, nsTArray<plugins::PluginTag>&& aPluginTags,
+ nsTArray<plugins::FakePluginTag>&& aFakePluginTags) {
+ RefPtr<nsPluginHost> host = nsPluginHost::GetInst();
+ host->SetPluginsInContent(aPluginEpoch, aPluginTags, aFakePluginTags);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvShareCodeCoverageMutex(
+ const CrossProcessMutexHandle& aHandle) {
+#ifdef MOZ_CODE_COVERAGE
+ CodeCoverageHandler::Init(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::RecvGetMemoryUniqueSetSize(
+ GetMemoryUniqueSetSizeResolver&& aResolver) {
+ MemoryTelemetry::Get().GetUniqueSetSize(std::move(aResolver));
+ return IPC_OK();
+}
+
+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(),
+ getter_AddRefs(loadInfo));
+ if (NS_FAILED(rv)) {
+ MOZ_DIAGNOSTIC_ASSERT(false, "LoadInfoArgsToLoadInfo failed");
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsIChannel> newChannel;
+ MOZ_ASSERT((aArgs.loadStateLoadFlags() &
+ 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());
+
+ // 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 (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.loadStateLoadFlags());
+ 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().refOr(nullptr), 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");
+
+ ContentBlocking::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();
+}
+
+void ContentChild::OnChannelReceivedMessage(const Message& aMsg) {
+ if (aMsg.is_sync() && !aMsg.is_reply()) {
+ LSObject::OnSyncMessageReceived();
+ }
+
+#ifdef NIGHTLY_BUILD
+ if (nsContentUtils::IsMessageInputEvent(aMsg)) {
+ mPendingInputEvents++;
+ }
+#endif
+}
+
+#ifdef NIGHTLY_BUILD
+PContentChild::Result ContentChild::OnMessageReceived(const Message& aMsg) {
+ if (nsContentUtils::IsMessageInputEvent(aMsg)) {
+ DebugOnly<uint32_t> prevEvts = mPendingInputEvents--;
+ MOZ_ASSERT(prevEvts > 0);
+ }
+
+ return PContentChild::OnMessageReceived(aMsg);
+}
+#endif
+
+PContentChild::Result ContentChild::OnMessageReceived(const Message& aMsg,
+ Message*& aReply) {
+ Result result = PContentChild::OnMessageReceived(aMsg, aReply);
+
+ if (aMsg.is_sync()) {
+ // OnMessageReceived shouldn't be called for sync replies.
+ MOZ_ASSERT(!aMsg.is_reply());
+
+ LSObject::OnSyncMessageHandled();
+ }
+
+ return result;
+}
+
+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);
+ BrowsingContext::CreateFromIPC(std::move(aInit), group, nullptr);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvDiscardBrowsingContext(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ DiscardBrowsingContextResolver&& aResolve) {
+ if (!aContext.IsNullOrDiscarded()) {
+ aContext.get()->Detach(/* aFromIPC */ true);
+ }
+
+ // 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::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();
+ }
+
+ 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();
+ }
+ nsGlobalWindowOuter::Cast(window)->FocusOuter(aCallerType, aActionId);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvWindowBlur(
+ 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();
+ }
+ nsGlobalWindowOuter::Cast(window)->BlurOuter();
+ 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();
+ }
+
+ nsFocusManager* fm = nsFocusManager::GetFocusManager();
+ if (fm) {
+ fm->RaiseWindow(window, aCallerType, aActionId);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvAdjustWindowFocus(
+ const MaybeDiscarded<BrowsingContext>& aContext, bool aCheckPermission,
+ bool aIsVisible) {
+ 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->AdjustWindowFocus(aContext.get(), aCheckPermission, aIsVisible);
+ }
+ 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();
+ }
+
+ nsFocusManager* fm = nsFocusManager::GetFocusManager();
+ if (fm) {
+ fm->ClearFocus(window);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvSetFocusedBrowsingContext(
+ 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();
+ }
+
+ nsFocusManager* fm = nsFocusManager::GetFocusManager();
+ if (fm) {
+ fm->SetFocusedBrowsingContextFromOtherProcess(aContext.get());
+ }
+ 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());
+ }
+ 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());
+ }
+ 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();
+ }
+
+ BrowsingContext* toClear = aBrowsingContextToClear.IsDiscarded()
+ ? nullptr
+ : aBrowsingContextToClear.get();
+ BrowsingContext* toFocus = aAncestorBrowsingContextToFocus.IsDiscarded()
+ ? nullptr
+ : aAncestorBrowsingContextToFocus.get();
+
+ nsFocusManager* fm = nsFocusManager::GetFocusManager();
+ if (fm) {
+ fm->BlurFromOtherProcess(aFocusedBrowsingContext.get(), toClear, toFocus,
+ aIsLeavingDocument, aAdjustWidget, aActionId);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvSetupFocusedAndActive(
+ const MaybeDiscarded<BrowsingContext>& aFocusedBrowsingContext,
+ const MaybeDiscarded<BrowsingContext>& aActiveBrowsingContext) {
+ nsFocusManager* fm = nsFocusManager::GetFocusManager();
+ if (fm) {
+ if (!aActiveBrowsingContext.IsNullOrDiscarded()) {
+ fm->SetActiveBrowsingContextFromOtherProcess(
+ aActiveBrowsingContext.get());
+ }
+ if (!aFocusedBrowsingContext.IsNullOrDiscarded()) {
+ fm->SetFocusedBrowsingContextFromOtherProcess(
+ aFocusedBrowsingContext.get());
+ }
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvReviseActiveBrowsingContext(
+ const MaybeDiscarded<BrowsingContext>& aActiveBrowsingContext,
+ uint64_t aActionId) {
+ nsFocusManager* fm = nsFocusManager::GetFocusManager();
+ if (fm && !aActiveBrowsingContext.IsNullOrDiscarded()) {
+ fm->ReviseActiveBrowsingContext(aActiveBrowsingContext.get(), aActionId);
+ }
+ 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();
+ }
+
+ // 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(
+ uint64_t innerWindowId, const nsString& entryName,
+ const nsString& initiatorType, UniquePtr<PerformanceTimingData>&& aData) {
+ auto* innerWindow = nsGlobalWindowInner::GetInnerWindowWithId(innerWindowId);
+ if (!innerWindow) {
+ return IPC_OK();
+ }
+
+ mozilla::dom::Performance* performance = innerWindow->GetPerformance();
+ if (!performance) {
+ return IPC_OK();
+ }
+
+ performance->AsPerformanceStorage()->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();
+ }
+ 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();
+ }
+ 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();
+ }
+ 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::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(nsIContentViewer::eAllowNavigation);
+ } else {
+ DispatchBeforeUnloadToSubtree(aStartingAt.get(), std::move(aResolver));
+ }
+ return IPC_OK();
+}
+
+/* static */ void ContentChild::DispatchBeforeUnloadToSubtree(
+ BrowsingContext* aStartingAt,
+ const DispatchBeforeUnloadToSubtreeResolver& aResolver) {
+ bool resolved = false;
+
+ aStartingAt->PreOrderWalk([&](dom::BrowsingContext* aBC) {
+ if (aBC->GetDocShell()) {
+ nsCOMPtr<nsIContentViewer> contentViewer;
+ aBC->GetDocShell()->GetContentViewer(getter_AddRefs(contentViewer));
+ if (contentViewer &&
+ contentViewer->DispatchBeforeUnload() ==
+ nsIContentViewer::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(nsIContentViewer::eRequestBlockNavigation);
+ resolved = true;
+ }
+ }
+ });
+
+ if (!resolved) {
+ aResolver(nsIContentViewer::eAllowNavigation);
+ }
+}
+
+mozilla::ipc::IPCResult ContentChild::RecvGoBack(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const Maybe<int32_t>& aCancelContentJSEpoch, bool aRequireUserInteraction) {
+ if (aContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+ BrowsingContext* bc = aContext.get();
+
+ if (auto* docShell = nsDocShell::Cast(bc->GetDocShell())) {
+ if (aCancelContentJSEpoch) {
+ docShell->SetCancelContentJSEpoch(*aCancelContentJSEpoch);
+ }
+ docShell->GoBack(aRequireUserInteraction);
+
+ 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) {
+ if (aContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+ BrowsingContext* bc = aContext.get();
+
+ if (auto* docShell = nsDocShell::Cast(bc->GetDocShell())) {
+ if (aCancelContentJSEpoch) {
+ docShell->SetCancelContentJSEpoch(*aCancelContentJSEpoch);
+ }
+ docShell->GoForward(aRequireUserInteraction);
+
+ 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) {
+ if (aContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+ BrowsingContext* bc = aContext.get();
+
+ if (auto* docShell = nsDocShell::Cast(bc->GetDocShell())) {
+ if (aCancelContentJSEpoch) {
+ docShell->SetCancelContentJSEpoch(*aCancelContentJSEpoch);
+ }
+ docShell->GotoIndex(aIndex);
+
+ 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 (auto* 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;
+}
+
+already_AddRefed<JSActor> ContentChild::InitJSActor(
+ JS::HandleObject 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->BorrowFromClonedMessageDataForChild(*aData);
+ }
+ Maybe<StructuredCloneData> stack;
+ if (aStack) {
+ stack.emplace();
+ stack->BorrowFromClonedMessageDataForChild(*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) {
+#ifdef MOZ_GLEAN
+ glean::FlushFOGData(std::move(aResolver));
+#endif
+ return IPC_OK();
+}
+
+IPCResult ContentChild::RecvUpdateMediaCodecsSupported(
+ RemoteDecodeIn aLocation,
+ const PDMFactory::MediaCodecsSupported& aSupported) {
+ RemoteDecoderManagerChild::SetSupported(aLocation, aSupported);
+
+ return IPC_OK();
+}
+
+} // 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_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 {
+ if (unveil(PromiseFlatCString(pledgePath).get(), "r") == -1) {
+ err(1, "unveil(%s, r) failed", PromiseFlatCString(pledgePath).get());
+ }
+ }
+
+ return NS_OK;
+}
+
+bool StartOpenBSDSandbox(GeckoProcessType type) {
+ nsAutoCString pledgeFile;
+ nsAutoCString unveilFile;
+
+ 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_CACHE_HOME/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;
+
+ default:
+ MOZ_ASSERT(false, "unknown process type");
+ return false;
+ }
+
+ 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
+
+#if !defined(XP_WIN)
+bool IsDevelopmentBuild() {
+ nsCOMPtr<nsIFile> path = mozilla::Omnijar::GetPath(mozilla::Omnijar::GRE);
+ // If the path doesn't exist, we're a dev build.
+ return path == nullptr;
+}
+#endif /* !XP_WIN */
+
+} // 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..a179bf7147
--- /dev/null
+++ b/dom/ipc/ContentChild.h
@@ -0,0 +1,945 @@
+/* -*- 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_ContentChild_h
+#define mozilla_dom_ContentChild_h
+
+#include "mozilla/Atomics.h"
+#include "mozilla/dom/BlobImpl.h"
+#include "mozilla/dom/GetFilesHelper.h"
+#include "mozilla/dom/PContentChild.h"
+#include "mozilla/dom/ProcessActor.h"
+#include "mozilla/dom/RemoteType.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 "nsTHashtable.h"
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+# include "nsIFile.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 PChildToParentStreamChild;
+class PFileDescriptorSetChild;
+} // namespace ipc
+
+namespace loader {
+class PScriptCacheChild;
+}
+
+namespace widget {
+enum class ThemeChangeKind : uint8_t;
+}
+
+using mozilla::loader::PScriptCacheChild;
+
+#if !defined(XP_WIN)
+// Returns whether or not the currently running build is an unpackaged
+// developer build. This check is implemented by looking for omni.ja in the
+// the obj/dist dir. We use this routine to detect when the build dir will
+// use symlinks to the repo and object dir. On Windows, dev builds don't
+// use symlinks.
+bool IsDevelopmentBuild();
+#endif /* !XP_WIN */
+
+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 mozilla::ipc::ChildToParentStreamActorManager,
+ public ProcessActor {
+ typedef mozilla::dom::ClonedMessageData ClonedMessageData;
+ typedef mozilla::ipc::FileDescriptor FileDescriptor;
+ typedef mozilla::ipc::PFileDescriptorSetChild PFileDescriptorSetChild;
+
+ 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(
+ BrowserChild* aTabOpener, nsIOpenWindowInfo* aOpenWindowInfo,
+ uint32_t aChromeFlags, bool aCalledFromJS, bool aWidthSpecified,
+ nsIURI* aURI, const nsAString& aName, const nsACString& aFeatures,
+ bool aForceNoOpener, bool aForceNoReferrer,
+ nsDocShellLoadState* aLoadState, bool* aWindowIsNew,
+ BrowsingContext** aReturn);
+
+ bool Init(MessageLoop* aIOLoop, base::ProcessId aParentPid,
+ const char* aParentBuildID, UniquePtr<IPC::Channel> aChannel,
+ uint64_t aChildID, bool aIsForBrowser);
+
+ void InitXPCOM(XPCOMInitData&& aXPCOMInit,
+ const mozilla::dom::ipc::StructuredCloneData& aInitialData);
+
+ void InitSharedUASheets(const 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);
+
+ 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 void 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 RecvRequestPerformanceMetrics(const nsID& aID);
+
+ mozilla::ipc::IPCResult RecvReinitRendering(
+ Endpoint<PCompositorManagerChild>&& aCompositor,
+ Endpoint<PImageBridgeChild>&& aImageBridge,
+ Endpoint<PVRManagerChild>&& aVRBridge,
+ Endpoint<PRemoteDecoderManagerChild>&& aVideoManager,
+ nsTArray<uint32_t>&& namespaces);
+
+ mozilla::ipc::IPCResult RecvAudioDefaultDeviceChange();
+
+ mozilla::ipc::IPCResult RecvReinitRenderingForDeviceReset();
+
+ mozilla::ipc::IPCResult RecvSetProcessSandbox(
+ const Maybe<FileDescriptor>& aBroker);
+
+ already_AddRefed<PRemoteLazyInputStreamChild>
+ AllocPRemoteLazyInputStreamChild(const nsID& aID, const uint64_t& aSize);
+
+ 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;
+
+ PWebBrowserPersistDocumentChild* AllocPWebBrowserPersistDocumentChild(
+ PBrowserChild* aBrowser, const MaybeDiscarded<BrowsingContext>& aContext);
+
+ virtual mozilla::ipc::IPCResult RecvPWebBrowserPersistDocumentConstructor(
+ PWebBrowserPersistDocumentChild* aActor, PBrowserChild* aBrowser,
+ const MaybeDiscarded<BrowsingContext>& aContext) override;
+
+ bool DeallocPWebBrowserPersistDocumentChild(
+ PWebBrowserPersistDocumentChild* aActor);
+
+ PTestShellChild* AllocPTestShellChild();
+
+ bool DeallocPTestShellChild(PTestShellChild*);
+
+ 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;
+
+ PNeckoChild* AllocPNeckoChild();
+
+ bool DeallocPNeckoChild(PNeckoChild*);
+
+ PPrintingChild* AllocPPrintingChild();
+
+ bool DeallocPPrintingChild(PPrintingChild*);
+
+ PChildToParentStreamChild* AllocPChildToParentStreamChild();
+ bool DeallocPChildToParentStreamChild(PChildToParentStreamChild*);
+
+ PParentToChildStreamChild* AllocPParentToChildStreamChild();
+ bool DeallocPParentToChildStreamChild(PParentToChildStreamChild*);
+
+ PMediaChild* AllocPMediaChild();
+
+ bool DeallocPMediaChild(PMediaChild* aActor);
+
+ PBenchmarkStorageChild* AllocPBenchmarkStorageChild();
+
+ bool DeallocPBenchmarkStorageChild(PBenchmarkStorageChild* aActor);
+
+ PPresentationChild* AllocPPresentationChild();
+
+ bool DeallocPPresentationChild(PPresentationChild* aActor);
+
+ mozilla::ipc::IPCResult RecvNotifyPresentationReceiverLaunched(
+ PBrowserChild* aIframe, const nsString& aSessionId);
+
+ mozilla::ipc::IPCResult RecvNotifyPresentationReceiverCleanUp(
+ const nsString& aSessionId);
+
+ mozilla::ipc::IPCResult RecvNotifyEmptyHTTPCache();
+
+#ifdef MOZ_WEBSPEECH
+ PSpeechSynthesisChild* AllocPSpeechSynthesisChild();
+ bool DeallocPSpeechSynthesisChild(PSpeechSynthesisChild* aActor);
+#endif
+
+ 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);
+ 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 RecvBidiKeyboardNotify(const bool& isLangRTL,
+ const bool& haveBidiKeyboards);
+
+ mozilla::ipc::IPCResult RecvNotifyVisited(nsTArray<VisitedQueryResult>&&);
+
+ mozilla::ipc::IPCResult RecvThemeChanged(LookAndFeelData&& aLookAndFeelData,
+ widget::ThemeChangeKind);
+
+ mozilla::ipc::IPCResult RecvUpdateSystemParameters(
+ nsTArray<SystemParameterKVPair>&& aUpdates);
+
+ // 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 RecvDataStoragePut(const nsString& aFilename,
+ const DataStorageItem& aItem);
+
+ mozilla::ipc::IPCResult RecvDataStorageRemove(const nsString& aFilename,
+ const nsCString& aKey,
+ const DataStorageType& aType);
+
+ mozilla::ipc::IPCResult RecvDataStorageClear(const nsString& aFilename);
+
+ 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 RecvUpdateSharedData(
+ const FileDescriptor& aMapFile, const uint32_t& aMapSize,
+ nsTArray<IPCBlob>&& aBlobs, nsTArray<nsCString>&& aChangedKeys);
+
+ mozilla::ipc::IPCResult RecvFontListChanged();
+
+ 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(
+ nsTArray<SystemFontListEntry>&& aFontList);
+ mozilla::ipc::IPCResult RecvRebuildFontList(const bool& aFullRebuild);
+
+ mozilla::ipc::IPCResult RecvUpdateAppLocales(
+ nsTArray<nsCString>&& aAppLocales);
+ mozilla::ipc::IPCResult RecvUpdateRequestedLocales(
+ nsTArray<nsCString>&& aRequestedLocales);
+
+ mozilla::ipc::IPCResult RecvAddPermission(const IPC::Permission& permission);
+
+ mozilla::ipc::IPCResult RecvRemoveAllPermissions();
+
+ mozilla::ipc::IPCResult RecvFlushMemory(const nsString& reason);
+
+ mozilla::ipc::IPCResult RecvActivateA11y(const uint32_t& aMainChromeTid,
+ const uint32_t& aMsaaID);
+ 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);
+
+ // Call RemoteTypePrefix() on the result to remove URIs if you want to use
+ // this for telemetry.
+ const nsACString& GetRemoteType() const override;
+
+ mozilla::ipc::IPCResult RecvInitServiceWorkers(
+ const ServiceWorkerConfiguration& aConfig);
+
+ 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 RecvShutdown();
+
+ mozilla::ipc::IPCResult RecvInvokeDragSession(
+ nsTArray<IPCDataTransfer>&& aTransfers, const uint32_t& aAction);
+
+ MOZ_CAN_RUN_SCRIPT_BOUNDARY
+ mozilla::ipc::IPCResult RecvEndDragSession(
+ const bool& aDoneDrag, const bool& aUserCancelled,
+ const mozilla::LayoutDeviceIntPoint& aEndDragPoint,
+ const uint32_t& aKeyModifiers);
+
+ mozilla::ipc::IPCResult RecvPush(const nsCString& aScope,
+ const IPC::Principal& aPrincipal,
+ const nsString& aMessageId);
+
+ mozilla::ipc::IPCResult RecvPushWithData(const nsCString& aScope,
+ const IPC::Principal& aPrincipal,
+ const nsString& aMessageId,
+ nsTArray<uint8_t>&& aData);
+
+ mozilla::ipc::IPCResult RecvPushSubscriptionChange(
+ const nsCString& aScope, const IPC::Principal& aPrincipal);
+
+ mozilla::ipc::IPCResult RecvPushError(const nsCString& aScope,
+ const IPC::Principal& aPrincipal,
+ const nsString& aMessage,
+ const uint32_t& aFlags);
+
+ mozilla::ipc::IPCResult RecvNotifyPushSubscriptionModifiedObservers(
+ const nsCString& aScope, const IPC::Principal& aPrincipal);
+
+ mozilla::ipc::IPCResult RecvRefreshScreens(
+ nsTArray<ScreenDetails>&& aScreens);
+
+ mozilla::ipc::IPCResult RecvNetworkLinkTypeChange(const uint32_t& aType);
+ uint32_t NetworkLinkType() const { return mNetworkLinkType; }
+
+ // Get the directory for IndexedDB files. We query the parent for this and
+ // cache the value
+ nsString& GetIndexedDBPath();
+
+ ContentParentId GetID() const { return mID; }
+
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+ uint32_t GetChromeMainThreadId() const { return mMainChromeTid; }
+
+ uint32_t GetMsaaID() const { return mMsaaID; }
+#endif
+
+ bool IsForBrowser() const { return mIsForBrowser; }
+
+ PFileDescriptorSetChild* AllocPFileDescriptorSetChild(const FileDescriptor&);
+
+ bool DeallocPFileDescriptorSetChild(PFileDescriptorSetChild*);
+
+ 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);
+
+ PWebrtcGlobalChild* AllocPWebrtcGlobalChild();
+
+ bool DeallocPWebrtcGlobalChild(PWebrtcGlobalChild* aActor);
+
+ PContentPermissionRequestChild* AllocPContentPermissionRequestChild(
+ const nsTArray<PermissionRequest>& aRequests,
+ const IPC::Principal& aPrincipal,
+ const IPC::Principal& 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,
+ const IPC::Principal& aPrincipal, const Maybe<nsID>& aAgentClusterId);
+
+ 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);
+
+ mozilla::ipc::IPCResult RecvGetUntrustedModulesData(
+ GetUntrustedModulesDataResolver&& aResolver);
+
+ mozilla::ipc::IPCResult RecvSetXPCOMProcessAttributes(
+ XPCOMInitData&& aXPCOMInit, const StructuredCloneData& aInitialData,
+ LookAndFeelData&& aLookAndFeelData,
+ nsTArray<SystemFontListEntry>&& aFontList,
+ const Maybe<base::SharedMemoryHandle>& aSharedUASheetHandle,
+ const uintptr_t& aSharedUASheetAddress,
+ nsTArray<base::SharedMemoryHandle>&& aSharedFontListBlocks);
+
+ 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(
+ const CrossProcessMutexHandle& aHandle);
+
+ mozilla::ipc::IPCResult RecvFlushCodeCoverageCounters(
+ FlushCodeCoverageCountersResolver&& aResolver);
+
+ mozilla::ipc::IPCResult RecvGetMemoryUniqueSetSize(
+ GetMemoryUniqueSetSizeResolver&& aResolver);
+
+ mozilla::ipc::IPCResult RecvSetInputEventQueueEnabled();
+
+ mozilla::ipc::IPCResult RecvFlushInputEventQueue();
+
+ mozilla::ipc::IPCResult RecvSuspendInputEventQueue();
+
+ mozilla::ipc::IPCResult RecvResumeInputEventQueue();
+
+ mozilla::ipc::IPCResult RecvAddDynamicScalars(
+ nsTArray<DynamicScalarDefinition>&& aDefs);
+
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+ bool SendGetA11yContentId();
+#endif // defined(XP_WIN) && defined(ACCESSIBILITY)
+
+ // Get a reference to the font list passed from the chrome process,
+ // for use during gfx initialization.
+ nsTArray<mozilla::dom::SystemFontListEntry>& SystemFontList() {
+ return mFontList;
+ }
+
+ nsTArray<base::SharedMemoryHandle>& SharedFontListBlocks() {
+ return mSharedFontListBlocks;
+ }
+
+ // PURLClassifierChild
+ PURLClassifierChild* AllocPURLClassifierChild(const Principal& aPrincipal,
+ bool* aSuccess);
+ bool DeallocPURLClassifierChild(PURLClassifierChild* aActor);
+
+ // PURLClassifierLocalChild
+ PURLClassifierLocalChild* AllocPURLClassifierLocalChild(
+ nsIURI* aUri, const nsTArray<IPCURLClassifierFeature>& aFeatures);
+ bool DeallocPURLClassifierLocalChild(PURLClassifierLocalChild* aActor);
+
+ PLoginReputationChild* AllocPLoginReputationChild(nsIURI* aUri);
+
+ bool DeallocPLoginReputationChild(PLoginReputationChild* aActor);
+
+ PSessionStorageObserverChild* AllocPSessionStorageObserverChild();
+
+ bool DeallocPSessionStorageObserverChild(
+ PSessionStorageObserverChild* aActor);
+
+ LookAndFeelData& 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);
+
+ typedef std::function<void(PRFileDesc*)> AnonymousTemporaryFileCallback;
+ nsresult AsyncOpenAnonymousTemporaryFile(
+ const AnonymousTemporaryFileCallback& aCallback);
+
+ mozilla::ipc::IPCResult RecvSetPluginList(
+ const uint32_t& aPluginEpoch, nsTArray<PluginTag>&& aPluginTags,
+ nsTArray<FakePluginTag>&& aFakePluginTags);
+
+ 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);
+
+ void HoldBrowsingContextGroup(BrowsingContextGroup* aBCG);
+ void ReleaseBrowsingContextGroup(BrowsingContextGroup* aBCG);
+
+ // 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
+
+ PChildToParentStreamChild* SendPChildToParentStreamConstructor(
+ PChildToParentStreamChild* aActor) override;
+ PFileDescriptorSetChild* SendPFileDescriptorSetConstructor(
+ const FileDescriptor& aFD) override;
+
+ 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;
+
+ virtual void OnChannelReceivedMessage(const Message& aMsg) override;
+
+ mozilla::ipc::IPCResult RecvCreateBrowsingContext(
+ uint64_t aGroupId, BrowsingContext::IPCInitializer&& aInit);
+
+ mozilla::ipc::IPCResult RecvDiscardBrowsingContext(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ DiscardBrowsingContextResolver&& aResolve);
+
+ mozilla::ipc::IPCResult RecvRegisterBrowsingContextGroup(
+ uint64_t aGroupId, nsTArray<SyncedContextInitializer>&& aInits);
+
+ 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);
+ mozilla::ipc::IPCResult RecvRaiseWindow(
+ const MaybeDiscarded<BrowsingContext>& aContext, CallerType aCallerType,
+ uint64_t aActionId);
+ mozilla::ipc::IPCResult RecvAdjustWindowFocus(
+ const MaybeDiscarded<BrowsingContext>& aContext, bool aCheckPermission,
+ bool aIsVisible);
+ mozilla::ipc::IPCResult RecvClearFocus(
+ const MaybeDiscarded<BrowsingContext>& aContext);
+ mozilla::ipc::IPCResult RecvSetFocusedBrowsingContext(
+ const MaybeDiscarded<BrowsingContext>& aContext);
+ 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);
+ 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,
+ const MaybeDiscarded<BrowsingContext>& aActiveBrowsingContext);
+ mozilla::ipc::IPCResult RecvReviseActiveBrowsingContext(
+ const MaybeDiscarded<BrowsingContext>& aActiveBrowsingContext,
+ 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);
+
+ 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(
+ uint64_t innerWindowId, 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);
+ mozilla::ipc::IPCResult RecvGoForward(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const Maybe<int32_t>& aCancelContentJSEpoch,
+ bool aRequireUserInteraction);
+ mozilla::ipc::IPCResult RecvGoToIndex(
+ const MaybeDiscarded<BrowsingContext>& aContext, const int32_t& aIndex,
+ const Maybe<int32_t>& aCancelContentJSEpoch);
+ 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::HandleObject 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 RecvDispatchLocationChangeEvent(
+ const MaybeDiscarded<BrowsingContext>& aContext);
+
+ mozilla::ipc::IPCResult RecvDispatchBeforeUnloadToSubtree(
+ const MaybeDiscarded<BrowsingContext>& aStartingAt,
+ DispatchBeforeUnloadToSubtreeResolver&& aResolver);
+
+ public:
+ static void DispatchBeforeUnloadToSubtree(
+ BrowsingContext* aStartingAt,
+ const DispatchBeforeUnloadToSubtreeResolver& aResolver);
+
+ private:
+ mozilla::ipc::IPCResult RecvFlushFOGData(FlushFOGDataResolver&& aResolver);
+
+ mozilla::ipc::IPCResult RecvUpdateMediaCodecsSupported(
+ RemoteDecodeIn aLocation,
+ const PDMFactory::MediaCodecsSupported& aSupported);
+
+#ifdef NIGHTLY_BUILD
+ virtual PContentChild::Result OnMessageReceived(const Message& aMsg) override;
+#else
+ using PContentChild::OnMessageReceived;
+#endif
+
+ virtual PContentChild::Result OnMessageReceived(const Message& aMsg,
+ Message*& aReply) override;
+
+ nsTArray<mozilla::UniquePtr<AlertObserver>> mAlertObservers;
+ RefPtr<ConsoleListener> mConsoleListener;
+
+ nsTHashtable<nsPtrHashKey<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.
+ nsTArray<mozilla::dom::SystemFontListEntry> mFontList;
+ // Temporary storage for look and feel data.
+ LookAndFeelData 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;
+
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+ /**
+ * The thread ID of the main thread in the chrome process.
+ */
+ uint32_t mMainChromeTid;
+
+ /**
+ * This is an a11y-specific unique id for the content process that is
+ * generated by the chrome process.
+ */
+ uint32_t mMsaaID;
+#endif // defined(XP_WIN) && defined(ACCESSIBILITY)
+
+ 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;
+
+#ifdef MOZ_GECKO_PROFILER
+ RefPtr<ChildProfilerController> mProfilerController;
+#endif
+
+#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;
+};
+
+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..8e88f743d1
--- /dev/null
+++ b/dom/ipc/ContentParent.cpp
@@ -0,0 +1,7516 @@
+/* -*- 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/DebugOnly.h"
+
+#include "base/basictypes.h"
+#include "base/shared_memory.h"
+
+#include "ContentParent.h"
+#include "ProcessUtils.h"
+#include "BrowserParent.h"
+
+#include "chrome/common/process_watcher.h"
+#include "mozilla/Result.h"
+#include "mozilla/XREAppData.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIBrowserDOMWindow.h"
+
+#ifdef ACCESSIBILITY
+# include "mozilla/a11y/PDocAccessible.h"
+#endif
+#include "GeckoProfiler.h"
+#include "GMPServiceParent.h"
+#include "HandlerServiceParent.h"
+#include "IHistory.h"
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+# include "mozilla/a11y/AccessibleWrap.h"
+# include "mozilla/a11y/Compatibility.h"
+#endif
+#include <map>
+#include <utility>
+
+#include "BrowserParent.h"
+#include "ContentProcessManager.h"
+#include "Geolocation.h"
+#include "GfxInfoBase.h"
+#include "MMPrinter.h"
+#include "PreallocatedProcessManager.h"
+#include "ProcessPriorityManager.h"
+#include "SandboxHal.h"
+#include "SourceSurfaceRawData.h"
+#include "URIUtils.h"
+#include "gfxPlatform.h"
+#include "gfxPlatformFontList.h"
+#include "mozilla/AutoRestore.h"
+#include "mozilla/ContentBlocking.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/BenchmarkStorageParent.h"
+#include "mozilla/ContentBlockingUserInteraction.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/Components.h"
+#include "mozilla/DataStorage.h"
+#ifdef MOZ_GLEAN
+# include "mozilla/FOGIPC.h"
+#endif
+#include "mozilla/GlobalStyleSheetCache.h"
+#include "mozilla/HangDetails.h"
+#include "mozilla/LoginReputationIPC.h"
+#include "mozilla/LookAndFeel.h"
+#include "mozilla/NullPrincipal.h"
+#include "mozilla/PerformanceMetricsCollector.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/PresShell.h"
+#include "mozilla/ProcessHangMonitor.h"
+#include "mozilla/ProcessHangMonitorIPC.h"
+#include "mozilla/ScopeExit.h"
+#include "mozilla/ScriptPreloader.h"
+#include "mozilla/Services.h"
+#include "mozilla/Sprintf.h"
+#include "mozilla/StaticPrefs_dom.h"
+#include "mozilla/StaticPrefs_media.h"
+#include "mozilla/StaticPrefs_widget.h"
+#include "mozilla/StyleSheet.h"
+#include "mozilla/StyleSheetInlines.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/TelemetryIPC.h"
+#include "mozilla/Unused.h"
+#include "mozilla/WebBrowserPersistDocumentParent.h"
+#include "mozilla/devtools/HeapSnapshotTempFileHelperParent.h"
+#include "mozilla/docshell/OfflineCacheUpdateParent.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/PPresentationParent.h"
+#include "mozilla/dom/ParentProcessMessageManager.h"
+#include "mozilla/dom/Permissions.h"
+#include "mozilla/dom/PresentationParent.h"
+#include "mozilla/dom/ProcessMessageManager.h"
+#include "mozilla/dom/PushNotifier.h"
+#include "mozilla/dom/ServiceWorkerManager.h"
+#include "mozilla/dom/ServiceWorkerRegistrar.h"
+#include "mozilla/dom/ServiceWorkerUtils.h"
+#include "mozilla/dom/SessionHistoryEntry.h"
+#include "mozilla/dom/SessionStorageManager.h"
+#include "mozilla/dom/StorageIPC.h"
+#include "mozilla/dom/URLClassifierParent.h"
+#include "mozilla/dom/WakeLock.h"
+#include "mozilla/dom/WindowGlobalParent.h"
+#include "mozilla/dom/ipc/SharedMap.h"
+#include "mozilla/dom/ipc/StructuredCloneData.h"
+#include "mozilla/dom/nsMixedContentBlocker.h"
+#include "mozilla/dom/power/PowerManagerService.h"
+#include "mozilla/dom/quota/QuotaManagerService.h"
+#include "mozilla/embedding/printingui/PrintingParent.h"
+#include "mozilla/extensions/StreamFilterParent.h"
+#include "mozilla/gfx/GPUProcessManager.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/hal_sandbox/PHalParent.h"
+#include "mozilla/intl/LocaleService.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/BackgroundParent.h"
+#include "mozilla/ipc/CrashReporterHost.h"
+#include "mozilla/ipc/Endpoint.h"
+#include "mozilla/ipc/FileDescriptorSetParent.h"
+#include "mozilla/ipc/FileDescriptorUtils.h"
+#include "mozilla/ipc/IPCStreamAlloc.h"
+#include "mozilla/ipc/IPCStreamDestination.h"
+#include "mozilla/ipc/IPCStreamSource.h"
+#include "mozilla/ipc/IPCStreamUtils.h"
+#include "mozilla/ipc/PChildToParentStreamParent.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/plugins/PluginBridge.h"
+#include "mozilla/RemoteLazyInputStreamParent.h"
+#include "mozilla/widget/RemoteLookAndFeel.h"
+#include "mozilla/widget/ScreenManager.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 "nsDebugImpl.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsDocShell.h"
+#include "nsEmbedCID.h"
+#include "nsFocusManager.h"
+#include "nsFrameLoader.h"
+#include "nsFrameMessageManager.h"
+#include "nsHashPropertyBag.h"
+#include "nsHyphenationManager.h"
+#include "nsIAlertsService.h"
+#include "nsIAppStartup.h"
+#include "nsIAppWindow.h"
+#include "nsIAsyncInputStream.h"
+#include "nsIBidiKeyboard.h"
+#include "nsICaptivePortalService.h"
+#include "nsICertOverrideService.h"
+#include "nsIClipboard.h"
+#include "nsIContentProcess.h"
+#include "nsIContentSecurityPolicy.h"
+#include "nsICookie.h"
+#include "nsICrashService.h"
+#include "nsICycleCollectorListener.h"
+#include "nsIDOMChromeWindow.h"
+#include "nsIDocShell.h"
+#include "nsIDocShellTreeOwner.h"
+#include "nsIDragService.h"
+#include "nsIExternalProtocolService.h"
+#include "nsIGfxInfo.h"
+#include "nsIUserIdleService.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsILocalStorageManager.h"
+#include "nsIMemoryInfoDumper.h"
+#include "nsIMemoryReporter.h"
+#include "nsIMozBrowserFrame.h"
+#include "nsINetworkLinkService.h"
+#include "nsIObserverService.h"
+#include "nsIParentChannel.h"
+#include "nsIScriptError.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsIServiceWorkerManager.h"
+#include "nsISiteSecurityService.h"
+#include "nsISound.h"
+#include "nsIStringBundle.h"
+#include "nsITimer.h"
+#include "nsIURL.h"
+#include "nsIWebBrowserChrome.h"
+#include "nsIX509Cert.h"
+#include "nsIXULRuntime.h"
+#include "nsMemoryInfoDumper.h"
+#include "nsMemoryReporterManager.h"
+#include "nsOpenURIInFrameParams.h"
+#include "nsPIWindowWatcher.h"
+#include "nsPluginHost.h"
+#include "nsPluginTags.h"
+#include "nsQueryObject.h"
+#include "nsReadableUtils.h"
+#include "nsSHistory.h"
+#include "nsScriptError.h"
+#include "nsSerializationHelper.h"
+#include "nsServiceManagerUtils.h"
+#include "nsStreamUtils.h"
+#include "nsStyleSheetService.h"
+#include "nsThread.h"
+#include "nsThreadUtils.h"
+#include "nsWidgetsCID.h"
+#include "nsWindowWatcher.h"
+#include "prio.h"
+#include "private/pprio.h"
+#include "xpcpublic.h"
+#include "nsOpenWindowInfo.h"
+
+#ifdef MOZ_WEBRTC
+# include "jsapi/WebrtcGlobalParent.h"
+#endif
+
+#if defined(XP_MACOSX)
+# include "nsMacUtilsImpl.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>
+#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/audio/AudioNotificationSender.h"
+# include "mozilla/widget/AudioSession.h"
+# include "mozilla/widget/WinContentSystemParameters.h"
+# include "mozilla/WinDllServices.h"
+#endif
+
+#ifdef ACCESSIBILITY
+# include "nsAccessibilityService.h"
+#endif
+
+#ifdef MOZ_GECKO_PROFILER
+# include "nsIProfiler.h"
+# include "ProfilerParent.h"
+#endif
+
+#ifdef MOZ_CODE_COVERAGE
+# include "mozilla/CodeCoverageHandler.h"
+#endif
+
+// For VP9Benchmark::sBenchmarkFpsPref
+#include "Benchmark.h"
+
+// XXX need another bug to move this to a common header.
+#ifdef DISABLE_ASSERTS_FOR_FUZZING
+# define ASSERT_UNLESS_FUZZING(...) \
+ do { \
+ } while (0)
+#else
+# define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false, __VA_ARGS__)
+#endif
+
+static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
+
+using base::KillProcess;
+
+using namespace CrashReporter;
+using namespace mozilla::dom::power;
+using namespace mozilla::media;
+using namespace mozilla::embedding;
+using namespace mozilla::gfx;
+using namespace mozilla::gmp;
+using namespace mozilla::hal;
+using namespace mozilla::ipc;
+using namespace mozilla::intl;
+using namespace mozilla::layers;
+using namespace mozilla::layout;
+using namespace mozilla::net;
+using namespace mozilla::psm;
+using namespace mozilla::widget;
+using mozilla::loader::PScriptCacheParent;
+using mozilla::Telemetry::ProcessID;
+
+extern mozilla::LazyLogModule gFocusLog;
+
+#define LOGFOCUS(args) MOZ_LOG(gFocusLog, mozilla::LogLevel::Debug, args)
+
+// XXX Workaround for bug 986973 to maintain the existing broken semantics
+template <>
+struct nsIConsoleService::COMTypeInfo<nsConsoleService, void> {
+ static const nsIID kIID;
+};
+const nsIID nsIConsoleService::COMTypeInfo<nsConsoleService, void>::kIID =
+ NS_ICONSOLESERVICE_IID;
+
+namespace mozilla {
+namespace CubebUtils {
+extern FileDescriptor CreateAudioIPCConnection();
+}
+
+namespace dom {
+
+LazyLogModule gProcessLog("Process");
+
+static std::map<RemoteDecodeIn, PDMFactory::MediaCodecsSupported>
+ sCodecsSupported;
+
+/* static */
+LogModule* ContentParent::GetLog() { return gProcessLog; }
+
+#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->Unsound_IsClosed()) {
+ channelStr = "closed channel";
+ } else {
+ channelStr = "open channel";
+ }
+ numQueuedMessages = channel->Unsound_NumQueuedMessages();
+ }
+
+ 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
+
+UniquePtr<nsDataHashtable<nsUint32HashKey, ContentParent*>>
+ ContentParent::sJSPluginContentParents;
+UniquePtr<nsTArray<ContentParent*>> ContentParent::sPrivateContent;
+UniquePtr<LinkedList<ContentParent>> ContentParent::sContentParents;
+StaticRefPtr<ContentParent> ContentParent::sRecycledE10SProcess;
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+UniquePtr<SandboxBrokerPolicyFactory>
+ ContentParent::sSandboxBrokerPolicyFactory;
+#endif
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+UniquePtr<std::vector<std::string>> ContentParent::sMacSandboxParams;
+#endif
+
+// Whether a private docshell has been seen before.
+static bool sHasSeenPrivateDocShell = false;
+
+// This is true when subprocess launching is enabled. This is the
+// case between StartUp() and ShutDown().
+static bool sCanLaunchSubprocesses;
+
+// 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,
+};
+
+// PreallocateProcess is called by the PreallocatedProcessManager.
+// ContentParent then takes this process back within GetNewOrUsedBrowserProcess.
+/*static*/ RefPtr<ContentParent::LaunchPromise>
+ContentParent::PreallocateProcess() {
+ RefPtr<ContentParent> process = new ContentParent(PREALLOC_REMOTE_TYPE);
+
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("Preallocating process of type prealloc"));
+ return process->LaunchSubprocessAsync(PROCESS_PRIORITY_PREALLOC);
+}
+
+/*static*/
+void ContentParent::StartUp() {
+ // We could launch sub processes from content process
+ // FIXME Bug 1023701 - Stop using ContentParent static methods in
+ // child process
+ sCanLaunchSubprocesses = true;
+
+ 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();
+
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+ sSandboxBrokerPolicyFactory = MakeUnique<SandboxBrokerPolicyFactory>();
+#endif
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+ sMacSandboxParams = MakeUnique<std::vector<std::string>>();
+#endif
+}
+
+/*static*/
+void ContentParent::ShutDown() {
+ // No-op for now. We rely on normal process shutdown and
+ // ClearOnShutdown() to clean up our state.
+ sCanLaunchSubprocesses = false;
+
+#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->LookupOrAdd(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 as well as web (and webLargeAllocation, and
+ // webCOOP+COEP)
+ return StringBeginsWith(aContentProcessType, DEFAULT_REMOTE_TYPE);
+}
+
+bool IsWebCoopCoepRemoteType(const nsACString& aContentProcessType) {
+ return StringBeginsWith(aContentProcessType,
+ WITH_COOP_COEP_REMOTE_TYPE_PREFIX);
+}
+
+bool IsPrivilegedMozillaRemoteType(const nsACString& aContentProcessType) {
+ return aContentProcessType == PRIVILEGEDMOZILLA_REMOTE_TYPE;
+}
+
+bool IsExtensionRemoteType(const nsACString& aContentProcessType) {
+ return aContentProcessType == EXTENSION_REMOTE_TYPE;
+}
+
+/*static*/
+uint32_t ContentParent::GetMaxProcessCount(
+ const nsACString& aContentProcessType) {
+ // Max process count is based only on the prefix.
+ const nsDependentCSubstring processTypePrefix =
+ RemoteTypePrefix(aContentProcessType);
+
+ // Check for the default remote type of "web", as it uses different prefs.
+ if (processTypePrefix == DEFAULT_REMOTE_TYPE) {
+ return GetMaxWebProcessCount();
+ }
+
+ // Read the pref controling this remote type. `dom.ipc.processCount` is not
+ // used as a fallback, as it is intended to control the number of "web"
+ // content processes, checked in `mozilla::GetMaxWebProcessCount()`.
+ nsAutoCString processCountPref("dom.ipc.processCount.");
+ processCountPref.Append(processTypePrefix);
+
+ int32_t maxContentParents = Preferences::GetInt(processCountPref.get(), 1);
+ if (maxContentParents < 1) {
+ maxContentParents = 1;
+ }
+
+ return static_cast<uint32_t>(maxContentParents);
+}
+
+/*static*/
+bool ContentParent::IsMaxProcessCountReached(
+ const nsACString& aContentProcessType) {
+ return GetPoolSize(aContentProcessType) >=
+ GetMaxProcessCount(aContentProcessType);
+}
+
+// Really more ReleaseUnneededProcesses()
+/*static*/
+void ContentParent::ReleaseCachedProcesses() {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("ReleaseCachedProcesses:"));
+ if (!sBrowserContentParents) {
+ return;
+ }
+
+#ifdef DEBUG
+ int num = 0;
+ for (auto iter = sBrowserContentParents->Iter(); !iter.Done(); iter.Next()) {
+ nsTArray<ContentParent*>* contentParents = iter.Data().get();
+ num += contentParents->Length();
+ for (auto* cp : *contentParents) {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("%s: %zu processes", cp->mRemoteType.get(),
+ contentParents->Length()));
+ break;
+ }
+ }
+#endif
+ // We process the toRelease array outside of the iteration to avoid modifying
+ // the list (via RemoveFromList()) while we're iterating it.
+ nsTArray<ContentParent*> toRelease;
+ for (auto iter = sBrowserContentParents->Iter(); !iter.Done(); iter.Next()) {
+ nsTArray<ContentParent*>* contentParents = iter.Data().get();
+
+ // Shutting down these processes will change the array so let's use another
+ // array for the removal.
+ for (auto* cp : *contentParents) {
+ if (cp->ManagedPBrowserParent().Count() == 0 &&
+ !cp->HasActiveWorkerOrJSPlugin() &&
+ cp->mRemoteType == DEFAULT_REMOTE_TYPE) {
+ toRelease.AppendElement(cp);
+ } else {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ (" Skipping %p (%s), count %d, HasActiveWorkerOrJSPlugin %d",
+ cp, cp->mRemoteType.get(), cp->ManagedPBrowserParent().Count(),
+ cp->HasActiveWorkerOrJSPlugin()));
+ }
+ }
+ }
+
+ for (auto* cp : toRelease) {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ (" Shutdown %p (%s)", cp, cp->mRemoteType.get()));
+ PreallocatedProcessManager::Erase(cp);
+ // Start a soft shutdown.
+ cp->ShutDownProcess(SEND_SHUTDOWN_MESSAGE);
+ // Make sure we don't select this process for new tabs.
+ cp->MarkAsDead();
+ // Make sure that this process is no longer accessible from JS by its
+ // message manager.
+ cp->ShutDownMessageManager();
+ }
+}
+
+/*static*/
+already_AddRefed<ContentParent> ContentParent::MinTabSelect(
+ const nsTArray<ContentParent*>& aContentParents,
+ int32_t aMaxContentParents) {
+ uint32_t maxSelectable =
+ std::min(static_cast<uint32_t>(aContentParents.Length()),
+ static_cast<uint32_t>(aMaxContentParents));
+ uint32_t min = INT_MAX;
+ RefPtr<ContentParent> candidate;
+ ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+
+ for (uint32_t i = 0; i < maxSelectable; i++) {
+ ContentParent* p = aContentParents[i];
+ MOZ_DIAGNOSTIC_ASSERT(!p->IsDead());
+
+ 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> 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));
+
+ return BasePrincipal::CreateContentPrincipal(origin);
+}
+
+/*static*/
+already_AddRefed<ContentParent> ContentParent::GetUsedBrowserProcess(
+ const nsACString& aRemoteType, nsTArray<ContentParent*>& aContentParents,
+ uint32_t aMaxContentParents, bool aPreferUsed) {
+#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) {
+ // For the preloaded browser we don't want to create a new process but
+ // reuse an existing one.
+ 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];
+#ifdef MOZ_GECKO_PROFILER
+ if (profiler_thread_is_being_profiled()) {
+ nsPrintfCString marker("Reused process %u",
+ (unsigned int)retval->ChildID());
+ PROFILER_MARKER_TEXT("Process", DOM, {}, marker);
+ }
+#endif
+ 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->StopRecycling();
+ 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->StopRecycling();
+ 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->StopRecycling();
+
+#ifdef MOZ_GECKO_PROFILER
+ if (profiler_thread_is_being_profiled()) {
+ nsPrintfCString marker("Recycled process %u (%p)",
+ (unsigned int)recycled->ChildID(), recycled.get());
+ PROFILER_MARKER_TEXT("Process", DOM, {}, marker);
+ }
+#endif
+ 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.
+ RefPtr<ContentParent> preallocated;
+ if (aRemoteType != FILE_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();
+
+#ifdef MOZ_GECKO_PROFILER
+ if (profiler_thread_is_being_profiled()) {
+ nsPrintfCString marker("Assigned preallocated process %u",
+ (unsigned int)preallocated->ChildID());
+ PROFILER_MARKER_TEXT("Process", DOM, {}, marker);
+ }
+#endif
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("Adopted preallocated process %p for type %s", preallocated.get(),
+ PromiseFlatCString(aRemoteType).get()));
+
+ // Specialize this process for the appropriate remote type, and activate it.
+ preallocated->mActivateTS = TimeStamp::Now();
+ preallocated->AddToPool(aContentParents);
+
+ preallocated->mRemoteType.Assign(aRemoteType);
+ preallocated->mRemoteTypeIsolationPrincipal =
+ CreateRemoteTypeIsolationPrincipal(aRemoteType);
+ Unused << preallocated->SendRemoteType(preallocated->mRemoteType);
+
+ 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()));
+
+ // 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);
+ if (contentParent) {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("GetNewOrUsedProcess: Existing host process %p (launching %d)",
+ contentParent.get(), contentParent->IsLaunching()));
+ contentParent->AssertAlive();
+ contentParent->StopRecycling();
+ return contentParent.forget();
+ }
+ }
+
+ nsTArray<ContentParent*>& contentParents = GetOrCreatePool(aRemoteType);
+ uint32_t maxContentParents = GetMaxProcessCount(aRemoteType);
+ // We never want to re-use Large-Allocation processes.
+ if (aRemoteType == LARGE_ALLOCATION_REMOTE_TYPE &&
+ contentParents.Length() >= maxContentParents) {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("GetNewOrUsedProcess: returning Large Used process"));
+ return GetNewOrUsedLaunchingBrowserProcess(DEFAULT_REMOTE_TYPE, aGroup,
+ aPriority,
+ /*aPreferUsed =*/false);
+ }
+
+ // Let's try and reuse an existing process.
+ contentParent = GetUsedBrowserProcess(aRemoteType, contentParents,
+ maxContentParents, aPreferUsed);
+
+ if (contentParent) {
+ // We have located a process. It may not have finished initializing,
+ // this will be for the caller to handle.
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("GetNewOrUsedProcess: Used process %p (launching %d)",
+ contentParent.get(), contentParent->IsLaunching()));
+ contentParent->AssertAlive();
+ contentParent->StopRecycling();
+ if (aGroup) {
+ aGroup->EnsureHostProcess(contentParent);
+ }
+ return contentParent.forget();
+ }
+
+ // 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 (!contentParent->BeginSubprocessLaunch(aPriority)) {
+ // Launch aborted because of shutdown. Bailout.
+ contentParent->LaunchSubprocessReject();
+ return nullptr;
+ }
+ // Store this process for future reuse.
+ contentParent->AddToPool(contentParents);
+
+ // 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);
+
+ MOZ_ASSERT(contentParent->IsLaunching());
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("GetNewOrUsedProcess: new process %p", contentParent.get()));
+ contentParent->AssertAlive();
+ contentParent->StopRecycling();
+ if (aGroup) {
+ aGroup->EnsureHostProcess(contentParent);
+ }
+ return contentParent.forget();
+}
+
+/*static*/
+RefPtr<ContentParent::LaunchPromise>
+ContentParent::GetNewOrUsedBrowserProcessAsync(const nsACString& aRemoteType,
+ BrowsingContextGroup* aGroup,
+ ProcessPriority aPriority,
+ bool aPreferUsed) {
+ // Obtain a `ContentParent` launched asynchronously.
+ RefPtr<ContentParent> contentParent = GetNewOrUsedLaunchingBrowserProcess(
+ aRemoteType, aGroup, aPriority, aPreferUsed);
+ if (!contentParent) {
+ // In case of launch error, stop here.
+ return LaunchPromise::CreateAndReject(LaunchError(), __func__);
+ }
+ return contentParent->WaitForLaunchAsync(aPriority);
+}
+
+/*static*/
+already_AddRefed<ContentParent> ContentParent::GetNewOrUsedBrowserProcess(
+ const nsACString& aRemoteType, BrowsingContextGroup* aGroup,
+ ProcessPriority aPriority, bool aPreferUsed) {
+ RefPtr<ContentParent> contentParent = GetNewOrUsedLaunchingBrowserProcess(
+ aRemoteType, aGroup, aPriority, aPreferUsed);
+ if (!contentParent || !contentParent->WaitForLaunchSync(aPriority)) {
+ // In case of launch error, stop here.
+ return nullptr;
+ }
+ return contentParent.forget();
+}
+
+RefPtr<ContentParent::LaunchPromise> ContentParent::WaitForLaunchAsync(
+ ProcessPriority aPriority) {
+ MOZ_DIAGNOSTIC_ASSERT(!IsDead());
+ if (!IsLaunching()) {
+ 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)) {
+ self->mActivateTS = TimeStamp::Now();
+ return LaunchPromise::CreateAndResolve(self, __func__);
+ }
+
+ self->LaunchSubprocessReject();
+ return LaunchPromise::CreateAndReject(LaunchError(), __func__);
+ },
+ [self = RefPtr{this}] {
+ self->LaunchSubprocessReject();
+ return LaunchPromise::CreateAndReject(LaunchError(), __func__);
+ });
+}
+
+bool ContentParent::WaitForLaunchSync(ProcessPriority aPriority) {
+ MOZ_DIAGNOSTIC_ASSERT(!IsDead());
+ if (!IsLaunching()) {
+ return true;
+ }
+
+ // We've started a sync content process launch.
+ Telemetry::Accumulate(Telemetry::CONTENT_PROCESS_LAUNCH_IS_SYNC, 1);
+
+ // We're a process which hasn't finished initializing. We may be racing
+ // against whoever launched it (and whoever else is already racing). Since
+ // we're sync, we win the race and finish the initialization.
+ bool launchSuccess = mSubprocess->WaitForProcessHandle();
+ if (launchSuccess &&
+ LaunchSubprocessResolve(/* aIsSync = */ true, aPriority)) {
+ mActivateTS = TimeStamp::Now();
+ return true;
+ }
+ // In case of failure.
+ LaunchSubprocessReject();
+ return false;
+}
+
+/*static*/
+already_AddRefed<ContentParent> ContentParent::GetNewOrUsedJSPluginProcess(
+ uint32_t aPluginID, const hal::ProcessPriority& aPriority) {
+ RefPtr<ContentParent> p;
+ if (sJSPluginContentParents) {
+ p = sJSPluginContentParents->Get(aPluginID);
+ } else {
+ sJSPluginContentParents =
+ MakeUnique<nsDataHashtable<nsUint32HashKey, ContentParent*>>();
+ }
+
+ if (p) {
+ return p.forget();
+ }
+
+ p = new ContentParent(aPluginID);
+
+ if (!p->LaunchSubprocessSync(aPriority)) {
+ return nullptr;
+ }
+
+ sJSPluginContentParents->Put(aPluginID, p);
+
+ return p.forget();
+}
+
+#if defined(XP_WIN)
+extern const wchar_t* kPluginWidgetContentParentProperty;
+
+/*static*/
+void ContentParent::SendAsyncUpdate(nsIWidget* aWidget) {
+ if (!aWidget || aWidget->Destroyed()) {
+ return;
+ }
+ // Fire off an async request to the plugin to paint its window
+ HWND hwnd = (HWND)aWidget->GetNativeData(NS_NATIVE_WINDOW);
+ NS_ASSERTION(hwnd, "Expected valid hwnd value.");
+ ContentParent* cp = reinterpret_cast<ContentParent*>(
+ ::GetPropW(hwnd, kPluginWidgetContentParentProperty));
+ if (cp && cp->CanSend()) {
+ Unused << cp->SendUpdateWindow((uintptr_t)hwnd);
+ }
+}
+#endif // defined(XP_WIN)
+
+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;
+
+ nsresult rv;
+ rv = PGMPService::CreateEndpoints(base::GetCurrentProcId(), OtherPid(),
+ &parent, &child);
+ if (NS_FAILED(rv)) {
+ MOZ_ASSERT(false, "CreateEndpoints failed");
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ if (!GMPServiceParent::Create(std::move(parent))) {
+ MOZ_ASSERT(false, "GMPServiceParent::Create failed");
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ if (!SendInitGMPService(std::move(child))) {
+ MOZ_ASSERT(false, "SendInitGMPService failed");
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvLoadPlugin(
+ const uint32_t& aPluginId, nsresult* aRv, uint32_t* aRunID,
+ Endpoint<PPluginModuleParent>* aEndpoint) {
+ *aRv = NS_OK;
+ if (!mozilla::plugins::SetupBridge(aPluginId, this, aRv, aRunID, aEndpoint)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvUngrabPointer(
+ const uint32_t& aTime) {
+#if !defined(MOZ_WIDGET_GTK)
+ MOZ_CRASH("This message only makes sense on GTK platforms");
+#else
+ gdk_pointer_ungrab(aTime);
+ return IPC_OK();
+#endif
+}
+
+static void LogFailedPrincipalValidationInfo(nsIPrincipal* aPrincipal,
+ const char* aMethod) {
+ // no need to do the dance if logging is disabled
+ if (MOZ_LOG_TEST(ContentParent::GetLog(), LogLevel::Error)) {
+ nsAutoCString spec;
+ if (!aPrincipal) {
+ spec.AssignLiteral("NullPtr");
+ } else if (aPrincipal->IsSystemPrincipal()) {
+ spec.AssignLiteral("SystemPrincipal");
+ } else if (aPrincipal->GetIsExpandedPrincipal()) {
+ spec.AssignLiteral("ExpandedPrincipal");
+ } else if (aPrincipal->GetIsContentPrincipal()) {
+ aPrincipal->GetSpec(spec);
+ }
+
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Error,
+ (" Receiving unexpected Principal (%s) within %s", spec.get(),
+ aMethod));
+ }
+}
+
+bool ContentParent::ValidatePrincipal(
+ nsIPrincipal* aPrincipal,
+ const EnumSet<ValidatePrincipalOptions>& aOptions) {
+ // 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")) {
+ 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 (RemoteTypePrefix(mRemoteType) != FISSION_WEB_REMOTE_TYPE) {
+ return true;
+ }
+
+ // Web content can contain extension content frames, so a content process may
+ // send us an extension's principal.
+ auto* addonPolicy = BasePrincipal::Cast(aPrincipal)->AddonPolicy();
+ if (addonPolicy) {
+ return true;
+ }
+
+ // Ensure that the expected site-origin matches the one specified by our
+ // mRemoteTypeIsolationPrincipal.
+ nsAutoCString siteOriginNoSuffix;
+ if (NS_FAILED(aPrincipal->GetSiteOriginNoSuffix(siteOriginNoSuffix))) {
+ return false;
+ }
+ nsAutoCString remoteTypeSiteOriginNoSuffix;
+ if (NS_FAILED(mRemoteTypeIsolationPrincipal->GetSiteOriginNoSuffix(
+ remoteTypeSiteOriginNoSuffix))) {
+ return false;
+ }
+
+ return remoteTypeSiteOriginNoSuffix.Equals(siteOriginNoSuffix);
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvRemovePermission(
+ const IPC::Principal& aPrincipal, const nsCString& aPermissionType,
+ nsresult* aRv) {
+ if (!ValidatePrincipal(aPrincipal)) {
+ LogFailedPrincipalValidationInfo(aPrincipal, __func__);
+ }
+ *aRv = Permissions::RemovePermission(aPrincipal, aPermissionType);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvConnectPluginBridge(
+ const uint32_t& aPluginId, nsresult* aRv,
+ Endpoint<PPluginModuleParent>* aEndpoint) {
+ *aRv = NS_OK;
+ // We don't need to get the run ID for the plugin, since we already got it
+ // in the first call to SetupBridge in RecvLoadPlugin, so we pass in a dummy
+ // pointer and just throw it away.
+ uint32_t dummy = 0;
+ if (!mozilla::plugins::SetupBridge(aPluginId, this, aRv, &dummy, aEndpoint)) {
+ return IPC_FAIL(this, "SetupBridge failed");
+ }
+ return IPC_OK();
+}
+
+/*static*/
+already_AddRefed<RemoteBrowser> ContentParent::CreateBrowser(
+ const TabContext& aContext, Element* aFrameElement,
+ const nsACString& aRemoteType, BrowsingContext* aBrowsingContext,
+ ContentParent* aOpenerContentParent) {
+ AUTO_PROFILER_LABEL("ContentParent::CreateBrowser", OTHER);
+
+ if (!sCanLaunchSubprocesses) {
+ 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);
+ }
+
+ bool isPreloadBrowser = false;
+ nsAutoString isPreloadBrowserStr;
+ if (aFrameElement->GetAttr(kNameSpaceID_None, nsGkAtoms::preloadedState,
+ isPreloadBrowserStr)) {
+ isPreloadBrowser = isPreloadBrowserStr.EqualsLiteral("preloaded");
+ }
+
+ RefPtr<ContentParent> constructorSender;
+ MOZ_RELEASE_ASSERT(XRE_IsParentProcess(),
+ "Cannot allocate BrowserParent in content process");
+ if (aOpenerContentParent && aOpenerContentParent->IsAlive()) {
+ constructorSender = aOpenerContentParent;
+ } else {
+ if (aContext.IsJSPlugin()) {
+ constructorSender = GetNewOrUsedJSPluginProcess(
+ aContext.JSPluginId(), PROCESS_PRIORITY_FOREGROUND);
+ } else {
+ constructorSender = GetNewOrUsedBrowserProcess(
+ remoteType, aBrowsingContext->Group(), PROCESS_PRIORITY_FOREGROUND,
+ isPreloadBrowser);
+ }
+ 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 (docShell->GetAffectPrivateSessionLifetime()) {
+ chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME;
+ }
+
+ 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();
+ 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;
+ }
+
+ 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();
+ }
+}
+
+static LookAndFeelData GetLookAndFeelData() {
+ if (StaticPrefs::widget_remote_look_and_feel_AtStartup()) {
+ return *RemoteLookAndFeel::ExtractData();
+ }
+ return LookAndFeel::GetCache();
+}
+
+void ContentParent::BroadcastThemeUpdate(widget::ThemeChangeKind aKind) {
+ LookAndFeelData lnfData = GetLookAndFeelData();
+
+ for (auto* cp : AllProcesses(eLive)) {
+ Unused << cp->SendThemeChanged(lnfData, aKind);
+ }
+}
+
+/*static */
+void ContentParent::BroadcastMediaCodecsSupportedUpdate(
+ RemoteDecodeIn aLocation,
+ const PDMFactory::MediaCodecsSupported& aSupported) {
+ sCodecsSupported[aLocation] = aSupported;
+ for (auto* cp : AllProcesses(eAll)) {
+ Unused << cp->SendUpdateMediaCodecsSupported(aLocation, aSupported);
+ }
+}
+
+const nsACString& ContentParent::GetRemoteType() const { return mRemoteType; }
+
+void ContentParent::Init() {
+ 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);
+ }
+ }
+
+ AddShutdownBlockers();
+
+ 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 (PresShell::IsAccessibilityActive()) {
+# if defined(XP_WIN)
+ // Don't init content a11y if we detect an incompat version of JAWS in use.
+ if (!mozilla::a11y::Compatibility::IsOldJAWS()) {
+ Unused << SendActivateA11y(
+ ::GetCurrentThreadId(),
+ a11y::AccessibleWrap::GetContentProcessIdFor(ChildID()));
+ }
+# else
+ Unused << SendActivateA11y(0, 0);
+# endif
+ }
+#endif // #ifdef ACCESSIBILITY
+
+#ifdef MOZ_GECKO_PROFILER
+ Unused << SendInitProfiler(ProfilerParent::CreateForProcess(OtherPid()));
+#endif
+
+ // Ensure that the default set of permissions are avaliable in the content
+ // process before we try to load any URIs in it.
+ EnsurePermissionsByKey(""_ns, ""_ns);
+
+ RefPtr<GeckoMediaPluginServiceParent> gmps(
+ GeckoMediaPluginServiceParent::GetSingleton());
+ gmps->UpdateContentProcessGMPCapabilities();
+
+ // 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();
+}
+
+void ContentParent::MaybeBeginShutDown(uint32_t aExpectedBrowserCount,
+ bool aSendShutDown) {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
+ ("MaybeBeginShutdown %p", this));
+ MOZ_ASSERT(NS_IsMainThread());
+
+ if (ManagedPBrowserParent().Count() != aExpectedBrowserCount ||
+ ShouldKeepProcessAlive() || TryToRecycle()) {
+ 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();
+ StartForceKillTimer();
+
+ if (aSendShutDown) {
+ MaybeAsyncSendShutDownMessage();
+ }
+}
+
+void ContentParent::MaybeAsyncSendShutDownMessage() {
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
+ ("MaybeAsyncSendShutDownMessage %p", this));
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(sRecycledE10SProcess != this);
+
+#ifdef DEBUG
+ // Calling this below while the lock is acquired will deadlock.
+ bool shouldKeepProcessAlive = ShouldKeepProcessAlive();
+#endif
+
+ auto lock = mRemoteWorkerActorData.Lock();
+ MOZ_ASSERT_IF(!lock->mCount, !shouldKeepProcessAlive);
+
+ if (lock->mCount) {
+ return;
+ }
+
+ MOZ_ASSERT(!lock->mShutdownStarted);
+ lock->mShutdownStarted = true;
+
+ // In the case of normal shutdown, send a shutdown message to child to
+ // allow it to perform shutdown tasks.
+ GetCurrentSerialEventTarget()->Dispatch(NewRunnableMethod<ShutDownMethod>(
+ "dom::ContentParent::ShutDownProcess", this,
+ &ContentParent::ShutDownProcess, SEND_SHUTDOWN_MESSAGE));
+}
+
+void ContentParent::ShutDownProcess(ShutDownMethod aMethod) {
+ // 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 (CanSend() && !mShutdownPending) {
+ // Stop sending input events with input priority when shutting down.
+ SetInputPriorityEventEnabled(false);
+ if (SendShutdown()) {
+ mShutdownPending = true;
+ // Start the force-kill timer if we haven't already.
+ StartForceKillTimer();
+ }
+ }
+ // If call was not successful, the channel must have been broken
+ // somehow, and we will clean up the error in ActorDestroy.
+ return;
+ }
+
+ using mozilla::dom::quota::QuotaManagerService;
+
+ if (QuotaManagerService* qms = QuotaManagerService::GetOrCreate()) {
+ qms->AbortOperationsForProcess(mChildID);
+ }
+
+ // If Close() fails with an error, we'll end up back in this function, but
+ // with aMethod = CLOSE_CHANNEL_WITH_ERROR.
+
+ if (aMethod == CLOSE_CHANNEL && !mCalledClose) {
+ // Close() can only be called once: It kicks off the destruction
+ // sequence.
+ mCalledClose = true;
+ Close();
+ }
+
+ const ManagedContainer<POfflineCacheUpdateParent>& ocuParents =
+ ManagedPOfflineCacheUpdateParent();
+ for (auto iter = ocuParents.ConstIter(); !iter.Done(); iter.Next()) {
+ RefPtr<mozilla::docshell::OfflineCacheUpdateParent> ocuParent =
+ static_cast<mozilla::docshell::OfflineCacheUpdateParent*>(
+ iter.Get()->GetKey());
+ ocuParent->StopSendingMessagesToChild();
+ }
+
+ // 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();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvFinishShutdown() {
+ // At this point, we already called ShutDownProcess once with
+ // SEND_SHUTDOWN_MESSAGE. To actually close the channel, we call
+ // ShutDownProcess again with CLOSE_CHANNEL.
+ MOZ_ASSERT(mShutdownPending);
+ 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(!sPrivateContent || !sPrivateContent->Contains(this));
+ MOZ_RELEASE_ASSERT(sRecycledE10SProcess != this);
+ if (IsForJSPlugin()) {
+ MOZ_RELEASE_ASSERT(!sJSPluginContentParents ||
+ !sJSPluginContentParents->Get(mJSPluginID));
+ } else {
+ MOZ_RELEASE_ASSERT(
+ !sBrowserContentParents ||
+ !sBrowserContentParents->Contains(mRemoteType) ||
+ !sBrowserContentParents->Get(mRemoteType)->Contains(this) ||
+ !sCanLaunchSubprocesses); // aka in shutdown - avoid timing issues
+
+ for (auto& group : mGroups) {
+ MOZ_RELEASE_ASSERT(group.GetKey()->GetHostProcess(mRemoteType) != this,
+ "still a host process for one of our groups?");
+ }
+ }
+}
+
+void ContentParent::AssertAlive() { MOZ_DIAGNOSTIC_ASSERT(!IsDead()); }
+
+void ContentParent::RemoveFromList() {
+ if (IsForJSPlugin()) {
+ if (sJSPluginContentParents) {
+ sJSPluginContentParents->Remove(mJSPluginID);
+ if (!sJSPluginContentParents->Count()) {
+ sJSPluginContentParents = nullptr;
+ }
+ }
+ return;
+ }
+
+ if (sPrivateContent) {
+ sPrivateContent->RemoveElement(this);
+ if (!sPrivateContent->Length()) {
+ sPrivateContent = nullptr;
+ }
+ }
+
+ 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 (auto& group : mGroups) {
+ group.GetKey()->RemoveHostProcess(this);
+ }
+
+ StopRecycling(/* 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();
+
+ PreallocatedProcessManager::Erase(this);
+
+#ifdef MOZ_WIDGET_ANDROID
+ if (IsAlive()) {
+ // We're intentionally killing the content process at this point to ensure
+ // that we never have a "dead" content process sitting around and occupying
+ // an Android Service.
+ nsCOMPtr<nsIEventTarget> launcherThread(GetIPCLauncher());
+ MOZ_ASSERT(launcherThread);
+
+ auto procType = java::GeckoProcessType::CONTENT();
+ auto selector =
+ java::GeckoProcessManager::Selector::New(procType, OtherPid());
+
+ launcherThread->Dispatch(NS_NewRunnableFunction(
+ "ContentParent::MarkAsDead",
+ [selector =
+ java::GeckoProcessManager::Selector::GlobalRef(selector)]() {
+ java::GeckoProcessManager::ShutdownProcess(selector);
+ }));
+ }
+#endif
+
+ if (mScriptableHelper) {
+ static_cast<ScriptableCPInfo*>(mScriptableHelper.get())->ProcessDied();
+ mScriptableHelper = nullptr;
+ }
+
+ mLifecycleState = LifecycleState::DEAD;
+}
+
+void ContentParent::OnChannelError() {
+ RefPtr<ContentParent> kungFuDeathGrip(this);
+ PContentParent::OnChannelError();
+}
+
+void ContentParent::OnChannelConnected(int32_t pid) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ SetOtherProcessId(pid);
+}
+
+void ContentParent::ProcessingError(Result aCode, const char* aReason) {
+ if (MsgDropped == aCode) {
+ return;
+ }
+#ifndef FUZZING
+ // Other errors are big deals.
+ KillHard(aReason);
+#endif
+}
+
+void ContentParent::ActorDestroy(ActorDestroyReason why) {
+ MOZ_RELEASE_ASSERT(mSelfRef);
+
+ if (mForceKillTimer) {
+ mForceKillTimer->Cancel();
+ mForceKillTimer = nullptr;
+ }
+
+ // Signal shutdown completion regardless of error state, so we can
+ // finish waiting in the xpcom-shutdown/profile-before-change observer.
+ RemoveShutdownBlockers();
+
+ if (mHangMonitorActor) {
+ ProcessHangMonitor::RemoveProcess(mHangMonitorActor);
+ mHangMonitorActor = nullptr;
+ }
+
+ RefPtr<FileSystemSecurity> fss = FileSystemSecurity::Get();
+ if (fss) {
+ fss->Forget(ChildID());
+ }
+
+ if (why == NormalShutdown && !mCalledClose) {
+ // If we shut down normally but haven't called Close, assume somebody
+ // else called Close on us. In that case, we still need to call
+ // ShutDownProcess below to perform other necessary clean up.
+ mCalledClose = true;
+ }
+
+ // Make sure we always clean up.
+ ShutDownProcess(why == NormalShutdown ? CLOSE_CHANNEL
+ : CLOSE_CHANNEL_WITH_ERROR);
+
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ size_t length = ArrayLength(sObserverTopics);
+ for (size_t i = 0; i < length; ++i) {
+ obs->RemoveObserver(static_cast<nsIObserver*>(this), sObserverTopics[i]);
+ }
+ }
+
+ // remove the global remote preferences observers
+ Preferences::RemoveObserver(this, "");
+ gfxVars::RemoveReceiver(this);
+
+ if (GPUProcessManager* gpu = GPUProcessManager::Get()) {
+ // Note: the manager could have shutdown already.
+ gpu->RemoveListener(this);
+ }
+
+ RecvRemoveGeolocationListener();
+
+ mConsoleService = nullptr;
+
+ // 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 (mCrashReporter->GenerateCrashReport(OtherPid())) {
+ // Propagate `isLikelyOOM`.
+ Unused << props->SetPropertyAsBool(u"isLikelyOOM"_ns,
+ mCrashReporter->IsLikelyOOM());
+ }
+ }
+
+ 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.
+ nsCOMPtr<nsIUserIdleService> idleService =
+ do_GetService("@mozilla.org/widget/useridleservice;1");
+ MOZ_ASSERT(idleService);
+ RefPtr<ParentIdleListener> listener;
+ for (int32_t i = mIdleListeners.Length() - 1; i >= 0; --i) {
+ listener = static_cast<ParentIdleListener*>(mIdleListeners[i].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?
+ 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();
+ cpm->RemoveContentProcess(this->ChildID());
+
+ if (mDriverCrashGuard) {
+ mDriverCrashGuard->NotifyCrashed();
+ }
+
+ // Unregister all the BlobURLs registered by the ContentChild.
+ for (uint32_t i = 0; i < mBlobURLs.Length(); ++i) {
+ BlobURLProtocolHandler::RemoveDataEntry(mBlobURLs[i]);
+ }
+
+ mBlobURLs.Clear();
+
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+ a11y::AccessibleWrap::ReleaseContentProcessIdFor(ChildID());
+#endif
+
+#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
+ AssertNotInPool();
+#endif
+
+ // As this process is going away, ensure that every BrowsingContext hosted by
+ // it has been detached, and every BrowsingContextGroup has been fully
+ // unsubscribed.
+ BrowsingContext::DiscardFromContentParent(this);
+
+ nsTHashtable<nsRefPtrHashKey<BrowsingContextGroup>> groups;
+ mGroups.SwapElements(groups);
+ for (auto& group : groups) {
+ group.GetKey()->Unsubscribe(this);
+ }
+ MOZ_DIAGNOSTIC_ASSERT(mGroups.IsEmpty());
+}
+
+void ContentParent::ActorDealloc() { mSelfRef = nullptr; }
+
+bool ContentParent::TryToRecycle() {
+ // 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.
+ StopRecycling(/* 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::StopRecycling(bool aForeground) {
+ if (sRecycledE10SProcess != this) {
+ return;
+ }
+
+ sRecycledE10SProcess = nullptr;
+ if (aForeground) {
+ ProcessPriorityManager::SetProcessPriority(this,
+ PROCESS_PRIORITY_FOREGROUND);
+ }
+}
+
+bool ContentParent::HasActiveWorkerOrJSPlugin() {
+ if (IsForJSPlugin()) {
+ return true;
+ }
+
+ // If we have active workers, we need to stay alive.
+ {
+ const auto lock = mRemoteWorkerActorData.Lock();
+ if (lock->mCount) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ContentParent::ShouldKeepProcessAlive() {
+ if (HasActiveWorkerOrJSPlugin()) {
+ return true;
+ }
+
+ if (mNumKeepaliveCalls > 0) {
+ return true;
+ }
+
+ if (IsLaunching()) {
+ return true;
+ }
+
+ // If we have already been marked as dead, don't prevent shutdown.
+ if (IsDead()) {
+ return false;
+ }
+
+ if (!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() {
+ // 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() {
+ // Something wants to keep this content process alive.
+ ++mNumKeepaliveCalls;
+}
+
+void ContentParent::RemoveKeepAlive() {
+ MOZ_DIAGNOSTIC_ASSERT(mNumKeepaliveCalls > 0);
+ --mNumKeepaliveCalls;
+
+ MaybeBeginShutDown();
+}
+
+void ContentParent::StartForceKillTimer() {
+ if (mForceKillTimer || !CanSend()) {
+ return;
+ }
+
+ NotifyImpendingShutdown();
+
+ int32_t timeoutSecs = StaticPrefs::dom_ipc_tabs_shutdownTimeoutSecs();
+ if (timeoutSecs > 0) {
+ NS_NewTimerWithFuncCallback(getter_AddRefs(mForceKillTimer),
+ ContentParent::ForceKillTimerCallback, this,
+ timeoutSecs * 1000, nsITimer::TYPE_ONE_SHOT,
+ "dom::ContentParent::StartForceKillTimer");
+ MOZ_ASSERT(mForceKillTimer);
+ }
+}
+
+void ContentParent::NotifyTabDestroyed(const TabId& aTabId,
+ bool aNotifiedDestroying) {
+ if (aNotifiedDestroying) {
+ --mNumDestroyingTabs;
+ }
+
+ nsTArray<PContentPermissionRequestParent*> parentArray =
+ nsContentPermissionUtils::GetContentPermissionRequestParentById(aTabId);
+
+ // Need to close undeleted ContentPermissionRequestParents before tab is
+ // closed.
+ for (auto& permissionRequestParent : parentArray) {
+ Unused << PContentPermissionRequestParent::Send__delete__(
+ permissionRequestParent);
+ }
+
+ // There can be more than one PBrowser for a given app process
+ // because of popup windows. When the last one closes, shut
+ // us down.
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
+ ("NotifyTabDestroyed %p", this));
+
+ MaybeBeginShutDown(/* aExpectedBrowserCount */ 1);
+}
+
+TestShellParent* ContentParent::CreateTestShell() {
+ return static_cast<TestShellParent*>(SendPTestShellConstructor());
+}
+
+bool ContentParent::DestroyTestShell(TestShellParent* aTestShell) {
+ return PTestShellParent::Send__delete__(aTestShell);
+}
+
+TestShellParent* ContentParent::GetTestShellSingleton() {
+ PTestShellParent* p = LoneManagedOrNullAsserts(ManagedPTestShellParent());
+ return static_cast<TestShellParent*>(p);
+}
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+// Append the sandbox command line parameters that are not static. i.e.,
+// parameters that can be different for different child processes.
+void ContentParent::AppendDynamicSandboxParams(
+ std::vector<std::string>& aArgs) {
+ // For file content processes
+ if (GetRemoteType() == FILE_REMOTE_TYPE) {
+ MacSandboxInfo::AppendFileAccessParam(aArgs, true);
+ }
+}
+
+// Generate the static sandbox command line parameters and store
+// them in the provided params vector to be used each time a new
+// content process is launched.
+static void CacheSandboxParams(std::vector<std::string>& aCachedParams) {
+ // This must only be called once and we should
+ // be starting with an empty list of parameters.
+ MOZ_ASSERT(aCachedParams.empty());
+
+ MacSandboxInfo info;
+ info.type = MacSandboxType_Content;
+ info.level = GetEffectiveContentSandboxLevel();
+
+ // Sandbox logging
+ if (Preferences::GetBool("security.sandbox.logging.enabled") ||
+ PR_GetEnv("MOZ_SANDBOX_LOGGING")) {
+ info.shouldLog = true;
+ }
+
+ // Audio access
+ if (!StaticPrefs::media_cubeb_sandbox()) {
+ info.hasAudio = true;
+ }
+
+ // Windowserver access
+ if (!Preferences::GetBool(
+ "security.sandbox.content.mac.disconnect-windowserver")) {
+ 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 development builds,
+ // these are used to whitelist the repo dir and object dir respectively.
+ nsresult rv;
+ if (mozilla::IsDevelopmentBuild()) {
+ // 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);
+
+ if (!ContentProcessManager::GetSingleton()) {
+ // Shutdown has begun, we shouldn't spawn any more child processes.
+ return false;
+ }
+
+ std::vector<std::string> extraArgs;
+ extraArgs.push_back("-childID");
+ char idStr[21];
+ SprintfLiteral(idStr, "%" PRId64, static_cast<uint64_t>(mChildID));
+ extraArgs.push_back(idStr);
+ extraArgs.push_back(IsForBrowser() ? "-isForBrowser" : "-notForBrowser");
+
+ // 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()) {
+ MarkAsDead();
+ return false;
+ }
+ mPrefSerializer->AddSharedPrefCmdLineArgs(*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) {
+ extraArgs.push_back("-safeMode");
+ }
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+ if (IsContentSandboxEnabled()) {
+ AppendSandboxParams(extraArgs);
+ mSubprocess->DisableOSActivityMode();
+ }
+#endif
+
+ nsCString parentBuildID(mozilla::PlatformBuildID());
+ extraArgs.push_back("-parentBuildID");
+ extraArgs.push_back(parentBuildID.get());
+
+ // See also ActorDealloc.
+ mSelfRef = this;
+ mLaunchYieldTS = TimeStamp::Now();
+ return mSubprocess->AsyncLaunch(std::move(extraArgs));
+}
+
+void ContentParent::LaunchSubprocessReject() {
+ NS_ERROR("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();
+}
+
+bool ContentParent::LaunchSubprocessResolve(bool aIsSync,
+ ProcessPriority aPriority) {
+ AUTO_PROFILER_LABEL("ContentParent::LaunchSubprocess::resolve", OTHER);
+
+ // Take the pending IPC channel. This channel will be used to open the raw IPC
+ // connection between this process and the launched content process.
+ UniquePtr<IPC::Channel> channel = mSubprocess->TakeChannel();
+ if (!channel) {
+ // We don't have a channel, so this method must've been called already.
+ MOZ_ASSERT(sCreatedFirstContentProcess);
+ MOZ_ASSERT(!mPrefSerializer);
+ MOZ_ASSERT(mLifecycleState != LifecycleState::LAUNCHING);
+ return true;
+ }
+
+ // Now that communication with the child is complete, we can cleanup
+ // the preference serializer.
+ mPrefSerializer = nullptr;
+
+ const auto launchResumeTS = TimeStamp::Now();
+#ifdef MOZ_GECKO_PROFILER
+ if (profiler_thread_is_being_profiled()) {
+ 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);
+ }
+#endif
+
+ if (!sCreatedFirstContentProcess) {
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ obs->NotifyObservers(nullptr, "ipc:first-content-process-created", nullptr);
+ sCreatedFirstContentProcess = true;
+ }
+
+ base::ProcessId procId =
+ base::GetProcId(mSubprocess->GetChildProcessHandle());
+ Open(std::move(channel), procId);
+
+ ContentProcessManager::GetSingleton()->AddContentProcess(this);
+
+#ifdef MOZ_CODE_COVERAGE
+ Unused << SendShareCodeCoverageMutex(
+ CodeCoverageHandler::Get()->GetMutexHandle(procId));
+#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()) {
+ 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()));
+ }
+ 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)) {
+ return false;
+ }
+ const bool ok = mSubprocess->WaitForProcessHandle();
+ if (ok && LaunchSubprocessResolve(/* aIsSync = */ true, aInitialPriority)) {
+ return true;
+ }
+ LaunchSubprocessReject();
+ return false;
+}
+
+RefPtr<ContentParent::LaunchPromise> ContentParent::LaunchSubprocessAsync(
+ hal::ProcessPriority aInitialPriority) {
+ // We've started an async content process launch.
+ Telemetry::Accumulate(Telemetry::CONTENT_PROCESS_LAUNCH_IS_SYNC, 0);
+
+ if (!BeginSubprocessLaunch(aInitialPriority)) {
+ // Launch aborted because of shutdown. Bailout.
+ LaunchSubprocessReject();
+ return LaunchPromise::CreateAndReject(LaunchError(), __func__);
+ }
+
+ // Otherwise, wait until the process is ready.
+ RefPtr<ProcessHandlePromise> ready = mSubprocess->WhenProcessHandleReady();
+ RefPtr<ContentParent> self = this;
+ mLaunchYieldTS = TimeStamp::Now();
+
+ return ready->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [self, aInitialPriority](
+ const ProcessHandlePromise::ResolveOrRejectValue& aValue) {
+ if (aValue.IsResolve() &&
+ self->LaunchSubprocessResolve(/* aIsSync = */ false,
+ aInitialPriority)) {
+ return LaunchPromise::CreateAndResolve(self, __func__);
+ }
+ self->LaunchSubprocessReject();
+ return LaunchPromise::CreateAndReject(LaunchError(), __func__);
+ });
+}
+
+ContentParent::ContentParent(const nsACString& aRemoteType, int32_t aJSPluginID)
+ : mSelfRef(nullptr),
+ mSubprocess(nullptr),
+ mLaunchTS(TimeStamp::Now()),
+ mLaunchYieldTS(mLaunchTS),
+ mActivateTS(mLaunchTS),
+ mIsAPreallocBlocker(false),
+ mRemoteType(aRemoteType),
+ mChildID(gContentChildID++),
+ mGeolocationWatchID(-1),
+ mJSPluginID(aJSPluginID),
+ mRemoteWorkerActorData("ContentParent::mRemoteWorkerActorData"),
+ mNumDestroyingTabs(0),
+ mNumKeepaliveCalls(0),
+ mLifecycleState(LifecycleState::LAUNCHING),
+ mIsForBrowser(!mRemoteType.IsEmpty()),
+ mCalledClose(false),
+ mCalledKillHard(false),
+ mCreatedPairedMinidumps(false),
+ mShutdownPending(false),
+ mIsRemoteInputEventQueueEnabled(false),
+ mIsInputPriorityEventEnabled(false),
+ mIsInPool(false),
+ mHangMonitorActor(nullptr) {
+ MOZ_DIAGNOSTIC_ASSERT(!IsForJSPlugin(),
+ "XXX(nika): How are we creating a JSPlugin?");
+
+ mRemoteTypeIsolationPrincipal =
+ CreateRemoteTypeIsolationPrincipal(aRemoteType);
+
+ // Insert ourselves into the global linked list of ContentParent objects.
+ if (!sContentParents) {
+ sContentParents = MakeUnique<LinkedList<ContentParent>>();
+ }
+ sContentParents->insertBack(this);
+
+ mMessageManager = nsFrameMessageManager::NewProcessMessageManager(true);
+
+#if defined(XP_WIN)
+ if (XRE_IsParentProcess()) {
+ audio::AudioNotificationSender::Init();
+ }
+ // Request Windows message deferral behavior on our side of the PContent
+ // channel. Generally only applies to the situation where we get caught in
+ // a deadlock with the plugin process when sending CPOWs.
+ GetIPCChannel()->SetChannelFlags(
+ MessageChannel::REQUIRE_DEFERRED_MESSAGE_PROTECTION);
+#endif
+
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+ bool isFile = mRemoteType == FILE_REMOTE_TYPE;
+ mSubprocess = new GeckoChildProcessHost(GeckoProcessType_Content, isFile);
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Verbose,
+ ("CreateSubprocess: ContentParent %p mSubprocess %p handle %" PRIuPTR,
+ this, mSubprocess,
+ mSubprocess ? (uintptr_t)mSubprocess->GetChildProcessHandle() : -1));
+
+ // This is safe to do in the constructor, as it doesn't take a strong
+ // reference.
+ mScriptableHelper = new ScriptableCPInfo(this);
+}
+
+ContentParent::~ContentParent() {
+ if (mForceKillTimer) {
+ mForceKillTimer->Cancel();
+ }
+
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+ 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::Shutdown)) {
+ 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());
+ }
+
+ 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());
+
+ nsCOMPtr<nsIClipboard> clipboard(
+ do_GetService("@mozilla.org/widget/clipboard;1"));
+ MOZ_ASSERT(clipboard, "No clipboard?");
+
+ rv = clipboard->SupportsSelectionClipboard(
+ &xpcomInit.clipboardCaps().supportsSelectionClipboard());
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+ rv = clipboard->SupportsFindClipboard(
+ &xpcomInit.clipboardCaps().supportsFindClipboard());
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+ // 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::RootedValue 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.
+ nsTArray<SystemFontListEntry> fontList;
+ gfxPlatform::GetPlatform()->ReadSystemFontList(&fontList);
+
+ LookAndFeelData lnfData = GetLookAndFeelData();
+
+ // 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 = services::GetGfxInfo();
+ if (gfxInfo) {
+ GfxInfoBase* gfxInfoRaw = static_cast<GfxInfoBase*>(gfxInfo.get());
+ xpcomInit.gfxFeatureStatus() = gfxInfoRaw->GetAllFeatures();
+ }
+
+#ifdef XP_WIN
+ xpcomInit.systemParameters() =
+ widget::WinContentSystemParameters::GetSingleton()->GetParentValues();
+#endif
+
+ DataStorage::GetAllChildProcessData(xpcomInit.dataStorage());
+
+ // Send the dynamic scalar definitions to the new process.
+ TelemetryIPC::GetDynamicScalarDefinitions(xpcomInit.dynamicScalarDefs());
+
+ for (auto const& [location, supported] : sCodecsSupported) {
+ Unused << SendUpdateMediaCodecsSupported(location, supported);
+ }
+
+ // Must send screen info before send initialData
+ ScreenManager& screenManager = ScreenManager::GetSingleton();
+ screenManager.CopyScreensToRemote(this);
+
+ // Send the UA sheet shared memory buffer and the address it is mapped at.
+ Maybe<SharedMemoryHandle> sharedUASheetHandle;
+ uintptr_t sharedUASheetAddress = sheetCache->GetSharedMemoryAddress();
+
+ SharedMemoryHandle handle;
+ if (sheetCache->ShareToProcess(OtherPid(), &handle)) {
+ sharedUASheetHandle.emplace(handle);
+ } else {
+ sharedUASheetAddress = 0;
+ }
+
+ Unused << SendSetXPCOMProcessAttributes(
+ xpcomInit, initialData, lnfData, fontList, sharedUASheetHandle,
+ sharedUASheetAddress, sharedFontListBlocks);
+
+ 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 =
+ services::GetStringBundleService();
+ 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);
+
+ ScriptPreloader::InitContentChild(*this);
+
+ // Initialize the message manager (and load delayed scripts) now that we
+ // have established communications with the child.
+ mMessageManager->InitWithCallback(this);
+ mMessageManager->SetOsPid(Pid());
+
+ // Set the subprocess's priority. We do this early on because we're likely
+ // /lowering/ the process's CPU and memory priority, which it has inherited
+ // from this process.
+ //
+ // This call can cause us to send IPC messages to the child process, so it
+ // must come after the Open() call above.
+ ProcessPriorityManager::SetProcessPriority(this, aInitialPriority);
+
+ // NB: internally, this will send an IPC message to the child
+ // process to get it to create the CompositorBridgeChild. This
+ // message goes through the regular IPC queue for this
+ // channel, so delivery will happen-before any other messages
+ // we send. The CompositorBridgeChild must be created before any
+ // PBrowsers are created, because they rely on the Compositor
+ // already being around. (Creation is async, so can't happen
+ // on demand.)
+ GPUProcessManager* gpm = GPUProcessManager::Get();
+
+ Endpoint<PCompositorManagerChild> compositor;
+ Endpoint<PImageBridgeChild> imageBridge;
+ Endpoint<PVRManagerChild> vrBridge;
+ Endpoint<PRemoteDecoderManagerChild> videoManager;
+ AutoTArray<uint32_t, 3> namespaces;
+
+ if (!gpm->CreateContentBridges(OtherPid(), &compositor, &imageBridge,
+ &vrBridge, &videoManager, &namespaces)) {
+ // This can fail if we've already started shutting down the compositor
+ // thread. See Bug 1562763 comment 8.
+ return false;
+ }
+
+ Unused << SendInitRendering(std::move(compositor), std::move(imageBridge),
+ std::move(vrBridge), std::move(videoManager),
+ namespaces);
+
+ gpm->AddListener(this);
+
+ nsStyleSheetService* sheetService = nsStyleSheetService::GetInstance();
+ if (sheetService) {
+ // This looks like a lot of work, but in a normal browser session we just
+ // send two loads.
+ //
+ // The URIs of the Gecko and Servo sheets should be the same, so it
+ // shouldn't matter which we look at.
+
+ for (StyleSheet* sheet : *sheetService->AgentStyleSheets()) {
+ Unused << SendLoadAndRegisterSheet(sheet->GetSheetURI(),
+ nsIStyleSheetService::AGENT_SHEET);
+ }
+
+ for (StyleSheet* sheet : *sheetService->UserStyleSheets()) {
+ Unused << SendLoadAndRegisterSheet(sheet->GetSheetURI(),
+ nsIStyleSheetService::USER_SHEET);
+ }
+
+ for (StyleSheet* sheet : *sheetService->AuthorStyleSheets()) {
+ Unused << SendLoadAndRegisterSheet(sheet->GetSheetURI(),
+ nsIStyleSheetService::AUTHOR_SHEET);
+ }
+ }
+
+#ifdef MOZ_SANDBOX
+ bool shouldSandbox = true;
+ Maybe<FileDescriptor> brokerFd;
+ // XXX: Checking the pref here makes it possible to enable/disable sandboxing
+ // during an active session. Currently the pref is only used for testing
+ // purpose. If the decision is made to permanently rely on the pref, this
+ // should be changed so that it is required to restart firefox for the change
+ // of value to take effect. Always send SetProcessSandbox message on macOS.
+# if !defined(XP_MACOSX)
+ shouldSandbox = IsContentSandboxEnabled();
+# endif
+
+# ifdef XP_LINUX
+ if (shouldSandbox) {
+ MOZ_ASSERT(!mSandboxBroker);
+ bool isFileProcess = mRemoteType == FILE_REMOTE_TYPE;
+ UniquePtr<SandboxBroker::Policy> policy =
+ sSandboxBrokerPolicyFactory->GetContentPolicy(Pid(), isFileProcess);
+ if (policy) {
+ brokerFd = Some(FileDescriptor());
+ mSandboxBroker =
+ SandboxBroker::Create(std::move(policy), Pid(), brokerFd.ref());
+ if (!mSandboxBroker) {
+ KillHard("SandboxBroker::Create failed");
+ return false;
+ }
+ MOZ_ASSERT(brokerFd.ref().IsValid());
+ }
+ }
+# endif
+ if (shouldSandbox && !SendSetProcessSandbox(brokerFd)) {
+ KillHard("SandboxInitFailed");
+ }
+#endif
+
+ if (!ServiceWorkerParentInterceptEnabled()) {
+ RefPtr<ServiceWorkerRegistrar> swr = ServiceWorkerRegistrar::Get();
+ MOZ_ASSERT(swr);
+
+ nsTArray<ServiceWorkerRegistrationData> registrations;
+ swr->GetRegistrations(registrations);
+
+ // Send down to the content process the permissions for each of the
+ // registered service worker scopes.
+ for (auto& registration : registrations) {
+ auto principalOrErr = PrincipalInfoToPrincipal(registration.principal());
+ if (principalOrErr.isOk()) {
+ nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap();
+ TransmitPermissionsForPrincipal(principal);
+ }
+ }
+
+ Unused << SendInitServiceWorkers(ServiceWorkerConfiguration(registrations));
+ }
+
+ {
+ nsTArray<BlobURLRegistrationData> registrations;
+ BlobURLProtocolHandler::ForEachBlobURL(
+ [&](BlobImpl* aBlobImpl, nsIPrincipal* aPrincipal,
+ const Maybe<nsID>& aAgentClusterId, const nsACString& aURI,
+ bool aRevoked) {
+ nsAutoCString origin;
+ nsresult rv = aPrincipal->GetOrigin(origin);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return false;
+ }
+
+ // 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 (!StringBeginsWith(origin, "moz-extension://"_ns) &&
+ !aPrincipal->IsSystemPrincipal()) {
+ return true;
+ }
+
+ IPCBlob ipcBlob;
+ rv = IPCBlobUtils::Serialize(aBlobImpl, this, ipcBlob);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return false;
+ }
+
+ registrations.AppendElement(BlobURLRegistrationData(
+ nsCString(aURI), ipcBlob, aPrincipal, aAgentClusterId, aRevoked));
+
+ rv = TransmitPermissionsForPrincipal(aPrincipal);
+ Unused << NS_WARN_IF(NS_FAILED(rv));
+ return true;
+ });
+
+ if (!registrations.IsEmpty()) {
+ Unused << SendInitBlobURLs(registrations);
+ }
+ }
+
+ // Send down { Parent, Window }ActorOptions at startup to content process.
+ RefPtr<JSActorService> actorSvc = JSActorService::GetSingleton();
+ if (actorSvc) {
+ nsTArray<JSProcessActorInfo> contentInfos;
+ actorSvc->GetJSProcessActorInfos(contentInfos);
+
+ nsTArray<JSWindowActorInfo> windowInfos;
+ actorSvc->GetJSWindowActorInfos(windowInfos);
+
+ Unused << SendInitJSActorInfos(contentInfos, windowInfos);
+ }
+
+ // Begin subscribing to any BrowsingContextGroups which were hosted by this
+ // process before it finished launching.
+ for (auto& group : mGroups) {
+ group.GetKey()->Subscribe(this);
+ }
+
+ // Start up nsPluginHost and run FindPlugins to cache the plugin list.
+ // If this isn't our first content process, just send over cached list.
+ RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
+ pluginHost->SendPluginsToContent(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 || !mSubprocess->GetChildProcessHandle()) {
+ return -1;
+ }
+ return base::GetProcId(mSubprocess->GetChildProcessHandle());
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvGetGfxVars(
+ nsTArray<GfxVarUpdate>* aVars) {
+ // Ensure gfxVars is initialized (for xpcshell tests).
+ gfxVars::Initialize();
+
+ *aVars = gfxVars::FetchNonDefaultVars();
+
+ // Now that content has initialized gfxVars, we can start listening for
+ // updates.
+ gfxVars::AddReceiver(this);
+ return IPC_OK();
+}
+
+void ContentParent::OnCompositorUnexpectedShutdown() {
+ GPUProcessManager* gpm = GPUProcessManager::Get();
+
+ Endpoint<PCompositorManagerChild> compositor;
+ Endpoint<PImageBridgeChild> imageBridge;
+ Endpoint<PVRManagerChild> vrBridge;
+ Endpoint<PRemoteDecoderManagerChild> videoManager;
+ AutoTArray<uint32_t, 3> namespaces;
+
+ DebugOnly<bool> opened =
+ gpm->CreateContentBridges(OtherPid(), &compositor, &imageBridge,
+ &vrBridge, &videoManager, &namespaces);
+ MOZ_ASSERT(opened);
+
+ Unused << SendReinitRendering(std::move(compositor), std::move(imageBridge),
+ std::move(vrBridge), std::move(videoManager),
+ namespaces);
+}
+
+void ContentParent::OnCompositorDeviceReset() {
+ Unused << SendReinitRenderingForDeviceReset();
+}
+
+void ContentParent::MaybeEnableRemoteInputEventQueue() {
+ MOZ_ASSERT(!mIsRemoteInputEventQueueEnabled);
+ if (!IsInputEventQueueSupported()) {
+ return;
+ }
+ mIsRemoteInputEventQueueEnabled = true;
+ Unused << SendSetInputEventQueueEnabled();
+ SetInputPriorityEventEnabled(true);
+}
+
+void ContentParent::SetInputPriorityEventEnabled(bool aEnabled) {
+ if (!IsInputEventQueueSupported() || !mIsRemoteInputEventQueueEnabled ||
+ mIsInputPriorityEventEnabled == aEnabled) {
+ return;
+ }
+ mIsInputPriorityEventEnabled = aEnabled;
+ // Send IPC messages to flush the pending events in the input event queue and
+ // the normal event queue. See PContent.ipdl for more details.
+ Unused << SendSuspendInputEventQueue();
+ Unused << SendFlushInputEventQueue();
+ Unused << SendResumeInputEventQueue();
+}
+
+/*static*/
+bool ContentParent::IsInputEventQueueSupported() {
+ static bool sSupported = false;
+ static bool sInitialized = false;
+ if (!sInitialized) {
+ MOZ_ASSERT(Preferences::IsServiceAvailable());
+ sSupported = Preferences::GetBool("input_event_queue.supported", false);
+ sInitialized = true;
+ }
+ return sSupported;
+}
+
+void ContentParent::OnVarChanged(const GfxVarUpdate& aVar) {
+ if (!CanSend()) {
+ return;
+ }
+ Unused << SendVarUpdate(aVar);
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvSetClipboard(
+ const IPCDataTransfer& aDataTransfer, const bool& aIsPrivateData,
+ const IPC::Principal& aRequestingPrincipal,
+ const nsContentPolicyType& aContentPolicyType,
+ const int32_t& aWhichClipboard) {
+ if (!ValidatePrincipal(aRequestingPrincipal,
+ {ValidatePrincipalOptions::AllowNullPtr})) {
+ LogFailedPrincipalValidationInfo(aRequestingPrincipal, __func__);
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
+ NS_ENSURE_SUCCESS(rv, IPC_OK());
+
+ nsCOMPtr<nsITransferable> trans =
+ do_CreateInstance("@mozilla.org/widget/transferable;1", &rv);
+ NS_ENSURE_SUCCESS(rv, IPC_OK());
+ trans->Init(nullptr);
+
+ rv = nsContentUtils::IPCTransferableToTransferable(
+ aDataTransfer, aIsPrivateData, aRequestingPrincipal, aContentPolicyType,
+ trans, this, nullptr);
+ NS_ENSURE_SUCCESS(rv, IPC_OK());
+
+ clipboard->SetData(trans, nullptr, aWhichClipboard);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvGetClipboard(
+ nsTArray<nsCString>&& aTypes, const int32_t& aWhichClipboard,
+ IPCDataTransfer* aDataTransfer) {
+ 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);
+ // 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);
+
+ for (uint32_t t = 0; t < aTypes.Length(); t++) {
+ trans->AddDataFlavor(aTypes[t].get());
+ }
+
+ clipboard->GetData(trans, aWhichClipboard);
+ nsContentUtils::TransferableToIPCTransferable(trans, aDataTransfer, true,
+ nullptr, 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();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvPlaySound(nsIURI* aURI) {
+ // If the check here fails, it can only mean that this message was spoofed.
+ if (!aURI || !aURI->SchemeIs("chrome")) {
+ // PlaySound only accepts a valid chrome URI.
+ return IPC_FAIL_NO_REASON(this);
+ }
+ nsCOMPtr<nsIURL> soundURL(do_QueryInterface(aURI));
+ if (!soundURL) {
+ return IPC_OK();
+ }
+
+ nsresult rv;
+ nsCOMPtr<nsISound> sound(do_GetService(NS_SOUND_CID, &rv));
+ NS_ENSURE_SUCCESS(rv, IPC_OK());
+
+ sound->Play(soundURL);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvBeep() {
+ nsresult rv;
+ nsCOMPtr<nsISound> sound(do_GetService(NS_SOUND_CID, &rv));
+ NS_ENSURE_SUCCESS(rv, IPC_OK());
+
+ sound->Beep();
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvPlayEventSound(
+ const uint32_t& aEventId) {
+ nsresult rv;
+ nsCOMPtr<nsISound> sound(do_GetService(NS_SOUND_CID, &rv));
+ NS_ENSURE_SUCCESS(rv, IPC_OK());
+
+ sound->PlayEventSound(aEventId);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvGetIconForExtension(
+ const nsCString& 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) {
+ PreallocatedProcessManager::RemoveBlocker(mRemoteType, this);
+ mIsAPreallocBlocker = false;
+ }
+ 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
+
+// Async shutdown blocker
+NS_IMETHODIMP
+ContentParent::BlockShutdown(nsIAsyncShutdownClient* aClient) {
+ // Make sure that our process will get scheduled.
+ ProcessPriorityManager::SetProcessPriority(this, PROCESS_PRIORITY_FOREGROUND);
+
+ // Okay to call ShutDownProcess multiple times.
+ ShutDownProcess(SEND_SHUTDOWN_MESSAGE);
+ MarkAsDead();
+
+ 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 StaticRefPtr<nsIAsyncShutdownClient> sXPCOMShutdownClient;
+static StaticRefPtr<nsIAsyncShutdownClient> sProfileBeforeChangeClient;
+
+static void InitClients() {
+ if (!sXPCOMShutdownClient) {
+ nsresult rv;
+ nsCOMPtr<nsIAsyncShutdownService> svc = services::GetAsyncShutdownService();
+
+ nsCOMPtr<nsIAsyncShutdownClient> client;
+ rv = svc->GetXpcomWillShutdown(getter_AddRefs(client));
+ sXPCOMShutdownClient = client.forget();
+ ClearOnShutdown(&sXPCOMShutdownClient);
+ MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv), "XPCOMShutdown shutdown blocker");
+
+ rv = svc->GetProfileBeforeChange(getter_AddRefs(client));
+ sProfileBeforeChangeClient = client.forget();
+ ClearOnShutdown(&sProfileBeforeChangeClient);
+ MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv),
+ "profileBeforeChange shutdown blocker");
+ }
+}
+
+void ContentParent::AddShutdownBlockers() {
+ InitClients();
+
+ sXPCOMShutdownClient->AddBlocker(
+ this, NS_LITERAL_STRING_FROM_CSTRING(__FILE__), __LINE__, u""_ns);
+ sProfileBeforeChangeClient->AddBlocker(
+ this, NS_LITERAL_STRING_FROM_CSTRING(__FILE__), __LINE__, u""_ns);
+}
+
+void ContentParent::RemoveShutdownBlockers() {
+ Unused << sXPCOMShutdownClient->RemoveBlocker(this);
+ Unused << sProfileBeforeChangeClient->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")) {
+ // A pref changed. If it is useful to do so, inform child processes.
+ if (!ShouldSyncPreference(aData)) {
+ return NS_OK;
+ }
+
+ // We know prefs are ASCII here.
+ NS_LossyConvertUTF16toASCII strData(aData);
+
+ Pref pref(strData, /* isLocked */ false, Nothing(), Nothing());
+ Preferences::GetPreference(&pref);
+ if (IsInitialized()) {
+ MOZ_ASSERT(mQueuedPrefs.IsEmpty());
+ if (!SendPreferenceUpdate(pref)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ } else {
+ MOZ_ASSERT(!IsDead());
+ mQueuedPrefs.AppendElement(pref);
+ }
+ }
+
+ if (!IsAlive()) {
+ return NS_OK;
+ }
+
+ // listening for memory pressure event
+ if (!strcmp(aTopic, "memory-pressure")) {
+ Unused << SendFlushMemory(nsDependentString(aData));
+ } else if (!strcmp(aTopic, "application-background")) {
+ Unused << SendApplicationBackground();
+ } else if (!strcmp(aTopic, "application-foreground")) {
+ Unused << SendApplicationForeground();
+ } else if (!strcmp(aTopic, NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC)) {
+ NS_ConvertUTF16toUTF8 dataStr(aData);
+ const char* offline = dataStr.get();
+ if (!SendSetOffline(!strcmp(offline, "true"))) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ } else if (!strcmp(aTopic, NS_IPC_IOSERVICE_SET_CONNECTIVITY_TOPIC)) {
+ if (!SendSetConnectivity(u"true"_ns.Equals(aData))) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ } else if (!strcmp(aTopic, NS_IPC_CAPTIVE_PORTAL_SET_STATE)) {
+ nsCOMPtr<nsICaptivePortalService> cps = do_QueryInterface(aSubject);
+ MOZ_ASSERT(cps, "Should QI to a captive portal service");
+ if (!cps) {
+ return NS_ERROR_FAILURE;
+ }
+ int32_t state;
+ cps->GetState(&state);
+ if (!SendSetCaptivePortalState(state)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ }
+ // listening for alert notifications
+ else if (!strcmp(aTopic, "alertfinished") ||
+ !strcmp(aTopic, "alertclickcallback") ||
+ !strcmp(aTopic, "alertshow") ||
+ !strcmp(aTopic, "alertdisablecallback") ||
+ !strcmp(aTopic, "alertsettingscallback")) {
+ if (!SendNotifyAlertsObserver(nsDependentCString(aTopic),
+ nsDependentString(aData)))
+ return NS_ERROR_NOT_AVAILABLE;
+ } else if (!strcmp(aTopic, "child-gc-request")) {
+ Unused << SendGarbageCollect();
+ } else if (!strcmp(aTopic, "child-cc-request")) {
+ Unused << SendCycleCollect();
+ } else if (!strcmp(aTopic, "child-mmu-request")) {
+ Unused << SendMinimizeMemoryUsage();
+ } else if (!strcmp(aTopic, "child-ghost-request")) {
+ Unused << SendUnlinkGhosts();
+ } else if (!strcmp(aTopic, "last-pb-context-exited")) {
+ Unused << SendLastPrivateDocShellDestroyed();
+ }
+#ifdef ACCESSIBILITY
+ else if (aData && !strcmp(aTopic, "a11y-init-or-shutdown")) {
+ if (*aData == '1') {
+ // Make sure accessibility is running in content process when
+ // accessibility gets initiated in chrome process.
+# if defined(XP_WIN)
+ // Don't init content a11y if we detect an incompat version of JAWS in
+ // use.
+ if (!mozilla::a11y::Compatibility::IsOldJAWS()) {
+ Unused << SendActivateA11y(
+ ::GetCurrentThreadId(),
+ a11y::AccessibleWrap::GetContentProcessIdFor(ChildID()));
+ }
+# else
+ Unused << SendActivateA11y(0, 0);
+# endif
+ } else {
+ // If possible, shut down accessibility in content process when
+ // accessibility gets shutdown in chrome process.
+ Unused << SendShutdownA11y();
+ }
+ }
+#endif
+ else if (!strcmp(aTopic, "cacheservice:empty-cache")) {
+ Unused << SendNotifyEmptyHTTPCache();
+ } else if (!strcmp(aTopic, "intl:app-locales-changed")) {
+ nsTArray<nsCString> appLocales;
+ LocaleService::GetInstance()->GetAppLocalesAsBCP47(appLocales);
+ Unused << SendUpdateAppLocales(appLocales);
+ } else if (!strcmp(aTopic, "intl:requested-locales-changed")) {
+ nsTArray<nsCString> requestedLocales;
+ LocaleService::GetInstance()->GetRequestedLocales(requestedLocales);
+ Unused << SendUpdateRequestedLocales(requestedLocales);
+ } else if (!strcmp(aTopic, "cookie-changed") ||
+ !strcmp(aTopic, "private-cookie-changed")) {
+ if (!aData) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ PNeckoParent* neckoParent = LoneManagedOrNullAsserts(ManagedPNeckoParent());
+ if (!neckoParent) {
+ return NS_OK;
+ }
+ PCookieServiceParent* csParent =
+ LoneManagedOrNullAsserts(neckoParent->ManagedPCookieServiceParent());
+ if (!csParent) {
+ return NS_OK;
+ }
+ auto* cs = static_cast<CookieServiceParent*>(csParent);
+ // Do not push these cookie updates to the same process they originated
+ // from.
+ if (cs->ProcessingCookie()) {
+ return NS_OK;
+ }
+ if (!nsCRT::strcmp(aData, u"batch-deleted")) {
+ nsCOMPtr<nsIArray> cookieList = do_QueryInterface(aSubject);
+ NS_ASSERTION(cookieList, "couldn't get cookie list");
+ cs->RemoveBatchDeletedCookies(cookieList);
+ return NS_OK;
+ }
+
+ if (!nsCRT::strcmp(aData, u"cleared")) {
+ cs->RemoveAll();
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsICookie> xpcCookie = do_QueryInterface(aSubject);
+ NS_ASSERTION(xpcCookie, "couldn't get cookie");
+ if (!nsCRT::strcmp(aData, u"deleted")) {
+ cs->RemoveCookie(xpcCookie);
+ } else if ((!nsCRT::strcmp(aData, u"added")) ||
+ (!nsCRT::strcmp(aData, u"changed"))) {
+ cs->AddCookie(xpcCookie);
+ }
+ } else if (!strcmp(aTopic, NS_NETWORK_LINK_TYPE_TOPIC)) {
+ UpdateNetworkLinkType();
+ }
+
+ return NS_OK;
+}
+
+/* static */
+bool ContentParent::ShouldSyncPreference(const char16_t* aData) {
+#define PARENT_ONLY_PREF_LIST_ENTRY(s) \
+ { s, (sizeof(s) / sizeof(char16_t)) - 1 }
+ struct ParentOnlyPrefListEntry {
+ const char16_t* mPrefBranch;
+ size_t mLen;
+ };
+ // These prefs are not useful in child processes.
+ static const ParentOnlyPrefListEntry sParentOnlyPrefBranchList[] = {
+ PARENT_ONLY_PREF_LIST_ENTRY(u"app.update.lastUpdateTime."),
+ PARENT_ONLY_PREF_LIST_ENTRY(u"datareporting.policy."),
+ PARENT_ONLY_PREF_LIST_ENTRY(u"browser.safebrowsing.provider."),
+ PARENT_ONLY_PREF_LIST_ENTRY(u"browser.shell."),
+ PARENT_ONLY_PREF_LIST_ENTRY(u"browser.slowStartup."),
+ PARENT_ONLY_PREF_LIST_ENTRY(u"browser.startup."),
+ PARENT_ONLY_PREF_LIST_ENTRY(u"extensions.getAddons.cache."),
+ PARENT_ONLY_PREF_LIST_ENTRY(u"media.gmp-manager."),
+ PARENT_ONLY_PREF_LIST_ENTRY(u"media.gmp-gmpopenh264."),
+ PARENT_ONLY_PREF_LIST_ENTRY(u"privacy.sanitize."),
+ };
+#undef PARENT_ONLY_PREF_LIST_ENTRY
+
+ for (const auto& entry : sParentOnlyPrefBranchList) {
+ if (NS_strncmp(entry.mPrefBranch, aData, entry.mLen) == 0) {
+ return false;
+ }
+ }
+ return true;
+}
+
+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<PBackgroundParent>&& aEndpoint) {
+ if (!BackgroundParent::Alloc(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) {
+ ASSERT_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.openerParent());
+ if (!opener) {
+ ASSERT_UNLESS_FUZZING(
+ "Got null opener from child; aborting AllocPBrowserParent.");
+ return false;
+ }
+ }
+
+ MaybeInvalidTabContext tc(aContext);
+ if (!tc.IsValid()) {
+ NS_ERROR(nsPrintfCString("Child passed us an invalid TabContext. (%s) "
+ "Aborting AllocPBrowserParent.",
+ tc.GetInvalidReason())
+ .get());
+ return false;
+ }
+
+ return true;
+}
+
+static bool CloneIsLegal(ContentParent* aCp, CanonicalBrowsingContext& aSource,
+ CanonicalBrowsingContext& aTarget) {
+ // Source and target must be in the same BCG
+ if (NS_WARN_IF(aSource.Group() != aTarget.Group())) {
+ return false;
+ }
+ // The source and target must be in different toplevel <browser>s
+ if (NS_WARN_IF(aSource.Top() == aTarget.Top())) {
+ return false;
+ }
+
+ // Neither source nor target must be toplevel.
+ if (NS_WARN_IF(aSource.IsTop()) || NS_WARN_IF(aTarget.IsTop())) {
+ return false;
+ }
+
+ // Both should be embedded by the same process.
+ auto* sourceEmbedder = aSource.GetParentWindowContext();
+ if (NS_WARN_IF(!sourceEmbedder) ||
+ NS_WARN_IF(sourceEmbedder->GetContentParent() != aCp)) {
+ return false;
+ }
+
+ auto* targetEmbedder = aSource.GetParentWindowContext();
+ if (NS_WARN_IF(!targetEmbedder) ||
+ NS_WARN_IF(targetEmbedder->GetContentParent() != aCp)) {
+ return false;
+ }
+
+ // All seems sane.
+ return true;
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvCloneDocumentTreeInto(
+ const MaybeDiscarded<BrowsingContext>& aSource,
+ const MaybeDiscarded<BrowsingContext>& aTarget) {
+ if (aSource.IsNullOrDiscarded() || aTarget.IsNullOrDiscarded()) {
+ 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
+ ->ChangeRemoteness(cp->GetRemoteType(), /* aLoadID = */ 0,
+ /* aReplaceBC = */ false, /* aSpecificGroupId = */ 0)
+ ->Then(
+ GetMainThreadSerialEventTarget(), __func__,
+ [source = RefPtr{source}](BrowserParent* aBp) {
+ Unused << aBp->SendCloneDocumentTreeIntoSelf(source);
+ },
+ [](nsresult aRv) {
+ NS_WARNING(
+ nsPrintfCString("Remote clone failed: %x\n", unsigned(aRv))
+ .get());
+ });
+ 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");
+ }
+
+ 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.openerParent());
+ 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->RegisterRemoteFrame(parent)) {
+ return IPC_FAIL(this, "RegisterRemoteFrame Failed");
+ }
+ }
+
+ if (NS_WARN_IF(!parent->BindPWindowGlobalEndpoint(std::move(aWindowEp),
+ initialWindow))) {
+ return IPC_FAIL(this, "BindPWindowGlobalEndpoint failed");
+ }
+
+ initialWindow->Init();
+
+ // When enabling input event prioritization, input events may preempt other
+ // normal priority IPC messages. To prevent the input events preempt
+ // PBrowserConstructor, we use an IPC 'RemoteIsReadyToHandleInputEvents' to
+ // notify parent that BrowserChild is created. In this case, PBrowser is
+ // initiated from content so that we can set BrowserParent as ready to handle
+ // input
+ parent->SetReadyToHandleInputEvents();
+ return IPC_OK();
+}
+
+mozilla::PRemoteSpellcheckEngineParent*
+ContentParent::AllocPRemoteSpellcheckEngineParent() {
+ mozilla::RemoteSpellcheckEngineParent* parent =
+ new mozilla::RemoteSpellcheckEngineParent();
+ return parent;
+}
+
+bool ContentParent::DeallocPRemoteSpellcheckEngineParent(
+ PRemoteSpellcheckEngineParent* parent) {
+ delete parent;
+ return true;
+}
+
+/* static */
+void ContentParent::ForceKillTimerCallback(nsITimer* aTimer, void* aClosure) {
+ // We don't want to time out the content process during XPCShell tests. This
+ // is the easiest way to ensure that.
+ if (PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR")) {
+ return;
+ }
+
+ auto self = static_cast<ContentParent*>(aClosure);
+ self->KillHard("ShutDownKill");
+}
+
+void ContentParent::GeneratePairedMinidump(const char* aReason) {
+ // We're about to kill the child process associated with this content.
+ // Something has gone wrong to get us here, so we generate a minidump
+ // of the parent and child for submission to the crash server unless we're
+ // already shutting down.
+ nsCOMPtr<nsIAppStartup> appStartup = components::AppStartup::Service();
+ if (mCrashReporter && !appStartup->GetShuttingDown() &&
+ 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, nullptr, "browser"_ns)) {
+ mCreatedPairedMinidumps = mCrashReporter->FinalizeCrashReport();
+ }
+ }
+}
+
+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 = %d crashed without "
+ "leaving a minidump behind",
+ OtherPid())
+ .get());
+ }
+}
+
+// WARNING: aReason appears in telemetry, so any new value passed in requires
+// data review.
+void ContentParent::KillHard(const char* aReason) {
+ AUTO_PROFILER_LABEL("ContentParent::KillHard", OTHER);
+
+ // On Windows, calling KillHard multiple times causes problems - the
+ // process handle becomes invalid on the first call, causing a second call
+ // to crash our process - more details in bug 890840.
+ if (mCalledKillHard) {
+ return;
+ }
+ mCalledKillHard = true;
+ mForceKillTimer = nullptr;
+
+ RemoveShutdownBlockers();
+
+ GeneratePairedMinidump(aReason);
+
+ nsDependentCString reason(aReason);
+ Telemetry::Accumulate(Telemetry::SUBPROCESS_KILL_HARD, reason, 1);
+
+ ProcessHandle otherProcessHandle;
+ if (!base::OpenProcessHandle(OtherPid(), &otherProcessHandle)) {
+ NS_ERROR("Failed to open child process when attempting kill.");
+ return;
+ }
+
+ if (!KillProcess(otherProcessHandle, base::PROCESS_END_KILLED_BY_USER,
+ false)) {
+ NS_WARNING("failed to kill subprocess!");
+ }
+
+ if (mSubprocess) {
+ MOZ_LOG(
+ ContentParent::GetLog(), LogLevel::Verbose,
+ ("KillHard Subprocess: ContentParent %p mSubprocess %p handle "
+ "%" PRIuPTR,
+ this, mSubprocess,
+ mSubprocess ? (uintptr_t)mSubprocess->GetChildProcessHandle() : -1));
+ mSubprocess->SetAlreadyDead();
+ }
+
+ // EnsureProcessTerminated has responsibilty for closing otherProcessHandle.
+ XRE_GetIOMessageLoop()->PostTask(
+ NewRunnableFunction("EnsureProcessTerminatedRunnable",
+ &ProcessWatcher::EnsureProcessTerminated,
+ otherProcessHandle, /*force=*/true));
+}
+
+void ContentParent::FriendlyName(nsAString& aName, bool aAnonymize) {
+ aName.Truncate();
+ if (mIsForBrowser) {
+ aName.AssignLiteral("Browser");
+ } else if (aAnonymize) {
+ aName.AssignLiteral("<anonymized-name>");
+ } else {
+ aName.AssignLiteral("???");
+ }
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvInitCrashReporter(
+ const NativeThreadId& aThreadId) {
+ mCrashReporter =
+ MakeUnique<CrashReporterHost>(GeckoProcessType_Content, aThreadId);
+
+ return IPC_OK();
+}
+
+hal_sandbox::PHalParent* ContentParent::AllocPHalParent() {
+ return hal_sandbox::CreateHalParent();
+}
+
+bool ContentParent::DeallocPHalParent(hal_sandbox::PHalParent* aHal) {
+ delete aHal;
+ return true;
+}
+
+devtools::PHeapSnapshotTempFileHelperParent*
+ContentParent::AllocPHeapSnapshotTempFileHelperParent() {
+ return devtools::HeapSnapshotTempFileHelperParent::Create();
+}
+
+bool ContentParent::DeallocPHeapSnapshotTempFileHelperParent(
+ devtools::PHeapSnapshotTempFileHelperParent* aHeapSnapshotHelper) {
+ delete aHeapSnapshotHelper;
+ return true;
+}
+
+bool ContentParent::SendRequestMemoryReport(
+ const uint32_t& aGeneration, const bool& aAnonymize,
+ const bool& aMinimizeMemoryUsage, const Maybe<FileDescriptor>& aDMDFile) {
+ // This automatically cancels the previous request.
+ mMemoryReportRequest = MakeUnique<MemoryReportRequestHost>(aGeneration);
+ // If we run the callback in response to a reply, then by definition |this|
+ // is still alive, so the ref pointer is redundant, but it seems easier
+ // to hold a strong reference than to worry about that.
+ RefPtr<ContentParent> self(this);
+ PContentParent::SendRequestMemoryReport(
+ aGeneration, aAnonymize, aMinimizeMemoryUsage, aDMDFile,
+ [&, self](const uint32_t& aGeneration2) {
+ if (self->mMemoryReportRequest) {
+ self->mMemoryReportRequest->Finish(aGeneration2);
+ self->mMemoryReportRequest = nullptr;
+ }
+ },
+ [&, self](mozilla::ipc::ResponseRejectReason) {
+ self->mMemoryReportRequest = nullptr;
+ });
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvAddMemoryReport(
+ const MemoryReport& aReport) {
+ if (mMemoryReportRequest) {
+ mMemoryReportRequest->RecvReport(aReport);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvAddPerformanceMetrics(
+ const nsID& aID, nsTArray<PerformanceInfo>&& aMetrics) {
+ nsresult rv = PerformanceMetricsCollector::DataReceived(aID, aMetrics);
+ Unused << NS_WARN_IF(NS_FAILED(rv));
+ return IPC_OK();
+}
+
+PCycleCollectWithLogsParent* ContentParent::AllocPCycleCollectWithLogsParent(
+ const bool& aDumpAllTraces, const FileDescriptor& aGCLog,
+ const FileDescriptor& aCCLog) {
+ MOZ_CRASH("Don't call this; use ContentParent::CycleCollectWithLogs");
+}
+
+bool ContentParent::DeallocPCycleCollectWithLogsParent(
+ PCycleCollectWithLogsParent* aActor) {
+ delete aActor;
+ return true;
+}
+
+bool ContentParent::CycleCollectWithLogs(
+ bool aDumpAllTraces, nsICycleCollectorLogSink* aSink,
+ nsIDumpGCAndCCLogsCallback* aCallback) {
+ return CycleCollectWithLogsParent::AllocAndSendConstructor(
+ this, aDumpAllTraces, aSink, aCallback);
+}
+
+PTestShellParent* ContentParent::AllocPTestShellParent() {
+ return new TestShellParent();
+}
+
+bool ContentParent::DeallocPTestShellParent(PTestShellParent* shell) {
+ delete shell;
+ return true;
+}
+
+PScriptCacheParent* ContentParent::AllocPScriptCacheParent(
+ const FileDescOrError& cacheFile, const bool& wantCacheData) {
+ return new loader::ScriptCacheParent(wantCacheData);
+}
+
+bool ContentParent::DeallocPScriptCacheParent(PScriptCacheParent* cache) {
+ delete static_cast<loader::ScriptCacheParent*>(cache);
+ return true;
+}
+
+PNeckoParent* ContentParent::AllocPNeckoParent() { return new NeckoParent(); }
+
+bool ContentParent::DeallocPNeckoParent(PNeckoParent* necko) {
+ delete necko;
+ return true;
+}
+
+PPrintingParent* ContentParent::AllocPPrintingParent() {
+#ifdef NS_PRINTING
+ if (mPrintingParent) {
+ // Only one PrintingParent should be created per process.
+ return nullptr;
+ }
+
+ // Create the printing singleton for this process.
+ mPrintingParent = new PrintingParent();
+
+ // Take another reference for IPDL code.
+ mPrintingParent.get()->AddRef();
+
+ return mPrintingParent.get();
+#else
+ MOZ_ASSERT_UNREACHABLE("Should never be created if no printing.");
+ return nullptr;
+#endif
+}
+
+bool ContentParent::DeallocPPrintingParent(PPrintingParent* printing) {
+#ifdef NS_PRINTING
+ MOZ_RELEASE_ASSERT(
+ mPrintingParent == printing,
+ "Only one PrintingParent should have been created per process.");
+
+ // Release reference taken for IPDL code.
+ static_cast<PrintingParent*>(printing)->Release();
+
+ mPrintingParent = nullptr;
+#else
+ MOZ_ASSERT_UNREACHABLE("Should never have been created if no printing.");
+#endif
+ return true;
+}
+
+#ifdef NS_PRINTING
+already_AddRefed<embedding::PrintingParent> ContentParent::GetPrintingParent() {
+ MOZ_ASSERT(mPrintingParent);
+
+ RefPtr<embedding::PrintingParent> printingParent = mPrintingParent;
+ return printingParent.forget();
+}
+#endif
+
+mozilla::ipc::IPCResult ContentParent::RecvInitStreamFilter(
+ const uint64_t& aChannelId, const nsString& 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();
+}
+
+PChildToParentStreamParent* ContentParent::AllocPChildToParentStreamParent() {
+ return mozilla::ipc::AllocPChildToParentStreamParent();
+}
+
+bool ContentParent::DeallocPChildToParentStreamParent(
+ PChildToParentStreamParent* aActor) {
+ delete aActor;
+ return true;
+}
+
+PParentToChildStreamParent* ContentParent::AllocPParentToChildStreamParent() {
+ MOZ_CRASH(
+ "PParentToChildStreamParent actors should be manually constructed!");
+}
+
+bool ContentParent::DeallocPParentToChildStreamParent(
+ PParentToChildStreamParent* aActor) {
+ delete aActor;
+ return true;
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvAddSecurityState(
+ const MaybeDiscarded<WindowContext>& aContext, uint32_t aStateFlags) {
+ if (aContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+
+ aContext.get()->AddSecurityState(aStateFlags);
+ return IPC_OK();
+}
+
+already_AddRefed<PExternalHelperAppParent>
+ContentParent::AllocPExternalHelperAppParent(
+ nsIURI* uri, const Maybe<mozilla::net::LoadInfoArgs>& aLoadInfoArgs,
+ const nsCString& aMimeContentType, const nsCString& aContentDisposition,
+ const uint32_t& aContentDispositionHint,
+ const nsString& aContentDispositionFilename, const bool& aForceSave,
+ const int64_t& aContentLength, const bool& aWasFileChannel,
+ nsIURI* aReferrer, const MaybeDiscarded<BrowsingContext>& aContext,
+ const bool& aShouldCloseWindow) {
+ RefPtr<ExternalHelperAppParent> parent = new ExternalHelperAppParent(
+ uri, aContentLength, aWasFileChannel, aContentDisposition,
+ aContentDispositionHint, aContentDispositionFilename);
+ return parent.forget();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvPExternalHelperAppConstructor(
+ PExternalHelperAppParent* actor, nsIURI* uri,
+ const Maybe<LoadInfoArgs>& loadInfoArgs, const nsCString& aMimeContentType,
+ const nsCString& aContentDisposition,
+ const uint32_t& aContentDispositionHint,
+ const nsString& 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();
+ static_cast<ExternalHelperAppParent*>(actor)->Init(
+ loadInfoArgs, aMimeContentType, aForceSave, aReferrer, context,
+ aShouldCloseWindow);
+ 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;
+}
+
+PPresentationParent* ContentParent::AllocPPresentationParent() {
+ RefPtr<PresentationParent> actor = new PresentationParent();
+ return actor.forget().take();
+}
+
+bool ContentParent::DeallocPPresentationParent(PPresentationParent* aActor) {
+ RefPtr<PresentationParent> actor =
+ dont_AddRef(static_cast<PresentationParent*>(aActor));
+ return true;
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvPPresentationConstructor(
+ PPresentationParent* aActor) {
+ if (!static_cast<PresentationParent*>(aActor)->Init(mChildID)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ return IPC_OK();
+}
+
+#ifdef MOZ_WEBSPEECH
+PSpeechSynthesisParent* ContentParent::AllocPSpeechSynthesisParent() {
+ return new mozilla::dom::SpeechSynthesisParent();
+}
+
+bool ContentParent::DeallocPSpeechSynthesisParent(
+ PSpeechSynthesisParent* aActor) {
+ delete aActor;
+ return true;
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvPSpeechSynthesisConstructor(
+ PSpeechSynthesisParent* aActor) {
+ if (!static_cast<SpeechSynthesisParent*>(aActor)->SendInit()) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ return IPC_OK();
+}
+#endif
+
+mozilla::ipc::IPCResult ContentParent::RecvStartVisitedQueries(
+ const nsTArray<RefPtr<nsIURI>>& aUris) {
+ nsCOMPtr<IHistory> history = services::GetHistory();
+ if (!history) {
+ return IPC_OK();
+ }
+ for (const auto& params : aUris) {
+ if (NS_WARN_IF(!params)) {
+ continue;
+ }
+ history->RegisterVisitedCallback(params, nullptr);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvSetURITitle(nsIURI* uri,
+ const nsString& title) {
+ if (!uri) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ nsCOMPtr<IHistory> history = services::GetHistory();
+ if (history) {
+ history->SetURITitle(uri, title);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvIsSecureURI(
+ const uint32_t& aType, nsIURI* aURI, const uint32_t& aFlags,
+ const OriginAttributes& aOriginAttributes, bool* aIsSecureURI) {
+ nsCOMPtr<nsISiteSecurityService> sss(do_GetService(NS_SSSERVICE_CONTRACTID));
+ if (!sss) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ if (!aURI) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ nsresult rv = sss->IsSecureURI(aType, aURI, aFlags, aOriginAttributes,
+ nullptr, nullptr, aIsSecureURI);
+ if (NS_FAILED(rv)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvAccumulateMixedContentHSTS(
+ nsIURI* aURI, const bool& aActive,
+ const OriginAttributes& aOriginAttributes) {
+ if (!aURI) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ nsMixedContentBlocker::AccumulateMixedContentHSTS(aURI, aActive,
+ aOriginAttributes);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvLoadURIExternal(
+ nsIURI* uri, nsIPrincipal* aTriggeringPrincipal,
+ const MaybeDiscarded<BrowsingContext>& aContext) {
+ if (aContext.IsDiscarded()) {
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsIExternalProtocolService> extProtService(
+ do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID));
+ if (!extProtService) {
+ return IPC_OK();
+ }
+
+ if (!uri) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ BrowsingContext* bc = aContext.get();
+ extProtService->LoadURI(uri, aTriggeringPrincipal, bc);
+ 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_NO_REASON(this);
+ }
+ nsCOMPtr<nsIAlertsService> sysAlerts(components::Alerts::Service());
+ if (sysAlerts) {
+ sysAlerts->ShowAlert(aAlert, this);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvCloseAlert(const nsString& aName) {
+ nsCOMPtr<nsIAlertsService> sysAlerts(components::Alerts::Service());
+ if (sysAlerts) {
+ sysAlerts->CloseAlert(aName);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvDisableNotifications(
+ const IPC::Principal& aPrincipal) {
+ if (!ValidatePrincipal(aPrincipal)) {
+ LogFailedPrincipalValidationInfo(aPrincipal, __func__);
+ }
+ Unused << Notification::RemovePermission(aPrincipal);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvOpenNotificationSettings(
+ const IPC::Principal& aPrincipal) {
+ if (!ValidatePrincipal(aPrincipal)) {
+ LogFailedPrincipalValidationInfo(aPrincipal, __func__);
+ }
+ Unused << Notification::OpenSettings(aPrincipal);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvNotificationEvent(
+ const nsString& aType, const NotificationEventData& aData) {
+ nsCOMPtr<nsIServiceWorkerManager> swm =
+ mozilla::services::GetServiceWorkerManager();
+ 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 nsString& 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::UnpackClonedMessageDataForParent(aData, data);
+
+ ppm->ReceiveMessage(ppm, nullptr, aMsg, true, &data, aRetvals,
+ IgnoreErrors());
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvAsyncMessage(
+ const nsString& 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::UnpackClonedMessageDataForParent(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();
+ 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;
+}
+
+nsConsoleService* ContentParent::GetConsoleService() {
+ if (mConsoleService) {
+ return mConsoleService.get();
+ }
+
+ // XXXkhuey everything about this is terrible.
+ // Get the ConsoleService by CID rather than ContractID, so that we
+ // can cast the returned pointer to an nsConsoleService (rather than
+ // just an nsIConsoleService). This allows us to call the non-idl function
+ // nsConsoleService::LogMessageWithMode.
+ NS_DEFINE_CID(consoleServiceCID, NS_CONSOLESERVICE_CID);
+ nsCOMPtr<nsIConsoleService> consoleService(do_GetService(consoleServiceCID));
+ mConsoleService = static_cast<nsConsoleService*>(consoleService.get());
+ return mConsoleService.get();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvConsoleMessage(
+ const nsString& aMessage) {
+ RefPtr<nsConsoleService> consoleService = GetConsoleService();
+ if (!consoleService) {
+ return IPC_OK();
+ }
+
+ RefPtr<nsConsoleMessage> msg(new nsConsoleMessage(aMessage.get()));
+ consoleService->LogMessageWithMode(msg, nsConsoleService::SuppressLog);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvReportFrameTimingData(
+ uint64_t aInnerWindowId, const nsString& entryName,
+ const nsString& initiatorType, UniquePtr<PerformanceTimingData>&& aData) {
+ RefPtr<WindowGlobalParent> parent =
+ WindowGlobalParent::GetByInnerWindowId(aInnerWindowId);
+ 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(
+ aInnerWindowId, entryName, initiatorType, std::move(aData));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::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) {
+ return RecvScriptErrorInternal(aMessage, aSourceName, aSourceLine,
+ aLineNumber, aColNumber, aFlags, aCategory,
+ aFromPrivateWindow, aFromChromeContext);
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvScriptErrorWithStack(
+ 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 bool& aFromChromeContext, const ClonedMessageData& aFrame) {
+ return RecvScriptErrorInternal(
+ aMessage, aSourceName, aSourceLine, aLineNumber, aColNumber, aFlags,
+ aCategory, aFromPrivateWindow, aFromChromeContext, &aFrame);
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvScriptErrorInternal(
+ 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 bool& aFromChromeContext, const ClonedMessageData* aStack) {
+ RefPtr<nsConsoleService> consoleService = GetConsoleService();
+ if (!consoleService) {
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsIScriptError> msg;
+
+ if (aStack) {
+ StructuredCloneData data;
+ UnpackClonedMessageDataForParent(*aStack, data);
+
+ AutoJSAPI jsapi;
+ if (NS_WARN_IF(!jsapi.Init(xpc::PrivilegedJunkScope()))) {
+ MOZ_CRASH();
+ }
+ JSContext* cx = jsapi.cx();
+
+ JS::RootedValue stack(cx);
+ ErrorResult rv;
+ data.Read(cx, &stack, rv);
+ if (rv.Failed() || !stack.isObject()) {
+ rv.SuppressException();
+ return IPC_OK();
+ }
+
+ JS::RootedObject stackObj(cx, &stack.toObject());
+ MOZ_ASSERT(JS::IsUnwrappedSavedFrame(stackObj));
+
+ JS::RootedObject stackGlobal(cx, JS::GetNonCCWObjectGlobal(stackObj));
+ msg = new nsScriptErrorWithStack(JS::NothingHandleValue, stackObj,
+ stackGlobal);
+ } else {
+ msg = new nsScriptError();
+ }
+
+ nsresult rv = msg->Init(aMessage, aSourceName, aSourceLine, aLineNumber,
+ aColNumber, aFlags, aCategory.get(),
+ aFromPrivateWindow, aFromChromeContext);
+ if (NS_FAILED(rv)) return IPC_OK();
+
+ msg->SetIsForwardedFromContentProcess(true);
+
+ consoleService->LogMessageWithMode(msg, nsConsoleService::SuppressLog);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvPrivateDocShellsExist(
+ const bool& aExist) {
+ if (!sPrivateContent) {
+ sPrivateContent = MakeUnique<nsTArray<ContentParent*>>();
+ if (!sHasSeenPrivateDocShell) {
+ sHasSeenPrivateDocShell = true;
+ Telemetry::ScalarSet(
+ Telemetry::ScalarID::DOM_PARENTPROCESS_PRIVATE_WINDOW_USED, true);
+ }
+ }
+ if (aExist) {
+ sPrivateContent->AppendElement(this);
+ } else {
+ sPrivateContent->RemoveElement(this);
+
+ // Only fire the notification if we have private and non-private
+ // windows: if privatebrowsing.autostart is true, all windows are
+ // private.
+ if (!sPrivateContent->Length() &&
+ !Preferences::GetBool("browser.privatebrowsing.autostart")) {
+ nsCOMPtr<nsIObserverService> obs =
+ mozilla::services::GetObserverService();
+ obs->NotifyObservers(nullptr, "last-pb-context-exited", nullptr);
+ sPrivateContent = nullptr;
+ }
+ }
+ return IPC_OK();
+}
+
+bool ContentParent::DoLoadMessageManagerScript(const nsAString& aURL,
+ bool aRunInGlobalScope) {
+ MOZ_ASSERT(!aRunInGlobalScope);
+ return SendLoadProcessScript(nsString(aURL));
+}
+
+nsresult ContentParent::DoSendAsyncMessage(const nsAString& aMessage,
+ StructuredCloneData& aHelper) {
+ ClonedMessageData data;
+ if (!BuildClonedMessageDataForParent(this, aHelper, data)) {
+ return NS_ERROR_DOM_DATA_CLONE_ERR;
+ }
+ if (!SendAsyncMessage(nsString(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();
+}
+
+bool ContentParent::ShouldContinueFromReplyTimeout() {
+ RefPtr<ProcessHangMonitor> monitor = ProcessHangMonitor::Get();
+ return !monitor || !monitor->ShouldTimeOutCPOWs();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvRecordingDeviceEvents(
+ const nsString& aRecordingStatus, const nsString& aPageURL,
+ const bool& aIsAudio, const bool& aIsVideo) {
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ // recording-device-ipc-events needs to gather more information from content
+ // process
+ RefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();
+ props->SetPropertyAsUint64(u"childID"_ns, ChildID());
+ props->SetPropertyAsBool(u"isAudio"_ns, aIsAudio);
+ props->SetPropertyAsBool(u"isVideo"_ns, aIsVideo);
+ props->SetPropertyAsAString(u"requestURL"_ns, aPageURL);
+
+ obs->NotifyObservers((nsIPropertyBag2*)props, "recording-device-ipc-events",
+ aRecordingStatus.get());
+ } else {
+ NS_WARNING(
+ "Could not get the Observer service for "
+ "ContentParent::RecvRecordingDeviceEvents.");
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvAddIdleObserver(
+ const uint64_t& aObserver, const uint32_t& aIdleTimeInS) {
+ nsresult rv;
+ nsCOMPtr<nsIUserIdleService> idleService =
+ do_GetService("@mozilla.org/widget/useridleservice;1", &rv);
+ NS_ENSURE_SUCCESS(rv, IPC_FAIL_NO_REASON(this));
+
+ RefPtr<ParentIdleListener> listener =
+ new ParentIdleListener(this, aObserver, aIdleTimeInS);
+ rv = idleService->AddIdleObserver(listener, aIdleTimeInS);
+ NS_ENSURE_SUCCESS(rv, IPC_FAIL_NO_REASON(this));
+ 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_NO_REASON(this));
+ idleService->RemoveIdleObserver(listener, aIdleTimeInS);
+ mIdleListeners.RemoveElementAt(i);
+ break;
+ }
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvBackUpXResources(
+ const FileDescriptor& aXSocketFd) {
+#ifndef MOZ_X11
+ MOZ_CRASH("This message only makes sense on X11 platforms");
+#else
+ MOZ_ASSERT(0 > mChildXSocketFdDup.get(), "Already backed up X resources??");
+ if (aXSocketFd.IsValid()) {
+ auto rawFD = aXSocketFd.ClonePlatformHandle();
+ mChildXSocketFdDup.reset(rawFD.release());
+ }
+#endif
+ return IPC_OK();
+}
+
+class AnonymousTemporaryFileRequestor final : public Runnable {
+ public:
+ AnonymousTemporaryFileRequestor(ContentParent* aCP, const uint64_t& aID)
+ : Runnable("dom::AnonymousTemporaryFileRequestor"),
+ mCP(aCP),
+ mID(aID),
+ mRv(NS_OK),
+ mPRFD(nullptr) {}
+
+ NS_IMETHOD Run() override {
+ if (NS_IsMainThread()) {
+ FileDescOrError result;
+ if (NS_WARN_IF(NS_FAILED(mRv))) {
+ // Returning false will kill the child process; instead
+ // propagate the error and let the child handle it.
+ result = mRv;
+ } else {
+ result = FileDescriptor(FileDescriptor::PlatformHandleType(
+ PR_FileDesc2NativeHandle(mPRFD)));
+ // The FileDescriptor object owns a duplicate of the file handle; we
+ // must close the original (and clean up the NSPR descriptor).
+ PR_Close(mPRFD);
+ }
+ Unused << mCP->SendProvideAnonymousTemporaryFile(mID, result);
+ // It's important to release this reference while wr're on the main
+ // thread!
+ mCP = nullptr;
+ } else {
+ mRv = NS_OpenAnonymousTemporaryFile(&mPRFD);
+ NS_DispatchToMainThread(this);
+ }
+ return NS_OK;
+ }
+
+ private:
+ RefPtr<ContentParent> mCP;
+ uint64_t mID;
+ nsresult mRv;
+ PRFileDesc* mPRFD;
+};
+
+mozilla::ipc::IPCResult ContentParent::RecvRequestAnonymousTemporaryFile(
+ const uint64_t& aID) {
+ // Make sure to send a callback to the child if we bail out early.
+ nsresult rv = NS_OK;
+ RefPtr<ContentParent> self(this);
+ auto autoNotifyChildOnError = MakeScopeExit([&, self]() {
+ if (NS_FAILED(rv)) {
+ FileDescOrError result(rv);
+ Unused << self->SendProvideAnonymousTemporaryFile(aID, result);
+ }
+ });
+
+ // We use a helper runnable to open the anonymous temporary file on the IO
+ // thread. The same runnable will call us back on the main thread when the
+ // file has been opened.
+ nsCOMPtr<nsIEventTarget> target =
+ do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
+ if (!target) {
+ return IPC_OK();
+ }
+
+ rv = target->Dispatch(new AnonymousTemporaryFileRequestor(this, aID),
+ NS_DISPATCH_NORMAL);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return IPC_OK();
+ }
+
+ rv = NS_OK;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvCreateAudioIPCConnection(
+ CreateAudioIPCConnectionResolver&& aResolver) {
+ FileDescriptor fd = CubebUtils::CreateAudioIPCConnection();
+ FileDescOrError result;
+ if (fd.IsValid()) {
+ result = fd;
+ } else {
+ result = NS_ERROR_FAILURE;
+ }
+ aResolver(std::move(result));
+ return IPC_OK();
+}
+
+PFileDescriptorSetParent* ContentParent::AllocPFileDescriptorSetParent(
+ const FileDescriptor& aFD) {
+ return new FileDescriptorSetParent(aFD);
+}
+
+bool ContentParent::DeallocPFileDescriptorSetParent(
+ PFileDescriptorSetParent* aActor) {
+ delete static_cast<FileDescriptorSetParent*>(aActor);
+ return true;
+}
+
+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;
+ }
+
+ nsTArray<SystemFontListEntry> fontList;
+ gfxPlatform::GetPlatform()->ReadSystemFontList(&fontList);
+
+ for (auto* cp : AllProcesses(eLive)) {
+ Unused << cp->SendUpdateFontList(fontList);
+ }
+}
+
+already_AddRefed<mozilla::docshell::POfflineCacheUpdateParent>
+ContentParent::AllocPOfflineCacheUpdateParent(
+ nsIURI* aManifestURI, nsIURI* aDocumentURI,
+ const PrincipalInfo& aLoadingPrincipalInfo, const bool& aStickDocument,
+ const CookieJarSettingsArgs& aCookieJarSettingsArgs) {
+ RefPtr<mozilla::docshell::OfflineCacheUpdateParent> update =
+ new mozilla::docshell::OfflineCacheUpdateParent();
+ return update.forget();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvPOfflineCacheUpdateConstructor(
+ POfflineCacheUpdateParent* aActor, nsIURI* aManifestURI,
+ nsIURI* aDocumentURI, const PrincipalInfo& aLoadingPrincipal,
+ const bool& aStickDocument,
+ const CookieJarSettingsArgs& aCookieJarSettingsArgs) {
+ MOZ_ASSERT(aActor);
+
+ RefPtr<mozilla::docshell::OfflineCacheUpdateParent> update =
+ static_cast<mozilla::docshell::OfflineCacheUpdateParent*>(aActor);
+
+ nsresult rv = update->Schedule(aManifestURI, aDocumentURI, aLoadingPrincipal,
+ aStickDocument, aCookieJarSettingsArgs);
+ if (NS_FAILED(rv) && IsAlive()) {
+ // Inform the child of failure.
+ Unused << update->SendFinish(false, false);
+ }
+
+ return IPC_OK();
+}
+
+PWebrtcGlobalParent* ContentParent::AllocPWebrtcGlobalParent() {
+#ifdef MOZ_WEBRTC
+ return WebrtcGlobalParent::Alloc();
+#else
+ return nullptr;
+#endif
+}
+
+bool ContentParent::DeallocPWebrtcGlobalParent(PWebrtcGlobalParent* aActor) {
+#ifdef MOZ_WEBRTC
+ WebrtcGlobalParent::Dealloc(static_cast<WebrtcGlobalParent*>(aActor));
+ return true;
+#else
+ return false;
+#endif
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvSetOfflinePermission(
+ const Principal& aPrincipal) {
+ if (!ValidatePrincipal(aPrincipal)) {
+ LogFailedPrincipalValidationInfo(aPrincipal, __func__);
+ }
+ nsCOMPtr<nsIOfflineCacheUpdateService> updateService =
+ components::OfflineCacheUpdate::Service();
+ if (!updateService) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ nsresult rv = updateService->AllowOfflineApp(aPrincipal);
+ NS_ENSURE_SUCCESS(rv, IPC_FAIL_NO_REASON(this));
+
+ return IPC_OK();
+}
+
+void ContentParent::MaybeInvokeDragSession(BrowserParent* aParent) {
+ // dnd uses IPCBlob to transfer data to the content process and the IPC
+ // message is sent as normal priority. When sending input events with input
+ // priority, the message may be preempted by the later dnd events. To make
+ // sure the input events and the blob message are processed in time order
+ // on the content process, we temporarily send the input events with normal
+ // priority when there is an active dnd session.
+ SetInputPriorityEventEnabled(false);
+
+ nsCOMPtr<nsIDragService> dragService =
+ do_GetService("@mozilla.org/widget/dragservice;1");
+ if (dragService && dragService->MaybeAddChildProcess(this)) {
+ // We need to send transferable data to child process.
+ nsCOMPtr<nsIDragSession> session;
+ dragService->GetCurrentSession(getter_AddRefs(session));
+ if (session) {
+ nsTArray<IPCDataTransfer> dataTransfers;
+ RefPtr<DataTransfer> transfer = session->GetDataTransfer();
+ if (!transfer) {
+ // Pass eDrop to get DataTransfer with external
+ // drag formats cached.
+ transfer = new DataTransfer(nullptr, eDrop, true, -1);
+ session->SetDataTransfer(transfer);
+ }
+ // Note, even though this fills the DataTransfer object with
+ // external data, the data is usually transfered over IPC lazily when
+ // needed.
+ transfer->FillAllExternalData();
+ nsCOMPtr<nsILoadContext> lc =
+ aParent ? aParent->GetLoadContext() : nullptr;
+ nsCOMPtr<nsIArray> transferables = transfer->GetTransferables(lc);
+ nsContentUtils::TransferablesToIPCTransferables(
+ transferables, dataTransfers, false, nullptr, this);
+ uint32_t action;
+ session->GetDragAction(&action);
+ mozilla::Unused << SendInvokeDragSession(dataTransfers, action);
+ }
+ }
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvUpdateDropEffect(
+ const uint32_t& aDragAction, const uint32_t& aDropEffect) {
+ nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
+ if (dragSession) {
+ dragSession->SetDragAction(aDragAction);
+ RefPtr<DataTransfer> dt = dragSession->GetDataTransfer();
+ if (dt) {
+ dt->SetDropEffectInt(aDropEffect);
+ }
+ dragSession->UpdateDragEffect();
+ }
+ return IPC_OK();
+}
+
+PContentPermissionRequestParent*
+ContentParent::AllocPContentPermissionRequestParent(
+ const nsTArray<PermissionRequest>& aRequests,
+ const IPC::Principal& aPrincipal, const IPC::Principal& aTopLevelPrincipal,
+ const bool& aIsHandlingUserInput,
+ const bool& aMaybeUnsafePermissionDelegate, const TabId& aTabId) {
+ ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+ RefPtr<BrowserParent> tp =
+ cpm->GetTopLevelBrowserParentByProcessAndTabId(this->ChildID(), aTabId);
+ if (!tp) {
+ return nullptr;
+ }
+
+ nsIPrincipal* topPrincipal = aTopLevelPrincipal;
+ if (!topPrincipal) {
+ nsCOMPtr<nsIPrincipal> principal = tp->GetContentPrincipal();
+ topPrincipal = principal;
+ }
+ return nsContentPermissionUtils::CreateContentPermissionRequestParent(
+ aRequests, tp->GetOwnerElement(), aPrincipal, topPrincipal,
+ aIsHandlingUserInput, aMaybeUnsafePermissionDelegate, aTabId);
+}
+
+bool ContentParent::DeallocPContentPermissionRequestParent(
+ PContentPermissionRequestParent* actor) {
+ nsContentPermissionUtils::NotifyRemoveContentPermissionRequestParent(actor);
+ delete actor;
+ return true;
+}
+
+PWebBrowserPersistDocumentParent*
+ContentParent::AllocPWebBrowserPersistDocumentParent(
+ PBrowserParent* aBrowser, const MaybeDiscarded<BrowsingContext>& aContext) {
+ return new WebBrowserPersistDocumentParent();
+}
+
+bool ContentParent::DeallocPWebBrowserPersistDocumentParent(
+ PWebBrowserPersistDocumentParent* aActor) {
+ delete aActor;
+ return true;
+}
+
+mozilla::ipc::IPCResult ContentParent::CommonCreateWindow(
+ PBrowserParent* aThisTab, BrowsingContext* aParent, bool aSetOpener,
+ const uint32_t& aChromeFlags, const bool& aCalledFromJS,
+ const bool& aWidthSpecified, const bool& aForPrinting,
+ const bool& aForWindowDotPrint, nsIURI* aURIToLoad,
+ const nsCString& aFeatures, const float& aFullZoom,
+ BrowserParent* aNextRemoteBrowser, const nsString& aName, nsresult& aResult,
+ nsCOMPtr<nsIRemoteTab>& aNewRemoteTab, bool* aWindowIsNew,
+ int32_t& aOpenLocation, nsIPrincipal* aTriggeringPrincipal,
+ nsIReferrerInfo* aReferrerInfo, bool aLoadURI,
+ nsIContentSecurityPolicy* aCsp, const OriginAttributes& aOriginAttributes) {
+ // The content process should never be in charge of computing whether or
+ // not a window should be private - the parent will do that.
+ const uint32_t badFlags = nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW |
+ nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW |
+ nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME;
+ if (!!(aChromeFlags & badFlags)) {
+ return IPC_FAIL(this, "Forbidden aChromeFlags passed");
+ }
+
+ RefPtr<nsOpenWindowInfo> openInfo = new nsOpenWindowInfo();
+ openInfo->mForceNoOpener = !aSetOpener;
+ openInfo->mParent = aParent;
+ openInfo->mIsRemote = true;
+ openInfo->mIsForPrinting = aForPrinting;
+ openInfo->mIsForWindowDotPrint = aForWindowDotPrint;
+ openInfo->mNextRemoteBrowser = aNextRemoteBrowser;
+ openInfo->mOriginAttributes = aOriginAttributes;
+
+ MOZ_ASSERT_IF(aForWindowDotPrint, aForPrinting);
+
+ RefPtr<BrowserParent> topParent = BrowserParent::GetFrom(aThisTab);
+ while (topParent && topParent->GetBrowserBridgeParent()) {
+ topParent = topParent->GetBrowserBridgeParent()->Manager();
+ }
+ RefPtr<BrowserHost> thisBrowserHost =
+ topParent ? topParent->GetBrowserHost() : nullptr;
+ MOZ_ASSERT_IF(topParent, thisBrowserHost);
+ RefPtr<BrowsingContext> topBC =
+ topParent ? topParent->GetBrowsingContext() : nullptr;
+ MOZ_ASSERT_IF(topParent, topBC);
+
+ // The content process should have set its remote and fission flags correctly.
+ if (topBC) {
+ if ((!!(aChromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW) !=
+ topBC->UseRemoteTabs()) ||
+ (!!(aChromeFlags & nsIWebBrowserChrome::CHROME_FISSION_WINDOW) !=
+ topBC->UseRemoteSubframes())) {
+ return IPC_FAIL(this, "Unexpected aChromeFlags passed");
+ }
+
+ if (!aOriginAttributes.EqualsIgnoringFPD(topBC->OriginAttributesRef())) {
+ return IPC_FAIL(this, "Passed-in OriginAttributes does not match opener");
+ }
+ }
+
+ nsCOMPtr<nsIContent> frame;
+ if (topParent) {
+ frame = topParent->GetOwnerElement();
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> outerWin;
+ if (frame) {
+ outerWin = frame->OwnerDoc()->GetWindow();
+
+ // If our chrome window is in the process of closing, don't try to open a
+ // new tab in it.
+ if (outerWin && outerWin->Closed()) {
+ outerWin = nullptr;
+ }
+ }
+
+ nsCOMPtr<nsIBrowserDOMWindow> browserDOMWin;
+ if (topParent) {
+ browserDOMWin = topParent->GetBrowserDOMWindow();
+ }
+
+ // If we haven't found a chrome window to open in, just use the most recently
+ // opened one.
+ if (!outerWin) {
+ outerWin = nsContentUtils::GetMostRecentNonPBWindow();
+ if (NS_WARN_IF(!outerWin)) {
+ aResult = NS_ERROR_FAILURE;
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsIDOMChromeWindow> rootChromeWin = do_QueryInterface(outerWin);
+ if (rootChromeWin) {
+ rootChromeWin->GetBrowserDOMWindow(getter_AddRefs(browserDOMWin));
+ }
+ }
+
+ aOpenLocation = nsWindowWatcher::GetWindowOpenLocation(
+ outerWin, aChromeFlags, aCalledFromJS, aWidthSpecified, aForPrinting);
+
+ MOZ_ASSERT(aOpenLocation == nsIBrowserDOMWindow::OPEN_NEWTAB ||
+ aOpenLocation == nsIBrowserDOMWindow::OPEN_NEWWINDOW ||
+ aOpenLocation == nsIBrowserDOMWindow::OPEN_PRINT_BROWSER);
+
+ if (NS_WARN_IF(!browserDOMWin)) {
+ // Opening in the same window or headless requires an nsIBrowserDOMWindow.
+ aOpenLocation = nsIBrowserDOMWindow::OPEN_NEWWINDOW;
+ }
+
+ if (aOpenLocation == nsIBrowserDOMWindow::OPEN_NEWTAB ||
+ aOpenLocation == nsIBrowserDOMWindow::OPEN_PRINT_BROWSER) {
+ RefPtr<Element> openerElement = do_QueryObject(frame);
+
+ nsCOMPtr<nsIOpenURIInFrameParams> params =
+ new nsOpenURIInFrameParams(openInfo, openerElement);
+ params->SetReferrerInfo(aReferrerInfo);
+ MOZ_ASSERT(aTriggeringPrincipal, "need a valid triggeringPrincipal");
+ params->SetTriggeringPrincipal(aTriggeringPrincipal);
+ params->SetCsp(aCsp);
+
+ RefPtr<Element> el;
+
+ if (aLoadURI) {
+ aResult = browserDOMWin->OpenURIInFrame(aURIToLoad, params, aOpenLocation,
+ nsIBrowserDOMWindow::OPEN_NEW,
+ aName, getter_AddRefs(el));
+ } else {
+ aResult = browserDOMWin->CreateContentWindowInFrame(
+ aURIToLoad, params, aOpenLocation, nsIBrowserDOMWindow::OPEN_NEW,
+ aName, getter_AddRefs(el));
+ }
+ RefPtr<nsFrameLoaderOwner> frameLoaderOwner = do_QueryObject(el);
+ if (NS_SUCCEEDED(aResult) && frameLoaderOwner) {
+ RefPtr<nsFrameLoader> frameLoader = frameLoaderOwner->GetFrameLoader();
+ if (frameLoader) {
+ aNewRemoteTab = frameLoader->GetRemoteTab();
+ // At this point, it's possible the inserted frameloader hasn't gone
+ // through layout yet. To ensure that the dimensions that we send down
+ // when telling the frameloader to display will be correct (instead of
+ // falling back to a 10x10 default), we force layout if necessary to get
+ // the most up-to-date dimensions. See bug 1358712 for details.
+ frameLoader->ForceLayoutIfNecessary();
+ }
+ } else if (NS_SUCCEEDED(aResult) && !frameLoaderOwner) {
+ // Fall through to the normal window opening code path when there is no
+ // window which we can open a new tab in.
+ aOpenLocation = nsIBrowserDOMWindow::OPEN_NEWWINDOW;
+ } else {
+ *aWindowIsNew = false;
+ }
+
+ // If we didn't retarget our window open into a new window, we should return
+ // now.
+ if (aOpenLocation != nsIBrowserDOMWindow::OPEN_NEWWINDOW) {
+ return IPC_OK();
+ }
+ }
+
+ nsCOMPtr<nsPIWindowWatcher> pwwatch =
+ do_GetService(NS_WINDOWWATCHER_CONTRACTID, &aResult);
+ if (NS_WARN_IF(NS_FAILED(aResult))) {
+ return IPC_OK();
+ }
+
+ aResult = pwwatch->OpenWindowWithRemoteTab(thisBrowserHost, aFeatures,
+ aCalledFromJS, aFullZoom, 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.
+ //
+ // This involves doing a bit of gymnastics in order to get at the FrameLoader,
+ // so we scope this to avoid polluting the main function scope.
+ {
+ nsCOMPtr<Element> frameElement = newBrowserHost->GetOwnerElement();
+ MOZ_ASSERT(frameElement);
+ 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& aWidthSpecified,
+ const bool& aForPrinting, const bool& aForPrintPreview, nsIURI* aURIToLoad,
+ const nsCString& aFeatures, const float& aFullZoom,
+ const IPC::Principal& aTriggeringPrincipal, nsIContentSecurityPolicy* aCsp,
+ nsIReferrerInfo* aReferrerInfo, const OriginAttributes& aOriginAttributes,
+ CreateWindowResolver&& aResolve) {
+ if (!ValidatePrincipal(aTriggeringPrincipal,
+ {ValidatePrincipalOptions::AllowSystem})) {
+ LogFailedPrincipalValidationInfo(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;
+ cwi.hasSiblings() = false;
+
+ // 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 discarded.
+ if (aParent.IsDiscarded()) {
+ 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");
+ }
+
+ RefPtr<BrowsingContext> newBCOpener = newBC->GetOpener();
+ if (newBCOpener && aParent.get() != newBCOpener) {
+ 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, aParent.get(), !!newBCOpener, aChromeFlags, aCalledFromJS,
+ aWidthSpecified, aForPrinting, aForPrintPreview, aURIToLoad, aFeatures,
+ aFullZoom, 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());
+
+ newTab->SwapFrameScriptsFrom(cwi.frameScripts());
+ newTab->MaybeShowFrame();
+
+ nsCOMPtr<nsIWidget> widget = newTab->GetWidget();
+ if (widget) {
+ cwi.dimensions() = newTab->GetDimensionInfo();
+ }
+
+ cwi.maxTouchPoints() = newTab->GetMaxTouchPoints();
+ cwi.hasSiblings() = (openLocation == nsIBrowserDOMWindow::OPEN_NEWTAB);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvCreateWindowInDifferentProcess(
+ PBrowserParent* aThisTab, const MaybeDiscarded<BrowsingContext>& aParent,
+ const uint32_t& aChromeFlags, const bool& aCalledFromJS,
+ const bool& aWidthSpecified, nsIURI* aURIToLoad, const nsCString& aFeatures,
+ const float& aFullZoom, const nsString& 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 discarded.
+ if (NS_WARN_IF(aParent.IsDiscarded())) {
+ 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, aParent.get(), /* aSetOpener = */ false, aChromeFlags,
+ aCalledFromJS, aWidthSpecified, /* aForPrinting = */ false,
+ /* aForPrintPreview = */ false, aURIToLoad, aFeatures, aFullZoom,
+ /* 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 nsCString& aProfile) {
+#ifdef MOZ_GECKO_PROFILER
+ nsCOMPtr<nsIProfiler> profiler(
+ do_GetService("@mozilla.org/tools/profiler;1"));
+ profiler->ReceiveShutdownProfile(aProfile);
+#endif
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvGetGraphicsDeviceInitData(
+ ContentDeviceData* aOut) {
+ gfxPlatform::GetPlatform()->BuildContentDeviceData(aOut);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvGetOutputColorProfileData(
+ nsTArray<uint8_t>* aOutputColorProfileData) {
+ (*aOutputColorProfileData) =
+ gfxPlatform::GetPlatform()->GetPlatformCMSOutputProfileData();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvGetFontListShmBlock(
+ const uint32_t& aGeneration, const uint32_t& aIndex,
+ base::SharedMemoryHandle* aOut) {
+ auto* fontList = gfxPlatformFontList::PlatformFontList();
+ MOZ_RELEASE_ASSERT(fontList, "gfxPlatformFontList not initialized?");
+ fontList->ShareFontListShmBlockToProcess(aGeneration, aIndex, Pid(), aOut);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvInitializeFamily(
+ const uint32_t& aGeneration, const uint32_t& aFamilyIndex,
+ const bool& aLoadCmaps) {
+ auto* fontList = gfxPlatformFontList::PlatformFontList();
+ MOZ_RELEASE_ASSERT(fontList, "gfxPlatformFontList not initialized?");
+ fontList->InitializeFamily(aGeneration, aFamilyIndex, aLoadCmaps);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvSetCharacterMap(
+ const uint32_t& aGeneration, const mozilla::fontlist::Pointer& aFacePtr,
+ const gfxSparseBitSet& aMap) {
+ auto* fontList = gfxPlatformFontList::PlatformFontList();
+ MOZ_RELEASE_ASSERT(fontList, "gfxPlatformFontList not initialized?");
+ fontList->SetCharacterMap(aGeneration, aFacePtr, aMap);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvInitOtherFamilyNames(
+ const uint32_t& aGeneration, const bool& aDefer, bool* aLoaded) {
+ auto* fontList = gfxPlatformFontList::PlatformFontList();
+ MOZ_RELEASE_ASSERT(fontList, "gfxPlatformFontList not initialized?");
+ *aLoaded = fontList->InitOtherFamilyNames(aGeneration, aDefer);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvSetupFamilyCharMap(
+ const uint32_t& aGeneration, const mozilla::fontlist::Pointer& aFamilyPtr) {
+ auto* fontList = gfxPlatformFontList::PlatformFontList();
+ MOZ_RELEASE_ASSERT(fontList, "gfxPlatformFontList not initialized?");
+ fontList->SetupFamilyCharMap(aGeneration, aFamilyPtr);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvStartCmapLoading(
+ const uint32_t& aGeneration, const uint32_t& aStartIndex) {
+ auto* fontList = gfxPlatformFontList::PlatformFontList();
+ MOZ_RELEASE_ASSERT(fontList, "gfxPlatformFontList not initialized?");
+ fontList->StartCmapLoading(aGeneration, aStartIndex);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvGetHyphDict(
+ nsIURI* aURI, base::SharedMemoryHandle* aOutHandle, uint32_t* aOutSize) {
+ if (!aURI) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ nsHyphenationManager::Instance()->ShareHyphDictToProcess(
+ aURI, Pid(), aOutHandle, aOutSize);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvGraphicsError(
+ const nsCString& aError) {
+ gfx::LogForwarder* lf = gfx::Factory::GetLogForwarder();
+ if (lf) {
+ std::stringstream message;
+ message << "CP+" << aError.get();
+ 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:
+ MOZ_ASSERT_UNREACHABLE("unknown crash guard type");
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ 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 nsString& 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 nsCString& aScope, const IPC::Principal& aPrincipal,
+ const nsString& aMessageId) {
+ if (!ValidatePrincipal(aPrincipal)) {
+ LogFailedPrincipalValidationInfo(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 nsCString& aScope, const IPC::Principal& aPrincipal,
+ const nsString& aMessageId, nsTArray<uint8_t>&& aData) {
+ if (!ValidatePrincipal(aPrincipal)) {
+ LogFailedPrincipalValidationInfo(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 nsCString& aScope, const IPC::Principal& aPrincipal) {
+ if (!ValidatePrincipal(aPrincipal)) {
+ LogFailedPrincipalValidationInfo(aPrincipal, __func__);
+ }
+ PushSubscriptionChangeDispatcher dispatcher(aScope, aPrincipal);
+ Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers()));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvPushError(
+ const nsCString& aScope, const IPC::Principal& aPrincipal,
+ const nsString& aMessage, const uint32_t& aFlags) {
+ if (!ValidatePrincipal(aPrincipal)) {
+ LogFailedPrincipalValidationInfo(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 nsCString& aScope, const IPC::Principal& aPrincipal) {
+ if (!ValidatePrincipal(aPrincipal)) {
+ LogFailedPrincipalValidationInfo(aPrincipal, __func__);
+ }
+ PushSubscriptionModifiedDispatcher dispatcher(aScope, aPrincipal);
+ Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObservers()));
+ return IPC_OK();
+}
+
+/* static */
+void ContentParent::BroadcastBlobURLRegistration(
+ const nsACString& aURI, BlobImpl* aBlobImpl, nsIPrincipal* aPrincipal,
+ const Maybe<nsID>& aAgentClusterId, ContentParent* aIgnoreThisCP) {
+ nsAutoCString origin;
+ nsresult rv = aPrincipal->GetOrigin(origin);
+ NS_ENSURE_SUCCESS_VOID(rv);
+
+ uint64_t originHash = ComputeLoadedOriginHash(aPrincipal);
+
+ bool toBeSent = StringBeginsWith(origin, "moz-extension://"_ns) ||
+ aPrincipal->IsSystemPrincipal();
+
+ nsCString uri(aURI);
+ IPC::Principal principal(aPrincipal);
+
+ for (auto* cp : AllProcesses(eLive)) {
+ if (cp != aIgnoreThisCP) {
+ if (!toBeSent && !cp->mLoadedOriginHashes.Contains(originHash)) {
+ continue;
+ }
+
+ nsresult rv = cp->TransmitPermissionsForPrincipal(principal);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ break;
+ }
+
+ IPCBlob ipcBlob;
+ rv = IPCBlobUtils::Serialize(aBlobImpl, cp, ipcBlob);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ break;
+ }
+
+ Unused << cp->SendBlobURLRegistration(uri, ipcBlob, principal,
+ aAgentClusterId);
+ }
+ }
+}
+
+/* static */
+void ContentParent::BroadcastBlobURLUnregistration(
+ const nsACString& aURI, nsIPrincipal* aPrincipal,
+ ContentParent* aIgnoreThisCP) {
+ nsAutoCString origin;
+ nsresult rv = aPrincipal->GetOrigin(origin);
+ NS_ENSURE_SUCCESS_VOID(rv);
+
+ uint64_t originHash = ComputeLoadedOriginHash(aPrincipal);
+
+ bool toBeSent = StringBeginsWith(origin, "moz-extension://"_ns) ||
+ aPrincipal->IsSystemPrincipal();
+
+ 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 nsCString& aURI, const IPCBlob& aBlob, const Principal& aPrincipal,
+ const Maybe<nsID>& aAgentClusterId) {
+ if (!ValidatePrincipal(aPrincipal, {ValidatePrincipalOptions::AllowSystem})) {
+ LogFailedPrincipalValidationInfo(aPrincipal, __func__);
+ }
+ RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(aBlob);
+ if (NS_WARN_IF(!blobImpl)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ BlobURLProtocolHandler::AddDataEntry(aURI, aPrincipal, aAgentClusterId,
+ blobImpl);
+ BroadcastBlobURLRegistration(aURI, blobImpl, aPrincipal, aAgentClusterId,
+ this);
+
+ // We want to store this blobURL, so we can unregister it if the child
+ // crashes.
+ mBlobURLs.AppendElement(aURI);
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+ContentParent::RecvUnstoreAndBroadcastBlobURLUnregistration(
+ const nsCString& aURI, const Principal& aPrincipal) {
+ if (!ValidatePrincipal(aPrincipal, {ValidatePrincipalOptions::AllowSystem})) {
+ LogFailedPrincipalValidationInfo(aPrincipal, __func__);
+ }
+ BlobURLProtocolHandler::RemoveDataEntry(aURI, false /* Don't broadcast */);
+ BroadcastBlobURLUnregistration(aURI, aPrincipal, this);
+ mBlobURLs.RemoveElement(aURI);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvGetA11yContentId(
+ uint32_t* aContentId) {
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+ *aContentId = a11y::AccessibleWrap::GetContentProcessIdFor(ChildID());
+ MOZ_ASSERT(*aContentId);
+ return IPC_OK();
+#else
+ return IPC_FAIL_NO_REASON(this);
+#endif
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvA11yHandlerControl(
+ const uint32_t& aPid, const IHandlerControlHolder& aHandlerControl) {
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+ MOZ_ASSERT(!aHandlerControl.IsNull());
+ RefPtr<IHandlerControl> proxy(aHandlerControl.Get());
+ a11y::AccessibleWrap::SetHandlerControl(aPid, std::move(proxy));
+ return IPC_OK();
+#else
+ return IPC_FAIL_NO_REASON(this);
+#endif
+}
+
+bool ContentParent::HandleWindowsMessages(const Message& aMsg) const {
+ MOZ_ASSERT(aMsg.is_sync());
+
+#ifdef ACCESSIBILITY
+ // a11y messages can be triggered by windows messages, which means if we
+ // allow handling windows messages while we wait for the response to a sync
+ // a11y message we can reenter the ipc message sending code.
+ if (a11y::PDocAccessible::PDocAccessibleStart < aMsg.type() &&
+ a11y::PDocAccessible::PDocAccessibleEnd > aMsg.type()) {
+ return false;
+ }
+#endif
+
+ return true;
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvGetFilesRequest(
+ const nsID& aUUID, const nsString& aDirectoryPath,
+ const bool& aRecursiveFlag) {
+ MOZ_ASSERT(!mGetFilesPendingRequests.GetWeak(aUUID));
+
+ if (!mozilla::Preferences::GetBool("dom.filesystem.pathcheck.disabled",
+ false)) {
+ RefPtr<FileSystemSecurity> fss = FileSystemSecurity::Get();
+ if (NS_WARN_IF(!fss || !fss->ContentProcessHasAccessTo(ChildID(),
+ aDirectoryPath))) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ }
+
+ 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_NO_REASON(this);
+ }
+ return IPC_OK();
+ }
+
+ mGetFilesPendingRequests.Put(aUUID, std::move(helper));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvDeleteGetFilesRequest(
+ const nsID& aUUID) {
+ mGetFilesPendingRequests.Remove(aUUID);
+ return IPC_OK();
+}
+
+void ContentParent::SendGetFilesResponseAndForget(
+ const nsID& aUUID, const GetFilesResponseResult& aResult) {
+ if (mGetFilesPendingRequests.Remove(aUUID)) {
+ Unused << SendGetFilesResponse(aUUID, aResult);
+ }
+}
+
+void ContentParent::PaintTabWhileInterruptingJS(
+ BrowserParent* aBrowserParent, const layers::LayersObserverEpoch& aEpoch) {
+ if (!mHangMonitorActor) {
+ return;
+ }
+ ProcessHangMonitor::PaintWhileInterruptingJS(mHangMonitorActor,
+ aBrowserParent, aEpoch);
+}
+
+void ContentParent::CancelContentJSExecutionIfRunning(
+ BrowserParent* aBrowserParent, nsIRemoteTab::NavigationType aNavigationType,
+ const CancelContentJSOptions& aCancelContentJSOptions) {
+ if (!mHangMonitorActor) {
+ return;
+ }
+
+ ProcessHangMonitor::CancelContentJSExecutionIfRunning(
+ mHangMonitorActor, aBrowserParent, aNavigationType,
+ aCancelContentJSOptions);
+}
+
+void ContentParent::UpdateCookieStatus(nsIChannel* aChannel) {
+ PNeckoParent* neckoParent = LoneManagedOrNullAsserts(ManagedPNeckoParent());
+ PCookieServiceParent* csParent =
+ LoneManagedOrNullAsserts(neckoParent->ManagedPCookieServiceParent());
+ if (csParent) {
+ auto* cs = static_cast<CookieServiceParent*>(csParent);
+ cs->TrackCookieLoad(aChannel);
+ }
+}
+
+nsresult ContentParent::AboutToLoadHttpFtpDocumentForChild(
+ nsIChannel* aChannel, bool* aShouldWaitForPermissionCookieUpdate) {
+ MOZ_ASSERT(aChannel);
+
+ if (aShouldWaitForPermissionCookieUpdate) {
+ *aShouldWaitForPermissionCookieUpdate = false;
+ }
+
+ nsresult rv;
+ bool isDocument = aChannel->IsDocument();
+ if (!isDocument) {
+ // We may be looking at a nsIHttpChannel which has isMainDocumentChannel set
+ // (e.g. the internal http channel for a view-source: load.).
+ nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(aChannel);
+ if (httpChannel) {
+ rv = httpChannel->GetIsMainDocumentChannel(&isDocument);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+ if (!isDocument) {
+ return NS_OK;
+ }
+
+ // Get the principal for the channel result, so that we can get the permission
+ // key for the document which will be created from this response.
+ nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
+ if (NS_WARN_IF(!ssm)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIPrincipal> principal;
+ rv = ssm->GetChannelResultPrincipal(aChannel, getter_AddRefs(principal));
+ 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);
+
+ rv = TransmitPermissionsForPrincipal(principal);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsLoadFlags newLoadFlags;
+ aChannel->GetLoadFlags(&newLoadFlags);
+ if (newLoadFlags & nsIRequest::LOAD_DOCUMENT_NEEDS_COOKIE) {
+ UpdateCookieStatus(aChannel);
+ }
+
+ RefPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
+ RefPtr<BrowsingContext> browsingContext;
+ rv = loadInfo->GetTargetBrowsingContext(getter_AddRefs(browsingContext));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!NextGenLocalStorageEnabled()) {
+ return NS_OK;
+ }
+
+ if (principal->GetIsContentPrincipal()) {
+ nsCOMPtr<nsILocalStorageManager> lsm =
+ do_GetService("@mozilla.org/dom/localStorage-manager;1");
+ if (NS_WARN_IF(!lsm)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIPrincipal> storagePrincipal;
+ rv = ssm->GetChannelResultStoragePrincipal(
+ aChannel, getter_AddRefs(storagePrincipal));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RefPtr<Promise> dummy;
+ rv = lsm->Preload(storagePrincipal, nullptr, getter_AddRefs(dummy));
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Failed to preload local storage!");
+ }
+ }
+
+ return NS_OK;
+}
+
+nsresult ContentParent::TransmitPermissionsForPrincipal(
+ nsIPrincipal* aPrincipal) {
+ // Create the key, and send it down to the content process.
+ nsTArray<std::pair<nsCString, nsCString>> pairs =
+ PermissionManager::GetAllKeysForPrincipal(aPrincipal);
+ MOZ_ASSERT(pairs.Length() >= 1);
+ for (auto& pair : pairs) {
+ EnsurePermissionsByKey(pair.first, pair.second);
+ }
+
+ return NS_OK;
+}
+
+void ContentParent::TransmitBlobURLsForPrincipal(nsIPrincipal* aPrincipal) {
+ uint64_t originHash = ComputeLoadedOriginHash(aPrincipal);
+
+ if (!mLoadedOriginHashes.Contains(originHash)) {
+ mLoadedOriginHashes.AppendElement(originHash);
+
+ nsTArray<BlobURLRegistrationData> registrations;
+ BlobURLProtocolHandler::ForEachBlobURL(
+ [&](BlobImpl* aBlobImpl, nsIPrincipal* aBlobPrincipal,
+ const Maybe<nsID>& aAgentClusterId, const nsACString& aURI,
+ bool aRevoked) {
+ if (!aPrincipal->Subsumes(aBlobPrincipal)) {
+ return true;
+ }
+
+ IPCBlob ipcBlob;
+ nsresult rv = IPCBlobUtils::Serialize(aBlobImpl, this, ipcBlob);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return false;
+ }
+
+ registrations.AppendElement(BlobURLRegistrationData(
+ nsCString(aURI), ipcBlob, aPrincipal, aAgentClusterId, aRevoked));
+
+ rv = TransmitPermissionsForPrincipal(aPrincipal);
+ Unused << NS_WARN_IF(NS_FAILED(rv));
+ return true;
+ });
+
+ if (!registrations.IsEmpty()) {
+ Unused << SendInitBlobURLs(registrations);
+ }
+ }
+}
+
+void ContentParent::TransmitBlobDataIfBlobURL(nsIURI* aURI,
+ nsIPrincipal* aPrincipal) {
+ MOZ_ASSERT(aURI);
+ MOZ_ASSERT(aPrincipal);
+
+ if (!IsBlobURI(aURI)) {
+ return;
+ }
+
+ TransmitBlobURLsForPrincipal(aPrincipal);
+}
+
+void ContentParent::EnsurePermissionsByKey(const nsCString& aKey,
+ const nsCString& 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.Contains(aKey)) {
+ return;
+ }
+ mActivePermissionKeys.PutEntry(aKey);
+
+ nsTArray<IPC::Permission> perms;
+ if (permManager->GetPermissionsFromOriginOrKey(aOrigin, aKey, perms)) {
+ Unused << SendSetPermissionsWithKey(aKey, perms);
+ }
+}
+
+bool ContentParent::NeedsPermissionsUpdate(
+ const nsACString& aPermissionKey) const {
+ return mActivePermissionKeys.Contains(aPermissionKey);
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvAccumulateChildHistograms(
+ nsTArray<HistogramAccumulation>&& aAccumulations) {
+ TelemetryIPC::AccumulateChildHistograms(GetTelemetryProcessID(mRemoteType),
+ aAccumulations);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvAccumulateChildKeyedHistograms(
+ nsTArray<KeyedHistogramAccumulation>&& aAccumulations) {
+ TelemetryIPC::AccumulateChildKeyedHistograms(
+ GetTelemetryProcessID(mRemoteType), aAccumulations);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvUpdateChildScalars(
+ nsTArray<ScalarAction>&& aScalarActions) {
+ TelemetryIPC::UpdateChildScalars(GetTelemetryProcessID(mRemoteType),
+ aScalarActions);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvUpdateChildKeyedScalars(
+ nsTArray<KeyedScalarAction>&& aScalarActions) {
+ TelemetryIPC::UpdateChildKeyedScalars(GetTelemetryProcessID(mRemoteType),
+ aScalarActions);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvRecordChildEvents(
+ nsTArray<mozilla::Telemetry::ChildEventData>&& aEvents) {
+ TelemetryIPC::RecordChildEvents(GetTelemetryProcessID(mRemoteType), aEvents);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvRecordDiscardedData(
+ const mozilla::Telemetry::DiscardedData& aDiscardedData) {
+ TelemetryIPC::RecordDiscardedData(GetTelemetryProcessID(mRemoteType),
+ aDiscardedData);
+ return IPC_OK();
+}
+
+//////////////////////////////////////////////////////////////////
+// PURLClassifierParent
+
+PURLClassifierParent* ContentParent::AllocPURLClassifierParent(
+ const Principal& 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, const Principal& 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)) {
+ LogFailedPrincipalValidationInfo(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) {
+ NS_WARNING("aURI should not be null");
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ auto* actor = static_cast<URLClassifierLocalParent*>(aActor);
+ return actor->StartClassify(aURI, features);
+}
+
+bool ContentParent::DeallocPURLClassifierLocalParent(
+ PURLClassifierLocalParent* aActor) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aActor);
+
+ RefPtr<URLClassifierLocalParent> actor =
+ dont_AddRef(static_cast<URLClassifierLocalParent*>(aActor));
+ return true;
+}
+
+PLoginReputationParent* ContentParent::AllocPLoginReputationParent(
+ nsIURI* aURI) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ RefPtr<LoginReputationParent> actor = new LoginReputationParent();
+ return actor.forget().take();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvPLoginReputationConstructor(
+ PLoginReputationParent* aActor, nsIURI* aURI) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aActor);
+
+ if (!aURI) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ auto* actor = static_cast<LoginReputationParent*>(aActor);
+ return actor->QueryReputation(aURI);
+}
+
+bool ContentParent::DeallocPLoginReputationParent(
+ PLoginReputationParent* aActor) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aActor);
+
+ RefPtr<LoginReputationParent> actor =
+ dont_AddRef(static_cast<LoginReputationParent*>(aActor));
+ return true;
+}
+
+PSessionStorageObserverParent*
+ContentParent::AllocPSessionStorageObserverParent() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ return mozilla::dom::AllocPSessionStorageObserverParent();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvPSessionStorageObserverConstructor(
+ PSessionStorageObserverParent* aActor) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aActor);
+
+ if (!mozilla::dom::RecvPSessionStorageObserverConstructor(aActor)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ return IPC_OK();
+}
+
+bool ContentParent::DeallocPSessionStorageObserverParent(
+ PSessionStorageObserverParent* aActor) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aActor);
+
+ return mozilla::dom::DeallocPSessionStorageObserverParent(aActor);
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvMaybeReloadPlugins() {
+ RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
+ pluginHost->ReloadPlugins();
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvDeviceReset() {
+ GPUProcessManager* pm = GPUProcessManager::Get();
+ if (pm) {
+ pm->SimulateDeviceReset();
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvBHRThreadHang(
+ const HangDetails& aDetails) {
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ // Copy the HangDetails recieved over the network into a nsIHangDetails, and
+ // then fire our own observer notification.
+ // XXX: We should be able to avoid this potentially expensive copy here by
+ // moving our deserialized argument.
+ nsCOMPtr<nsIHangDetails> hangDetails =
+ new nsHangDetails(HangDetails(aDetails), PersistedToDisk::No);
+ obs->NotifyObservers(hangDetails, "bhr-thread-hang", nullptr);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvAddCertException(
+ const nsACString& aSerializedCert, uint32_t aFlags,
+ const nsACString& aHostName, int32_t aPort, bool aIsTemporary,
+ AddCertExceptionResolver&& aResolver) {
+ nsCOMPtr<nsISupports> certObj;
+ nsresult rv = NS_DeserializeObject(aSerializedCert, getter_AddRefs(certObj));
+ if (NS_SUCCEEDED(rv)) {
+ nsCOMPtr<nsIX509Cert> cert = do_QueryInterface(certObj);
+ if (!cert) {
+ rv = NS_ERROR_INVALID_ARG;
+ } else {
+ nsCOMPtr<nsICertOverrideService> overrideService =
+ do_GetService(NS_CERTOVERRIDE_CONTRACTID);
+ if (!overrideService) {
+ rv = NS_ERROR_FAILURE;
+ } else {
+ rv = overrideService->RememberValidityOverride(aHostName, aPort, cert,
+ aFlags, aIsTemporary);
+ }
+ }
+ }
+ aResolver(rv);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+ContentParent::RecvAutomaticStorageAccessPermissionCanBeGranted(
+ const Principal& aPrincipal,
+ AutomaticStorageAccessPermissionCanBeGrantedResolver&& aResolver) {
+ if (!ValidatePrincipal(aPrincipal)) {
+ LogFailedPrincipalValidationInfo(aPrincipal, __func__);
+ }
+ aResolver(Document::AutomaticStorageAccessPermissionCanBeGranted(aPrincipal));
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+ContentParent::RecvStorageAccessPermissionGrantedForOrigin(
+ uint64_t aTopLevelWindowId,
+ const MaybeDiscarded<BrowsingContext>& aParentContext,
+ const Principal& aTrackingPrincipal, const nsCString& aTrackingOrigin,
+ const int& aAllowMode,
+ const Maybe<ContentBlockingNotifier::StorageAccessPermissionGrantedReason>&
+ aReason,
+ StorageAccessPermissionGrantedForOriginResolver&& aResolver) {
+ if (aParentContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+
+ // 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());
+ }
+
+ ContentBlocking::SaveAccessForOriginOnParentProcess(
+ aTopLevelWindowId, aParentContext.get_canonical(), aTrackingPrincipal,
+ aTrackingOrigin, aAllowMode)
+ ->Then(
+ GetCurrentSerialEventTarget(), __func__,
+ [aResolver = std::move(aResolver)](
+ ContentBlocking::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, const Principal& aTrackingPrincipal,
+ const nsCString& aTrackingOrigin, uint32_t aCookieBehavior,
+ const ContentBlockingNotifier::StorageAccessPermissionGrantedReason&
+ aReason,
+ CompleteAllowAccessForResolver&& aResolver) {
+ if (aParentContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+
+ ContentBlocking::CompleteAllowAccessFor(
+ aParentContext.get_canonical(), aTopLevelWindowId, aTrackingPrincipal,
+ aTrackingOrigin, aCookieBehavior, aReason, nullptr)
+ ->Then(GetCurrentSerialEventTarget(), __func__,
+ [aResolver = std::move(aResolver)](
+ ContentBlocking::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::RecvStoreUserInteractionAsPermission(
+ const Principal& aPrincipal) {
+ if (!ValidatePrincipal(aPrincipal)) {
+ LogFailedPrincipalValidationInfo(aPrincipal, __func__);
+ }
+ ContentBlockingUserInteraction::Observe(aPrincipal);
+ 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::RecvGetModulesTrust(
+ ModulePaths&& aModPaths, bool aRunAtNormalPriority,
+ GetModulesTrustResolver&& aResolver) {
+#if defined(XP_WIN)
+ 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();
+#else
+ return IPC_FAIL(this, "Unsupported on this platform");
+#endif // defined(XP_WIN)
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvCreateBrowsingContext(
+ uint64_t aGroupId, BrowsingContext::IPCInitializer&& aInit) {
+ RefPtr<WindowGlobalParent> parent;
+ if (aInit.mParentId != 0) {
+ parent = WindowGlobalParent::GetByInnerWindowId(aInit.mParentId);
+ if (!parent) {
+ return IPC_FAIL(this, "Parent doesn't exist in parent process");
+ }
+ }
+
+ if (parent && parent->GetContentParent() != this) {
+ // We're trying attach a child BrowsingContext to a parent
+ // WindowContext in another process. This is illegal since the
+ // only thing that could create that child BrowsingContext is the parent
+ // window's process.
+ return IPC_FAIL(this,
+ "Must create BrowsingContext from the parent's process");
+ }
+
+ RefPtr<BrowsingContext> opener;
+ if (aInit.GetOpenerId() != 0) {
+ opener = BrowsingContext::Get(aInit.GetOpenerId());
+ if (!opener) {
+ return IPC_FAIL(this, "Opener doesn't exist in parent process");
+ }
+ }
+
+ RefPtr<BrowsingContext> child = BrowsingContext::Get(aInit.mId);
+ if (child) {
+ // This is highly suspicious. BrowsingContexts should only be created once,
+ // so finding one indicates that someone is doing something they shouldn't.
+ return IPC_FAIL(this, "A BrowsingContext with this ID already exists");
+ }
+
+ // Ensure that the passed-in BrowsingContextGroup is valid.
+ RefPtr<BrowsingContextGroup> group =
+ BrowsingContextGroup::GetOrCreate(aGroupId);
+ if (parent && parent->Group() != group) {
+ if (parent->Group()->Id() != aGroupId) {
+ return IPC_FAIL(this, "Parent has different group ID");
+ } else {
+ return IPC_FAIL(this, "Parent has different group object");
+ }
+ }
+ if (opener && opener->Group() != group) {
+ if (opener->Group()->Id() != aGroupId) {
+ return IPC_FAIL(this, "Opener has different group ID");
+ } else {
+ return IPC_FAIL(this, "Opener has different group object");
+ }
+ }
+ if (!parent && !opener && !group->Toplevels().IsEmpty()) {
+ return IPC_FAIL(this, "Unrelated context from child in stale group");
+ }
+
+ BrowsingContext::CreateFromIPC(std::move(aInit), group, this);
+ return IPC_OK();
+}
+
+bool ContentParent::CheckBrowsingContextEmbedder(CanonicalBrowsingContext* aBC,
+ const char* aOperation) const {
+ if (!aBC->IsEmbeddedInProcess(ChildID())) {
+ MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Warning,
+ ("ParentIPC: Trying to %s out of process context 0x%08" PRIx64,
+ aOperation, aBC->Id()));
+ return false;
+ }
+ return true;
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvDiscardBrowsingContext(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ DiscardBrowsingContextResolver&& aResolve) {
+ if (!aContext.IsNullOrDiscarded()) {
+ RefPtr<CanonicalBrowsingContext> context = aContext.get_canonical();
+ if (!CheckBrowsingContextEmbedder(context, "discard")) {
+ return IPC_FAIL(this, "Illegal Discard attempt");
+ }
+
+ context->Detach(/* aFromIPC */ true);
+ }
+
+ // 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::RegisterRemoteWorkerActor() {
+ auto lock = mRemoteWorkerActorData.Lock();
+ ++lock->mCount;
+}
+
+void ContentParent::UnregisterRemoveWorkerActor() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ {
+ auto lock = mRemoteWorkerActorData.Lock();
+ if (--lock->mCount) {
+ 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.
+
+ ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+ ContentParent* cp =
+ cpm->GetContentProcessById(ContentParentId(context->OwnerProcessId()));
+ 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();
+ }
+ CanonicalBrowsingContext* context = aContext.get_canonical();
+
+ ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+ ContentParent* cp =
+ cpm->GetContentProcessById(ContentParentId(context->OwnerProcessId()));
+ Unused << cp->SendWindowFocus(context, aCallerType, aActionId);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvWindowBlur(
+ 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();
+
+ ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+ ContentParent* cp =
+ cpm->GetContentProcessById(ContentParentId(context->OwnerProcessId()));
+ Unused << cp->SendWindowBlur(context);
+ 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();
+ }
+
+ CanonicalBrowsingContext* context = aContext.get_canonical();
+
+ ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+ ContentParent* cp =
+ cpm->GetContentProcessById(ContentParentId(context->OwnerProcessId()));
+ Unused << cp->SendRaiseWindow(context, aCallerType, aActionId);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvAdjustWindowFocus(
+ const MaybeDiscarded<BrowsingContext>& aContext, bool aCheckPermission,
+ bool aIsVisible) {
+ 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();
+ BrowsingContext* parent = context->GetParent();
+ if (!parent) {
+ return IPC_OK();
+ }
+
+ CanonicalBrowsingContext* canonicalParent = parent->Canonical();
+
+ ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+ ContentParent* cp = cpm->GetContentProcessById(
+ ContentParentId(canonicalParent->OwnerProcessId()));
+ Unused << cp->SendAdjustWindowFocus(context, aCheckPermission, aIsVisible);
+ 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();
+
+ ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+ ContentParent* cp =
+ cpm->GetContentProcessById(ContentParentId(context->OwnerProcessId()));
+ Unused << cp->SendClearFocus(context);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvSetFocusedBrowsingContext(
+ 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();
+
+ nsFocusManager* fm = nsFocusManager::GetFocusManager();
+ if (fm) {
+ fm->SetFocusedBrowsingContextInChrome(context);
+ BrowserParent::UpdateFocusFromBrowsingContext();
+ }
+
+ context->Group()->EachOtherParent(this, [&](ContentParent* aParent) {
+ Unused << aParent->SendSetFocusedBrowsingContext(context);
+ });
+
+ 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();
+ }
+ 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(
+ fm->GetActiveBrowsingContextInChrome(), aActionId);
+ 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();
+ }
+ 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(
+ fm->GetActiveBrowsingContextInChrome(), aActionId);
+ 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();
+ }
+ CanonicalBrowsingContext* context = aContext.get_canonical();
+
+ ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+
+ ContentParent* cp =
+ cpm->GetContentProcessById(ContentParentId(context->OwnerProcessId()));
+ 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();
+ }
+ CanonicalBrowsingContext* context = aContext.get_canonical();
+ ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+
+ 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) {
+ 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();
+ }
+
+ CanonicalBrowsingContext* focusedBrowsingContext =
+ aFocusedBrowsingContext.get_canonical();
+
+ ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+
+ // If aBrowsingContextToClear and aAncestorBrowsingContextToFocusHandled
+ // didn't get handled in the process that sent this IPC message and they
+ // aren't in the same process as aFocusedBrowsingContext, we need to split
+ // off their handling here and use SendSetFocusedElement to send them
+ // elsewhere than the blurring itself.
+
+ bool ancestorDifferent =
+ (!aAncestorBrowsingContextToFocusHandled &&
+ !aAncestorBrowsingContextToFocus.IsNullOrDiscarded() &&
+ (focusedBrowsingContext->OwnerProcessId() !=
+ aAncestorBrowsingContextToFocus.get_canonical()->OwnerProcessId()));
+ if (!aBrowsingContextToClearHandled &&
+ !aBrowsingContextToClear.IsNullOrDiscarded() &&
+ (focusedBrowsingContext->OwnerProcessId() !=
+ aBrowsingContextToClear.get_canonical()->OwnerProcessId())) {
+ MOZ_RELEASE_ASSERT(!ancestorDifferent,
+ "This combination is not supposed to happen.");
+ ContentParent* cp = cpm->GetContentProcessById(ContentParentId(
+ aBrowsingContextToClear.get_canonical()->OwnerProcessId()));
+ Unused << cp->SendSetFocusedElement(aBrowsingContextToClear, false);
+ } else if (ancestorDifferent) {
+ ContentParent* cp = cpm->GetContentProcessById(ContentParentId(
+ aAncestorBrowsingContextToFocus.get_canonical()->OwnerProcessId()));
+ Unused << cp->SendSetFocusedElement(aAncestorBrowsingContextToFocus, true);
+ }
+
+ ContentParent* cp = cpm->GetContentProcessById(
+ ContentParentId(focusedBrowsingContext->OwnerProcessId()));
+ Unused << cp->SendBlurToChild(aFocusedBrowsingContext,
+ aBrowsingContextToClear,
+ aAncestorBrowsingContextToFocus,
+ aIsLeavingDocument, aAdjustWidget, aActionId);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvMaybeExitFullscreen(
+ const MaybeDiscarded<BrowsingContext>& aContext) {
+ if (aContext.IsNullOrDiscarded()) {
+ MOZ_LOG(
+ BrowsingContext::GetLog(), LogLevel::Debug,
+ ("ParentIPC: Trying to send a message to dead or detached context"));
+ return IPC_OK();
+ }
+ CanonicalBrowsingContext* context = aContext.get_canonical();
+
+ ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+
+ ContentParent* cp =
+ cpm->GetContentProcessById(ContentParentId(context->OwnerProcessId()));
+ 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) {
+ UnpackClonedMessageDataForParent(aMessage, messageFromChild);
+
+ ClonedMessageData clonedMessageData;
+ if (BuildClonedMessageDataForParent(cp, 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
+ // BrowisngContextGroup when unsubscribing, so we don't need to do it here.
+ mGroups.RemoveEntry(aGroup);
+}
+
+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);
+}
+
+PParentToChildStreamParent* ContentParent::SendPParentToChildStreamConstructor(
+ PParentToChildStreamParent* aActor) {
+ MOZ_ASSERT(NS_IsMainThread());
+ return PContentParent::SendPParentToChildStreamConstructor(aActor);
+}
+
+PFileDescriptorSetParent* ContentParent::SendPFileDescriptorSetConstructor(
+ const FileDescriptor& aFD) {
+ MOZ_ASSERT(NS_IsMainThread());
+ return PContentParent::SendPFileDescriptorSetConstructor(aFD);
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvBlobURLDataRequest(
+ const nsCString& aBlobURL, nsIPrincipal* aTriggeringPrincipal,
+ nsIPrincipal* aLoadingPrincipal, const OriginAttributes& aOriginAttributes,
+ const Maybe<nsID>& aAgentClusterId,
+ BlobURLDataRequestResolver&& aResolver) {
+ RefPtr<BlobImpl> blobImpl;
+
+ // Since revoked blobs are also retrieved, it is possible that the blob no
+ // longer exists (due to the 5 second timeout) when execution reaches here
+ if (!BlobURLProtocolHandler::GetDataEntry(
+ aBlobURL, getter_AddRefs(blobImpl), aLoadingPrincipal,
+ aTriggeringPrincipal, aOriginAttributes, aAgentClusterId,
+ true /* AlsoIfRevoked */)) {
+ aResolver(NS_ERROR_DOM_BAD_URI);
+ return IPC_OK();
+ }
+
+ IPCBlob ipcBlob;
+ nsresult rv = IPCBlobUtils::Serialize(blobImpl, this, 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<RefPtr<nsDocShellLoadState>> loadState;
+ Maybe<bool> reloadActiveEntry;
+ if (!aContext.IsDiscarded()) {
+ aContext.get_canonical()->NotifyOnHistoryReload(
+ aForceReload, canReload, loadState, reloadActiveEntry);
+ }
+ aResolver(Tuple<const bool&, const Maybe<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) {
+ if (!aContext.IsDiscarded()) {
+ aContext.get_canonical()->SessionHistoryCommit(
+ aLoadID, aChangeID, aLoadType, aPersist, aCloneEntryChildren);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvHistoryGo(
+ const MaybeDiscarded<BrowsingContext>& aContext, int32_t aOffset,
+ uint64_t aHistoryEpoch, bool aRequireUserInteraction,
+ HistoryGoResolver&& aResolveRequestedIndex) {
+ if (!aContext.IsDiscarded()) {
+ aContext.get_canonical()->HistoryGo(
+ aOffset, aHistoryEpoch, aRequireUserInteraction, Some(ChildID()),
+ std::move(aResolveRequestedIndex));
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvSessionHistoryUpdate(
+ const MaybeDiscarded<BrowsingContext>& aContext, const int32_t& aIndex,
+ const int32_t& aLength, const nsID& aChangeID) {
+ 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();
+ context->Group()->EachParent([&](ContentParent* aParent) {
+ Unused << aParent->SendHistoryCommitIndexAndLength(aContext, aIndex,
+ aLength, aChangeID);
+ });
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvSynchronizeLayoutHistoryState(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ nsILayoutHistoryState* aState) {
+ if (aContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+
+ SessionHistoryEntry* entry =
+ aContext.get_canonical()->GetActiveSessionHistoryEntry();
+ if (entry) {
+ entry->SetLayoutHistoryState(aState);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvSessionHistoryEntryTitle(
+ const MaybeDiscarded<BrowsingContext>& aContext, const nsString& 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::RecvSessionHistoryEntryStoreWindowNameInContiguousEntries(
+ const MaybeDiscarded<BrowsingContext>& aContext, const nsString& 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::RecvGetLoadingSessionHistoryInfoFromParent(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ GetLoadingSessionHistoryInfoFromParentResolver&& aResolver) {
+ if (aContext.IsNullOrDiscarded()) {
+ return IPC_OK();
+ }
+
+ Maybe<LoadingSessionHistoryInfo> info;
+ int32_t requestedIndex = -1;
+ int32_t sessionHistoryLength = 0;
+ aContext.get_canonical()->GetLoadingSessionHistoryInfoFromParent(
+ info, &requestedIndex, &sessionHistoryLength);
+ aResolver(
+ Tuple<const mozilla::Maybe<LoadingSessionHistoryInfo>&, const int32_t&,
+ const int32_t&>(info, requestedIndex, sessionHistoryLength));
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvSetActiveSessionHistoryEntry(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const Maybe<nsPoint>& aPreviousScrollPos, SessionHistoryInfo&& aInfo,
+ uint32_t aLoadType, uint32_t aUpdatedCacheKey, const nsID& aChangeID) {
+ if (!aContext.IsDiscarded()) {
+ aContext.get_canonical()->SetActiveSessionHistoryEntry(
+ aPreviousScrollPos, &aInfo, aLoadType, aUpdatedCacheKey, aChangeID);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvReplaceActiveSessionHistoryEntry(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ SessionHistoryInfo&& aInfo) {
+ if (!aContext.IsDiscarded()) {
+ aContext.get_canonical()->ReplaceActiveSessionHistoryEntry(&aInfo);
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+ContentParent::RecvRemoveDynEntriesFromActiveSessionHistoryEntry(
+ const MaybeDiscarded<BrowsingContext>& aContext) {
+ if (!aContext.IsDiscarded()) {
+ aContext.get_canonical()->RemoveDynEntriesFromActiveSessionHistoryEntry();
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvRemoveFromSessionHistory(
+ const MaybeDiscarded<BrowsingContext>& aContext) {
+ if (!aContext.IsDiscarded()) {
+ aContext.get_canonical()->RemoveFromSessionHistory();
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvHistoryReload(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const uint32_t aReloadFlags) {
+ if (!aContext.IsDiscarded()) {
+ nsISHistory* shistory = aContext.get_canonical()->GetSessionHistory();
+ if (shistory) {
+ shistory->Reload(aReloadFlags);
+ }
+ }
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult ContentParent::RecvCommitWindowContextTransaction(
+ const MaybeDiscarded<WindowContext>& aContext,
+ WindowContext::BaseTransaction&& aTransaction, uint64_t aEpoch) {
+ // Record the new BrowsingContextFieldEpoch associated with this transaction.
+ // This should be done unconditionally, so that we're always in-sync.
+ //
+ // The order the parent process receives transactions is considered the
+ // "canonical" ordering, so we don't need to worry about doing any
+ // epoch-related validation.
+ MOZ_ASSERT(aEpoch == mBrowsingContextFieldEpoch + 1,
+ "Child process skipped an epoch?");
+ mBrowsingContextFieldEpoch = aEpoch;
+
+ return aTransaction.CommitFromIPC(aContext, this);
+}
+
+NS_IMETHODIMP ContentParent::GetChildID(uint64_t* aOut) {
+ *aOut = this->ChildID();
+ return NS_OK;
+}
+
+NS_IMETHODIMP ContentParent::GetOsPid(int32_t* aOut) {
+ *aOut = Pid();
+ return NS_OK;
+}
+
+NS_IMETHODIMP ContentParent::GetRemoteType(nsACString& aRemoteType) {
+ aRemoteType = GetRemoteType();
+ return NS_OK;
+}
+
+IPCResult ContentParent::RecvRawMessage(
+ const JSActorMessageMeta& aMeta, const Maybe<ClonedMessageData>& aData,
+ const Maybe<ClonedMessageData>& aStack) {
+ Maybe<StructuredCloneData> data;
+ if (aData) {
+ data.emplace();
+ data->BorrowFromClonedMessageDataForParent(*aData);
+ }
+ Maybe<StructuredCloneData> stack;
+ if (aStack) {
+ stack.emplace();
+ stack->BorrowFromClonedMessageDataForParent(*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;
+}
+
+already_AddRefed<JSActor> ContentParent::InitJSActor(
+ JS::HandleObject 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) {
+#ifdef MOZ_GLEAN
+ glean::FOGData(std::move(buf));
+#endif
+ 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; }
+
+} // 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;
+}
diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h
new file mode 100644
index 0000000000..3cb4ca0eed
--- /dev/null
+++ b/dom/ipc/ContentParent.h
@@ -0,0 +1,1671 @@
+/* -*- 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/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/ipc/PParentToChildStreamParent.h"
+#include "mozilla/ipc/PChildToParentStreamParent.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/DataMutex.h"
+#include "mozilla/FileUtils.h"
+#include "mozilla/HalTypes.h"
+#include "mozilla/LinkedList.h"
+#include "mozilla/MemoryReportingProcess.h"
+#include "mozilla/MozPromise.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/UniquePtr.h"
+
+#include "nsClassHashtable.h"
+#include "nsDataHashtable.h"
+#include "nsPluginTags.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;
+
+namespace mozilla {
+class PRemoteSpellcheckEngineParent;
+
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+class SandboxBroker;
+class SandboxBrokerPolicyFactory;
+#endif
+
+class PreallocatedProcessManagerImpl;
+class BenchmarkStorageParent;
+
+using mozilla::loader::PScriptCacheParent;
+
+namespace embedding {
+class PrintingParent;
+}
+
+namespace ipc {
+class CrashReporterHost;
+class PFileDescriptorSetParent;
+class TestShellParent;
+#ifdef FUZZING
+class ProtocolFuzzerHelper;
+#endif
+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;
+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 mozilla::ipc::ParentToChildStreamActorManager,
+ public ProcessActor {
+ typedef mozilla::ipc::GeckoChildProcessHost GeckoChildProcessHost;
+ typedef mozilla::ipc::PFileDescriptorSetParent PFileDescriptorSetParent;
+ 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;
+#ifdef FUZZING
+ friend class mozilla::ipc::ProtocolFuzzerHelper;
+#endif
+
+ public:
+ using LaunchError = mozilla::ipc::LaunchError;
+ using LaunchPromise =
+ mozilla::MozPromise<RefPtr<ContentParent>, LaunchError, false>;
+
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_CONTENTPARENT_IID)
+
+ static LogModule* GetLog();
+
+ /**
+ * Create a subprocess suitable for use later as a content process.
+ */
+ static RefPtr<LaunchPromise> PreallocateProcess();
+
+ /**
+ * 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();
+
+ /**
+ * 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 a JS plugin. aPluginID is the id of the
+ * JS plugin
+ * (@see nsFakePlugin::mId). There is a maximum of one process per JS plugin.
+ */
+ static already_AddRefed<ContentParent> GetNewOrUsedJSPluginProcess(
+ uint32_t aPluginID, const hal::ProcessPriority& aPriority);
+
+ /**
+ * 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 BroadcastThemeUpdate(widget::ThemeChangeKind);
+
+ static void BroadcastMediaCodecsSupportedUpdate(
+ RemoteDecodeIn aLocation,
+ const PDMFactory::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) {
+ 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);
+
+#if defined(XP_WIN)
+ /**
+ * Windows helper for firing off an update window request to a plugin
+ * instance.
+ *
+ * aWidget - the eWindowType_plugin_ipc_chrome widget associated with
+ * this plugin window.
+ */
+ static void SendAsyncUpdate(nsIWidget* aWidget);
+#endif
+
+ mozilla::ipc::IPCResult RecvCreateGMPService();
+
+ mozilla::ipc::IPCResult RecvLoadPlugin(
+ const uint32_t& aPluginId, nsresult* aRv, uint32_t* aRunID,
+ Endpoint<PPluginModuleParent>* aEndpoint);
+
+ mozilla::ipc::IPCResult RecvMaybeReloadPlugins();
+
+ mozilla::ipc::IPCResult RecvConnectPluginBridge(
+ const uint32_t& aPluginId, nsresult* aRv,
+ Endpoint<PPluginModuleParent>* aEndpoint);
+
+ mozilla::ipc::IPCResult RecvUngrabPointer(const uint32_t& aTime);
+
+ mozilla::ipc::IPCResult RecvRemovePermission(const IPC::Principal& aPrincipal,
+ const nsCString& aPermissionType,
+ nsresult* aRv);
+
+ 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;
+
+ /** 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 IsDead() const { return mLifecycleState == LifecycleState::DEAD; }
+
+ bool IsForBrowser() const { return mIsForBrowser; }
+ bool IsForJSPlugin() const {
+ return mJSPluginID != nsFakePluginTag::NOT_JSPLUGIN;
+ }
+
+ GeckoChildProcessHost* Process() const { return mSubprocess; }
+
+ nsIContentProcessInfo* ScriptableHelper() const { return mScriptableHelper; }
+
+ mozilla::dom::ProcessMessageManager* GetMessageManager() const {
+ return mMessageManager;
+ }
+
+ bool NeedsPermissionsUpdate(const nsACString& aPermissionKey) const;
+
+ /**
+ * 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);
+
+ PNeckoParent* AllocPNeckoParent();
+
+ virtual mozilla::ipc::IPCResult RecvPNeckoConstructor(
+ PNeckoParent* aActor) override {
+ return PContentParent::RecvPNeckoConstructor(aActor);
+ }
+
+ PPrintingParent* AllocPPrintingParent();
+
+ bool DeallocPPrintingParent(PPrintingParent* aActor);
+
+#if defined(NS_PRINTING)
+ /**
+ * @return the PrintingParent for this ContentParent.
+ */
+ already_AddRefed<embedding::PrintingParent> GetPrintingParent();
+#endif
+
+ mozilla::ipc::IPCResult RecvInitStreamFilter(
+ const uint64_t& aChannelId, const nsString& aAddonId,
+ InitStreamFilterResolver&& aResolver);
+
+ PChildToParentStreamParent* AllocPChildToParentStreamParent();
+ bool DeallocPChildToParentStreamParent(PChildToParentStreamParent* aActor);
+
+ PParentToChildStreamParent* AllocPParentToChildStreamParent();
+ bool DeallocPParentToChildStreamParent(PParentToChildStreamParent* aActor);
+
+ PHalParent* AllocPHalParent();
+
+ virtual mozilla::ipc::IPCResult RecvPHalConstructor(
+ PHalParent* aActor) override {
+ return PContentParent::RecvPHalConstructor(aActor);
+ }
+
+ PHeapSnapshotTempFileHelperParent* AllocPHeapSnapshotTempFileHelperParent();
+
+ PRemoteSpellcheckEngineParent* AllocPRemoteSpellcheckEngineParent();
+
+ mozilla::ipc::IPCResult RecvRecordingDeviceEvents(
+ const nsString& aRecordingStatus, const nsString& aPageURL,
+ const bool& aIsAudio, const bool& aIsVideo);
+
+ bool CycleCollectWithLogs(bool aDumpAllTraces,
+ nsICycleCollectorLogSink* aSink,
+ nsIDumpGCAndCCLogsCallback* aCallback);
+
+ mozilla::ipc::IPCResult RecvNotifyTabDestroying(const TabId& aTabId,
+ const ContentParentId& aCpId);
+
+ already_AddRefed<POfflineCacheUpdateParent> AllocPOfflineCacheUpdateParent(
+ nsIURI* aManifestURI, nsIURI* aDocumentURI,
+ const PrincipalInfo& aLoadingPrincipalInfo, const bool& aStickDocument,
+ const CookieJarSettingsArgs& aCookieJarSettingsArgs);
+
+ virtual mozilla::ipc::IPCResult RecvPOfflineCacheUpdateConstructor(
+ POfflineCacheUpdateParent* aActor, nsIURI* aManifestURI,
+ nsIURI* aDocumentURI, const PrincipalInfo& aLoadingPrincipal,
+ const bool& stickDocument,
+ const CookieJarSettingsArgs& aCookieJarSettingsArgs) override;
+
+ mozilla::ipc::IPCResult RecvSetOfflinePermission(
+ const IPC::Principal& principal);
+
+ mozilla::ipc::IPCResult RecvFinishShutdown();
+
+ void MaybeInvokeDragSession(BrowserParent* aParent);
+
+ PContentPermissionRequestParent* AllocPContentPermissionRequestParent(
+ const nsTArray<PermissionRequest>& aRequests,
+ const IPC::Principal& aPrincipal,
+ const IPC::Principal& aTopLevelPrincipal,
+ const bool& aIsHandlingUserInput,
+ const bool& aMaybeUnsafePermissionDelegate, const TabId& aTabId);
+
+ bool DeallocPContentPermissionRequestParent(
+ PContentPermissionRequestParent* actor);
+
+ virtual bool HandleWindowsMessages(const Message& aMsg) const override;
+
+ 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& aWidthSpecified, const bool& aForPrinting,
+ const bool& aForWindowDotPrint, nsIURI* aURIToLoad,
+ const nsCString& aFeatures, const float& aFullZoom,
+ const IPC::Principal& 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,
+ const bool& aWidthSpecified, nsIURI* aURIToLoad,
+ const nsCString& aFeatures, const float& aFullZoom, const nsString& aName,
+ nsIPrincipal* aTriggeringPrincipal, nsIContentSecurityPolicy* aCsp,
+ nsIReferrerInfo* aReferrerInfo,
+ const OriginAttributes& aOriginAttributes);
+
+ static void BroadcastBlobURLRegistration(
+ const nsACString& aURI, BlobImpl* aBlobImpl, nsIPrincipal* aPrincipal,
+ const Maybe<nsID>& aAgentClusterId,
+ ContentParent* aIgnoreThisCP = nullptr);
+
+ static void BroadcastBlobURLUnregistration(
+ const nsACString& aURI, nsIPrincipal* aPrincipal,
+ ContentParent* aIgnoreThisCP = nullptr);
+
+ mozilla::ipc::IPCResult RecvStoreAndBroadcastBlobURLRegistration(
+ const nsCString& aURI, const IPCBlob& aBlob, const Principal& aPrincipal,
+ const Maybe<nsID>& aAgentCluster);
+
+ mozilla::ipc::IPCResult RecvUnstoreAndBroadcastBlobURLUnregistration(
+ const nsCString& aURI, const Principal& aPrincipal);
+
+ mozilla::ipc::IPCResult RecvGetA11yContentId(uint32_t* aContentId);
+
+ mozilla::ipc::IPCResult RecvA11yHandlerControl(
+ const uint32_t& aPid, const IHandlerControlHolder& aHandlerControl);
+
+ virtual int32_t Pid() const override;
+
+ // PURLClassifierParent.
+ PURLClassifierParent* AllocPURLClassifierParent(const Principal& aPrincipal,
+ bool* aSuccess);
+ virtual mozilla::ipc::IPCResult RecvPURLClassifierConstructor(
+ PURLClassifierParent* aActor, const Principal& 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;
+
+ PLoginReputationParent* AllocPLoginReputationParent(nsIURI* aURI);
+
+ virtual mozilla::ipc::IPCResult RecvPLoginReputationConstructor(
+ PLoginReputationParent* aActor, nsIURI* aURI) override;
+
+ bool DeallocPLoginReputationParent(PLoginReputationParent* aActor);
+
+ 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* aBrowserParent,
+ const layers::LayersObserverEpoch& aEpoch);
+
+ void CancelContentJSExecutionIfRunning(
+ BrowserParent* aBrowserParent,
+ nsIRemoteTab::NavigationType aNavigationType,
+ const CancelContentJSOptions& aCancelContentJSOptions);
+
+ // 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
+ // aPrincipal 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, nsIPrincipal* aPrincipal);
+
+ 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,
+ 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);
+ mozilla::ipc::IPCResult RecvRaiseWindow(
+ const MaybeDiscarded<BrowsingContext>& aContext, CallerType aCallerType,
+ uint64_t aActionId);
+ mozilla::ipc::IPCResult RecvAdjustWindowFocus(
+ const MaybeDiscarded<BrowsingContext>& aContext, bool aCheckPermission,
+ bool aIsVisible);
+ mozilla::ipc::IPCResult RecvClearFocus(
+ const MaybeDiscarded<BrowsingContext>& aContext);
+ mozilla::ipc::IPCResult RecvSetFocusedBrowsingContext(
+ const MaybeDiscarded<BrowsingContext>& aContext);
+ 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)
+
+ PParentToChildStreamParent* SendPParentToChildStreamConstructor(
+ PParentToChildStreamParent* aActor) override;
+ PFileDescriptorSetParent* SendPFileDescriptorSetConstructor(
+ const FileDescriptor& aFD) override;
+
+ mozilla::ipc::IPCResult RecvBlobURLDataRequest(
+ const nsCString& aBlobURL, nsIPrincipal* pTriggeringPrincipal,
+ nsIPrincipal* pLoadingPrincipal,
+ const OriginAttributes& aOriginAttributes,
+ const Maybe<nsID>& aAgentClusterId,
+ BlobURLDataRequestResolver&& aResolver);
+
+ protected:
+ bool CheckBrowsingContextEmbedder(CanonicalBrowsingContext* aBC,
+ const char* aOperation) const;
+
+ void OnChannelConnected(int32_t pid) override;
+
+ void ActorDestroy(ActorDestroyReason why) override;
+ void ActorDealloc() 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 UniquePtr<nsTArray<ContentParent*>> sPrivateContent;
+ static UniquePtr<nsDataHashtable<nsUint32HashKey, ContentParent*>>
+ sJSPluginContentParents;
+ static UniquePtr<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 UniquePtr<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& aWidthSpecified, const bool& aForPrinting,
+ const bool& aForWindowDotPrint, nsIURI* aURIToLoad,
+ const nsCString& aFeatures, const float& aFullZoom,
+ BrowserParent* aNextRemoteBrowser, const nsString& aName,
+ nsresult& aResult, nsCOMPtr<nsIRemoteTab>& aNewRemoteTab,
+ bool* aWindowIsNew, int32_t& aOpenLocation,
+ nsIPrincipal* aTriggeringPrincipal, nsIReferrerInfo* aReferrerInfo,
+ bool aLoadUri, nsIContentSecurityPolicy* aCsp,
+ const OriginAttributes& aOriginAttributes);
+
+ explicit ContentParent(int32_t aPluginID) : ContentParent(""_ns, aPluginID) {}
+ explicit ContentParent(const nsACString& aRemoteType)
+ : ContentParent(aRemoteType, nsFakePluginTag::NOT_JSPLUGIN) {}
+
+ ContentParent(const nsACString& aRemoteType, int32_t aPluginID);
+
+ // 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.
+ */
+ bool TryToRecycle();
+
+ /**
+ * 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.
+ */
+ void StopRecycling(bool aForeground = true);
+
+ /**
+ * 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 or JSPlugin
+ */
+ bool HasActiveWorkerOrJSPlugin();
+
+ /**
+ * 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();
+
+ /**
+ * 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,
+ // Close the channel with error and let the subprocess clean up itself.
+ CLOSE_CHANNEL_WITH_ERROR,
+ };
+
+ void MaybeAsyncSendShutDownMessage();
+
+ /**
+ * 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.
+ *
+ * If aMethod is CLOSE_CHANNEL_WITH_ERROR and this is the first call
+ * to ShutDownProcess, then we'll close our channel using CloseWithError()
+ * rather than vanilla Close(). CloseWithError() indicates to IPC that this
+ * is an abnormal shutdown (e.g. a crash).
+ */
+ void ShutDownProcess(ShutDownMethod aMethod);
+
+ // Perform any steps necesssary to gracefully shtudown the message
+ // manager and null out mMessageManager.
+ void ShutDownMessageManager();
+
+ // 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 nsCString& aKey, const nsCString& aOrigin);
+
+ 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::PBackgroundParent>&& aEndpoint);
+
+ mozilla::ipc::IPCResult RecvAddMemoryReport(const MemoryReport& aReport);
+ mozilla::ipc::IPCResult RecvAddPerformanceMetrics(
+ const nsID& aID, nsTArray<PerformanceInfo>&& aMetrics);
+
+ bool DeallocPRemoteSpellcheckEngineParent(PRemoteSpellcheckEngineParent*);
+
+ mozilla::ipc::IPCResult RecvCloneDocumentTreeInto(
+ const MaybeDiscarded<BrowsingContext>& aSource,
+ const MaybeDiscarded<BrowsingContext>& aTarget);
+
+ 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(
+ const uint32_t& aType, nsIURI* aURI, const uint32_t& aFlags,
+ 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);
+
+ PTestShellParent* AllocPTestShellParent();
+
+ bool DeallocPTestShellParent(PTestShellParent* shell);
+
+ PScriptCacheParent* AllocPScriptCacheParent(const FileDescOrError& cacheFile,
+ const bool& wantCacheData);
+
+ bool DeallocPScriptCacheParent(PScriptCacheParent* shell);
+
+ bool DeallocPNeckoParent(PNeckoParent* necko);
+
+ already_AddRefed<PExternalHelperAppParent> AllocPExternalHelperAppParent(
+ nsIURI* aUri, const Maybe<mozilla::net::LoadInfoArgs>& aLoadInfoArgs,
+ const nsCString& aMimeContentType, const nsCString& aContentDisposition,
+ const uint32_t& aContentDispositionHint,
+ const nsString& 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 Maybe<LoadInfoArgs>& loadInfoArgs,
+ const nsCString& aMimeContentType, const nsCString& aContentDisposition,
+ const uint32_t& aContentDispositionHint,
+ const nsString& 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);
+
+ PPresentationParent* AllocPPresentationParent();
+
+ bool DeallocPPresentationParent(PPresentationParent* aActor);
+
+ virtual mozilla::ipc::IPCResult RecvPPresentationConstructor(
+ PPresentationParent* aActor) override;
+
+#ifdef MOZ_WEBSPEECH
+ PSpeechSynthesisParent* AllocPSpeechSynthesisParent();
+ bool DeallocPSpeechSynthesisParent(PSpeechSynthesisParent* aActor);
+
+ virtual mozilla::ipc::IPCResult RecvPSpeechSynthesisConstructor(
+ PSpeechSynthesisParent* aActor) override;
+#endif
+
+ PWebBrowserPersistDocumentParent* AllocPWebBrowserPersistDocumentParent(
+ PBrowserParent* aBrowser,
+ const MaybeDiscarded<BrowsingContext>& aContext);
+
+ bool DeallocPWebBrowserPersistDocumentParent(
+ PWebBrowserPersistDocumentParent* aActor);
+
+ mozilla::ipc::IPCResult RecvGetGfxVars(nsTArray<GfxVarUpdate>* aVars);
+
+ mozilla::ipc::IPCResult RecvSetClipboard(
+ const IPCDataTransfer& aDataTransfer, const bool& aIsPrivateData,
+ const IPC::Principal& aRequestingPrincipal,
+ const nsContentPolicyType& aContentPolicyType,
+ const int32_t& aWhichClipboard);
+
+ mozilla::ipc::IPCResult RecvGetClipboard(nsTArray<nsCString>&& aTypes,
+ const int32_t& aWhichClipboard,
+ IPCDataTransfer* aDataTransfer);
+
+ 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 RecvPlaySound(nsIURI* aURI);
+ mozilla::ipc::IPCResult RecvBeep();
+ mozilla::ipc::IPCResult RecvPlayEventSound(const uint32_t& aEventId);
+
+ mozilla::ipc::IPCResult RecvGetIconForExtension(const nsCString& aFileExt,
+ const uint32_t& aIconSize,
+ nsTArray<uint8_t>* bits);
+
+ mozilla::ipc::IPCResult RecvStartVisitedQueries(
+ const nsTArray<RefPtr<nsIURI>>&);
+
+ mozilla::ipc::IPCResult RecvSetURITitle(nsIURI* uri, const nsString& title);
+
+ mozilla::ipc::IPCResult RecvShowAlert(nsIAlertNotification* aAlert);
+
+ mozilla::ipc::IPCResult RecvCloseAlert(const nsString& aName);
+
+ mozilla::ipc::IPCResult RecvDisableNotifications(
+ const IPC::Principal& aPrincipal);
+
+ mozilla::ipc::IPCResult RecvOpenNotificationSettings(
+ const IPC::Principal& aPrincipal);
+
+ mozilla::ipc::IPCResult RecvNotificationEvent(
+ const nsString& aType, const NotificationEventData& aData);
+
+ mozilla::ipc::IPCResult RecvLoadURIExternal(
+ nsIURI* uri, nsIPrincipal* triggeringPrincipal,
+ const MaybeDiscarded<BrowsingContext>& aContext);
+ mozilla::ipc::IPCResult RecvExtProtocolChannelConnectParent(
+ const uint64_t& registrarId);
+
+ mozilla::ipc::IPCResult RecvSyncMessage(
+ const nsString& aMsg, const ClonedMessageData& aData,
+ nsTArray<StructuredCloneData>* aRetvals);
+
+ mozilla::ipc::IPCResult RecvAsyncMessage(const nsString& 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 nsString& aMessage);
+
+ 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& aIsFromPrivateWindow,
+ const uint64_t& aInnerWindowId, const bool& aIsFromChromeContext);
+
+ mozilla::ipc::IPCResult RecvReportFrameTimingData(
+ uint64_t innerWindowId, const nsString& entryName,
+ const nsString& initiatorType, UniquePtr<PerformanceTimingData>&& aData);
+
+ mozilla::ipc::IPCResult RecvScriptErrorWithStack(
+ 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& aIsFromPrivateWindow,
+ const bool& aIsFromChromeContext, const ClonedMessageData& aStack);
+
+ private:
+ mozilla::ipc::IPCResult RecvScriptErrorInternal(
+ 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& aIsFromPrivateWindow,
+ const bool& aIsFromChromeContext,
+ const ClonedMessageData* aStack = nullptr);
+
+ public:
+ mozilla::ipc::IPCResult RecvPrivateDocShellsExist(const bool& aExist);
+
+ 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);
+
+ virtual void ProcessingError(Result aCode, const char* aMsgName) override;
+
+ mozilla::ipc::IPCResult RecvGraphicsError(const nsCString& 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);
+
+ PFileDescriptorSetParent* AllocPFileDescriptorSetParent(
+ const mozilla::ipc::FileDescriptor&);
+
+ bool DeallocPFileDescriptorSetParent(PFileDescriptorSetParent*);
+
+ PWebrtcGlobalParent* AllocPWebrtcGlobalParent();
+ bool DeallocPWebrtcGlobalParent(PWebrtcGlobalParent* aActor);
+
+ mozilla::ipc::IPCResult RecvUpdateDropEffect(const uint32_t& aDragAction,
+ const uint32_t& aDropEffect);
+
+ mozilla::ipc::IPCResult RecvShutdownProfile(const nsCString& aProfile);
+
+ mozilla::ipc::IPCResult RecvGetGraphicsDeviceInitData(
+ ContentDeviceData* aOut);
+
+ mozilla::ipc::IPCResult RecvGetOutputColorProfileData(
+ nsTArray<uint8_t>* aOutputColorProfileData);
+
+ 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 mozilla::fontlist::Pointer& aFacePtr,
+ 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 mozilla::fontlist::Pointer& aFamilyPtr);
+
+ 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 nsString& aCodecName,
+ const uint32_t& aDecodeFPS);
+
+ mozilla::ipc::IPCResult RecvNotifyPushObservers(
+ const nsCString& aScope, const IPC::Principal& aPrincipal,
+ const nsString& aMessageId);
+
+ mozilla::ipc::IPCResult RecvNotifyPushObserversWithData(
+ const nsCString& aScope, const IPC::Principal& aPrincipal,
+ const nsString& aMessageId, nsTArray<uint8_t>&& aData);
+
+ mozilla::ipc::IPCResult RecvNotifyPushSubscriptionChangeObservers(
+ const nsCString& aScope, const IPC::Principal& aPrincipal);
+
+ mozilla::ipc::IPCResult RecvPushError(const nsCString& aScope,
+ const IPC::Principal& aPrincipal,
+ const nsString& aMessage,
+ const uint32_t& aFlags);
+
+ mozilla::ipc::IPCResult RecvNotifyPushSubscriptionModifiedObservers(
+ const nsCString& aScope, const IPC::Principal& aPrincipal);
+
+ mozilla::ipc::IPCResult RecvGetFilesRequest(const nsID& aID,
+ const nsString& 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 RecvRecordOrigin(const uint32_t& aMetricId,
+ const nsCString& aOrigin);
+ mozilla::ipc::IPCResult RecvReportContentBlockingLog(
+ const IPCStream& aIPCStream);
+
+ mozilla::ipc::IPCResult RecvBHRThreadHang(const HangDetails& aHangDetails);
+
+ mozilla::ipc::IPCResult RecvAddCertException(
+ const nsACString& aSerializedCert, uint32_t aFlags,
+ const nsACString& aHostName, int32_t aPort, bool aIsTemporary,
+ AddCertExceptionResolver&& aResolver);
+
+ mozilla::ipc::IPCResult RecvAutomaticStorageAccessPermissionCanBeGranted(
+ const Principal& aPrincipal,
+ AutomaticStorageAccessPermissionCanBeGrantedResolver&& aResolver);
+
+ mozilla::ipc::IPCResult RecvStorageAccessPermissionGrantedForOrigin(
+ uint64_t aTopLevelWindowId,
+ const MaybeDiscarded<BrowsingContext>& aParentContext,
+ const Principal& aTrackingPrincipal, const nsCString& aTrackingOrigin,
+ const int& aAllowMode,
+ const Maybe<
+ ContentBlockingNotifier::StorageAccessPermissionGrantedReason>&
+ aReason,
+ StorageAccessPermissionGrantedForOriginResolver&& aResolver);
+
+ mozilla::ipc::IPCResult RecvCompleteAllowAccessFor(
+ const MaybeDiscarded<BrowsingContext>& aParentContext,
+ uint64_t aTopLevelWindowId, const Principal& aTrackingPrincipal,
+ const nsCString& aTrackingOrigin, uint32_t aCookieBehavior,
+ const ContentBlockingNotifier::StorageAccessPermissionGrantedReason&
+ aReason,
+ CompleteAllowAccessForResolver&& aResolver);
+
+ mozilla::ipc::IPCResult RecvStoreUserInteractionAsPermission(
+ const Principal& aPrincipal);
+
+ 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 RecvGetModulesTrust(
+ ModulePaths&& aModPaths, bool aRunAtNormalPriority,
+ GetModulesTrustResolver&& aResolver);
+
+ 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);
+
+ mozilla::ipc::IPCResult RecvHistoryGo(
+ const MaybeDiscarded<BrowsingContext>& aContext, int32_t aOffset,
+ uint64_t aHistoryEpoch, bool aRequireUserInteraction,
+ HistoryGoResolver&& aResolveRequestedIndex);
+
+ mozilla::ipc::IPCResult RecvSessionHistoryUpdate(
+ const MaybeDiscarded<BrowsingContext>& aContext, const int32_t& aIndex,
+ const int32_t& aLength, const nsID& aChangeID);
+
+ mozilla::ipc::IPCResult RecvSynchronizeLayoutHistoryState(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ nsILayoutHistoryState* aState);
+
+ mozilla::ipc::IPCResult RecvSessionHistoryEntryTitle(
+ const MaybeDiscarded<BrowsingContext>& aContext, const nsString& aTitle);
+
+ mozilla::ipc::IPCResult RecvSessionHistoryEntryScrollRestorationIsManual(
+ const MaybeDiscarded<BrowsingContext>& aContext, const bool& aIsManual);
+
+ mozilla::ipc::IPCResult RecvSessionHistoryEntryCacheKey(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const uint32_t& aCacheKey);
+
+ mozilla::ipc::IPCResult
+ RecvSessionHistoryEntryStoreWindowNameInContiguousEntries(
+ const MaybeDiscarded<BrowsingContext>& aContext, const nsString& aName);
+
+ mozilla::ipc::IPCResult RecvGetLoadingSessionHistoryInfoFromParent(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ GetLoadingSessionHistoryInfoFromParentResolver&& aResolver);
+
+ 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);
+
+ mozilla::ipc::IPCResult RecvHistoryReload(
+ const MaybeDiscarded<BrowsingContext>& aContext,
+ const uint32_t aReloadFlags);
+
+ // 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);
+
+ 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();
+
+ static bool ShouldSyncPreference(const char16_t* aData);
+
+ already_AddRefed<JSActor> InitJSActor(JS::HandleObject aMaybeActor,
+ const nsACString& aName,
+ ErrorResult& aRv) override;
+ mozilla::ipc::IProtocol* AsNativeActor() override { return this; }
+
+ 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);
+
+ void AddToPool(nsTArray<ContentParent*>&);
+ void RemoveFromPool(nsTArray<ContentParent*>&);
+ void AssertNotInPool();
+
+ void AssertAlive();
+
+ private:
+ // Released in ActorDealloc; deliberately not exposed to the CC.
+ RefPtr<ContentParent> mSelfRef;
+
+ // 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;
+ nsCOMPtr<nsIPrincipal> mRemoteTypeIsolationPrincipal;
+
+ ContentParentId mChildID;
+ int32_t mGeolocationWatchID;
+
+ // This contains the id for the JS plugin (@see nsFakePluginTag) if this is
+ // the ContentParent for a process containing iframes for that JS plugin. If
+ // this is not a ContentParent for a JS plugin then it contains the value
+ // nsFakePluginTag::NOT_JSPLUGIN.
+ int32_t mJSPluginID;
+
+ // 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;
+
+ // `mCount` is increased when a RemoteWorkerParent actor is created for this
+ // ContentProcess and it is decreased when the actor is destroyed.
+ //
+ // `mShutdownStarted` is flipped to `true` when a runnable that calls
+ // `ShutDownProcess` is dispatched; it's needed because the corresponding
+ // Content Process may be shutdown if there's no remote worker actors, and
+ // decrementing `mCount` and the call to `ShutDownProcess` are async. So,
+ // when a worker is going to be spawned and we see that `mCount` is 0,
+ // we can decide whether or not to use that process based on the value of
+ // `mShutdownStarted.`
+ //
+ // It's touched on PBackground thread and on main-thread.
+ struct RemoteWorkerActorData {
+ uint32_t mCount = 0;
+ bool mShutdownStarted = false;
+ };
+
+ DataMutex<RemoteWorkerActorData> mRemoteWorkerActorData;
+
+ // 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;
+
+ // 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;
+
+ RefPtr<nsConsoleService> mConsoleService;
+ nsConsoleService* GetConsoleService();
+ 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.
+ ScopedClose mChildXSocketFdDup;
+#endif
+
+ PProcessHangMonitorParent* mHangMonitorActor;
+
+ UniquePtr<gfx::DriverCrashGuard> mDriverCrashGuard;
+ UniquePtr<MemoryReportRequestHost> mMemoryReportRequest;
+
+#if defined(XP_LINUX) && defined(MOZ_SANDBOX)
+ mozilla::UniquePtr<SandboxBroker> mSandboxBroker;
+ static mozilla::UniquePtr<SandboxBrokerPolicyFactory>
+ sSandboxBrokerPolicyFactory;
+#endif
+
+#ifdef NS_PRINTING
+ RefPtr<embedding::PrintingParent> mPrintingParent;
+#endif
+
+ // This hashtable is used to run GetFilesHelper objects in the parent process.
+ // GetFilesHelper can be aborted by receiving RecvDeleteGetFilesRequest.
+ nsRefPtrHashtable<nsIDHashKey, GetFilesHelper> mGetFilesPendingRequests;
+
+ nsTHashtable<nsCStringHashKey> mActivePermissionKeys;
+
+ 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
+
+ nsTHashtable<nsRefPtrHashKey<BrowsingContextGroup>> mGroups;
+
+ // 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;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(ContentParent, NS_CONTENTPARENT_IID)
+
+// This is the C++ version of remoteTypePrefix in E10SUtils.jsm.
+const nsDependentCSubstring RemoteTypePrefix(
+ const nsACString& aContentProcessType);
+
+// This is based on isWebRemoteType in E10SUtils.jsm.
+bool IsWebRemoteType(const nsACString& aContentProcessType);
+
+bool IsWebCoopCoepRemoteType(const nsACString& aContentProcessType);
+
+bool IsPrivilegedMozillaRemoteType(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..49fff36bca
--- /dev/null
+++ b/dom/ipc/ContentProcess.cpp
@@ -0,0 +1,204 @@
+/* -*- 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"
+#endif
+
+#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_SANDBOX)
+# include "mozilla/SandboxSettings.h"
+# include "nsAppDirectoryServiceDefs.h"
+# include "nsDirectoryService.h"
+# include "nsDirectoryServiceDefs.h"
+#endif
+
+#include "nsAppRunner.h"
+#include "ProcessUtils.h"
+
+using mozilla::ipc::IOThreadChild;
+
+namespace mozilla::dom {
+
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+static void SetTmpEnvironmentVariable(nsIFile* aValue) {
+ // Save the TMP environment variable so that is is picked up by GetTempPath().
+ // Note that we specifically write to the TMP variable, as that is the first
+ // variable that is checked by GetTempPath() to determine its output.
+ nsAutoString fullTmpPath;
+ nsresult rv = aValue->GetPath(fullTmpPath);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+ Unused << NS_WARN_IF(!SetEnvironmentVariableW(L"TMP", fullTmpPath.get()));
+ // We also set TEMP in case there is naughty third-party code that is
+ // referencing the environment variable directly.
+ Unused << NS_WARN_IF(!SetEnvironmentVariableW(L"TEMP", fullTmpPath.get()));
+}
+#endif
+
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+static void SetUpSandboxEnvironment() {
+ MOZ_ASSERT(
+ nsDirectoryService::gService,
+ "SetUpSandboxEnvironment relies on nsDirectoryService being initialized");
+
+ // On Windows, a sandbox-writable temp directory is used whenever the sandbox
+ // is enabled.
+ if (!IsContentSandboxEnabled()) {
+ return;
+ }
+
+ nsCOMPtr<nsIFile> sandboxedContentTemp;
+ nsresult rv = nsDirectoryService::gService->Get(
+ NS_APP_CONTENT_PROCESS_TEMP_DIR, NS_GET_IID(nsIFile),
+ getter_AddRefs(sandboxedContentTemp));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+
+ // Change the gecko defined temp directory to our sandbox-writable one.
+ // Undefine returns a failure if the property is not already set.
+ Unused << nsDirectoryService::gService->Undefine(NS_OS_TEMP_DIR);
+ rv = nsDirectoryService::gService->Set(NS_OS_TEMP_DIR, sandboxedContentTemp);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+
+ SetTmpEnvironmentVariable(sandboxedContentTemp);
+}
+#endif
+
+bool ContentProcess::Init(int aArgc, char* aArgv[]) {
+ Maybe<uint64_t> childID;
+ Maybe<bool> isForBrowser;
+ Maybe<const char*> parentBuildID;
+ char* prefsHandle = nullptr;
+ char* prefMapHandle = nullptr;
+ char* prefsLen = nullptr;
+ char* prefMapSize = nullptr;
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+ nsCOMPtr<nsIFile> profileDir;
+#endif
+
+ for (int i = 1; i < aArgc; i++) {
+ if (!aArgv[i]) {
+ continue;
+ }
+
+ if (strcmp(aArgv[i], "-appdir") == 0) {
+ if (++i == aArgc) {
+ return false;
+ }
+ nsDependentCString appDir(aArgv[i]);
+ mXREEmbed.SetAppDir(appDir);
+
+ } else if (strcmp(aArgv[i], "-childID") == 0) {
+ if (++i == aArgc) {
+ return false;
+ }
+ char* str = aArgv[i];
+ childID = Some(strtoull(str, &str, 10));
+ if (str[0] != '\0') {
+ return false;
+ }
+
+ } else if (strcmp(aArgv[i], "-isForBrowser") == 0) {
+ isForBrowser = Some(true);
+
+ } else if (strcmp(aArgv[i], "-notForBrowser") == 0) {
+ isForBrowser = Some(false);
+
+#ifdef XP_WIN
+ } else if (strcmp(aArgv[i], "-prefsHandle") == 0) {
+ if (++i == aArgc) {
+ return false;
+ }
+ prefsHandle = aArgv[i];
+ } else if (strcmp(aArgv[i], "-prefMapHandle") == 0) {
+ if (++i == aArgc) {
+ return false;
+ }
+ prefMapHandle = aArgv[i];
+#endif
+
+ } else if (strcmp(aArgv[i], "-prefsLen") == 0) {
+ if (++i == aArgc) {
+ return false;
+ }
+ prefsLen = aArgv[i];
+ } else if (strcmp(aArgv[i], "-prefMapSize") == 0) {
+ if (++i == aArgc) {
+ return false;
+ }
+ prefMapSize = aArgv[i];
+ } else if (strcmp(aArgv[i], "-safeMode") == 0) {
+ gSafeMode = true;
+
+ } else if (strcmp(aArgv[i], "-parentBuildID") == 0) {
+ if (++i == aArgc) {
+ return false;
+ }
+ parentBuildID = Some(aArgv[i]);
+
+#if defined(XP_MACOSX) && defined(MOZ_SANDBOX)
+ } else if (strcmp(aArgv[i], "-profile") == 0) {
+ if (++i == aArgc) {
+ return false;
+ }
+ bool flag;
+ nsresult rv = XRE_GetFileFromPath(aArgv[i], getter_AddRefs(profileDir));
+ if (NS_FAILED(rv) || NS_FAILED(profileDir->Exists(&flag)) || !flag) {
+ NS_WARNING("Invalid profile directory passed to content process.");
+ profileDir = nullptr;
+ }
+#endif /* XP_MACOSX && MOZ_SANDBOX */
+ }
+ }
+
+ // Did we find all the mandatory flags?
+ if (childID.isNothing() || isForBrowser.isNothing() ||
+ parentBuildID.isNothing()) {
+ return false;
+ }
+
+ ::mozilla::ipc::SharedPreferenceDeserializer deserializer;
+ if (!deserializer.DeserializeFromSharedMemory(prefsHandle, prefMapHandle,
+ prefsLen, prefMapSize)) {
+ return false;
+ }
+
+ mContent.Init(IOThreadChild::message_loop(), ParentPid(), *parentBuildID,
+ IOThreadChild::TakeChannel(), *childID, *isForBrowser);
+
+ mXREEmbed.Start();
+#if (defined(XP_MACOSX)) && defined(MOZ_SANDBOX)
+ mContent.SetProfileDir(profileDir);
+# if defined(DEBUG)
+ if (IsContentSandboxEnabled()) {
+ AssertMacSandboxEnabled();
+ }
+# endif /* DEBUG */
+#endif /* XP_MACOSX && MOZ_SANDBOX */
+
+#if defined(XP_WIN) && defined(MOZ_SANDBOX)
+ SetUpSandboxEnvironment();
+#endif
+
+ return true;
+}
+
+// Note: CleanUp() never gets called in non-debug builds because we exit early
+// in ContentChild::ActorDestroy().
+void ContentProcess::CleanUp() { mXREEmbed.Stop(); }
+
+} // namespace mozilla::dom
diff --git a/dom/ipc/ContentProcess.h b/dom/ipc/ContentProcess.h
new file mode 100644
index 0000000000..70704a2573
--- /dev/null
+++ b/dom/ipc/ContentProcess.h
@@ -0,0 +1,53 @@
+/* -*- 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 "mozilla/ipc/ScopedXREEmbed.h"
+#include "ContentChild.h"
+
+#if defined(XP_WIN)
+# include "mozilla/mscom/ProcessRuntime.h"
+#endif
+
+namespace mozilla {
+namespace dom {
+
+/**
+ * ContentProcess is a singleton on the content process which represents
+ * the main thread where tab instances live.
+ */
+class ContentProcess : public mozilla::ipc::ProcessChild {
+ typedef mozilla::ipc::ProcessChild ProcessChild;
+
+ public:
+ explicit ContentProcess(ProcessId aParentPid) : ProcessChild(aParentPid) {}
+
+ ~ContentProcess() = default;
+
+ virtual bool Init(int aArgc, char* aArgv[]) override;
+ virtual void CleanUp() override;
+
+ private:
+ ContentChild mContent;
+ mozilla::ipc::ScopedXREEmbed mXREEmbed;
+
+#if defined(XP_WIN)
+ // This object initializes and configures COM.
+ mozilla::mscom::ProcessRuntime mCOMRuntime;
+#endif
+
+ ContentProcess(const ContentProcess&) = delete;
+
+ const ContentProcess& operator=(const ContentProcess&) = delete;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // ifndef dom_tabs_ContentThread_h
diff --git a/dom/ipc/ContentProcessManager.cpp b/dom/ipc/ContentProcessManager.cpp
new file mode 100644
index 0000000000..4a663c0c8b
--- /dev/null
+++ b/dom/ipc/ContentProcessManager.cpp
@@ -0,0 +1,135 @@
+/* -*- 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/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) {
+ sSingleton = new ContentProcessManager();
+ ClearOnShutdown(&sSingleton);
+ }
+ return sSingleton;
+}
+
+void ContentProcessManager::AddContentProcess(ContentParent* aChildCp) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aChildCp);
+
+ auto entry = mContentParentMap.LookupForAdd(aChildCp->ChildID());
+ MOZ_ASSERT_IF(entry, entry.Data() == aChildCp);
+ entry.OrInsert([&] { return 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);
+
+ auto entry = mBrowserParentMap.LookupForAdd(aChildBp->GetTabId());
+ MOZ_ASSERT_IF(entry, entry.Data() == aChildBp);
+ entry.OrInsert([&] {
+ // 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();
+ return aChildBp;
+ });
+ return !entry;
+}
+
+void ContentProcessManager::UnregisterRemoteFrame(const TabId& aChildTabId) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ auto childBp = mBrowserParentMap.GetAndRemove(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..b823da61ed
--- /dev/null
+++ b/dom/ipc/ContentProcessManager.h
@@ -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/. */
+
+#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 "nsDataHashtable.h"
+
+namespace mozilla {
+namespace 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;
+
+ nsDataHashtable<nsUint64HashKey, ContentParent*> mContentParentMap;
+ nsDataHashtable<nsUint64HashKey, BrowserParent*> mBrowserParentMap;
+
+ MOZ_COUNTED_DEFAULT_CTOR(ContentProcessManager);
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_ContentProcessManager_h
diff --git a/dom/ipc/DOMTypes.ipdlh b/dom/ipc/DOMTypes.ipdlh
new file mode 100644
index 0000000000..49b0dbfcfe
--- /dev/null
+++ b/dom/ipc/DOMTypes.ipdlh
@@ -0,0 +1,396 @@
+/* -*- 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 IPCBlob;
+include IPCStream;
+include protocol PRemoteLazyInputStream;
+include ProtocolTypes;
+
+using struct mozilla::void_t from "mozilla/ipc/IPCCore.h";
+
+using moveonly struct mozilla::SerializedStructuredCloneBuffer
+ from "mozilla/ipc/SerializedStructuredCloneBuffer.h";
+
+using class mozilla::dom::LoadingSessionHistoryInfo
+ from "mozilla/dom/SessionHistoryEntry.h";
+
+using LayoutDeviceIntRect from "Units.h";
+using DesktopIntRect from "Units.h";
+using DesktopToLayoutDeviceScale from "Units.h";
+using CSSToLayoutDeviceScale from "Units.h";
+using CSSRect from "Units.h";
+using CSSSize from "Units.h";
+using ScreenIntSize from "Units.h";
+using mozilla::LayoutDeviceIntPoint from "Units.h";
+using nsSizeMode from "nsIWidgetListener.h";
+using ScrollbarPreference from "mozilla/ScrollbarPreferences.h";
+using hal::ScreenOrientation from "mozilla/HalScreenConfiguration.h";
+using mozilla::gfx::SurfaceFormat from "mozilla/gfx/Types.h";
+using refcounted class nsIPrincipal from "nsIPrincipal.h";
+using mozilla::dom::MaybeDiscardedBrowsingContext from "mozilla/dom/BrowsingContext.h";
+using refcounted class nsIURI from "nsIURI.h";
+using refcounted class nsIContentSecurityPolicy from "nsIContentSecurityPolicy.h";
+using refcounted class nsIInputStream from "mozilla/ipc/IPCStreamUtils.h";
+using refcounted class nsIReferrerInfo from "nsIReferrerInfo.h";
+using refcounted class nsIVariant from "nsIVariant.h";
+using class mozilla::TimeStamp from "mozilla/TimeStamp.h";
+using refcounted class mozilla::dom::BrowsingContext from "mozilla/dom/BrowsingContext.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;
+};
+
+union IPCDataTransferData
+{
+ nsString; // text
+ Shmem; // images using Shmem
+ IPCBlob; // files
+};
+
+struct IPCDataTransferImage
+{
+ uint32_t width;
+ uint32_t height;
+ uint32_t stride;
+ SurfaceFormat format;
+};
+
+struct IPCDataTransferItem
+{
+ nsCString flavor;
+ // The image details are only used when transferring images.
+ IPCDataTransferImage imageDetails;
+ IPCDataTransferData data;
+};
+
+struct IPCDataTransfer
+{
+ IPCDataTransferItem[] items;
+};
+
+struct ScreenDetails {
+ LayoutDeviceIntRect rect;
+ DesktopIntRect rectDisplayPix;
+ LayoutDeviceIntRect availRect;
+ DesktopIntRect availRectDisplayPix;
+ int32_t pixelDepth;
+ int32_t colorDepth;
+ DesktopToLayoutDeviceScale contentsScaleFactor;
+ CSSToLayoutDeviceScale defaultCSSScaleFactor;
+ float dpi;
+};
+
+struct DimensionInfo
+{
+ CSSRect rect;
+ CSSSize size;
+ ScreenOrientation orientation;
+ LayoutDeviceIntPoint clientOffset;
+ LayoutDeviceIntPoint chromeOffset;
+};
+
+struct FrameScriptInfo
+{
+ nsString url;
+ bool runInGlobalScope;
+};
+
+struct FeaturePolicyInfo
+{
+ nsString[] inheritedDeniedFeatureNames;
+ nsString[] attributeEnabledFeatureNames;
+ nsString declaredString;
+ nsIPrincipal defaultOrigin;
+ nsIPrincipal selfOrigin;
+ nsIPrincipal srcOrigin;
+};
+
+/**
+ * The information required to complete a window creation request.
+ */
+struct CreatedWindowInfo
+{
+ nsresult rv;
+ bool windowOpened;
+ FrameScriptInfo[] frameScripts;
+ uint32_t maxTouchPoints;
+ DimensionInfo dimensions;
+ bool hasSiblings;
+};
+
+
+/**
+ * PerformanceInfo is used to pass performance info stored
+ * in WorkerPrivate and DocGroup instances, as well as
+ * memory-related information.
+ *
+ * Each (host, pid, windowId) is unique to a given DocGroup or
+ * Worker, and we collect the number of dispatches per Dispatch
+ * category and total execution duration as well as the current
+ * Zone JS Heap usage.
+ *
+ * This IPDL struct reflects the data collected in Performance counters,
+ * in addition of some memory usage information.
+ *
+ * see xpcom/threads/PerformanceCounter.h
+ */
+
+struct MediaMemoryInfo {
+ uint64_t audioSize;
+ uint64_t videoSize;
+ uint64_t resourcesSize;
+};
+
+struct PerformanceMemoryInfo {
+ MediaMemoryInfo media;
+ uint64_t domDom;
+ uint64_t domStyle;
+ uint64_t domOther;
+ uint64_t GCHeapUsage;
+};
+
+struct CategoryDispatch
+{
+ // DispatchCategory value
+ uint16_t category;
+ // Number of dispatch
+ uint16_t count;
+};
+
+struct PerformanceInfo
+{
+ // Host of the document, if any
+ nsCString host;
+ // process id
+ uint32_t pid;
+ // window id
+ uint64_t windowId;
+ // Execution time in microseconds
+ uint64_t duration;
+ // Counter ID (unique across processes)
+ uint64_t counterId;
+ // True if the data is collected in a worker
+ bool isWorker;
+ // True if the document window is the top window
+ bool isTopLevel;
+ // Memory
+ PerformanceMemoryInfo memory;
+ // Counters per category. For workers, a single entry
+ CategoryDispatch[] items;
+};
+
+struct DocShellLoadStateInit
+{
+ nsIURI URI;
+ nsIURI OriginalURI;
+ nsIURI ResultPrincipalURI;
+ bool ResultPrincipalURIIsSome;
+ nsIPrincipal TriggeringPrincipal;
+ nsIReferrerInfo ReferrerInfo;
+ bool KeepResultPrincipalURIIfSet;
+ bool LoadReplace;
+ bool InheritPrincipal;
+ bool PrincipalIsExplicit;
+ nsIPrincipal PrincipalToInherit;
+ nsIPrincipal PartitionedPrincipalToInherit;
+ bool ForceAllowDataURI;
+ bool OriginalFrameSrc;
+ bool IsFormSubmission;
+ uint32_t LoadType;
+ nsString Target;
+ nsIURI BaseURI;
+ uint32_t LoadFlags;
+ bool FirstParty;
+ bool HasValidUserGestureActivation;
+ bool AllowFocusMove;
+ nsCString TypeHint;
+ nsString FileName;
+ bool IsFromProcessingFrameAttributes;
+ // 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.
+ nsIContentSecurityPolicy Csp;
+
+ MaybeDiscardedBrowsingContext SourceBrowsingContext;
+ MaybeDiscardedBrowsingContext TargetBrowsingContext;
+
+ // The TriggineringSandboxFlags are the SandboxFlags of the entity
+ // responsible for causing the load to occur.
+ uint32_t TriggeringSandboxFlags;
+
+ nsCString? OriginalURIString;
+ int32_t? CancelContentJSEpoch;
+
+ nsIInputStream PostDataStream;
+ nsIInputStream HeadersStream;
+
+ nsString SrcdocData; // useless without sourcedocshell
+
+ // Fields missing due to lack of need or serialization
+ // nsCOMPtr<nsIDocShell> mSourceDocShell;
+ // bool mIsSrcDocLoad; // useless without sourcedocshell
+ // nsIChannel pendingRedirectedChannel; // sent through other mechanism
+
+ uint64_t LoadIdentifier;
+
+ bool ChannelInitialized;
+
+ bool TryToReplaceWithSessionHistoryLoad;
+
+ LoadingSessionHistoryInfo? loadingSessionHistoryInfo;
+};
+
+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;
+ uint32_t classOfService;
+ bool? privateBrowsing;
+ nsCString? method;
+ nsIReferrerInfo referrerInfo;
+ TimedChannelInfo? timedChannel;
+ nullable PRemoteLazyInputStream uploadStream;
+ 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;
+ nsIURI;
+ 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;
+ 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..d35d77840a
--- /dev/null
+++ b/dom/ipc/DocShellMessageUtils.cpp
@@ -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/. */
+
+#include "mozilla/dom/DocShellMessageUtils.h"
+#include "mozilla/dom/DOMTypes.h"
+#include "mozilla/ipc/IPDLParamTraits.h"
+#include "nsSerializationHelper.h"
+
+namespace mozilla::ipc {
+
+void IPDLParamTraits<nsDocShellLoadState*>::Write(IPC::Message* aMsg,
+ IProtocol* aActor,
+ nsDocShellLoadState* aParam) {
+ MOZ_RELEASE_ASSERT(aParam);
+ WriteIPDLParam(aMsg, aActor, aParam->Serialize());
+}
+
+bool IPDLParamTraits<nsDocShellLoadState*>::Read(
+ const IPC::Message* aMsg, PickleIterator* aIter, IProtocol* aActor,
+ RefPtr<nsDocShellLoadState>* aResult) {
+ dom::DocShellLoadStateInit loadState;
+ if (!ReadIPDLParam(aMsg, aIter, aActor, &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.
+ MOZ_ASSERT(loadState.URI());
+
+ *aResult = new nsDocShellLoadState(loadState);
+ return true;
+}
+
+} // namespace mozilla::ipc
diff --git a/dom/ipc/DocShellMessageUtils.h b/dom/ipc/DocShellMessageUtils.h
new file mode 100644
index 0000000000..b8e1316cd6
--- /dev/null
+++ b/dom/ipc/DocShellMessageUtils.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_docshell_message_utils_h__
+#define mozilla_dom_docshell_message_utils_h__
+
+#include "ipc/EnumSerializer.h"
+#include "nsCOMPtr.h"
+#include "nsDocShellLoadState.h"
+#include "nsIContentViewer.h"
+#include "mozilla/ScrollbarPreferences.h"
+
+namespace mozilla {
+namespace ipc {
+
+template <>
+struct IPDLParamTraits<nsDocShellLoadState*> {
+ static void Write(IPC::Message* aMsg, IProtocol* aActor,
+ nsDocShellLoadState* aParam);
+ static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+ IProtocol* aActor, RefPtr<nsDocShellLoadState>* aResult);
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+namespace IPC {
+
+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..e7bcbb1184
--- /dev/null
+++ b/dom/ipc/EffectsInfo.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_EffectsInfo_h
+#define mozilla_dom_EffectsInfo_h
+
+#include "nsRect.h"
+
+namespace mozilla {
+namespace 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() { *this = EffectsInfo::FullyHidden(); }
+
+ static EffectsInfo VisibleWithinRect(const nsRect& aVisibleRect,
+ float aScaleX, float aScaleY) {
+ return EffectsInfo{aVisibleRect, aScaleX, aScaleY};
+ }
+ static EffectsInfo FullyHidden() { return EffectsInfo{nsRect(), 1.0f, 1.0f}; }
+
+ bool operator==(const EffectsInfo& aOther) {
+ return mVisibleRect == aOther.mVisibleRect && mScaleX == aOther.mScaleX &&
+ mScaleY == aOther.mScaleY;
+ }
+ bool operator!=(const EffectsInfo& aOther) { return !(*this == aOther); }
+
+ bool IsVisible() const { return !mVisibleRect.IsEmpty(); }
+
+ // The visible rect of this browser relative to the root frame. If this is
+ // empty then the browser can be considered invisible.
+ nsRect mVisibleRect;
+ // The desired scale factors to apply to rasterized content to match
+ // transforms applied in ancestor browsers.
+ float mScaleX;
+ float mScaleY;
+ // If you add new fields here, you must also update operator==
+
+ private:
+ EffectsInfo(const nsRect& aVisibleRect, float aScaleX, float aScaleY)
+ : mVisibleRect(aVisibleRect), mScaleX(aScaleX), mScaleY(aScaleY) {}
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_EffectsInfo_h
diff --git a/dom/ipc/FilePickerParent.cpp b/dom/ipc/FilePickerParent.cpp
new file mode 100644
index 0000000000..5049e03c52
--- /dev/null
+++ b/dom/ipc/FilePickerParent.cpp
@@ -0,0 +1,291 @@
+/* -*- 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/FileBlobImpl.h"
+#include "mozilla/dom/FileSystemSecurity.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/BrowserParent.h"
+#include "mozilla/dom/IPCBlobUtils.h"
+
+using mozilla::Unused;
+using namespace mozilla::dom;
+
+NS_IMPL_ISUPPORTS(FilePickerParent::FilePickerShownCallback,
+ nsIFilePickerShownCallback);
+
+NS_IMETHODIMP
+FilePickerParent::FilePickerShownCallback::Done(int16_t 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, parent, 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(int16_t 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;
+ }
+
+ Element* element = BrowserParent::GetFrom(Manager())->GetOwnerElement();
+ if (!element) {
+ return false;
+ }
+
+ nsCOMPtr<mozIDOMWindowProxy> window = element->OwnerDoc()->GetWindow();
+ if (!window) {
+ return false;
+ }
+
+ return NS_SUCCEEDED(mFilePicker->Init(window, mTitle, mMode));
+}
+
+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 int16_t& 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);
+ }
+
+ mCallback = new FilePickerShownCallback(this);
+
+ mFilePicker->Open(mCallback);
+ 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..abe9fac68c
--- /dev/null
+++ b/dom/ipc/FilePickerParent.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_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 {
+namespace dom {
+
+class FilePickerParent : public PFilePickerParent {
+ public:
+ FilePickerParent(const nsString& aTitle, const int16_t& aMode)
+ : mTitle(aTitle), mMode(aMode), mResult(nsIFilePicker::returnOK) {}
+
+ virtual ~FilePickerParent();
+
+ void Done(int16_t 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 int16_t& aCapture);
+
+ 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;
+ FilePickerParent* mFilePickerParent;
+ };
+
+ private:
+ bool CreateFilePicker();
+
+ // This runnable is used to do some I/O operation on a separate thread.
+ class IORunnable : public Runnable {
+ 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;
+ int16_t mMode;
+ int16_t mResult;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_FilePickerParent_h
diff --git a/dom/ipc/IdType.h b/dom/ipc/IdType.h
new file mode 100644
index 0000000000..18c04051e8
--- /dev/null
+++ b/dom/ipc/IdType.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_IdType_h
+#define mozilla_dom_IdType_h
+
+#include "ipc/IPCMessageUtils.h"
+
+namespace IPC {
+template <typename T>
+struct ParamTraits;
+} // namespace IPC
+
+namespace mozilla {
+namespace 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;
+};
+
+typedef IdType<BrowserParent> TabId;
+typedef IdType<ContentParent> ContentParentId;
+} // namespace dom
+} // namespace mozilla
+
+namespace IPC {
+
+template <typename T>
+struct ParamTraits<mozilla::dom::IdType<T>> {
+ typedef mozilla::dom::IdType<T> paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mId);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ return ReadParam(aMsg, aIter, &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..c90e9f7157
--- /dev/null
+++ b/dom/ipc/InProcessChild.h
@@ -0,0 +1,71 @@
+/* -*- 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 {
+namespace 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::HandleObject 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 dom
+} // namespace mozilla
+
+#endif // defined(mozilla_dom_InProcessChild_h)
diff --git a/dom/ipc/InProcessImpl.cpp b/dom/ipc/InProcessImpl.cpp
new file mode 100644
index 0000000000..d35eea501b
--- /dev/null
+++ b/dom/ipc/InProcessImpl.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 "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->GetIPCChannel(), 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;
+}
+
+already_AddRefed<JSActor> InProcessParent::InitJSActor(
+ JS::HandleObject 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;
+}
+
+already_AddRefed<JSActor> InProcessChild::InitJSActor(
+ JS::HandleObject 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) {
+ if (current->GetProtocolId() == PInProcessMsgStart) {
+ break; // Found the correct actor.
+ }
+ current = current->Manager();
+ }
+ if (!current) {
+ return nullptr; // Not a 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..6a4acc7e77
--- /dev/null
+++ b/dom/ipc/InProcessParent.h
@@ -0,0 +1,76 @@
+/* -*- 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 {
+namespace 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::HandleObject 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 dom
+} // namespace mozilla
+
+#endif // defined(mozilla_dom_InProcessParent_h)
diff --git a/dom/ipc/MMPrinter.cpp b/dom/ipc/MMPrinter.cpp
new file mode 100644
index 0000000000..9a18ec657d
--- /dev/null
+++ b/dom/ipc/MMPrinter.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 "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 "nsFrameMessageManager.h"
+#include "prenv.h"
+
+namespace mozilla::dom {
+
+LazyLogModule MMPrinter::sMMLog("MessageManager");
+
+/* 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;
+ }
+
+ MOZ_LOG(MMPrinter::sMMLog, LogLevel::Debug,
+ ("%s Message: %s in process type: %s", 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::UnpackClonedMessageDataForChild(aData, data);
+
+ /* Read original StructuredCloneData. */
+ JS::RootedValue scdContent(cx);
+ data.Read(cx, &scdContent, rv);
+ if (NS_WARN_IF(rv.Failed())) {
+ rv.SuppressException();
+ return;
+ }
+
+ JS::RootedString unevalObj(cx, JS_ValueToSource(cx, scdContent));
+ nsAutoJSString srcString;
+ if (!srcString.init(cx, unevalObj)) return;
+
+ MOZ_LOG(MMPrinter::sMMLog, LogLevel::Verbose,
+ (" %s", 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..c58acd6911
--- /dev/null
+++ b/dom/ipc/MMPrinter.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 MMPrinter_h
+#define MMPrinter_h
+
+#include "mozilla/dom/DOMTypes.h"
+#include "nsString.h"
+
+namespace mozilla {
+namespace 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 dom
+} // namespace mozilla
+
+#endif /* MMPrinter_h */
diff --git a/dom/ipc/ManifestMessagesChild.jsm b/dom/ipc/ManifestMessagesChild.jsm
new file mode 100644
index 0000000000..b7a8dd959a
--- /dev/null
+++ b/dom/ipc/ManifestMessagesChild.jsm
@@ -0,0 +1,121 @@
+/* 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
+ */
+"use strict";
+
+var EXPORTED_SYMBOLS = ["ManifestMessagesChild"];
+
+ChromeUtils.defineModuleGetter(
+ this,
+ "ManifestObtainer",
+ "resource://gre/modules/ManifestObtainer.jsm"
+);
+ChromeUtils.defineModuleGetter(
+ this,
+ "ManifestFinder",
+ "resource://gre/modules/ManifestFinder.jsm"
+);
+ChromeUtils.defineModuleGetter(
+ this,
+ "ManifestIcons",
+ "resource://gre/modules/ManifestIcons.jsm"
+);
+
+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 = 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 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 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..ad96825478
--- /dev/null
+++ b/dom/ipc/MaybeDiscarded.h
@@ -0,0 +1,135 @@
+/* -*- 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 {
+namespace 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 dom
+} // namespace mozilla
+
+#endif // mozilla_dom_MaybeDiscarded_h
diff --git a/dom/ipc/MemMapSnapshot.cpp b/dom/ipc/MemMapSnapshot.cpp
new file mode 100644
index 0000000000..7a104c420e
--- /dev/null
+++ b/dom/ipc/MemMapSnapshot.cpp
@@ -0,0 +1,44 @@
+/* -*- 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/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..1a17e9c9af
--- /dev/null
+++ b/dom/ipc/MemMapSnapshot.h
@@ -0,0 +1,53 @@
+/* -*- 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"
+
+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..bff321c63a
--- /dev/null
+++ b/dom/ipc/MemoryReportRequest.h
@@ -0,0 +1,75 @@
+/* -*- 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 {
+namespace 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 dom
+} // namespace mozilla
+
+#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..90010a51b4
--- /dev/null
+++ b/dom/ipc/PBrowser.ipdl
@@ -0,0 +1,1048 @@
+/* -*- 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 PPluginWidget;
+include protocol PRemotePrintJob;
+include protocol PChildToParentStream;
+include protocol PParentToChildStream;
+include protocol PFileDescriptorSet;
+include protocol PRemoteLazyInputStream;
+include protocol PPaymentRequest;
+include protocol PWindowGlobal;
+include protocol PBrowserBridge;
+include protocol PVsync;
+
+include DOMTypes;
+include NeckoChannelParams;
+include WindowGlobalTypes;
+include IPCBlob;
+include IPCStream;
+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/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 mozilla::gfx::Matrix4x4 from "mozilla/gfx/Matrix.h";
+using mozilla::gfx::MaybeMatrix4x4 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 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 mozilla::layers::LayersId from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::LayersObserverEpoch from "mozilla/layers/LayersTypes.h";
+using mozilla::layers::MaybeZoomConstraints from "mozilla/layers/ZoomConstraints.h";
+using mozilla::layers::GeckoContentController_TapType from "mozilla/layers/GeckoContentControllerTypes.h";
+using 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 nsCursor from "nsIWidget.h";
+using struct LookAndFeelInt 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::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 class mozilla::NativeEventData from "ipc/nsGUIEventIPC.h";
+using struct mozilla::FontRange from "ipc/nsGUIEventIPC.h";
+using mozilla::a11y::IAccessibleHolder from "mozilla/a11y/IPCTypes.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 CallerType from "mozilla/dom/BindingDeclarations.h";
+using mozilla::dom::EmbedderElementEventType from "mozilla/dom/TabMessageTypes.h";
+using refcounted class nsDocShellLoadState from "nsDocShellLoadState.h";
+using mozilla::IntrinsicSize from "nsIFrame.h";
+using mozilla::AspectRatio from "mozilla/AspectRatio.h";
+
+namespace mozilla {
+namespace dom {
+
+struct WebProgressData
+{
+ bool isTopLevel;
+ bool isLoadingDocument;
+ uint32_t loadType;
+};
+
+struct RequestData
+{
+ nsIURI requestURI;
+ nsIURI originalRequestURI;
+ nsCString matchedList;
+};
+
+struct WebProgressStateChangeData
+{
+ bool isNavigating;
+ bool mayEnableCharacterEncodingMenu;
+ bool charsetAutodetected;
+
+ // The following fields are only set when the aStateFlags param passed with
+ // this struct is |nsIWebProgress.STATE_STOP|.
+ nsString contentType;
+ nsString charset;
+ nsIURI documentURI;
+};
+
+struct WebProgressLocationChangeData
+{
+ bool isNavigating;
+ bool isSyntheticDocument;
+ bool mayEnableCharacterEncodingMenu;
+ bool charsetAutodetected;
+ nsString contentType;
+ nsString title;
+ nsString charset;
+ nsIURI documentURI;
+ nsIPrincipal contentPrincipal;
+ nsIPrincipal contentPartitionedPrincipal;
+ nsIContentSecurityPolicy csp;
+ 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;
+};
+
+/**
+ * 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.
+ */
+nested(upto inside_cpow) sync refcounted protocol PBrowser
+{
+ manager PContent;
+
+ manages PColorPicker;
+
+#ifdef ACCESSIBILITY
+ manages PDocAccessible;
+#endif
+
+ manages PFilePicker;
+ manages PPluginWidget;
+ manages PPaymentRequest;
+ 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. aMsaaID is the MSAA id for this content process, and
+ * is only valid on Windows. Set to 0 on other platforms. aDocCOMProxy
+ * is also Windows-specific and should be set to 0 on other platforms.
+ */
+ async PDocAccessible(nullable PDocAccessible aParentDoc, uint64_t aParentAcc,
+ uint32_t aMsaaID, IAccessibleHolder aDocCOMProxy);
+#endif
+
+ /*
+ * Creates a new remoted nsIWidget connection for windowed plugins
+ * in e10s mode. This is always initiated from the child in response
+ * to windowed plugin creation.
+ */
+ sync PPluginWidget();
+
+ async PPaymentRequest();
+
+ /**
+ * Create a new Vsync connection for our associated root widget
+ */
+ async PVsync();
+
+ /**
+ * Sends an NS_NATIVE_CHILD_OF_SHAREABLE_WINDOW to be adopted by the
+ * widget's shareable window on the chrome side. Only used on Windows.
+ */
+ async SetNativeChildOfShareableWindow(uintptr_t childWindow);
+
+ /**
+ * When content moves focus from a native plugin window that's a child
+ * of the native browser window we need to move native focus to the
+ * browser. Otherwise the plugin window will never relinquish focus.
+ */
+ sync DispatchFocusToTopLevelWindow();
+
+parent:
+ /**
+ * When child sends this message, parent should move focus to
+ * the next or previous focusable element or document.
+ */
+ async MoveFocus(bool forward, bool forDocumentNavigation);
+
+ /**
+ * SizeShellTo request propagation to parent.
+ *
+ * aFlag Can indicate if one of the dimensions should be ignored.
+ * If only one dimension has changed it has to be indicated
+ * by the nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_* flags.
+ * aShellItemWidth,
+ * aShellItemHeight On parent side we won't be able to decide the dimensions
+ * of the shell item parameter in the original SizeShellTo
+ * call so we send over its dimensions that will be used
+ * for the actual resize.
+ **/
+ async SizeShellTo(uint32_t aFlag, int32_t aWidth, int32_t aHeight,
+ int32_t aShellItemWidth, int32_t aShellItemHeight);
+
+ /**
+ * 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);
+
+ async Event(RemoteDOMEvent aEvent);
+
+ 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.
+ *
+ * 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)
+ 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.
+ */
+ nested(inside_cpow) async OnEventNeedingAckHandled(EventMessage message);
+
+ /**
+ * Notifies the parent process of native key event data received in a
+ * plugin process directly.
+ *
+ * aKeyEventData The native key event data. The actual type copied into
+ * NativeEventData depending on the caller. Please check
+ * PluginInstanceChild.
+ */
+ nested(inside_cpow) async OnWindowedPluginKeyEvent(NativeEventData aKeyEventData);
+
+ /**
+ * 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 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,
+ nsCString customCursorData,
+ uint32_t width, uint32_t height,
+ 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);
+
+ async PFilePicker(nsString aTitle, int16_t aMode);
+
+ /**
+ * Initiates an asynchronous request for one of the special indexedDB
+ * permissions for the provided principal.
+ *
+ * @param principal
+ * The principal of the request.
+ *
+ * NOTE: The principal is untrusted in the parent process. Only
+ * principals that can live in the content process should
+ * provided.
+ */
+ async IndexedDBPermissionRequest(nsIPrincipal aPrincipal) returns (uint32_t permission);
+
+ /**
+ * 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);
+
+ /**
+ * Brings up the auth prompt dialog.
+ * Called when this is the PBrowserParent for a nested remote iframe.
+ * aCallbackId corresponds to an nsIAuthPromptCallback that lives in the
+ * root process. It will be passed back to the root process with either the
+ * OnAuthAvailable or OnAuthCancelled message.
+ */
+ async AsyncAuthPrompt(nsCString uri, nsString realm, uint64_t aCallbackId);
+
+ /**
+ * 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__();
+
+ async ReplyKeyEvent(WidgetKeyboardEvent event);
+
+ /**
+ * Retrieves edit commands for the key combination represented by aEvent.
+ *
+ * @param aType One of nsIWidget::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,
+ 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 SynthesizeNativeTouchTap(LayoutDeviceIntPoint aPoint,
+ bool aLongTap,
+ uint64_t aObserverId);
+ async ClearNativeTouchSequence(uint64_t aObserverId);
+
+ async AccessKeyNotHandled(WidgetKeyboardEvent event);
+
+ async RegisterProtocolHandler(nsString scheme, nsIURI handlerURI, nsString title,
+ nsIURI documentURI);
+
+ async OnStateChange(WebProgressData? aWebProgressData,
+ RequestData aRequestData, uint32_t aStateFlags,
+ nsresult aStatus,
+ WebProgressStateChangeData? aStateChangeData);
+
+ async OnProgressChange(WebProgressData? aWebProgressData,
+ RequestData aRequestData, int32_t aCurSelfProgress,
+ int32_t aMaxSelfProgress, int32_t aCurTotalProgress,
+ int32_t aMaxTotalProgress);
+
+ async OnLocationChange(WebProgressData? aWebProgressData,
+ RequestData aRequestData, nsIURI aLocation,
+ uint32_t aFlags, bool aCanGoBack,
+ bool aCanGoForward,
+ WebProgressLocationChangeData? aLocationChangeData);
+
+ async OnStatusChange(WebProgressData? aWebProgressData,
+ RequestData aRequestData, nsresult aStatus,
+ nsString aMessage);
+
+ async NotifyContentBlockingEvent(uint32_t aEvent, RequestData aRequestData,
+ bool aBlocked, nsCString aTrackingOrigin,
+ nsCString[] aTrackingFullHashes,
+ StorageAccessPermissionGrantedReason? aReason);
+
+ async NavigationFinished();
+
+ async SessionStoreUpdate(nsCString? aDocShellCaps, bool? aPrivatedMode,
+ nsCString[] aPositions, int32_t[] aPositionDescendants,
+ InputFormData[] aInputs, CollectedInputDataValue[] aIdVals,
+ CollectedInputDataValue[] aXPathVals,
+ nsCString[] aOrigins, nsString[] aKeys,
+ nsString[] aValues, bool aIsFullStorage,
+ bool aNeedCollectSHistory, uint32_t aFlushId,
+ bool aIsFinal, uint32_t aEpoch);
+
+ async IntrinsicSizeOrRatioChanged(IntrinsicSize? aIntrinsicSize,
+ AspectRatio? aIntrinsicRatio);
+
+ /**
+ * 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 FlushTabState(uint32_t aFlushId, bool aIsFinal);
+ async UpdateEpoch(uint32_t aEpoch);
+ async UpdateSHistory(bool aImmediately);
+ async CloneDocumentTreeIntoSelf(MaybeDiscardedBrowsingContext aBc);
+
+ /**
+ * Parent informs the child to release all pointer capture.
+ */
+ prio(input) async ReleaseAllPointerCapture();
+
+parent:
+
+ /**
+ * Child informs the parent that the graphics objects are ready for
+ * compositing. This is sent when all pending changes have been
+ * sent to the compositor and are ready to be shown on the next composite.
+ * @see PCompositor
+ * @see RequestNotifyAfterRemotePaint
+ */
+ async RemotePaintIsReady();
+
+ /**
+ * Child informs the parent that the content is ready to handle input
+ * events. This is sent when the BrowserChild is created.
+ */
+ async RemoteIsReadyToHandleInputEvents();
+
+ /**
+ * Child informs the parent that the layer tree is already available.
+ */
+ async PaintWhileInterruptingJSNoOp(LayersObserverEpoch aEpoch);
+
+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, likely through win.moveTo or resizeTo
+ */
+ async SetDimensions(uint32_t aFlags, int32_t aX, int32_t aY,
+ int32_t aCx, int32_t aCy, double aScale);
+
+ nested(inside_sync) sync DispatchWheelEvent(WidgetWheelEvent event);
+ nested(inside_sync) sync DispatchMouseEvent(WidgetMouseEvent event);
+ nested(inside_sync) sync DispatchKeyboardEvent(WidgetKeyboardEvent event);
+
+ async InvokeDragSession(IPCDataTransfer[] transfers, uint32_t action,
+ Shmem? visualData,
+ uint32_t stride, SurfaceFormat format,
+ LayoutDeviceIntRect dragRect,
+ nsIPrincipal principal, nsIContentSecurityPolicy csp,
+ CookieJarSettingsArgs cookieJarSettings);
+
+ // 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 SetAllowDeprecatedTls(bool value);
+
+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 ResumeLoad(uint64_t pendingSwitchID, ParentShowInfo info);
+
+ async UpdateDimensions(DimensionInfo dimensions) compressall;
+
+ async SizeModeChanged(nsSizeMode sizeMode);
+
+ async ChildToParentMatrix(MaybeMatrix4x4 aMatrix,
+ ScreenRect aRemoteDocumentRect);
+
+ async SetIsUnderHiddenEmbedderElement(bool aIsUnderHiddenEmbedderElement);
+
+ async DynamicToolbarMaxHeightChanged(ScreenIntCoord height);
+
+ async DynamicToolbarOffsetChanged(ScreenIntCoord height);
+
+ async SetKeyboardIndicators(UIStateChangeType showFocusRings);
+
+ /**
+ * StopIMEStateManagement() is called when the process loses focus and
+ * should stop managing IME state.
+ */
+ async StopIMEStateManagement();
+
+ /**
+ * @see nsIDOMWindowUtils sendMouseEvent.
+ */
+ async MouseEvent(nsString aType,
+ float aX,
+ float aY,
+ int32_t aButton,
+ int32_t aClickCount,
+ int32_t aModifiers);
+
+ /**
+ * When two consecutive mouse move events would be added to the message queue,
+ * they are 'compressed' by dumping the oldest one.
+ */
+ prio(input) async RealMouseMoveEvent(WidgetMouseEvent event,
+ ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId) compress;
+ async NormalPriorityRealMouseMoveEvent(WidgetMouseEvent event,
+ ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId) compress;
+
+ /**
+ * 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*()`.
+ */
+ prio(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.
+ */
+ prio(input) async SynthMouseMoveEvent(WidgetMouseEvent event,
+ ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId);
+ async NormalPrioritySynthMouseMoveEvent(WidgetMouseEvent event,
+ ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId);
+
+ prio(input) async RealMouseButtonEvent(WidgetMouseEvent event,
+ ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId);
+ async NormalPriorityRealMouseButtonEvent(WidgetMouseEvent event,
+ ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId);
+
+ prio(input) async RealKeyEvent(WidgetKeyboardEvent event);
+ async NormalPriorityRealKeyEvent(WidgetKeyboardEvent event);
+
+ prio(input) async MouseWheelEvent(WidgetWheelEvent event,
+ ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId);
+ async NormalPriorityMouseWheelEvent(WidgetWheelEvent event,
+ ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId);
+
+ prio(input) async RealTouchEvent(WidgetTouchEvent aEvent,
+ ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId,
+ nsEventStatus aApzResponse);
+ async NormalPriorityRealTouchEvent(WidgetTouchEvent aEvent,
+ ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId,
+ nsEventStatus aApzResponse);
+
+ prio(input) async HandleTap(GeckoContentController_TapType aType, LayoutDevicePoint point,
+ Modifiers aModifiers, ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId);
+ async NormalPriorityHandleTap(GeckoContentController_TapType aType, LayoutDevicePoint point,
+ Modifiers aModifiers, ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId);
+
+ prio(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;
+ prio(input) async RealTouchMoveEvent2(WidgetTouchEvent aEvent,
+ ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId,
+ nsEventStatus aApzResponse) compress;
+ async NormalPriorityRealTouchMoveEvent2(WidgetTouchEvent aEvent,
+ ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId,
+ nsEventStatus aApzResponse) compress;
+
+ /*
+ * 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, nsIPrincipal aPrincipal,
+ nsIContentSecurityPolicy csp);
+
+ prio(input) async CompositionEvent(WidgetCompositionEvent event);
+ async NormalPriorityCompositionEvent(WidgetCompositionEvent event);
+
+ prio(input) async SelectionEvent(WidgetSelectionEvent event);
+ async NormalPrioritySelectionEvent(WidgetSelectionEvent event);
+
+ /**
+ * Call PasteTransferable via a controller on the content process
+ * to handle the command content event, "pasteTransferable".
+ */
+ async PasteTransferable(IPCDataTransfer aDataTransfer,
+ bool aIsPrivateData,
+ nsIPrincipal aRequestingPrincipal,
+ nsContentPolicyType aContentPolicyType);
+
+ /**
+ * Activate event forwarding from client to parent.
+ */
+ async ActivateFrameEvent(nsString aType, bool capture);
+
+ 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.
+ * @param aEpoch
+ * The layer observer epoch for this activation. This message should be
+ * ignored if this epoch has already been observed (via
+ * PaintWhileInterruptingJS).
+ */
+ async RenderLayers(bool aEnabled, LayersObserverEpoch aEpoch);
+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);
+
+ /**
+ * The parent (chrome thread) requests that the child inform it when
+ * the graphics objects are ready to display.
+ * @see PCompositor
+ * @see RemotePaintIsReady
+ */
+ async RequestNotifyAfterRemotePaint();
+
+ /**
+ * 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);
+
+ /**
+ * HandledWindowedPluginKeyEvent() is always called after posting a native
+ * key event with OnWindowedPluginKeyEvent().
+ *
+ * @param aKeyEventData The key event which was posted to the parent
+ * process.
+ * @param aIsConsumed true if aKeyEventData is consumed in the
+ * parent process. Otherwise, false.
+ */
+ async HandledWindowedPluginKeyEvent(NativeEventData aKeyEventData,
+ bool aIsConsumed);
+
+ /**
+ * 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 aSourceOuterWindowID Optionally, the ID of the nsGlobalWindowOuter
+ * 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,
+ uint64_t? aSourceOuterWindowID) 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 aOuterWindowID the ID of the outer window to print
+ * @param aPrintData the serialized settings to print with
+ */
+ async Print(uint64_t aOuterWindowID, 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();
+
+ /**
+ * Pass the current handle for the current native widget to the content
+ * process, so it can be used by PuppetWidget.
+ */
+ async SetWidgetNativeData(WindowsHandle aHandle);
+
+ async WillChangeProcess() returns (bool success);
+
+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(nsIURI aURI, nsIURI aLastVisitedURI,
+ uint32_t aFlags);
+
+ /** Fetches the visited status for an array of URIs (Android-only). */
+ async QueryVisitedState(nsIURI[] aURIs);
+
+ /**
+ * 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..4d41c68515
--- /dev/null
+++ b/dom/ipc/PBrowserBridge.ipdl
@@ -0,0 +1,129 @@
+/* -*- 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 "mozilla/LayoutMessageUtils.h";
+include "mozilla/dom/BindingIPCUtils.h";
+include "mozilla/dom/DocShellMessageUtils.h";
+include "mozilla/dom/TabMessageUtils.h";
+
+using mozilla::layers::LayersObserverEpoch from "mozilla/layers/LayersTypes.h";
+using class mozilla::WidgetMouseEvent from "ipc/nsGUIEventIPC.h";
+using mozilla::a11y::IDispatchHolder from "mozilla/a11y/IPCTypes.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 CallerType from "mozilla/dom/BindingDeclarations.h";
+using nsIntRect from "nsRect.h";
+using mozilla::dom::EmbedderElementEventType from "mozilla/dom/TabMessageTypes.h";
+using refcounted class nsDocShellLoadState from "nsDocShellLoadState.h";
+using mozilla::IntrinsicSize from "nsIFrame.h";
+using mozilla::AspectRatio from "mozilla/AspectRatio.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 refcounted 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);
+
+ /**
+ * Send the child the COM proxy for the embedded document accessible.
+ */
+ async SetEmbeddedDocAccessibleCOMProxy(IDispatchHolder aCOMProxy);
+
+ /**
+ * 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);
+
+parent:
+ // Destroy the remote web browser due to the nsFrameLoader going away.
+ async __delete__();
+
+ // DocShell messaging.
+ async LoadURL(nsDocShellLoadState aLoadState);
+ async ResumeLoad(uint64_t aPendingSwitchID);
+
+ // Out of process rendering.
+ async Show(OwnerShowInfo info);
+ async ScrollbarPreferenceChanged(ScrollbarPreference pref);
+ async UpdateDimensions(nsIntRect rect, ScreenIntSize size) compressall;
+ async RenderLayers(bool aEnabled, LayersObserverEpoch aEpoch);
+
+ async UpdateEffects(EffectsInfo aEffects);
+
+ /**
+ * 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 SetIsUnderHiddenEmbedderElement(bool aIsUnderHiddenEmbedderElement);
+
+ 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(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..0e54ad598c
--- /dev/null
+++ b/dom/ipc/PColorPicker.ipdl
@@ -0,0 +1,27 @@
+/* -*- 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 {
+
+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..87293ed138
--- /dev/null
+++ b/dom/ipc/PContent.ipdl
@@ -0,0 +1,1865 @@
+/* -*- 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 PBackground;
+include protocol PBrowser;
+include protocol PCompositorManager;
+include protocol PContentPermissionRequest;
+include protocol PCycleCollectWithLogs;
+include protocol PExternalHelperApp;
+include protocol PHandlerService;
+include protocol PFileDescriptorSet;
+include protocol PHal;
+include protocol PHeapSnapshotTempFileHelper;
+include protocol PProcessHangMonitor;
+include protocol PImageBridge;
+include protocol PRemoteLazyInputStream;
+include protocol PLoginReputation;
+include protocol PMedia;
+include protocol PNecko;
+include protocol PStreamFilter;
+include protocol PGMPContent;
+include protocol PGMPService;
+include protocol PPluginModule;
+include protocol PGMP;
+include protocol PPrinting;
+include protocol PChildToParentStream;
+include protocol PParentToChildStream;
+include protocol POfflineCacheUpdate;
+#ifdef MOZ_WEBSPEECH
+include protocol PSpeechSynthesis;
+#endif
+include protocol PTestShell;
+include protocol PRemoteSpellcheckEngine;
+include protocol PWebBrowserPersistDocument;
+include protocol PWebrtcGlobal;
+include protocol PWindowGlobal;
+include protocol PPresentation;
+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 PTabContext;
+include PluginTypes;
+include ProtocolTypes;
+include PBackgroundSharedTypes;
+include PContentPermission;
+include ServiceWorkerConfiguration;
+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/DataStorageIPCUtils.h";
+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/ReferrerInfoUtils.h";
+include "mozilla/ipc/ByteBufUtils.h";
+include "mozilla/ipc/URIUtils.h";
+include "mozilla/PermissionDelegateIPCUtils.h";
+
+using refcounted class nsIDOMGeoPosition from "nsGeoPositionIPCSerialiser.h";
+using refcounted 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 class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h";
+using mozilla::a11y::IHandlerControlHolder from "mozilla/a11y/IPCTypes.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::dom::TabId from "mozilla/dom/ipc/IdType.h";
+using mozilla::dom::ContentParentId from "mozilla/dom/ipc/IdType.h";
+using mozilla::LayoutDeviceIntPoint 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";
+using moveonly mozilla::UntrustedModulesData from "mozilla/UntrustedModulesData.h";
+using moveonly mozilla::ModulePaths from "mozilla/UntrustedModulesData.h";
+using moveonly mozilla::ModulesMapResult from "mozilla/UntrustedModulesData.h";
+using mozilla::Telemetry::DiscardedData from "mozilla/TelemetryComms.h";
+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 "nsIContentViewer.h";
+using mozilla::dom::MaybeDiscardedWindowContext from "mozilla/dom/WindowContext.h";
+using mozilla::dom::WindowContextTransaction from "mozilla/dom/WindowContext.h";
+using base::SharedMemoryHandle from "base/shared_memory.h";
+using mozilla::fontlist::Pointer from "SharedFontList.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 refcounted class nsDocShellLoadState from "nsDocShellLoadState.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::ContentBlocking::StorageAccessPromptChoices from "mozilla/ContentBlocking.h";
+using JSActorMessageKind from "mozilla/dom/JSActor.h";
+using JSActorMessageMeta from "mozilla/dom/PWindowGlobal.h";
+using mozilla::PermissionDelegateHandler::DelegatedPermissionList from "mozilla/PermissionDelegateHandler.h";
+using refcounted 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::PDMFactory::MediaCodecsSupported from "PDMFactory.h";
+using mozilla::RemoteDecodeIn from "mozilla/RemoteDecoderManagerChild.h";
+using mozilla::dom::PerformanceTimingData from "mozilla/dom/PerformanceTiming.h";
+using refcounted mozilla::dom::FeaturePolicy from "mozilla/dom/FeaturePolicy.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
+
+union SystemParameterValue {
+ bool;
+ float;
+};
+
+struct SystemParameterKVPair {
+ uint8_t id;
+ SystemParameterValue value;
+};
+
+struct ClipboardCapabilities {
+ bool supportsSelectionClipboard;
+ bool supportsFindClipboard;
+};
+
+union FileDescOrError {
+ FileDescriptor;
+ nsresult;
+};
+
+struct DomainPolicyClone
+{
+ bool active;
+ nsIURI[] blocklist;
+ nsIURI[] allowlist;
+ nsIURI[] superBlocklist;
+ 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;
+ nsIPrincipal principal;
+ nsID? agentClusterId;
+ bool revoked;
+};
+
+struct JSWindowActorEventDecl
+{
+ nsString name;
+ bool capture;
+ bool systemGroup;
+ bool allowUntrusted;
+ bool? passive;
+};
+
+struct JSWindowActorInfo
+{
+ nsCString name;
+ bool allFrames;
+
+ // 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;
+ // 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 XPCOMInitData
+{
+ bool isOffline;
+ bool isConnected;
+ int32_t captivePortalState;
+ bool isLangRTL;
+ bool haveBidiKeyboards;
+ nsCString[] dictionaries;
+ ClipboardCapabilities clipboardCaps;
+ DomainPolicyClone domainPolicy;
+ nsIURI userContentSheetURL;
+ GfxVarUpdate[] gfxNonDefaultVarUpdates;
+ ContentDeviceData contentDeviceData;
+ GfxInfoFeatureStatus[] gfxFeatureStatus;
+ DataStorageEntry[] dataStorage;
+ nsCString[] appLocales;
+ nsCString[] requestedLocales;
+ DynamicScalarDefinition[] dynamicScalarDefs;
+ SystemParameterKVPair[] systemParameters;
+};
+
+struct VisitedQueryResult
+{
+ 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;
+ nsIURI targetOriginURI;
+ nsIPrincipal callerPrincipal;
+ nsIPrincipal subjectPrincipal;
+ nsIURI callerURI;
+ bool isFromPrivateWindow;
+ nsCString scriptLocation;
+ uint64_t innerWindowId;
+};
+
+union SyncedContextInitializer
+{
+ BrowsingContextInitializer;
+ WindowContextInitializer;
+};
+
+union BlobURLDataRequestResult
+{
+ IPCBlob;
+ 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.
+ */
+nested(upto inside_cpow) sync protocol PContent
+{
+ manages PBrowser;
+ manages PContentPermissionRequest;
+ manages PCycleCollectWithLogs;
+ manages PExternalHelperApp;
+ manages PFileDescriptorSet;
+ manages PHal;
+ manages PHandlerService;
+ manages PHeapSnapshotTempFileHelper;
+ manages PRemoteLazyInputStream;
+ manages PMedia;
+ manages PNecko;
+ manages POfflineCacheUpdate;
+ manages PPrinting;
+ manages PChildToParentStream;
+ manages PParentToChildStream;
+#ifdef MOZ_WEBSPEECH
+ manages PSpeechSynthesis;
+#endif
+ manages PTestShell;
+ manages PRemoteSpellcheckEngine;
+ manages PWebBrowserPersistDocument;
+ manages PWebrtcGlobal;
+ manages PPresentation;
+ manages PURLClassifier;
+ manages PURLClassifierLocal;
+ manages PScriptCache;
+ manages PLoginReputation;
+ 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);
+
+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:
+ async PFileDescriptorSet(FileDescriptor fd);
+
+ // 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 AudioDefaultDeviceChange();
+
+ async NetworkLinkTypeChange(uint32_t type);
+
+ // 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);
+
+ async RequestPerformanceMetrics(nsID aID);
+
+ /**
+ * Used by third-party modules telemetry (aka "untrusted modules" telemetry)
+ * to pull data from content processes.
+ */
+ async GetUntrustedModulesData() returns (UntrustedModulesData? data);
+
+ /**
+ * 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 ClearImageCache(bool privateLoader, bool chrome);
+
+ async ClearStyleSheetCache(nsIPrincipal? aForPrincipal);
+
+ async SetOffline(bool offline);
+ async SetConnectivity(bool connectivity);
+ async SetCaptivePortalState(int32_t aState);
+
+ async NotifyVisited(VisitedQueryResult[] uri);
+
+ /**
+ * Tell the child that the system theme has changed, and that a repaint is
+ * necessary.
+ */
+ async ThemeChanged(LookAndFeelData lookAndFeelData, ThemeChangeKind aKind);
+
+ async UpdateSystemParameters(SystemParameterKVPair[] aUpdates);
+
+ async PreferenceUpdate(Pref pref);
+ async VarUpdate(GfxVarUpdate var);
+
+ async UpdatePerfStatsCollectionMask(uint64_t aMask);
+ async CollectPerfStatsJSON() returns (nsCString aStats);
+
+ async DataStoragePut(nsString aFilename, DataStorageItem aItem);
+ async DataStorageRemove(nsString aFilename, nsCString aKey, DataStorageType aType);
+ async DataStorageClear(nsString aFilename);
+
+ async NotifyAlertsObserver(nsCString topic, nsString data);
+
+ async GeolocationUpdate(nsIDOMGeoPosition aPosition);
+
+ async GeolocationError(uint16_t errorCode);
+
+ async UpdateDictionaryList(nsCString[] dictionaries);
+
+ async UpdateFontList(SystemFontListEntry[] 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.
+ */
+ 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();
+
+ async UpdateAppLocales(nsCString[] appLocales);
+ async UpdateRequestedLocales(nsCString[] requestedLocales);
+
+ 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.
+ * @param aTid is the thread ID of the chrome process main thread. Only used
+ * on Windows; pass 0 on other platforms.
+ * @param aMsaaID is an a11y-specific unique id for the content process
+ * that is generated by the chrome process. Only used on
+ * Windows; pass 0 on other platforms.
+ */
+ async ActivateA11y(uint32_t aMainChromeTid, uint32_t aMsaaID);
+
+ /**
+ * 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);
+
+ /**
+ * Send ServiceWorkerRegistrationData to child process.
+ */
+ async InitServiceWorkers(ServiceWorkerConfiguration aConfig);
+
+ /**
+ * 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,
+ LookAndFeelData lookAndFeeldata,
+ /* used on MacOSX/Linux/Android only: */
+ SystemFontListEntry[] systemFontList,
+ SharedMemoryHandle? sharedUASheetHandle,
+ uintptr_t sharedUASheetAddress,
+ SharedMemoryHandle[] sharedFontListBlocks);
+
+ // 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(nsIURI uri, uint32_t type);
+ async UnregisterSheet(nsIURI uri, uint32_t type);
+
+ /**
+ * Notify idle observers in the child
+ */
+ async NotifyIdleObserver(uint64_t observerId, nsCString topic, nsString str);
+
+ async InvokeDragSession(IPCDataTransfer[] transfers, uint32_t action);
+
+ async EndDragSession(bool aDoneDrag, bool aUserCancelled,
+ LayoutDeviceIntPoint aDragEndPoint,
+ uint32_t aKeyModifiers);
+
+ async DomainSetChanged(uint32_t aSetType, uint32_t aChangeType, nsIURI aDomain);
+
+ /**
+ * 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 presentation receiver has been launched with the
+ * correspondent iframe.
+ */
+ async NotifyPresentationReceiverLaunched(PBrowser aIframe, nsString aSessionId);
+
+ /**
+ * Notify the child that the info about a presentation receiver needs to be
+ * cleaned up.
+ */
+ async NotifyPresentationReceiverCleanUp(nsString aSessionId);
+
+ /**
+ * 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, Principal principal, nsString messageId);
+
+ /**
+ * Send a `push` event with data to a service worker in the child.
+ */
+ async PushWithData(nsCString scope, Principal principal,
+ nsString messageId, uint8_t[] data);
+
+ /**
+ * Send a `pushsubscriptionchange` event to a service worker in the child.
+ */
+ async PushSubscriptionChange(nsCString scope, Principal principal);
+
+ async GetFilesResponse(nsID aID, GetFilesResponseResult aResult);
+
+ async BlobURLRegistration(nsCString aURI, IPCBlob aBlob,
+ Principal aPrincipal,
+ nsID? aAgentClusterId);
+
+ async BlobURLUnregistration(nsCString aURI);
+
+ async GMPsChanged(GMPCapabilityData[] capabilities);
+
+
+ async PParentToChildStream();
+
+ async ProvideAnonymousTemporaryFile(uint64_t aID, FileDescOrError aFD);
+
+ async SetPermissionsWithKey(nsCString aPermissionKey, Permission[] aPermissions);
+
+ async RefreshScreens(ScreenDetails[] aScreens);
+
+ async PRemoteLazyInputStream(nsID aID, uint64_t aSize);
+
+ /**
+ * This call takes the set of plugins loaded in the chrome process, and
+ * sends them to the content process. However, in many cases this set will
+ * not have changed since the last SetPluginList message. To keep track of
+ * this, the chrome process increments an epoch number every time the set of
+ * plugins changes. The chrome process sends up the last epoch it observed.
+ * If the epoch last seen by the content process is the same, the content
+ * process ignores the update. Otherwise the content process updates its
+ * list and reloads its plugins.
+ **/
+ async SetPluginList(uint32_t pluginEpoch, PluginTag[] plugins, FakePluginTag[] fakePlugins);
+
+ async ShareCodeCoverageMutex(CrossProcessMutexHandle handle);
+ async FlushCodeCoverageCounters() returns (bool unused);
+
+ async GetMemoryUniqueSetSize() returns (int64_t uss);
+
+ /*
+ * 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.
+ */
+ prio(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);
+
+#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);
+ async GoForward(MaybeDiscardedBrowsingContext aContext, int32_t? aCancelContentJSEpoch, bool aRequireUserInteraction);
+ async GoToIndex(MaybeDiscardedBrowsingContext aContext, int32_t aIndex, int32_t? aCancelContentJSEpoch);
+ 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 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);
+
+parent:
+ /**
+ * This is a temporary way to pass index and length through parent process.
+ * Used for testing.
+ */
+ async SessionHistoryUpdate(MaybeDiscardedBrowsingContext aTopContext,
+ int32_t aIndex, int32_t aLength, nsID aChangeID);
+
+ async SynchronizeLayoutHistoryState(MaybeDiscardedBrowsingContext aContext,
+ nsILayoutHistoryState aState);
+
+ async SessionHistoryEntryTitle(MaybeDiscardedBrowsingContext aContext,
+ nsString aTitle);
+
+ async SessionHistoryEntryScrollRestorationIsManual(MaybeDiscardedBrowsingContext aContext,
+ bool aIsManual);
+
+ async SessionHistoryEntryCacheKey(MaybeDiscardedBrowsingContext aContext,
+ uint32_t aCacheKey);
+
+ async SessionHistoryEntryStoreWindowNameInContiguousEntries(MaybeDiscardedBrowsingContext aContext,
+ nsString aName);
+
+ async GetLoadingSessionHistoryInfoFromParent(MaybeDiscardedBrowsingContext aContext)
+ returns (LoadingSessionHistoryInfo? aLoadingInfo, int32_t aRequestedIndex, int32_t aLength);
+
+ async InitBackground(Endpoint<PBackgroundParent> aEndpoint);
+
+ async CreateGMPService();
+
+ async InitStreamFilter(uint64_t channelId, nsString addonId)
+ returns (Endpoint<PStreamFilterChild> aEndpoint);
+
+ /**
+ * This call connects the content process to a plugin process. This call
+ * returns an endpoint for a new PluginModuleParent. The corresponding
+ * PluginModuleChild will be started up in the plugin process.
+ */
+ sync LoadPlugin(uint32_t aPluginId)
+ returns (nsresult aResult, uint32_t aRunID, Endpoint<PPluginModuleParent> aEndpoint);
+
+ /**
+ * This call is used by asynchronous plugin instantiation to notify the
+ * content parent that it is now safe to initiate the plugin bridge for
+ * the specified plugin id. The endpoint for the content process part of the
+ * bridge is returned.
+ */
+ sync ConnectPluginBridge(uint32_t aPluginId)
+ returns (nsresult rv, Endpoint<PPluginModuleParent> aEndpoint);
+
+ async PRemoteSpellcheckEngine();
+
+ async InitCrashReporter(NativeThreadId tid);
+
+ sync IsSecureURI(uint32_t aType, nsIURI aURI, uint32_t aFlags,
+ OriginAttributes aOriginAttributes)
+ returns (bool isSecureURI);
+
+ async AccumulateMixedContentHSTS(nsIURI aURI, bool aActive,
+ OriginAttributes aOriginAttributes);
+
+ nested(inside_cpow) async PHal();
+
+ async PHeapSnapshotTempFileHelper();
+
+ async PNecko();
+
+ async PPrinting();
+
+ async PChildToParentStream();
+
+#ifdef MOZ_WEBSPEECH
+ async PSpeechSynthesis();
+#endif
+
+ async PMedia();
+
+ async PWebrtcGlobal();
+
+ async PPresentation();
+
+ async CreateAudioIPCConnection() returns (FileDescOrError fd);
+
+ sync PURLClassifier(Principal principal)
+ returns (bool success);
+
+ async PURLClassifierLocal(nsIURI uri, IPCURLClassifierFeature[] features);
+
+ async PLoginReputation(nsIURI formURI);
+
+ async PSessionStorageObserver();
+
+ async PBenchmarkStorage();
+
+ // Services remoting
+
+ async StartVisitedQueries(nsIURI[] uri);
+ async SetURITitle(nsIURI uri, nsString title);
+
+ async LoadURIExternal(nsIURI uri, nsIPrincipal triggeringPrincipal, MaybeDiscardedBrowsingContext browsingContext);
+ async ExtProtocolChannelConnectParent(uint64_t registrarId);
+
+ // PrefService message
+ sync GetGfxVars() returns (GfxVarUpdate[] vars);
+
+ sync SyncMessage(nsString aMessage, ClonedMessageData aData)
+ returns (StructuredCloneData[] retval);
+
+ async ShowAlert(nsIAlertNotification alert);
+
+ async CloseAlert(nsString name);
+
+ async DisableNotifications(Principal principal);
+
+ async OpenNotificationSettings(Principal 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(nsIURI uri,
+ LoadInfoArgs? loadInfoArgs,
+ nsCString aMimeContentType,
+ nsCString aContentDisposition,
+ uint32_t aContentDispositionHint,
+ nsString aContentDispositionFilename,
+ bool aForceSave,
+ int64_t aContentLength,
+ bool aWasFileChannel,
+ 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(IPCDataTransfer aDataTransfer,
+ bool aIsPrivateData,
+ Principal aRequestingPrincipal,
+ nsContentPolicyType aContentPolicyType,
+ int32_t aWhichClipboard);
+
+ // Given a list of supported types, returns the clipboard data for the
+ // first type that matches.
+ sync GetClipboard(nsCString[] aTypes, int32_t aWhichClipboard)
+ returns (IPCDataTransfer dataTransfer);
+
+ // Returns a list of formats supported by the clipboard
+ sync GetExternalClipboardFormats(int32_t aWhichClipboard, bool aPlainTextOnly) returns (nsCString[] aTypes);
+
+ // 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);
+
+ // 'Play', 'Beep' and 'PlayEventSound' are the only nsISound methods used in
+ // the content process.
+ async PlaySound(nsIURI aURL) compress;
+ async Beep() compress;
+ async PlayEventSound(uint32_t aEventId) compress;
+
+ sync GetIconForExtension(nsCString aFileExt, uint32_t aIconSize)
+ returns (uint8_t[] bits);
+
+ // Notify the parent of the presence or absence of private docshells
+ async PrivateDocShellsExist(bool aExist);
+
+ // Tell the parent that the child has gone idle for the first time.
+ async FirstIdle();
+
+ async DeviceReset();
+
+ async CopyFavicon(nsIURI oldURI, nsIURI newURI, bool isPrivate);
+
+ /**
+ * Notifies the parent about a recording device is starting or shutdown.
+ * @param recordingStatus starting or shutdown
+ * @param pageURL URL that request that changing the recording status
+ * @param isAudio recording start with microphone
+ * @param isVideo recording start with camera
+ */
+ async RecordingDeviceEvents(nsString recordingStatus,
+ nsString pageURL,
+ bool isAudio,
+ bool isVideo);
+
+ // 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);
+
+ /**
+ * Starts an offline application cache update.
+ * @param manifestURI
+ * URI of the manifest to fetch, the application cache group ID
+ * @param documentURI
+ * URI of the document that referred the manifest
+ * @param loadingPrincipal
+ * Principal of the document that referred the manifest
+ * @param stickDocument
+ * True if the update was initiated by a document load that referred
+ * a manifest.
+ * False if the update was initiated by applicationCache.update() call.
+ *
+ * Tells the update to carry the documentURI to a potential separate
+ * update of implicit (master) items.
+ *
+ * Why this argument? If the document was not found in an offline cache
+ * before load and refers a manifest and this manifest itself has not
+ * been changed since the last fetch, we will not do the application
+ * cache group update. But we must cache the document (identified by the
+ * documentURI). This argument will ensure that a previously uncached
+ * document will get cached and that we don't re-cache a document that
+ * has already been cached (stickDocument=false).
+ * @param tabId
+ * To identify which tab owns the app.
+ */
+ async POfflineCacheUpdate(nsIURI manifestURI, nsIURI documentURI,
+ PrincipalInfo loadingPrincipal, bool stickDocument,
+ CookieJarSettingsArgs cookieJarSettings);
+
+ /**
+ * Sets "offline-app" permission for the principal. Called when we hit
+ * a web app with the manifest attribute in <html>
+ */
+ async SetOfflinePermission(Principal principal);
+
+ /**
+ * 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,
+ Principal aPrincipal,
+ Principal aTopLevelPrincipal,
+ bool aIsHandlingUserInput,
+ bool aMaybeUnsafePermissionDelegate,
+ TabId tabId);
+
+ async ShutdownProfile(nsCString aProfile);
+
+ /**
+ * Request graphics initialization information from the parent.
+ */
+ sync GetGraphicsDeviceInitData()
+ returns (ContentDeviceData aData);
+
+ /**
+ * Request a buffer containing the contents of the output color profile.
+ * If set, this is the file pointed to by
+ * gfx.color_management.display_profile, otherwise it contains a
+ * platform-specific default
+ */
+ sync GetOutputColorProfileData()
+ returns (uint8_t[] aOutputColorProfileData);
+
+ /**
+ * 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 aFacePtr
+ * Font-list shared-memory "pointer" to the Face record to be updated.
+ * A Pointer is a record of a shared-memory block index and an offset
+ * within that block, which each process that maps the block can convert
+ * into a real pointer in its address space.
+ * @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, Pointer aFacePtr, gfxSparseBitSet aMap);
+
+ /**
+ * Ask the parent to set up the merged charmap for a family, to accelerate
+ * future fallback searches.
+ * aFamilyPtr may refer to an element of either the Families() or AliasFamilies().
+ */
+ async SetupFamilyCharMap(uint32_t aGeneration, Pointer aFamilyPtr);
+
+ /**
+ * 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(nsIURI aURI) returns (SharedMemoryHandle aHandle, uint32_t aSize);
+
+ async CreateWindow(PBrowser aThisTab,
+ MaybeDiscardedBrowsingContext aParent,
+ PBrowser aNewTab,
+ uint32_t aChromeFlags,
+ bool aCalledFromJS,
+ bool aWidthSpecified,
+ bool aForPrinting,
+ bool aForWindowDotPrint,
+ nsIURI aURIToLoad,
+ nsCString aFeatures,
+ float aFullZoom,
+ Principal aTriggeringPrincipal,
+ nsIContentSecurityPolicy aCsp,
+ nsIReferrerInfo aReferrerInfo,
+ OriginAttributes aOriginAttributes)
+ returns (CreatedWindowInfo window);
+
+ async CreateWindowInDifferentProcess(
+ PBrowser aThisTab,
+ MaybeDiscardedBrowsingContext aParent,
+ uint32_t aChromeFlags,
+ bool aCalledFromJS,
+ bool aWidthSpecified,
+ nsIURI aURIToLoad,
+ nsCString aFeatures,
+ float aFullZoom,
+ nsString aName,
+ nsIPrincipal aTriggeringPrincipal,
+ nsIContentSecurityPolicy aCsp,
+ nsIReferrerInfo aReferrerInfo,
+ OriginAttributes aOriginAttributes);
+
+ /**
+ * Tells the parent to ungrab the pointer on the default display.
+ *
+ * This is for GTK platforms where we have to ensure the pointer ungrab happens in the
+ * chrome process as that's the process that receives the pointer event.
+ */
+ sync UngrabPointer(uint32_t time);
+
+ sync RemovePermission(Principal principal, nsCString permissionType) returns (nsresult rv);
+
+ /**
+ * 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, Principal principal,
+ nsString messageId);
+
+ /**
+ * Notify `push-message` observers with data in the parent.
+ */
+ async NotifyPushObserversWithData(nsCString scope, Principal principal,
+ nsString messageId, uint8_t[] data);
+
+ /**
+ * Notify `push-subscription-change` observers in the parent.
+ */
+ async NotifyPushSubscriptionChangeObservers(nsCString scope,
+ Principal principal);
+
+ async GetFilesRequest(nsID aID, nsString aDirectory, bool aRecursiveFlag);
+ async DeleteGetFilesRequest(nsID aID);
+
+ async StoreAndBroadcastBlobURLRegistration(nsCString url, IPCBlob blob,
+ Principal principal,
+ nsID? aAgentClusterId);
+
+ async UnstoreAndBroadcastBlobURLUnregistration(nsCString url, Principal principal);
+
+ /**
+ * 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);
+
+ sync GetA11yContentId() returns (uint32_t aContentId);
+ async A11yHandlerControl(uint32_t aPid,
+ IHandlerControlHolder aHandlerControl);
+
+ async AddMemoryReport(MemoryReport aReport);
+
+ async MaybeReloadPlugins();
+
+ async BHRThreadHang(HangDetails aHangDetails);
+
+ async AddPerformanceMetrics(nsID aID, PerformanceInfo[] aMetrics);
+
+ /*
+ * Adds a certificate exception for the given hostname and port.
+ */
+ async AddCertException(nsCString aSerializedCert, uint32_t aFlags,
+ nsCString aHostName, int32_t aPort,
+ 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(Principal 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,
+ Principal aTrackingPrincipal,
+ nsCString aTrackingOrigin,
+ int aAllowMode,
+ StorageAccessPermissionGrantedReason? aReason)
+ returns (bool unused);
+
+ async CompleteAllowAccessFor(MaybeDiscardedBrowsingContext aParentContext,
+ uint64_t aTopLevelWindowId,
+ Principal aTrackingPrincipal,
+ nsCString aTrackingOrigin,
+ uint32_t aCookieBehavior,
+ StorageAccessPermissionGrantedReason aReason)
+ returns (StorageAccessPromptChoices? choice);
+
+ async StoreUserInteractionAsPermission(Principal aPrincipal);
+
+ /**
+ * 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);
+
+ /**
+ * 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);
+
+ /**
+ * 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);
+
+ async HistoryGo(MaybeDiscardedBrowsingContext aContext,
+ int32_t aOffset, uint64_t aHistoryEpoch, bool aRequireUserInteraction) returns(int32_t requestedIndex);
+
+ async BlobURLDataRequest(nsCString aBlobURL,
+ nsIPrincipal aTriggeringPrincipal,
+ nsIPrincipal aLoadingPrincipal,
+ OriginAttributes aOriginAttributes,
+ nsID? aAgentClusterId)
+ 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);
+
+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.
+ */
+ async ReportFrameTimingData(uint64_t innerWindowId, 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,
+ Principal principal);
+
+ /**
+ * Send a Push error message to all service worker clients in the parent or
+ * child.
+ */
+ async PushError(nsCString scope, Principal 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.
+ */
+ async CreateBrowsingContext(uint64_t aGroupId, BrowsingContextInitializer aInit);
+
+ /**
+ * Discards the passed-in BrowsingContext. If the BrowsingContext has
+ * already been discarded, this message does nothing.
+ * The response promise is fulfilled when the process has flagged the
+ * BrowsingContext as discarded.
+ */
+ async DiscardBrowsingContext(MaybeDiscardedBrowsingContext aContext)
+ returns (bool unused);
+
+ async AdjustWindowFocus(MaybeDiscardedBrowsingContext aContext,
+ bool aCheckPermission,
+ bool aIsVisible);
+ async WindowClose(MaybeDiscardedBrowsingContext aContext,
+ bool aTrustedCaller);
+ async WindowFocus(MaybeDiscardedBrowsingContext aContext,
+ CallerType aCallerType, uint64_t aActionId);
+ async WindowBlur(MaybeDiscardedBrowsingContext aContext);
+ async RaiseWindow(MaybeDiscardedBrowsingContext aContext, CallerType aCallerType, uint64_t aActionId);
+ async ClearFocus(MaybeDiscardedBrowsingContext aContext);
+ async SetFocusedBrowsingContext(MaybeDiscardedBrowsingContext aContext);
+ 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:
+ 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,
+ MaybeDiscardedBrowsingContext aActiveBrowsingContext);
+ async ReviseActiveBrowsingContext(MaybeDiscardedBrowsingContext aActiveBrowsingContext,
+ uint64_t aActionId);
+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.
+ 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,
+ FeaturePolicy aContainerFeaturePolicy);
+};
+
+}
+}
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..182e5e9dc1
--- /dev/null
+++ b/dom/ipc/PContentPermissionRequest.ipdl
@@ -0,0 +1,26 @@
+/* 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;
+
+namespace mozilla {
+namespace dom {
+
+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..f420c6ddae
--- /dev/null
+++ b/dom/ipc/PCycleCollectWithLogs.ipdl
@@ -0,0 +1,22 @@
+/* -*- 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 {
+
+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..c65640d50e
--- /dev/null
+++ b/dom/ipc/PFilePicker.ipdl
@@ -0,0 +1,53 @@
+/* -*- 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 protocol PChildToParentStream;
+include protocol PFileDescriptorSet;
+include protocol PParentToChildStream;
+include protocol PRemoteLazyInputStream;
+
+include IPCBlob;
+
+using struct mozilla::void_t from "mozilla/ipc/IPCCore.h";
+
+namespace mozilla {
+namespace dom {
+
+struct InputBlobs
+{
+ IPCBlob[] blobs;
+};
+
+struct InputDirectory
+{
+ nsString directoryPath;
+};
+
+union MaybeInputData
+{
+ InputBlobs;
+ InputDirectory;
+ void_t;
+};
+
+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,
+ int16_t capture);
+
+child:
+ async __delete__(MaybeInputData data, int16_t result);
+};
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/PInProcess.ipdl b/dom/ipc/PInProcess.ipdl
new file mode 100644
index 0000000000..6b14ae3663
--- /dev/null
+++ b/dom/ipc/PInProcess.ipdl
@@ -0,0 +1,28 @@
+/* -*- 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 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.
+ */
+async refcounted protocol PInProcess
+{
+ manages PWindowGlobal;
+};
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/PLoginReputation.ipdl b/dom/ipc/PLoginReputation.ipdl
new file mode 100644
index 0000000000..98c508f650
--- /dev/null
+++ b/dom/ipc/PLoginReputation.ipdl
@@ -0,0 +1,26 @@
+/* -*- 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;
+
+namespace mozilla {
+namespace dom {
+
+// PLoginReputation allows child to send URL to parent when user focuses
+// on a password field. Right now this is an one way IPC call (No callback
+// will return after parent receives the IPC message) since we just process
+// the URL in parent (LoginReputationService) and stores the result to telemetry.
+protocol PLoginReputation
+{
+ manager PContent;
+
+child:
+ async __delete__();
+};
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/PPluginWidget.ipdl b/dom/ipc/PPluginWidget.ipdl
new file mode 100644
index 0000000000..1e5d308292
--- /dev/null
+++ b/dom/ipc/PPluginWidget.ipdl
@@ -0,0 +1,63 @@
+ /* 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 "mozilla/dom/BindingIPCUtils.h";
+include "mozilla/GfxMessageUtils.h";
+
+using mozilla::dom::CallerType from "mozilla/dom/BindingDeclarations.h";
+using nsIntRect from "nsRect.h";
+
+namespace mozilla {
+namespace plugins {
+
+/**
+ * PPluginWidget - a nsIWidget'ish protocol for windowed plugins in e10s.
+ * On windows we create native widgets in chrome which we then manage
+ * from content. On the566595 content side there's PluginWidgetProxy which
+ * implements nsIWidget. We hand this around layout and plugins code. Anything
+ * not dealt with via PluginWidgetProxy falls through to PuppetWidget. Native
+ * widget exists on the chrome side (PluginWidgetParent) attached to the
+ * browser window as a child. Window management calls are forwarded from
+ * PluginWidgetProxy to PluginWidgetParent over this interface.
+ *
+ * Note lifetime management for PluginWidgetProxy (the plugin widget) and the
+ * connection (PluginWidgetChild) are separated. PluginWidgetChild will
+ * be torn down first by the tab, followed by the deref'ing of the nsIWidget
+ * via layout.
+ */
+sync protocol PPluginWidget {
+ manager PBrowser;
+
+parent:
+ async __delete__();
+
+ /**
+ * Used to set the ID of a scroll capture container from the parent process,
+ * so that we can create a proxy container in the layer tree.
+ * @param aScrollCaptureId async container ID of the parent container
+ * @param aPluginInstanceId plugin ID on which to set the scroll capture ID
+ */
+ sync Create() returns (nsresult aResult, uint64_t aScrollCaptureId,
+ uintptr_t aPluginInstanceId);
+ async SetFocus(bool aRaise, CallerType aCallerType);
+
+ /**
+ * Returns NS_NATIVE_PLUGIN_PORT and its variants: a sharable native
+ * window for plugins. On Linux, this returns an XID for a socket widget
+ * embedded in the chrome side native window. On Windows this returns the
+ * native HWND of the plugin widget.
+ */
+ sync GetNativePluginPort() returns (uintptr_t value);
+
+ /**
+ * Sends an NS_NATIVE_CHILD_WINDOW to be adopted by the widget's native window
+ * on the chrome side. This is only currently used on Windows.
+ */
+ sync SetNativeChildWindow(uintptr_t childWindow);
+};
+
+}
+}
diff --git a/dom/ipc/PProcessHangMonitor.ipdl b/dom/ipc/PProcessHangMonitor.ipdl
new file mode 100644
index 0000000000..224b98aded
--- /dev/null
+++ b/dom/ipc/PProcessHangMonitor.ipdl
@@ -0,0 +1,57 @@
+/* -*- 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 mozilla::layers::LayersObserverEpoch from "mozilla/layers/LayersTypes.h";
+
+namespace mozilla {
+
+struct SlowScriptData
+{
+ TabId tabId;
+ nsCString filename;
+ nsString addonId;
+ double duration;
+};
+
+struct PluginHangData
+{
+ uint32_t pluginId;
+ ProcessId contentProcessId;
+};
+
+union HangData
+{
+ SlowScriptData;
+ PluginHangData;
+};
+
+protocol PProcessHangMonitor
+{
+parent:
+ async HangEvidence(HangData data);
+ async ClearHang();
+
+child:
+ async TerminateScript(bool aTerminateGlobal);
+
+ async BeginStartingDebugger();
+ async EndStartingDebugger();
+
+ async PaintWhileInterruptingJS(TabId tabId, LayersObserverEpoch aEpoch);
+
+ async CancelContentJSExecutionIfRunning(
+ TabId tabId, NavigationType aNavigationType,
+ int32_t aNavigationIndex, nsCString? aNavigationURI, int32_t aEpoch);
+};
+
+} // namespace mozilla
diff --git a/dom/ipc/PTabContext.ipdlh b/dom/ipc/PTabContext.ipdlh
new file mode 100644
index 0000000000..b83f87d26b
--- /dev/null
+++ b/dom/ipc/PTabContext.ipdlh
@@ -0,0 +1,62 @@
+/* -*- 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 UIStateChangeType from "nsPIDOMWindow.h";
+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;
+
+ // The requested presentation URL.
+ // This value would be empty if the TabContext isn't created for
+ // presented content.
+ nsString presentationURL;
+
+ // Keyboard indicator state inherited from the parent.
+ UIStateChangeType showFocusRings;
+
+ // Maximum number of touch points on the screen.
+ uint32_t maxTouchPoints;
+};
+
+struct JSPluginFrameIPCTabContext
+{
+ uint32_t jsPluginId;
+};
+
+// 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;
+ JSPluginFrameIPCTabContext;
+};
+
+}
+}
diff --git a/dom/ipc/PURLClassifier.ipdl b/dom/ipc/PURLClassifier.ipdl
new file mode 100644
index 0000000000..b003be29f7
--- /dev/null
+++ b/dom/ipc/PURLClassifier.ipdl
@@ -0,0 +1,23 @@
+/* -*- 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 {
+
+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..c835359725
--- /dev/null
+++ b/dom/ipc/PURLClassifierLocal.ipdl
@@ -0,0 +1,35 @@
+/* -*- 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";
+
+using refcounted class nsIURI from "nsIURI.h";
+
+namespace mozilla {
+namespace dom {
+
+struct URLClassifierLocalResult
+{
+ nsIURI uri;
+ nsCString featureName;
+ nsCString matchingList;
+};
+
+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..19fdc4164d
--- /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.
+ prio(high) async Notify(VsyncEvent aVsync, float aVsyncRate) compress;
+
+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..b257f64b75
--- /dev/null
+++ b/dom/ipc/PWindowGlobal.ipdl
@@ -0,0 +1,203 @@
+/* -*- 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/PermissionMessageUtils.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 NeckoChannelParams;
+
+include "mozilla/layers/LayersMessageUtils.h";
+
+using JSActorMessageKind from "mozilla/dom/JSActor.h";
+using mozilla::gfx::IntRect from "mozilla/gfx/Rect.h";
+using moveonly mozilla::gfx::PaintFragment from "mozilla/gfx/CrossProcessPaint.h";
+using nscolor from "nsColor.h";
+using refcounted class nsDocShellLoadState from "nsDocShellLoadState.h";
+using mozilla::dom::XPCOMPermitUnloadAction from "nsIContentViewer.h";
+using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
+using mozilla::layers::LayersId from "mozilla/layers/LayersTypes.h";
+using refcounted class nsITransportSecurityInfo from "nsITransportSecurityInfo.h";
+using mozilla::UseCounters from "mozilla/UseCounter.h";
+using mozilla::dom::MaybeDiscardedWindowContext from "mozilla/dom/WindowContext.h";
+using refcounted mozilla::dom::FeaturePolicy from "mozilla/dom/FeaturePolicy.h";
+
+namespace mozilla {
+namespace dom {
+
+struct JSActorMessageMeta {
+ nsCString actorName;
+ nsString messageName;
+ uint64_t queryId;
+ JSActorMessageKind kind;
+};
+
+struct IPCWebShareData
+{
+ nsCString title;
+ nsCString text;
+ 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 refcounted 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);
+
+ /**
+ * Returns the serialized security info associated with this window.
+ */
+ async GetSecurityInfo() returns(nsCString? serializedSecInfo);
+
+ 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(FeaturePolicy aContainerFeaturePolicy);
+
+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.
+ 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.
+ async UpdateDocumentPrincipal(nsIPrincipal aPrincipal);
+
+ // Update document's `documentHasLoaded` bit in this WindowGlobal.
+ async UpdateDocumentHasLoaded(bool aDocumentHasLoaded);
+
+ // Update document's 'documentHasUserInteracted' bit in this WindowGlobal.
+ async UpdateDocumentHasUserInteracted(bool aDocumentHasUserInteracted);
+
+ // Update document's sandbox flags in this WindowGlobal.
+ async UpdateSandboxFlags(uint32_t aSandboxFlags);
+
+ // Update document csp's fields in this WindowGlobal.
+ async UpdateDocumentCspSettings(bool aBlockAllMixedContent, bool aUpgradeInsecureRequests);
+
+ // Update document's cookie settings in this WindowGlobal.
+ async UpdateCookieJarSettings(CookieJarSettingsArgs cookieJarSettings);
+
+ // Update the title of the document in this WindowGlobal.
+ async UpdateDocumentTitle(nsString aTitle);
+
+ async UpdateDocumentSecurityInfo(nsITransportSecurityInfo aSecurityInfo);
+
+ // Update the document's HTTPS-Only Mode flags in this WindowGlobal.
+ async UpdateHttpsOnlyStatus(uint32_t aHttpsOnlyStatus);
+
+ /// Send down initial document bit to the parent.
+ 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.
+ async SetClientInfo(IPCClientInfo aClientInfo);
+
+ /**
+ * Inform the parent that the document will preload a resource if the
+ * network.preload pref is enabled.
+ */
+ async UpdateDocumentWouldPreloadResources();
+
+ /**
+ * Submit load event telemetry affiliated with whether or not the document
+ * tree preloaded any resources.
+ */
+ async SubmitLoadEventPreloadTelemetry(TimeStamp aNavigationStart,
+ TimeStamp aLoadStart,
+ TimeStamp aLoadEnd);
+
+
+ /**
+ * Submit Time-to-First-Interaction telemetry correlated with whether or not
+ * the document tree preloaded any resources.
+ */
+ async SubmitTimeToFirstInteractionPreloadTelemetry(uint32_t aMillis);
+
+ /**
+ * Submit Load Input Event Response time telemetry correlated with whether
+ * or not the document tree preloaded any resources.
+ */
+ async SubmitLoadInputEventResponsePreloadTelemetry(uint32_t aMillis);
+
+ // 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 nsIContentViewer::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 Destroy();
+};
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/PermissionMessageUtils.cpp b/dom/ipc/PermissionMessageUtils.cpp
new file mode 100644
index 0000000000..2e26aaa78a
--- /dev/null
+++ b/dom/ipc/PermissionMessageUtils.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/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::Message* aMsg,
+ IProtocol* aActor,
+ nsIPrincipal* aParam) {
+ MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
+
+ Maybe<PrincipalInfo> info;
+ if (aParam) {
+ info.emplace();
+ nsresult rv = PrincipalToPrincipalInfo(aParam, info.ptr());
+ MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
+ }
+
+ WriteIPDLParam(aMsg, aActor, info);
+}
+
+bool IPDLParamTraits<nsIPrincipal*>::Read(const IPC::Message* aMsg,
+ PickleIterator* aIter,
+ IProtocol* aActor,
+ RefPtr<nsIPrincipal>* aResult) {
+ Maybe<PrincipalInfo> info;
+ if (!ReadIPDLParam(aMsg, aIter, 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..e73ce7922e
--- /dev/null
+++ b/dom/ipc/PermissionMessageUtils.h
@@ -0,0 +1,76 @@
+/* -*- 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 IPC {
+
+/**
+ * Legacy IPC::Principal type. Use nsIPrincipal directly in new IPDL code.
+ */
+class Principal {
+ friend struct mozilla::ipc::IPDLParamTraits<Principal>;
+
+ public:
+ Principal() = default;
+
+ explicit Principal(nsIPrincipal* aPrincipal) : mPrincipal(aPrincipal) {}
+
+ operator nsIPrincipal*() const { return mPrincipal.get(); }
+
+ Principal& operator=(const Principal& aOther) = delete;
+
+ private:
+ RefPtr<nsIPrincipal> mPrincipal;
+};
+
+} // namespace IPC
+
+namespace mozilla {
+namespace ipc {
+
+template <>
+struct IPDLParamTraits<nsIPrincipal*> {
+ static void Write(IPC::Message* aMsg, IProtocol* aActor,
+ nsIPrincipal* aParam);
+ static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+ IProtocol* aActor, RefPtr<nsIPrincipal>* aResult);
+
+ // Overload to support deserializing nsCOMPtr<nsIPrincipal> directly.
+ static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+ IProtocol* aActor, nsCOMPtr<nsIPrincipal>* aResult) {
+ RefPtr<nsIPrincipal> result;
+ if (!Read(aMsg, aIter, aActor, &result)) {
+ return false;
+ }
+ *aResult = std::move(result);
+ return true;
+ }
+};
+
+template <>
+struct IPDLParamTraits<IPC::Principal> {
+ typedef IPC::Principal paramType;
+ static void Write(IPC::Message* aMsg, IProtocol* aActor,
+ const paramType& aParam) {
+ WriteIPDLParam(aMsg, aActor, aParam.mPrincipal);
+ }
+ static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+ IProtocol* aActor, paramType* aResult) {
+ return ReadIPDLParam(aMsg, aIter, aActor, &aResult->mPrincipal);
+ }
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+#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..9c297ca74c
--- /dev/null
+++ b/dom/ipc/PreallocatedProcessManager.cpp
@@ -0,0 +1,428 @@
+/* -*- 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/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 <deque>
+
+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();
+ void AllocateOnIdle();
+ void AllocateNow();
+
+ void RereadPrefs();
+ void Enable(uint32_t aProcesses);
+ void Disable();
+ void CloseProcesses();
+
+ bool IsEmpty() const {
+ return mPreallocatedProcesses.empty() && !mLaunchInProgress;
+ }
+
+ bool mEnabled;
+ static bool sShutdown;
+ bool mLaunchInProgress;
+ uint32_t mNumberPreallocs;
+ std::deque<RefPtr<ContentParent>> 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;
+bool PreallocatedProcessManagerImpl::sShutdown = false;
+
+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), mLaunchInProgress(false), mNumberPreallocs(1) {}
+
+PreallocatedProcessManagerImpl::~PreallocatedProcessManagerImpl() {
+ // This shouldn't happen, because the promise callbacks should
+ // hold strong references, but let't make absolutely sure:
+ MOZ_RELEASE_ASSERT(!mLaunchInProgress);
+}
+
+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);
+ }
+ // Let's prevent any new preallocated processes from starting. ContentParent
+ // will handle the shutdown of the existing process and the
+ // mPreallocatedProcesses reference will be cleared by the ClearOnShutdown
+ // of the manager singleton.
+ sShutdown = true;
+ } 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();
+ }
+ if (number >= 0) {
+ Enable(number);
+ // We have one prealloc queue for all types except File now
+ if (static_cast<uint64_t>(number) < mPreallocatedProcesses.size()) {
+ CloseProcesses();
+ }
+ }
+ } else {
+ Disable();
+ }
+}
+
+already_AddRefed<ContentParent> PreallocatedProcessManagerImpl::Take(
+ const nsACString& aRemoteType) {
+ if (!mEnabled || sShutdown) {
+ return nullptr;
+ }
+ RefPtr<ContentParent> process;
+ if (!mPreallocatedProcesses.empty()) {
+ process = mPreallocatedProcesses.front().forget();
+ mPreallocatedProcesses.pop_front(); // holds a nullptr
+
+ ProcessPriorityManager::SetProcessPriority(process,
+ PROCESS_PRIORITY_FOREGROUND);
+
+ // We took a preallocated process. Let's try to start up a new one
+ // soon.
+ AllocateOnIdle();
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("Use prealloc process %p", process.get()));
+ }
+ return process.forget();
+}
+
+void PreallocatedProcessManagerImpl::Erase(ContentParent* aParent) {
+ // Ensure this ContentParent isn't cached
+ for (auto it = mPreallocatedProcesses.begin();
+ it != mPreallocatedProcesses.end(); it++) {
+ if (*it == aParent) {
+ mPreallocatedProcesses.erase(it);
+ break;
+ }
+ }
+}
+
+void PreallocatedProcessManagerImpl::Enable(uint32_t aProcesses) {
+ mNumberPreallocs = aProcesses;
+ if (mEnabled) {
+ return;
+ }
+
+ mEnabled = true;
+ AllocateAfterDelay();
+}
+
+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 mEnabled && sNumBlockers == 0 &&
+ mPreallocatedProcesses.size() < mNumberPreallocs && !sShutdown &&
+ (FissionAutostart() ||
+ !ContentParent::IsMaxProcessCountReached(DEFAULT_REMOTE_TYPE));
+}
+
+void PreallocatedProcessManagerImpl::AllocateAfterDelay() {
+ if (!mEnabled) {
+ return;
+ }
+
+ NS_DelayedDispatchToCurrentThread(
+ NewRunnableMethod("PreallocatedProcessManagerImpl::AllocateOnIdle", this,
+ &PreallocatedProcessManagerImpl::AllocateOnIdle),
+ StaticPrefs::dom_ipc_processPrelaunch_delayMs());
+}
+
+void PreallocatedProcessManagerImpl::AllocateOnIdle() {
+ if (!mEnabled) {
+ return;
+ }
+
+ NS_DispatchToCurrentThreadQueue(
+ NewRunnableMethod("PreallocatedProcessManagerImpl::AllocateNow", this,
+ &PreallocatedProcessManagerImpl::AllocateNow),
+ EventQueuePriority::Idle);
+}
+
+void PreallocatedProcessManagerImpl::AllocateNow() {
+ if (!CanAllocate()) {
+ if (mEnabled && !sShutdown && IsEmpty() && sNumBlockers > 0) {
+ // If it's too early to allocate a process let's retry later.
+ AllocateAfterDelay();
+ }
+ return;
+ }
+
+ RefPtr<PreallocatedProcessManagerImpl> self(this);
+ mLaunchInProgress = true;
+
+ ContentParent::PreallocateProcess()->Then(
+ GetCurrentSerialEventTarget(), __func__,
+
+ [self, this](const RefPtr<ContentParent>& process) {
+ mLaunchInProgress = false;
+ if (process->IsDead()) {
+ // 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 {
+ if (CanAllocate()) {
+ // slight perf reason for push_back - while the cpu cache
+ // probably has stack/etc associated with the most recent
+ // process created, we don't know that it has finished startup.
+ // If we added it to the queue on completion of startup, we
+ // could push_front it, but that would require a bunch more
+ // logic.
+ mPreallocatedProcesses.push_back(process);
+ MOZ_LOG(ContentParent::GetLog(), LogLevel::Debug,
+ ("Preallocated = %lu of %d processes",
+ (unsigned long)mPreallocatedProcesses.size(),
+ mNumberPreallocs));
+
+ // Continue prestarting processes if needed
+ if (mPreallocatedProcesses.size() < mNumberPreallocs) {
+ AllocateOnIdle();
+ }
+ } else {
+ process->ShutDownProcess(ContentParent::SEND_SHUTDOWN_MESSAGE);
+ }
+ }
+ },
+
+ [self, this](ContentParent::LaunchError err) {
+ mLaunchInProgress = false;
+ });
+}
+
+void PreallocatedProcessManagerImpl::Disable() {
+ if (!mEnabled) {
+ return;
+ }
+
+ mEnabled = false;
+ CloseProcesses();
+}
+
+void PreallocatedProcessManagerImpl::CloseProcesses() {
+ while (!mPreallocatedProcesses.empty()) {
+ RefPtr<ContentParent> process(mPreallocatedProcesses.front().forget());
+ mPreallocatedProcesses.pop_front();
+ 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::sShutdown) {
+ return nullptr;
+ }
+ return PreallocatedProcessManagerImpl::Singleton();
+}
+
+/* static */
+bool PreallocatedProcessManager::Enabled() {
+ if (auto impl = GetPPMImpl()) {
+ return impl->mEnabled;
+ }
+ 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..762351a429
--- /dev/null
+++ b/dom/ipc/PrefsTypes.ipdlh
@@ -0,0 +1,31 @@
+/* -*- 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;
+ 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..6e091494c2
--- /dev/null
+++ b/dom/ipc/ProcessActor.cpp
@@ -0,0 +1,38 @@
+/* -*- 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 "mozJSComponentLoader.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..354bb4c59e
--- /dev/null
+++ b/dom/ipc/ProcessHangMonitor.cpp
@@ -0,0 +1,1460 @@
+/* -*- 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/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/TaskFactory.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/plugins/PluginBridge.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/StaticPrefs_browser.h"
+#include "mozilla/StaticPrefs_dom.h"
+#include "mozilla/Unused.h"
+#include "mozilla/WeakPtr.h"
+
+#include "nsExceptionHandler.h"
+#include "nsFrameLoader.h"
+#include "nsIHangReport.h"
+#include "nsIRemoteTab.h"
+#include "nsNetUtil.h"
+#include "nsQueryObject.h"
+#include "nsPluginHost.h"
+#include "nsThreadUtils.h"
+
+#include "base/task.h"
+#include "base/thread.h"
+
+#ifdef XP_WIN
+// For IsDebuggerPresent()
+# include <windows.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:
+ explicit HangMonitorChild(ProcessHangMonitor* aMonitor);
+ ~HangMonitorChild() override;
+
+ void Bind(Endpoint<PProcessHangMonitorChild>&& aEndpoint);
+
+ typedef ProcessHangMonitor::SlowScriptAction 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 NotifyPluginHang(uint32_t aPluginId);
+ void NotifyPluginHangAsync(uint32_t aPluginId);
+
+ void ClearHang();
+ void ClearHangAsync();
+ void ClearPaintWhileInterruptingJS(const LayersObserverEpoch& aEpoch);
+
+ // 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(
+ const bool& aTerminateGlobal) override;
+ mozilla::ipc::IPCResult RecvBeginStartingDebugger() override;
+ mozilla::ipc::IPCResult RecvEndStartingDebugger() override;
+
+ mozilla::ipc::IPCResult RecvPaintWhileInterruptingJS(
+ const TabId& aTabId, const LayersObserverEpoch& aEpoch) 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;
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ bool InterruptCallback();
+ void Shutdown();
+
+ static HangMonitorChild* Get() { return sInstance; }
+
+ 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;
+ static Maybe<Monitor> sMonitor;
+
+ static Atomic<bool, SequentiallyConsistent> sInitializing;
+
+ private:
+ void ShutdownOnThread();
+
+ static Atomic<HangMonitorChild*, SequentiallyConsistent> sInstance;
+
+ const RefPtr<ProcessHangMonitor> mHangMonitor;
+ Monitor mMonitor;
+
+ // Main thread-only.
+ bool mSentReport;
+
+ // These fields must be accessed with mMonitor held.
+ bool mTerminateScript;
+ bool mTerminateGlobal;
+ bool mStartDebugger;
+ bool mFinishedStartingDebugger;
+ bool mPaintWhileInterruptingJS;
+ TabId mPaintWhileInterruptingJSTab;
+ MOZ_INIT_OUTSIDE_CTOR LayersObserverEpoch mPaintWhileInterruptingJSEpoch;
+ bool mCancelContentJS;
+ TabId mCancelContentJSTab;
+ nsIRemoteTab::NavigationType mCancelContentJSNavigationType;
+ int32_t mCancelContentJSNavigationIndex;
+ mozilla::Maybe<nsCString> mCancelContentJSNavigationURI;
+ int32_t mCancelContentJSEpoch;
+ JSContext* mContext;
+ bool mShutdownDone;
+
+ // 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;
+};
+
+Maybe<Monitor> HangMonitorChild::sMonitor;
+
+Atomic<bool, SequentiallyConsistent> HangMonitorChild::sInitializing;
+
+Atomic<HangMonitorChild*, SequentiallyConsistent> 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 ID of
+ * the plugin which caused the hang as well as the content PID. The ID of
+ * a minidump taken during the hang can also be provided.
+ *
+ * @param aHangData The hang information
+ * @param aDumpId The ID of a minidump taken when the hang occurred
+ */
+ void SetHangData(const HangData& aHangData, const nsAString& aDumpId) {
+ mHangData = aHangData;
+ mDumpId = aDumpId;
+ }
+
+ void ClearHang() {
+ mHangData = HangData();
+ mDumpId.Truncate();
+ }
+
+ private:
+ ~HangMonitoredProcess() = default;
+
+ // Everything here is main thread-only.
+ HangMonitorParent* mActor;
+ ContentParent* mContentParent;
+ HangData mHangData;
+ nsAutoString mDumpId;
+};
+
+class HangMonitorParent : public PProcessHangMonitorParent,
+ public SupportsWeakPtr {
+ public:
+ explicit HangMonitorParent(ProcessHangMonitor* aMonitor);
+ ~HangMonitorParent() override;
+
+ void Bind(Endpoint<PProcessHangMonitorParent>&& aEndpoint);
+
+ mozilla::ipc::IPCResult RecvHangEvidence(const HangData& aHangData) override;
+ mozilla::ipc::IPCResult RecvClearHang() override;
+
+ void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ void SetProcess(HangMonitoredProcess* aProcess) { mProcess = aProcess; }
+
+ void Shutdown();
+
+ void PaintWhileInterruptingJS(dom::BrowserParent* aBrowserParent,
+ const LayersObserverEpoch& aEpoch);
+ void CancelContentJSExecutionIfRunning(
+ dom::BrowserParent* aBrowserParent,
+ nsIRemoteTab::NavigationType aNavigationType,
+ const dom::CancelContentJSOptions& aCancelContentJSOptions);
+
+ void TerminateScript(bool aTerminateGlobal);
+ void BeginStartingDebugger();
+ void EndStartingDebugger();
+ void CleanupPluginHang(uint32_t aPluginId, bool aRemoveFiles);
+
+ /**
+ * Update the dump for the specified plugin. This method is thread-safe and
+ * is used to replace a browser minidump with a full minidump. If aDumpId is
+ * empty this is a no-op.
+ */
+ void UpdateMinidump(uint32_t aPluginId, const nsString& aDumpId);
+
+ void Dispatch(already_AddRefed<nsIRunnable> aRunnable) {
+ mHangMonitor->Dispatch(std::move(aRunnable));
+ }
+ bool IsOnThread() { return mHangMonitor->IsOnThread(); }
+
+ private:
+ bool TakeBrowserMinidump(const PluginHangData& aPhd, nsString& aCrashId);
+
+ void SendHangNotification(const HangData& aHangData,
+ const nsString& aBrowserDumpId, bool aTakeMinidump);
+
+ void ClearHangNotification();
+
+ void PaintWhileInterruptingJSOnThread(TabId aTabId,
+ const LayersObserverEpoch& aEpoch);
+ void CancelContentJSExecutionIfRunningOnThread(
+ TabId aTabId, nsIRemoteTab::NavigationType aNavigationType,
+ int32_t aNavigationIndex, nsIURI* aNavigationURI, int32_t aEpoch);
+
+ void ShutdownOnThread();
+
+ const RefPtr<ProcessHangMonitor> mHangMonitor;
+
+ // This field is only accessed on the hang thread.
+ bool mIPCOpen;
+
+ Monitor mMonitor;
+
+ // Must be accessed with mMonitor held.
+ RefPtr<HangMonitoredProcess> mProcess;
+ bool mShutdownDone;
+ // Map from plugin ID to crash dump ID. Protected by
+ // mBrowserCrashDumpHashLock.
+ nsDataHashtable<nsUint32HashKey, nsString> mBrowserCrashDumpIds;
+ Mutex mBrowserCrashDumpHashLock;
+ mozilla::ipc::TaskFactory<HangMonitorParent> mMainThreadTaskFactory;
+};
+
+} // namespace
+
+/* HangMonitorChild implementation */
+
+HangMonitorChild::HangMonitorChild(ProcessHangMonitor* aMonitor)
+ : mHangMonitor(aMonitor),
+ mMonitor("HangMonitorChild lock"),
+ mSentReport(false),
+ mTerminateScript(false),
+ mTerminateGlobal(false),
+ mStartDebugger(false),
+ mFinishedStartingDebugger(false),
+ mPaintWhileInterruptingJS(false),
+ mCancelContentJS(false),
+ mCancelContentJSNavigationType(nsIRemoteTab::NAVIGATE_BACK),
+ mCancelContentJSNavigationIndex(0),
+ mCancelContentJSEpoch(0),
+ mShutdownDone(false),
+ mIPCOpen(true),
+ mPaintWhileInterruptingJSActive(false) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!sInstance);
+ mContext = danger::GetJSContext();
+
+ BackgroundHangMonitor::RegisterAnnotator(*this);
+
+ MOZ_ASSERT(!sMonitor.isSome());
+ sMonitor.emplace("HangMonitorChild::sMonitor");
+ MonitorAutoLock mal(*sMonitor);
+
+ MOZ_ASSERT(!sInitializing);
+ sInitializing = true;
+}
+
+HangMonitorChild::~HangMonitorChild() {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(sInstance == this);
+ sInstance = nullptr;
+}
+
+bool HangMonitorChild::InterruptCallback() {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ // 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;
+ }
+
+ bool paintWhileInterruptingJS;
+ TabId paintWhileInterruptingJSTab;
+ LayersObserverEpoch paintWhileInterruptingJSEpoch;
+
+ {
+ MonitorAutoLock lock(mMonitor);
+ paintWhileInterruptingJS = mPaintWhileInterruptingJS;
+ paintWhileInterruptingJSTab = mPaintWhileInterruptingJSTab;
+ paintWhileInterruptingJSEpoch = mPaintWhileInterruptingJSEpoch;
+
+ mPaintWhileInterruptingJS = false;
+ }
+
+ if (paintWhileInterruptingJS) {
+ RefPtr<BrowserChild> browserChild =
+ BrowserChild::FindBrowserChild(paintWhileInterruptingJSTab);
+ if (browserChild) {
+ js::AutoAssertNoContentJS nojs(mContext);
+ browserChild->PaintWhileInterruptingJS(paintWhileInterruptingJSEpoch);
+ }
+ }
+
+ // 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::RootedObject 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()) {
+ if (Document* topLevelDoc = doc->GetTopLevelContentDocument()) {
+ topLevelDoc->DisallowBFCaching();
+ }
+ }
+
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void HangMonitorChild::AnnotateHang(BackgroundHangAnnotations& aAnnotations) {
+ if (mPaintWhileInterruptingJSActive) {
+ aAnnotations.AddAnnotation(u"PaintWhileInterruptingJS"_ns, true);
+ }
+}
+
+void HangMonitorChild::Shutdown() {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ BackgroundHangMonitor::UnregisterAnnotator(*this);
+
+ MonitorAutoLock lock(mMonitor);
+ while (!mShutdownDone) {
+ mMonitor.Wait();
+ }
+}
+
+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(
+ const bool& aTerminateGlobal) {
+ MOZ_RELEASE_ASSERT(IsOnThread());
+
+ MonitorAutoLock lock(mMonitor);
+ if (aTerminateGlobal) {
+ mTerminateGlobal = true;
+ } else {
+ mTerminateScript = true;
+ }
+ 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, const LayersObserverEpoch& aEpoch) {
+ MOZ_RELEASE_ASSERT(IsOnThread());
+
+ {
+ MonitorAutoLock lock(mMonitor);
+ MaybeStartPaintWhileInterruptingJS();
+ mPaintWhileInterruptingJS = true;
+ mPaintWhileInterruptingJSTab = aTabId;
+ mPaintWhileInterruptingJSEpoch = aEpoch;
+ }
+
+ JS_RequestInterruptCallback(mContext);
+
+ return IPC_OK();
+}
+
+void HangMonitorChild::MaybeStartPaintWhileInterruptingJS() {
+ mPaintWhileInterruptingJSActive = true;
+}
+
+void HangMonitorChild::ClearPaintWhileInterruptingJS(
+ const LayersObserverEpoch& aEpoch) {
+ 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();
+}
+
+void HangMonitorChild::Bind(Endpoint<PProcessHangMonitorChild>&& aEndpoint) {
+ MOZ_RELEASE_ASSERT(IsOnThread());
+
+ MonitorAutoLock mal(*sMonitor);
+
+ MOZ_ASSERT(!sInstance);
+ sInstance = this;
+
+ DebugOnly<bool> ok = aEndpoint.Bind(this);
+ MOZ_ASSERT(ok);
+
+ sInitializing = false;
+ mal.Notify();
+}
+
+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 (mTerminateGlobal) {
+ mTerminateGlobal = false;
+ return SlowScriptAction::TerminateGlobal;
+ }
+
+ 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::NotifyPluginHang(uint32_t aPluginId) {
+ // main thread in the child
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ mSentReport = true;
+
+ // bounce to background thread
+ Dispatch(NewNonOwningRunnableMethod<uint32_t>(
+ "HangMonitorChild::NotifyPluginHangAsync", this,
+ &HangMonitorChild::NotifyPluginHangAsync, aPluginId));
+}
+
+void HangMonitorChild::NotifyPluginHangAsync(uint32_t aPluginId) {
+ MOZ_RELEASE_ASSERT(IsOnThread());
+
+ // bounce back to parent on background thread
+ if (mIPCOpen) {
+ Unused << SendHangEvidence(
+ PluginHangData(aPluginId, base::GetCurrentProcId()));
+ }
+}
+
+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;
+ mTerminateGlobal = 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),
+ mBrowserCrashDumpHashLock("mBrowserCrashDumpIds lock"),
+ mMainThreadTaskFactory(this) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+}
+
+HangMonitorParent::~HangMonitorParent() {
+ MutexAutoLock lock(mBrowserCrashDumpHashLock);
+
+ for (auto iter = mBrowserCrashDumpIds.Iter(); !iter.Done(); iter.Next()) {
+ nsString crashId = iter.UserData();
+ if (!crashId.IsEmpty()) {
+ CrashReporter::DeleteMinidumpFilesForID(crashId);
+ }
+ }
+}
+
+void HangMonitorParent::Shutdown() {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ MonitorAutoLock lock(mMonitor);
+
+ if (mProcess) {
+ mProcess->Clear();
+ mProcess = nullptr;
+ }
+
+ Dispatch(NewNonOwningRunnableMethod("HangMonitorParent::ShutdownOnThread",
+ this,
+ &HangMonitorParent::ShutdownOnThread));
+
+ 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, const LayersObserverEpoch& aEpoch) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ if (StaticPrefs::browser_tabs_remote_force_paint()) {
+ TabId id = aTab->GetTabId();
+ Dispatch(NewNonOwningRunnableMethod<TabId, LayersObserverEpoch>(
+ "HangMonitorParent::PaintWhileInterruptingJSOnThread", this,
+ &HangMonitorParent::PaintWhileInterruptingJSOnThread, id, aEpoch));
+ }
+}
+
+void HangMonitorParent::PaintWhileInterruptingJSOnThread(
+ TabId aTabId, const LayersObserverEpoch& aEpoch) {
+ MOZ_RELEASE_ASSERT(IsOnThread());
+
+ if (mIPCOpen) {
+ Unused << SendPaintWhileInterruptingJS(aTabId, aEpoch);
+ }
+}
+
+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::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 HangData& aHangData,
+ const nsString& aBrowserDumpId,
+ bool aTakeMinidump) {
+ // chrome process, main thread
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ nsString dumpId;
+ if ((aHangData.type() == HangData::TPluginHangData) && aTakeMinidump) {
+ // We've been handed a partial minidump; complete it with plugin and
+ // content process dumps.
+ const PluginHangData& phd = aHangData.get_PluginHangData();
+
+ plugins::TakeFullMinidump(phd.pluginId(), phd.contentProcessId(),
+ aBrowserDumpId, dumpId);
+ UpdateMinidump(phd.pluginId(), dumpId);
+ } else {
+ // We already have a full minidump; go ahead and use it.
+ dumpId = aBrowserDumpId;
+ }
+
+ mProcess->SetHangData(aHangData, 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();
+}
+
+// Take a minidump of the browser process if one wasn't already taken for the
+// plugin that caused the hang. Return false if a dump was already available or
+// true if new one has been taken.
+bool HangMonitorParent::TakeBrowserMinidump(const PluginHangData& aPhd,
+ nsString& aCrashId) {
+ MutexAutoLock lock(mBrowserCrashDumpHashLock);
+ if (!mBrowserCrashDumpIds.Get(aPhd.pluginId(), &aCrashId)) {
+ nsCOMPtr<nsIFile> browserDump;
+ if (CrashReporter::TakeMinidump(getter_AddRefs(browserDump), true)) {
+ if (!CrashReporter::GetIDFromMinidump(browserDump, aCrashId) ||
+ aCrashId.IsEmpty()) {
+ browserDump->Remove(false);
+ NS_WARNING(
+ "Failed to generate timely browser stack, "
+ "this is bad for plugin hang analysis!");
+ } else {
+ mBrowserCrashDumpIds.Put(aPhd.pluginId(), aCrashId);
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+mozilla::ipc::IPCResult HangMonitorParent::RecvHangEvidence(
+ const HangData& aHangData) {
+ // 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;
+ bool takeMinidump = false;
+ if (aHangData.type() == HangData::TPluginHangData) {
+ takeMinidump = TakeBrowserMinidump(aHangData.get_PluginHangData(), crashId);
+ }
+
+ mHangMonitor->InitiateCPOWTimeout();
+
+ MonitorAutoLock lock(mMonitor);
+
+ NS_DispatchToMainThread(mMainThreadTaskFactory.NewRunnableMethod(
+ &HangMonitorParent::SendHangNotification, aHangData, crashId,
+ takeMinidump));
+
+ 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(bool aTerminateGlobal) {
+ MOZ_RELEASE_ASSERT(IsOnThread());
+
+ if (mIPCOpen) {
+ Unused << SendTerminateScript(aTerminateGlobal);
+ }
+}
+
+void HangMonitorParent::BeginStartingDebugger() {
+ MOZ_RELEASE_ASSERT(IsOnThread());
+
+ if (mIPCOpen) {
+ Unused << SendBeginStartingDebugger();
+ }
+}
+
+void HangMonitorParent::EndStartingDebugger() {
+ MOZ_RELEASE_ASSERT(IsOnThread());
+
+ if (mIPCOpen) {
+ Unused << SendEndStartingDebugger();
+ }
+}
+
+void HangMonitorParent::CleanupPluginHang(uint32_t aPluginId,
+ bool aRemoveFiles) {
+ MutexAutoLock lock(mBrowserCrashDumpHashLock);
+ nsAutoString crashId;
+ if (!mBrowserCrashDumpIds.Get(aPluginId, &crashId)) {
+ return;
+ }
+ mBrowserCrashDumpIds.Remove(aPluginId);
+
+ if (aRemoveFiles && !crashId.IsEmpty()) {
+ CrashReporter::DeleteMinidumpFilesForID(crashId);
+ }
+}
+
+void HangMonitorParent::UpdateMinidump(uint32_t aPluginId,
+ const nsString& aDumpId) {
+ if (aDumpId.IsEmpty()) {
+ return;
+ }
+
+ MutexAutoLock lock(mBrowserCrashDumpHashLock);
+ mBrowserCrashDumpIds.Put(aPluginId, aDumpId);
+}
+
+/* HangMonitoredProcess implementation */
+
+NS_IMPL_ISUPPORTS(HangMonitoredProcess, nsIHangReport)
+
+NS_IMETHODIMP
+HangMonitoredProcess::GetHangType(uint32_t* aHangType) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ switch (mHangData.type()) {
+ case HangData::TSlowScriptData:
+ *aHangType = SLOW_SCRIPT;
+ break;
+ case HangData::TPluginHangData:
+ *aHangType = PLUGIN_HANG;
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unexpected HangData type");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+HangMonitoredProcess::GetHangDuration(double* aHangDuration) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ if (mHangData.type() != HangData::TSlowScriptData) {
+ *aHangDuration = -1;
+ } else {
+ *aHangDuration = mHangData.get_SlowScriptData().duration();
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+HangMonitoredProcess::GetScriptBrowser(Element** aBrowser) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ if (mHangData.type() != HangData::TSlowScriptData) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ TabId tabId = mHangData.get_SlowScriptData().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());
+ if (mHangData.type() != HangData::TSlowScriptData) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ aFileName = mHangData.get_SlowScriptData().filename();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+HangMonitoredProcess::GetAddonId(nsAString& aAddonId) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ if (mHangData.type() != HangData::TSlowScriptData) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ aAddonId = mHangData.get_SlowScriptData().addonId();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+HangMonitoredProcess::GetPluginName(nsACString& aPluginName) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ if (mHangData.type() != HangData::TPluginHangData) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ uint32_t id = mHangData.get_PluginHangData().pluginId();
+
+ RefPtr<nsPluginHost> host = nsPluginHost::GetInst();
+ nsPluginTag* tag = host->PluginWithId(id);
+ if (!tag) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ aPluginName = tag->Name();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+HangMonitoredProcess::TerminateScript() {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ if (mHangData.type() != HangData::TSlowScriptData) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (!mActor) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ ProcessHangMonitor::Get()->Dispatch(NewNonOwningRunnableMethod<bool>(
+ "HangMonitorParent::TerminateScript", mActor,
+ &HangMonitorParent::TerminateScript, false));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+HangMonitoredProcess::TerminateGlobal() {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ if (mHangData.type() != HangData::TSlowScriptData) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (!mActor) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ ProcessHangMonitor::Get()->Dispatch(NewNonOwningRunnableMethod<bool>(
+ "HangMonitorParent::TerminateScript", mActor,
+ &HangMonitorParent::TerminateScript, true));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+HangMonitoredProcess::BeginStartingDebugger() {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ if (mHangData.type() != HangData::TSlowScriptData) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ 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 (mHangData.type() != HangData::TSlowScriptData) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ if (!mActor) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ ProcessHangMonitor::Get()->Dispatch(NewNonOwningRunnableMethod(
+ "HangMonitorParent::EndStartingDebugger", mActor,
+ &HangMonitorParent::EndStartingDebugger));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+HangMonitoredProcess::TerminatePlugin() {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ if (mHangData.type() != HangData::TPluginHangData) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ // Use the multi-process crash report generated earlier.
+ uint32_t id = mHangData.get_PluginHangData().pluginId();
+ base::ProcessId contentPid =
+ mHangData.get_PluginHangData().contentProcessId();
+ plugins::TerminatePlugin(id, contentPid, "HangMonitor"_ns, mDumpId);
+
+ if (mActor) {
+ mActor->CleanupPluginHang(id, false);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+HangMonitoredProcess::IsReportForBrowser(nsFrameLoader* aFrameLoader,
+ bool* aResult) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ if (!mActor) {
+ *aResult = false;
+ return NS_OK;
+ }
+
+ NS_ENSURE_STATE(aFrameLoader);
+
+ BrowserParent* tp = BrowserParent::GetFrom(aFrameLoader);
+ if (!tp) {
+ *aResult = false;
+ return NS_OK;
+ }
+
+ *aResult = mContentParent == tp->Manager();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+HangMonitoredProcess::UserCanceled() {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ if (mHangData.type() != HangData::TPluginHangData) {
+ return NS_OK;
+ }
+
+ if (mActor) {
+ uint32_t id = mHangData.get_PluginHangData().pluginId();
+ mActor->CleanupPluginHang(id, true);
+ }
+ 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) {
+ 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;
+ }
+}
+
+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) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ if (!strcmp(aTopic, "xpcom-shutdown")) {
+ if (HangMonitorChild::sMonitor) {
+ MonitorAutoLock mal(*HangMonitorChild::sMonitor);
+ if (HangMonitorChild::sInitializing) {
+ mal.Wait();
+ }
+
+ if (HangMonitorChild* child = HangMonitorChild::Get()) {
+ child->Shutdown();
+ delete child;
+ }
+ }
+ HangMonitorChild::sMonitor.reset();
+
+ 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) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ return HangMonitorChild::Get()->NotifySlowScript(aBrowserChild, aFileName,
+ aAddonId, aDuration);
+}
+
+bool ProcessHangMonitor::IsDebuggerStartupComplete() {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ 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;
+}
+
+void ProcessHangMonitor::NotifyPluginHang(uint32_t aPluginId) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ return HangMonitorChild::Get()->NotifyPluginHang(aPluginId);
+}
+
+static PProcessHangMonitorParent* CreateHangMonitorParent(
+ ContentParent* aContentParent,
+ Endpoint<PProcessHangMonitorParent>&& aEndpoint) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ ProcessHangMonitor* monitor = ProcessHangMonitor::GetOrCreate();
+ auto* 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;
+}
+
+void mozilla::CreateHangMonitorChild(
+ Endpoint<PProcessHangMonitorChild>&& aEndpoint) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ JSContext* cx = danger::GetJSContext();
+ JS_AddInterruptCallback(cx, InterruptCallback);
+
+ ProcessHangMonitor* monitor = ProcessHangMonitor::GetOrCreate();
+ auto* child = new HangMonitorChild(monitor);
+
+ monitor->Dispatch(
+ NewNonOwningRunnableMethod<Endpoint<PProcessHangMonitorChild>&&>(
+ "HangMonitorChild::Bind", child, &HangMonitorChild::Bind,
+ std::move(aEndpoint)));
+}
+
+void ProcessHangMonitor::Dispatch(already_AddRefed<nsIRunnable> aRunnable) {
+ mThread->Dispatch(std::move(aRunnable), nsIEventTarget::NS_DISPATCH_NORMAL);
+}
+
+bool ProcessHangMonitor::IsOnThread() {
+ bool on;
+ return NS_SUCCEEDED(mThread->IsOnCurrentThread(&on)) && on;
+}
+
+/* static */
+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(
+ base::GetCurrentProcId(), aContentParent->OtherPid(), &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();
+ delete parent;
+}
+
+/* static */
+void ProcessHangMonitor::ClearHang() {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (HangMonitorChild* child = HangMonitorChild::Get()) {
+ child->ClearHang();
+ }
+}
+
+/* static */
+void ProcessHangMonitor::PaintWhileInterruptingJS(
+ PProcessHangMonitorParent* aParent, dom::BrowserParent* aBrowserParent,
+ const layers::LayersObserverEpoch& aEpoch) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ auto parent = static_cast<HangMonitorParent*>(aParent);
+ parent->PaintWhileInterruptingJS(aBrowserParent, aEpoch);
+}
+
+/* static */
+void ProcessHangMonitor::ClearPaintWhileInterruptingJS(
+ const layers::LayersObserverEpoch& aEpoch) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ MOZ_RELEASE_ASSERT(XRE_IsContentProcess());
+
+ if (HangMonitorChild* child = HangMonitorChild::Get()) {
+ child->ClearPaintWhileInterruptingJS(aEpoch);
+ }
+}
+
+/* static */
+void ProcessHangMonitor::MaybeStartPaintWhileInterruptingJS() {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ 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) {
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ auto parent = static_cast<HangMonitorParent*>(aParent);
+ parent->CancelContentJSExecutionIfRunning(aBrowserParent, aNavigationType,
+ aCancelContentJSOptions);
+}
diff --git a/dom/ipc/ProcessHangMonitor.h b/dom/ipc/ProcessHangMonitor.h
new file mode 100644
index 0000000000..d66d1efaed
--- /dev/null
+++ b/dom/ipc/ProcessHangMonitor.h
@@ -0,0 +1,96 @@
+/* -*- 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 "nsStringFwd.h"
+
+class nsIRunnable;
+class nsIBrowserChild;
+class nsIThread;
+
+namespace mozilla {
+
+namespace dom {
+class ContentParent;
+class BrowserParent;
+struct CancelContentJSOptions;
+} // namespace dom
+
+namespace layers {
+struct LayersObserverEpoch;
+} // namespace layers
+
+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 PProcessHangMonitorParent* AddProcess(
+ dom::ContentParent* aContentParent);
+ static void RemoveProcess(PProcessHangMonitorParent* aParent);
+
+ static void ClearHang();
+
+ static void PaintWhileInterruptingJS(
+ PProcessHangMonitorParent* aParent, dom::BrowserParent* aTab,
+ const layers::LayersObserverEpoch& aEpoch);
+ static void ClearPaintWhileInterruptingJS(
+ const layers::LayersObserverEpoch& aEpoch);
+ static void MaybeStartPaintWhileInterruptingJS();
+
+ static void CancelContentJSExecutionIfRunning(
+ PProcessHangMonitorParent* aParent, dom::BrowserParent* aTab,
+ nsIRemoteTab::NavigationType aNavigationType,
+ const dom::CancelContentJSOptions& aCancelContentJSOptions);
+
+ enum SlowScriptAction {
+ Continue,
+ Terminate,
+ StartDebugger,
+ TerminateGlobal,
+ };
+ SlowScriptAction NotifySlowScript(nsIBrowserChild* aBrowserChild,
+ const char* aFileName,
+ const nsString& aAddonId,
+ const double aDuration);
+
+ void NotifyPluginHang(uint32_t aPluginId);
+
+ bool IsDebuggerStartupComplete();
+
+ void InitiateCPOWTimeout();
+ bool ShouldTimeOutCPOWs();
+
+ void 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/ProcessPriorityManager.cpp b/dom/ipc/ProcessPriorityManager.cpp
new file mode 100644
index 0000000000..060f17968a
--- /dev/null
+++ b/dom/ipc/ProcessPriorityManager.cpp
@@ -0,0 +1,978 @@
+/* -*- 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/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/Services.h"
+#include "mozilla/StaticPrefs_dom.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 "nsTHashtable.h"
+#include "nsQueryObject.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 {
+
+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 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 = ""_ns);
+
+ /**
+ * This must be called by a ParticularProcessPriorityManager when it changes
+ * its priority.
+ */
+ void NotifyProcessPriorityChanged(
+ ParticularProcessPriorityManager* aParticularManager,
+ hal::ProcessPriority aOldPriority);
+
+ void TabActivityChanged(BrowserParent* aBrowserParent, bool aIsActive);
+
+ 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 ObserveContentParentCreated(nsISupports* aContentParent);
+ void ObserveContentParentDestroyed(nsISupports* aSubject);
+
+ nsDataHashtable<nsUint64HashKey, RefPtr<ParticularProcessPriorityManager> >
+ mParticularManagers;
+
+ /** Contains the PIDs of child processes holding high-priority wakelocks */
+ nsTHashtable<nsUint64HashKey> 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 nsIObserver,
+ public nsITimerCallback,
+ public nsINamed,
+ public nsSupportsWeakReference {
+ ~ParticularProcessPriorityManager();
+
+ public:
+ explicit ParticularProcessPriorityManager(ContentParent* aContentParent);
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+ 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();
+
+ void OnRemoteBrowserFrameShown(nsISupports* aSubject);
+ void OnBrowserParentDestroyed(nsISupports* aSubject);
+
+ 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 TabActivityChanged(BrowserParent* aBrowserParent, bool aIsActive);
+
+ void ShutDown();
+
+ NS_IMETHOD GetName(nsACString& aName) override {
+ aName.AssignLiteral("ParticularProcessPriorityManager");
+ return NS_OK;
+ }
+
+ private:
+ void FireTestOnlyObserverNotification(const char* aTopic,
+ const nsACString& aData = ""_ns);
+
+ void FireTestOnlyObserverNotification(const char* aTopic,
+ const char* aData = nullptr);
+
+ 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 active TabId for this process.
+ nsTHashtable<nsUint64HashKey> mActiveBrowserParents;
+};
+
+/* 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() &&
+ hal::SetProcessPrioritySupported() &&
+ !StaticPrefs::dom_ipc_tabs_disabled();
+}
+
+/* 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;
+ }
+
+ // If IPC tabs aren't enabled at startup, don't bother with any of this.
+ if (!PrefsEnabled()) {
+ LOG("InitProcessPriorityManager bailing due to prefs.");
+
+ // Run StaticInit() again if the prefs change. 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");
+ Preferences::RegisterCallback(PrefChangedCallback,
+ "dom.ipc.tabs.disabled");
+ }
+ return;
+ }
+
+ 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.
+ hal::SetProcessPriority(getpid(), PROCESS_PRIORITY_PARENT_PROCESS);
+
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ if (os) {
+ os->AddObserver(this, "ipc:content-created", /* ownsWeak */ true);
+ 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-created")) {
+ ObserveContentParentCreated(aSubject);
+ } else if (topic.EqualsLiteral("ipc:content-shutdown")) {
+ ObserveContentParentDestroyed(aSubject);
+ } else {
+ MOZ_ASSERT(false);
+ }
+
+ return NS_OK;
+}
+
+already_AddRefed<ParticularProcessPriorityManager>
+ProcessPriorityManagerImpl::GetParticularProcessPriorityManager(
+ ContentParent* aContentParent) {
+ uint64_t cpId = aContentParent->ChildID();
+ auto entry = mParticularManagers.LookupForAdd(cpId);
+ RefPtr<ParticularProcessPriorityManager> pppm =
+ entry.OrInsert([aContentParent]() {
+ return new ParticularProcessPriorityManager(aContentParent);
+ });
+
+ if (!entry) {
+ // We created a new entry.
+ pppm->Init();
+ FireTestOnlyObserverNotification("process-created",
+ nsPrintfCString("%" PRIu64, cpId));
+ }
+
+ return pppm.forget();
+}
+
+void ProcessPriorityManagerImpl::SetProcessPriority(
+ ContentParent* aContentParent, ProcessPriority aPriority) {
+ MOZ_ASSERT(aContentParent);
+ RefPtr<ParticularProcessPriorityManager> pppm =
+ GetParticularProcessPriorityManager(aContentParent);
+ if (pppm) {
+ pppm->SetPriorityNow(aPriority);
+ }
+}
+
+void ProcessPriorityManagerImpl::ObserveContentParentCreated(
+ nsISupports* aContentParent) {
+ // Do nothing; it's sufficient to get the PPPM. But assign to nsRefPtr so we
+ // don't leak the already_AddRefed object.
+ RefPtr<ContentParent> cp = do_QueryObject(aContentParent);
+ RefPtr<ParticularProcessPriorityManager> pppm =
+ GetParticularProcessPriorityManager(cp);
+}
+
+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.RemoveEntry(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.PutEntry(aParticularManager->ChildID());
+ } else if (newPriority < PROCESS_PRIORITY_FOREGROUND_HIGH &&
+ aOldPriority >= PROCESS_PRIORITY_FOREGROUND_HIGH) {
+ mHighPriorityChildIDs.RemoveEntry(aParticularManager->ChildID());
+ }
+}
+
+void ProcessPriorityManagerImpl::TabActivityChanged(
+ BrowserParent* aBrowserParent, bool aIsActive) {
+ RefPtr<ParticularProcessPriorityManager> pppm =
+ GetParticularProcessPriorityManager(aBrowserParent->Manager());
+ if (!pppm) {
+ return;
+ }
+
+ Telemetry::ScalarAdd(
+ Telemetry::ScalarID::DOM_CONTENTPROCESS_OS_PRIORITY_CHANGE_CONSIDERED, 1);
+
+ pppm->TabActivityChanged(aBrowserParent, aIsActive);
+}
+
+NS_IMPL_ISUPPORTS(ParticularProcessPriorityManager, nsIObserver,
+ 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());
+ LOGP("Creating ParticularProcessPriorityManager.");
+}
+
+void ParticularProcessPriorityManager::Init() {
+ RegisterWakeLockObserver(this);
+
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ if (os) {
+ os->AddObserver(this, "remote-browser-shown", /* ownsWeak */ true);
+ os->AddObserver(this, "ipc:browser-destroyed", /* ownsWeak */ true);
+ }
+
+ // 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.");
+
+ // 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);
+ }
+}
+
+/* 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();
+ }
+ }
+}
+
+NS_IMETHODIMP
+ParticularProcessPriorityManager::Observe(nsISupports* aSubject,
+ const char* aTopic,
+ const char16_t* aData) {
+ if (!mContentParent) {
+ // We've been shut down.
+ return NS_OK;
+ }
+
+ nsDependentCString topic(aTopic);
+
+ if (topic.EqualsLiteral("remote-browser-shown")) {
+ OnRemoteBrowserFrameShown(aSubject);
+ } else if (topic.EqualsLiteral("ipc:browser-destroyed")) {
+ OnBrowserParentDestroyed(aSubject);
+ } else {
+ MOZ_ASSERT(false);
+ }
+
+ return NS_OK;
+}
+
+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::OnRemoteBrowserFrameShown(
+ nsISupports* aSubject) {
+ RefPtr<nsFrameLoader> fl = do_QueryObject(aSubject);
+ NS_ENSURE_TRUE_VOID(fl);
+
+ BrowserParent* tp = BrowserParent::GetFrom(fl);
+ NS_ENSURE_TRUE_VOID(tp);
+
+ MOZ_ASSERT(XRE_IsParentProcess());
+ if (tp->Manager() != mContentParent) {
+ return;
+ }
+
+ // Ignore notifications that aren't from a Browser
+ if (fl->OwnerIsMozBrowserFrame()) {
+ ResetPriority();
+ }
+
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ if (os) {
+ os->RemoveObserver(this, "remote-browser-shown");
+ }
+}
+
+void ParticularProcessPriorityManager::OnBrowserParentDestroyed(
+ nsISupports* aSubject) {
+ nsCOMPtr<nsIRemoteTab> remoteTab = do_QueryInterface(aSubject);
+ NS_ENSURE_TRUE_VOID(remoteTab);
+ BrowserHost* browserHost = BrowserHost::GetFrom(remoteTab.get());
+
+ MOZ_ASSERT(XRE_IsParentProcess());
+ if (browserHost->GetContentParent() &&
+ browserHost->GetContentParent() != mContentParent) {
+ return;
+ }
+
+ mActiveBrowserParents.RemoveEntry(browserHost->GetTabId());
+
+ ResetPriority();
+}
+
+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 (!mActiveBrowserParents.IsEmpty() ||
+ mContentParent->GetRemoteType() == EXTENSION_REMOTE_TYPE ||
+ mHoldsPlayingAudioWakeLock) {
+ return PROCESS_PRIORITY_FOREGROUND;
+ }
+
+ if (mHoldsCPUWakeLock || mHoldsHighPriorityWakeLock ||
+ mHoldsPlayingVideoWakeLock) {
+ return PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE;
+ }
+
+ return PROCESS_PRIORITY_BACKGROUND;
+}
+
+void ParticularProcessPriorityManager::SetPriorityNow(
+ ProcessPriority aPriority) {
+ if (aPriority == PROCESS_PRIORITY_UNKNOWN) {
+ MOZ_ASSERT(false);
+ return;
+ }
+
+ if (!ProcessPriorityManagerImpl::PrefsEnabled() || !mContentParent ||
+ mPriority == aPriority) {
+ return;
+ }
+
+ if (mPriority == aPriority) {
+ hal::SetProcessPriority(Pid(), mPriority);
+ return;
+ }
+
+ LOGP("Changing priority from %s to %s.", ProcessPriorityToString(mPriority),
+ 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);
+ }
+
+ hal::SetProcessPriority(Pid(), mPriority);
+
+ if (oldPriority != mPriority) {
+ ProcessPriorityManagerImpl::GetSingleton()->NotifyProcessPriorityChanged(
+ this, oldPriority);
+
+ Unused << mContentParent->SendNotifyProcessPriorityChanged(mPriority);
+ }
+
+ FireTestOnlyObserverNotification("process-priority-set",
+ ProcessPriorityToString(mPriority));
+}
+
+void ParticularProcessPriorityManager::TabActivityChanged(
+ BrowserParent* aBrowserParent, bool aIsActive) {
+ MOZ_ASSERT(aBrowserParent);
+
+ if (!aIsActive) {
+ mActiveBrowserParents.RemoveEntry(aBrowserParent->GetTabId());
+ } else {
+ mActiveBrowserParents.PutEntry(aBrowserParent->GetTabId());
+ }
+
+ ResetPriority();
+}
+
+void ParticularProcessPriorityManager::ShutDown() {
+ MOZ_ASSERT(mContentParent);
+
+ LOGP("shutdown for %p (mContentParent %p)", this, mContentParent);
+
+ UnregisterWakeLockObserver(this);
+
+ if (mResetPriorityTimer) {
+ mResetPriorityTimer->Cancel();
+ mResetPriorityTimer = nullptr;
+ }
+
+ mContentParent = nullptr;
+}
+
+void ProcessPriorityManagerImpl::FireTestOnlyObserverNotification(
+ const char* aTopic, const nsACString& aData /* = ""_ns */) {
+ 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 /* = nullptr */) {
+ if (!ProcessPriorityManagerImpl::TestMode()) {
+ return;
+ }
+
+ nsAutoCString data;
+ if (aData) {
+ data.AppendASCII(aData);
+ }
+
+ FireTestOnlyObserverNotification(aTopic, data);
+}
+
+void ParticularProcessPriorityManager::FireTestOnlyObserverNotification(
+ const char* aTopic, const nsACString& aData /* = ""_ns */) {
+ if (!ProcessPriorityManagerImpl::TestMode()) {
+ return;
+ }
+
+ nsAutoCString data(nsPrintfCString("%" PRIu64, ChildID()));
+ if (!aData.IsEmpty()) {
+ data.Append(':');
+ data.Append(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::TabActivityChanged(BrowserParent* aBrowserParent,
+ bool aIsActive) {
+ MOZ_ASSERT(aBrowserParent);
+
+ ProcessPriorityManagerImpl* singleton =
+ ProcessPriorityManagerImpl::GetSingleton();
+ if (!singleton) {
+ return;
+ }
+
+ singleton->TabActivityChanged(aBrowserParent, aIsActive);
+}
+
+} // namespace mozilla
diff --git a/dom/ipc/ProcessPriorityManager.h b/dom/ipc/ProcessPriorityManager.h
new file mode 100644
index 0000000000..08525cf3c3
--- /dev/null
+++ b/dom/ipc/ProcessPriorityManager.h
@@ -0,0 +1,84 @@
+/* -*- 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"
+
+namespace mozilla {
+namespace dom {
+class ContentParent;
+class BrowserParent;
+} // 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();
+
+ static void TabActivityChanged(dom::BrowserParent* aBrowserParent,
+ bool aIsActive);
+
+ 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..a43aa7168f
--- /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(Message* aMsg, 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(aMsg, aActor, variant);
+}
+
+bool IPDLParamTraits<nsIVariant*>::Read(const Message* aMsg,
+ PickleIterator* aIter,
+ IProtocol* aActor,
+ RefPtr<nsIVariant>* aResult) {
+ IDPLVariant value;
+ if (!ReadIPDLParam(aMsg, aIter, 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(Message* aMsg, 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(aMsg, aActor, bag);
+}
+
+bool IPDLParamTraits<nsIPropertyBag2*>::Read(const Message* aMsg,
+ PickleIterator* aIter,
+ IProtocol* aActor,
+ RefPtr<nsIPropertyBag2>* aResult) {
+ nsTArray<IPDLProperty> bag;
+ if (!ReadIPDLParam(aMsg, aIter, 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..a85f87b16f
--- /dev/null
+++ b/dom/ipc/PropertyBagUtils.h
@@ -0,0 +1,39 @@
+/* -*- 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 {
+namespace 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::Message* aMsg, IProtocol* aActor, nsIVariant* aParam);
+ static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+ IProtocol* aActor, RefPtr<nsIVariant>* aResult);
+};
+
+template <>
+struct IPDLParamTraits<nsIPropertyBag2*> {
+ static void Write(IPC::Message* aMsg, IProtocol* aActor,
+ nsIPropertyBag2* aParam);
+ static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+ IProtocol* aActor, RefPtr<nsIPropertyBag2>* aResult);
+};
+
+} // namespace ipc
+} // namespace mozilla
+
+#endif // mozilla_ipc_URIUtils_h
diff --git a/dom/ipc/RefMessageBodyService.cpp b/dom/ipc/RefMessageBodyService.cpp
new file mode 100644
index 0000000000..53d3a9cecd
--- /dev/null
+++ b/dom/ipc/RefMessageBodyService.cpp
@@ -0,0 +1,159 @@
+/* -*- 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 "nsContentUtils.h"
+#include "nsDebug.h"
+
+namespace mozilla::dom {
+
+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();
+ }
+ return sService;
+}
+
+RefMessageBodyService::RefMessageBodyService() {
+ MOZ_DIAGNOSTIC_ASSERT(sService == nullptr);
+}
+
+RefMessageBodyService::~RefMessageBodyService() {
+ 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 = nsContentUtils::GenerateUUIDInPlace(uuid);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return nsID();
+ }
+
+ StaticMutexAutoLock lock(sRefMessageBodyServiceMutex);
+ GetOrCreateInternal(lock)->mMessages.Put(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.ConstIter(); !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..6b37cf3fb3
--- /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;
+
+ 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:
+ RefMessageBodyService();
+ ~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..a1d54d29d8
--- /dev/null
+++ b/dom/ipc/ReferrerInfoUtils.cpp
@@ -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/. */
+
+#include "mozilla/dom/ReferrerInfoUtils.h"
+
+#include "ipc/IPCMessageUtilsSpecializations.h"
+#include "nsSerializationHelper.h"
+#include "nsString.h"
+
+namespace IPC {
+
+void ParamTraits<nsIReferrerInfo*>::Write(Message* aMsg,
+ nsIReferrerInfo* aParam) {
+ bool isNull = !aParam;
+ WriteParam(aMsg, 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(aMsg, infoString);
+}
+
+bool ParamTraits<nsIReferrerInfo*>::Read(const Message* aMsg,
+ PickleIterator* aIter,
+ RefPtr<nsIReferrerInfo>* aResult) {
+ bool isNull;
+ if (!ReadParam(aMsg, aIter, &isNull)) {
+ return false;
+ }
+ if (isNull) {
+ *aResult = nullptr;
+ return true;
+ }
+ nsAutoCString infoString;
+ if (!ReadParam(aMsg, aIter, &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..803a308c7e
--- /dev/null
+++ b/dom/ipc/ReferrerInfoUtils.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_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(Message* aMsg, nsIReferrerInfo* aParam);
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ 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..41a890c737
--- /dev/null
+++ b/dom/ipc/RemoteBrowser.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_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 {
+
+namespace 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:
+ typedef mozilla::layers::LayersId 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 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 dom
+} // namespace mozilla
+
+#endif // mozilla_dom_ipc_RemoteBrowser_h
diff --git a/dom/ipc/RemoteType.h b/dom/ipc/RemoteType.h
new file mode 100644
index 0000000000..b79c97f8c9
--- /dev/null
+++ b/dom/ipc/RemoteType.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 mozilla_dom_RemoteType_h
+#define mozilla_dom_RemoteType_h
+
+#include "nsString.h"
+#include "nsReadableUtils.h"
+
+// These must match the similar ones in E10SUtils.jsm and ProcInfo.h.
+// 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 DEFAULT_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
+
+// These must start with the DEFAULT_REMOTE_TYPE above.
+#define FISSION_WEB_REMOTE_TYPE "webIsolated"_ns
+#define WITH_COOP_COEP_REMOTE_TYPE_PREFIX "webCOOP+COEP="_ns
+#define LARGE_ALLOCATION_REMOTE_TYPE "webLargeAllocation"_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/RemoteWebProgress.cpp b/dom/ipc/RemoteWebProgress.cpp
new file mode 100644
index 0000000000..0bba9a3aa9
--- /dev/null
+++ b/dom/ipc/RemoteWebProgress.cpp
@@ -0,0 +1,58 @@
+/* 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 "RemoteWebProgress.h"
+
+namespace mozilla::dom {
+
+NS_IMPL_ADDREF(RemoteWebProgress)
+NS_IMPL_RELEASE(RemoteWebProgress)
+
+NS_INTERFACE_MAP_BEGIN(RemoteWebProgress)
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+ NS_INTERFACE_MAP_ENTRY(nsIWebProgress)
+NS_INTERFACE_MAP_END
+
+NS_IMETHODIMP RemoteWebProgress::AddProgressListener(
+ nsIWebProgressListener* aListener, uint32_t aNotifyMask) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgress::RemoveProgressListener(
+ nsIWebProgressListener* aListener) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgress::GetDOMWindow(mozIDOMWindowProxy** aDOMWindow) {
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP RemoteWebProgress::GetIsTopLevel(bool* aIsTopLevel) {
+ NS_ENSURE_ARG_POINTER(aIsTopLevel);
+ *aIsTopLevel = mIsTopLevel;
+ return NS_OK;
+}
+
+NS_IMETHODIMP RemoteWebProgress::GetIsLoadingDocument(
+ bool* aIsLoadingDocument) {
+ NS_ENSURE_ARG_POINTER(aIsLoadingDocument);
+ *aIsLoadingDocument = mIsLoadingDocument;
+ return NS_OK;
+}
+
+NS_IMETHODIMP RemoteWebProgress::GetLoadType(uint32_t* aLoadType) {
+ NS_ENSURE_ARG_POINTER(aLoadType);
+ *aLoadType = mLoadType;
+ return NS_OK;
+}
+
+NS_IMETHODIMP RemoteWebProgress::GetTarget(nsIEventTarget** aTarget) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP RemoteWebProgress::SetTarget(nsIEventTarget* aTarget) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+} // namespace mozilla::dom
diff --git a/dom/ipc/RemoteWebProgress.h b/dom/ipc/RemoteWebProgress.h
new file mode 100644
index 0000000000..118b41438d
--- /dev/null
+++ b/dom/ipc/RemoteWebProgress.h
@@ -0,0 +1,39 @@
+/* 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_RemoteWebProgress_h
+#define mozilla_dom_RemoteWebProgress_h
+
+#include "nsIWebProgress.h"
+#include "nsCOMPtr.h"
+
+namespace mozilla {
+namespace dom {
+
+class RemoteWebProgress final : public nsIWebProgress {
+ public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIWEBPROGRESS
+
+ RemoteWebProgress()
+ : mLoadType(0), mIsLoadingDocument(false), mIsTopLevel(false) {}
+
+ RemoteWebProgress(uint32_t aLoadType, bool aIsLoadingDocument,
+ bool aIsTopLevel)
+ : mLoadType(aLoadType),
+ mIsLoadingDocument(aIsLoadingDocument),
+ mIsTopLevel(aIsTopLevel) {}
+
+ private:
+ virtual ~RemoteWebProgress() = default;
+
+ uint32_t mLoadType;
+ bool mIsLoadingDocument;
+ bool mIsTopLevel;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_RemoteWebProgress_h
diff --git a/dom/ipc/RemoteWebProgressRequest.cpp b/dom/ipc/RemoteWebProgressRequest.cpp
new file mode 100644
index 0000000000..e7cf2a824c
--- /dev/null
+++ b/dom/ipc/RemoteWebProgressRequest.cpp
@@ -0,0 +1,256 @@
+/* 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(
+ nsISupports** 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::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..f583b71ec5
--- /dev/null
+++ b/dom/ipc/RemoteWebProgressRequest.h
@@ -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/. */
+
+#ifndef mozilla_dom_RemoteWebProgressRequest_h
+#define mozilla_dom_RemoteWebProgressRequest_h
+
+#include "nsIChannel.h"
+#include "nsIClassifiedChannel.h"
+
+namespace mozilla {
+namespace 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 dom
+} // namespace mozilla
+
+#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..8bcdb5a438
--- /dev/null
+++ b/dom/ipc/SharedMap.cpp
@@ -0,0 +1,463 @@
+/* -*- 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/BlobImpl.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/ContentProcessMessageManager.h"
+#include "mozilla/dom/IPCBlobUtils.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/IOBuffers.h"
+#include "mozilla/ScriptPreloader.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() : DOMEventTargetHelper() {}
+
+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::MutableHandleValue 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::MutableHandleValue 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 : IterHash(mEntries)) {
+ array.AppendElement(entry);
+ }
+ }
+
+ 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: Order of evaluation of function arguments is not guaranteed, so we
+ // can't use entry.release() in place of entry.get() without entry->Name()
+ // sometimes resulting in a null dereference.
+ mEntries.Put(entry->Name(), entry.get());
+ Unused << entry.release();
+ }
+
+ return Ok();
+}
+
+void SharedMap::MaybeRebuild() const {
+ Unused << const_cast<SharedMap*>(this)->MaybeRebuild();
+}
+
+WritableSharedMap::WritableSharedMap() : SharedMap() {
+ 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 (auto& entry : IterHash(mEntries)) {
+ 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 (auto& entry : IterHash(mEntries)) {
+ 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, aParent, *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::HandleValue 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.LookupOrAdd(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::HandleObject aGivenProto) {
+ return MozSharedMap_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+JSObject* WritableSharedMap::WrapObject(JSContext* aCx,
+ JS::HandleObject 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..8dceee70c0
--- /dev/null
+++ b/dom/ipc/SharedMap.h
@@ -0,0 +1,360 @@
+/* -*- 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 {
+namespace 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::MutableHandleValue 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::MutableHandleValue 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::HandleObject 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::MutableHandleValue 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::HandleValue 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::HandleValue 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::HandleObject 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 dom
+} // namespace mozilla
+
+#endif // dom_ipc_SharedMap_h
diff --git a/dom/ipc/SharedMapChangeEvent.h b/dom/ipc/SharedMapChangeEvent.h
new file mode 100644
index 0000000000..f191553fcb
--- /dev/null
+++ b/dom/ipc/SharedMapChangeEvent.h
@@ -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/. */
+
+#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 {
+namespace dom {
+namespace 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 ipc
+} // namespace dom
+} // namespace mozilla
+
+#endif // dom_ipc_SharedMapChangeEvent_h
diff --git a/dom/ipc/SharedMessageBody.cpp b/dom/ipc/SharedMessageBody.cpp
new file mode 100644
index 0000000000..7b01b0e815
--- /dev/null
+++ b/dom/ipc/SharedMessageBody.cpp
@@ -0,0 +1,297 @@
+/* -*- 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->BuildClonedMessageDataForBackgroundChild(aManager,
+ 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->StealFromClonedMessageDataForBackgroundChild(
+ 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->BorrowFromClonedMessageDataForBackgroundChild(
+ 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->BuildClonedMessageDataForBackgroundParent(aManager,
+ 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) {
+ 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->StealFromClonedMessageDataForBackgroundParent(
+ 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..e162a09928
--- /dev/null
+++ b/dom/ipc/SharedStringMap.cpp
@@ -0,0 +1,143 @@
+/* -*- 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/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 aKey.Compare(keys.GetBare(aEntry.mKey));
+ },
+ aIndex);
+}
+
+void SharedStringMapBuilder::Add(const nsCString& aKey,
+ const nsString& aValue) {
+ mEntries.Put(aKey, {mKeyTable.Add(aKey), mValueTable.Add(aValue)});
+}
+
+Result<Ok, nsresult> SharedStringMapBuilder::Finalize(
+ loader::AutoMemMap& aMap) {
+ using Header = SharedStringMap::Header;
+
+ MOZ_ASSERT(mEntries.Count() == mKeyTable.Count());
+
+ nsTArray<nsCString> keys(mEntries.Count());
+ for (auto iter = mEntries.Iter(); !iter.Done(); iter.Next()) {
+ keys.AppendElement(iter.Key());
+ }
+ 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..35a753fd55
--- /dev/null
+++ b/dom/ipc/SharedStringMap.h
@@ -0,0 +1,224 @@
+/* -*- 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 "nsDataHashtable.h"
+
+namespace mozilla {
+namespace dom {
+namespace 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;
+
+ nsDataHashtable<nsCStringHashKey, Entry> mEntries;
+};
+
+} // namespace ipc
+} // namespace dom
+} // namespace mozilla
+
+#endif // dom_ipc_SharedStringMap_h
diff --git a/dom/ipc/StringTable.h b/dom/ipc/StringTable.h
new file mode 100644
index 0000000000..849d0f9866
--- /dev/null
+++ b/dom/ipc/StringTable.h
@@ -0,0 +1,117 @@
+/* -*- 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 "nsDataHashtable.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 {
+namespace dom {
+namespace 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) {
+ const auto& entry = mEntries.LookupForAdd(aKey).OrInsert([&]() {
+ Entry newEntry{mSize, aKey};
+ mSize += aKey.Length() + 1;
+
+ return newEntry;
+ });
+
+ return {entry.mOffset, aKey.Length()};
+ }
+
+ void Write(const RangedPtr<uint8_t>& aBuffer) {
+ auto buffer = aBuffer.ReinterpretCast<ElemType>();
+
+ for (auto iter = mEntries.Iter(); !iter.Done(); iter.Next()) {
+ auto& entry = iter.Data();
+ 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;
+ };
+
+ nsDataHashtable<KeyType, Entry> mEntries;
+ uint32_t mSize = 0;
+};
+
+} // namespace ipc
+} // namespace dom
+} // namespace mozilla
+
+#endif
diff --git a/dom/ipc/StructuredCloneData.cpp b/dom/ipc/StructuredCloneData.cpp
new file mode 100644
index 0000000000..2ef85b5d0f
--- /dev/null
+++ b/dom/ipc/StructuredCloneData.cpp
@@ -0,0 +1,427 @@
+/* -*- 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::AutoIPCStream;
+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);
+ mIPCStreams = std::move(aOther.mIPCStreams);
+ 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->abandon();
+ mBuffer->steal(&data);
+ mBuffer = nullptr;
+ mSharedData = new SharedJSAllocatedData(std::move(data));
+ mInitialized = true;
+}
+
+enum ActorFlavorEnum {
+ Parent = 0,
+ Child,
+};
+
+enum ManagerFlavorEnum { ContentProtocol = 0, BackgroundProtocol };
+
+template <typename M>
+bool BuildClonedMessageData(M* aManager, StructuredCloneData& aData,
+ ClonedMessageData& aClonedData) {
+ SerializedStructuredCloneBuffer& buffer = aClonedData.data();
+ auto iter = aData.Data().Start();
+ size_t size = aData.Data().Size();
+ bool success;
+ buffer.data = aData.Data().Borrow(iter, size, &success);
+ if (NS_WARN_IF(!success)) {
+ return false;
+ }
+ if (aData.SupportsTransferring()) {
+ aClonedData.identifiers().AppendElements(aData.PortIdentifiers());
+ }
+
+ const nsTArray<RefPtr<BlobImpl>>& blobImpls = aData.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], aManager,
+ aClonedData.blobs()[i]);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return false;
+ }
+ }
+ }
+
+ const nsTArray<nsCOMPtr<nsIInputStream>>& inputStreams = aData.InputStreams();
+ if (!inputStreams.IsEmpty()) {
+ if (NS_WARN_IF(
+ !aData.IPCStreams().SetCapacity(inputStreams.Length(), fallible))) {
+ return false;
+ }
+
+ nsTArray<IPCStream>& streams = aClonedData.inputStreams();
+ uint32_t length = inputStreams.Length();
+ streams.SetCapacity(length);
+ for (uint32_t i = 0; i < length; ++i) {
+ AutoIPCStream* stream = aData.IPCStreams().AppendElement(fallible);
+ if (NS_WARN_IF(!stream)) {
+ return false;
+ }
+
+ if (!stream->Serialize(inputStreams[i], aManager)) {
+ return false;
+ }
+ streams.AppendElement(stream->TakeValue());
+ }
+ }
+
+ return true;
+}
+
+bool StructuredCloneData::BuildClonedMessageDataForParent(
+ ContentParent* aParent, ClonedMessageData& aClonedData) {
+ return BuildClonedMessageData(aParent, *this, aClonedData);
+}
+
+bool StructuredCloneData::BuildClonedMessageDataForChild(
+ ContentChild* aChild, ClonedMessageData& aClonedData) {
+ return BuildClonedMessageData(aChild, *this, aClonedData);
+}
+
+bool StructuredCloneData::BuildClonedMessageDataForBackgroundParent(
+ PBackgroundParent* aParent, ClonedMessageData& aClonedData) {
+ return BuildClonedMessageData(aParent, *this, aClonedData);
+}
+
+bool StructuredCloneData::BuildClonedMessageDataForBackgroundChild(
+ PBackgroundChild* aChild, ClonedMessageData& aClonedData) {
+ return BuildClonedMessageData(aChild, *this, aClonedData);
+}
+
+// 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> {
+ typedef const mozilla::dom::ClonedMessageData ClonedMessageType;
+
+ static void ProvideBuffer(const ClonedMessageData& aClonedData,
+ StructuredCloneData& aData) {
+ const SerializedStructuredCloneBuffer& buffer = aClonedData.data();
+ aData.UseExternalData(buffer.data);
+ }
+};
+
+template <>
+struct MemoryTraits<CopyMemory> {
+ typedef const mozilla::dom::ClonedMessageData ClonedMessageType;
+
+ static void ProvideBuffer(const ClonedMessageData& aClonedData,
+ StructuredCloneData& aData) {
+ const SerializedStructuredCloneBuffer& buffer = aClonedData.data();
+ aData.CopyExternalData(buffer.data);
+ }
+};
+
+template <>
+struct MemoryTraits<StealMemory> {
+ // note: not const!
+ typedef mozilla::dom::ClonedMessageData ClonedMessageType;
+
+ 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, ActorFlavorEnum Flavor>
+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::BorrowFromClonedMessageDataForParent(
+ const ClonedMessageData& aClonedData) {
+ // PContent parent is always main thread and actor constraints demand we're
+ // likewise on that thread.
+ MOZ_ASSERT(NS_IsMainThread());
+ UnpackClonedMessageData<BorrowMemory, Parent>(aClonedData, *this);
+}
+
+void StructuredCloneData::BorrowFromClonedMessageDataForChild(
+ const ClonedMessageData& aClonedData) {
+ // PContent child is always main thread and actor constraints demand we're
+ // likewise on that thread.
+ MOZ_ASSERT(NS_IsMainThread());
+ UnpackClonedMessageData<BorrowMemory, Child>(aClonedData, *this);
+}
+
+void StructuredCloneData::BorrowFromClonedMessageDataForBackgroundParent(
+ const ClonedMessageData& aClonedData) {
+ MOZ_ASSERT(IsOnBackgroundThread());
+ UnpackClonedMessageData<BorrowMemory, Parent>(aClonedData, *this);
+}
+
+void StructuredCloneData::BorrowFromClonedMessageDataForBackgroundChild(
+ const ClonedMessageData& aClonedData) {
+ // No thread assertion; BackgroundChild can happen on any thread.
+ UnpackClonedMessageData<BorrowMemory, Child>(aClonedData, *this);
+}
+
+void StructuredCloneData::CopyFromClonedMessageDataForParent(
+ const ClonedMessageData& aClonedData) {
+ MOZ_ASSERT(NS_IsMainThread());
+ UnpackClonedMessageData<CopyMemory, Parent>(aClonedData, *this);
+}
+
+void StructuredCloneData::CopyFromClonedMessageDataForChild(
+ const ClonedMessageData& aClonedData) {
+ MOZ_ASSERT(NS_IsMainThread());
+ UnpackClonedMessageData<CopyMemory, Child>(aClonedData, *this);
+}
+
+void StructuredCloneData::CopyFromClonedMessageDataForBackgroundParent(
+ const ClonedMessageData& aClonedData) {
+ MOZ_ASSERT(IsOnBackgroundThread());
+ UnpackClonedMessageData<CopyMemory, Parent>(aClonedData, *this);
+}
+
+void StructuredCloneData::CopyFromClonedMessageDataForBackgroundChild(
+ const ClonedMessageData& aClonedData) {
+ UnpackClonedMessageData<CopyMemory, Child>(aClonedData, *this);
+}
+
+void StructuredCloneData::StealFromClonedMessageDataForParent(
+ ClonedMessageData& aClonedData) {
+ MOZ_ASSERT(NS_IsMainThread());
+ UnpackClonedMessageData<StealMemory, Parent>(aClonedData, *this);
+}
+
+void StructuredCloneData::StealFromClonedMessageDataForChild(
+ ClonedMessageData& aClonedData) {
+ MOZ_ASSERT(NS_IsMainThread());
+ UnpackClonedMessageData<StealMemory, Child>(aClonedData, *this);
+}
+
+void StructuredCloneData::StealFromClonedMessageDataForBackgroundParent(
+ ClonedMessageData& aClonedData) {
+ MOZ_ASSERT(IsOnBackgroundThread());
+ UnpackClonedMessageData<StealMemory, Parent>(aClonedData, *this);
+}
+
+void StructuredCloneData::StealFromClonedMessageDataForBackgroundChild(
+ ClonedMessageData& aClonedData) {
+ UnpackClonedMessageData<StealMemory, Child>(aClonedData, *this);
+}
+
+void StructuredCloneData::WriteIPCParams(IPC::Message* aMsg) const {
+ WriteParam(aMsg, Data());
+}
+
+bool StructuredCloneData::ReadIPCParams(const IPC::Message* aMsg,
+ PickleIterator* aIter) {
+ MOZ_ASSERT(!mInitialized);
+ JSStructuredCloneData data(JS::StructuredCloneScope::DifferentProcess);
+ if (!ReadParam(aMsg, aIter, &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..524f088159
--- /dev/null
+++ b/dom/ipc/StructuredCloneData.h
@@ -0,0 +1,301 @@
+/* -*- 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 PickleIterator;
+
+namespace mozilla {
+namespace ipc {
+
+class AutoIPCStream;
+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 uses ExtractBuffers,
+ * returning a fatal false if unable to extract. (And
+ * SerializedStructuredCloneBuffer wraps/defers to it.) 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;
+
+ // Actor-varying methods to convert the structured clone stored in this holder
+ // by a previous call to Write() into ClonedMessageData IPC representation.
+ // (Blobs are represented in IPC by IPCBlob actors, so we need the parent to
+ // be able to create them.)
+ bool BuildClonedMessageDataForParent(ContentParent* aParent,
+ ClonedMessageData& aClonedData);
+ bool BuildClonedMessageDataForChild(ContentChild* aChild,
+ ClonedMessageData& aClonedData);
+ bool BuildClonedMessageDataForBackgroundParent(
+ mozilla::ipc::PBackgroundParent* aParent, ClonedMessageData& aClonedData);
+ bool BuildClonedMessageDataForBackgroundChild(
+ mozilla::ipc::PBackgroundChild* aChild, ClonedMessageData& aClonedData);
+
+ // Actor-varying and memory-management-strategy-varying methods to initialize
+ // this holder from a ClonedMessageData representation.
+ void BorrowFromClonedMessageDataForParent(
+ const ClonedMessageData& aClonedData);
+ void BorrowFromClonedMessageDataForChild(
+ const ClonedMessageData& aClonedData);
+ void BorrowFromClonedMessageDataForBackgroundParent(
+ const ClonedMessageData& aClonedData);
+ void BorrowFromClonedMessageDataForBackgroundChild(
+ const ClonedMessageData& aClonedData);
+
+ void CopyFromClonedMessageDataForParent(const ClonedMessageData& aClonedData);
+ void CopyFromClonedMessageDataForChild(const ClonedMessageData& aClonedData);
+ void CopyFromClonedMessageDataForBackgroundParent(
+ const ClonedMessageData& aClonedData);
+ void CopyFromClonedMessageDataForBackgroundChild(
+ const ClonedMessageData& aClonedData);
+
+ // The steal variants of course take a non-const ClonedMessageData.
+ void StealFromClonedMessageDataForParent(ClonedMessageData& aClonedData);
+ void StealFromClonedMessageDataForChild(ClonedMessageData& aClonedData);
+ void StealFromClonedMessageDataForBackgroundParent(
+ ClonedMessageData& aClonedData);
+ void StealFromClonedMessageDataForBackgroundChild(
+ 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; }
+
+ FallibleTArray<mozilla::ipc::AutoIPCStream>& IPCStreams() {
+ return mIPCStreams;
+ }
+
+ // For IPC serialization
+ void WriteIPCParams(IPC::Message* aMessage) const;
+ bool ReadIPCParams(const IPC::Message* aMessage, PickleIterator* aIter);
+
+ protected:
+ already_AddRefed<SharedJSAllocatedData> TakeSharedData();
+
+ private:
+ JSStructuredCloneData mExternalData;
+ RefPtr<SharedJSAllocatedData> mSharedData;
+
+ // This array is needed because AutoIPCStream DTOR must be executed after the
+ // sending of the data via IPC. This will be fixed by bug 1353475.
+ FallibleTArray<mozilla::ipc::AutoIPCStream> mIPCStreams;
+ 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..2a1c8d9123
--- /dev/null
+++ b/dom/ipc/TabContext.cpp
@@ -0,0 +1,154 @@
+/* -*- 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),
+ mJSPluginID(-1),
+ mShowFocusRings(UIStateChangeType_NoChange),
+ mMaxTouchPoints(0) {}
+
+bool TabContext::IsJSPlugin() const { return mJSPluginID >= 0; }
+
+int32_t TabContext::JSPluginId() const { return mJSPluginID; }
+
+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;
+}
+
+const nsAString& TabContext::PresentationURL() const {
+ return mPresentationURL;
+}
+
+UIStateChangeType TabContext::ShowFocusRings() const { return mShowFocusRings; }
+
+bool TabContext::SetTabContext(uint64_t aChromeOuterWindowID,
+ UIStateChangeType aShowFocusRings,
+ const nsAString& aPresentationURL,
+ uint32_t aMaxTouchPoints) {
+ NS_ENSURE_FALSE(mInitialized, false);
+
+ mInitialized = true;
+ mChromeOuterWindowID = aChromeOuterWindowID;
+ mPresentationURL = aPresentationURL;
+ mShowFocusRings = aShowFocusRings;
+ mMaxTouchPoints = aMaxTouchPoints;
+ return true;
+}
+
+bool TabContext::SetTabContextForJSPluginFrame(int32_t aJSPluginID) {
+ NS_ENSURE_FALSE(mInitialized, false);
+
+ mInitialized = true;
+ mJSPluginID = aJSPluginID;
+ return true;
+}
+
+IPCTabContext TabContext::AsIPCTabContext() const {
+ if (IsJSPlugin()) {
+ return IPCTabContext(JSPluginFrameIPCTabContext(mJSPluginID));
+ }
+
+ return IPCTabContext(FrameIPCTabContext(mChromeOuterWindowID,
+ mPresentationURL, mShowFocusRings,
+ mMaxTouchPoints));
+}
+
+MaybeInvalidTabContext::MaybeInvalidTabContext(const IPCTabContext& aParams)
+ : mInvalidReason(nullptr) {
+ uint64_t chromeOuterWindowID = 0;
+ int32_t jsPluginId = -1;
+ nsAutoString presentationURL;
+ UIStateChangeType showFocusRings = UIStateChangeType_NoChange;
+ uint32_t maxTouchPoints = 0;
+
+ switch (aParams.type()) {
+ case IPCTabContext::TPopupIPCTabContext: {
+ const PopupIPCTabContext& ipcContext = aParams.get_PopupIPCTabContext();
+
+ chromeOuterWindowID = ipcContext.chromeOuterWindowID();
+ break;
+ }
+ case IPCTabContext::TJSPluginFrameIPCTabContext: {
+ const JSPluginFrameIPCTabContext& ipcContext =
+ aParams.get_JSPluginFrameIPCTabContext();
+
+ jsPluginId = ipcContext.jsPluginId();
+ break;
+ }
+ case IPCTabContext::TFrameIPCTabContext: {
+ const FrameIPCTabContext& ipcContext = aParams.get_FrameIPCTabContext();
+
+ chromeOuterWindowID = ipcContext.chromeOuterWindowID();
+ presentationURL = ipcContext.presentationURL();
+ showFocusRings = ipcContext.showFocusRings();
+ maxTouchPoints = ipcContext.maxTouchPoints();
+ break;
+ }
+ default: {
+ MOZ_CRASH();
+ }
+ }
+
+ bool rv;
+ if (jsPluginId >= 0) {
+ rv = mTabContext.SetTabContextForJSPluginFrame(jsPluginId);
+ } else {
+ rv = mTabContext.SetTabContext(chromeOuterWindowID, showFocusRings,
+ presentationURL, maxTouchPoints);
+ }
+ if (!rv) {
+ 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..a7ec447321
--- /dev/null
+++ b/dom/ipc/TabContext.h
@@ -0,0 +1,217 @@
+/* -*- 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 {
+namespace 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;
+
+ bool IsJSPlugin() const;
+ int32_t JSPluginId() const;
+
+ uint64_t ChromeOuterWindowID() const;
+
+ /**
+ * Returns the presentation URL associated with the tab if this tab is
+ * created for presented content
+ */
+ const nsAString& PresentationURL() const;
+
+ UIStateChangeType ShowFocusRings() 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,
+ UIStateChangeType aShowFocusRings,
+ const nsAString& aPresentationURL,
+ 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);
+
+ /**
+ * Set this TabContext to be for a JS plugin. aPluginID is the id of the JS
+ * plugin
+ * (@see nsFakePlugin::mId).
+ * As with the other protected mutator methods, this lets you modify a
+ * TabContext once.
+ * (@see TabContext::SetTabContext above for more details).
+ */
+ bool SetTabContextForJSPluginFrame(int32_t aJSPluginID);
+
+ 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;
+
+ int32_t mJSPluginID;
+
+ /**
+ * The requested presentation URL.
+ */
+ nsString mPresentationURL;
+
+ /**
+ * Keyboard indicator state (focus rings).
+ */
+ UIStateChangeType mShowFocusRings;
+
+ /**
+ * 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,
+ UIStateChangeType aShowFocusRings,
+ const nsAString& aPresentationURL,
+ uint32_t aMaxTouchPoints) {
+ return TabContext::SetTabContext(aChromeOuterWindowID, aShowFocusRings,
+ aPresentationURL, aMaxTouchPoints);
+ }
+
+ bool SetTabContextForJSPluginFrame(uint32_t aJSPluginID) {
+ return TabContext::SetTabContextForJSPluginFrame(aJSPluginID);
+ }
+};
+
+/**
+ * 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 dom
+} // namespace mozilla
+
+#endif
diff --git a/dom/ipc/TabMessageTypes.h b/dom/ipc/TabMessageTypes.h
new file mode 100644
index 0000000000..8678a79859
--- /dev/null
+++ b/dom/ipc/TabMessageTypes.h
@@ -0,0 +1,29 @@
+/* -*- 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 {
+class Event;
+
+struct RemoteDOMEvent {
+ // Make sure to set the owner after deserializing.
+ RefPtr<Event> mEvent;
+};
+
+enum class EmbedderElementEventType {
+ NoEvent,
+ LoadEvent,
+ ErrorEvent,
+ EndGuard_,
+};
+
+} // namespace mozilla::dom
+
+#endif // TABMESSAGE_TYPES_H
diff --git a/dom/ipc/TabMessageUtils.cpp b/dom/ipc/TabMessageUtils.cpp
new file mode 100644
index 0000000000..12b219a1ec
--- /dev/null
+++ b/dom/ipc/TabMessageUtils.cpp
@@ -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/. */
+
+#include "mozilla/EventDispatcher.h"
+#include "mozilla/dom/Event.h"
+#include "mozilla/dom/TabMessageUtils.h"
+#include "nsCOMPtr.h"
+
+namespace mozilla::dom {
+
+bool ReadRemoteEvent(const IPC::Message* aMsg, PickleIterator* aIter,
+ RemoteDOMEvent* aResult) {
+ aResult->mEvent = nullptr;
+ nsString type;
+ NS_ENSURE_TRUE(ReadParam(aMsg, aIter, &type), false);
+
+ aResult->mEvent =
+ EventDispatcher::CreateEvent(nullptr, nullptr, nullptr, type);
+
+ return aResult->mEvent->Deserialize(aMsg, aIter);
+}
+
+} // namespace mozilla::dom
diff --git a/dom/ipc/TabMessageUtils.h b/dom/ipc/TabMessageUtils.h
new file mode 100644
index 0000000000..6eca1dec1f
--- /dev/null
+++ b/dom/ipc/TabMessageUtils.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 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 mozilla::dom {
+
+bool ReadRemoteEvent(const IPC::Message* aMsg, PickleIterator* aIter,
+ mozilla::dom::RemoteDOMEvent* aResult);
+
+} // namespace mozilla::dom
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::dom::RemoteDOMEvent> {
+ typedef mozilla::dom::RemoteDOMEvent paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ aParam.mEvent->Serialize(aMsg, true);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ return mozilla::dom::ReadRemoteEvent(aMsg, aIter, aResult);
+ }
+
+ static void Log(const paramType& aParam, std::wstring* aLog) {}
+};
+
+template <>
+struct ParamTraits<nsSizeMode>
+ : public ContiguousEnumSerializer<nsSizeMode, nsSizeMode_Normal,
+ nsSizeMode_Invalid> {};
+
+template <>
+struct ParamTraits<UIStateChangeType>
+ : public ContiguousEnumSerializer<UIStateChangeType,
+ UIStateChangeType_NoChange,
+ UIStateChangeType_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(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mVisibleRect);
+ WriteParam(aMsg, aParam.mScaleX);
+ WriteParam(aMsg, aParam.mScaleY);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ return ReadParam(aMsg, aIter, &aResult->mVisibleRect) &&
+ ReadParam(aMsg, aIter, &aResult->mScaleX) &&
+ ReadParam(aMsg, aIter, &aResult->mScaleY);
+ }
+};
+
+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::ScrollAxis> {
+ typedef mozilla::ScrollAxis paramType;
+
+ static void Write(Message* aMsg, const paramType& aParam) {
+ WriteParam(aMsg, aParam.mWhereToScroll);
+ WriteParam(aMsg, aParam.mWhenToScroll);
+ WriteParam(aMsg, aParam.mOnlyIfPerceivedScrollableDirection);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter,
+ paramType* aResult) {
+ if (!ReadParam(aMsg, aIter, &aResult->mWhereToScroll)) {
+ return false;
+ }
+ if (!ReadParam(aMsg, aIter, &aResult->mWhenToScroll)) {
+ return false;
+ }
+
+ // We can't set mOnlyIfPerceivedScrollableDirection directly since it's
+ // a bitfield.
+ bool value;
+ if (!ReadParam(aMsg, aIter, &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..31e24e7df7
--- /dev/null
+++ b/dom/ipc/URLClassifierChild.h
@@ -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/. */
+
+#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 {
+namespace 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 dom
+} // namespace mozilla
+
+#endif // mozilla_dom_URLClassifierChild_h
diff --git a/dom/ipc/URLClassifierParent.cpp b/dom/ipc/URLClassifierParent.cpp
new file mode 100644
index 0000000000..1a0e0aa420
--- /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, nullptr, 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..9f21b581b2
--- /dev/null
+++ b/dom/ipc/URLClassifierParent.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_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"
+#include "mozilla/dom/PContent.h"
+
+namespace mozilla {
+namespace 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 dom
+} // namespace mozilla
+
+#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.cpp b/dom/ipc/VsyncChild.cpp
new file mode 100644
index 0000000000..3b186e126e
--- /dev/null
+++ b/dom/ipc/VsyncChild.cpp
@@ -0,0 +1,75 @@
+/* -*- 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 "VsyncChild.h"
+
+#include "mozilla/SchedulerGroup.h"
+#include "mozilla/VsyncDispatcher.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla::dom {
+
+VsyncChild::VsyncChild()
+ : mIsShutdown(false), mVsyncRate(TimeDuration::Forever()) {
+ MOZ_ASSERT(NS_IsMainThread());
+}
+
+VsyncChild::~VsyncChild() { MOZ_ASSERT(NS_IsMainThread()); }
+
+void VsyncChild::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 VsyncChild::RemoveChildRefreshTimer(VsyncObserver* aVsyncObserver) {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (mIsShutdown) {
+ return;
+ }
+
+ if (mObservers.RemoveElement(aVsyncObserver) && mObservers.IsEmpty()) {
+ Unused << PVsyncChild::SendUnobserve();
+ }
+}
+
+void VsyncChild::ActorDestroy(ActorDestroyReason aActorDestroyReason) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!mIsShutdown);
+ mIsShutdown = true;
+
+ if (!mObservers.IsEmpty()) {
+ Unused << PVsyncChild::SendUnobserve();
+ }
+ mObservers.Clear();
+}
+
+mozilla::ipc::IPCResult VsyncChild::RecvNotify(const VsyncEvent& aVsync,
+ const float& aVsyncRate) {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!mIsShutdown);
+
+ SchedulerGroup::MarkVsyncRan();
+
+ mVsyncRate = TimeDuration::FromMilliseconds(aVsyncRate);
+
+ for (VsyncObserver* observer : mObservers.ForwardRange()) {
+ observer->NotifyVsync(aVsync);
+ }
+ return IPC_OK();
+}
+
+TimeDuration VsyncChild::GetVsyncRate() { return mVsyncRate; }
+
+} // namespace mozilla::dom
diff --git a/dom/ipc/VsyncChild.h b/dom/ipc/VsyncChild.h
new file mode 100644
index 0000000000..6230302437
--- /dev/null
+++ b/dom/ipc/VsyncChild.h
@@ -0,0 +1,53 @@
+/* -*- 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"
+#include "mozilla/RefPtr.h"
+#include "nsISupportsImpl.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 VsyncChild final : public PVsyncChild {
+ NS_INLINE_DECL_REFCOUNTING(VsyncChild)
+
+ friend class PVsyncChild;
+
+ public:
+ VsyncChild();
+
+ void AddChildRefreshTimer(VsyncObserver* aVsyncObserver);
+ void RemoveChildRefreshTimer(VsyncObserver* aVsyncObserver);
+
+ TimeDuration GetVsyncRate();
+
+ private:
+ virtual ~VsyncChild();
+
+ mozilla::ipc::IPCResult RecvNotify(const VsyncEvent& aVsync,
+ const float& aVsyncRate);
+ virtual 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..a640e0ff34
--- /dev/null
+++ b/dom/ipc/VsyncParent.cpp
@@ -0,0 +1,107 @@
+/* -*- 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 "BackgroundParent.h"
+#include "BackgroundParentImpl.h"
+#include "gfxPlatform.h"
+#include "mozilla/Unused.h"
+#include "nsThreadUtils.h"
+#include "VsyncSource.h"
+
+namespace mozilla::dom {
+
+VsyncParent::VsyncParent()
+ : mObservingVsync(false),
+ mDestroyed(false),
+ mInitialThread(NS_GetCurrentThread()) {}
+
+void VsyncParent::UpdateVsyncSource(
+ const RefPtr<gfx::VsyncSource>& aVsyncSource) {
+ mVsyncSource = aVsyncSource;
+ if (!mVsyncSource) {
+ mVsyncSource = gfxPlatform::GetPlatform()->GetHardwareVsync();
+ }
+
+ if (mObservingVsync && mVsyncDispatcher) {
+ mVsyncDispatcher->RemoveChildRefreshTimer(this);
+ }
+ mVsyncDispatcher = mVsyncSource->GetRefreshTimerVsyncDispatcher();
+ if (mObservingVsync) {
+ mVsyncDispatcher->AddChildRefreshTimer(this);
+ }
+}
+
+bool VsyncParent::NotifyVsync(const VsyncEvent& aVsync) {
+ if (IsOnInitialThread()) {
+ DispatchVsyncEvent(aVsync);
+ return true;
+ }
+
+ // 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(mInitialThread->Dispatch(vsyncEvent, NS_DISPATCH_NORMAL));
+ return true;
+}
+
+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 = mVsyncSource->GetGlobalDisplay().GetVsyncRate();
+ Unused << SendNotify(aVsync, vsyncRate.ToMilliseconds());
+ }
+}
+
+mozilla::ipc::IPCResult VsyncParent::RecvObserve() {
+ AssertIsOnInitialThread();
+ if (!mObservingVsync) {
+ if (mVsyncDispatcher) {
+ mVsyncDispatcher->AddChildRefreshTimer(this);
+ }
+ mObservingVsync = true;
+ return IPC_OK();
+ }
+ return IPC_FAIL_NO_REASON(this);
+}
+
+mozilla::ipc::IPCResult VsyncParent::RecvUnobserve() {
+ AssertIsOnInitialThread();
+ if (mObservingVsync) {
+ if (mVsyncDispatcher) {
+ mVsyncDispatcher->RemoveChildRefreshTimer(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->RemoveChildRefreshTimer(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..af452fd508
--- /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"
+#include "VsyncSource.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;
+
+ public:
+ VsyncParent();
+ void UpdateVsyncSource(const RefPtr<gfx::VsyncSource>& aVsyncSource);
+
+ private:
+ virtual ~VsyncParent() = default;
+
+ virtual bool 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<gfx::VsyncSource> mVsyncSource;
+ RefPtr<RefreshTimerVsyncDispatcher> mVsyncDispatcher;
+};
+
+} // namespace mozilla::dom
+
+#endif // mozilla_dom_ipc_VsyncParent_h
diff --git a/dom/ipc/WindowGlobalActor.cpp b/dom/ipc/WindowGlobalActor.cpp
new file mode 100644
index 0000000000..64085dbe5d
--- /dev/null
+++ b/dom/ipc/WindowGlobalActor.cpp
@@ -0,0 +1,169 @@
+/* -*- 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 "mozJSComponentLoader.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/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);
+
+ 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.mEmbedderPolicy = InheritedPolicy(aBrowsingContext);
+ fields.mAutoplayPermission = nsIPermissionManager::UNKNOWN_ACTION;
+ return init;
+}
+
+WindowGlobalInit WindowGlobalActor::AboutBlankInitializer(
+ dom::BrowsingContext* aBrowsingContext, nsIPrincipal* aPrincipal) {
+ WindowGlobalInit init =
+ BaseInitializer(aBrowsingContext, nsContentUtils::GenerateWindowId(),
+ nsContentUtils::GenerateWindowId());
+
+ init.principal() = aPrincipal;
+ Unused << NS_NewURI(getter_AddRefs(init.documentURI()), "about:blank");
+
+ return init;
+}
+
+WindowGlobalInit WindowGlobalActor::WindowInitializer(
+ nsGlobalWindowInner* aWindow) {
+ WindowGlobalInit init =
+ BaseInitializer(aWindow->GetBrowsingContext(), aWindow->WindowID(),
+ aWindow->GetOuterWindow()->WindowID());
+
+ init.principal() = aWindow->GetPrincipal();
+ init.documentURI() = aWindow->GetDocumentURI();
+
+ Document* doc = aWindow->GetDocument();
+
+ 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();
+
+ auto& fields = init.context().mFields;
+ fields.mCookieBehavior = Some(doc->CookieJarSettings()->GetCookieBehavior());
+ fields.mIsOnContentBlockingAllowList =
+ doc->CookieJarSettings()->GetIsOnContentBlockingAllowList();
+ fields.mIsThirdPartyWindow = doc->HasThirdPartyChannel();
+ fields.mIsThirdPartyTrackingResourceWindow =
+ nsContentUtils::IsThirdPartyTrackingResourceWindow(aWindow);
+ fields.mIsSecureContext = aWindow->IsSecureContext();
+
+ // Initialze permission fields
+ fields.mAutoplayPermission =
+ AutoplayPolicy::GetSiteAutoplayPermission(init.principal());
+ fields.mPopupPermission = PopupBlocker::GetPopupPermission(init.principal());
+
+ // Initialize top level permission fields
+ if (aWindow->GetBrowsingContext()->IsTop()) {
+ fields.mShortcutsPermission =
+ nsGlobalWindowInner::GetShortcutsPermission(init.principal());
+ }
+
+ auto policy = doc->GetEmbedderPolicy();
+ if (policy.isSome()) {
+ fields.mEmbedderPolicy = *policy;
+ }
+
+ // Init Mixed Content Fields
+ nsCOMPtr<nsIURI> innerDocURI = NS_GetInnermostURI(doc->GetDocumentURI());
+ if (innerDocURI) {
+ fields.mIsSecure = innerDocURI->SchemeIs("https");
+ }
+ nsCOMPtr<nsIChannel> mixedChannel;
+ aWindow->GetDocShell()->GetMixedContentChannel(getter_AddRefs(mixedChannel));
+ // A non null mixedContent channel on the docshell indicates,
+ // that the user has overriden mixed content to allow mixed
+ // content loads to happen.
+ if (mixedChannel && (mixedChannel == doc->GetChannel())) {
+ fields.mAllowMixedContent = true;
+ }
+
+ nsCOMPtr<nsITransportSecurityInfo> securityInfo;
+ if (nsCOMPtr<nsIChannel> channel = doc->GetChannel()) {
+ nsCOMPtr<nsILoadInfo> loadInfo(channel->LoadInfo());
+ fields.mIsOriginalFrameSource = loadInfo->GetOriginalFrameSrcLoad();
+
+ nsCOMPtr<nsISupports> securityInfoSupports;
+ channel->GetSecurityInfo(getter_AddRefs(securityInfoSupports));
+ securityInfo = do_QueryInterface(securityInfoSupports);
+ }
+ init.securityInfo() = securityInfo;
+
+ fields.mIsLocalIP = 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..cb157abcfa
--- /dev/null
+++ b/dom/ipc/WindowGlobalChild.cpp
@@ -0,0 +1,700 @@
+/* -*- 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/WindowGlobalActorsBinding.h"
+#include "mozilla/dom/WindowGlobalParent.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 "nsContentUtils.h"
+#include "nsDocShell.h"
+#include "nsFocusManager.h"
+#include "nsFrameLoaderOwner.h"
+#include "nsGlobalWindowInner.h"
+#include "nsFrameLoaderOwner.h"
+#include "nsNetUtil.h"
+#include "nsQueryObject.h"
+#include "nsSerializationHelper.h"
+#include "nsFrameLoader.h"
+
+#include "mozilla/dom/JSWindowActorBinding.h"
+#include "mozilla/dom/JSWindowActorChild.h"
+#include "mozilla/dom/JSActorService.h"
+#include "nsIHttpChannelInternal.h"
+#include "nsIURIMutator.h"
+
+using namespace mozilla::ipc;
+using namespace mozilla::dom::ipc;
+
+namespace mozilla::dom {
+
+typedef nsRefPtrHashtable<nsUint64HashKey, WindowGlobalChild> WGCByIdMap;
+static StaticAutoPtr<WGCByIdMap> gWindowGlobalChildById;
+
+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");
+ }
+
+#ifdef MOZ_GECKO_PROFILER
+ // 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()->Id(), InnerWindowId(),
+ aDocumentURI->GetSpecOrDefault(),
+ embedderInnerWindowID);
+#endif
+}
+
+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, /* aInProcess */ true);
+ } else {
+ dom::WindowContext::FieldValues fields = aInit.context().mFields;
+ windowContext =
+ new dom::WindowContext(browsingContext, aInit.context().mInnerWindowId,
+ aInit.context().mOuterWindowId,
+ /* aInProcess */ true, std::move(fields));
+ }
+
+ RefPtr<WindowGlobalChild> windowChild = new WindowGlobalChild(
+ windowContext, aInit.principal(), aInit.documentURI());
+ return windowChild.forget();
+}
+
+void WindowGlobalChild::Init() {
+ mWindowContext->Init();
+
+ // Register this WindowGlobal in the gWindowGlobalParentsById map.
+ if (!gWindowGlobalChildById) {
+ gWindowGlobalChildById = new WGCByIdMap();
+ ClearOnShutdown(&gWindowGlobalChildById);
+ }
+ auto entry = gWindowGlobalChildById->LookupForAdd(InnerWindowId());
+ MOZ_RELEASE_ASSERT(!entry, "Duplicate WindowGlobalChild entry for ID!");
+ entry.OrInsert([&] { return this; });
+}
+
+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?
+ SetDocumentURI(aDocument->GetDocumentURI());
+ SetDocumentPrincipal(aDocument->NodePrincipal());
+
+ nsCOMPtr<nsITransportSecurityInfo> securityInfo;
+ if (nsCOMPtr<nsIChannel> channel = aDocument->GetChannel()) {
+ nsCOMPtr<nsISupports> securityInfoSupports;
+ channel->GetSecurityInfo(getter_AddRefs(securityInfoSupports));
+ securityInfo = do_QueryInterface(securityInfoSupports);
+ }
+ 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());
+ auto policy = aDocument->GetEmbedderPolicy();
+ if (policy.isSome()) {
+ txn.SetEmbedderPolicy(policy.ref());
+ }
+
+ if (nsCOMPtr<nsIChannel> channel = aDocument->GetChannel()) {
+ nsCOMPtr<nsILoadInfo> loadInfo(channel->LoadInfo());
+ txn.SetIsOriginalFrameSource(loadInfo->GetOriginalFrameSrcLoad());
+ } else {
+ txn.SetIsOriginalFrameSource(false);
+ }
+
+ // Init Mixed Content Fields
+ nsCOMPtr<nsIURI> innerDocURI =
+ NS_GetInnermostURI(aDocument->GetDocumentURI());
+ if (innerDocURI) {
+ txn.SetIsSecure(innerDocURI->SchemeIs("https"));
+ }
+ nsCOMPtr<nsIChannel> mixedChannel;
+ mWindowGlobal->GetDocShell()->GetMixedContentChannel(
+ getter_AddRefs(mixedChannel));
+ // A non null mixedContent channel on the docshell indicates,
+ // that the user has overriden mixed content to allow mixed
+ // content loads to happen.
+ if (mixedChannel && (mixedChannel == aDocument->GetChannel())) {
+ txn.SetAllowMixedContent(true);
+ }
+
+ MOZ_DIAGNOSTIC_ASSERT(mDocumentPrincipal->GetIsLocalIpAddress() ==
+ mWindowContext->IsLocalIP());
+
+ MOZ_ALWAYS_SUCCEEDS(txn.Commit(mWindowContext));
+}
+
+/* static */
+already_AddRefed<WindowGlobalChild> WindowGlobalChild::GetByInnerWindowId(
+ uint64_t aInnerWindowId) {
+ if (!gWindowGlobalChildById) {
+ return nullptr;
+ }
+ return gWindowGlobalChildById->Get(aInnerWindowId);
+}
+
+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();
+
+ // 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()));
+
+ // Immediately resolve the promise, acknowledging the request.
+ aResolve(true);
+
+ if (!aLayersId.IsValid()) {
+ return IPC_FAIL(this, "Received an invalid LayersId");
+ }
+
+ // 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();
+ }
+
+ // Immediately tear down the actor if we don't have a valid FrameContext.
+ if (NS_WARN_IF(aFrameContext.IsNullOrDiscarded())) {
+ BrowserBridgeChild::Send__delete__(bridge);
+ return IPC_OK();
+ }
+
+ RefPtr<Element> embedderElt = frameContext->GetEmbedderElement();
+ if (NS_WARN_IF(!embedderElt)) {
+ BrowserBridgeChild::Send__delete__(bridge);
+ return IPC_OK();
+ }
+
+ if (NS_WARN_IF(embedderElt->GetOwnerGlobal() != GetWindowGlobal())) {
+ BrowserBridgeChild::Send__delete__(bridge);
+ 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())) {
+ BrowserBridgeChild::Send__delete__(bridge);
+ return IPC_OK();
+ }
+
+ 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::RecvGetSecurityInfo(
+ GetSecurityInfoResolver&& aResolve) {
+ Maybe<nsCString> result;
+
+ if (nsCOMPtr<Document> doc = mWindowGlobal->GetDoc()) {
+ nsCOMPtr<nsISupports> secInfo;
+ nsresult rv = NS_OK;
+
+ // First check if there's a failed channel, in case of a certificate
+ // error.
+ if (nsIChannel* failedChannel = doc->GetFailedChannel()) {
+ rv = failedChannel->GetSecurityInfo(getter_AddRefs(secInfo));
+ } else {
+ // When there's no failed channel we should have a regular
+ // security info on the document. In some cases there's no
+ // security info at all, i.e. on HTTP sites.
+ secInfo = doc->GetSecurityInfo();
+ }
+
+ if (NS_SUCCEEDED(rv) && secInfo) {
+ nsCOMPtr<nsISerializable> secInfoSer = do_QueryInterface(secInfo);
+ result.emplace();
+ NS_SerializeToString(secInfoSer, result.ref());
+ }
+ }
+
+ aResolve(result);
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+WindowGlobalChild::RecvSaveStorageAccessPermissionGranted() {
+ nsCOMPtr<nsPIDOMWindowInner> inner = GetWindowGlobal();
+ if (inner) {
+ inner->SaveStorageAccessPermissionGranted();
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> outer =
+ nsPIDOMWindowOuter::GetFromCurrentInner(inner);
+ if (outer) {
+ nsGlobalWindowOuter::Cast(outer)->SetStorageAccessPermissionGranted(true);
+ }
+
+ 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();
+}
+
+IPCResult WindowGlobalChild::RecvRawMessage(
+ const JSActorMessageMeta& aMeta, const Maybe<ClonedMessageData>& aData,
+ const Maybe<ClonedMessageData>& aStack) {
+ Maybe<StructuredCloneData> data;
+ if (aData) {
+ data.emplace();
+ data->BorrowFromClonedMessageDataForChild(*aData);
+ }
+ Maybe<StructuredCloneData> stack;
+ if (aStack) {
+ stack.emplace();
+ stack->BorrowFromClonedMessageDataForChild(*aStack);
+ }
+ ReceiveRawMessage(aMeta, std::move(data), std::move(stack));
+ return IPC_OK();
+}
+
+void WindowGlobalChild::SetDocumentURI(nsIURI* aDocumentURI) {
+#ifdef MOZ_GECKO_PROFILER
+ // 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()->Id(), InnerWindowId(),
+ aDocumentURI->GetSpecOrDefault(),
+ embedderInnerWindowID);
+#endif
+ mDocumentURI = aDocumentURI;
+ SendUpdateDocumentURI(aDocumentURI);
+}
+
+void WindowGlobalChild::SetDocumentPrincipal(
+ nsIPrincipal* aNewDocumentPrincipal) {
+ MOZ_ASSERT(mDocumentPrincipal->Equals(aNewDocumentPrincipal));
+ mDocumentPrincipal = aNewDocumentPrincipal;
+ SendUpdateDocumentPrincipal(aNewDocumentPrincipal);
+}
+
+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<JSActor> WindowGlobalChild::InitJSActor(
+ JS::HandleObject 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");
+
+ gWindowGlobalChildById->Remove(InnerWindowId());
+
+#ifdef MOZ_GECKO_PROFILER
+ profiler_unregister_page(InnerWindowId());
+#endif
+
+ // 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());
+}
+
+WindowGlobalChild::~WindowGlobalChild() {
+ MOZ_ASSERT(!gWindowGlobalChildById ||
+ !gWindowGlobalChildById->Contains(InnerWindowId()));
+}
+
+JSObject* WindowGlobalChild::WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) {
+ return WindowGlobalChild_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+nsISupports* WindowGlobalChild::GetParentObject() {
+ return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
+}
+
+void WindowGlobalChild::MaybeSendUpdateDocumentWouldPreloadResources() {
+ if (!mDocumentWouldPreloadResources) {
+ mDocumentWouldPreloadResources = true;
+ SendUpdateDocumentWouldPreloadResources();
+ }
+}
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WindowGlobalChild, mWindowGlobal,
+ mContainerFeaturePolicy)
+
+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..91f5829081
--- /dev/null
+++ b/dom/ipc/WindowGlobalChild.h
@@ -0,0 +1,195 @@
+/* -*- 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/dom/PWindowGlobalChild.h"
+#include "nsRefPtrHashtable.h"
+#include "nsWrapperCache.h"
+#include "mozilla/dom/WindowGlobalActor.h"
+
+class nsGlobalWindowInner;
+class nsDocShell;
+
+namespace mozilla {
+namespace 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 {
+ friend class PWindowGlobalChild;
+
+ public:
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_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* 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);
+
+ // 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();
+
+ nsISupports* GetParentObject();
+ JSObject* WrapObject(JSContext* aCx,
+ JS::Handle<JSObject*> aGivenProto) override;
+
+ void MaybeSendUpdateDocumentWouldPreloadResources();
+
+ dom::FeaturePolicy* GetContainerFeaturePolicy() const {
+ return mContainerFeaturePolicy;
+ }
+
+ protected:
+ const nsACString& GetRemoteType() override;
+
+ already_AddRefed<JSActor> InitJSActor(JS::HandleObject 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 RecvGetSecurityInfo(
+ GetSecurityInfoResolver&& aResolve);
+
+ mozilla::ipc::IPCResult RecvSaveStorageAccessPermissionGranted();
+
+ mozilla::ipc::IPCResult RecvAddBlockedFrameNodeByClassifier(
+ const MaybeDiscardedBrowsingContext& aNode);
+
+ mozilla::ipc::IPCResult RecvResetScalingZoom();
+
+ mozilla::ipc::IPCResult RecvSetContainerFeaturePolicy(
+ dom::FeaturePolicy* aContainerFeaturePolicy);
+
+ 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;
+ bool mDocumentWouldPreloadResources = false;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // !defined(mozilla_dom_WindowGlobalChild_h)
diff --git a/dom/ipc/WindowGlobalParent.cpp b/dom/ipc/WindowGlobalParent.cpp
new file mode 100644
index 0000000000..ae32c88402
--- /dev/null
+++ b/dom/ipc/WindowGlobalParent.cpp
@@ -0,0 +1,1322 @@
+/* -*- 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/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/MediaController.h"
+#include "mozilla/dom/RemoteWebProgress.h"
+#include "mozilla/dom/WindowGlobalChild.h"
+#include "mozilla/dom/ChromeUtils.h"
+#include "mozilla/dom/ipc/IdType.h"
+#include "mozilla/dom/ipc/StructuredCloneData.h"
+#include "mozilla/ScopeExit.h"
+#include "mozilla/ServoCSSParser.h"
+#include "mozilla/ServoStyleSet.h"
+#include "mozilla/StaticPrefs_network.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/Variant.h"
+#include "mozJSComponentLoader.h"
+#include "nsContentUtils.h"
+#include "nsDocShell.h"
+#include "nsDocShellLoadState.h"
+#include "nsError.h"
+#include "nsFrameLoader.h"
+#include "nsFrameLoaderOwner.h"
+#include "nsGlobalWindowInner.h"
+#include "nsQueryObject.h"
+#include "nsFrameLoaderOwner.h"
+#include "nsNetUtil.h"
+#include "nsSandboxFlags.h"
+#include "nsSerializationHelper.h"
+#include "nsIBrowser.h"
+#include "nsIPromptCollection.h"
+#include "nsITimer.h"
+#include "nsITransportSecurityInfo.h"
+#include "nsISharePicker.h"
+#include "mozilla/Telemetry.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"
+
+using namespace mozilla::ipc;
+using namespace mozilla::dom::ipc;
+
+extern mozilla::LazyLogModule gUseCountersLog;
+
+namespace mozilla::dom {
+
+WindowGlobalParent::WindowGlobalParent(
+ CanonicalBrowsingContext* aBrowsingContext, uint64_t aInnerWindowId,
+ uint64_t aOuterWindowId, bool aInProcess, FieldValues&& aInit)
+ : WindowContext(aBrowsingContext, aInnerWindowId, aOuterWindowId,
+ aInProcess, std::move(aInit)),
+ mIsInitialDocument(false),
+ 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, bool aInProcess) {
+ 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, aInProcess,
+ std::move(fields));
+ wgp->mDocumentPrincipal = aInit.principal();
+ wgp->mDocumentURI = aInit.documentURI();
+ 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");
+
+ 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.GetOrInsert(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::Telemetry::Accumulate(
+ mozilla::Telemetry::HistogramID::
+ FX_NUMBER_OF_UNIQUE_SITE_ORIGINS_PER_DOCUMENT,
+ 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();
+ }
+ 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();
+ }
+ 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(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?)
+ mDocumentURI = aURI;
+ return IPC_OK();
+}
+
+IPCResult WindowGlobalParent::RecvUpdateDocumentPrincipal(
+ nsIPrincipal* aNewDocumentPrincipal) {
+ 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;
+ return IPC_OK();
+}
+mozilla::ipc::IPCResult WindowGlobalParent::RecvUpdateDocumentTitle(
+ const nsString& aTitle) {
+ if (mDocumentTitle == aTitle) {
+ return IPC_OK();
+ }
+
+ mDocumentTitle = 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();
+ }
+
+ (new AsyncEventDispatcher(frameElement, u"pagetitlechanged"_ns,
+ CanBubble::eYes, ChromeOnlyDispatch::eYes))
+ ->RunDOMEventWhenSafe();
+
+ 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->BorrowFromClonedMessageDataForParent(*aData);
+ }
+ Maybe<StructuredCloneData> stack;
+ if (aStack) {
+ stack.emplace();
+ stack->BorrowFromClonedMessageDataForParent(*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) {
+ MOZ_ASSERT(NS_IsMainThread());
+ DebugOnly<bool> isCookiesBlocked =
+ aEvent == nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER ||
+ aEvent == nsIWebProgressListener::STATE_COOKIES_BLOCKED_SOCIALTRACKER ||
+ (aEvent == nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN &&
+ StaticPrefs::network_cookie_rejectForeignWithExceptions_enabled());
+ 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);
+
+ // Notify the OnContentBlockingEvent if necessary.
+ if (event) {
+ if (!GetBrowsingContext()->GetWebProgress()) {
+ return;
+ }
+
+ nsCOMPtr<nsIWebProgress> webProgress =
+ new RemoteWebProgress(0, false, BrowsingContext()->IsTopContent());
+ GetBrowsingContext()->Top()->GetWebProgress()->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<JSActor> WindowGlobalParent::InitJSActor(
+ JS::HandleObject 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() {
+ return CanSend() && BrowsingContext()->GetCurrentWindowGlobal() == this;
+}
+
+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) override {
+ mResolver(NS_OK);
+ }
+
+ virtual void RejectedCallback(JSContext* aCx,
+ JS::Handle<JS::Value> aValue) 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();
+}
+
+mozilla::ipc::IPCResult
+WindowGlobalParent::RecvUpdateDocumentWouldPreloadResources() {
+ TopWindowContext()->mDocumentTreeWouldPreloadResources = true;
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult WindowGlobalParent::RecvSubmitLoadEventPreloadTelemetry(
+ TimeStamp aNavigationStart, TimeStamp aLoadEventStart,
+ TimeStamp aLoadEventEnd) {
+ if (!IsTop()) {
+ return IPC_FAIL(this, "submit preload telemetry on non-toplevel document");
+ }
+
+ if (mDocumentTreeWouldPreloadResources) {
+ Telemetry::AccumulateTimeDelta(
+ Telemetry::TIME_TO_LOAD_EVENT_START_PRELOAD_MS, aNavigationStart,
+ aLoadEventStart);
+ Telemetry::AccumulateTimeDelta(Telemetry::TIME_TO_LOAD_EVENT_END_PRELOAD_MS,
+ aNavigationStart, aLoadEventEnd);
+ } else {
+ Telemetry::AccumulateTimeDelta(
+ Telemetry::TIME_TO_LOAD_EVENT_START_NO_PRELOAD_MS, aNavigationStart,
+ aLoadEventStart);
+ Telemetry::AccumulateTimeDelta(
+ Telemetry::TIME_TO_LOAD_EVENT_END_NO_PRELOAD_MS, aNavigationStart,
+ aLoadEventEnd);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+WindowGlobalParent::RecvSubmitTimeToFirstInteractionPreloadTelemetry(
+ uint32_t aMillis) {
+ if (!IsTop()) {
+ return IPC_FAIL(this, "submit preload telemetry on non-toplevel document");
+ }
+
+ if (mDocumentTreeWouldPreloadResources) {
+ Telemetry::Accumulate(Telemetry::TIME_TO_FIRST_INTERACTION_PRELOAD_MS,
+ aMillis);
+ } else {
+ Telemetry::Accumulate(Telemetry::TIME_TO_FIRST_INTERACTION_NO_PRELOAD_MS,
+ aMillis);
+ }
+
+ return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
+WindowGlobalParent::RecvSubmitLoadInputEventResponsePreloadTelemetry(
+ uint32_t aMillis) {
+ if (!IsTop()) {
+ return IPC_FAIL(this, "submit preload telemetry on non-toplevel document");
+ }
+
+ if (mDocumentTreeWouldPreloadResources) {
+ Telemetry::Accumulate(Telemetry::LOAD_INPUT_EVENT_RESPONSE_PRELOAD_MS,
+ aMillis);
+ } else {
+ Telemetry::Accumulate(Telemetry::LOAD_INPUT_EVENT_RESPONSE_NO_PRELOAD_MS,
+ aMillis);
+ }
+
+ return IPC_OK();
+}
+
+namespace {
+
+class CheckPermitUnloadRequest final : public PromiseNativeHandler,
+ public nsITimerCallback {
+ public:
+ CheckPermitUnloadRequest(WindowGlobalParent* aWGP, bool aHasInProcessBlocker,
+ nsIContentViewer::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 = nsIContentViewer::eDontPromptAndUnload;
+ }
+ if (action != nsIContentViewer::ePrompt) {
+ SendReply(action == nsIContentViewer::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) override {
+ MOZ_ASSERT(mState == State::PROMPTING);
+
+ SendReply(JS::ToBoolean(aValue));
+ }
+
+ void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) 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;
+
+ nsIContentViewer::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,
+ nsIContentViewer::PermitUnloadAction(aAction),
+ [promise](bool aAllow) { promise->MaybeResolve(aAllow); });
+ request->Run(/* aIgnoreProcess */ nullptr, aTimeout);
+
+ return promise.forget();
+}
+
+already_AddRefed<mozilla::dom::Promise> WindowGlobalParent::DrawSnapshot(
+ const DOMRect* aRect, double aScale, const nsACString& aBackgroundColor,
+ 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::None;
+ if (!aRect) {
+ // If no explicit Rect was passed, we want the currently visible viewport.
+ flags = gfx::CrossProcessPaintFlags::DrawView;
+ }
+
+ 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);
+ });
+}
+
+already_AddRefed<Promise> WindowGlobalParent::GetSecurityInfo(
+ ErrorResult& aRv) {
+ RefPtr<BrowserParent> browserParent = GetBrowserParent();
+ if (NS_WARN_IF(!browserParent)) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return nullptr;
+ }
+
+ nsIGlobalObject* global = GetParentObject();
+ RefPtr<Promise> promise = Promise::Create(global, aRv);
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+
+ SendGetSecurityInfo(
+ [promise](Maybe<nsCString>&& aResult) {
+ if (aResult) {
+ nsCOMPtr<nsISupports> infoObj;
+ nsresult rv =
+ NS_DeserializeObject(aResult.value(), getter_AddRefs(infoObj));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ promise->MaybeReject(NS_ERROR_FAILURE);
+ }
+ nsCOMPtr<nsITransportSecurityInfo> info = do_QueryInterface(infoObj);
+ if (!info) {
+ promise->MaybeReject(NS_ERROR_FAILURE);
+ }
+ promise->MaybeResolve(info);
+ } else {
+ promise->MaybeResolveWithUndefined();
+ }
+ },
+ [promise](ResponseRejectReason&& aReason) {
+ promise->MaybeReject(NS_ERROR_FAILURE);
+ });
+
+ return promise.forget();
+}
+
+/**
+ * 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()));
+
+ Telemetry::Accumulate(Telemetry::TOP_LEVEL_CONTENT_DOCUMENTS_DESTROYED, 1);
+
+ for (int32_t c = 0; c < eUseCounter_Count; ++c) {
+ auto uc = static_cast<UseCounter>(c);
+ if (!mPageUseCounters->mUseCounters[uc]) {
+ continue;
+ }
+
+ auto id = static_cast<Telemetry::HistogramID>(
+ Telemetry::HistogramFirstUseCounter + uc * 2 + 1);
+ MOZ_LOG(gUseCountersLog, LogLevel::Debug,
+ (" > %s\n", Telemetry::GetHistogramName(id)));
+ Telemetry::Accumulate(id, 1);
+ }
+ } else {
+ MOZ_LOG(gUseCountersLog, LogLevel::Debug,
+ (" > no page use counter data was received"));
+ }
+
+ mSentPageUseCounters = true;
+ mPageUseCounters = nullptr;
+}
+
+void WindowGlobalParent::ActorDestroy(ActorDestroyReason aWhy) {
+ 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);
+ }
+ }
+
+ // Note that our WindowContext has become discarded.
+ WindowContext::Discard();
+
+ ContentParent* cp = nullptr;
+ if (!IsInProcess()) {
+ cp = static_cast<ContentParent*>(Manager()->Manager());
+ }
+
+ RefPtr<WindowGlobalParent> self(this);
+ Group()->EachOtherParent(cp, [&](ContentParent* otherContent) {
+ // Keep the WindowContext alive until other processes have acknowledged it
+ // has been discarded.
+ auto resolve = [self](bool) {};
+ auto reject = [self](mozilla::ipc::ResponseRejectReason) {};
+ otherContent->SendDiscardWindowContext(InnerWindowId(), resolve, reject);
+ });
+
+ // Report content blocking log when destroyed.
+ // There shouldn't have any content blocking log when a documnet is loaded in
+ // the parent process(See NotifyContentBlockingeEvent), 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()->ReportOrigins();
+ }
+ }
+ }
+ }
+
+ // 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);
+ }
+}
+
+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)) ==
+ aStateFlags,
+ "Invalid flags specified!");
+
+ if ((mSecurityState & aStateFlags) == aStateFlags) {
+ return;
+ }
+
+ mSecurityState |= aStateFlags;
+
+ if (GetBrowsingContext()->GetCurrentWindowGlobal() == this) {
+ GetBrowsingContext()->UpdateSecurityState();
+ }
+}
+
+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..77a5b09ab6
--- /dev/null
+++ b/dom/ipc/WindowGlobalParent.h
@@ -0,0 +1,351 @@
+/* -*- 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/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 "nsDataHashtable.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;
+
+/**
+ * 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() {
+ return CanonicalBrowsingContext::Cast(WindowContext::GetBrowsingContext());
+ }
+
+ // 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);
+
+ // 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; }
+
+ // 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; }
+
+ nsIPrincipal* GetContentBlockingAllowListPrincipal() const {
+ return mDocContentBlockingAllowListPrincipal;
+ }
+
+ Maybe<ClientInfo> GetClientInfo() { return mClientInfo; }
+
+ uint64_t ContentParentId();
+
+ int32_t OsPid();
+
+ bool IsCurrentGlobal();
+
+ bool IsProcessRoot();
+
+ uint32_t ContentBlockingEvents();
+
+ void GetContentBlockingLog(nsAString& aLog);
+
+ bool IsInitialDocument() { return mIsInitialDocument; }
+
+ already_AddRefed<mozilla::dom::Promise> PermitUnload(
+ PermitUnloadAction aAction, uint32_t aTimeout, mozilla::ErrorResult& aRv);
+
+ already_AddRefed<mozilla::dom::Promise> DrawSnapshot(
+ const DOMRect* aRect, double aScale, const nsACString& aBackgroundColor,
+ mozilla::ErrorResult& aRv);
+
+ already_AddRefed<Promise> GetSecurityInfo(ErrorResult& aRv);
+
+ static already_AddRefed<WindowGlobalParent> CreateDisconnected(
+ const WindowGlobalInit& aInit, bool aInProcess = false);
+
+ // 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 = Nothing());
+
+ 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;
+
+ protected:
+ already_AddRefed<JSActor> InitJSActor(JS::HandleObject 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(nsIURI* aURI);
+ mozilla::ipc::IPCResult RecvUpdateDocumentPrincipal(
+ nsIPrincipal* aNewDocumentPrincipal);
+ 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) {
+ mIsInitialDocument = 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 RecvUpdateDocumentWouldPreloadResources();
+ mozilla::ipc::IPCResult RecvSubmitLoadEventPreloadTelemetry(
+ TimeStamp aNavigationStart, TimeStamp aLoadEventStart,
+ TimeStamp aLoadEventEnd);
+ mozilla::ipc::IPCResult RecvSubmitTimeToFirstInteractionPreloadTelemetry(
+ uint32_t aMillis);
+ mozilla::ipc::IPCResult RecvSubmitLoadInputEventResponsePreloadTelemetry(
+ uint32_t aMillis);
+
+ 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);
+
+ private:
+ WindowGlobalParent(CanonicalBrowsingContext* aBrowsingContext,
+ uint64_t aInnerWindowId, uint64_t aOuterWindowId,
+ bool aInProcess, FieldValues&& aInit);
+
+ ~WindowGlobalParent();
+
+ bool ShouldTrackSiteOriginTelemetry();
+ void FinishAccumulatingPageUseCounters();
+
+ // NOTE: This document principal doesn't reflect possible |document.domain|
+ // mutations which may have been made in the actual document.
+ nsCOMPtr<nsIPrincipal> mDocumentPrincipal;
+ // The principal to use for the content blocking allow list.
+ nsCOMPtr<nsIPrincipal> mDocContentBlockingAllowListPrincipal;
+ nsCOMPtr<nsIURI> mDocumentURI;
+ nsString mDocumentTitle;
+
+ 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();
+
+ nsDataHashtable<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;
+};
+
+} // 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..933443b775
--- /dev/null
+++ b/dom/ipc/WindowGlobalTypes.ipdlh
@@ -0,0 +1,38 @@
+/* -*- 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";
+using refcounted 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.
+ nsIPrincipal principal;
+ nsIURI documentURI;
+
+ bool blockAllMixedContent;
+ bool upgradeInsecureRequests;
+ uint32_t sandboxFlags;
+ CookieJarSettingsArgs cookieJarSettings;
+ uint32_t httpsOnlyStatus;
+ nsITransportSecurityInfo securityInfo;
+};
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/fuzztest/content_parent_ipc_libfuzz.cpp b/dom/ipc/fuzztest/content_parent_ipc_libfuzz.cpp
new file mode 100644
index 0000000000..a506705702
--- /dev/null
+++ b/dom/ipc/fuzztest/content_parent_ipc_libfuzz.cpp
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 8; 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 "gtest/gtest.h"
+
+#include "FuzzingInterface.h"
+#include "ProtocolFuzzer.h"
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/devtools/PHeapSnapshotTempFileHelper.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/gfx/gfxVars.h"
+
+int FuzzingInitContentParentIPC(int* argc, char*** argv) { return 0; }
+
+static int RunContentParentIPCFuzzing(const uint8_t* data, size_t size) {
+ static mozilla::dom::ContentParent* p =
+ mozilla::ipc::ProtocolFuzzerHelper::CreateContentParent(
+ DEFAULT_REMOTE_TYPE);
+
+ static nsTArray<nsCString> ignored = mozilla::ipc::LoadIPCMessageBlacklist(
+ getenv("MOZ_IPC_MESSAGE_FUZZ_BLACKLIST"));
+
+ mozilla::ipc::FuzzProtocol(p, data, size, ignored);
+
+ return 0;
+}
+
+MOZ_FUZZING_INTERFACE_RAW(FuzzingInitContentParentIPC,
+ RunContentParentIPCFuzzing, ContentParentIPC);
diff --git a/dom/ipc/fuzztest/moz.build b/dom/ipc/fuzztest/moz.build
new file mode 100644
index 0000000000..831c9fd9a9
--- /dev/null
+++ b/dom/ipc/fuzztest/moz.build
@@ -0,0 +1,20 @@
+# -*- 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/.
+
+Library("FuzzingContentParentIPC")
+
+LOCAL_INCLUDES += [
+ "/dom/base",
+]
+
+SOURCES += ["content_parent_ipc_libfuzz.cpp"]
+
+include("/ipc/chromium/chromium-config.mozbuild")
+
+FINAL_LIBRARY = "xul-gtest"
+
+# Add libFuzzer configuration directives
+include("/tools/fuzzing/libfuzzer-config.mozbuild")
diff --git a/dom/ipc/jar.mn b/dom/ipc/jar.mn
new file mode 100644
index 0000000000..8cc8826703
--- /dev/null
+++ b/dom/ipc/jar.mn
@@ -0,0 +1,9 @@
+# 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/test-ipc.xhtml (test.xhtml)
+ content/global/remote-test-ipc.js (remote-test.js)
+ 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..6af20323c3
--- /dev/null
+++ b/dom/ipc/jsactor/JSActor.cpp
@@ -0,0 +1,479 @@
+/* -*- 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/ClonedErrorHolder.h"
+#include "mozilla/dom/ClonedErrorHolderBinding.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 "js/Promise.h"
+#include "xpcprivate.h"
+#include "nsFrameMessageManager.h"
+#include "nsICrashReporter.h"
+
+namespace mozilla::dom {
+
+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_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 (auto& query : tmp->mPendingQueries) {
+ CycleCollectionNoteChild(cb, query.GetData().mPromise.get(),
+ "Pending Query Promise");
+ }
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(JSActor)
+
+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.
+ nsDataHashtable<nsUint64HashKey, PendingQuery> pendingQueries;
+ mPendingQueries.SwapElements(pendingQueries);
+ for (auto& entry : pendingQueries) {
+ nsPrintfCString message(
+ "Actor '%s' destroyed before query '%s' was resolved", mName.get(),
+ NS_LossyConvertUTF16toASCII(entry.GetData().mMessageName).get());
+ entry.GetData().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);
+}
+
+/* static */
+bool JSActor::AllowMessage(const JSActorMessageMeta& aMetadata,
+ size_t aDataLength) {
+ // A message includes more than structured clone data, so subtract
+ // 20KB to make it more likely that a message within this bound won't
+ // result in an overly large IPC message.
+ static const size_t kMaxMessageSize =
+ IPC::Channel::kMaximumMessageSize - 20 * 1024;
+ if (aDataLength < kMaxMessageSize) {
+ return true;
+ }
+
+ return false;
+}
+
+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, ErrorResult& aRv) {
+ 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;
+ }
+
+ 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) {
+ 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;
+
+ mPendingQueries.Put(meta.queryId(),
+ PendingQuery{promise, meta.messageName()});
+
+ SendRawMessage(meta, std::move(data), CaptureJSStack(aCx), aRv);
+ 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);
+ 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);
+
+ // 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.GetAndRemove(aMetadata.queryId());
+ if (NS_WARN_IF(!query)) {
+ aRv.ThrowUnknownError("Received reply for non-pending query");
+ return;
+ }
+
+ 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) {
+ 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 (RefPtr<ClonedErrorHolder> ceh =
+ ClonedErrorHolder::Create(aCx, error, IgnoreErrors())) {
+ JS::RootedObject obj(aCx);
+ // Note: We can't use `ToJSValue` here because ClonedErrorHolder isn't
+ // wrapper cached.
+ if (ceh->WrapObject(aCx, nullptr, &obj)) {
+ value.setObject(*obj);
+ } else {
+ 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) {
+ 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);
+ } 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);
+
+ 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..ee2e1da601
--- /dev/null
+++ b/dom/ipc/jsactor/JSActor.h
@@ -0,0 +1,178 @@
+/* -*- 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 "nsDataHashtable.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_SCRIPT_HOLDER_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, 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;
+
+ // Check if a message is so large that IPC will probably crash if we try to
+ // send it. If it is too large, record telemetry about the message.
+ static bool AllowMessage(const JSActorMessageMeta& aMetadata,
+ size_t aDataLength);
+
+ // 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) override;
+
+ void ResolvedCallback(JSContext* aCx,
+ JS::Handle<JS::Value> aValue) 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;
+ nsDataHashtable<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..fda2cf7efc
--- /dev/null
+++ b/dom/ipc/jsactor/JSActorManager.cpp
@@ -0,0 +1,225 @@
+/* -*- 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/JSActorService.h"
+#include "mozilla/dom/PWindowGlobal.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+#include "mozilla/ScopeExit.h"
+#include "mozJSComponentLoader.h"
+#include "jsapi.h"
+#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;
+ }
+
+ bool isParent = nativeActor->GetSide() == mozilla::ipc::ParentSide;
+ auto& side = isParent ? protocol->Parent() : protocol->Child();
+
+ // We're about to construct the actor, so make sure we're in the JSM realm
+ // while importing etc.
+ JSAutoRealm ar(aCx, xpc::PrivilegedJunkScope());
+
+ // Load the module using mozJSComponentLoader.
+ RefPtr<mozJSComponentLoader> loader = mozJSComponentLoader::Get();
+ MOZ_ASSERT(loader);
+
+ // If a module URI was provided, use it to construct an instance of the actor.
+ JS::RootedObject actorObj(aCx);
+ if (side.mModuleURI) {
+ JS::RootedObject global(aCx);
+ JS::RootedObject exports(aCx);
+ aRv = loader->Import(aCx, side.mModuleURI.ref(), &global, &exports);
+ if (aRv.Failed()) {
+ return nullptr;
+ }
+ MOZ_ASSERT(exports, "null exports!");
+
+ // Load the specific property from our module.
+ JS::RootedValue ctor(aCx);
+ nsAutoCString ctorName(aName);
+ ctorName.Append(isParent ? "Parent"_ns : "Child"_ns);
+ 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.Put(aName, RefPtr{actor});
+ return actor.forget();
+}
+
+#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);
+ if (error.Failed()) {
+ CHILD_DIAGNOSTIC_ASSERT(false, "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 (auto& entry : mJSActors) {
+ entry.GetData()->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.
+ nsRefPtrHashtable<nsCStringHashKey, JSActor> actors;
+ mJSActors.SwapElements(actors);
+ for (auto& entry : actors) {
+ CrashReporter::AutoAnnotateCrashReport autoActorName(
+ CrashReporter::Annotation::JSActorName, entry.GetData()->Name());
+ entry.GetData()->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..80f4893b06
--- /dev/null
+++ b/dom/ipc/jsactor/JSActorManager.h
@@ -0,0 +1,94 @@
+/* -*- 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 it's 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);
+
+ /**
+ * 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::HandleObject 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/JSActorService.cpp b/dom/ipc/jsactor/JSActorService.cpp
new file mode 100644
index 0000000000..93b80b87c7
--- /dev/null
+++ b/dom/ipc/jsactor/JSActorService.cpp
@@ -0,0 +1,306 @@
+/* -*- 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());
+
+ auto entry = mWindowActorDescriptors.LookupForAdd(aName);
+ if (entry) {
+ aRv.ThrowNotSupportedError(nsPrintfCString(
+ "'%s' actor is already registered.", PromiseFlatCString(aName).get()));
+ return;
+ }
+
+ // Insert a new entry for the protocol.
+ RefPtr<JSWindowActorProtocol> proto =
+ JSWindowActorProtocol::FromWebIDLOptions(aName, aOptions, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ entry.OrRemove();
+ return;
+ }
+
+ entry.OrInsert([&] { return proto; });
+
+ // 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 (auto& bp : cp->ManagedPBrowserParent()) {
+ for (auto& wgp : bp.GetKey()->ManagedPWindowGlobalParent()) {
+ managers.AppendElement(
+ static_cast<WindowGlobalParent*>(wgp.GetKey()));
+ }
+ }
+ }
+
+ for (auto& wgp :
+ InProcessParent::Singleton()->ManagedPWindowGlobalParent()) {
+ managers.AppendElement(static_cast<WindowGlobalParent*>(wgp.GetKey()));
+ }
+ for (auto& wgc :
+ InProcessChild::Singleton()->ManagedPWindowGlobalChild()) {
+ managers.AppendElement(static_cast<WindowGlobalChild*>(wgc.GetKey()));
+ }
+ } else {
+ for (auto& bc : ContentChild::GetSingleton()->ManagedPBrowserChild()) {
+ for (auto& wgc : bc.GetKey()->ManagedPWindowGlobalChild()) {
+ managers.AppendElement(static_cast<WindowGlobalChild*>(wgc.GetKey()));
+ }
+ }
+ }
+
+ 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.Put(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.Put(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 (auto iter = mWindowActorDescriptors.ConstIter(); !iter.Done();
+ iter.Next()) {
+ aInfos.AppendElement(iter.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 (auto iter = mWindowActorDescriptors.Iter(); !iter.Done(); iter.Next()) {
+ iter.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());
+
+ auto entry = mProcessActorDescriptors.LookupForAdd(aName);
+ if (entry) {
+ aRv.ThrowNotSupportedError(nsPrintfCString(
+ "'%s' actor is already registered.", PromiseFlatCString(aName).get()));
+ return;
+ }
+
+ // Insert a new entry for the protocol.
+ RefPtr<JSProcessActorProtocol> proto =
+ JSProcessActorProtocol::FromWebIDLOptions(aName, aOptions, aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ entry.OrRemove();
+ return;
+ }
+
+ entry.OrInsert([&] { return proto; });
+
+ // 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 (auto iter = mProcessActorDescriptors.ConstIter(); !iter.Done();
+ iter.Next()) {
+ aInfos.AppendElement(iter.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..610d0fc996
--- /dev/null
+++ b/dom/ipc/jsactor/JSActorService.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_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 clsas for both `JSWindowActorProtocol` and `JSProcessActorProtocol`
+ * which can be used by generic code.
+ */
+class JSActorProtocol : public nsISupports {
+ public:
+ struct Sided {
+ Maybe<nsCString> mModuleURI;
+ };
+
+ virtual const Sided& Parent() const = 0;
+ virtual const Sided& Child() const = 0;
+};
+
+} // 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..be0d49743c
--- /dev/null
+++ b/dom/ipc/jsactor/JSProcessActorChild.cpp
@@ -0,0 +1,105 @@
+/* -*- 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_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(JSProcessActorChild, JSActor)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+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;
+ }
+
+ size_t length = 0;
+ if (aData) {
+ length += aData->DataLength();
+ }
+ if (aStack) {
+ length += aStack->DataLength();
+ }
+ if (NS_WARN_IF(!AllowMessage(aMeta, length))) {
+ aRv.ThrowDataCloneError(
+ nsPrintfCString("JSProcessActorChild serialization error: data too "
+ "large, in actor '%s'",
+ PromiseFlatCString(aMeta.actorName()).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.
+ 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->BuildClonedMessageDataForChild(contentChild, *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->BuildClonedMessageDataForChild(contentChild, *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..fc7df73751
--- /dev/null
+++ b/dom/ipc/jsactor/JSProcessActorChild.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_JSProcessActorChild_h
+#define mozilla_dom_JSProcessActorChild_h
+
+#include "mozilla/dom/JSActor.h"
+#include "nsIDOMProcessChild.h"
+
+namespace mozilla {
+namespace dom {
+
+// Placeholder implementation.
+class JSProcessActorChild final : public JSActor {
+ public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_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 dom
+} // namespace mozilla
+
+#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..4b7cc17105
--- /dev/null
+++ b/dom/ipc/jsactor/JSProcessActorParent.cpp
@@ -0,0 +1,109 @@
+/* -*- 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_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(JSProcessActorParent, JSActor)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+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;
+ }
+
+ size_t length = 0;
+ if (aData) {
+ length += aData->DataLength();
+ }
+ if (aStack) {
+ length += aStack->DataLength();
+ }
+ if (NS_WARN_IF(!AllowMessage(aMeta, length))) {
+ aRv.ThrowDataError(nsPrintfCString(
+ "Actor '%s' cannot send message '%s': message too long.",
+ 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->BuildClonedMessageDataForParent(contentParent, *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->BuildClonedMessageDataForParent(contentParent, *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..239efbeb59
--- /dev/null
+++ b/dom/ipc/jsactor/JSProcessActorParent.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_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_SCRIPT_HOLDER_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..58d0051210
--- /dev/null
+++ b/dom/ipc/jsactor/JSProcessActorProtocol.cpp
@@ -0,0 +1,169 @@
+/* -*- 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"
+
+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());
+
+ // Content processes aren't the parent process, so this flag is irrelevant and
+ // not propagated.
+ proto->mIncludeParent = false;
+ proto->mRemoteTypes = aInfo.remoteTypes().Clone();
+ proto->mChild.mModuleURI = aInfo.url();
+ proto->mChild.mObservers = aInfo.observers().Clone();
+
+ return proto.forget();
+}
+
+JSProcessActorInfo JSProcessActorProtocol::ToIPC() {
+ MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
+
+ JSProcessActorInfo info;
+ info.name() = mName;
+ info.remoteTypes() = mRemoteTypes.Clone();
+ info.url() = mChild.mModuleURI;
+ info.observers() = mChild.mObservers.Clone();
+ 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);
+ proto->mIncludeParent = aOptions.mIncludeParent;
+
+ if (aOptions.mRemoteTypes.WasPassed()) {
+ MOZ_ASSERT(aOptions.mRemoteTypes.Value().Length());
+ proto->mRemoteTypes = aOptions.mRemoteTypes.Value();
+ }
+
+ if (aOptions.mParent.WasPassed()) {
+ proto->mParent.mModuleURI.emplace(aOptions.mParent.Value().mModuleURI);
+ }
+ if (aOptions.mChild.WasPassed()) {
+ proto->mChild.mModuleURI.emplace(aOptions.mChild.Value().mModuleURI);
+ }
+
+ if (!aOptions.mChild.WasPassed() && !aOptions.mParent.WasPassed()) {
+ aRv.ThrowNotSupportedError(
+ "No point registering an actor with neither child nor parent "
+ "specifications.");
+ return nullptr;
+ }
+
+ if (aOptions.mChild.WasPassed() &&
+ aOptions.mChild.Value().mObservers.WasPassed()) {
+ proto->mChild.mObservers = aOptions.mChild.Value().mObservers.Value();
+ }
+
+ 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..2230283dcf
--- /dev/null
+++ b/dom/ipc/jsactor/JSProcessActorProtocol.h
@@ -0,0 +1,76 @@
+/* -*- 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;
+
+/**
+ * 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;
+
+ 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..2b533e296d
--- /dev/null
+++ b/dom/ipc/jsactor/JSWindowActorChild.cpp
@@ -0,0 +1,155 @@
+/* -*- 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; }
+
+void JSWindowActorChild::Init(const nsACString& aName,
+ WindowGlobalChild* aManager) {
+ MOZ_ASSERT(!mManager, "Cannot Init() a JSWindowActorChild twice!");
+ SetName(aName);
+ mManager = aManager;
+
+ InvokeCallback(CallbackFunction::ActorCreated);
+}
+
+void JSWindowActorChild::SendRawMessage(
+ const JSActorMessageMeta& aMeta, Maybe<ipc::StructuredCloneData>&& aData,
+ Maybe<ipc::StructuredCloneData>&& aStack, ErrorResult& aRv) {
+ if (NS_WARN_IF(!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;
+ }
+
+ size_t length = 0;
+ if (aData) {
+ length += aData->DataLength();
+ }
+ if (aStack) {
+ length += aStack->DataLength();
+ }
+
+ if (NS_WARN_IF(!AllowMessage(aMeta, length))) {
+ aRv.ThrowDataCloneError(
+ nsPrintfCString("JSWindowActorChild serialization error: data too "
+ "large, in actor '%s'",
+ PromiseFlatCString(aMeta.actorName()).get()));
+ return;
+ }
+
+ // Cross-process case - send data over WindowGlobalChild to other side.
+ ContentChild* cc = ContentChild::GetSingleton();
+ Maybe<ClonedMessageData> msgData;
+ if (aData) {
+ msgData.emplace();
+ if (NS_WARN_IF(!aData->BuildClonedMessageDataForChild(cc, *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->BuildClonedMessageDataForChild(cc, *stackData)) {
+ stackData.reset();
+ }
+ }
+
+ if (NS_WARN_IF(!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_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(JSWindowActorChild, JSActor)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+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..73521c376a
--- /dev/null
+++ b/dom/ipc/jsactor/JSWindowActorChild.h
@@ -0,0 +1,84 @@
+/* -*- 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 {
+namespace dom {
+
+class JSWindowActorChild final : public JSActor {
+ public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_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;
+ 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 dom
+} // namespace mozilla
+
+#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..66db461ae4
--- /dev/null
+++ b/dom/ipc/jsactor/JSWindowActorParent.cpp
@@ -0,0 +1,121 @@
+/* -*- 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; }
+
+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;
+ }
+
+ size_t length = 0;
+ if (aData) {
+ length += aData->DataLength();
+ }
+ if (aStack) {
+ length += aStack->DataLength();
+ }
+
+ if (NS_WARN_IF(!AllowMessage(aMeta, length))) {
+ aRv.ThrowDataCloneError(
+ nsPrintfCString("JSWindowActorParent serialization error: data too "
+ "large, in actor '%s'",
+ PromiseFlatCString(aMeta.actorName()).get()));
+ return;
+ }
+
+ // Cross-process case - send data over WindowGlobalParent to other side.
+ RefPtr<BrowserParent> browserParent = mManager->GetBrowserParent();
+ ContentParent* cp = browserParent->Manager();
+
+ Maybe<ClonedMessageData> msgData;
+ if (aData) {
+ msgData.emplace();
+ if (NS_WARN_IF(!aData->BuildClonedMessageDataForParent(cp, *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->BuildClonedMessageDataForParent(cp, *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_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(JSWindowActorParent, JSActor)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+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..56d6fd3f7d
--- /dev/null
+++ b/dom/ipc/jsactor/JSWindowActorParent.h
@@ -0,0 +1,68 @@
+/* -*- 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 {
+namespace dom {
+
+class JSWindowActorParent final : public JSActor {
+ public:
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_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;
+ 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 dom
+} // namespace mozilla
+
+#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..4d18b02116
--- /dev/null
+++ b/dom/ipc/jsactor/JSWindowActorProtocol.cpp
@@ -0,0 +1,388 @@
+/* -*- 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 "nsContentUtils.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, mURIMatcher)
+
+/* static */ already_AddRefed<JSWindowActorProtocol>
+JSWindowActorProtocol::FromIPC(const JSWindowActorInfo& aInfo) {
+ MOZ_DIAGNOSTIC_ASSERT(XRE_IsContentProcess());
+
+ RefPtr<JSWindowActorProtocol> proto = new JSWindowActorProtocol(aInfo.name());
+ // 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->mRemoteTypes = aInfo.remoteTypes().Clone();
+ proto->mMessageManagerGroups = aInfo.messageManagerGroups().Clone();
+ proto->mChild.mModuleURI = aInfo.url();
+
+ 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());
+ }
+ }
+
+ proto->mChild.mObservers = aInfo.observers().Clone();
+ return proto.forget();
+}
+
+JSWindowActorInfo JSWindowActorProtocol::ToIPC() {
+ MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
+
+ JSWindowActorInfo info;
+ info.name() = mName;
+ info.allFrames() = mAllFrames;
+ info.matches() = mMatches.Clone();
+ info.remoteTypes() = mRemoteTypes.Clone();
+ info.messageManagerGroups() = mMessageManagerGroups.Clone();
+ info.url() = mChild.mModuleURI;
+
+ 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());
+ }
+ }
+
+ info.observers() = mChild.mObservers.Clone();
+ 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);
+ 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.mRemoteTypes.WasPassed()) {
+ MOZ_ASSERT(aOptions.mRemoteTypes.Value().Length());
+ proto->mRemoteTypes = aOptions.mRemoteTypes.Value();
+ }
+
+ if (aOptions.mMessageManagerGroups.WasPassed()) {
+ proto->mMessageManagerGroups = aOptions.mMessageManagerGroups.Value();
+ }
+
+ if (aOptions.mParent.WasPassed()) {
+ proto->mParent.mModuleURI.emplace(aOptions.mParent.Value().mModuleURI);
+ }
+ if (aOptions.mChild.WasPassed()) {
+ proto->mChild.mModuleURI.emplace(aOptions.mChild.Value().mModuleURI);
+ }
+
+ if (!aOptions.mChild.WasPassed() && !aOptions.mParent.WasPassed()) {
+ aRv.ThrowNotSupportedError(
+ "No point registering an actor with neither child nor parent "
+ "specifications.");
+ return nullptr;
+ }
+
+ // 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());
+ }
+ }
+ }
+
+ if (aOptions.mChild.WasPassed() &&
+ aOptions.mChild.Value().mObservers.WasPassed()) {
+ proto->mChild.mObservers = aOptions.mChild.Value().mObservers.Value();
+ }
+
+ 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;
+ }
+
+ // 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 our event listener & call it.
+ JS::Rooted<JSObject*> global(jsapi.cx(),
+ 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",
+ /* 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::MatchPatternSet* JSWindowActorProtocol::GetURIMatcher() {
+ // If we've already created the pattern set, return it.
+ if (mURIMatcher || mMatches.IsEmpty()) {
+ return mURIMatcher;
+ }
+
+ // Constructing the MatchPatternSet requires a JS environment to be run in.
+ // We can construct it here in the JSM scope, as we will be keeping it around.
+ AutoJSAPI jsapi;
+ MOZ_ALWAYS_TRUE(jsapi.Init(xpc::PrivilegedJunkScope()));
+ GlobalObject global(jsapi.cx(), xpc::PrivilegedJunkScope());
+
+ nsTArray<OwningStringOrMatchPattern> patterns;
+ patterns.SetCapacity(mMatches.Length());
+ for (nsString& s : mMatches) {
+ auto entry = patterns.AppendElement();
+ entry->SetAsString() = s;
+ }
+
+ MatchPatternOptions matchPatternOptions;
+ // Make MatchPattern's mSchemes create properly.
+ matchPatternOptions.mRestrictSchemes = false;
+ mURIMatcher = extensions::MatchPatternSet::Constructor(
+ global, patterns, matchPatternOptions, IgnoreErrors());
+ 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::MatchPatternSet* 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..993e6b4e6e
--- /dev/null
+++ b/dom/ipc/jsactor/JSWindowActorProtocol.h
@@ -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/. */
+
+#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;
+
+/**
+ * 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;
+ };
+
+ 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::MatchPatternSet* 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;
+
+ ParentSide mParent;
+ ChildSide mChild;
+
+ RefPtr<extensions::MatchPatternSet> 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..caa1bfc88e
--- /dev/null
+++ b/dom/ipc/moz.build
@@ -0,0 +1,247 @@
+# -*- 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"]
+
+XPIDL_SOURCES += [
+ "nsIDOMProcessChild.idl",
+ "nsIDOMProcessParent.idl",
+ "nsIHangReport.idl",
+]
+
+XPIDL_MODULE = "dom"
+
+EXTRA_JS_MODULES += [
+ "ManifestMessagesChild.jsm",
+]
+
+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",
+ "CoalescedWheelData.h",
+ "ContentChild.h",
+ "ContentParent.h",
+ "ContentProcess.h",
+ "ContentProcessManager.h",
+ "CSPMessageUtils.h",
+ "DocShellMessageUtils.h",
+ "EffectsInfo.h",
+ "FilePickerParent.h",
+ "InProcessChild.h",
+ "InProcessParent.h",
+ "MaybeDiscarded.h",
+ "MemoryReportRequest.h",
+ "NativeThreadId.h",
+ "PermissionMessageUtils.h",
+ "ProcessActor.h",
+ "PropertyBagUtils.h",
+ "ReferrerInfoUtils.h",
+ "RefMessageBodyService.h",
+ "RemoteBrowser.h",
+ "RemoteType.h",
+ "RemoteWebProgress.h",
+ "RemoteWebProgressRequest.h",
+ "SharedMessageBody.h",
+ "TabContext.h",
+ "TabMessageTypes.h",
+ "TabMessageUtils.h",
+ "URLClassifierChild.h",
+ "URLClassifierParent.h",
+ "UserActivationIPCUtils.h",
+ "VsyncChild.h",
+ "VsyncParent.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",
+ "CoalescedMouseData.cpp",
+ "CoalescedWheelData.cpp",
+ "ColorPickerParent.cpp",
+ "ContentParent.cpp",
+ "ContentProcess.cpp",
+ "ContentProcessManager.cpp",
+ "CSPMessageUtils.cpp",
+ "DocShellMessageUtils.cpp",
+ "FilePickerParent.cpp",
+ "InProcessImpl.cpp",
+ "MemMapSnapshot.cpp",
+ "MemoryReportRequest.cpp",
+ "MMPrinter.cpp",
+ "PermissionMessageUtils.cpp",
+ "PreallocatedProcessManager.cpp",
+ "ProcessActor.cpp",
+ "ProcessPriorityManager.cpp",
+ "PropertyBagUtils.cpp",
+ "ReferrerInfoUtils.cpp",
+ "RefMessageBodyService.cpp",
+ "RemoteBrowser.cpp",
+ "RemoteWebProgress.cpp",
+ "RemoteWebProgressRequest.cpp",
+ "SharedMap.cpp",
+ "SharedMessageBody.cpp",
+ "SharedStringMap.cpp",
+ "StructuredCloneData.cpp",
+ "TabContext.cpp",
+ "TabMessageUtils.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",
+ "VsyncChild.cpp",
+ "VsyncParent.cpp",
+]
+
+PREPROCESSED_IPDL_SOURCES += [
+ "PBrowser.ipdl",
+ "PBrowserBridge.ipdl",
+ "PContent.ipdl",
+]
+
+IPDL_SOURCES += [
+ "DOMTypes.ipdlh",
+ "MemoryReportTypes.ipdlh",
+ "PColorPicker.ipdl",
+ "PContentPermission.ipdlh",
+ "PContentPermissionRequest.ipdl",
+ "PCycleCollectWithLogs.ipdl",
+ "PFilePicker.ipdl",
+ "PInProcess.ipdl",
+ "PLoginReputation.ipdl",
+ "PPluginWidget.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/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["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.ini",
+ "tests/JSProcessActor/browser.ini",
+ "tests/JSWindowActor/browser.ini",
+]
+
+MOCHITEST_CHROME_MANIFESTS += ["tests/chrome.ini"]
+MOCHITEST_MANIFESTS += ["tests/mochitest.ini"]
+XPCSHELL_TESTS_MANIFESTS += ["tests/xpcshell.ini"]
+
+CXXFLAGS += CONFIG["TK_CFLAGS"]
+
+if CONFIG["CC_TYPE"] in ("clang", "gcc"):
+ CXXFLAGS += ["-Wno-error=shadow"]
+
+if CONFIG["FUZZING"] and CONFIG["FUZZING_INTERFACES"]:
+ TEST_DIRS += ["fuzztest"]
+
+# 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..5c6e04fee9
--- /dev/null
+++ b/dom/ipc/nsIDOMProcessChild.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 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);
+
+ /** 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..a96f8eb3c0
--- /dev/null
+++ b/dom/ipc/nsIDOMProcessParent.idl
@@ -0,0 +1,57 @@
+/* -*- 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);
+
+ /** 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..cf3ecdf984
--- /dev/null
+++ b/dom/ipc/nsIHangReport.idl
@@ -0,0 +1,76 @@
+/* -*- 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
+{
+ const unsigned long SLOW_SCRIPT = 1;
+ const unsigned long PLUGIN_HANG = 2;
+
+ // The type of hang being reported: SLOW_SCRIPT or PLUGIN_HANG.
+ readonly attribute unsigned long hangType;
+
+ // For SLOW_SCRIPT reports, these fields contain information about the
+ // slow script.
+ // Only valid for SLOW_SCRIPT reports.
+ readonly attribute Element scriptBrowser;
+ readonly attribute ACString scriptFileName;
+ // Duration of the hang so far.
+ readonly attribute double hangDuration;
+ readonly attribute AString addonId;
+
+ // For PLUGIN_HANGs, this field contains information about the plugin.
+ // Only valid for PLUGIN_HANG reports.
+ readonly attribute ACString pluginName;
+
+ // 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.
+ // Only valid for SLOW_SCRIPT reports.
+ void terminateScript();
+
+ // Terminate all scripts on the global that triggered the slow script
+ // warning.
+ // Only valid for SLOW_SCRIPT reports.
+ void terminateGlobal();
+
+ // Terminate the plugin if it is still hung.
+ // Only valid for PLUGIN_HANG reports.
+ void terminatePlugin();
+
+ // Ask the content process to start up the slow script debugger.
+ // Only valid for SLOW_SCRIPT reports.
+ 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.
+ // Only valid for SLOW_SCRIPT reports.
+ void endStartingDebugger();
+
+ // Inquire whether the report is for a content process loaded by the given
+ // frameloader.
+ bool isReportForBrowser(in FrameLoader aFrameLoader);
+};
diff --git a/dom/ipc/remote-test.js b/dom/ipc/remote-test.js
new file mode 100644
index 0000000000..395aef8e47
--- /dev/null
+++ b/dom/ipc/remote-test.js
@@ -0,0 +1,55 @@
+/* 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/. */
+
+/* eslint-env mozilla/frame-script */
+
+dump("Loading remote script!\n");
+dump(content + "\n");
+
+var cpm = Cc["@mozilla.org/childprocessmessagemanager;1"].getService();
+cpm.addMessageListener("cpm-async", function(m) {
+ cpm.sendSyncMessage("ppm-sync");
+ dump(content.document.documentElement);
+ cpm.sendAsyncMessage("ppm-async");
+});
+
+var dshell = content.docShell.sameTypeRootTreeItem.QueryInterface(
+ Ci.nsIDocShell
+);
+
+addEventListener(
+ "click",
+ function(e) {
+ dump(e.target + "\n");
+ if (
+ ChromeUtils.getClassName(e.target) === "HTMLAnchorElement" &&
+ dshell == docShell
+ ) {
+ var retval = docShell.messageManager.sendSyncMessage("linkclick", {
+ href: e.target.href,
+ });
+ dump(uneval(retval[0]) + "\n");
+ // Test here also that both retvals are the same
+ sendAsyncMessage(
+ "linkclick-reply-object",
+ uneval(retval[0]) == uneval(retval[1]) ? retval[0] : ""
+ );
+ }
+ },
+ true
+);
+
+addMessageListener("chrome-message", function(m) {
+ dump(uneval(m.json) + "\n");
+ sendAsyncMessage("chrome-message-reply", m.json);
+});
+
+addMessageListener("speed-test-start", function(m) {
+ // eslint-disable-next-line no-empty
+ while (sendSyncMessage("speed-test")[0].message != "done") {}
+});
+
+addMessageListener("async-echo", function(m) {
+ sendAsyncMessage(m.name);
+});
diff --git a/dom/ipc/test.xhtml b/dom/ipc/test.xhtml
new file mode 100644
index 0000000000..6f6cea9c7b
--- /dev/null
+++ b/dom/ipc/test.xhtml
@@ -0,0 +1,259 @@
+<?xml version="1.0"?>
+<!-- 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/. -->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ width="800" height="800" orient="vertical" onload="initRemoteFrameScript();">
+ <script>
+
+ function dumpClientRect(r) {
+ dump(r.left + "," + r.top + "," + r.right + "," +
+ r.bottom + "," + r.width + "," + r.height + "\n");
+ }
+
+ function handleMozAfterPaint(e) {
+ return;
+ dump(e.type + "\n")
+ var rects = e.clientRects;
+ var i;
+ dump("\tclientRects:\n");
+ for (i = 0; i < rects.length; ++i) {
+ var r = rects.item(i);
+ dump("\t\t");
+ dumpClientRect(rects.item(i));
+ }
+
+ dump("\tboundingClientRect\n\t\t");
+ dumpClientRect(e.boundingClientRect);
+
+ var paintRequests = e.paintRequests;
+ dump("\tpaintRequests\n");
+ for (i = 0; i < paintRequests.length; ++i) {
+ var pr = paintRequests.item(i);
+ dump("\t\t");
+ dumpClientRect(pr.clientRect);
+ if (pr.reason)
+ dump("\t\t" + pr.reason + "\n");
+ }
+ }
+
+ function handleMozScrolledAreaChanged(e) {
+ return;
+ dump(e.type + "\n");
+ dump("\t" + e.x + "," + e.y + "," + e.width + "," + e.height + "\n");
+ }
+
+ function restart() {
+ var y = document.getElementById('page');
+ var p = y.parentNode;
+ p.removeChild(y);
+ p.appendChild(y);
+
+ var fl = y.frameLoader;
+ fl.activateFrameEvent("MozAfterPaint", true);
+ fl.activateFrameEvent("MozScrolledAreaChanged", true);
+ y.addEventListener("MozAfterPaint", handleMozAfterPaint, true);
+ y.addEventListener("MozScrolledAreaChanged", handleMozScrolledAreaChanged, true);
+ }
+
+ function loadURL(url) {
+ document.getElementById('page').setAttribute('src', url);
+ }
+
+ function randomClick() {
+ // First focus the remote frame, then dispatch click. This way remote frame gets focus before
+ // mouse event.
+ document.getElementById('page').focus();
+ var frameLoader = document.getElementById('page').frameLoader;
+ var x = parseInt(Math.random() * 100);
+ var y = parseInt(Math.random() * 100);
+ frameLoader.sendCrossProcessMouseEvent("mousedown", x, y, 0, 1, 0);
+ frameLoader.sendCrossProcessMouseEvent("mouseup", x, y, 0, 1, 0);
+ }
+
+ function openWindow() {
+ window.open('chrome://global/content/test-ipc.xhtml', '_blank', 'chrome,resizable,width=800,height=800');
+ }
+
+ function closeWindow() {
+ window.close();
+ }
+
+ function initRemoteFrameScript() {
+ // 1. Test that loading a script works, and that accessing process level mm and
+ // global mm works.
+ var ppm = Components.classes["@mozilla.org/parentprocessmessagemanager;1"]
+ .getService();
+ var gm = Components.classes["@mozilla.org/globalmessagemanager;1"]
+ .getService();
+ var cpm = Components.classes["@mozilla.org/childprocessmessagemanager;1"]
+ .getService();
+
+ if (ppm.childCount != 2) {
+ alert("Should have two child processes!");
+ }
+ var childprocessmm = ppm.getChildAt(1); // 0 is the in-process child process mm
+
+ childprocessmm.addMessageListener("ppm-sync",
+ function(m) {
+ if (m.target != childprocessmm) alert("Wrong target!");
+ document.getElementById("messageLog").value += "[SYNC1 PPM]";
+ }
+ );
+
+ ppm.addMessageListener("ppm-sync",
+ function(m) {
+ // Check that global process message manager gets the per-process mm as target.
+ if (m.target != childprocessmm) alert("Wrong target!");
+ document.getElementById("messageLog").value += "[SYNC2 PPM]";
+ }
+ );
+ childprocessmm.addMessageListener("ppm-async",
+ function(m) {
+ if (m.target != childprocessmm) alert("Wrong target!");
+ document.getElementById("messageLog").value += "[ASYNC1 PPM]";
+ }
+ );
+ ppm.addMessageListener("ppm-async",
+ function(m) {
+ // Check that global process message manager gets the per-process mm as target.
+ if (m.target != childprocessmm) alert("Wrong target!");
+ document.getElementById("messageLog").value += "[ASYNC2 PPM]";
+ }
+ );
+ messageManager.loadFrameScript("chrome://global/content/remote-test-ipc.js", true);
+ ppm.sendAsyncMessage("cpm-async");
+
+ // 2. Test that adding message listener works, and that receiving a sync message works.
+ messageManager.addMessageListener("linkclick",
+ function(m) {
+ // This checks that json sending works in sync messages.
+ document.getElementById("messageLog").value = m.name + ": " + m.json.href;
+ return { message: "linkclick-received" };
+ });
+
+ // 3. Test that returning multiple json results works.
+ messageManager.addMessageListener("linkclick",
+ function(m) {
+ return { message: "linkclick-received" };
+ });
+
+ // 4. Test that receiving an async message works.
+ // Test also that sending async message to content works.
+ // Test also that { receiveMessage: function(m) {} } works.
+ messageManager.addMessageListener("linkclick-reply-object",
+ { foobarObjectVar: true,
+ receiveMessage: function(m) {
+ var s = (m.json.message == "linkclick-received") &&
+ (this.foobarObjectVar) ? "PASS" : "FAIL";
+ messageManager.broadcastAsyncMessage("chrome-message", { ok : s } );
+ }
+ }
+ );
+
+ // 5. Final test to check that everything went ok.
+ messageManager.addMessageListener("chrome-message-reply",
+ function(m) {
+ // Check that 'this' and .target values are handled correctly
+ if (m.target == document.getElementById("page") &&
+ this == messageManager) {
+ // Check that the message properties are enumerable.
+ var hasName = false;
+ var hasSync = false;
+ var hasJSON = false;
+ for (i in m) {
+ if (i == "name") {
+ hasName = true;
+ } else if (i == "sync") {
+ hasSync = true;
+ } else if (i == "json") {
+ hasJSON = true;
+ }
+ }
+ if (hasName && hasSync && hasJSON) {
+ document.getElementById("messageLog").value += ", " + m.json.ok;
+ }
+ }
+ });
+ }
+
+ var speedTestStartTime = 0;
+ var speedTestCount = 0;
+ function messageSpeed() {
+ speedTestCount = 0;
+ messageManager.addMessageListener("speed-test", speedHandler);
+ messageManager.broadcastAsyncMessage("speed-test-start");
+ }
+
+ function speedHandler() {
+ if (!speedTestCount) {
+ speedTestStartTime = new Date().getTime();
+ }
+ if (++speedTestCount == 1000) {
+ setTimeout("alert('" + speedTestCount + " in " + (new Date().getTime() - speedTestStartTime) + "ms')", 0);
+ return { message: "done" };
+ }
+ return { message: "continue" };
+ }
+
+ var addRemoveTestCount = 0;
+
+ function echoListener() {
+ if (++addRemoveTestCount == 1) {
+ alert("Expected echo message");
+ messageManager.removeMessageListener("async-echo", echoListener);
+ messageManager.broadcastAsyncMessage("async-echo");
+ return;
+ }
+ alert("Unexpected echo message");
+ }
+
+ function listenerAddRemove() {
+ addRemoveTestCount = 0;
+ messageManager.addMessageListener("async-echo", echoListener);
+ messageManager.broadcastAsyncMessage("async-echo");
+ }
+
+ var MozAfterPaintCount = 0;
+ function enableMozAfterPaint() {
+ messageManager.addMessageListener("MozAfterPaint",
+ function(m) {
+ document.getElementById("messageLog").value = m.name + "[" + (++MozAfterPaintCount) + "]";
+ });
+ messageManager.loadFrameScript("data:,addEventListener('MozAfterPaint', function(e) { sendAsyncMessage('MozAfterPaint'); },true);", false);
+ }
+
+ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+ </script>
+
+ <toolbar id="controls">
+ <toolbarbutton label="Back"/>
+ <toolbarbutton label="Forward"/>
+ <html:input onchange="loadURL(this.value)" style="-moz-box-flex: 1" id="URL"/>
+ </toolbar>
+ <toolbar>
+ <toolbarbutton onclick="restart()" label="Recover"/>
+ <toolbarbutton onclick="randomClick()" label="random click"/>
+ <toolbarbutton onclick="messageSpeed()" label="test message handling speed"/>
+ <toolbarbutton onclick="listenerAddRemove()" label="test listener add/remove"/>
+ <toolbarbutton onclick="enableMozAfterPaint()" label="MozAfterPaint"/>
+ <toolbarbutton onclick="openWindow()" label="open new window"/>
+ <toolbarbutton onclick="closeWindow()" label="close this window"/>
+ </toolbar>
+ <toolbar><label value="Load a script (URL) to content process:"/>
+ <html:input style="-moz-box-flex: 1" id="script"/><button
+ label="send" oncommand="document.getElementById('page')
+ .frameLoader.messageManager
+ .loadFrameScript(this.previousSibling.value, false);"/>
+ </toolbar>
+ <toolbar>
+ <label value="Eval script in chrome context"/>
+ <html:input style="-moz-box-flex: 1"/><button label="run" oncommand="eval(this.previousSibling.value); // eslint-disable-line no-eval"/>
+ </toolbar>
+
+ <browser type="content" src="http://www.google.com/" flex="1" id="page" remote="true"/>
+ <label id="messageLog" value="" crop="center"/>
+</window>
diff --git a/dom/ipc/tests/.eslintrc.js b/dom/ipc/tests/.eslintrc.js
new file mode 100644
index 0000000000..4e9d6a74a8
--- /dev/null
+++ b/dom/ipc/tests/.eslintrc.js
@@ -0,0 +1,9 @@
+"use strict";
+
+module.exports = {
+ extends: [
+ "plugin:mozilla/browser-test",
+ "plugin:mozilla/mochitest-test",
+ "plugin:mozilla/xpcshell-test",
+ ],
+};
diff --git a/dom/ipc/tests/JSProcessActor/browser.ini b/dom/ipc/tests/JSProcessActor/browser.ini
new file mode 100644
index 0000000000..c3d8645a33
--- /dev/null
+++ b/dom/ipc/tests/JSProcessActor/browser.ini
@@ -0,0 +1,11 @@
+[DEFAULT]
+support-files =
+ head.js
+
+[browser_getActor.js]
+[browser_getActor_filter.js]
+[browser_observer_notification.js]
+[browser_registerProcessActor.js]
+[browser_sendAsyncMessage.js]
+[browser_sendQuery.js]
+
diff --git a/dom/ipc/tests/JSProcessActor/browser_getActor.js b/dom/ipc/tests/JSProcessActor/browser_getActor.js
new file mode 100644
index 0000000000..e972eaaac9
--- /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..3d5c620d16
--- /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..dfe92ad240
--- /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..e57f762744
--- /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() {
+ SimpleTest.doesThrow(
+ () =>
+ ChromeUtils.registerContentActor(
+ "TestProcessActor",
+ processActorOptions
+ ),
+ "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..f81d4ddbee
--- /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..69fe881367
--- /dev/null
+++ b/dom/ipc/tests/JSProcessActor/browser_sendQuery.js
@@ -0,0 +1,82 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+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) {
+ 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.jsm:33:31\n" +
+ asyncStack,
+ "Error should have the correct stack"
+ );
+ },
+});
+
+declTest("sendQuery Exception", {
+ async test(browser) {
+ 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.jsm:36:22\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/head.js b/dom/ipc/tests/JSProcessActor/head.js
new file mode 100644
index 0000000000..86f35f4c57
--- /dev/null
+++ b/dom/ipc/tests/JSProcessActor/head.js
@@ -0,0 +1,66 @@
+/* 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 = {
+ parent: {
+ moduleURI: "resource://testing-common/TestProcessActorParent.jsm",
+ },
+ child: {
+ moduleURI: "resource://testing-common/TestProcessActorChild.jsm",
+ observers: ["test-js-content-actor-child-observer"],
+ },
+};
+
+function promiseNotification(aNotification) {
+ const { Services } = ChromeUtils.import(
+ "resource://gre/modules/Services.jsm"
+ );
+ 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) {
+ let { url = "about:blank", includeParent = false, remoteTypes, test } = cfg;
+
+ // Build the actor options object which will be used to register & unregister
+ // our process actor.
+ let actorOptions = {
+ parent: Object.assign({}, processActorOptions.parent),
+ child: Object.assign({}, processActorOptions.child),
+ };
+ actorOptions.includeParent = includeParent;
+ if (remoteTypes !== undefined) {
+ actorOptions.remoteTypes = remoteTypes;
+ }
+
+ // 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));
+ });
+ } 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.ini b/dom/ipc/tests/JSWindowActor/browser.ini
new file mode 100644
index 0000000000..fe07d9a97d
--- /dev/null
+++ b/dom/ipc/tests/JSWindowActor/browser.ini
@@ -0,0 +1,19 @@
+[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]
+[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]
diff --git a/dom/ipc/tests/JSWindowActor/browser_contentWindow.js b/dom/ipc/tests/JSWindowActor/browser_contentWindow.js
new file mode 100644
index 0000000000..cfdc5b114f
--- /dev/null
+++ b/dom/ipc/tests/JSWindowActor/browser_contentWindow.js
@@ -0,0 +1,47 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+declTest("contentWindow null when inner window inactive", {
+ matches: [TEST_URL + "*"],
+ url: TEST_URL + "?1",
+
+ async test(browser) {
+ {
+ let parent = browser.browsingContext.currentWindowGlobal;
+ let actorParent = parent.getActor("TestWindow");
+
+ await actorParent.sendQuery("storeActor");
+ }
+
+ {
+ let url = TEST_URL + "?2";
+ let loaded = BrowserTestUtils.browserLoaded(browser, false, url);
+ await BrowserTestUtils.loadURI(browser, url);
+ await loaded;
+ }
+
+ let parent = browser.browsingContext.currentWindowGlobal;
+ let actorParent = parent.getActor("TestWindow");
+
+ let result = await actorParent.sendQuery("checkActor");
+ if (SpecialPowers.useRemoteSubframes) {
+ is(
+ result.status,
+ "error",
+ "Should get an error when bfcache is disabled for Fission"
+ );
+ is(
+ result.errorType,
+ "InvalidStateError",
+ "Should get an InvalidStateError without bfcache"
+ );
+ } else {
+ 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..a1ef293cc0
--- /dev/null
+++ b/dom/ipc/tests/JSWindowActor/browser_crash_report.js
@@ -0,0 +1,112 @@
+/* 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..25feb47179
--- /dev/null
+++ b/dom/ipc/tests/JSWindowActor/browser_destroy_callbacks.js
@@ -0,0 +1,194 @@
+/* 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..1874a0a174
--- /dev/null
+++ b/dom/ipc/tests/JSWindowActor/browser_event_listener.js
@@ -0,0 +1,43 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+declTest("test event triggering actor creation", {
+ async test(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>`;
+ });
+
+ // Wait for the observer notification.
+ let observePromise = new Promise(resolve => {
+ const TOPIC = "test-js-window-actor-parent-event";
+ Services.obs.addObserver(function obs(subject, topic, data) {
+ is(topic, TOPIC, "topic matches");
+
+ Services.obs.removeObserver(obs, TOPIC);
+ resolve({ subject, data });
+ }, TOPIC);
+ });
+
+ // Click on the select to show the dropdown.
+ await BrowserTestUtils.synthesizeMouseAtCenter("#testSelect", {}, 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"
+ );
+ },
+});
diff --git a/dom/ipc/tests/JSWindowActor/browser_getActor.js b/dom/ipc/tests/JSWindowActor/browser_getActor.js
new file mode 100644
index 0000000000..53205fc7d9
--- /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..7ee938dddb
--- /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..ae8c40f781
--- /dev/null
+++ b/dom/ipc/tests/JSWindowActor/browser_observer_notification.js
@@ -0,0 +1,111 @@
+/* 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-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", {
+ 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", {
+ 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", {
+ 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..95e1a0c422
--- /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..838fa653b4
--- /dev/null
+++ b/dom/ipc/tests/JSWindowActor/browser_registerWindowActor.js
@@ -0,0 +1,12 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+declTest("double register", {
+ async test() {
+ SimpleTest.doesThrow(
+ () => ChromeUtils.registerWindowActor("TestWindow", windowActorOptions),
+ "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..077732c45e
--- /dev/null
+++ b/dom/ipc/tests/JSWindowActor/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;
+ 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;
+ });
+ },
+});
diff --git a/dom/ipc/tests/JSWindowActor/browser_sendQuery.js b/dom/ipc/tests/JSWindowActor/browser_sendQuery.js
new file mode 100644
index 0000000000..149744a2fc
--- /dev/null
+++ b/dom/ipc/tests/JSWindowActor/browser_sendQuery.js
@@ -0,0 +1,117 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+"use strict";
+
+const ERROR_LINE_NUMBER = 41;
+const EXCEPTION_LINE_NUMBER = ERROR_LINE_NUMBER + 3;
+
+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) {
+ 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.jsm:${ERROR_LINE_NUMBER}:31\n` +
+ asyncStack,
+ "Error should have the correct stack"
+ );
+ },
+});
+
+declTest("sendQuery Exception", {
+ async test(browser) {
+ 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.jsm:${EXCEPTION_LINE_NUMBER}:22\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/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..a1a98f66e0
--- /dev/null
+++ b/dom/ipc/tests/JSWindowActor/head.js
@@ -0,0 +1,71 @@
+/* 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 = {
+ parent: {
+ moduleURI: "resource://testing-common/TestWindowParent.jsm",
+ },
+ child: {
+ moduleURI: "resource://testing-common/TestWindowChild.jsm",
+
+ events: {
+ mozshowdropdown: {},
+ },
+
+ observers: ["test-js-window-actor-child-observer", "audio-playback"],
+ },
+};
+
+function declTest(name, cfg) {
+ let {
+ url = "about:blank",
+ allFrames = false,
+ includeChrome = false,
+ matches,
+ remoteTypes,
+ messageManagerGroups,
+ test,
+ } = cfg;
+
+ // Build the actor options object which will be used to register & unregister
+ // our window actor.
+ let actorOptions = {
+ parent: Object.assign({}, windowActorOptions.parent),
+ child: Object.assign({}, windowActorOptions.child),
+ };
+ 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));
+ });
+ } 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..cf50371c8a
--- /dev/null
+++ b/dom/ipc/tests/blob_verify.sjs
@@ -0,0 +1,20 @@
+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 = [];
+ 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.ini b/dom/ipc/tests/browser.ini
new file mode 100644
index 0000000000..aa60226764
--- /dev/null
+++ b/dom/ipc/tests/browser.ini
@@ -0,0 +1,22 @@
+[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]
+skip-if = os != "win" # The Process Priority Manager is only enabled for Windows so far. Bug 1522879.
+[browser_crash_oopiframe.js]
+skip-if = !fission || !crashreporter || verify
+[browser_domainPolicy.js]
+[browser_memory_distribution_telemetry.js]
+skip-if = true || !e10s # This is an e10s only probe, but the test is currently broken. See Bug 1449991
+[browser_cancel_content_js.js]
+skip-if = !e10s
+[browser_bug1646088.js]
+support-files = file_dummy.html
+skip-if = !e10s
diff --git a/dom/ipc/tests/browser_CrashService_crash.js b/dom/ipc/tests/browser_CrashService_crash.js
new file mode 100644
index 0000000000..9504d68ae7
--- /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.jsm).
+
+/* 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.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..b74b2b84cd
--- /dev/null
+++ b/dom/ipc/tests/browser_ProcessPriorityManager.js
@@ -0,0 +1,520 @@
+/* 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;
+
+/**
+ * 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.priorityMap = new WeakMap();
+ this.priorityMap.set(
+ this.tabbrowser.selectedBrowser,
+ PROCESS_PRIORITY_FOREGROUND
+ );
+ this.noChangeBrowsers = new WeakMap();
+ 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.window = null;
+ }
+
+ /**
+ * Returns a Promise that resolves when a particular <browser>
+ * has its content process reach a particular priority. Will
+ * eventually time out if that priority is never reached.
+ *
+ * @param browser (<browser>)
+ * The <browser> that we expect to change priority.
+ * @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(browser, expectedPriority) {
+ return TestUtils.waitForCondition(() => {
+ let currentPriority = this.priorityMap.get(browser);
+ if (currentPriority == expectedPriority) {
+ Assert.ok(
+ true,
+ `Browser at ${browser.currentURI.spec} reached expected ` +
+ `priority: ${currentPriority}`
+ );
+ return true;
+ }
+ return false;
+ }, `Waiting for browser at ${browser.currentURI.spec} to reach priority ` + expectedPriority);
+ }
+
+ /**
+ * Returns a Promise that resolves after a duration of
+ * WAIT_FOR_CHANGE_TIME_MS. During that time, if the passed browser
+ * changes priority, a test failure will be registered.
+ *
+ * @param browser (<browser>)
+ * The <browser> that we expect to change priority.
+ * @return Promise
+ * @resolves undefined
+ * Once the WAIT_FOR_CHANGE_TIME_MS duration has passed.
+ */
+ async ensureNoPriorityChange(browser) {
+ this.noChangeBrowsers.set(browser, null);
+ // eslint-disable-next-line mozilla/no-arbitrary-setTimeout
+ await new Promise(resolve => setTimeout(resolve, WAIT_FOR_CHANGE_TIME_MS));
+ let priority = this.noChangeBrowsers.get(browser);
+ Assert.equal(
+ priority,
+ null,
+ `Should have seen no process priority change for a browser at ${browser.currentURI.spec}`
+ );
+ this.noChangeBrowsers.delete(browser);
+ }
+
+ /**
+ * Makes sure that a particular foreground browser has been
+ * registered in the priority map. This is needed because browsers are
+ * only registered when their priorities change - and if a browser's
+ * priority never changes during a test, then they wouldn't be registered.
+ *
+ * The passed browser must be a foreground browser, since it's assumed that
+ * the associated content process is running with foreground priority.
+ *
+ * @param browser (browser)
+ * A _foreground_ browser.
+ */
+ ensureForegroundRegistered(browser) {
+ if (!this.priorityMap.has(browser)) {
+ this.priorityMap.set(browser, PROCESS_PRIORITY_FOREGROUND);
+ }
+ }
+
+ /**
+ * Synchronously returns the priority of a particular browser's
+ * content process.
+ *
+ * @param browser (browser)
+ * The browser to get the content process priority for.
+ * @return String
+ * The priority that the browser's content process is at.
+ */
+ currentPriority(browser) {
+ return this.priorityMap.get(browser);
+ }
+
+ /**
+ * 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);
+ for (let browser of this.tabbrowser.browsers) {
+ if (browser.frameLoader.childID == childID) {
+ info(
+ `Browser at: ${browser.currentURI.spec} transitioning to ${priority}`
+ );
+ if (this.noChangeBrowsers.has(browser)) {
+ this.noChangeBrowsers.set(browser, priority);
+ }
+ this.priorityMap.set(browser, priority);
+ }
+ }
+ }
+}
+
+let gTabPriorityWatcher;
+
+add_task(async function setup() {
+ // 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."
+ );
+
+ gTabPriorityWatcher.ensureForegroundRegistered(fromBrowser);
+
+ let fromPromise;
+ if (
+ gTabPriorityWatcher.currentPriority(fromBrowser) == fromTabExpectedPriority
+ ) {
+ fromPromise = gTabPriorityWatcher.ensureNoPriorityChange(fromBrowser);
+ } else {
+ fromPromise = gTabPriorityWatcher.waitForPriorityChange(
+ fromBrowser,
+ fromTabExpectedPriority
+ );
+ }
+
+ let toPromise;
+ if (
+ gTabPriorityWatcher.currentPriority(toBrowser) ==
+ PROCESS_PRIORITY_FOREGROUND
+ ) {
+ toPromise = gTabPriorityWatcher.ensureNoPriorityChange(toBrowser);
+ } else {
+ toPromise = gTabPriorityWatcher.waitForPriorityChange(
+ 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.
+ */
+add_task(async function test_normal_background_tab() {
+ let originalTab = gBrowser.selectedTab;
+
+ await BrowserTestUtils.withNewTab("http://example.com", 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,
+ });
+ });
+});
+
+/**
+ * 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("http://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.
+ await SpecialPowers.spawn(browser, [], async () => {
+ let video = content.document.createElement("video");
+ video.src = "http://mochi.test:8888/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();
+ });
+
+ 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.
+ await 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("http://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 = "http://mochi.test:8888/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("http://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,
+ });
+ });
+});
diff --git a/dom/ipc/tests/browser_bug1646088.js b/dom/ipc/tests/browser_bug1646088.js
new file mode 100644
index 0000000000..4466c67129
--- /dev/null
+++ b/dom/ipc/tests/browser_bug1646088.js
@@ -0,0 +1,70 @@
+const { PromiseUtils } = ChromeUtils.import(
+ "resource://gre/modules/PromiseUtils.jsm"
+);
+
+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 = PromiseUtils.defer();
+ let finishSwitch = PromiseUtils.defer();
+ 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. We do this from the content process to make sure the frontend
+ // has no chance to trigger an eager process switch.
+ info("Beginning process switch into file URI process");
+ let browserLoaded = BrowserTestUtils.browserLoaded(browser);
+ await SpecialPowers.spawn(browser, [uriString], uri => {
+ content.location = uri;
+ });
+ 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 = PromiseUtils.defer();
+ 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_cancel_content_js.js b/dom/ipc/tests/browser_cancel_content_js.js
new file mode 100644
index 0000000000..d319de87e4
--- /dev/null
+++ b/dom/ipc/tests/browser_cancel_content_js.js
@@ -0,0 +1,68 @@
+/* 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, cancelContentJSPref, shouldCancel) {
+ await SpecialPowers.pushPrefEnv({
+ set: [
+ ["dom.ipc.cancel_content_js_when_navigating", cancelContentJSPref],
+ ["dom.max_script_run_time", 20],
+ ],
+ });
+ 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} with cancel content JS ${
+ cancelContentJSPref ? "enabled" : "disabled"
+ }`
+ );
+ const nextPageLoaded = BrowserTestUtils.waitForContentEvent(
+ tab.linkedBrowser,
+ "DOMTitleChanged"
+ );
+ BrowserTestUtils.loadURI(gBrowser, nextPage);
+
+ const result = await Promise.race([
+ nextPageLoaded,
+ loopEnded.then(() => "timeout"),
+ ]);
+
+ const timedOut = result === "timeout";
+ if (shouldCancel) {
+ ok(timedOut === false, "expected next page to be loaded");
+ } else {
+ ok(timedOut === true, "expected timeout");
+ }
+
+ BrowserTestUtils.removeTab(tab);
+}
+
+add_task(async () => test_navigation(NEXT_PAGE, true, true));
+add_task(async () => test_navigation(NEXT_PAGE, false, false));
+add_task(async () => test_navigation(JS_URI, true, false));
+add_task(async () => test_navigation(JS_URI, false, false));
diff --git a/dom/ipc/tests/browser_crash_oopiframe.js b/dom/ipc/tests/browser_crash_oopiframe.js
new file mode 100644
index 0000000000..2cf133e5e3
--- /dev/null
+++ b/dom/ipc/tests/browser_crash_oopiframe.js
@@ -0,0 +1,167 @@
+"use strict";
+
+/**
+ * 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 browser, rootBC, iframeBC;
+
+ for (let count = 0; count < numTabs; count++) {
+ let tab = await BrowserTestUtils.openNewForegroundTab({
+ gBrowser,
+ url: "about:blank",
+ });
+
+ browser = tab.linkedBrowser;
+ rootBC = browser.browsingContext;
+
+ // 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(browser, [], 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;
+ });
+ }
+
+ 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");
+ ok(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"
+ );
+ }
+
+ // 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.querySelectorAll(".notification-button");
+ is(
+ buttons.length,
+ 2,
+ "Notification " + count + " should have only two buttons."
+ );
+ }
+
+ // Press the ignore button on the visible notification.
+ let notificationBox = gBrowser.getNotificationBox(gBrowser.selectedBrowser);
+ let notification = notificationBox.currentNotification;
+ notification.dismiss();
+
+ for (let count = 1; count <= numTabs; count++) {
+ let nb = gBrowser.getNotificationBox(gBrowser.browsers[count]);
+
+ await TestUtils.waitForCondition(
+ () => !nb.currentNotification,
+ "notification closed"
+ );
+
+ ok(
+ !nb.currentNotification,
+ "notification " + count + " closed when dismiss button is pressed"
+ );
+ }
+
+ 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() {
+ // 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);
+});
diff --git a/dom/ipc/tests/browser_domainPolicy.js b/dom/ipc/tests/browser_domainPolicy.js
new file mode 100644
index 0000000000..cec360f2e1
--- /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_task(async function setup() {
+ 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_memory_distribution_telemetry.js b/dom/ipc/tests/browser_memory_distribution_telemetry.js
new file mode 100644
index 0000000000..bec51fa1cd
--- /dev/null
+++ b/dom/ipc/tests/browser_memory_distribution_telemetry.js
@@ -0,0 +1,92 @@
+"use strict";
+
+var session = ChromeUtils.import(
+ "resource://gre/modules/TelemetrySession.jsm",
+ null
+);
+
+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");
+ });
+
+ session.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];
+ ok(
+ fewTabsSnapshot.sum > 0,
+ "Zero difference between all the content processes is unlikely, what happened?"
+ );
+ ok(
+ 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/chrome.ini b/dom/ipc/tests/chrome.ini
new file mode 100644
index 0000000000..206f0a1501
--- /dev/null
+++ b/dom/ipc/tests/chrome.ini
@@ -0,0 +1,11 @@
+[DEFAULT]
+skip-if = os == 'android'
+support-files =
+ process_error.xhtml
+
+[test_process_error.xhtml]
+skip-if = !crashreporter
+
+
+[test_process_error_oom.xhtml]
+skip-if = !crashreporter
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_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..1a481c4163
--- /dev/null
+++ b/dom/ipc/tests/file_dummy.html
@@ -0,0 +1,4 @@
+<!doctype html>
+<body>
+ <h1>This is a dummy file</h1>
+</body>
diff --git a/dom/ipc/tests/mochitest.ini b/dom/ipc/tests/mochitest.ini
new file mode 100644
index 0000000000..263a1cfb1f
--- /dev/null
+++ b/dom/ipc/tests/mochitest.ini
@@ -0,0 +1,12 @@
+[DEFAULT]
+
+[test_temporaryfile_stream.html]
+skip-if = !e10s || toolkit == 'android' || (os == "win" && processor == "aarch64") # Bug 1525959, aarch64 due to 1531150
+support-files =
+ blob_verify.sjs
+ !/dom/canvas/test/captureStream_common.js
+[test_Preallocated.html]
+skip-if = !e10s || toolkit == 'android' || tsan # Bug 1525959. tsan: Bug 1683730
+[test_window_open_discarded_bc.html]
+skip-if = toolkit == 'android'
+[test_bcg_processes.html]
diff --git a/dom/ipc/tests/process_error.xhtml b/dom/ipc/tests/process_error.xhtml
new file mode 100644
index 0000000000..7f0a5e20dd
--- /dev/null
+++ b/dom/ipc/tests/process_error.xhtml
@@ -0,0 +1,63 @@
+<?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 {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+ const {BrowserTestUtils} = ChromeUtils.import("resource://testing-common/BrowserTestUtils.jsm");
+
+ 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");
+
+ // Let's check whether we have correctly reported OOM.
+ var isLikelyOOM = subject.getPropertyAsBool('isLikelyOOM');
+ is(isLikelyOOM, crashType == 'CRASH_OOM', 'isLikelyOOM is correct');
+ }
+
+ 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..5d2b2bd3a6
--- /dev/null
+++ b/dom/ipc/tests/test_Preallocated.html
@@ -0,0 +1,52 @@
+<!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";
+/* eslint-env mozilla/frame-script */
+
+SimpleTest.waitForExplicitFinish();
+
+function expectProcessCreated() {
+ return new Promise(resolve => {
+ function parentExpectProcessCreated() {
+ const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
+ 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..8109b59a42
--- /dev/null
+++ b/dom/ipc/tests/test_bcg_processes.html
@@ -0,0 +1,46 @@
+<!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";
+/* eslint-env mozilla/frame-script */
+
+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..e3498320ee
--- /dev/null
+++ b/dom/ipc/tests/test_blob_sliced_from_child_process.js
@@ -0,0 +1,140 @@
+"use strict";
+/* eslint-env mozilla/frame-script */
+
+const { ExtensionTestUtils } = ChromeUtils.import(
+ "resource://testing-common/ExtensionXPCShellUtils.jsm"
+);
+
+ExtensionTestUtils.init(this);
+
+function childFrameScript() {
+ "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 ExtensionTestUtils.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..4f3b10d7a2
--- /dev/null
+++ b/dom/ipc/tests/test_blob_sliced_from_parent_process.js
@@ -0,0 +1,167 @@
+"use strict";
+/* eslint-env mozilla/frame-script */
+
+const { ExtensionTestUtils } = ChromeUtils.import(
+ "resource://testing-common/ExtensionXPCShellUtils.jsm"
+);
+
+ExtensionTestUtils.init(this);
+
+function childFrameScript() {
+ 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 ExtensionTestUtils.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_bug1086684.js b/dom/ipc/tests/test_bug1086684.js
new file mode 100644
index 0000000000..c5d4836d98
--- /dev/null
+++ b/dom/ipc/tests/test_bug1086684.js
@@ -0,0 +1,99 @@
+"use strict";
+/* eslint-env mozilla/frame-script */
+
+const { AddonTestUtils } = ChromeUtils.import(
+ "resource://testing-common/AddonTestUtils.jsm"
+);
+const { ExtensionTestUtils } = ChromeUtils.import(
+ "resource://testing-common/ExtensionXPCShellUtils.jsm"
+);
+
+AddonTestUtils.init(this);
+ExtensionTestUtils.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 = AddonTestUtils.createHttpServer({ hosts: ["example.com"] });
+server.registerPathHandler(childFramePath, (request, response) => {
+ response.write(childFrameContents);
+});
+
+function childFrameScript() {
+ "use strict";
+
+ let { MockFilePicker } = ChromeUtils.import(
+ "resource://testing-common/MockFilePicker.jsm"
+ );
+
+ 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() {
+ let page = await ExtensionTestUtils.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();
+});
diff --git a/dom/ipc/tests/test_child_docshell.js b/dom/ipc/tests/test_child_docshell.js
new file mode 100644
index 0000000000..3871c35496
--- /dev/null
+++ b/dom/ipc/tests/test_child_docshell.js
@@ -0,0 +1,93 @@
+"use strict";
+/* eslint-env mozilla/frame-script */
+
+const { ExtensionTestUtils } = ChromeUtils.import(
+ "resource://testing-common/ExtensionXPCShellUtils.jsm"
+);
+
+ExtensionTestUtils.init(this);
+
+add_task(async function test() {
+ let page = await ExtensionTestUtils.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() {
+ const { Services } = ChromeUtils.import(
+ "resource://gre/modules/Services.jsm"
+ );
+
+ 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_process_error_oom.xhtml b/dom/ipc/tests/test_process_error_oom.xhtml
new file mode 100644
index 0000000000..03de3b07eb
--- /dev/null
+++ b/dom/ipc/tests/test_process_error_oom.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?crashType=CRASH_OOM', '_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..112093e44c
--- /dev/null
+++ b/dom/ipc/tests/test_sharedMap.js
@@ -0,0 +1,382 @@
+"use strict";
+
+const { AppConstants } = ChromeUtils.import(
+ "resource://gre/modules/AppConstants.jsm"
+);
+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
+const { ExtensionUtils } = ChromeUtils.import(
+ "resource://gre/modules/ExtensionUtils.jsm"
+);
+const { ExtensionTestUtils } = ChromeUtils.import(
+ "resource://testing-common/ExtensionXPCShellUtils.jsm"
+);
+
+const PROCESS_COUNT_PREF = "dom.ipc.processCount";
+
+const remote = AppConstants.platform !== "android";
+
+ExtensionTestUtils.init(this);
+
+let contentPage;
+
+async function readBlob(key, sharedData = Services.cpmm.sharedData) {
+ 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(undefined, getContents);
+ checkMap(contents, expected);
+ }
+}
+
+async function loadContentPage() {
+ let page = await ExtensionTestUtils.loadContentPage("about:blank", {
+ remote,
+ });
+ registerCleanupFunction(() => page.close());
+
+ page.addFrameScriptHelper(`
+ var {ExtensionUtils} = ChromeUtils.import("resource://gre/modules/ExtensionUtils.jsm");
+ Cu.importGlobalProperties(["FileReader"]);
+ `);
+ return page;
+}
+
+add_task(async function setup() {
+ // 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..f101264eeb
--- /dev/null
+++ b/dom/ipc/tests/test_window_open_discarded_bc.html
@@ -0,0 +1,37 @@
+<!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(() => !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.ini b/dom/ipc/tests/xpcshell.ini
new file mode 100644
index 0000000000..a93537c9b6
--- /dev/null
+++ b/dom/ipc/tests/xpcshell.ini
@@ -0,0 +1,10 @@
+[test_sharedMap.js]
+skip-if = os == "android" && processor == "x86_64"
+[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"