summaryrefslogtreecommitdiffstats
path: root/dom/ipc
diff options
context:
space:
mode:
authorMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
committerMatt A. Tobin <mattatobin@localhost.localdomain>2018-02-02 04:16:08 -0500
commit5f8de423f190bbb79a62f804151bc24824fa32d8 (patch)
tree10027f336435511475e392454359edea8e25895d /dom/ipc
parent49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff)
downloadUXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.lz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.xz
UXP-5f8de423f190bbb79a62f804151bc24824fa32d8.zip
Add m-esr52 at 52.6.0
Diffstat (limited to 'dom/ipc')
-rw-r--r--dom/ipc/AppProcessChecker.cpp95
-rw-r--r--dom/ipc/AppProcessChecker.h160
-rw-r--r--dom/ipc/Blob.cpp4793
-rw-r--r--dom/ipc/BlobChild.h246
-rw-r--r--dom/ipc/BlobParent.h260
-rw-r--r--dom/ipc/BlobTypes.ipdlh21
-rw-r--r--dom/ipc/CPOWManagerGetter.h27
-rw-r--r--dom/ipc/ColorPickerParent.cpp87
-rw-r--r--dom/ipc/ColorPickerParent.h61
-rw-r--r--dom/ipc/ContentBridgeChild.cpp206
-rw-r--r--dom/ipc/ContentBridgeChild.h102
-rw-r--r--dom/ipc/ContentBridgeParent.cpp221
-rw-r--r--dom/ipc/ContentBridgeParent.h154
-rw-r--r--dom/ipc/ContentChild.cpp3310
-rw-r--r--dom/ipc/ContentChild.h702
-rw-r--r--dom/ipc/ContentParent.cpp5185
-rw-r--r--dom/ipc/ContentParent.h1197
-rw-r--r--dom/ipc/ContentProcess.cpp162
-rw-r--r--dom/ipc/ContentProcess.h65
-rw-r--r--dom/ipc/ContentProcessManager.cpp372
-rw-r--r--dom/ipc/ContentProcessManager.h166
-rw-r--r--dom/ipc/CrashReporterChild.cpp42
-rw-r--r--dom/ipc/CrashReporterChild.h32
-rw-r--r--dom/ipc/CrashReporterParent.cpp146
-rw-r--r--dom/ipc/CrashReporterParent.h306
-rw-r--r--dom/ipc/DOMTypes.ipdlh181
-rw-r--r--dom/ipc/DatePickerParent.cpp87
-rw-r--r--dom/ipc/DatePickerParent.h61
-rw-r--r--dom/ipc/FilePickerParent.cpp306
-rw-r--r--dom/ipc/FilePickerParent.h110
-rw-r--r--dom/ipc/IdType.h73
-rw-r--r--dom/ipc/PBlob.ipdl57
-rw-r--r--dom/ipc/PBlobStream.ipdl22
-rw-r--r--dom/ipc/PBrowser.ipdl928
-rw-r--r--dom/ipc/PBrowserOrId.ipdlh21
-rw-r--r--dom/ipc/PColorPicker.ipdl27
-rw-r--r--dom/ipc/PContent.ipdl1063
-rw-r--r--dom/ipc/PContentBridge.ipdl69
-rw-r--r--dom/ipc/PContentPermission.ipdlh20
-rw-r--r--dom/ipc/PContentPermissionRequest.ipdl28
-rw-r--r--dom/ipc/PCrashReporter.ipdl31
-rw-r--r--dom/ipc/PCycleCollectWithLogs.ipdl22
-rw-r--r--dom/ipc/PDatePicker.ipdl27
-rw-r--r--dom/ipc/PDocumentRenderer.ipdl25
-rw-r--r--dom/ipc/PFilePicker.ipdl46
-rw-r--r--dom/ipc/PMemoryReportRequest.ipdl29
-rw-r--r--dom/ipc/PPluginWidget.ipdl61
-rw-r--r--dom/ipc/PProcessHangMonitor.ipdl47
-rw-r--r--dom/ipc/PScreenManager.ipdl62
-rw-r--r--dom/ipc/PTabContext.ipdlh84
-rw-r--r--dom/ipc/PermissionMessageUtils.cpp69
-rw-r--r--dom/ipc/PermissionMessageUtils.h52
-rw-r--r--dom/ipc/PreallocatedProcessManager.cpp252
-rw-r--r--dom/ipc/PreallocatedProcessManager.h88
-rw-r--r--dom/ipc/ProcessHangMonitor.cpp1278
-rw-r--r--dom/ipc/ProcessHangMonitor.h82
-rw-r--r--dom/ipc/ProcessHangMonitorIPC.h34
-rw-r--r--dom/ipc/ProcessPriorityManager.cpp1454
-rw-r--r--dom/ipc/ProcessPriorityManager.h84
-rw-r--r--dom/ipc/ScreenManagerParent.cpp229
-rw-r--r--dom/ipc/ScreenManagerParent.h56
-rw-r--r--dom/ipc/ServiceWorkerConfiguration.ipdlh18
-rw-r--r--dom/ipc/StructuredCloneData.cpp133
-rw-r--r--dom/ipc/StructuredCloneData.h160
-rw-r--r--dom/ipc/TabChild.cpp3371
-rw-r--r--dom/ipc/TabChild.h818
-rw-r--r--dom/ipc/TabContext.cpp434
-rw-r--r--dom/ipc/TabContext.h337
-rw-r--r--dom/ipc/TabMessageUtils.cpp30
-rw-r--r--dom/ipc/TabMessageUtils.h117
-rw-r--r--dom/ipc/TabParent.cpp3306
-rw-r--r--dom/ipc/TabParent.h813
-rw-r--r--dom/ipc/extensions.js17
-rw-r--r--dom/ipc/jar.mn13
-rw-r--r--dom/ipc/manifestMessages.js108
-rw-r--r--dom/ipc/moz.build179
-rw-r--r--dom/ipc/nsIContentChild.cpp159
-rw-r--r--dom/ipc/nsIContentChild.h118
-rw-r--r--dom/ipc/nsIContentParent.cpp328
-rw-r--r--dom/ipc/nsIContentParent.h145
-rw-r--r--dom/ipc/nsIHangReport.idl66
-rw-r--r--dom/ipc/nsIRemoteBlob.h45
-rw-r--r--dom/ipc/preload.js130
-rw-r--r--dom/ipc/remote-test.js54
-rw-r--r--dom/ipc/test.xul274
-rw-r--r--dom/ipc/tests/blob_verify.sjs20
-rw-r--r--dom/ipc/tests/browser.ini6
-rw-r--r--dom/ipc/tests/browser_domainPolicy.js240
-rw-r--r--dom/ipc/tests/chrome.ini8
-rw-r--r--dom/ipc/tests/file_bug1086684.html16
-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/mochitest.ini22
-rw-r--r--dom/ipc/tests/process_error.xul58
-rw-r--r--dom/ipc/tests/process_error_contentscript.js7
-rw-r--r--dom/ipc/tests/test_CrashService_crash.html95
-rw-r--r--dom/ipc/tests/test_blob_sliced_from_child_process.html185
-rw-r--r--dom/ipc/tests/test_blob_sliced_from_parent_process.html213
-rw-r--r--dom/ipc/tests/test_bug1086684.html107
-rw-r--r--dom/ipc/tests/test_child_docshell.html86
-rw-r--r--dom/ipc/tests/test_cpow_cookies.html90
-rw-r--r--dom/ipc/tests/test_process_error.xul22
-rw-r--r--dom/ipc/tests/test_temporaryfile_stream.html80
103 files changed, 37730 insertions, 0 deletions
diff --git a/dom/ipc/AppProcessChecker.cpp b/dom/ipc/AppProcessChecker.cpp
new file mode 100644
index 000000000..fa91c94ff
--- /dev/null
+++ b/dom/ipc/AppProcessChecker.cpp
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "AppProcessChecker.h"
+#include "nsIPermissionManager.h"
+
+namespace mozilla {
+namespace dom {
+class PContentParent;
+} // namespace dom
+} // namespace mozilla
+
+class nsIPrincipal;
+
+namespace mozilla {
+
+#if DEBUG
+ #define LOG(...) printf_stderr(__VA_ARGS__)
+#else
+ #define LOG(...)
+#endif
+
+bool
+AssertAppProcess(mozilla::dom::PBrowserParent* aActor,
+ AssertAppProcessType aType,
+ const char* aCapability)
+{
+ return true;
+}
+
+bool
+AssertAppStatus(mozilla::dom::PBrowserParent* aActor,
+ unsigned short aStatus)
+{
+ return true;
+}
+
+bool
+AssertAppProcess(const mozilla::dom::TabContext& aContext,
+ AssertAppProcessType aType,
+ const char* aCapability)
+{
+ return true;
+}
+
+bool
+AssertAppStatus(const mozilla::dom::TabContext& aContext,
+ unsigned short aStatus)
+{
+ return true;
+}
+
+
+bool
+AssertAppProcess(mozilla::dom::PContentParent* aActor,
+ AssertAppProcessType aType,
+ const char* aCapability)
+{
+ return true;
+}
+
+bool
+AssertAppStatus(mozilla::dom::PContentParent* aActor,
+ unsigned short aStatus)
+{
+ return true;
+}
+
+bool
+AssertAppProcess(mozilla::hal_sandbox::PHalParent* aActor,
+ AssertAppProcessType aType,
+ const char* aCapability)
+{
+ return true;
+}
+
+bool
+AssertAppPrincipal(mozilla::dom::PContentParent* aActor,
+ nsIPrincipal* aPrincipal)
+{
+ return true;
+}
+
+uint32_t
+CheckPermission(mozilla::dom::PContentParent* aActor,
+ nsIPrincipal* aPrincipal,
+ const char* aPermission)
+{
+ return nsIPermissionManager::ALLOW_ACTION;
+}
+
+} // namespace mozilla
diff --git a/dom/ipc/AppProcessChecker.h b/dom/ipc/AppProcessChecker.h
new file mode 100644
index 000000000..e5e7a90ba
--- /dev/null
+++ b/dom/ipc/AppProcessChecker.h
@@ -0,0 +1,160 @@
+/* -*- 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_AppProcessChecker_h
+#define mozilla_AppProcessChecker_h
+
+#include <stdint.h>
+
+class nsIPrincipal;
+
+namespace mozilla {
+
+namespace dom {
+class TabContext;
+class PBrowserParent;
+class PContentParent;
+} // namespace dom
+
+namespace hal_sandbox {
+class PHalParent;
+} // namespace hal_sandbox
+
+enum AssertAppProcessType {
+ ASSERT_APP_PROCESS_PERMISSION,
+ ASSERT_APP_PROCESS_MANIFEST_URL,
+ ASSERT_APP_HAS_PERMISSION
+};
+
+/**
+ * Return true if the specified browser has the specified capability.
+ * If this returns false, the browser didn't have the capability and
+ * will be killed.
+ */
+bool
+AssertAppProcess(mozilla::dom::PBrowserParent* aActor,
+ AssertAppProcessType aType,
+ const char* aCapability);
+
+/**
+ * Return true if the specified app has the specified status.
+ * If this returns false, the browser will be killed.
+ */
+bool
+AssertAppStatus(mozilla::dom::PBrowserParent* aActor,
+ unsigned short aStatus);
+
+/**
+ * Return true if the specified browser has the specified capability.
+ * If this returns false, the browser didn't have the capability and
+ * will be killed.
+ */
+bool
+AssertAppProcess(const mozilla::dom::TabContext& aContext,
+ AssertAppProcessType aType,
+ const char* aCapability);
+
+/**
+ * Return true if the specified app has the specified status.
+ * If this returns false, the browser will be killed.
+ */
+bool
+AssertAppStatus(const mozilla::dom::TabContext& aContext,
+ unsigned short aStatus);
+
+/**
+ * Return true if any of the PBrowsers loaded in this content process
+ * has the specified capability. If this returns false, the process
+ * didn't have the capability and will be killed.
+ */
+bool
+AssertAppProcess(mozilla::dom::PContentParent* aActor,
+ AssertAppProcessType aType,
+ const char* aCapability);
+
+/**
+ * Return true if any of the PBrowsers loaded in this content process
+ * has an app with the specified status. If this returns false, the process
+ * didn't have the status and will be killed.
+ */
+bool
+AssertAppStatus(mozilla::dom::PContentParent* aActor,
+ unsigned short aStatus);
+
+bool
+AssertAppProcess(mozilla::hal_sandbox::PHalParent* aActor,
+ AssertAppProcessType aType,
+ const char* aCapability);
+
+// NB: when adding capability checks for other IPDL actors, please add
+// them to this file and have them delegate to the two functions above
+// as appropriate. For example,
+//
+// bool AppProcessHasCapability(PNeckoParent* aActor, AssertAppProcessType aType) {
+// return AssertAppProcess(aActor->Manager(), aType);
+// }
+
+bool
+AssertAppPrincipal(mozilla::dom::PContentParent* aParent,
+ nsIPrincipal* aPrincipal);
+
+/**
+ * Check if the specified principal is valid, and return the saved permission
+ * value for permission `aPermission' on that principal.
+ * See nsIPermissionManager.idl for possible return values.
+ *
+ * nsIPermissionManager::UNKNOWN_ACTION is retuned if the principal is invalid.
+ */
+uint32_t
+CheckPermission(mozilla::dom::PContentParent* aParent,
+ nsIPrincipal* aPrincipal, const char* aPermission);
+
+/**
+ * Inline function for asserting the process's permission.
+ */
+template<typename T>
+inline bool
+AssertAppProcessPermission(T* aActor,
+ const char* aPermission) {
+ return AssertAppProcess(aActor,
+ ASSERT_APP_PROCESS_PERMISSION,
+ aPermission);
+}
+
+/**
+ * Inline function for asserting the process's manifest URL.
+ */
+template<typename T>
+inline bool
+AssertAppProcessManifestURL(T* aActor,
+ const char* aManifestURL) {
+ return AssertAppProcess(aActor,
+ ASSERT_APP_PROCESS_MANIFEST_URL,
+ aManifestURL);
+}
+
+/**
+ * Inline function for asserting the process's manifest URL.
+ */
+template<typename T>
+inline bool
+AssertAppHasPermission(T* aActor,
+ const char* aPermission) {
+ return AssertAppProcess(aActor,
+ ASSERT_APP_HAS_PERMISSION,
+ aPermission);
+}
+
+template<typename T>
+inline bool
+AssertAppHasStatus(T* aActor,
+ unsigned short aStatus) {
+ return AssertAppStatus(aActor, aStatus);
+}
+
+} // namespace mozilla
+
+#endif // mozilla_AppProcessChecker_h
diff --git a/dom/ipc/Blob.cpp b/dom/ipc/Blob.cpp
new file mode 100644
index 000000000..95d2e0513
--- /dev/null
+++ b/dom/ipc/Blob.cpp
@@ -0,0 +1,4793 @@
+/* -*- 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 "BlobChild.h"
+#include "BlobParent.h"
+
+#include "BackgroundParent.h"
+#include "ContentChild.h"
+#include "ContentParent.h"
+#include "FileDescriptorSetChild.h"
+#include "jsapi.h"
+#include "mozilla/Assertions.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/DebugOnly.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/Unused.h"
+#include "mozilla/dom/File.h"
+#include "mozilla/dom/nsIContentParent.h"
+#include "mozilla/dom/nsIContentChild.h"
+#include "mozilla/dom/PBlobStreamChild.h"
+#include "mozilla/dom/PBlobStreamParent.h"
+#include "mozilla/dom/indexedDB/FileSnapshot.h"
+#include "mozilla/dom/IndexedDatabaseManager.h"
+#include "mozilla/ipc/InputStreamUtils.h"
+#include "mozilla/ipc/IPCStreamUtils.h"
+#include "mozilla/ipc/PBackgroundChild.h"
+#include "mozilla/ipc/PBackgroundParent.h"
+#include "mozilla/ipc/PFileDescriptorSetParent.h"
+#include "MultipartBlobImpl.h"
+#include "nsDataHashtable.h"
+#include "nsHashKeys.h"
+#include "nsID.h"
+#include "nsIFileStreams.h"
+#include "nsIInputStream.h"
+#include "nsIIPCSerializableInputStream.h"
+#include "nsIMultiplexInputStream.h"
+#include "nsIRemoteBlob.h"
+#include "nsISeekableStream.h"
+#include "nsIUUIDGenerator.h"
+#include "nsNetCID.h"
+#include "nsServiceManagerUtils.h"
+#include "nsStringStream.h"
+#include "nsThreadUtils.h"
+#include "nsXULAppAPI.h"
+#include "WorkerPrivate.h"
+#include "WorkerRunnable.h"
+
+#ifdef DEBUG
+#include "BackgroundChild.h" // BackgroundChild::GetForCurrentThread().
+#endif
+
+#ifdef OS_POSIX
+#include "chrome/common/file_descriptor_set_posix.h"
+#endif
+
+#define DISABLE_ASSERTS_FOR_FUZZING 0
+
+#if DISABLE_ASSERTS_FOR_FUZZING
+#define ASSERT_UNLESS_FUZZING(...) do { } while (0)
+#else
+#define ASSERT_UNLESS_FUZZING(...) MOZ_ASSERT(false, __VA_ARGS__)
+#endif
+
+#define PRIVATE_REMOTE_INPUT_STREAM_IID \
+ {0x30c7699f, 0x51d2, 0x48c8, {0xad, 0x56, 0xc0, 0x16, 0xd7, 0x6f, 0x71, 0x27}}
+
+namespace mozilla {
+namespace dom {
+
+using namespace mozilla::ipc;
+using namespace mozilla::dom::indexedDB;
+using namespace mozilla::dom::workers;
+
+namespace {
+
+const char kUUIDGeneratorContractId[] = "@mozilla.org/uuid-generator;1";
+
+const uint32_t kMaxFileDescriptorsPerMessage = 250;
+
+#ifdef OS_POSIX
+// Keep this in sync with other platforms.
+static_assert(FileDescriptorSet::MAX_DESCRIPTORS_PER_MESSAGE == 250,
+ "MAX_DESCRIPTORS_PER_MESSAGE mismatch!");
+#endif
+
+StaticRefPtr<nsIUUIDGenerator> gUUIDGenerator;
+
+GeckoProcessType gProcessType = GeckoProcessType_Invalid;
+
+void
+CommonStartup()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ gProcessType = XRE_GetProcessType();
+ MOZ_ASSERT(gProcessType != GeckoProcessType_Invalid);
+
+ nsCOMPtr<nsIUUIDGenerator> uuidGen = do_GetService(kUUIDGeneratorContractId);
+ MOZ_RELEASE_ASSERT(uuidGen);
+
+ gUUIDGenerator = uuidGen;
+ ClearOnShutdown(&gUUIDGenerator);
+}
+
+template <class ManagerType>
+struct ConcreteManagerTypeTraits;
+
+template <>
+struct ConcreteManagerTypeTraits<nsIContentChild>
+{
+ typedef ContentChild Type;
+};
+
+template <>
+struct ConcreteManagerTypeTraits<PBackgroundChild>
+{
+ typedef PBackgroundChild Type;
+};
+
+template <>
+struct ConcreteManagerTypeTraits<nsIContentParent>
+{
+ typedef ContentParent Type;
+};
+
+template <>
+struct ConcreteManagerTypeTraits<PBackgroundParent>
+{
+ typedef PBackgroundParent Type;
+};
+
+void
+AssertCorrectThreadForManager(nsIContentChild* aManager)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+}
+
+void
+AssertCorrectThreadForManager(nsIContentParent* aManager)
+{
+ MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
+ MOZ_ASSERT(NS_IsMainThread());
+}
+
+void
+AssertCorrectThreadForManager(PBackgroundChild* aManager)
+{
+#ifdef DEBUG
+ if (aManager) {
+ PBackgroundChild* backgroundChild = BackgroundChild::GetForCurrentThread();
+ MOZ_ASSERT(backgroundChild);
+ MOZ_ASSERT(backgroundChild == aManager);
+ }
+#endif
+}
+
+void
+AssertCorrectThreadForManager(PBackgroundParent* aManager)
+{
+ MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
+ AssertIsOnBackgroundThread();
+}
+
+intptr_t
+ActorManagerProcessID(nsIContentParent* aManager)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+
+ return reinterpret_cast<intptr_t>(aManager);
+}
+
+intptr_t
+ActorManagerProcessID(PBackgroundParent* aManager)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+
+ return BackgroundParent::GetRawContentParentForComparison(aManager);
+}
+
+bool
+ActorManagerIsSameProcess(nsIContentParent* aManager)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+
+ return false;
+}
+
+bool
+ActorManagerIsSameProcess(PBackgroundParent* aManager)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+
+ return !BackgroundParent::IsOtherProcessActor(aManager);
+}
+
+bool
+EventTargetIsOnCurrentThread(nsIEventTarget* aEventTarget)
+{
+ if (!aEventTarget) {
+ return NS_IsMainThread();
+ }
+
+ bool current;
+
+ // If this fails, we are probably shutting down.
+ if (NS_WARN_IF(NS_FAILED(aEventTarget->IsOnCurrentThread(&current)))) {
+ return true;
+ }
+
+ return current;
+}
+
+class CancelableRunnableWrapper final
+ : public CancelableRunnable
+{
+ nsCOMPtr<nsIRunnable> mRunnable;
+#ifdef DEBUG
+ nsCOMPtr<nsIEventTarget> mDEBUGEventTarget;
+#endif
+
+public:
+ CancelableRunnableWrapper(nsIRunnable* aRunnable,
+ nsIEventTarget* aEventTarget)
+ : mRunnable(aRunnable)
+#ifdef DEBUG
+ , mDEBUGEventTarget(aEventTarget)
+#endif
+ {
+ MOZ_ASSERT(aRunnable);
+ MOZ_ASSERT(aEventTarget);
+ }
+
+ NS_DECL_ISUPPORTS_INHERITED
+
+private:
+ ~CancelableRunnableWrapper()
+ { }
+
+ NS_DECL_NSIRUNNABLE
+ nsresult Cancel() override;
+};
+
+NS_IMPL_ISUPPORTS_INHERITED0(CancelableRunnableWrapper, CancelableRunnable)
+
+NS_IMETHODIMP
+CancelableRunnableWrapper::Run()
+{
+ DebugOnly<bool> onTarget;
+ MOZ_ASSERT(mDEBUGEventTarget);
+ MOZ_ASSERT(NS_SUCCEEDED(mDEBUGEventTarget->IsOnCurrentThread(&onTarget)));
+ MOZ_ASSERT(onTarget);
+
+ nsCOMPtr<nsIRunnable> runnable;
+ mRunnable.swap(runnable);
+
+ if (runnable) {
+ return runnable->Run();
+ }
+
+ return NS_OK;
+}
+
+nsresult
+CancelableRunnableWrapper::Cancel()
+{
+ DebugOnly<bool> onTarget;
+ MOZ_ASSERT(mDEBUGEventTarget);
+ MOZ_ASSERT(NS_SUCCEEDED(mDEBUGEventTarget->IsOnCurrentThread(&onTarget)));
+ MOZ_ASSERT(onTarget);
+
+ if (NS_WARN_IF(!mRunnable)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ Unused << Run();
+ MOZ_ASSERT(!mRunnable);
+
+ return NS_OK;
+}
+
+// Ensure that a nsCOMPtr/nsRefPtr is released on the target thread.
+template <template <class> class SmartPtr, class T>
+void
+ReleaseOnTarget(SmartPtr<T>& aDoomed, nsIEventTarget* aTarget)
+{
+ MOZ_ASSERT(aDoomed);
+ MOZ_ASSERT(!EventTargetIsOnCurrentThread(aTarget));
+
+ T* doomedRaw;
+ aDoomed.forget(&doomedRaw);
+
+ auto* doomedSupports = static_cast<nsISupports*>(doomedRaw);
+
+ nsCOMPtr<nsIRunnable> releaseRunnable =
+ NewNonOwningRunnableMethod(doomedSupports, &nsISupports::Release);
+ MOZ_ASSERT(releaseRunnable);
+
+ if (aTarget) {
+ // If we're targeting a non-main thread then make sure the runnable is
+ // cancelable.
+ releaseRunnable = new CancelableRunnableWrapper(releaseRunnable, aTarget);
+
+ MOZ_ALWAYS_SUCCEEDS(aTarget->Dispatch(releaseRunnable,
+ NS_DISPATCH_NORMAL));
+ } else {
+ MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(releaseRunnable));
+ }
+}
+
+template <class ManagerType>
+void
+ConstructFileDescriptorSet(ManagerType* aManager,
+ nsTArray<FileDescriptor>& aFDs,
+ OptionalFileDescriptorSet& aOptionalFDSet)
+{
+ typedef typename ConcreteManagerTypeTraits<ManagerType>::Type
+ ConcreteManagerType;
+
+ MOZ_ASSERT(aManager);
+
+ if (aFDs.IsEmpty()) {
+ aOptionalFDSet = void_t();
+ return;
+ }
+
+ if (aFDs.Length() <= kMaxFileDescriptorsPerMessage) {
+ aOptionalFDSet = nsTArray<FileDescriptor>();
+ aOptionalFDSet.get_ArrayOfFileDescriptor().SwapElements(aFDs);
+ return;
+ }
+
+ auto* concreteManager = static_cast<ConcreteManagerType*>(aManager);
+
+ PFileDescriptorSetParent* fdSet =
+ concreteManager->SendPFileDescriptorSetConstructor(aFDs[0]);
+ if (!fdSet) {
+ aOptionalFDSet = void_t();
+ return;
+ }
+
+ for (uint32_t index = 1; index < aFDs.Length(); index++) {
+ if (!fdSet->SendAddFileDescriptor(aFDs[index])) {
+ aOptionalFDSet = void_t();
+ return;
+ }
+ }
+
+ aOptionalFDSet = fdSet;
+}
+
+void
+OptionalFileDescriptorSetToFDs(OptionalFileDescriptorSet& aOptionalSet,
+ nsTArray<FileDescriptor>& aFDs)
+{
+ MOZ_ASSERT(aFDs.IsEmpty());
+
+ switch (aOptionalSet.type()) {
+ case OptionalFileDescriptorSet::Tvoid_t:
+ return;
+
+ case OptionalFileDescriptorSet::TArrayOfFileDescriptor:
+ aOptionalSet.get_ArrayOfFileDescriptor().SwapElements(aFDs);
+ return;
+
+ case OptionalFileDescriptorSet::TPFileDescriptorSetChild: {
+ FileDescriptorSetChild* fdSetActor =
+ static_cast<FileDescriptorSetChild*>(
+ aOptionalSet.get_PFileDescriptorSetChild());
+ MOZ_ASSERT(fdSetActor);
+
+ fdSetActor->ForgetFileDescriptors(aFDs);
+ MOZ_ASSERT(!aFDs.IsEmpty());
+
+ PFileDescriptorSetChild::Send__delete__(fdSetActor);
+ return;
+ }
+
+ default:
+ MOZ_CRASH("Unknown type!");
+ }
+
+ MOZ_CRASH("Should never get here!");
+}
+
+class NS_NO_VTABLE IPrivateRemoteInputStream
+ : public nsISupports
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(PRIVATE_REMOTE_INPUT_STREAM_IID)
+
+ // This will return the underlying stream.
+ virtual nsIInputStream*
+ BlockAndGetInternalStream() = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(IPrivateRemoteInputStream,
+ PRIVATE_REMOTE_INPUT_STREAM_IID)
+
+// This class exists to keep a blob alive at least as long as its internal
+// stream.
+class BlobInputStreamTether final
+ : public nsIMultiplexInputStream
+ , public nsISeekableStream
+ , public nsIIPCSerializableInputStream
+ , public nsIFileMetadata
+{
+ nsCOMPtr<nsIInputStream> mStream;
+ RefPtr<BlobImpl> mBlobImpl;
+
+ nsIMultiplexInputStream* mWeakMultiplexStream;
+ nsISeekableStream* mWeakSeekableStream;
+ nsIIPCSerializableInputStream* mWeakSerializableStream;
+ nsIFileMetadata* mWeakFileMetadata;
+
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_FORWARD_NSIINPUTSTREAM(mStream->)
+ NS_FORWARD_SAFE_NSIMULTIPLEXINPUTSTREAM(mWeakMultiplexStream)
+ NS_FORWARD_SAFE_NSISEEKABLESTREAM(mWeakSeekableStream)
+ NS_FORWARD_SAFE_NSIIPCSERIALIZABLEINPUTSTREAM(mWeakSerializableStream)
+ NS_FORWARD_SAFE_NSIFILEMETADATA(mWeakFileMetadata)
+
+ BlobInputStreamTether(nsIInputStream* aStream, BlobImpl* aBlobImpl)
+ : mStream(aStream)
+ , mBlobImpl(aBlobImpl)
+ , mWeakMultiplexStream(nullptr)
+ , mWeakSeekableStream(nullptr)
+ , mWeakSerializableStream(nullptr)
+ , mWeakFileMetadata(nullptr)
+ {
+ MOZ_ASSERT(aStream);
+ MOZ_ASSERT(aBlobImpl);
+
+ nsCOMPtr<nsIMultiplexInputStream> multiplexStream =
+ do_QueryInterface(aStream);
+ if (multiplexStream) {
+ MOZ_ASSERT(SameCOMIdentity(aStream, multiplexStream));
+ mWeakMultiplexStream = multiplexStream;
+ }
+
+ nsCOMPtr<nsISeekableStream> seekableStream = do_QueryInterface(aStream);
+ if (seekableStream) {
+ MOZ_ASSERT(SameCOMIdentity(aStream, seekableStream));
+ mWeakSeekableStream = seekableStream;
+ }
+
+ nsCOMPtr<nsIIPCSerializableInputStream> serializableStream =
+ do_QueryInterface(aStream);
+ if (serializableStream) {
+ MOZ_ASSERT(SameCOMIdentity(aStream, serializableStream));
+ mWeakSerializableStream = serializableStream;
+ }
+
+ nsCOMPtr<nsIFileMetadata> fileMetadata = do_QueryInterface(aStream);
+ if (fileMetadata) {
+ MOZ_ASSERT(SameCOMIdentity(aStream, fileMetadata));
+ mWeakFileMetadata = fileMetadata;
+ }
+ }
+
+private:
+ ~BlobInputStreamTether()
+ { }
+};
+
+NS_IMPL_ADDREF(BlobInputStreamTether)
+NS_IMPL_RELEASE(BlobInputStreamTether)
+
+NS_INTERFACE_MAP_BEGIN(BlobInputStreamTether)
+ NS_INTERFACE_MAP_ENTRY(nsIInputStream)
+ NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIMultiplexInputStream,
+ mWeakMultiplexStream)
+ NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISeekableStream, mWeakSeekableStream)
+ NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIIPCSerializableInputStream,
+ mWeakSerializableStream)
+ NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIFileMetadata,
+ mWeakFileMetadata)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
+NS_INTERFACE_MAP_END
+
+class RemoteInputStream final
+ : public nsIInputStream
+ , public nsISeekableStream
+ , public nsIIPCSerializableInputStream
+ , public nsIFileMetadata
+ , public IPrivateRemoteInputStream
+{
+ Monitor mMonitor;
+ BlobChild* mActor;
+ nsCOMPtr<nsIInputStream> mStream;
+ RefPtr<BlobImpl> mBlobImpl;
+ nsCOMPtr<nsIEventTarget> mEventTarget;
+ nsISeekableStream* mWeakSeekableStream;
+ nsIFileMetadata* mWeakFileMetadata;
+ uint64_t mStart;
+ uint64_t mLength;
+
+public:
+ RemoteInputStream(BlobImpl* aBlobImpl,
+ uint64_t aStart,
+ uint64_t aLength);
+
+ RemoteInputStream(BlobChild* aActor,
+ BlobImpl* aBlobImpl,
+ uint64_t aStart,
+ uint64_t aLength);
+
+ bool
+ IsOnOwningThread() const
+ {
+ return EventTargetIsOnCurrentThread(mEventTarget);
+ }
+
+ void
+ AssertIsOnOwningThread() const
+ {
+ MOZ_ASSERT(IsOnOwningThread());
+ }
+
+ bool
+ IsWorkerStream() const
+ {
+ return !!mActor;
+ }
+
+ void
+ SetStream(nsIInputStream* aStream);
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+private:
+ ~RemoteInputStream();
+
+ nsresult
+ BlockAndWaitForStream();
+
+ void
+ ReallyBlockAndWaitForStream();
+
+ bool
+ IsSeekableStream();
+
+ bool
+ IsFileMetadata();
+
+ NS_DECL_NSIINPUTSTREAM
+ NS_DECL_NSISEEKABLESTREAM
+ NS_DECL_NSIIPCSERIALIZABLEINPUTSTREAM
+ NS_DECL_NSIFILEMETADATA
+
+ virtual nsIInputStream*
+ BlockAndGetInternalStream() override;
+};
+
+class InputStreamChild final
+ : public PBlobStreamChild
+{
+ RefPtr<RemoteInputStream> mRemoteStream;
+
+public:
+ explicit
+ InputStreamChild(RemoteInputStream* aRemoteStream)
+ : mRemoteStream(aRemoteStream)
+ {
+ MOZ_ASSERT(aRemoteStream);
+ aRemoteStream->AssertIsOnOwningThread();
+ }
+
+ InputStreamChild()
+ { }
+
+ ~InputStreamChild()
+ { }
+
+private:
+ // This method is only called by the IPDL message machinery.
+ virtual bool
+ Recv__delete__(const InputStreamParams& aParams,
+ const OptionalFileDescriptorSet& aFDs) override;
+};
+
+class InputStreamParent final
+ : public PBlobStreamParent
+{
+ typedef mozilla::ipc::InputStreamParams InputStreamParams;
+ typedef mozilla::dom::OptionalFileDescriptorSet OptionalFileDescriptorSet;
+
+ bool* mSyncLoopGuard;
+ InputStreamParams* mParams;
+ OptionalFileDescriptorSet* mFDs;
+
+#ifdef DEBUG
+ PRThread* mOwningThread;
+#endif
+
+public:
+ InputStreamParent()
+ : mSyncLoopGuard(nullptr)
+ , mParams(nullptr)
+ , mFDs(nullptr)
+ {
+#ifdef DEBUG
+ mOwningThread = PR_GetCurrentThread();
+#endif
+
+ AssertIsOnOwningThread();
+
+ MOZ_COUNT_CTOR(InputStreamParent);
+ }
+
+ InputStreamParent(bool* aSyncLoopGuard,
+ InputStreamParams* aParams,
+ OptionalFileDescriptorSet* aFDs)
+ : mSyncLoopGuard(aSyncLoopGuard)
+ , mParams(aParams)
+ , mFDs(aFDs)
+ {
+#ifdef DEBUG
+ mOwningThread = PR_GetCurrentThread();
+#endif
+
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aSyncLoopGuard);
+ MOZ_ASSERT(!*aSyncLoopGuard);
+ MOZ_ASSERT(aParams);
+ MOZ_ASSERT(aFDs);
+
+ MOZ_COUNT_CTOR(InputStreamParent);
+ }
+
+ ~InputStreamParent()
+ {
+ AssertIsOnOwningThread();
+
+ MOZ_COUNT_DTOR(InputStreamParent);
+ }
+
+ void
+ AssertIsOnOwningThread() const
+ {
+#ifdef DEBUG
+ MOZ_ASSERT(PR_GetCurrentThread() == mOwningThread);
+#endif
+ }
+
+ bool
+ Destroy(const InputStreamParams& aParams,
+ const OptionalFileDescriptorSet& aFDs)
+ {
+ AssertIsOnOwningThread();
+
+ if (mSyncLoopGuard) {
+ MOZ_ASSERT(!*mSyncLoopGuard);
+
+ *mSyncLoopGuard = true;
+ *mParams = aParams;
+ *mFDs = aFDs;
+
+ // We're not a live actor so manage the memory ourselves.
+ delete this;
+ return true;
+ }
+
+ // This will be destroyed by BlobParent::DeallocPBlobStreamParent.
+ return PBlobStreamParent::Send__delete__(this, aParams, aFDs);
+ }
+
+private:
+ // This method is only called by the IPDL message machinery.
+ virtual void
+ ActorDestroy(ActorDestroyReason aWhy) override
+ {
+ // Nothing needs to be done here.
+ }
+};
+
+struct MOZ_STACK_CLASS CreateBlobImplMetadata final
+{
+ nsString mContentType;
+ nsString mName;
+ uint64_t mLength;
+ int64_t mLastModifiedDate;
+ bool mHasRecursed;
+ const bool mIsSameProcessActor;
+
+ explicit CreateBlobImplMetadata(bool aIsSameProcessActor)
+ : mLength(0)
+ , mLastModifiedDate(0)
+ , mHasRecursed(false)
+ , mIsSameProcessActor(aIsSameProcessActor)
+ {
+ MOZ_COUNT_CTOR(CreateBlobImplMetadata);
+
+ mName.SetIsVoid(true);
+ }
+
+ ~CreateBlobImplMetadata()
+ {
+ MOZ_COUNT_DTOR(CreateBlobImplMetadata);
+ }
+
+ bool
+ IsFile() const
+ {
+ return !mName.IsVoid();
+ }
+};
+
+already_AddRefed<BlobImpl>
+CreateBlobImpl(const nsID& aKnownBlobIDData,
+ const CreateBlobImplMetadata& aMetadata)
+{
+ MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
+ MOZ_ASSERT(aMetadata.mHasRecursed);
+
+ RefPtr<BlobImpl> blobImpl = BlobParent::GetBlobImplForID(aKnownBlobIDData);
+ if (NS_WARN_IF(!blobImpl)) {
+ ASSERT_UNLESS_FUZZING();
+ return nullptr;
+ }
+
+ DebugOnly<bool> isMutable;
+ MOZ_ASSERT(NS_SUCCEEDED(blobImpl->GetMutable(&isMutable)));
+ MOZ_ASSERT(!isMutable);
+
+ return blobImpl.forget();
+}
+
+already_AddRefed<BlobImpl>
+CreateBlobImpl(const BlobDataStream& aStream,
+ const CreateBlobImplMetadata& aMetadata)
+{
+ MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
+
+ nsCOMPtr<nsIInputStream> inputStream = DeserializeIPCStream(aStream.stream());
+ if (!inputStream) {
+ ASSERT_UNLESS_FUZZING();
+ return nullptr;
+ }
+
+ uint64_t length = aStream.length();
+
+ RefPtr<BlobImpl> blobImpl;
+ if (!aMetadata.mHasRecursed && aMetadata.IsFile()) {
+ if (length) {
+ blobImpl =
+ BlobImplStream::Create(inputStream,
+ aMetadata.mName,
+ aMetadata.mContentType,
+ aMetadata.mLastModifiedDate,
+ length);
+ } else {
+ blobImpl =
+ new EmptyBlobImpl(aMetadata.mName,
+ aMetadata.mContentType,
+ aMetadata.mLastModifiedDate);
+ }
+ } else if (length) {
+ blobImpl =
+ BlobImplStream::Create(inputStream, aMetadata.mContentType,
+ length);
+ } else {
+ blobImpl = new EmptyBlobImpl(aMetadata.mContentType);
+ }
+
+ MOZ_ALWAYS_SUCCEEDS(blobImpl->SetMutable(false));
+
+ return blobImpl.forget();
+}
+
+already_AddRefed<BlobImpl>
+CreateBlobImpl(const nsTArray<BlobData>& aBlobData,
+ CreateBlobImplMetadata& aMetadata);
+
+already_AddRefed<BlobImpl>
+CreateBlobImplFromBlobData(const BlobData& aBlobData,
+ CreateBlobImplMetadata& aMetadata)
+{
+ MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
+
+ RefPtr<BlobImpl> blobImpl;
+
+ switch (aBlobData.type()) {
+ case BlobData::TnsID: {
+ blobImpl = CreateBlobImpl(aBlobData.get_nsID(), aMetadata);
+ break;
+ }
+
+ case BlobData::TBlobDataStream: {
+ blobImpl = CreateBlobImpl(aBlobData.get_BlobDataStream(), aMetadata);
+ break;
+ }
+
+ case BlobData::TArrayOfBlobData: {
+ blobImpl = CreateBlobImpl(aBlobData.get_ArrayOfBlobData(), aMetadata);
+ break;
+ }
+
+ default:
+ MOZ_CRASH("Unknown params!");
+ }
+
+ return blobImpl.forget();
+}
+
+already_AddRefed<BlobImpl>
+CreateBlobImpl(const nsTArray<BlobData>& aBlobDatas,
+ CreateBlobImplMetadata& aMetadata)
+{
+ MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
+
+ // Special case for a multipart blob with only one part.
+ if (aBlobDatas.Length() == 1) {
+ const BlobData& blobData = aBlobDatas[0];
+
+ RefPtr<BlobImpl> blobImpl =
+ CreateBlobImplFromBlobData(blobData, aMetadata);
+ if (NS_WARN_IF(!blobImpl)) {
+ return nullptr;
+ }
+
+ DebugOnly<bool> isMutable;
+ MOZ_ASSERT(NS_SUCCEEDED(blobImpl->GetMutable(&isMutable)));
+ MOZ_ASSERT(!isMutable);
+
+ return blobImpl.forget();
+ }
+
+ nsTArray<RefPtr<BlobImpl>> blobImpls;
+ if (NS_WARN_IF(!blobImpls.SetLength(aBlobDatas.Length(), fallible))) {
+ return nullptr;
+ }
+
+ const bool hasRecursed = aMetadata.mHasRecursed;
+ aMetadata.mHasRecursed = true;
+
+ for (uint32_t count = aBlobDatas.Length(), index = 0;
+ index < count;
+ index++) {
+ const BlobData& blobData = aBlobDatas[index];
+ RefPtr<BlobImpl>& blobImpl = blobImpls[index];
+
+ blobImpl = CreateBlobImplFromBlobData(blobData, aMetadata);
+ if (NS_WARN_IF(!blobImpl)) {
+ return nullptr;
+ }
+
+ DebugOnly<bool> isMutable;
+ MOZ_ASSERT(NS_SUCCEEDED(blobImpl->GetMutable(&isMutable)));
+ MOZ_ASSERT(!isMutable);
+ }
+
+ ErrorResult rv;
+ RefPtr<BlobImpl> blobImpl;
+ if (!hasRecursed && aMetadata.IsFile()) {
+ blobImpl = MultipartBlobImpl::Create(Move(blobImpls), aMetadata.mName,
+ aMetadata.mContentType, rv);
+ } else {
+ blobImpl = MultipartBlobImpl::Create(Move(blobImpls), aMetadata.mContentType, rv);
+ }
+
+ if (NS_WARN_IF(rv.Failed())) {
+ rv.SuppressException();
+ return nullptr;
+ }
+
+ MOZ_ALWAYS_SUCCEEDS(blobImpl->SetMutable(false));
+
+ return blobImpl.forget();
+}
+
+already_AddRefed<BlobImpl>
+CreateBlobImpl(const ParentBlobConstructorParams& aParams,
+ const BlobData& aBlobData,
+ bool aIsSameProcessActor)
+{
+ MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
+ MOZ_ASSERT(aParams.blobParams().type() ==
+ AnyBlobConstructorParams::TNormalBlobConstructorParams ||
+ aParams.blobParams().type() ==
+ AnyBlobConstructorParams::TFileBlobConstructorParams);
+
+ CreateBlobImplMetadata metadata(aIsSameProcessActor);
+
+ if (aParams.blobParams().type() ==
+ AnyBlobConstructorParams::TNormalBlobConstructorParams) {
+ const NormalBlobConstructorParams& params =
+ aParams.blobParams().get_NormalBlobConstructorParams();
+
+ if (NS_WARN_IF(params.length() == UINT64_MAX)) {
+ ASSERT_UNLESS_FUZZING();
+ return nullptr;
+ }
+
+ metadata.mContentType = params.contentType();
+ metadata.mLength = params.length();
+ } else {
+ const FileBlobConstructorParams& params =
+ aParams.blobParams().get_FileBlobConstructorParams();
+
+ if (NS_WARN_IF(params.length() == UINT64_MAX)) {
+ ASSERT_UNLESS_FUZZING();
+ return nullptr;
+ }
+
+ if (NS_WARN_IF(params.modDate() == INT64_MAX)) {
+ ASSERT_UNLESS_FUZZING();
+ return nullptr;
+ }
+
+ if (NS_WARN_IF(!params.path().IsEmpty())) {
+ ASSERT_UNLESS_FUZZING();
+ return nullptr;
+ }
+
+ metadata.mContentType = params.contentType();
+ metadata.mName = params.name();
+ metadata.mLength = params.length();
+ metadata.mLastModifiedDate = params.modDate();
+ }
+
+ RefPtr<BlobImpl> blobImpl =
+ CreateBlobImplFromBlobData(aBlobData, metadata);
+ return blobImpl.forget();
+}
+
+template <class ChildManagerType>
+void
+BlobDataFromBlobImpl(ChildManagerType* aManager, BlobImpl* aBlobImpl,
+ BlobData& aBlobData,
+ nsTArray<UniquePtr<AutoIPCStream>>& aIPCStreams)
+{
+ MOZ_ASSERT(gProcessType != GeckoProcessType_Default);
+ MOZ_ASSERT(aBlobImpl);
+
+ const nsTArray<RefPtr<BlobImpl>>* subBlobs = aBlobImpl->GetSubBlobImpls();
+
+ if (subBlobs) {
+ MOZ_ASSERT(subBlobs->Length());
+
+ aBlobData = nsTArray<BlobData>();
+
+ nsTArray<BlobData>& subBlobDatas = aBlobData.get_ArrayOfBlobData();
+ subBlobDatas.SetLength(subBlobs->Length());
+
+ for (uint32_t count = subBlobs->Length(), index = 0;
+ index < count;
+ index++) {
+ BlobDataFromBlobImpl(aManager, subBlobs->ElementAt(index),
+ subBlobDatas[index], aIPCStreams);
+ }
+
+ return;
+ }
+
+ nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryInterface(aBlobImpl);
+ if (remoteBlob) {
+ BlobChild* actor = remoteBlob->GetBlobChild();
+ MOZ_ASSERT(actor);
+
+ aBlobData = actor->ParentID();
+ return;
+ }
+
+ ErrorResult rv;
+ uint64_t length = aBlobImpl->GetSize(rv);
+ MOZ_ALWAYS_TRUE(!rv.Failed());
+
+ nsCOMPtr<nsIInputStream> inputStream;
+ aBlobImpl->GetInternalStream(getter_AddRefs(inputStream), rv);
+ MOZ_ALWAYS_TRUE(!rv.Failed());
+
+ UniquePtr<AutoIPCStream> autoStream(new AutoIPCStream());
+ autoStream->Serialize(inputStream, aManager);
+ aBlobData = BlobDataStream(autoStream->TakeValue(), length);
+
+ aIPCStreams.AppendElement(Move(autoStream));
+}
+
+RemoteInputStream::RemoteInputStream(BlobImpl* aBlobImpl,
+ uint64_t aStart,
+ uint64_t aLength)
+ : mMonitor("RemoteInputStream.mMonitor")
+ , mActor(nullptr)
+ , mBlobImpl(aBlobImpl)
+ , mWeakSeekableStream(nullptr)
+ , mWeakFileMetadata(nullptr)
+ , mStart(aStart)
+ , mLength(aLength)
+{
+ MOZ_ASSERT(aBlobImpl);
+
+ if (!NS_IsMainThread()) {
+ mEventTarget = do_GetCurrentThread();
+ MOZ_ASSERT(mEventTarget);
+ }
+
+ MOZ_ASSERT(IsOnOwningThread());
+}
+
+RemoteInputStream::RemoteInputStream(BlobChild* aActor,
+ BlobImpl* aBlobImpl,
+ uint64_t aStart,
+ uint64_t aLength)
+ : mMonitor("RemoteInputStream.mMonitor")
+ , mActor(aActor)
+ , mBlobImpl(aBlobImpl)
+ , mEventTarget(NS_GetCurrentThread())
+ , mWeakSeekableStream(nullptr)
+ , mWeakFileMetadata(nullptr)
+ , mStart(aStart)
+ , mLength(aLength)
+{
+ MOZ_ASSERT(!NS_IsMainThread());
+ MOZ_ASSERT(aActor);
+ MOZ_ASSERT(aBlobImpl);
+
+ MOZ_ASSERT(IsOnOwningThread());
+}
+
+RemoteInputStream::~RemoteInputStream()
+{
+ if (!IsOnOwningThread()) {
+ mStream = nullptr;
+ mWeakSeekableStream = nullptr;
+ mWeakFileMetadata = nullptr;
+
+ if (mBlobImpl) {
+ ReleaseOnTarget(mBlobImpl, mEventTarget);
+ }
+ }
+}
+
+void
+RemoteInputStream::SetStream(nsIInputStream* aStream)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aStream);
+
+ nsCOMPtr<nsIInputStream> stream = aStream;
+ nsCOMPtr<nsISeekableStream> seekableStream = do_QueryInterface(aStream);
+ nsCOMPtr<nsIFileMetadata> fileMetadata = do_QueryInterface(aStream);
+
+ MOZ_ASSERT_IF(seekableStream, SameCOMIdentity(aStream, seekableStream));
+ MOZ_ASSERT_IF(fileMetadata, SameCOMIdentity(aStream, fileMetadata));
+
+ {
+ MonitorAutoLock lock(mMonitor);
+
+ MOZ_ASSERT_IF(mStream, IsWorkerStream());
+
+ if (!mStream) {
+ MOZ_ASSERT(!mWeakSeekableStream);
+ MOZ_ASSERT(!mWeakFileMetadata);
+
+ mStream.swap(stream);
+ mWeakSeekableStream = seekableStream;
+ mWeakFileMetadata = fileMetadata;
+
+ mMonitor.Notify();
+ }
+ }
+}
+
+nsresult
+RemoteInputStream::BlockAndWaitForStream()
+{
+ if (mStream) {
+ return NS_OK;
+ }
+
+ if (IsOnOwningThread()) {
+ if (NS_IsMainThread()) {
+ NS_WARNING("Blocking the main thread is not supported!");
+ return NS_ERROR_FAILURE;
+ }
+
+ MOZ_ASSERT(IsWorkerStream());
+
+ InputStreamParams params;
+ OptionalFileDescriptorSet optionalFDs;
+
+ mActor->SendBlobStreamSync(mStart, mLength, &params, &optionalFDs);
+
+ nsTArray<FileDescriptor> fds;
+ OptionalFileDescriptorSetToFDs(optionalFDs, fds);
+
+ nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(params, fds);
+ MOZ_ASSERT(stream);
+
+ SetStream(stream);
+ return NS_OK;
+ }
+
+ ReallyBlockAndWaitForStream();
+
+ return NS_OK;
+}
+
+void
+RemoteInputStream::ReallyBlockAndWaitForStream()
+{
+ MOZ_ASSERT(!IsOnOwningThread());
+
+ DebugOnly<bool> waited;
+
+ {
+ MonitorAutoLock lock(mMonitor);
+
+ waited = !mStream;
+
+ while (!mStream) {
+ mMonitor.Wait();
+ }
+ }
+
+ MOZ_ASSERT(mStream);
+
+#ifdef DEBUG
+ if (waited && mWeakSeekableStream) {
+ int64_t position;
+ if (NS_SUCCEEDED(mWeakSeekableStream->Tell(&position))) {
+ MOZ_ASSERT(!position, "Stream not starting at 0!");
+ }
+ }
+#endif
+}
+
+bool
+RemoteInputStream::IsSeekableStream()
+{
+ if (IsOnOwningThread()) {
+ if (!mStream) {
+ NS_WARNING("Don't know if this stream is seekable yet!");
+ return true;
+ }
+ } else {
+ ReallyBlockAndWaitForStream();
+ }
+
+ return !!mWeakSeekableStream;
+}
+
+bool
+RemoteInputStream::IsFileMetadata()
+{
+ if (IsOnOwningThread()) {
+ if (!mStream) {
+ NS_WARNING("Don't know if this stream supports file metadata yet!");
+ return true;
+ }
+ } else {
+ ReallyBlockAndWaitForStream();
+ }
+
+ return !!mWeakFileMetadata;
+}
+
+NS_IMPL_ADDREF(RemoteInputStream)
+NS_IMPL_RELEASE(RemoteInputStream)
+
+NS_INTERFACE_MAP_BEGIN(RemoteInputStream)
+ NS_INTERFACE_MAP_ENTRY(nsIInputStream)
+ NS_INTERFACE_MAP_ENTRY(nsIIPCSerializableInputStream)
+ NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsISeekableStream, IsSeekableStream())
+ NS_INTERFACE_MAP_ENTRY_CONDITIONAL(nsIFileMetadata, IsFileMetadata())
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIInputStream)
+ NS_INTERFACE_MAP_ENTRY(IPrivateRemoteInputStream)
+NS_INTERFACE_MAP_END
+
+NS_IMETHODIMP
+RemoteInputStream::Close()
+{
+ nsresult rv = BlockAndWaitForStream();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ RefPtr<BlobImpl> blobImpl;
+ mBlobImpl.swap(blobImpl);
+
+ rv = mStream->Close();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RemoteInputStream::Available(uint64_t* aAvailable)
+{
+ if (!IsOnOwningThread()) {
+ nsresult rv = BlockAndWaitForStream();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mStream->Available(aAvailable);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+ }
+
+#ifdef DEBUG
+ if (NS_IsMainThread()) {
+ NS_WARNING("Someone is trying to do main-thread I/O...");
+ }
+#endif
+
+ nsresult rv;
+
+ // See if we already have our real stream.
+ nsCOMPtr<nsIInputStream> inputStream;
+ {
+ MonitorAutoLock lock(mMonitor);
+
+ inputStream = mStream;
+ }
+
+ // If we do then just call through.
+ if (inputStream) {
+ rv = inputStream->Available(aAvailable);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+ }
+
+ // If the stream is already closed then we can't do anything.
+ if (!mBlobImpl) {
+ return NS_BASE_STREAM_CLOSED;
+ }
+
+ // Otherwise fake it...
+ NS_WARNING("Available() called before real stream has been delivered, "
+ "guessing the amount of data available!");
+
+ ErrorResult error;
+ *aAvailable = mBlobImpl->GetSize(error);
+ if (NS_WARN_IF(error.Failed())) {
+ return error.StealNSResult();
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RemoteInputStream::Read(char* aBuffer, uint32_t aCount, uint32_t* aResult)
+{
+ nsresult rv = BlockAndWaitForStream();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mStream->Read(aBuffer, aCount, aResult);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RemoteInputStream::ReadSegments(nsWriteSegmentFun aWriter,
+ void* aClosure,
+ uint32_t aCount,
+ uint32_t* aResult)
+{
+ nsresult rv = BlockAndWaitForStream();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mStream->ReadSegments(aWriter, aClosure, aCount, aResult);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RemoteInputStream::IsNonBlocking(bool* aNonBlocking)
+{
+ NS_ENSURE_ARG_POINTER(aNonBlocking);
+
+ *aNonBlocking = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RemoteInputStream::Seek(int32_t aWhence, int64_t aOffset)
+{
+ nsresult rv = BlockAndWaitForStream();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!mWeakSeekableStream) {
+ NS_WARNING("Underlying blob stream is not seekable!");
+ return NS_ERROR_NO_INTERFACE;
+ }
+
+ rv = mWeakSeekableStream->Seek(aWhence, aOffset);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RemoteInputStream::Tell(int64_t* aResult)
+{
+ // We can cheat here and assume that we're going to start at 0 if we don't yet
+ // have our stream. Though, really, this should abort since most input streams
+ // could block here.
+ if (IsOnOwningThread() && !mStream) {
+ *aResult = 0;
+ return NS_OK;
+ }
+
+ nsresult rv = BlockAndWaitForStream();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!mWeakSeekableStream) {
+ NS_WARNING("Underlying blob stream is not seekable!");
+ return NS_ERROR_NO_INTERFACE;
+ }
+
+ rv = mWeakSeekableStream->Tell(aResult);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RemoteInputStream::SetEOF()
+{
+ nsresult rv = BlockAndWaitForStream();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!mWeakSeekableStream) {
+ NS_WARNING("Underlying blob stream is not seekable!");
+ return NS_ERROR_NO_INTERFACE;
+ }
+
+ rv = mWeakSeekableStream->SetEOF();
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
+void
+RemoteInputStream::Serialize(InputStreamParams& aParams,
+ FileDescriptorArray& /* aFDs */)
+{
+ MOZ_RELEASE_ASSERT(mBlobImpl);
+
+ nsCOMPtr<nsIRemoteBlob> remote = do_QueryInterface(mBlobImpl);
+ MOZ_ASSERT(remote);
+
+ BlobChild* actor = remote->GetBlobChild();
+ MOZ_ASSERT(actor);
+
+ aParams = RemoteInputStreamParams(actor->ParentID());
+}
+
+bool
+RemoteInputStream::Deserialize(const InputStreamParams& /* aParams */,
+ const FileDescriptorArray& /* aFDs */)
+{
+ // See InputStreamUtils.cpp to see how deserialization of a
+ // RemoteInputStream is special-cased.
+ MOZ_CRASH("RemoteInputStream should never be deserialized");
+}
+
+NS_IMETHODIMP
+RemoteInputStream::GetSize(int64_t* aSize)
+{
+ nsresult rv = BlockAndWaitForStream();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (!mWeakFileMetadata) {
+ NS_WARNING("Underlying blob stream doesn't support file metadata!");
+ return NS_ERROR_NO_INTERFACE;
+ }
+
+ rv = mWeakFileMetadata->GetSize(aSize);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RemoteInputStream::GetLastModified(int64_t* aLastModified)
+{
+ nsresult rv = BlockAndWaitForStream();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (!mWeakFileMetadata) {
+ NS_WARNING("Underlying blob stream doesn't support file metadata!");
+ return NS_ERROR_NO_INTERFACE;
+ }
+
+ rv = mWeakFileMetadata->GetLastModified(aLastModified);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+RemoteInputStream::GetFileDescriptor(PRFileDesc** aFileDescriptor)
+{
+ nsresult rv = BlockAndWaitForStream();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ if (!mWeakFileMetadata) {
+ NS_WARNING("Underlying blob stream doesn't support file metadata!");
+ return NS_ERROR_NO_INTERFACE;
+ }
+
+ rv = mWeakFileMetadata->GetFileDescriptor(aFileDescriptor);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+Maybe<uint64_t>
+RemoteInputStream::ExpectedSerializedLength()
+{
+ return Nothing();
+}
+
+nsIInputStream*
+RemoteInputStream::BlockAndGetInternalStream()
+{
+ MOZ_ASSERT(!IsOnOwningThread());
+
+ nsresult rv = BlockAndWaitForStream();
+ NS_ENSURE_SUCCESS(rv, nullptr);
+
+ return mStream;
+}
+
+} // namespace
+
+StaticAutoPtr<BlobParent::IDTable> BlobParent::sIDTable;
+StaticAutoPtr<Mutex> BlobParent::sIDTableMutex;
+
+/*******************************************************************************
+ * BlobParent::IDTableEntry Declaration
+ ******************************************************************************/
+
+class BlobParent::IDTableEntry final
+{
+ const nsID mID;
+ const intptr_t mProcessID;
+ const RefPtr<BlobImpl> mBlobImpl;
+
+public:
+ static already_AddRefed<IDTableEntry>
+ Create(const nsID& aID, intptr_t aProcessID, BlobImpl* aBlobImpl)
+ {
+ MOZ_ASSERT(aBlobImpl);
+
+ DebugOnly<bool> isMutable;
+ MOZ_ASSERT(NS_SUCCEEDED(aBlobImpl->GetMutable(&isMutable)));
+ MOZ_ASSERT(!isMutable);
+
+ return GetOrCreateInternal(aID,
+ aProcessID,
+ aBlobImpl,
+ /* aMayCreate */ true,
+ /* aMayGet */ false,
+ /* aIgnoreProcessID */ false);
+ }
+
+ static already_AddRefed<IDTableEntry>
+ Get(const nsID& aID, intptr_t aProcessID)
+ {
+ return GetOrCreateInternal(aID,
+ aProcessID,
+ nullptr,
+ /* aMayCreate */ false,
+ /* aMayGet */ true,
+ /* aIgnoreProcessID */ false);
+ }
+
+ static already_AddRefed<IDTableEntry>
+ Get(const nsID& aID)
+ {
+ return GetOrCreateInternal(aID,
+ 0,
+ nullptr,
+ /* aMayCreate */ false,
+ /* aMayGet */ true,
+ /* aIgnoreProcessID */ true);
+ }
+
+ static already_AddRefed<IDTableEntry>
+ GetOrCreate(const nsID& aID, intptr_t aProcessID, BlobImpl* aBlobImpl)
+ {
+ MOZ_ASSERT(aBlobImpl);
+
+ DebugOnly<bool> isMutable;
+ MOZ_ASSERT(NS_SUCCEEDED(aBlobImpl->GetMutable(&isMutable)));
+ MOZ_ASSERT(!isMutable);
+
+ return GetOrCreateInternal(aID,
+ aProcessID,
+ aBlobImpl,
+ /* aMayCreate */ true,
+ /* aMayGet */ true,
+ /* aIgnoreProcessID */ false);
+ }
+
+ const nsID&
+ ID() const
+ {
+ return mID;
+ }
+
+ intptr_t
+ ProcessID() const
+ {
+ return mProcessID;
+ }
+
+ BlobImpl*
+ GetBlobImpl() const
+ {
+ return mBlobImpl;
+ }
+
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(IDTableEntry)
+
+private:
+ IDTableEntry(const nsID& aID, intptr_t aProcessID, BlobImpl* aBlobImpl);
+ ~IDTableEntry();
+
+ static already_AddRefed<IDTableEntry>
+ GetOrCreateInternal(const nsID& aID,
+ intptr_t aProcessID,
+ BlobImpl* aBlobImpl,
+ bool aMayCreate,
+ bool aMayGet,
+ bool aIgnoreProcessID);
+};
+
+/*******************************************************************************
+ * BlobParent::OpenStreamRunnable Declaration
+ ******************************************************************************/
+
+// Each instance of this class will be dispatched to the network stream thread
+// pool to run the first time where it will open the file input stream. It will
+// then dispatch itself back to the owning thread to send the child process its
+// response (assuming that the child has not crashed). The runnable will then
+// dispatch itself to the thread pool again in order to close the file input
+// stream.
+class BlobParent::OpenStreamRunnable final
+ : public Runnable
+{
+ friend class nsRevocableEventPtr<OpenStreamRunnable>;
+
+ // Only safe to access these pointers if mRevoked is false!
+ BlobParent* mBlobActor;
+ InputStreamParent* mStreamActor;
+
+ nsCOMPtr<nsIInputStream> mStream;
+ nsCOMPtr<nsIIPCSerializableInputStream> mSerializable;
+ nsCOMPtr<nsIEventTarget> mActorTarget;
+ nsCOMPtr<nsIThread> mIOTarget;
+
+ bool mRevoked;
+ bool mClosing;
+
+public:
+ OpenStreamRunnable(BlobParent* aBlobActor,
+ InputStreamParent* aStreamActor,
+ nsIInputStream* aStream,
+ nsIIPCSerializableInputStream* aSerializable,
+ nsIThread* aIOTarget)
+ : mBlobActor(aBlobActor)
+ , mStreamActor(aStreamActor)
+ , mStream(aStream)
+ , mSerializable(aSerializable)
+ , mIOTarget(aIOTarget)
+ , mRevoked(false)
+ , mClosing(false)
+ {
+ MOZ_ASSERT(aBlobActor);
+ aBlobActor->AssertIsOnOwningThread();
+ MOZ_ASSERT(aStreamActor);
+ MOZ_ASSERT(aStream);
+ // aSerializable may be null.
+ MOZ_ASSERT(aIOTarget);
+
+ if (!NS_IsMainThread()) {
+ AssertIsOnBackgroundThread();
+
+ mActorTarget = do_GetCurrentThread();
+ MOZ_ASSERT(mActorTarget);
+ }
+
+ AssertIsOnOwningThread();
+ }
+
+ nsresult
+ Dispatch()
+ {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mIOTarget);
+
+ nsresult rv = mIOTarget->Dispatch(this, NS_DISPATCH_NORMAL);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+ }
+
+ NS_DECL_ISUPPORTS_INHERITED
+
+private:
+ ~OpenStreamRunnable()
+ { }
+
+ bool
+ IsOnOwningThread() const
+ {
+ return EventTargetIsOnCurrentThread(mActorTarget);
+ }
+
+ void
+ AssertIsOnOwningThread() const
+ {
+ MOZ_ASSERT(IsOnOwningThread());
+ }
+
+ void
+ Revoke()
+ {
+ AssertIsOnOwningThread();
+#ifdef DEBUG
+ mBlobActor = nullptr;
+ mStreamActor = nullptr;
+#endif
+ mRevoked = true;
+ }
+
+ nsresult
+ OpenStream()
+ {
+ MOZ_ASSERT(!IsOnOwningThread());
+ MOZ_ASSERT(mStream);
+
+ if (!mSerializable) {
+ nsCOMPtr<IPrivateRemoteInputStream> remoteStream =
+ do_QueryInterface(mStream);
+ MOZ_ASSERT(remoteStream, "Must QI to IPrivateRemoteInputStream here!");
+
+ nsCOMPtr<nsIInputStream> realStream =
+ remoteStream->BlockAndGetInternalStream();
+ NS_ENSURE_TRUE(realStream, NS_ERROR_FAILURE);
+
+ mSerializable = do_QueryInterface(realStream);
+ if (!mSerializable) {
+ MOZ_ASSERT(false, "Must be serializable!");
+ return NS_ERROR_FAILURE;
+ }
+
+ mStream.swap(realStream);
+ }
+
+ // To force the stream open we call Available(). We don't actually care
+ // how much data is available.
+ uint64_t available;
+ if (NS_FAILED(mStream->Available(&available))) {
+ NS_WARNING("Available failed on this stream!");
+ }
+
+ if (mActorTarget) {
+ nsresult rv = mActorTarget->Dispatch(this, NS_DISPATCH_NORMAL);
+ NS_ENSURE_SUCCESS(rv, rv);
+ } else {
+ MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
+ }
+
+ return NS_OK;
+ }
+
+ nsresult
+ CloseStream()
+ {
+ MOZ_ASSERT(!IsOnOwningThread());
+ MOZ_ASSERT(mStream);
+
+ // Going to always release here.
+ nsCOMPtr<nsIInputStream> stream;
+ mStream.swap(stream);
+
+ nsCOMPtr<nsIThread> ioTarget;
+ mIOTarget.swap(ioTarget);
+
+ DebugOnly<nsresult> rv = stream->Close();
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to close stream!");
+
+ MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(NewRunnableMethod(ioTarget, &nsIThread::Shutdown)));
+
+ return NS_OK;
+ }
+
+ nsresult
+ SendResponse()
+ {
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mStream);
+ MOZ_ASSERT(mSerializable);
+ MOZ_ASSERT(mIOTarget);
+ MOZ_ASSERT(!mClosing);
+
+ nsCOMPtr<nsIIPCSerializableInputStream> serializable;
+ mSerializable.swap(serializable);
+
+ if (mRevoked) {
+ MOZ_ASSERT(!mBlobActor);
+ MOZ_ASSERT(!mStreamActor);
+ }
+ else {
+ MOZ_ASSERT(mBlobActor);
+ MOZ_ASSERT(mBlobActor->HasManager());
+ MOZ_ASSERT(mStreamActor);
+
+ InputStreamParams params;
+ nsTArray<FileDescriptor> fds;
+ serializable->Serialize(params, fds);
+
+ MOZ_ASSERT(params.type() != InputStreamParams::T__None);
+
+ OptionalFileDescriptorSet optionalFDSet;
+ if (nsIContentParent* contentManager = mBlobActor->GetContentManager()) {
+ ConstructFileDescriptorSet(contentManager, fds, optionalFDSet);
+ } else {
+ ConstructFileDescriptorSet(mBlobActor->GetBackgroundManager(),
+ fds,
+ optionalFDSet);
+ }
+
+ mStreamActor->Destroy(params, optionalFDSet);
+
+ mBlobActor->NoteRunnableCompleted(this);
+
+#ifdef DEBUG
+ mBlobActor = nullptr;
+ mStreamActor = nullptr;
+#endif
+ }
+
+ // If our luck is *really* bad then it is possible for the CloseStream() and
+ // nsIThread::Shutdown() functions to run before the Dispatch() call here
+ // finishes... Keep the thread alive until this method returns.
+ nsCOMPtr<nsIThread> kungFuDeathGrip = mIOTarget;
+
+ mClosing = true;
+
+ nsresult rv = kungFuDeathGrip->Dispatch(this, NS_DISPATCH_NORMAL);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+ }
+
+ NS_IMETHOD
+ Run() override
+ {
+ MOZ_ASSERT(mIOTarget);
+
+ if (IsOnOwningThread()) {
+ return SendResponse();
+ }
+
+ if (!mClosing) {
+ return OpenStream();
+ }
+
+ return CloseStream();
+ }
+};
+
+NS_IMPL_ISUPPORTS_INHERITED0(BlobParent::OpenStreamRunnable, Runnable)
+
+/*******************************************************************************
+ * BlobChild::RemoteBlobImpl Declaration
+ ******************************************************************************/
+
+class BlobChild::RemoteBlobImpl
+ : public BlobImplBase
+ , public nsIRemoteBlob
+{
+protected:
+ class CreateStreamHelper;
+ class WorkerHolder;
+
+ BlobChild* mActor;
+ nsCOMPtr<nsIEventTarget> mActorTarget;
+
+ // These member variables are protected by mutex and it's set to null when the
+ // worker goes away.
+ WorkerPrivate* mWorkerPrivate;
+ nsAutoPtr<WorkerHolder> mWorkerHolder;
+ Mutex mMutex;
+
+ // We use this pointer to keep a live a blobImpl coming from a different
+ // process until this one is fully created. We set it to null when
+ // SendCreatedFromKnownBlob() is received. This is used only with KnownBlob
+ // params in the CTOR of a IPC BlobImpl.
+ RefPtr<BlobImpl> mDifferentProcessBlobImpl;
+
+ RefPtr<BlobImpl> mSameProcessBlobImpl;
+
+ const bool mIsSlice;
+
+ const bool mIsDirectory;
+
+public:
+
+ enum BlobImplIsDirectory
+ {
+ eNotDirectory,
+ eDirectory
+ };
+
+ // For File.
+ RemoteBlobImpl(BlobChild* aActor,
+ BlobImpl* aRemoteBlobImpl,
+ const nsAString& aName,
+ const nsAString& aContentType,
+ const nsAString& aPath,
+ uint64_t aLength,
+ int64_t aModDate,
+ BlobImplIsDirectory aIsDirectory,
+ bool aIsSameProcessBlob);
+
+ // For Blob.
+ RemoteBlobImpl(BlobChild* aActor,
+ BlobImpl* aRemoteBlobImpl,
+ const nsAString& aContentType,
+ uint64_t aLength,
+ bool aIsSameProcessBlob);
+
+ // For mystery blobs.
+ explicit
+ RemoteBlobImpl(BlobChild* aActor);
+
+ void
+ NoteDyingActor();
+
+ BlobChild*
+ GetActor() const
+ {
+ MOZ_ASSERT(ActorEventTargetIsOnCurrentThread());
+
+ return mActor;
+ }
+
+ nsIEventTarget*
+ GetActorEventTarget() const
+ {
+ return mActorTarget;
+ }
+
+ bool
+ ActorEventTargetIsOnCurrentThread() const
+ {
+ return EventTargetIsOnCurrentThread(BaseRemoteBlobImpl()->mActorTarget);
+ }
+
+ bool
+ IsSlice() const
+ {
+ return mIsSlice;
+ }
+
+ RemoteBlobSliceImpl*
+ AsSlice() const;
+
+ RemoteBlobImpl*
+ BaseRemoteBlobImpl() const;
+
+ NS_DECL_ISUPPORTS_INHERITED
+
+ virtual void
+ GetMozFullPathInternal(nsAString& aFileName, ErrorResult& aRv) const override;
+
+ virtual bool
+ IsDirectory() const override;
+
+ virtual already_AddRefed<BlobImpl>
+ CreateSlice(uint64_t aStart,
+ uint64_t aLength,
+ const nsAString& aContentType,
+ ErrorResult& aRv) override;
+
+ virtual void
+ GetInternalStream(nsIInputStream** aStream, ErrorResult& aRv) override;
+
+ virtual int64_t
+ GetFileId() override;
+
+ virtual int64_t
+ GetLastModified(ErrorResult& aRv) override;
+
+ virtual void
+ SetLastModified(int64_t aLastModified) override;
+
+ virtual nsresult
+ SetMutable(bool aMutable) override;
+
+ virtual BlobChild*
+ GetBlobChild() override;
+
+ virtual BlobParent*
+ GetBlobParent() override;
+
+ void
+ NullifyDifferentProcessBlobImpl()
+ {
+ MOZ_ASSERT(mDifferentProcessBlobImpl);
+ mDifferentProcessBlobImpl = nullptr;
+ }
+
+ // Used only by CreateStreamHelper, it dispatches a runnable to the target
+ // thread. This thread can be the main-thread, the background thread or a
+ // worker. If the thread is a worker, the aRunnable is wrapper into a
+ // ControlRunnable in order to avoid to be blocked into a sync event loop.
+ nsresult
+ DispatchToTarget(nsIRunnable* aRunnable);
+
+ void
+ WorkerHasNotified();
+
+protected:
+ // For SliceImpl.
+ RemoteBlobImpl(const nsAString& aContentType, uint64_t aLength);
+
+ ~RemoteBlobImpl()
+ {
+ MOZ_ASSERT_IF(mActorTarget,
+ EventTargetIsOnCurrentThread(mActorTarget));
+ }
+
+ void
+ CommonInit(BlobChild* aActor);
+
+ void
+ Destroy();
+};
+
+class BlobChild::RemoteBlobImpl::WorkerHolder final
+ : public workers::WorkerHolder
+{
+ // Raw pointer because this class is kept alive by the mRemoteBlobImpl.
+ RemoteBlobImpl* mRemoteBlobImpl;
+
+public:
+ explicit WorkerHolder(RemoteBlobImpl* aRemoteBlobImpl)
+ : mRemoteBlobImpl(aRemoteBlobImpl)
+ {
+ MOZ_ASSERT(aRemoteBlobImpl);
+ }
+
+ bool Notify(Status aStatus) override
+ {
+ mRemoteBlobImpl->WorkerHasNotified();
+ return true;
+ }
+};
+
+class BlobChild::RemoteBlobImpl::CreateStreamHelper final
+ : public CancelableRunnable
+{
+ Monitor mMonitor;
+ RefPtr<RemoteBlobImpl> mRemoteBlobImpl;
+ RefPtr<RemoteInputStream> mInputStream;
+ const uint64_t mStart;
+ const uint64_t mLength;
+ bool mDone;
+
+public:
+ explicit CreateStreamHelper(RemoteBlobImpl* aRemoteBlobImpl);
+
+ nsresult
+ GetStream(nsIInputStream** aInputStream);
+
+ NS_IMETHOD Run() override;
+
+private:
+ ~CreateStreamHelper()
+ {
+ MOZ_ASSERT(!mRemoteBlobImpl);
+ MOZ_ASSERT(!mInputStream);
+ MOZ_ASSERT(mDone);
+ }
+
+ void
+ RunInternal(RemoteBlobImpl* aBaseRemoteBlobImpl, bool aNotify);
+};
+
+class BlobChild::RemoteBlobSliceImpl final
+ : public RemoteBlobImpl
+{
+ RefPtr<RemoteBlobImpl> mParent;
+ bool mActorWasCreated;
+
+public:
+ RemoteBlobSliceImpl(RemoteBlobImpl* aParent,
+ uint64_t aStart,
+ uint64_t aLength,
+ const nsAString& aContentType);
+
+ RemoteBlobImpl*
+ Parent() const
+ {
+ MOZ_ASSERT(mParent);
+
+ return const_cast<RemoteBlobImpl*>(mParent.get());
+ }
+
+ uint64_t
+ Start() const
+ {
+ return mStart;
+ }
+
+ void
+ EnsureActorWasCreated()
+ {
+ MOZ_ASSERT_IF(!ActorEventTargetIsOnCurrentThread(),
+ mActorWasCreated);
+
+ if (!mActorWasCreated) {
+ EnsureActorWasCreatedInternal();
+ }
+ }
+
+ NS_DECL_ISUPPORTS_INHERITED
+
+ virtual BlobChild*
+ GetBlobChild() override;
+
+private:
+ ~RemoteBlobSliceImpl()
+ { }
+
+ void
+ EnsureActorWasCreatedInternal();
+};
+
+/*******************************************************************************
+ * BlobParent::RemoteBlobImpl Declaration
+ ******************************************************************************/
+
+class BlobParent::RemoteBlobImpl final
+ : public BlobImpl
+ , public nsIRemoteBlob
+{
+ BlobParent* mActor;
+ nsCOMPtr<nsIEventTarget> mActorTarget;
+ RefPtr<BlobImpl> mBlobImpl;
+
+public:
+ RemoteBlobImpl(BlobParent* aActor, BlobImpl* aBlobImpl);
+
+ void
+ NoteDyingActor();
+
+ NS_DECL_ISUPPORTS_INHERITED
+
+ virtual void
+ GetName(nsAString& aName) const override;
+
+ virtual void
+ GetDOMPath(nsAString& aPath) const override;
+
+ virtual void
+ SetDOMPath(const nsAString& aPath) override;
+
+ virtual int64_t
+ GetLastModified(ErrorResult& aRv) override;
+
+ virtual void
+ SetLastModified(int64_t aLastModified) override;
+
+ virtual void
+ GetMozFullPath(nsAString& aName, ErrorResult& aRv) const override;
+
+ virtual void
+ GetMozFullPathInternal(nsAString& aFileName, ErrorResult& aRv) const override;
+
+ virtual bool
+ IsDirectory() const override;
+
+ virtual uint64_t
+ GetSize(ErrorResult& aRv) override;
+
+ virtual void
+ GetType(nsAString& aType) override;
+
+ virtual uint64_t
+ GetSerialNumber() const override;
+
+ virtual already_AddRefed<BlobImpl>
+ CreateSlice(uint64_t aStart,
+ uint64_t aLength,
+ const nsAString& aContentType,
+ ErrorResult& aRv) override;
+
+ virtual const nsTArray<RefPtr<BlobImpl>>*
+ GetSubBlobImpls() const override;
+
+ virtual void
+ GetInternalStream(nsIInputStream** aStream, ErrorResult& aRv) override;
+
+ virtual int64_t
+ GetFileId() override;
+
+ virtual nsresult
+ GetSendInfo(nsIInputStream** aBody,
+ uint64_t* aContentLength,
+ nsACString& aContentType,
+ nsACString& aCharset) override;
+
+ virtual nsresult
+ GetMutable(bool* aMutable) const override;
+
+ virtual nsresult
+ SetMutable(bool aMutable) override;
+
+ virtual void
+ SetLazyData(const nsAString& aName,
+ const nsAString& aContentType,
+ uint64_t aLength,
+ int64_t aLastModifiedDate) override;
+
+ virtual bool
+ IsMemoryFile() const override;
+
+ virtual bool
+ IsSizeUnknown() const override;
+
+ virtual bool
+ IsDateUnknown() const override;
+
+ virtual bool
+ IsFile() const override;
+
+ virtual bool
+ MayBeClonedToOtherThreads() const override;
+
+ virtual BlobChild*
+ GetBlobChild() override;
+
+ virtual BlobParent*
+ GetBlobParent() override;
+
+private:
+ ~RemoteBlobImpl()
+ {
+ MOZ_ASSERT_IF(mActorTarget,
+ EventTargetIsOnCurrentThread(mActorTarget));
+ }
+
+ void
+ Destroy();
+};
+
+/*******************************************************************************
+ * BlobChild::RemoteBlobImpl
+ ******************************************************************************/
+
+BlobChild::
+RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor,
+ BlobImpl* aRemoteBlobImpl,
+ const nsAString& aName,
+ const nsAString& aContentType,
+ const nsAString& aDOMPath,
+ uint64_t aLength,
+ int64_t aModDate,
+ BlobImplIsDirectory aIsDirectory,
+ bool aIsSameProcessBlob)
+ : BlobImplBase(aName, aContentType, aLength, aModDate)
+ , mWorkerPrivate(nullptr)
+ , mMutex("BlobChild::RemoteBlobImpl::mMutex")
+ , mIsSlice(false), mIsDirectory(aIsDirectory == eDirectory)
+{
+ SetDOMPath(aDOMPath);
+
+ if (aIsSameProcessBlob) {
+ MOZ_ASSERT(aRemoteBlobImpl);
+ mSameProcessBlobImpl = aRemoteBlobImpl;
+ MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
+ } else {
+ mDifferentProcessBlobImpl = aRemoteBlobImpl;
+ }
+
+ CommonInit(aActor);
+}
+
+BlobChild::
+RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor,
+ BlobImpl* aRemoteBlobImpl,
+ const nsAString& aContentType,
+ uint64_t aLength,
+ bool aIsSameProcessBlob)
+ : BlobImplBase(aContentType, aLength)
+ , mWorkerPrivate(nullptr)
+ , mMutex("BlobChild::RemoteBlobImpl::mMutex")
+ , mIsSlice(false), mIsDirectory(false)
+{
+ if (aIsSameProcessBlob) {
+ MOZ_ASSERT(aRemoteBlobImpl);
+ mSameProcessBlobImpl = aRemoteBlobImpl;
+ MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
+ } else {
+ mDifferentProcessBlobImpl = aRemoteBlobImpl;
+ }
+
+ CommonInit(aActor);
+}
+
+BlobChild::
+RemoteBlobImpl::RemoteBlobImpl(BlobChild* aActor)
+ : BlobImplBase(EmptyString(), EmptyString(), UINT64_MAX, INT64_MAX)
+ , mWorkerPrivate(nullptr)
+ , mMutex("BlobChild::RemoteBlobImpl::mMutex")
+ , mIsSlice(false), mIsDirectory(false)
+{
+ CommonInit(aActor);
+}
+
+BlobChild::
+RemoteBlobImpl::RemoteBlobImpl(const nsAString& aContentType, uint64_t aLength)
+ : BlobImplBase(aContentType, aLength)
+ , mActor(nullptr)
+ , mWorkerPrivate(nullptr)
+ , mMutex("BlobChild::RemoteBlobImpl::mMutex")
+ , mIsSlice(true)
+ , mIsDirectory(false)
+{
+ mImmutable = true;
+}
+
+void
+BlobChild::
+RemoteBlobImpl::CommonInit(BlobChild* aActor)
+{
+ MOZ_ASSERT(aActor);
+ aActor->AssertIsOnOwningThread();
+
+ mActor = aActor;
+ mActorTarget = aActor->EventTarget();
+
+ if (!NS_IsMainThread()) {
+ mWorkerPrivate = GetCurrentThreadWorkerPrivate();
+ // We must comunicate via IPC in the owning thread, so, if this BlobImpl has
+ // been created on a Workerr and then it's sent to a different thread (for
+ // instance the main-thread), we still need to keep alive that Worker.
+ if (mWorkerPrivate) {
+ mWorkerHolder = new RemoteBlobImpl::WorkerHolder(this);
+ if (NS_WARN_IF(!mWorkerHolder->HoldWorker(mWorkerPrivate, Closing))) {
+ // We don't care too much if the worker is already going away because no
+ // sync-event-loop can be created at this point.
+ mWorkerPrivate = nullptr;
+ mWorkerHolder = nullptr;
+ }
+ }
+ }
+
+ mImmutable = true;
+}
+
+void
+BlobChild::
+RemoteBlobImpl::NoteDyingActor()
+{
+ MOZ_ASSERT(mActor);
+ mActor->AssertIsOnOwningThread();
+
+ mActor = nullptr;
+}
+
+BlobChild::RemoteBlobSliceImpl*
+BlobChild::
+RemoteBlobImpl::AsSlice() const
+{
+ MOZ_ASSERT(IsSlice());
+
+ return static_cast<RemoteBlobSliceImpl*>(const_cast<RemoteBlobImpl*>(this));
+}
+
+BlobChild::RemoteBlobImpl*
+BlobChild::
+RemoteBlobImpl::BaseRemoteBlobImpl() const
+{
+ if (IsSlice()) {
+ return AsSlice()->Parent()->BaseRemoteBlobImpl();
+ }
+
+ return const_cast<RemoteBlobImpl*>(this);
+}
+
+void
+BlobChild::
+RemoteBlobImpl::Destroy()
+{
+ if (EventTargetIsOnCurrentThread(mActorTarget)) {
+ if (mActor) {
+ mActor->AssertIsOnOwningThread();
+ mActor->NoteDyingRemoteBlobImpl();
+ }
+
+ if (mWorkerHolder) {
+ // We are in the worker thread.
+ MutexAutoLock lock(mMutex);
+ mWorkerPrivate = nullptr;
+ mWorkerHolder = nullptr;
+ }
+
+ delete this;
+ return;
+ }
+
+ nsCOMPtr<nsIRunnable> destroyRunnable =
+ NewNonOwningRunnableMethod(this, &RemoteBlobImpl::Destroy);
+
+ if (mActorTarget) {
+ destroyRunnable =
+ new CancelableRunnableWrapper(destroyRunnable, mActorTarget);
+
+ MOZ_ALWAYS_SUCCEEDS(mActorTarget->Dispatch(destroyRunnable,
+ NS_DISPATCH_NORMAL));
+ } else {
+ MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(destroyRunnable));
+ }
+}
+
+NS_IMPL_ADDREF(BlobChild::RemoteBlobImpl)
+NS_IMPL_RELEASE_WITH_DESTROY(BlobChild::RemoteBlobImpl, Destroy())
+NS_IMPL_QUERY_INTERFACE_INHERITED(BlobChild::RemoteBlobImpl,
+ BlobImpl,
+ nsIRemoteBlob)
+
+void
+BlobChild::
+RemoteBlobImpl::GetMozFullPathInternal(nsAString& aFilePath,
+ ErrorResult& aRv) const
+{
+ if (!EventTargetIsOnCurrentThread(mActorTarget)) {
+ MOZ_CRASH("Not implemented!");
+ }
+
+ if (mSameProcessBlobImpl) {
+ MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
+
+ mSameProcessBlobImpl->GetMozFullPathInternal(aFilePath, aRv);
+ return;
+ }
+
+ if (!mActor) {
+ aRv.Throw(NS_ERROR_UNEXPECTED);
+ return;
+ }
+
+ nsString filePath;
+ if (!mActor->SendGetFilePath(&filePath)) {
+ aRv.Throw(NS_ERROR_FAILURE);
+ return;
+ }
+
+ aFilePath = filePath;
+}
+
+bool
+BlobChild::
+RemoteBlobImpl::IsDirectory() const
+{
+ return mIsDirectory;
+}
+
+already_AddRefed<BlobImpl>
+BlobChild::
+RemoteBlobImpl::CreateSlice(uint64_t aStart,
+ uint64_t aLength,
+ const nsAString& aContentType,
+ ErrorResult& aRv)
+{
+ // May be called on any thread.
+ if (mSameProcessBlobImpl) {
+ MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
+
+ return mSameProcessBlobImpl->CreateSlice(aStart,
+ aLength,
+ aContentType,
+ aRv);
+ }
+
+ RefPtr<RemoteBlobSliceImpl> slice =
+ new RemoteBlobSliceImpl(this, aStart, aLength, aContentType);
+ return slice.forget();
+}
+
+void
+BlobChild::
+RemoteBlobImpl::GetInternalStream(nsIInputStream** aStream, ErrorResult& aRv)
+{
+ // May be called on any thread.
+ if (mSameProcessBlobImpl) {
+ MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
+
+ nsCOMPtr<nsIInputStream> realStream;
+ mSameProcessBlobImpl->GetInternalStream(getter_AddRefs(realStream), aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
+
+ RefPtr<BlobInputStreamTether> tether =
+ new BlobInputStreamTether(realStream, mSameProcessBlobImpl);
+ tether.forget(aStream);
+ return;
+ }
+
+ RefPtr<CreateStreamHelper> helper = new CreateStreamHelper(this);
+ aRv = helper->GetStream(aStream);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
+}
+
+int64_t
+BlobChild::
+RemoteBlobImpl::GetFileId()
+{
+ if (!EventTargetIsOnCurrentThread(mActorTarget)) {
+ MOZ_CRASH("Not implemented!");
+ }
+
+ if (mSameProcessBlobImpl) {
+ MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
+
+ return mSameProcessBlobImpl->GetFileId();
+ }
+
+ int64_t fileId;
+ if (mActor && mActor->SendGetFileId(&fileId)) {
+ return fileId;
+ }
+
+ return -1;
+}
+
+int64_t
+BlobChild::
+RemoteBlobImpl::GetLastModified(ErrorResult& aRv)
+{
+ if (IsDateUnknown()) {
+ return 0;
+ }
+
+ return mLastModificationDate;
+}
+
+void
+BlobChild::
+RemoteBlobImpl::SetLastModified(int64_t aLastModified)
+{
+ MOZ_CRASH("SetLastModified of a remote blob is not allowed!");
+}
+
+nsresult
+BlobChild::
+RemoteBlobImpl::SetMutable(bool aMutable)
+{
+ if (!aMutable && IsSlice()) {
+ // Make sure that slices are backed by a real actor now while we are still
+ // on the correct thread.
+ AsSlice()->EnsureActorWasCreated();
+ }
+
+ nsresult rv = BlobImplBase::SetMutable(aMutable);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ MOZ_ASSERT_IF(!aMutable, mImmutable);
+
+ return NS_OK;
+}
+
+BlobChild*
+BlobChild::
+RemoteBlobImpl::GetBlobChild()
+{
+ return mActor;
+}
+
+BlobParent*
+BlobChild::
+RemoteBlobImpl::GetBlobParent()
+{
+ return nullptr;
+}
+
+class RemoteBlobControlRunnable : public WorkerControlRunnable
+{
+ nsCOMPtr<nsIRunnable> mRunnable;
+
+public:
+ RemoteBlobControlRunnable(WorkerPrivate* aWorkerPrivate,
+ nsIRunnable* aRunnable)
+ : WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
+ , mRunnable(aRunnable)
+ {
+ MOZ_ASSERT(aRunnable);
+ }
+
+ bool
+ WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
+ {
+ mRunnable->Run();
+ return true;
+ }
+};
+
+nsresult
+BlobChild::
+RemoteBlobImpl::DispatchToTarget(nsIRunnable* aRunnable)
+{
+ MOZ_ASSERT(aRunnable);
+
+ // We have to protected mWorkerPrivate because this method can be called by
+ // any thread (sort of).
+ MutexAutoLock lock(mMutex);
+
+ if (mWorkerPrivate) {
+ MOZ_ASSERT(mWorkerHolder);
+
+ RefPtr<RemoteBlobControlRunnable> controlRunnable =
+ new RemoteBlobControlRunnable(mWorkerPrivate, aRunnable);
+ if (!controlRunnable->Dispatch()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIEventTarget> target = BaseRemoteBlobImpl()->GetActorEventTarget();
+ if (!target) {
+ target = do_GetMainThread();
+ }
+
+ MOZ_ASSERT(target);
+
+ return target->Dispatch(aRunnable, NS_DISPATCH_NORMAL);
+}
+
+void
+BlobChild::
+RemoteBlobImpl::WorkerHasNotified()
+{
+ MutexAutoLock lock(mMutex);
+
+ mWorkerHolder->ReleaseWorker();
+
+ mWorkerHolder = nullptr;
+ mWorkerPrivate = nullptr;
+}
+
+/*******************************************************************************
+ * BlobChild::RemoteBlobImpl::CreateStreamHelper
+ ******************************************************************************/
+
+BlobChild::RemoteBlobImpl::
+CreateStreamHelper::CreateStreamHelper(RemoteBlobImpl* aRemoteBlobImpl)
+ : mMonitor("BlobChild::RemoteBlobImpl::CreateStreamHelper::mMonitor")
+ , mRemoteBlobImpl(aRemoteBlobImpl)
+ , mStart(aRemoteBlobImpl->IsSlice() ? aRemoteBlobImpl->AsSlice()->Start() : 0)
+ , mLength(0)
+ , mDone(false)
+{
+ // This may be created on any thread.
+ MOZ_ASSERT(aRemoteBlobImpl);
+
+ ErrorResult rv;
+ const_cast<uint64_t&>(mLength) = aRemoteBlobImpl->GetSize(rv);
+ MOZ_ASSERT(!rv.Failed());
+}
+
+nsresult
+BlobChild::RemoteBlobImpl::
+CreateStreamHelper::GetStream(nsIInputStream** aInputStream)
+{
+ // This may be called on any thread.
+ MOZ_ASSERT(aInputStream);
+ MOZ_ASSERT(mRemoteBlobImpl);
+ MOZ_ASSERT(!mInputStream);
+ MOZ_ASSERT(!mDone);
+
+ RefPtr<RemoteBlobImpl> baseRemoteBlobImpl =
+ mRemoteBlobImpl->BaseRemoteBlobImpl();
+ MOZ_ASSERT(baseRemoteBlobImpl);
+
+ if (EventTargetIsOnCurrentThread(baseRemoteBlobImpl->GetActorEventTarget())) {
+ RunInternal(baseRemoteBlobImpl, false);
+ } else {
+ nsresult rv = baseRemoteBlobImpl->DispatchToTarget(this);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ DebugOnly<bool> warned = false;
+
+ {
+ MonitorAutoLock lock(mMonitor);
+
+ while (!mDone) {
+#ifdef DEBUG
+ if (!warned) {
+ NS_WARNING("RemoteBlobImpl::GetInternalStream() called on thread "
+ "that can't send messages, blocking here to wait for the "
+ "actor's thread to send the message!");
+ }
+#endif
+ lock.Wait();
+ }
+ }
+ }
+
+ MOZ_ASSERT(!mRemoteBlobImpl);
+ MOZ_ASSERT(mDone);
+
+ if (!mInputStream) {
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ mInputStream.forget(aInputStream);
+ return NS_OK;
+}
+
+void
+BlobChild::RemoteBlobImpl::
+CreateStreamHelper::RunInternal(RemoteBlobImpl* aBaseRemoteBlobImpl,
+ bool aNotify)
+{
+ MOZ_ASSERT(aBaseRemoteBlobImpl);
+ MOZ_ASSERT(aBaseRemoteBlobImpl->ActorEventTargetIsOnCurrentThread());
+ MOZ_ASSERT(!mInputStream);
+ MOZ_ASSERT(!mDone);
+
+ if (BlobChild* actor = aBaseRemoteBlobImpl->GetActor()) {
+ RefPtr<RemoteInputStream> stream;
+
+ if (!NS_IsMainThread() && GetCurrentThreadWorkerPrivate()) {
+ stream =
+ new RemoteInputStream(actor, mRemoteBlobImpl, mStart, mLength);
+ } else {
+ stream = new RemoteInputStream(mRemoteBlobImpl, mStart, mLength);
+ }
+
+ InputStreamChild* streamActor = new InputStreamChild(stream);
+ if (actor->SendPBlobStreamConstructor(streamActor, mStart, mLength)) {
+ stream.swap(mInputStream);
+ }
+ }
+
+ mRemoteBlobImpl = nullptr;
+
+ if (aNotify) {
+ MonitorAutoLock lock(mMonitor);
+ mDone = true;
+ lock.Notify();
+ } else {
+ mDone = true;
+ }
+}
+
+NS_IMETHODIMP
+BlobChild::RemoteBlobImpl::
+CreateStreamHelper::Run()
+{
+ MOZ_ASSERT(mRemoteBlobImpl);
+ MOZ_ASSERT(mRemoteBlobImpl->ActorEventTargetIsOnCurrentThread());
+
+ RefPtr<RemoteBlobImpl> baseRemoteBlobImpl =
+ mRemoteBlobImpl->BaseRemoteBlobImpl();
+ MOZ_ASSERT(baseRemoteBlobImpl);
+
+ RunInternal(baseRemoteBlobImpl, true);
+ return NS_OK;
+}
+
+/*******************************************************************************
+ * BlobChild::RemoteBlobSliceImpl
+ ******************************************************************************/
+
+BlobChild::
+RemoteBlobSliceImpl::RemoteBlobSliceImpl(RemoteBlobImpl* aParent,
+ uint64_t aStart,
+ uint64_t aLength,
+ const nsAString& aContentType)
+ : RemoteBlobImpl(aContentType, aLength)
+ , mParent(aParent->BaseRemoteBlobImpl())
+ , mActorWasCreated(false)
+{
+ MOZ_ASSERT(mParent);
+ MOZ_ASSERT(mParent->BaseRemoteBlobImpl() == mParent);
+
+ DebugOnly<bool> isMutable;
+ MOZ_ASSERT(NS_SUCCEEDED(aParent->GetMutable(&isMutable)));
+ MOZ_ASSERT(!isMutable);
+
+#ifdef DEBUG
+ {
+ ErrorResult rv;
+ uint64_t parentSize = aParent->GetSize(rv);
+ MOZ_ASSERT(!rv.Failed());
+ MOZ_ASSERT(parentSize >= aStart + aLength);
+ }
+#endif
+
+ // Account for the offset of the parent slice, if any.
+ mStart = aParent->IsSlice() ? aParent->AsSlice()->mStart + aStart : aStart;
+}
+
+void
+BlobChild::
+RemoteBlobSliceImpl::EnsureActorWasCreatedInternal()
+{
+ MOZ_ASSERT(ActorEventTargetIsOnCurrentThread());
+ MOZ_ASSERT(!mActorWasCreated);
+
+ mActorWasCreated = true;
+
+ BlobChild* baseActor = mParent->GetActor();
+ MOZ_ASSERT(baseActor);
+ MOZ_ASSERT(baseActor->HasManager());
+
+ nsID id;
+ MOZ_ALWAYS_SUCCEEDS(gUUIDGenerator->GenerateUUIDInPlace(&id));
+
+ ParentBlobConstructorParams params(
+ SlicedBlobConstructorParams(nullptr /* sourceParent */,
+ baseActor /* sourceChild */,
+ id /* id */,
+ mStart /* begin */,
+ mStart + mLength /* end */,
+ mContentType /* contentType */));
+
+ BlobChild* actor;
+
+ if (nsIContentChild* contentManager = baseActor->GetContentManager()) {
+ actor = SendSliceConstructor(contentManager, this, params);
+ } else {
+ actor =
+ SendSliceConstructor(baseActor->GetBackgroundManager(), this, params);
+ }
+
+ CommonInit(actor);
+}
+
+NS_IMPL_ISUPPORTS_INHERITED0(BlobChild::RemoteBlobSliceImpl,
+ BlobChild::RemoteBlobImpl)
+
+BlobChild*
+BlobChild::
+RemoteBlobSliceImpl::GetBlobChild()
+{
+ EnsureActorWasCreated();
+
+ return RemoteBlobImpl::GetBlobChild();
+}
+
+/*******************************************************************************
+ * BlobParent::RemoteBlobImpl
+ ******************************************************************************/
+
+BlobParent::
+RemoteBlobImpl::RemoteBlobImpl(BlobParent* aActor, BlobImpl* aBlobImpl)
+ : mActor(aActor)
+ , mActorTarget(aActor->EventTarget())
+ , mBlobImpl(aBlobImpl)
+{
+ MOZ_ASSERT(aActor);
+ aActor->AssertIsOnOwningThread();
+ MOZ_ASSERT(aBlobImpl);
+
+ DebugOnly<bool> isMutable;
+ MOZ_ASSERT(NS_SUCCEEDED(aBlobImpl->GetMutable(&isMutable)));
+ MOZ_ASSERT(!isMutable);
+}
+
+void
+BlobParent::
+RemoteBlobImpl::NoteDyingActor()
+{
+ MOZ_ASSERT(mActor);
+ mActor->AssertIsOnOwningThread();
+
+ mActor = nullptr;
+}
+
+void
+BlobParent::
+RemoteBlobImpl::Destroy()
+{
+ if (EventTargetIsOnCurrentThread(mActorTarget)) {
+ if (mActor) {
+ mActor->AssertIsOnOwningThread();
+ mActor->NoteDyingRemoteBlobImpl();
+ }
+
+ delete this;
+ return;
+ }
+
+ nsCOMPtr<nsIRunnable> destroyRunnable =
+ NewNonOwningRunnableMethod(this, &RemoteBlobImpl::Destroy);
+
+ if (mActorTarget) {
+ destroyRunnable =
+ new CancelableRunnableWrapper(destroyRunnable, mActorTarget);
+
+ MOZ_ALWAYS_SUCCEEDS(mActorTarget->Dispatch(destroyRunnable,
+ NS_DISPATCH_NORMAL));
+ } else {
+ MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(destroyRunnable));
+ }
+}
+
+NS_IMPL_ADDREF(BlobParent::RemoteBlobImpl)
+NS_IMPL_RELEASE_WITH_DESTROY(BlobParent::RemoteBlobImpl, Destroy())
+NS_IMPL_QUERY_INTERFACE_INHERITED(BlobParent::RemoteBlobImpl,
+ BlobImpl,
+ nsIRemoteBlob)
+
+void
+BlobParent::
+RemoteBlobImpl::GetName(nsAString& aName) const
+{
+ mBlobImpl->GetName(aName);
+}
+
+void
+BlobParent::
+RemoteBlobImpl::GetDOMPath(nsAString& aPath) const
+{
+ mBlobImpl->GetDOMPath(aPath);
+}
+
+void
+BlobParent::
+RemoteBlobImpl::SetDOMPath(const nsAString& aPath)
+{
+ mBlobImpl->SetDOMPath(aPath);
+}
+
+int64_t
+BlobParent::
+RemoteBlobImpl::GetLastModified(ErrorResult& aRv)
+{
+ return mBlobImpl->GetLastModified(aRv);
+}
+
+void
+BlobParent::
+RemoteBlobImpl::SetLastModified(int64_t aLastModified)
+{
+ MOZ_CRASH("SetLastModified of a remote blob is not allowed!");
+}
+
+void
+BlobParent::
+RemoteBlobImpl::GetMozFullPath(nsAString& aName, ErrorResult& aRv) const
+{
+ mBlobImpl->GetMozFullPath(aName, aRv);
+}
+
+void
+BlobParent::
+RemoteBlobImpl::GetMozFullPathInternal(nsAString& aFileName, ErrorResult& aRv) const
+{
+ mBlobImpl->GetMozFullPathInternal(aFileName, aRv);
+}
+
+bool
+BlobParent::
+RemoteBlobImpl::IsDirectory() const
+{
+ return mBlobImpl->IsDirectory();
+}
+
+uint64_t
+BlobParent::
+RemoteBlobImpl::GetSize(ErrorResult& aRv)
+{
+ return mBlobImpl->GetSize(aRv);
+}
+
+void
+BlobParent::
+RemoteBlobImpl::GetType(nsAString& aType)
+{
+ mBlobImpl->GetType(aType);
+}
+
+uint64_t
+BlobParent::
+RemoteBlobImpl::GetSerialNumber() const
+{
+ return mBlobImpl->GetSerialNumber();
+}
+
+already_AddRefed<BlobImpl>
+BlobParent::
+RemoteBlobImpl::CreateSlice(uint64_t aStart,
+ uint64_t aLength,
+ const nsAString& aContentType,
+ ErrorResult& aRv)
+{
+ return mBlobImpl->CreateSlice(aStart, aLength, aContentType, aRv);
+}
+
+const nsTArray<RefPtr<BlobImpl>>*
+BlobParent::
+RemoteBlobImpl::GetSubBlobImpls() const
+{
+ return mBlobImpl->GetSubBlobImpls();
+}
+
+void
+BlobParent::
+RemoteBlobImpl::GetInternalStream(nsIInputStream** aStream, ErrorResult& aRv)
+{
+ mBlobImpl->GetInternalStream(aStream, aRv);
+}
+
+int64_t
+BlobParent::
+RemoteBlobImpl::GetFileId()
+{
+ return mBlobImpl->GetFileId();
+}
+
+nsresult
+BlobParent::
+RemoteBlobImpl::GetSendInfo(nsIInputStream** aBody,
+ uint64_t* aContentLength,
+ nsACString& aContentType,
+ nsACString& aCharset)
+{
+ return mBlobImpl->GetSendInfo(aBody,
+ aContentLength,
+ aContentType,
+ aCharset);
+}
+
+nsresult
+BlobParent::
+RemoteBlobImpl::GetMutable(bool* aMutable) const
+{
+ return mBlobImpl->GetMutable(aMutable);
+}
+
+nsresult
+BlobParent::
+RemoteBlobImpl::SetMutable(bool aMutable)
+{
+ return mBlobImpl->SetMutable(aMutable);
+}
+
+void
+BlobParent::
+RemoteBlobImpl::SetLazyData(const nsAString& aName,
+ const nsAString& aContentType,
+ uint64_t aLength,
+ int64_t aLastModifiedDate)
+{
+ MOZ_CRASH("This should never be called!");
+}
+
+bool
+BlobParent::
+RemoteBlobImpl::IsMemoryFile() const
+{
+ return mBlobImpl->IsMemoryFile();
+}
+
+bool
+BlobParent::
+RemoteBlobImpl::IsSizeUnknown() const
+{
+ return mBlobImpl->IsSizeUnknown();
+}
+
+bool
+BlobParent::
+RemoteBlobImpl::IsDateUnknown() const
+{
+ return mBlobImpl->IsDateUnknown();
+}
+
+bool
+BlobParent::
+RemoteBlobImpl::IsFile() const
+{
+ return mBlobImpl->IsFile();
+}
+
+bool
+BlobParent::
+RemoteBlobImpl::MayBeClonedToOtherThreads() const
+{
+ return mBlobImpl->MayBeClonedToOtherThreads();
+}
+
+BlobChild*
+BlobParent::
+RemoteBlobImpl::GetBlobChild()
+{
+ return nullptr;
+}
+
+BlobParent*
+BlobParent::
+RemoteBlobImpl::GetBlobParent()
+{
+ return mActor;
+}
+
+/*******************************************************************************
+ * BlobChild
+ ******************************************************************************/
+
+BlobChild::BlobChild(nsIContentChild* aManager, BlobImpl* aBlobImpl)
+ : mBackgroundManager(nullptr)
+ , mContentManager(aManager)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+
+ CommonInit(aBlobImpl);
+}
+
+BlobChild::BlobChild(PBackgroundChild* aManager, BlobImpl* aBlobImpl)
+ : mBackgroundManager(aManager)
+ , mContentManager(nullptr)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+
+ if (!NS_IsMainThread()) {
+ mEventTarget = do_GetCurrentThread();
+ MOZ_ASSERT(mEventTarget);
+ }
+
+ CommonInit(aBlobImpl);
+}
+
+BlobChild::BlobChild(nsIContentChild* aManager, BlobChild* aOther)
+ : mBackgroundManager(nullptr)
+ , mContentManager(aManager)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+
+ CommonInit(aOther, /* aBlobImpl */ nullptr);
+}
+
+BlobChild::BlobChild(PBackgroundChild* aManager,
+ BlobChild* aOther,
+ BlobImpl* aBlobImpl)
+ : mBackgroundManager(aManager)
+ , mContentManager(nullptr)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+ MOZ_ASSERT(aBlobImpl);
+
+ if (!NS_IsMainThread()) {
+ mEventTarget = do_GetCurrentThread();
+ MOZ_ASSERT(mEventTarget);
+ }
+
+ CommonInit(aOther, aBlobImpl);
+}
+
+BlobChild::BlobChild(nsIContentChild* aManager,
+ const ChildBlobConstructorParams& aParams)
+ : mBackgroundManager(nullptr)
+ , mContentManager(aManager)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+
+ CommonInit(aParams);
+}
+
+BlobChild::BlobChild(PBackgroundChild* aManager,
+ const ChildBlobConstructorParams& aParams)
+ : mBackgroundManager(aManager)
+ , mContentManager(nullptr)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+
+ if (!NS_IsMainThread()) {
+ mEventTarget = do_GetCurrentThread();
+ MOZ_ASSERT(mEventTarget);
+ }
+
+ CommonInit(aParams);
+}
+
+BlobChild::BlobChild(nsIContentChild* aManager,
+ const nsID& aParentID,
+ RemoteBlobSliceImpl* aRemoteBlobSliceImpl)
+ : mBackgroundManager(nullptr)
+ , mContentManager(aManager)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+
+ CommonInit(aParentID, aRemoteBlobSliceImpl);
+}
+
+BlobChild::BlobChild(PBackgroundChild* aManager,
+ const nsID& aParentID,
+ RemoteBlobSliceImpl* aRemoteBlobSliceImpl)
+ : mBackgroundManager(aManager)
+ , mContentManager(nullptr)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+
+ if (!NS_IsMainThread()) {
+ mEventTarget = do_GetCurrentThread();
+ MOZ_ASSERT(mEventTarget);
+ }
+
+ CommonInit(aParentID, aRemoteBlobSliceImpl);
+}
+
+BlobChild::~BlobChild()
+{
+ AssertIsOnOwningThread();
+
+ MOZ_COUNT_DTOR(BlobChild);
+}
+
+void
+BlobChild::CommonInit(BlobImpl* aBlobImpl)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aBlobImpl);
+
+ MOZ_COUNT_CTOR(BlobChild);
+
+ mBlobImpl = aBlobImpl;
+ mRemoteBlobImpl = nullptr;
+
+ mBlobImpl->AddRef();
+ mOwnsBlobImpl = true;
+
+ memset(&mParentID, 0, sizeof(mParentID));
+}
+
+void
+BlobChild::CommonInit(BlobChild* aOther, BlobImpl* aBlobImpl)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aOther);
+ MOZ_ASSERT_IF(mContentManager, aOther->GetBackgroundManager());
+ MOZ_ASSERT_IF(mContentManager, !aBlobImpl);
+ MOZ_ASSERT_IF(mBackgroundManager, aBlobImpl);
+
+ RefPtr<BlobImpl> otherImpl;
+ if (mBackgroundManager && aOther->GetBackgroundManager()) {
+ otherImpl = aBlobImpl;
+ } else {
+ otherImpl = aOther->GetBlobImpl();
+ }
+ MOZ_ASSERT(otherImpl);
+
+ nsString contentType;
+ otherImpl->GetType(contentType);
+
+ ErrorResult rv;
+ uint64_t length = otherImpl->GetSize(rv);
+ MOZ_ASSERT(!rv.Failed());
+
+ RemoteBlobImpl* remoteBlob = nullptr;
+ if (otherImpl->IsFile()) {
+ nsAutoString name;
+ otherImpl->GetName(name);
+
+ nsAutoString domPath;
+ otherImpl->GetDOMPath(domPath);
+
+ int64_t modDate = otherImpl->GetLastModified(rv);
+ MOZ_ASSERT(!rv.Failed());
+
+ RemoteBlobImpl::BlobImplIsDirectory directory = otherImpl->IsDirectory() ?
+ RemoteBlobImpl::BlobImplIsDirectory::eDirectory :
+ RemoteBlobImpl::BlobImplIsDirectory::eNotDirectory;
+
+ remoteBlob =
+ new RemoteBlobImpl(this, otherImpl, name, contentType, domPath,
+ length, modDate, directory,
+ false /* SameProcessBlobImpl */);
+ } else {
+ remoteBlob = new RemoteBlobImpl(this, otherImpl, contentType, length,
+ false /* SameProcessBlobImpl */);
+ }
+
+ // This RemoteBlob must be kept alive untill RecvCreatedFromKnownBlob is
+ // called because the parent will send this notification and we must be able
+ // to manage it.
+ MOZ_ASSERT(remoteBlob);
+ remoteBlob->AddRef();
+
+ CommonInit(aOther->ParentID(), remoteBlob);
+}
+
+void
+BlobChild::CommonInit(const ChildBlobConstructorParams& aParams)
+{
+ AssertIsOnOwningThread();
+
+ MOZ_COUNT_CTOR(BlobChild);
+
+ const AnyBlobConstructorParams& blobParams = aParams.blobParams();
+
+ AnyBlobConstructorParams::Type paramsType = blobParams.type();
+ MOZ_ASSERT(paramsType != AnyBlobConstructorParams::T__None &&
+ paramsType !=
+ AnyBlobConstructorParams::TSlicedBlobConstructorParams &&
+ paramsType !=
+ AnyBlobConstructorParams::TKnownBlobConstructorParams);
+
+ RefPtr<RemoteBlobImpl> remoteBlob;
+
+ switch (paramsType) {
+ case AnyBlobConstructorParams::TNormalBlobConstructorParams: {
+ const NormalBlobConstructorParams& params =
+ blobParams.get_NormalBlobConstructorParams();
+ remoteBlob =
+ new RemoteBlobImpl(this, nullptr, params.contentType(), params.length(),
+ false /* SameProcessBlobImpl */);
+ break;
+ }
+
+ case AnyBlobConstructorParams::TFileBlobConstructorParams: {
+ const FileBlobConstructorParams& params =
+ blobParams.get_FileBlobConstructorParams();
+ RemoteBlobImpl::BlobImplIsDirectory directory = params.isDirectory() ?
+ RemoteBlobImpl::BlobImplIsDirectory::eDirectory :
+ RemoteBlobImpl::BlobImplIsDirectory::eNotDirectory;
+ remoteBlob = new RemoteBlobImpl(this,
+ nullptr,
+ params.name(),
+ params.contentType(),
+ params.path(),
+ params.length(),
+ params.modDate(),
+ directory,
+ false /* SameProcessBlobImpl */);
+ break;
+ }
+
+ case AnyBlobConstructorParams::TSameProcessBlobConstructorParams: {
+ MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
+
+ const SameProcessBlobConstructorParams& params =
+ blobParams.get_SameProcessBlobConstructorParams();
+ MOZ_ASSERT(params.addRefedBlobImpl());
+
+ RefPtr<BlobImpl> blobImpl =
+ dont_AddRef(reinterpret_cast<BlobImpl*>(params.addRefedBlobImpl()));
+
+ ErrorResult rv;
+ uint64_t size = blobImpl->GetSize(rv);
+ MOZ_ASSERT(!rv.Failed());
+
+ nsString contentType;
+ blobImpl->GetType(contentType);
+
+ if (blobImpl->IsFile()) {
+ nsAutoString name;
+ blobImpl->GetName(name);
+
+ nsAutoString domPath;
+ blobImpl->GetDOMPath(domPath);
+
+ int64_t lastModifiedDate = blobImpl->GetLastModified(rv);
+ MOZ_ASSERT(!rv.Failed());
+
+ RemoteBlobImpl::BlobImplIsDirectory directory =
+ blobImpl->IsDirectory() ?
+ RemoteBlobImpl::BlobImplIsDirectory::eDirectory :
+ RemoteBlobImpl::BlobImplIsDirectory::eNotDirectory;
+
+ remoteBlob =
+ new RemoteBlobImpl(this,
+ blobImpl,
+ name,
+ contentType,
+ domPath,
+ size,
+ lastModifiedDate,
+ directory,
+ true /* SameProcessBlobImpl */);
+ } else {
+ remoteBlob = new RemoteBlobImpl(this, blobImpl, contentType, size,
+ true /* SameProcessBlobImpl */);
+ }
+
+ break;
+ }
+
+ case AnyBlobConstructorParams::TMysteryBlobConstructorParams: {
+ remoteBlob = new RemoteBlobImpl(this);
+ break;
+ }
+
+ default:
+ MOZ_CRASH("Unknown params!");
+ }
+
+ MOZ_ASSERT(remoteBlob);
+
+ DebugOnly<bool> isMutable;
+ MOZ_ASSERT(NS_SUCCEEDED(remoteBlob->GetMutable(&isMutable)));
+ MOZ_ASSERT(!isMutable);
+
+ mRemoteBlobImpl = remoteBlob;
+
+ remoteBlob.forget(&mBlobImpl);
+ mOwnsBlobImpl = true;
+
+ mParentID = aParams.id();
+}
+
+void
+BlobChild::CommonInit(const nsID& aParentID, RemoteBlobImpl* aRemoteBlobImpl)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aRemoteBlobImpl);
+
+ DebugOnly<bool> isMutable;
+ MOZ_ASSERT(NS_SUCCEEDED(aRemoteBlobImpl->GetMutable(&isMutable)));
+ MOZ_ASSERT(!isMutable);
+
+ MOZ_COUNT_CTOR(BlobChild);
+
+ RefPtr<RemoteBlobImpl> remoteBlob = aRemoteBlobImpl;
+
+ mRemoteBlobImpl = remoteBlob;
+
+ remoteBlob.forget(&mBlobImpl);
+ mOwnsBlobImpl = true;
+
+ mParentID = aParentID;
+}
+
+#ifdef DEBUG
+
+void
+BlobChild::AssertIsOnOwningThread() const
+{
+ MOZ_ASSERT(IsOnOwningThread());
+}
+
+#endif // DEBUG
+
+// static
+void
+BlobChild::Startup(const FriendKey& /* aKey */)
+{
+ MOZ_ASSERT(!XRE_IsParentProcess());
+
+ CommonStartup();
+}
+
+// static
+BlobChild*
+BlobChild::GetOrCreate(nsIContentChild* aManager, BlobImpl* aBlobImpl)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+
+ return GetOrCreateFromImpl(aManager, aBlobImpl);
+}
+
+// static
+BlobChild*
+BlobChild::GetOrCreate(PBackgroundChild* aManager, BlobImpl* aBlobImpl)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+
+ return GetOrCreateFromImpl(aManager, aBlobImpl);
+}
+
+// static
+BlobChild*
+BlobChild::Create(nsIContentChild* aManager,
+ const ChildBlobConstructorParams& aParams)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+
+ return CreateFromParams(aManager, aParams);
+}
+
+// static
+BlobChild*
+BlobChild::Create(PBackgroundChild* aManager,
+ const ChildBlobConstructorParams& aParams)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+
+ return CreateFromParams(aManager, aParams);
+}
+
+// static
+template <class ChildManagerType>
+BlobChild*
+BlobChild::GetOrCreateFromImpl(ChildManagerType* aManager,
+ BlobImpl* aBlobImpl)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+ MOZ_ASSERT(aBlobImpl);
+
+ // If the blob represents a wrapper around real blob implementation (so called
+ // snapshot) then we need to get the real one.
+ if (nsCOMPtr<PIBlobImplSnapshot> snapshot = do_QueryInterface(aBlobImpl)) {
+ aBlobImpl = snapshot->GetBlobImpl();
+ if (!aBlobImpl) {
+ // The snapshot is not valid anymore.
+ return nullptr;
+ }
+ }
+
+ // If the blob represents a remote blob then we can simply pass its actor back
+ // here.
+ if (nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryInterface(aBlobImpl)) {
+ BlobChild* actor =
+ MaybeGetActorFromRemoteBlob(remoteBlob, aManager, aBlobImpl);
+ if (actor) {
+ return actor;
+ }
+ }
+
+ // All blobs shared between threads or processes must be immutable.
+ if (NS_WARN_IF(NS_FAILED(aBlobImpl->SetMutable(false)))) {
+ return nullptr;
+ }
+
+ MOZ_ASSERT(!aBlobImpl->IsSizeUnknown());
+ MOZ_ASSERT(!aBlobImpl->IsDateUnknown());
+
+ AnyBlobConstructorParams blobParams;
+ nsTArray<UniquePtr<AutoIPCStream>> autoIPCStreams;
+
+ if (gProcessType == GeckoProcessType_Default) {
+ RefPtr<BlobImpl> sameProcessImpl = aBlobImpl;
+ auto addRefedBlobImpl =
+ reinterpret_cast<intptr_t>(sameProcessImpl.forget().take());
+
+ blobParams = SameProcessBlobConstructorParams(addRefedBlobImpl);
+ } else {
+ // BlobData is going to be populate here and it _must_ be send via IPC in
+ // order to avoid leaks.
+ BlobData blobData;
+ BlobDataFromBlobImpl(aManager, aBlobImpl, blobData, autoIPCStreams);
+
+ nsString contentType;
+ aBlobImpl->GetType(contentType);
+
+ ErrorResult rv;
+ uint64_t length = aBlobImpl->GetSize(rv);
+ MOZ_ASSERT(!rv.Failed());
+
+ if (aBlobImpl->IsFile()) {
+ nsAutoString name;
+ aBlobImpl->GetName(name);
+
+ nsAutoString domPath;
+ aBlobImpl->GetDOMPath(domPath);
+
+ int64_t modDate = aBlobImpl->GetLastModified(rv);
+ MOZ_ASSERT(!rv.Failed());
+
+ blobParams =
+ FileBlobConstructorParams(name, contentType, domPath, length, modDate,
+ aBlobImpl->IsDirectory(), blobData);
+ } else {
+ blobParams = NormalBlobConstructorParams(contentType, length, blobData);
+ }
+ }
+
+ BlobChild* actor = new BlobChild(aManager, aBlobImpl);
+
+ ParentBlobConstructorParams params(blobParams);
+
+ if (NS_WARN_IF(!aManager->SendPBlobConstructor(actor, params))) {
+ return nullptr;
+ }
+
+ autoIPCStreams.Clear();
+ return actor;
+}
+
+// static
+template <class ChildManagerType>
+BlobChild*
+BlobChild::CreateFromParams(ChildManagerType* aManager,
+ const ChildBlobConstructorParams& aParams)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+
+ const AnyBlobConstructorParams& blobParams = aParams.blobParams();
+
+ switch (blobParams.type()) {
+ case AnyBlobConstructorParams::TNormalBlobConstructorParams:
+ case AnyBlobConstructorParams::TFileBlobConstructorParams:
+ case AnyBlobConstructorParams::TSameProcessBlobConstructorParams:
+ case AnyBlobConstructorParams::TMysteryBlobConstructorParams: {
+ return new BlobChild(aManager, aParams);
+ }
+
+ case AnyBlobConstructorParams::TSlicedBlobConstructorParams: {
+ MOZ_CRASH("Parent should never send SlicedBlobConstructorParams!");
+ }
+
+ case AnyBlobConstructorParams::TKnownBlobConstructorParams: {
+ MOZ_CRASH("Parent should never send KnownBlobConstructorParams!");
+ }
+
+ default:
+ MOZ_CRASH("Unknown params!");
+ }
+
+ MOZ_CRASH("Should never get here!");
+}
+
+// static
+template <class ChildManagerType>
+BlobChild*
+BlobChild::SendSliceConstructor(ChildManagerType* aManager,
+ RemoteBlobSliceImpl* aRemoteBlobSliceImpl,
+ const ParentBlobConstructorParams& aParams)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+ MOZ_ASSERT(aRemoteBlobSliceImpl);
+ MOZ_ASSERT(aParams.blobParams().type() ==
+ AnyBlobConstructorParams::TSlicedBlobConstructorParams);
+
+ const nsID& id = aParams.blobParams().get_SlicedBlobConstructorParams().id();
+
+ BlobChild* newActor = new BlobChild(aManager, id, aRemoteBlobSliceImpl);
+
+ if (aManager->SendPBlobConstructor(newActor, aParams)) {
+ if (gProcessType != GeckoProcessType_Default || !NS_IsMainThread()) {
+ newActor->SendWaitForSliceCreation();
+ }
+ return newActor;
+ }
+
+ return nullptr;
+}
+
+// static
+BlobChild*
+BlobChild::MaybeGetActorFromRemoteBlob(nsIRemoteBlob* aRemoteBlob,
+ nsIContentChild* aManager,
+ BlobImpl* aBlobImpl)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aRemoteBlob);
+ MOZ_ASSERT(aManager);
+ MOZ_ASSERT(aBlobImpl);
+
+ if (BlobChild* actor = aRemoteBlob->GetBlobChild()) {
+ if (actor->GetContentManager() == aManager) {
+ return actor;
+ }
+
+ MOZ_ASSERT(actor->GetBackgroundManager());
+
+ actor = new BlobChild(aManager, actor);
+
+ ParentBlobConstructorParams params(
+ KnownBlobConstructorParams(actor->ParentID()));
+
+ aManager->SendPBlobConstructor(actor, params);
+
+ return actor;
+ }
+
+ return nullptr;
+}
+
+// static
+BlobChild*
+BlobChild::MaybeGetActorFromRemoteBlob(nsIRemoteBlob* aRemoteBlob,
+ PBackgroundChild* aManager,
+ BlobImpl* aBlobImpl)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aRemoteBlob);
+ MOZ_ASSERT(aManager);
+ MOZ_ASSERT(aBlobImpl);
+
+ if (BlobChild* actor = aRemoteBlob->GetBlobChild()) {
+ if (actor->GetBackgroundManager() == aManager) {
+ return actor;
+ }
+
+ actor = new BlobChild(aManager, actor, aBlobImpl);
+
+ ParentBlobConstructorParams params(
+ KnownBlobConstructorParams(actor->ParentID()));
+
+ aManager->SendPBlobConstructor(actor, params);
+
+ return actor;
+ }
+
+ return nullptr;
+}
+
+const nsID&
+BlobChild::ParentID() const
+{
+ MOZ_ASSERT(mRemoteBlobImpl);
+
+ return mParentID;
+}
+
+already_AddRefed<BlobImpl>
+BlobChild::GetBlobImpl()
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mBlobImpl);
+
+ RefPtr<BlobImpl> blobImpl;
+
+ // Remote blobs are held alive until the first call to GetBlobImpl. Thereafter
+ // we only hold a weak reference. Normal blobs are held alive until the actor
+ // is destroyed.
+ if (mRemoteBlobImpl && mOwnsBlobImpl) {
+ blobImpl = dont_AddRef(mBlobImpl);
+ mOwnsBlobImpl = false;
+ } else {
+ blobImpl = mBlobImpl;
+ }
+
+ MOZ_ASSERT(blobImpl);
+
+ return blobImpl.forget();
+}
+
+bool
+BlobChild::SetMysteryBlobInfo(const nsString& aName,
+ const nsString& aContentType,
+ uint64_t aLength,
+ int64_t aLastModifiedDate)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mBlobImpl);
+ MOZ_ASSERT(!mBlobImpl->IsDirectory());
+ MOZ_ASSERT(mRemoteBlobImpl);
+ MOZ_ASSERT(!mRemoteBlobImpl->IsDirectory());
+ MOZ_ASSERT(aLastModifiedDate != INT64_MAX);
+
+ mBlobImpl->SetLazyData(aName, aContentType, aLength, aLastModifiedDate);
+
+ FileBlobConstructorParams params(aName,
+ aContentType,
+ EmptyString(),
+ aLength,
+ aLastModifiedDate,
+ mBlobImpl->IsDirectory(),
+ void_t() /* optionalBlobData */);
+ return SendResolveMystery(params);
+}
+
+bool
+BlobChild::SetMysteryBlobInfo(const nsString& aContentType, uint64_t aLength)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mBlobImpl);
+ MOZ_ASSERT(mRemoteBlobImpl);
+
+ mBlobImpl->SetLazyData(NullString(), aContentType, aLength, INT64_MAX);
+
+ NormalBlobConstructorParams params(aContentType,
+ aLength,
+ void_t() /* optionalBlobData */);
+ return SendResolveMystery(params);
+}
+
+void
+BlobChild::NoteDyingRemoteBlobImpl()
+{
+ MOZ_ASSERT(mBlobImpl);
+ MOZ_ASSERT(mRemoteBlobImpl);
+ MOZ_ASSERT(!mOwnsBlobImpl);
+
+ // This may be called on any thread due to the fact that RemoteBlobImpl is
+ // designed to be passed between threads. We must start the shutdown process
+ // on the owning thread, so we proxy here if necessary.
+ if (!IsOnOwningThread()) {
+ nsCOMPtr<nsIRunnable> runnable =
+ NewNonOwningRunnableMethod(this, &BlobChild::NoteDyingRemoteBlobImpl);
+
+ if (mEventTarget) {
+ runnable = new CancelableRunnableWrapper(runnable, mEventTarget);
+
+ MOZ_ALWAYS_SUCCEEDS(mEventTarget->Dispatch(runnable,
+ NS_DISPATCH_NORMAL));
+ } else {
+ MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable));
+ }
+
+ return;
+ }
+
+ // Must do this before calling Send__delete__ or we'll crash there trying to
+ // access a dangling pointer.
+ mBlobImpl = nullptr;
+ mRemoteBlobImpl = nullptr;
+
+ PBlobChild::Send__delete__(this);
+}
+
+bool
+BlobChild::IsOnOwningThread() const
+{
+ return EventTargetIsOnCurrentThread(mEventTarget);
+}
+
+void
+BlobChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+ AssertIsOnOwningThread();
+
+ if (mRemoteBlobImpl) {
+ mRemoteBlobImpl->NoteDyingActor();
+ }
+
+ if (mBlobImpl && mOwnsBlobImpl) {
+ mBlobImpl->Release();
+ }
+
+#ifdef DEBUG
+ mBlobImpl = nullptr;
+ mRemoteBlobImpl = nullptr;
+ mBackgroundManager = nullptr;
+ mContentManager = nullptr;
+ mOwnsBlobImpl = false;
+#endif
+}
+
+PBlobStreamChild*
+BlobChild::AllocPBlobStreamChild(const uint64_t& aStart,
+ const uint64_t& aLength)
+{
+ AssertIsOnOwningThread();
+
+ return new InputStreamChild();
+}
+
+bool
+BlobChild::DeallocPBlobStreamChild(PBlobStreamChild* aActor)
+{
+ AssertIsOnOwningThread();
+
+ delete static_cast<InputStreamChild*>(aActor);
+ return true;
+}
+
+bool
+BlobChild::RecvCreatedFromKnownBlob()
+{
+ MOZ_ASSERT(mRemoteBlobImpl);
+
+ // Releasing the other blob now that this blob is fully created.
+ mRemoteBlobImpl->NullifyDifferentProcessBlobImpl();
+
+ // Release the additional reference to ourself that was added in order to
+ // receive this RecvCreatedFromKnownBlob.
+ mRemoteBlobImpl->Release();
+ return true;
+}
+
+/*******************************************************************************
+ * BlobParent
+ ******************************************************************************/
+
+BlobParent::BlobParent(nsIContentParent* aManager, IDTableEntry* aIDTableEntry)
+ : mBackgroundManager(nullptr)
+ , mContentManager(aManager)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+
+ CommonInit(aIDTableEntry);
+}
+
+BlobParent::BlobParent(PBackgroundParent* aManager, IDTableEntry* aIDTableEntry)
+ : mBackgroundManager(aManager)
+ , mContentManager(nullptr)
+ , mEventTarget(do_GetCurrentThread())
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+ MOZ_ASSERT(mEventTarget);
+
+ CommonInit(aIDTableEntry);
+}
+
+BlobParent::BlobParent(nsIContentParent* aManager,
+ BlobImpl* aBlobImpl,
+ IDTableEntry* aIDTableEntry)
+ : mBackgroundManager(nullptr)
+ , mContentManager(aManager)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+
+ CommonInit(aBlobImpl, aIDTableEntry);
+}
+
+BlobParent::BlobParent(PBackgroundParent* aManager,
+ BlobImpl* aBlobImpl,
+ IDTableEntry* aIDTableEntry)
+ : mBackgroundManager(aManager)
+ , mContentManager(nullptr)
+ , mEventTarget(do_GetCurrentThread())
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+ MOZ_ASSERT(mEventTarget);
+
+ CommonInit(aBlobImpl, aIDTableEntry);
+}
+
+BlobParent::~BlobParent()
+{
+ AssertIsOnOwningThread();
+
+ MOZ_COUNT_DTOR(BlobParent);
+}
+
+void
+BlobParent::CommonInit(IDTableEntry* aIDTableEntry)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aIDTableEntry);
+ MOZ_ASSERT(aIDTableEntry->GetBlobImpl());
+
+ MOZ_COUNT_CTOR(BlobParent);
+
+ mBlobImpl = aIDTableEntry->GetBlobImpl();
+ mRemoteBlobImpl = nullptr;
+
+ mBlobImpl->AddRef();
+ mOwnsBlobImpl = true;
+
+ mIDTableEntry = aIDTableEntry;
+}
+
+void
+BlobParent::CommonInit(BlobImpl* aBlobImpl, IDTableEntry* aIDTableEntry)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aBlobImpl);
+ MOZ_ASSERT(aIDTableEntry);
+
+ MOZ_COUNT_CTOR(BlobParent);
+
+ DebugOnly<bool> isMutable;
+ MOZ_ASSERT(NS_SUCCEEDED(aBlobImpl->GetMutable(&isMutable)));
+ MOZ_ASSERT(!isMutable);
+
+ RefPtr<RemoteBlobImpl> remoteBlobImpl = new RemoteBlobImpl(this, aBlobImpl);
+
+ MOZ_ASSERT(NS_SUCCEEDED(remoteBlobImpl->GetMutable(&isMutable)));
+ MOZ_ASSERT(!isMutable);
+
+ mRemoteBlobImpl = remoteBlobImpl;
+
+ remoteBlobImpl.forget(&mBlobImpl);
+ mOwnsBlobImpl = true;
+
+ mIDTableEntry = aIDTableEntry;
+}
+
+#ifdef DEBUG
+
+void
+BlobParent::AssertIsOnOwningThread() const
+{
+ MOZ_ASSERT(IsOnOwningThread());
+}
+
+#endif // DEBUG
+
+// static
+void
+BlobParent::Startup(const FriendKey& /* aKey */)
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+
+ CommonStartup();
+
+ ClearOnShutdown(&sIDTable);
+
+ sIDTableMutex = new Mutex("BlobParent::sIDTableMutex");
+ ClearOnShutdown(&sIDTableMutex);
+}
+
+// static
+BlobParent*
+BlobParent::GetOrCreate(nsIContentParent* aManager, BlobImpl* aBlobImpl)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+
+ return GetOrCreateFromImpl(aManager, aBlobImpl);
+}
+
+// static
+BlobParent*
+BlobParent::GetOrCreate(PBackgroundParent* aManager, BlobImpl* aBlobImpl)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+
+ return GetOrCreateFromImpl(aManager, aBlobImpl);
+}
+
+// static
+BlobParent*
+BlobParent::Create(nsIContentParent* aManager,
+ const ParentBlobConstructorParams& aParams)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+
+ return CreateFromParams(aManager, aParams);
+}
+
+// static
+BlobParent*
+BlobParent::Create(PBackgroundParent* aManager,
+ const ParentBlobConstructorParams& aParams)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+
+ return CreateFromParams(aManager, aParams);
+}
+
+// static
+already_AddRefed<BlobImpl>
+BlobParent::GetBlobImplForID(const nsID& aID)
+{
+ if (NS_WARN_IF(gProcessType != GeckoProcessType_Default)) {
+ ASSERT_UNLESS_FUZZING();
+ return nullptr;
+ }
+
+ RefPtr<IDTableEntry> idTableEntry = IDTableEntry::Get(aID);
+ if (NS_WARN_IF(!idTableEntry)) {
+ return nullptr;
+ }
+
+ RefPtr<BlobImpl> blobImpl = idTableEntry->GetBlobImpl();
+ MOZ_ASSERT(blobImpl);
+
+ return blobImpl.forget();
+}
+
+// static
+template <class ParentManagerType>
+BlobParent*
+BlobParent::GetOrCreateFromImpl(ParentManagerType* aManager,
+ BlobImpl* aBlobImpl)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+ MOZ_ASSERT(aBlobImpl);
+
+ MOZ_ASSERT(!nsCOMPtr<PIBlobImplSnapshot>(do_QueryInterface(aBlobImpl)));
+
+ // If the blob represents a remote blob for this manager then we can simply
+ // pass its actor back here.
+ if (nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryInterface(aBlobImpl)) {
+ BlobParent* actor = MaybeGetActorFromRemoteBlob(remoteBlob, aManager);
+ if (actor) {
+ return actor;
+ }
+ }
+
+ // All blobs shared between threads or processes must be immutable.
+ if (NS_WARN_IF(NS_FAILED(aBlobImpl->SetMutable(false)))) {
+ return nullptr;
+ }
+
+ AnyBlobConstructorParams blobParams;
+
+ if (ActorManagerIsSameProcess(aManager)) {
+ RefPtr<BlobImpl> sameProcessImpl = aBlobImpl;
+ auto addRefedBlobImpl =
+ reinterpret_cast<intptr_t>(sameProcessImpl.forget().take());
+
+ blobParams = SameProcessBlobConstructorParams(addRefedBlobImpl);
+ } else {
+ if (aBlobImpl->IsSizeUnknown() || aBlobImpl->IsDateUnknown()) {
+ // We don't want to call GetSize or GetLastModifiedDate yet since that may
+ // stat a file on the this thread. Instead we'll learn the size lazily
+ // from the other side.
+ blobParams = MysteryBlobConstructorParams();
+ } else {
+ nsString contentType;
+ aBlobImpl->GetType(contentType);
+
+ ErrorResult rv;
+ uint64_t length = aBlobImpl->GetSize(rv);
+ MOZ_ASSERT(!rv.Failed());
+
+ if (aBlobImpl->IsFile()) {
+ nsAutoString name;
+ aBlobImpl->GetName(name);
+
+ nsAutoString domPath;
+ aBlobImpl->GetDOMPath(domPath);
+
+ int64_t modDate = aBlobImpl->GetLastModified(rv);
+ MOZ_ASSERT(!rv.Failed());
+
+ blobParams =
+ FileBlobConstructorParams(name, contentType, domPath, length, modDate,
+ aBlobImpl->IsDirectory(), void_t());
+ } else {
+ blobParams = NormalBlobConstructorParams(contentType, length, void_t());
+ }
+ }
+ }
+
+ nsID id;
+ MOZ_ALWAYS_SUCCEEDS(gUUIDGenerator->GenerateUUIDInPlace(&id));
+
+ RefPtr<IDTableEntry> idTableEntry =
+ IDTableEntry::GetOrCreate(id, ActorManagerProcessID(aManager), aBlobImpl);
+ MOZ_ASSERT(idTableEntry);
+
+ BlobParent* actor = new BlobParent(aManager, idTableEntry);
+
+ ChildBlobConstructorParams params(id, blobParams);
+ if (NS_WARN_IF(!aManager->SendPBlobConstructor(actor, params))) {
+ return nullptr;
+ }
+
+ return actor;
+}
+
+// static
+template <class ParentManagerType>
+BlobParent*
+BlobParent::CreateFromParams(ParentManagerType* aManager,
+ const ParentBlobConstructorParams& aParams)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+
+ const AnyBlobConstructorParams& blobParams = aParams.blobParams();
+
+ switch (blobParams.type()) {
+ case AnyBlobConstructorParams::TMysteryBlobConstructorParams: {
+ ASSERT_UNLESS_FUZZING();
+ return nullptr;
+ }
+
+ case AnyBlobConstructorParams::TNormalBlobConstructorParams:
+ case AnyBlobConstructorParams::TFileBlobConstructorParams: {
+ const OptionalBlobData& optionalBlobData =
+ blobParams.type() ==
+ AnyBlobConstructorParams::TNormalBlobConstructorParams ?
+ blobParams.get_NormalBlobConstructorParams().optionalBlobData() :
+ blobParams.get_FileBlobConstructorParams().optionalBlobData();
+
+ if (NS_WARN_IF(optionalBlobData.type() != OptionalBlobData::TBlobData)) {
+ ASSERT_UNLESS_FUZZING();
+ return nullptr;
+ }
+
+ RefPtr<BlobImpl> blobImpl =
+ CreateBlobImpl(aParams,
+ optionalBlobData.get_BlobData(),
+ ActorManagerIsSameProcess(aManager));
+ if (NS_WARN_IF(!blobImpl)) {
+ ASSERT_UNLESS_FUZZING();
+ return nullptr;
+ }
+
+ nsID id;
+ MOZ_ALWAYS_SUCCEEDS(gUUIDGenerator->GenerateUUIDInPlace(&id));
+
+ RefPtr<IDTableEntry> idTableEntry =
+ IDTableEntry::Create(id, ActorManagerProcessID(aManager), blobImpl);
+ if (NS_WARN_IF(!idTableEntry)) {
+ ASSERT_UNLESS_FUZZING();
+ return nullptr;
+ }
+
+ return new BlobParent(aManager, blobImpl, idTableEntry);
+ }
+
+ case AnyBlobConstructorParams::TSlicedBlobConstructorParams: {
+ const SlicedBlobConstructorParams& params =
+ blobParams.get_SlicedBlobConstructorParams();
+
+ if (NS_WARN_IF(params.end() < params.begin())) {
+ ASSERT_UNLESS_FUZZING();
+ return nullptr;
+ }
+
+ auto* actor =
+ const_cast<BlobParent*>(
+ static_cast<const BlobParent*>(params.sourceParent()));
+ MOZ_ASSERT(actor);
+
+ RefPtr<BlobImpl> source = actor->GetBlobImpl();
+ MOZ_ASSERT(source);
+
+ ErrorResult rv;
+ RefPtr<BlobImpl> slice =
+ source->CreateSlice(params.begin(),
+ params.end() - params.begin(),
+ params.contentType(),
+ rv);
+ if (NS_WARN_IF(rv.Failed())) {
+ ASSERT_UNLESS_FUZZING();
+ return nullptr;
+ }
+
+ MOZ_ALWAYS_SUCCEEDS(slice->SetMutable(false));
+
+ RefPtr<IDTableEntry> idTableEntry =
+ IDTableEntry::Create(params.id(),
+ ActorManagerProcessID(aManager),
+ slice);
+ if (NS_WARN_IF(!idTableEntry)) {
+ ASSERT_UNLESS_FUZZING();
+ return nullptr;
+ }
+
+ return new BlobParent(aManager, slice, idTableEntry);
+ }
+
+ case AnyBlobConstructorParams::TKnownBlobConstructorParams: {
+ const KnownBlobConstructorParams& params =
+ blobParams.get_KnownBlobConstructorParams();
+
+ RefPtr<IDTableEntry> idTableEntry =
+ IDTableEntry::Get(params.id(), ActorManagerProcessID(aManager));
+ if (NS_WARN_IF(!idTableEntry)) {
+ ASSERT_UNLESS_FUZZING();
+ return nullptr;
+ }
+
+ return new BlobParent(aManager, idTableEntry);
+ }
+
+ case AnyBlobConstructorParams::TSameProcessBlobConstructorParams: {
+ if (NS_WARN_IF(!ActorManagerIsSameProcess(aManager))) {
+ ASSERT_UNLESS_FUZZING();
+ return nullptr;
+ }
+
+ const SameProcessBlobConstructorParams& params =
+ blobParams.get_SameProcessBlobConstructorParams();
+
+ RefPtr<BlobImpl> blobImpl =
+ dont_AddRef(reinterpret_cast<BlobImpl*>(params.addRefedBlobImpl()));
+ MOZ_ASSERT(blobImpl);
+
+ nsID id;
+ MOZ_ALWAYS_SUCCEEDS(gUUIDGenerator->GenerateUUIDInPlace(&id));
+
+ RefPtr<IDTableEntry> idTableEntry =
+ IDTableEntry::Create(id, ActorManagerProcessID(aManager), blobImpl);
+ MOZ_ASSERT(idTableEntry);
+
+ return new BlobParent(aManager, blobImpl, idTableEntry);
+ }
+
+ default:
+ MOZ_CRASH("Unknown params!");
+ }
+
+ MOZ_CRASH("Should never get here!");
+}
+
+// static
+template <class ParentManagerType>
+BlobParent*
+BlobParent::SendSliceConstructor(
+ ParentManagerType* aManager,
+ const ParentBlobConstructorParams& aParams,
+ const ChildBlobConstructorParams& aOtherSideParams)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aManager);
+
+ BlobParent* newActor = BlobParent::Create(aManager, aParams);
+ MOZ_ASSERT(newActor);
+
+ if (aManager->SendPBlobConstructor(newActor, aOtherSideParams)) {
+ return newActor;
+ }
+
+ return nullptr;
+}
+
+// static
+BlobParent*
+BlobParent::MaybeGetActorFromRemoteBlob(nsIRemoteBlob* aRemoteBlob,
+ nsIContentParent* aManager)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aRemoteBlob);
+ MOZ_ASSERT(aManager);
+
+ BlobParent* actor = aRemoteBlob->GetBlobParent();
+ if (actor && actor->GetContentManager() == aManager) {
+ return actor;
+ }
+
+ return nullptr;
+}
+
+// static
+BlobParent*
+BlobParent::MaybeGetActorFromRemoteBlob(nsIRemoteBlob* aRemoteBlob,
+ PBackgroundParent* aManager)
+{
+ AssertCorrectThreadForManager(aManager);
+ MOZ_ASSERT(aRemoteBlob);
+ MOZ_ASSERT(aManager);
+
+ BlobParent* actor = aRemoteBlob->GetBlobParent();
+ if (actor && actor->GetBackgroundManager() == aManager) {
+ return actor;
+ }
+
+ return nullptr;
+}
+
+already_AddRefed<BlobImpl>
+BlobParent::GetBlobImpl()
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mBlobImpl);
+
+ RefPtr<BlobImpl> blobImpl;
+
+ // Remote blobs are held alive until the first call to GetBlobImpl. Thereafter
+ // we only hold a weak reference. Normal blobs are held alive until the actor
+ // is destroyed.
+ if (mRemoteBlobImpl && mOwnsBlobImpl) {
+ blobImpl = dont_AddRef(mBlobImpl);
+ mOwnsBlobImpl = false;
+ } else {
+ blobImpl = mBlobImpl;
+ }
+
+ MOZ_ASSERT(blobImpl);
+
+ return blobImpl.forget();
+}
+
+void
+BlobParent::NoteDyingRemoteBlobImpl()
+{
+ MOZ_ASSERT(mRemoteBlobImpl);
+ MOZ_ASSERT(!mOwnsBlobImpl);
+
+ // This may be called on any thread due to the fact that RemoteBlobImpl is
+ // designed to be passed between threads. We must start the shutdown process
+ // on the main thread, so we proxy here if necessary.
+ if (!IsOnOwningThread()) {
+ nsCOMPtr<nsIRunnable> runnable =
+ NewNonOwningRunnableMethod(this, &BlobParent::NoteDyingRemoteBlobImpl);
+
+ if (mEventTarget) {
+ runnable = new CancelableRunnableWrapper(runnable, mEventTarget);
+
+ MOZ_ALWAYS_SUCCEEDS(mEventTarget->Dispatch(runnable,
+ NS_DISPATCH_NORMAL));
+ } else {
+ MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable));
+ }
+
+ return;
+ }
+
+ // Must do this before calling Send__delete__ or we'll crash there trying to
+ // access a dangling pointer.
+ mBlobImpl = nullptr;
+ mRemoteBlobImpl = nullptr;
+
+ Unused << PBlobParent::Send__delete__(this);
+}
+
+void
+BlobParent::NoteRunnableCompleted(OpenStreamRunnable* aRunnable)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aRunnable);
+
+ for (uint32_t count = mOpenStreamRunnables.Length(), index = 0;
+ index < count;
+ index++) {
+ nsRevocableEventPtr<OpenStreamRunnable>& runnable =
+ mOpenStreamRunnables[index];
+
+ if (runnable.get() == aRunnable) {
+ runnable.Forget();
+ mOpenStreamRunnables.RemoveElementAt(index);
+ return;
+ }
+ }
+
+ MOZ_CRASH("Runnable not in our array!");
+}
+
+bool
+BlobParent::IsOnOwningThread() const
+{
+ return EventTargetIsOnCurrentThread(mEventTarget);
+}
+
+void
+BlobParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+ AssertIsOnOwningThread();
+
+ if (mRemoteBlobImpl) {
+ mRemoteBlobImpl->NoteDyingActor();
+ }
+
+ if (mBlobImpl && mOwnsBlobImpl) {
+ mBlobImpl->Release();
+ }
+
+#ifdef DEBUG
+ mBlobImpl = nullptr;
+ mRemoteBlobImpl = nullptr;
+ mBackgroundManager = nullptr;
+ mContentManager = nullptr;
+ mOwnsBlobImpl = false;
+#endif
+}
+
+PBlobStreamParent*
+BlobParent::AllocPBlobStreamParent(const uint64_t& aStart,
+ const uint64_t& aLength)
+{
+ AssertIsOnOwningThread();
+
+ if (NS_WARN_IF(mRemoteBlobImpl)) {
+ ASSERT_UNLESS_FUZZING();
+ return nullptr;
+ }
+
+ return new InputStreamParent();
+}
+
+bool
+BlobParent::RecvPBlobStreamConstructor(PBlobStreamParent* aActor,
+ const uint64_t& aStart,
+ const uint64_t& aLength)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aActor);
+ MOZ_ASSERT(mBlobImpl);
+ MOZ_ASSERT(!mRemoteBlobImpl);
+ MOZ_ASSERT(mOwnsBlobImpl);
+
+ auto* actor = static_cast<InputStreamParent*>(aActor);
+
+ // Make sure we can't overflow.
+ if (NS_WARN_IF(UINT64_MAX - aLength < aStart)) {
+ ASSERT_UNLESS_FUZZING();
+ return false;
+ }
+
+ ErrorResult errorResult;
+ uint64_t blobLength = mBlobImpl->GetSize(errorResult);
+ MOZ_ASSERT(!errorResult.Failed());
+
+ if (NS_WARN_IF(aStart + aLength > blobLength)) {
+ ASSERT_UNLESS_FUZZING();
+ return false;
+ }
+
+ RefPtr<BlobImpl> blobImpl;
+
+ if (!aStart && aLength == blobLength) {
+ blobImpl = mBlobImpl;
+ } else {
+ nsString type;
+ mBlobImpl->GetType(type);
+
+ blobImpl = mBlobImpl->CreateSlice(aStart, aLength, type, errorResult);
+ if (NS_WARN_IF(errorResult.Failed())) {
+ return false;
+ }
+ }
+
+ nsCOMPtr<nsIInputStream> stream;
+ blobImpl->GetInternalStream(getter_AddRefs(stream), errorResult);
+ if (NS_WARN_IF(errorResult.Failed())) {
+ return false;
+ }
+
+ // If the stream is entirely backed by memory then we can serialize and send
+ // it immediately.
+ if (mBlobImpl->IsMemoryFile()) {
+ InputStreamParams params;
+ nsTArray<FileDescriptor> fds;
+ SerializeInputStream(stream, params, fds);
+
+ MOZ_ASSERT(params.type() != InputStreamParams::T__None);
+ MOZ_ASSERT(fds.IsEmpty());
+
+ return actor->Destroy(params, void_t());
+ }
+
+ nsCOMPtr<nsIRemoteBlob> remoteBlob = do_QueryInterface(mBlobImpl);
+ nsCOMPtr<IPrivateRemoteInputStream> remoteStream;
+ if (remoteBlob) {
+ remoteStream = do_QueryInterface(stream);
+ }
+
+ // There are three cases in which we can use the stream obtained from the blob
+ // directly as our serialized stream:
+ //
+ // 1. The blob is not a remote blob.
+ // 2. The blob is a remote blob that represents this actor.
+ // 3. The blob is a remote blob representing a different actor but we
+ // already have a non-remote, i.e. serialized, serialized stream.
+ //
+ // In all other cases we need to be on a background thread before we can get
+ // to the real stream.
+ nsCOMPtr<nsIIPCSerializableInputStream> serializableStream;
+ if (!remoteBlob ||
+ remoteBlob->GetBlobParent() == this ||
+ !remoteStream) {
+ serializableStream = do_QueryInterface(stream);
+ if (!serializableStream) {
+ MOZ_ASSERT(false, "Must be serializable!");
+ return false;
+ }
+ }
+
+ nsCOMPtr<nsIThread> target;
+ errorResult = NS_NewNamedThread("Blob Opener", getter_AddRefs(target));
+ if (NS_WARN_IF(errorResult.Failed())) {
+ return false;
+ }
+
+ RefPtr<OpenStreamRunnable> runnable =
+ new OpenStreamRunnable(this, actor, stream, serializableStream, target);
+
+ errorResult = runnable->Dispatch();
+ if (NS_WARN_IF(errorResult.Failed())) {
+ return false;
+ }
+
+ // nsRevocableEventPtr lacks some of the operators needed for anything nicer.
+ *mOpenStreamRunnables.AppendElement() = runnable;
+ return true;
+}
+
+bool
+BlobParent::DeallocPBlobStreamParent(PBlobStreamParent* aActor)
+{
+ AssertIsOnOwningThread();
+
+ delete static_cast<InputStreamParent*>(aActor);
+ return true;
+}
+
+bool
+BlobParent::RecvResolveMystery(const ResolveMysteryParams& aParams)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(aParams.type() != ResolveMysteryParams::T__None);
+ MOZ_ASSERT(mBlobImpl);
+ MOZ_ASSERT(!mRemoteBlobImpl);
+ MOZ_ASSERT(mOwnsBlobImpl);
+
+ switch (aParams.type()) {
+ case ResolveMysteryParams::TNormalBlobConstructorParams: {
+ const NormalBlobConstructorParams& params =
+ aParams.get_NormalBlobConstructorParams();
+
+ if (NS_WARN_IF(params.length() == UINT64_MAX)) {
+ ASSERT_UNLESS_FUZZING();
+ return false;
+ }
+
+ mBlobImpl->SetLazyData(NullString(),
+ params.contentType(),
+ params.length(),
+ INT64_MAX);
+ return true;
+ }
+
+ case ResolveMysteryParams::TFileBlobConstructorParams: {
+ const FileBlobConstructorParams& params =
+ aParams.get_FileBlobConstructorParams();
+ if (NS_WARN_IF(params.name().IsVoid())) {
+ ASSERT_UNLESS_FUZZING();
+ return false;
+ }
+
+ if (NS_WARN_IF(params.length() == UINT64_MAX)) {
+ ASSERT_UNLESS_FUZZING();
+ return false;
+ }
+
+ if (NS_WARN_IF(params.modDate() == INT64_MAX)) {
+ ASSERT_UNLESS_FUZZING();
+ return false;
+ }
+
+ mBlobImpl->SetLazyData(params.name(),
+ params.contentType(),
+ params.length(),
+ params.modDate());
+ return true;
+ }
+
+ default:
+ MOZ_CRASH("Unknown params!");
+ }
+
+ MOZ_CRASH("Should never get here!");
+}
+
+bool
+BlobParent::RecvBlobStreamSync(const uint64_t& aStart,
+ const uint64_t& aLength,
+ InputStreamParams* aParams,
+ OptionalFileDescriptorSet* aFDs)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mBlobImpl);
+ MOZ_ASSERT(!mRemoteBlobImpl);
+ MOZ_ASSERT(mOwnsBlobImpl);
+
+ bool finished = false;
+
+ {
+ // Calling RecvPBlobStreamConstructor() may synchronously delete the actor
+ // we pass in so don't touch it outside this block.
+ auto* streamActor = new InputStreamParent(&finished, aParams, aFDs);
+
+ if (NS_WARN_IF(!RecvPBlobStreamConstructor(streamActor, aStart, aLength))) {
+ // If RecvPBlobStreamConstructor() returns false then it is our
+ // responsibility to destroy the actor.
+ delete streamActor;
+ return false;
+ }
+ }
+
+ if (finished) {
+ // The actor is already dead and we have already set our out params.
+ return true;
+ }
+
+ // The actor is alive and will be doing asynchronous work to load the stream.
+ // Spin a nested loop here while we wait for it.
+ nsIThread* currentThread = NS_GetCurrentThread();
+ MOZ_ASSERT(currentThread);
+
+ while (!finished) {
+ MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(currentThread));
+ }
+
+ return true;
+}
+
+bool
+BlobParent::RecvWaitForSliceCreation()
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mBlobImpl);
+ MOZ_ASSERT(mOwnsBlobImpl);
+
+ // The whole point of this message is to ensure that the sliced blob created
+ // by the child has been inserted into our IDTable.
+ MOZ_ASSERT(mIDTableEntry);
+
+#ifdef DEBUG
+ {
+ MOZ_ASSERT(sIDTableMutex);
+ MutexAutoLock lock(*sIDTableMutex);
+
+ MOZ_ASSERT(sIDTable);
+ MOZ_ASSERT(sIDTable->Contains(mIDTableEntry->ID()));
+ }
+#endif
+
+ return true;
+}
+
+bool
+BlobParent::RecvGetFileId(int64_t* aFileId)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mBlobImpl);
+ MOZ_ASSERT(!mRemoteBlobImpl);
+ MOZ_ASSERT(mOwnsBlobImpl);
+
+ if (NS_WARN_IF(!IndexedDatabaseManager::InTestingMode())) {
+ ASSERT_UNLESS_FUZZING();
+ return false;
+ }
+
+ *aFileId = mBlobImpl->GetFileId();
+ return true;
+}
+
+bool
+BlobParent::RecvGetFilePath(nsString* aFilePath)
+{
+ AssertIsOnOwningThread();
+ MOZ_ASSERT(mBlobImpl);
+ MOZ_ASSERT(!mRemoteBlobImpl);
+ MOZ_ASSERT(mOwnsBlobImpl);
+
+ // In desktop e10s the file picker code sends this message.
+
+ nsString filePath;
+ ErrorResult rv;
+ mBlobImpl->GetMozFullPathInternal(filePath, rv);
+ if (NS_WARN_IF(rv.Failed())) {
+ rv.SuppressException();
+ return false;
+ }
+
+ *aFilePath = filePath;
+ return true;
+}
+
+/*******************************************************************************
+ * BlobParent::IDTableEntry
+ ******************************************************************************/
+
+BlobParent::
+IDTableEntry::IDTableEntry(const nsID& aID,
+ intptr_t aProcessID,
+ BlobImpl* aBlobImpl)
+ : mID(aID)
+ , mProcessID(aProcessID)
+ , mBlobImpl(aBlobImpl)
+{
+ MOZ_ASSERT(aBlobImpl);
+}
+
+BlobParent::
+IDTableEntry::~IDTableEntry()
+{
+ MOZ_ASSERT(sIDTableMutex);
+ sIDTableMutex->AssertNotCurrentThreadOwns();
+ MOZ_ASSERT(sIDTable);
+
+ {
+ MutexAutoLock lock(*sIDTableMutex);
+ MOZ_ASSERT(sIDTable->Get(mID) == this);
+
+ sIDTable->Remove(mID);
+
+ if (!sIDTable->Count()) {
+ sIDTable = nullptr;
+ }
+ }
+}
+
+// static
+already_AddRefed<BlobParent::IDTableEntry>
+BlobParent::
+IDTableEntry::GetOrCreateInternal(const nsID& aID,
+ intptr_t aProcessID,
+ BlobImpl* aBlobImpl,
+ bool aMayCreate,
+ bool aMayGet,
+ bool aIgnoreProcessID)
+{
+ MOZ_ASSERT(gProcessType == GeckoProcessType_Default);
+ MOZ_ASSERT(sIDTableMutex);
+ sIDTableMutex->AssertNotCurrentThreadOwns();
+
+ RefPtr<IDTableEntry> entry;
+
+ {
+ MutexAutoLock lock(*sIDTableMutex);
+
+ if (!sIDTable) {
+ if (NS_WARN_IF(!aMayCreate)) {
+ return nullptr;
+ }
+
+ sIDTable = new IDTable();
+ }
+
+ entry = sIDTable->Get(aID);
+
+ if (entry) {
+ MOZ_ASSERT_IF(aBlobImpl, entry->GetBlobImpl() == aBlobImpl);
+
+ if (NS_WARN_IF(!aMayGet)) {
+ return nullptr;
+ }
+
+ if (!aIgnoreProcessID && NS_WARN_IF(entry->mProcessID != aProcessID)) {
+ return nullptr;
+ }
+ } else {
+ if (NS_WARN_IF(!aMayCreate)) {
+ return nullptr;
+ }
+
+ MOZ_ASSERT(aBlobImpl);
+
+ entry = new IDTableEntry(aID, aProcessID, aBlobImpl);
+
+ sIDTable->Put(aID, entry);
+ }
+ }
+
+ MOZ_ASSERT(entry);
+
+ return entry.forget();
+}
+
+/*******************************************************************************
+ * Other stuff
+ ******************************************************************************/
+
+bool
+InputStreamChild::Recv__delete__(const InputStreamParams& aParams,
+ const OptionalFileDescriptorSet& aOptionalSet)
+{
+ MOZ_ASSERT(mRemoteStream);
+ mRemoteStream->AssertIsOnOwningThread();
+
+ nsTArray<FileDescriptor> fds;
+ OptionalFileDescriptorSetToFDs(
+ // XXX Fix this somehow...
+ const_cast<OptionalFileDescriptorSet&>(aOptionalSet),
+ fds);
+
+ nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(aParams, fds);
+ MOZ_ASSERT(stream);
+
+ mRemoteStream->SetStream(stream);
+ return true;
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/BlobChild.h b/dom/ipc/BlobChild.h
new file mode 100644
index 000000000..affa9934a
--- /dev/null
+++ b/dom/ipc/BlobChild.h
@@ -0,0 +1,246 @@
+/* -*- 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_BlobChild_h
+#define mozilla_dom_ipc_BlobChild_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/dom/PBlobChild.h"
+#include "nsCOMPtr.h"
+#include "nsID.h"
+
+class nsIEventTarget;
+class nsIRemoteBlob;
+class nsString;
+
+namespace mozilla {
+namespace ipc {
+
+class PBackgroundChild;
+
+} // namespace ipc
+
+namespace dom {
+
+class Blob;
+class BlobImpl;
+class ContentChild;
+class nsIContentChild;
+class PBlobStreamChild;
+
+class BlobChild final
+ : public PBlobChild
+{
+ typedef mozilla::ipc::PBackgroundChild PBackgroundChild;
+
+ class RemoteBlobImpl;
+ friend class RemoteBlobImpl;
+
+ class RemoteBlobSliceImpl;
+ friend class RemoteBlobSliceImpl;
+
+ BlobImpl* mBlobImpl;
+ RemoteBlobImpl* mRemoteBlobImpl;
+
+ // One of these will be null and the other non-null.
+ PBackgroundChild* mBackgroundManager;
+ nsCOMPtr<nsIContentChild> mContentManager;
+
+ nsCOMPtr<nsIEventTarget> mEventTarget;
+
+ nsID mParentID;
+
+ bool mOwnsBlobImpl;
+
+public:
+ class FriendKey;
+
+ static void
+ Startup(const FriendKey& aKey);
+
+ // These create functions are called on the sending side.
+ static BlobChild*
+ GetOrCreate(nsIContentChild* aManager, BlobImpl* aBlobImpl);
+
+ static BlobChild*
+ GetOrCreate(PBackgroundChild* aManager, BlobImpl* aBlobImpl);
+
+ // These create functions are called on the receiving side.
+ static BlobChild*
+ Create(nsIContentChild* aManager, const ChildBlobConstructorParams& aParams);
+
+ static BlobChild*
+ Create(PBackgroundChild* aManager,
+ const ChildBlobConstructorParams& aParams);
+
+ static void
+ Destroy(PBlobChild* aActor)
+ {
+ delete static_cast<BlobChild*>(aActor);
+ }
+
+ bool
+ HasManager() const
+ {
+ return mBackgroundManager || mContentManager;
+ }
+
+ PBackgroundChild*
+ GetBackgroundManager() const
+ {
+ return mBackgroundManager;
+ }
+
+ nsIContentChild*
+ GetContentManager() const
+ {
+ return mContentManager;
+ }
+
+ const nsID&
+ ParentID() const;
+
+ // Get the BlobImpl associated with this actor. This may always be called
+ // on the sending side. It may also be called on the receiving side unless
+ // this is a "mystery" blob that has not yet received a SetMysteryBlobInfo()
+ // call.
+ already_AddRefed<BlobImpl>
+ GetBlobImpl();
+
+ // Use this for files.
+ bool
+ SetMysteryBlobInfo(const nsString& aName,
+ const nsString& aContentType,
+ uint64_t aLength,
+ int64_t aLastModifiedDate);
+
+ // Use this for non-file blobs.
+ bool
+ SetMysteryBlobInfo(const nsString& aContentType, uint64_t aLength);
+
+ void
+ AssertIsOnOwningThread() const
+#ifdef DEBUG
+ ;
+#else
+ { }
+#endif
+
+private:
+ // These constructors are called on the sending side.
+ BlobChild(nsIContentChild* aManager, BlobImpl* aBlobImpl);
+
+ BlobChild(PBackgroundChild* aManager, BlobImpl* aBlobImpl);
+
+ BlobChild(nsIContentChild* aManager, BlobChild* aOther);
+
+ BlobChild(PBackgroundChild* aManager, BlobChild* aOther, BlobImpl* aBlobImpl);
+
+ // These constructors are called on the receiving side.
+ BlobChild(nsIContentChild* aManager,
+ const ChildBlobConstructorParams& aParams);
+
+ BlobChild(PBackgroundChild* aManager,
+ const ChildBlobConstructorParams& aParams);
+
+ // These constructors are called for slices.
+ BlobChild(nsIContentChild* aManager,
+ const nsID& aParentID,
+ RemoteBlobSliceImpl* aRemoteBlobSliceImpl);
+
+ BlobChild(PBackgroundChild* aManager,
+ const nsID& aParentID,
+ RemoteBlobSliceImpl* aRemoteBlobSliceImpl);
+
+ // Only called by Destroy().
+ ~BlobChild();
+
+ void
+ CommonInit(BlobImpl* aBlobImpl);
+
+ void
+ CommonInit(BlobChild* aOther, BlobImpl* aBlobImpl);
+
+ void
+ CommonInit(const ChildBlobConstructorParams& aParams);
+
+ void
+ CommonInit(const nsID& aParentID, RemoteBlobImpl* aRemoteBlobImpl);
+
+ template <class ChildManagerType>
+ static BlobChild*
+ GetOrCreateFromImpl(ChildManagerType* aManager, BlobImpl* aBlobImpl);
+
+ template <class ChildManagerType>
+ static BlobChild*
+ CreateFromParams(ChildManagerType* aManager,
+ const ChildBlobConstructorParams& aParams);
+
+ template <class ChildManagerType>
+ static BlobChild*
+ SendSliceConstructor(ChildManagerType* aManager,
+ RemoteBlobSliceImpl* aRemoteBlobSliceImpl,
+ const ParentBlobConstructorParams& aParams);
+
+ static BlobChild*
+ MaybeGetActorFromRemoteBlob(nsIRemoteBlob* aRemoteBlob,
+ nsIContentChild* aManager,
+ BlobImpl* aBlobImpl);
+
+ static BlobChild*
+ MaybeGetActorFromRemoteBlob(nsIRemoteBlob* aRemoteBlob,
+ PBackgroundChild* aManager,
+ BlobImpl* aBlobImpl);
+
+ void
+ NoteDyingRemoteBlobImpl();
+
+ nsIEventTarget*
+ EventTarget() const
+ {
+ return mEventTarget;
+ }
+
+ bool
+ IsOnOwningThread() const;
+
+ // These methods are only called by the IPDL message machinery.
+ virtual void
+ ActorDestroy(ActorDestroyReason aWhy) override;
+
+ virtual PBlobStreamChild*
+ AllocPBlobStreamChild(const uint64_t& aStart,
+ const uint64_t& aLength) override;
+
+ virtual bool
+ DeallocPBlobStreamChild(PBlobStreamChild* aActor) override;
+
+ virtual bool
+ RecvCreatedFromKnownBlob() override;
+};
+
+// Only let ContentChild call BlobChild::Startup() and ensure that
+// ContentChild can't access any other BlobChild internals.
+class BlobChild::FriendKey final
+{
+ friend class ContentChild;
+
+private:
+ FriendKey()
+ { }
+
+ FriendKey(const FriendKey& /* aOther */)
+ { }
+
+public:
+ ~FriendKey()
+ { }
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_ipc_BlobChild_h
diff --git a/dom/ipc/BlobParent.h b/dom/ipc/BlobParent.h
new file mode 100644
index 000000000..98cfe6d28
--- /dev/null
+++ b/dom/ipc/BlobParent.h
@@ -0,0 +1,260 @@
+/* -*- 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_BlobParent_h
+#define mozilla_dom_ipc_BlobParent_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/dom/PBlobParent.h"
+#include "nsCOMPtr.h"
+#include "nsTArray.h"
+
+template <class, class> class nsDataHashtable;
+class nsIDHashKey;
+class nsIEventTarget;
+class nsIRemoteBlob;
+template <class> class nsRevocableEventPtr;
+class nsString;
+
+namespace mozilla {
+
+class Mutex;
+
+namespace ipc {
+
+class PBackgroundParent;
+
+} // namespace ipc
+
+namespace dom {
+
+class ContentParent;
+class BlobImpl;
+class nsIContentParent;
+class PBlobStreamParent;
+
+class BlobParent final
+ : public PBlobParent
+{
+ typedef mozilla::ipc::PBackgroundParent PBackgroundParent;
+
+ class IDTableEntry;
+ typedef nsDataHashtable<nsIDHashKey, IDTableEntry*> IDTable;
+
+ class OpenStreamRunnable;
+ friend class OpenStreamRunnable;
+
+ class RemoteBlobImpl;
+
+ struct CreateBlobImplMetadata;
+
+ static StaticAutoPtr<IDTable> sIDTable;
+ static StaticAutoPtr<Mutex> sIDTableMutex;
+
+ BlobImpl* mBlobImpl;
+ RemoteBlobImpl* mRemoteBlobImpl;
+
+ // One of these will be null and the other non-null.
+ PBackgroundParent* mBackgroundManager;
+ nsCOMPtr<nsIContentParent> mContentManager;
+
+ nsCOMPtr<nsIEventTarget> mEventTarget;
+
+ // nsIInputStreams backed by files must ensure that the files are actually
+ // opened and closed on a background thread before we can send their file
+ // handles across to the child. The child process could crash during this
+ // process so we need to make sure we cancel the intended response in such a
+ // case. We do that by holding an array of nsRevocableEventPtr. If the child
+ // crashes then this actor will be destroyed and the nsRevocableEventPtr
+ // destructor will cancel any stream events that are currently in flight.
+ nsTArray<nsRevocableEventPtr<OpenStreamRunnable>> mOpenStreamRunnables;
+
+ RefPtr<IDTableEntry> mIDTableEntry;
+
+ bool mOwnsBlobImpl;
+
+public:
+ class FriendKey;
+
+ static void
+ Startup(const FriendKey& aKey);
+
+ // These create functions are called on the sending side.
+ static BlobParent*
+ GetOrCreate(nsIContentParent* aManager, BlobImpl* aBlobImpl);
+
+ static BlobParent*
+ GetOrCreate(PBackgroundParent* aManager, BlobImpl* aBlobImpl);
+
+ // These create functions are called on the receiving side.
+ static BlobParent*
+ Create(nsIContentParent* aManager,
+ const ParentBlobConstructorParams& aParams);
+
+ static BlobParent*
+ Create(PBackgroundParent* aManager,
+ const ParentBlobConstructorParams& aParams);
+
+ static void
+ Destroy(PBlobParent* aActor)
+ {
+ delete static_cast<BlobParent*>(aActor);
+ }
+
+ static already_AddRefed<BlobImpl>
+ GetBlobImplForID(const nsID& aID);
+
+ bool
+ HasManager() const
+ {
+ return mBackgroundManager || mContentManager;
+ }
+
+ PBackgroundParent*
+ GetBackgroundManager() const
+ {
+ return mBackgroundManager;
+ }
+
+ nsIContentParent*
+ GetContentManager() const
+ {
+ return mContentManager;
+ }
+
+ // Get the BlobImpl associated with this actor.
+ already_AddRefed<BlobImpl>
+ GetBlobImpl();
+
+ void
+ AssertIsOnOwningThread() const
+#ifdef DEBUG
+ ;
+#else
+ { }
+#endif
+
+private:
+ // These constructors are called on the sending side.
+ BlobParent(nsIContentParent* aManager, IDTableEntry* aIDTableEntry);
+
+ BlobParent(PBackgroundParent* aManager, IDTableEntry* aIDTableEntry);
+
+ // These constructors are called on the receiving side.
+ BlobParent(nsIContentParent* aManager,
+ BlobImpl* aBlobImpl,
+ IDTableEntry* aIDTableEntry);
+
+ BlobParent(PBackgroundParent* aManager,
+ BlobImpl* aBlobImpl,
+ IDTableEntry* aIDTableEntry);
+
+ // Only destroyed by BackgroundParentImpl and ContentParent.
+ ~BlobParent();
+
+ void
+ CommonInit(IDTableEntry* aIDTableEntry);
+
+ void
+ CommonInit(BlobImpl* aBlobImpl, IDTableEntry* aIDTableEntry);
+
+ template <class ParentManagerType>
+ static BlobParent*
+ GetOrCreateFromImpl(ParentManagerType* aManager,
+ BlobImpl* aBlobImpl);
+
+ template <class ParentManagerType>
+ static BlobParent*
+ CreateFromParams(ParentManagerType* aManager,
+ const ParentBlobConstructorParams& aParams);
+
+ template <class ParentManagerType>
+ static BlobParent*
+ SendSliceConstructor(ParentManagerType* aManager,
+ const ParentBlobConstructorParams& aParams,
+ const ChildBlobConstructorParams& aOtherSideParams);
+
+ static BlobParent*
+ MaybeGetActorFromRemoteBlob(nsIRemoteBlob* aRemoteBlob,
+ nsIContentParent* aManager);
+
+ static BlobParent*
+ MaybeGetActorFromRemoteBlob(nsIRemoteBlob* aRemoteBlob,
+ PBackgroundParent* aManager);
+
+ void
+ NoteDyingRemoteBlobImpl();
+
+ void
+ NoteRunnableCompleted(OpenStreamRunnable* aRunnable);
+
+ nsIEventTarget*
+ EventTarget() const
+ {
+ return mEventTarget;
+ }
+
+ bool
+ IsOnOwningThread() const;
+
+ // These methods are only called by the IPDL message machinery.
+ virtual void
+ ActorDestroy(ActorDestroyReason aWhy) override;
+
+ virtual PBlobStreamParent*
+ AllocPBlobStreamParent(const uint64_t& aStart,
+ const uint64_t& aLength) override;
+
+ virtual bool
+ RecvPBlobStreamConstructor(PBlobStreamParent* aActor,
+ const uint64_t& aStart,
+ const uint64_t& aLength) override;
+
+ virtual bool
+ DeallocPBlobStreamParent(PBlobStreamParent* aActor) override;
+
+ virtual bool
+ RecvResolveMystery(const ResolveMysteryParams& aParams) override;
+
+ virtual bool
+ RecvBlobStreamSync(const uint64_t& aStart,
+ const uint64_t& aLength,
+ InputStreamParams* aParams,
+ OptionalFileDescriptorSet* aFDs) override;
+
+ virtual bool
+ RecvWaitForSliceCreation() override;
+
+ virtual bool
+ RecvGetFileId(int64_t* aFileId) override;
+
+ virtual bool
+ RecvGetFilePath(nsString* aFilePath) override;
+};
+
+// Only let ContentParent call BlobParent::Startup() and ensure that
+// ContentParent can't access any other BlobParent internals.
+class BlobParent::FriendKey final
+{
+ friend class ContentParent;
+
+private:
+ FriendKey()
+ { }
+
+ FriendKey(const FriendKey& /* aOther */)
+ { }
+
+public:
+ ~FriendKey()
+ { }
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_ipc_BlobParent_h
diff --git a/dom/ipc/BlobTypes.ipdlh b/dom/ipc/BlobTypes.ipdlh
new file mode 100644
index 000000000..b0bd7f951
--- /dev/null
+++ b/dom/ipc/BlobTypes.ipdlh
@@ -0,0 +1,21 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 PFileDescriptorSet;
+
+using struct mozilla::void_t
+ from "ipc/IPCMessageUtils.h";
+
+namespace mozilla {
+namespace dom {
+
+union OptionalFileDescriptorSet
+{
+ PFileDescriptorSet;
+ FileDescriptor[];
+ void_t;
+};
+
+}
+}
diff --git a/dom/ipc/CPOWManagerGetter.h b/dom/ipc/CPOWManagerGetter.h
new file mode 100644
index 000000000..541bff665
--- /dev/null
+++ b/dom/ipc/CPOWManagerGetter.h
@@ -0,0 +1,27 @@
+/* -*- 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_CPOWManagerGetter_h
+#define mozilla_dom_CPOWManagerGetter_h
+
+namespace mozilla {
+
+namespace jsipc {
+class CPOWManager;
+} /* namespace jsipc */
+
+namespace dom {
+
+class CPOWManagerGetter
+{
+public:
+ virtual mozilla::jsipc::CPOWManager* GetCPOWManager() = 0;
+};
+
+} /* namespace dom */
+} /* namespace mozilla */
+
+#endif /* mozilla_dom_CPOWManagerGetter_h */
diff --git a/dom/ipc/ColorPickerParent.cpp b/dom/ipc/ColorPickerParent.cpp
new file mode 100644
index 000000000..6d6f1dfe5
--- /dev/null
+++ b/dom/ipc/ColorPickerParent.cpp
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "ColorPickerParent.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIDocument.h"
+#include "nsIDOMWindow.h"
+#include "mozilla/Unused.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/TabParent.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 << mColorPickerParent->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 = TabParent::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));
+}
+
+bool
+ColorPickerParent::RecvOpen()
+{
+ if (!CreateColorPicker()) {
+ Unused << Send__delete__(this, mInitialColor);
+ return true;
+ }
+
+ mCallback = new ColorPickerShownCallback(this);
+
+ mPicker->Open(mCallback);
+ return true;
+};
+
+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 000000000..bfdc82bdb
--- /dev/null
+++ b/dom/ipc/ColorPickerParent.h
@@ -0,0 +1,61 @@
+/* -*- 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 bool 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() {}
+ ColorPickerParent* mColorPickerParent;
+ };
+
+ private:
+ virtual ~ColorPickerParent() {}
+
+ 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/ContentBridgeChild.cpp b/dom/ipc/ContentBridgeChild.cpp
new file mode 100644
index 000000000..ae07528a2
--- /dev/null
+++ b/dom/ipc/ContentBridgeChild.cpp
@@ -0,0 +1,206 @@
+/* -*- 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/ContentBridgeChild.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/File.h"
+#include "mozilla/dom/TabChild.h"
+#include "mozilla/dom/ipc/BlobChild.h"
+#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
+#include "mozilla/ipc/InputStreamUtils.h"
+#include "base/task.h"
+
+using namespace mozilla::ipc;
+using namespace mozilla::jsipc;
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_ISUPPORTS(ContentBridgeChild,
+ nsIContentChild)
+
+ContentBridgeChild::ContentBridgeChild(Transport* aTransport)
+ : mTransport(aTransport)
+{}
+
+ContentBridgeChild::~ContentBridgeChild()
+{
+}
+
+void
+ContentBridgeChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+ MessageLoop::current()->PostTask(NewRunnableMethod(this, &ContentBridgeChild::DeferredDestroy));
+}
+
+/*static*/ ContentBridgeChild*
+ContentBridgeChild::Create(Transport* aTransport, ProcessId aOtherPid)
+{
+ RefPtr<ContentBridgeChild> bridge =
+ new ContentBridgeChild(aTransport);
+ bridge->mSelfRef = bridge;
+
+ DebugOnly<bool> ok = bridge->Open(aTransport, aOtherPid, XRE_GetIOMessageLoop());
+ MOZ_ASSERT(ok);
+
+ return bridge;
+}
+
+void
+ContentBridgeChild::DeferredDestroy()
+{
+ mSelfRef = nullptr;
+ // |this| was just destroyed, hands off
+}
+
+bool
+ContentBridgeChild::RecvAsyncMessage(const nsString& aMsg,
+ InfallibleTArray<jsipc::CpowEntry>&& aCpows,
+ const IPC::Principal& aPrincipal,
+ const ClonedMessageData& aData)
+{
+ return nsIContentChild::RecvAsyncMessage(aMsg, Move(aCpows), aPrincipal, aData);
+}
+
+PBlobChild*
+ContentBridgeChild::SendPBlobConstructor(PBlobChild* actor,
+ const BlobConstructorParams& params)
+{
+ return PContentBridgeChild::SendPBlobConstructor(actor, params);
+}
+
+bool
+ContentBridgeChild::SendPBrowserConstructor(PBrowserChild* aActor,
+ const TabId& aTabId,
+ const IPCTabContext& aContext,
+ const uint32_t& aChromeFlags,
+ const ContentParentId& aCpID,
+ const bool& aIsForApp,
+ const bool& aIsForBrowser)
+{
+ return PContentBridgeChild::SendPBrowserConstructor(aActor,
+ aTabId,
+ aContext,
+ aChromeFlags,
+ aCpID,
+ aIsForApp,
+ aIsForBrowser);
+}
+
+PFileDescriptorSetChild*
+ContentBridgeChild::SendPFileDescriptorSetConstructor(const FileDescriptor& aFD)
+{
+ return PContentBridgeChild::SendPFileDescriptorSetConstructor(aFD);
+}
+
+PSendStreamChild*
+ContentBridgeChild::SendPSendStreamConstructor(PSendStreamChild* aActor)
+{
+ return PContentBridgeChild::SendPSendStreamConstructor(aActor);
+}
+
+// This implementation is identical to ContentChild::GetCPOWManager but we can't
+// move it to nsIContentChild because it calls ManagedPJavaScriptChild() which
+// only exists in PContentChild and PContentBridgeChild.
+jsipc::CPOWManager*
+ContentBridgeChild::GetCPOWManager()
+{
+ if (PJavaScriptChild* c = LoneManagedOrNullAsserts(ManagedPJavaScriptChild())) {
+ return CPOWManagerFor(c);
+ }
+ return CPOWManagerFor(SendPJavaScriptConstructor());
+}
+
+mozilla::jsipc::PJavaScriptChild *
+ContentBridgeChild::AllocPJavaScriptChild()
+{
+ return nsIContentChild::AllocPJavaScriptChild();
+}
+
+bool
+ContentBridgeChild::DeallocPJavaScriptChild(PJavaScriptChild *child)
+{
+ return nsIContentChild::DeallocPJavaScriptChild(child);
+}
+
+PBrowserChild*
+ContentBridgeChild::AllocPBrowserChild(const TabId& aTabId,
+ const IPCTabContext &aContext,
+ const uint32_t& aChromeFlags,
+ const ContentParentId& aCpID,
+ const bool& aIsForApp,
+ const bool& aIsForBrowser)
+{
+ return nsIContentChild::AllocPBrowserChild(aTabId,
+ aContext,
+ aChromeFlags,
+ aCpID,
+ aIsForApp,
+ aIsForBrowser);
+}
+
+bool
+ContentBridgeChild::DeallocPBrowserChild(PBrowserChild* aChild)
+{
+ return nsIContentChild::DeallocPBrowserChild(aChild);
+}
+
+bool
+ContentBridgeChild::RecvPBrowserConstructor(PBrowserChild* aActor,
+ const TabId& aTabId,
+ const IPCTabContext& aContext,
+ const uint32_t& aChromeFlags,
+ const ContentParentId& aCpID,
+ const bool& aIsForApp,
+ const bool& aIsForBrowser)
+{
+ return ContentChild::GetSingleton()->RecvPBrowserConstructor(aActor,
+ aTabId,
+ aContext,
+ aChromeFlags,
+ aCpID,
+ aIsForApp,
+ aIsForBrowser);
+}
+
+PBlobChild*
+ContentBridgeChild::AllocPBlobChild(const BlobConstructorParams& aParams)
+{
+ return nsIContentChild::AllocPBlobChild(aParams);
+}
+
+bool
+ContentBridgeChild::DeallocPBlobChild(PBlobChild* aActor)
+{
+ return nsIContentChild::DeallocPBlobChild(aActor);
+}
+
+PSendStreamChild*
+ContentBridgeChild::AllocPSendStreamChild()
+{
+ return nsIContentChild::AllocPSendStreamChild();
+}
+
+bool
+ContentBridgeChild::DeallocPSendStreamChild(PSendStreamChild* aActor)
+{
+ return nsIContentChild::DeallocPSendStreamChild(aActor);
+}
+
+PFileDescriptorSetChild*
+ContentBridgeChild::AllocPFileDescriptorSetChild(const FileDescriptor& aFD)
+{
+ return nsIContentChild::AllocPFileDescriptorSetChild(aFD);
+}
+
+bool
+ContentBridgeChild::DeallocPFileDescriptorSetChild(PFileDescriptorSetChild* aActor)
+{
+ return nsIContentChild::DeallocPFileDescriptorSetChild(aActor);
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/ContentBridgeChild.h b/dom/ipc/ContentBridgeChild.h
new file mode 100644
index 000000000..179328929
--- /dev/null
+++ b/dom/ipc/ContentBridgeChild.h
@@ -0,0 +1,102 @@
+/* -*- 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_ContentBridgeChild_h
+#define mozilla_dom_ContentBridgeChild_h
+
+#include "mozilla/dom/PContentBridgeChild.h"
+#include "mozilla/dom/nsIContentChild.h"
+
+namespace mozilla {
+namespace dom {
+
+class ContentBridgeChild final : public PContentBridgeChild
+ , public nsIContentChild
+{
+public:
+ explicit ContentBridgeChild(Transport* aTransport);
+
+ NS_DECL_ISUPPORTS
+
+ static ContentBridgeChild*
+ Create(Transport* aTransport, ProcessId aOtherProcess);
+
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+ void DeferredDestroy();
+
+ virtual bool RecvAsyncMessage(const nsString& aMsg,
+ InfallibleTArray<jsipc::CpowEntry>&& aCpows,
+ const IPC::Principal& aPrincipal,
+ const ClonedMessageData& aData) override;
+
+ virtual PBlobChild*
+ SendPBlobConstructor(PBlobChild* actor,
+ const BlobConstructorParams& aParams) override;
+
+ jsipc::CPOWManager* GetCPOWManager() override;
+
+ virtual bool SendPBrowserConstructor(PBrowserChild* aActor,
+ const TabId& aTabId,
+ const IPCTabContext& aContext,
+ const uint32_t& aChromeFlags,
+ const ContentParentId& aCpID,
+ const bool& aIsForApp,
+ const bool& aIsForBrowser) override;
+
+ virtual mozilla::ipc::PFileDescriptorSetChild*
+ SendPFileDescriptorSetConstructor(const mozilla::ipc::FileDescriptor&) override;
+
+ virtual mozilla::ipc::PSendStreamChild*
+ SendPSendStreamConstructor(mozilla::ipc::PSendStreamChild*) override;
+
+ FORWARD_SHMEM_ALLOCATOR_TO(PContentBridgeChild)
+
+protected:
+ virtual ~ContentBridgeChild();
+
+ virtual PBrowserChild* AllocPBrowserChild(const TabId& aTabId,
+ const IPCTabContext& aContext,
+ const uint32_t& aChromeFlags,
+ const ContentParentId& aCpID,
+ const bool& aIsForApp,
+ const bool& aIsForBrowser) override;
+ virtual bool DeallocPBrowserChild(PBrowserChild*) override;
+ virtual bool RecvPBrowserConstructor(PBrowserChild* aCctor,
+ const TabId& aTabId,
+ const IPCTabContext& aContext,
+ const uint32_t& aChromeFlags,
+ const ContentParentId& aCpID,
+ const bool& aIsForApp,
+ const bool& aIsForBrowser) override;
+
+ virtual mozilla::jsipc::PJavaScriptChild* AllocPJavaScriptChild() override;
+ virtual bool DeallocPJavaScriptChild(mozilla::jsipc::PJavaScriptChild*) override;
+
+ virtual PBlobChild* AllocPBlobChild(const BlobConstructorParams& aParams) override;
+ virtual bool DeallocPBlobChild(PBlobChild*) override;
+
+ virtual mozilla::ipc::PSendStreamChild* AllocPSendStreamChild() override;
+
+ virtual bool
+ DeallocPSendStreamChild(mozilla::ipc::PSendStreamChild* aActor) override;
+
+ virtual PFileDescriptorSetChild*
+ AllocPFileDescriptorSetChild(const mozilla::ipc::FileDescriptor& aFD) override;
+
+ virtual bool
+ DeallocPFileDescriptorSetChild(mozilla::ipc::PFileDescriptorSetChild* aActor) override;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ContentBridgeChild);
+
+protected: // members
+ RefPtr<ContentBridgeChild> mSelfRef;
+ Transport* mTransport; // owned
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_ContentBridgeChild_h
diff --git a/dom/ipc/ContentBridgeParent.cpp b/dom/ipc/ContentBridgeParent.cpp
new file mode 100644
index 000000000..eee0e1c10
--- /dev/null
+++ b/dom/ipc/ContentBridgeParent.cpp
@@ -0,0 +1,221 @@
+/* -*- 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/ContentBridgeParent.h"
+#include "mozilla/dom/TabParent.h"
+#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
+#include "nsXULAppAPI.h"
+#include "nsIObserverService.h"
+#include "base/task.h"
+
+using namespace mozilla::ipc;
+using namespace mozilla::jsipc;
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_ISUPPORTS(ContentBridgeParent,
+ nsIContentParent,
+ nsIObserver)
+
+ContentBridgeParent::ContentBridgeParent(Transport* aTransport)
+ : mTransport(aTransport)
+{}
+
+ContentBridgeParent::~ContentBridgeParent()
+{
+}
+
+void
+ContentBridgeParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+ nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+ if (os) {
+ os->RemoveObserver(this, "content-child-shutdown");
+ }
+ MessageLoop::current()->PostTask(NewRunnableMethod(this, &ContentBridgeParent::DeferredDestroy));
+}
+
+/*static*/ ContentBridgeParent*
+ContentBridgeParent::Create(Transport* aTransport, ProcessId aOtherPid)
+{
+ RefPtr<ContentBridgeParent> bridge =
+ new ContentBridgeParent(aTransport);
+ bridge->mSelfRef = bridge;
+
+ DebugOnly<bool> ok = bridge->Open(aTransport, aOtherPid,
+ XRE_GetIOMessageLoop());
+ MOZ_ASSERT(ok);
+
+ nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
+ if (os) {
+ os->AddObserver(bridge, "content-child-shutdown", false);
+ }
+
+ // Initialize the message manager (and load delayed scripts) now that we
+ // have established communications with the child.
+ bridge->mMessageManager->InitWithCallback(bridge);
+
+ return bridge.get();
+}
+
+void
+ContentBridgeParent::DeferredDestroy()
+{
+ mSelfRef = nullptr;
+ // |this| was just destroyed, hands off
+}
+
+bool
+ContentBridgeParent::RecvSyncMessage(const nsString& aMsg,
+ const ClonedMessageData& aData,
+ InfallibleTArray<jsipc::CpowEntry>&& aCpows,
+ const IPC::Principal& aPrincipal,
+ nsTArray<StructuredCloneData>* aRetvals)
+{
+ return nsIContentParent::RecvSyncMessage(aMsg, aData, Move(aCpows),
+ aPrincipal, aRetvals);
+}
+
+bool
+ContentBridgeParent::RecvAsyncMessage(const nsString& aMsg,
+ InfallibleTArray<jsipc::CpowEntry>&& aCpows,
+ const IPC::Principal& aPrincipal,
+ const ClonedMessageData& aData)
+{
+ return nsIContentParent::RecvAsyncMessage(aMsg, Move(aCpows),
+ aPrincipal, aData);
+}
+
+PBlobParent*
+ContentBridgeParent::SendPBlobConstructor(PBlobParent* actor,
+ const BlobConstructorParams& params)
+{
+ return PContentBridgeParent::SendPBlobConstructor(actor, params);
+}
+
+PBrowserParent*
+ContentBridgeParent::SendPBrowserConstructor(PBrowserParent* aActor,
+ const TabId& aTabId,
+ const IPCTabContext& aContext,
+ const uint32_t& aChromeFlags,
+ const ContentParentId& aCpID,
+ const bool& aIsForApp,
+ const bool& aIsForBrowser)
+{
+ return PContentBridgeParent::SendPBrowserConstructor(aActor,
+ aTabId,
+ aContext,
+ aChromeFlags,
+ aCpID,
+ aIsForApp,
+ aIsForBrowser);
+}
+
+PBlobParent*
+ContentBridgeParent::AllocPBlobParent(const BlobConstructorParams& aParams)
+{
+ return nsIContentParent::AllocPBlobParent(aParams);
+}
+
+bool
+ContentBridgeParent::DeallocPBlobParent(PBlobParent* aActor)
+{
+ return nsIContentParent::DeallocPBlobParent(aActor);
+}
+
+mozilla::jsipc::PJavaScriptParent *
+ContentBridgeParent::AllocPJavaScriptParent()
+{
+ return nsIContentParent::AllocPJavaScriptParent();
+}
+
+bool
+ContentBridgeParent::DeallocPJavaScriptParent(PJavaScriptParent *parent)
+{
+ return nsIContentParent::DeallocPJavaScriptParent(parent);
+}
+
+PBrowserParent*
+ContentBridgeParent::AllocPBrowserParent(const TabId& aTabId,
+ const IPCTabContext &aContext,
+ const uint32_t& aChromeFlags,
+ const ContentParentId& aCpID,
+ const bool& aIsForApp,
+ const bool& aIsForBrowser)
+{
+ return nsIContentParent::AllocPBrowserParent(aTabId,
+ aContext,
+ aChromeFlags,
+ aCpID,
+ aIsForApp,
+ aIsForBrowser);
+}
+
+bool
+ContentBridgeParent::DeallocPBrowserParent(PBrowserParent* aParent)
+{
+ return nsIContentParent::DeallocPBrowserParent(aParent);
+}
+
+void
+ContentBridgeParent::NotifyTabDestroyed()
+{
+ int32_t numLiveTabs = ManagedPBrowserParent().Count();
+ if (numLiveTabs == 1) {
+ MessageLoop::current()->PostTask(NewRunnableMethod(this, &ContentBridgeParent::Close));
+ }
+}
+
+// This implementation is identical to ContentParent::GetCPOWManager but we can't
+// move it to nsIContentParent because it calls ManagedPJavaScriptParent() which
+// only exists in PContentParent and PContentBridgeParent.
+jsipc::CPOWManager*
+ContentBridgeParent::GetCPOWManager()
+{
+ if (PJavaScriptParent* p = LoneManagedOrNullAsserts(ManagedPJavaScriptParent())) {
+ return CPOWManagerFor(p);
+ }
+ return nullptr;
+}
+
+NS_IMETHODIMP
+ContentBridgeParent::Observe(nsISupports* aSubject,
+ const char* aTopic,
+ const char16_t* aData)
+{
+ if (!strcmp(aTopic, "content-child-shutdown")) {
+ Close();
+ }
+ return NS_OK;
+}
+
+PFileDescriptorSetParent*
+ContentBridgeParent::AllocPFileDescriptorSetParent(const FileDescriptor& aFD)
+{
+ return nsIContentParent::AllocPFileDescriptorSetParent(aFD);
+}
+
+bool
+ContentBridgeParent::DeallocPFileDescriptorSetParent(PFileDescriptorSetParent* aActor)
+{
+ return nsIContentParent::DeallocPFileDescriptorSetParent(aActor);
+}
+
+PSendStreamParent*
+ContentBridgeParent::AllocPSendStreamParent()
+{
+ return nsIContentParent::AllocPSendStreamParent();
+}
+
+bool
+ContentBridgeParent::DeallocPSendStreamParent(PSendStreamParent* aActor)
+{
+ return nsIContentParent::DeallocPSendStreamParent(aActor);
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/ContentBridgeParent.h b/dom/ipc/ContentBridgeParent.h
new file mode 100644
index 000000000..2f7c951b5
--- /dev/null
+++ b/dom/ipc/ContentBridgeParent.h
@@ -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/. */
+
+#ifndef mozilla_dom_ContentBridgeParent_h
+#define mozilla_dom_ContentBridgeParent_h
+
+#include "mozilla/dom/PContentBridgeParent.h"
+#include "mozilla/dom/nsIContentParent.h"
+#include "mozilla/dom/ipc/IdType.h"
+#include "nsIObserver.h"
+
+namespace mozilla {
+namespace dom {
+
+class ContentBridgeParent : public PContentBridgeParent
+ , public nsIContentParent
+ , public nsIObserver
+{
+public:
+ explicit ContentBridgeParent(Transport* aTransport);
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+ void DeferredDestroy();
+ virtual bool IsContentBridgeParent() const override { return true; }
+ void NotifyTabDestroyed();
+
+ static ContentBridgeParent*
+ Create(Transport* aTransport, ProcessId aOtherProcess);
+
+ virtual PBlobParent*
+ SendPBlobConstructor(PBlobParent* actor,
+ const BlobConstructorParams& params) override;
+
+ virtual PBrowserParent*
+ SendPBrowserConstructor(PBrowserParent* aActor,
+ const TabId& aTabId,
+ const IPCTabContext& aContext,
+ const uint32_t& aChromeFlags,
+ const ContentParentId& aCpID,
+ const bool& aIsForApp,
+ const bool& aIsForBrowser) override;
+
+ FORWARD_SHMEM_ALLOCATOR_TO(PContentBridgeParent)
+
+ jsipc::CPOWManager* GetCPOWManager() override;
+
+ virtual ContentParentId ChildID() const override
+ {
+ return mChildID;
+ }
+ virtual bool IsForApp() const override
+ {
+ return mIsForApp;
+ }
+ virtual bool IsForBrowser() const override
+ {
+ return mIsForBrowser;
+ }
+ virtual int32_t Pid() const override
+ {
+ // XXX: do we need this for ContentBridgeParent?
+ return -1;
+ }
+
+protected:
+ virtual ~ContentBridgeParent();
+
+ void SetChildID(ContentParentId aId)
+ {
+ mChildID = aId;
+ }
+
+ void SetIsForApp(bool aIsForApp)
+ {
+ mIsForApp = aIsForApp;
+ }
+
+ void SetIsForBrowser(bool aIsForBrowser)
+ {
+ mIsForBrowser = aIsForBrowser;
+ }
+
+ void Close()
+ {
+ // Trick NewRunnableMethod
+ PContentBridgeParent::Close();
+ }
+
+protected:
+ virtual bool
+ RecvSyncMessage(const nsString& aMsg,
+ const ClonedMessageData& aData,
+ InfallibleTArray<jsipc::CpowEntry>&& aCpows,
+ const IPC::Principal& aPrincipal,
+ nsTArray<StructuredCloneData>* aRetvals) override;
+
+ virtual bool RecvAsyncMessage(const nsString& aMsg,
+ InfallibleTArray<jsipc::CpowEntry>&& aCpows,
+ const IPC::Principal& aPrincipal,
+ const ClonedMessageData& aData) override;
+
+ virtual jsipc::PJavaScriptParent* AllocPJavaScriptParent() override;
+
+ virtual bool
+ DeallocPJavaScriptParent(jsipc::PJavaScriptParent*) override;
+
+ virtual PBrowserParent*
+ AllocPBrowserParent(const TabId& aTabId,
+ const IPCTabContext &aContext,
+ const uint32_t& aChromeFlags,
+ const ContentParentId& aCpID,
+ const bool& aIsForApp,
+ const bool& aIsForBrowser) override;
+
+ virtual bool DeallocPBrowserParent(PBrowserParent*) override;
+
+ virtual PBlobParent*
+ AllocPBlobParent(const BlobConstructorParams& aParams) override;
+
+ virtual bool DeallocPBlobParent(PBlobParent*) override;
+
+ virtual PSendStreamParent* AllocPSendStreamParent() override;
+
+ virtual bool DeallocPSendStreamParent(PSendStreamParent* aActor) override;
+
+ virtual PFileDescriptorSetParent*
+ AllocPFileDescriptorSetParent(const mozilla::ipc::FileDescriptor&) override;
+
+ virtual bool
+ DeallocPFileDescriptorSetParent(PFileDescriptorSetParent*) override;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ContentBridgeParent);
+
+protected: // members
+ RefPtr<ContentBridgeParent> mSelfRef;
+ Transport* mTransport; // owned
+ ContentParentId mChildID;
+ bool mIsForApp;
+ bool mIsForBrowser;
+
+private:
+ friend class ContentParent;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_ContentBridgeParent_h
diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp
new file mode 100644
index 000000000..ca4acf114
--- /dev/null
+++ b/dom/ipc/ContentChild.cpp
@@ -0,0 +1,3310 @@
+/* -*- 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 <gtk/gtk.h>
+#endif
+
+#include "ContentChild.h"
+
+#include "BlobChild.h"
+#include "CrashReporterChild.h"
+#include "GeckoProfiler.h"
+#include "TabChild.h"
+#include "HandlerServiceChild.h"
+
+#include "mozilla/Attributes.h"
+#include "mozilla/LookAndFeel.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/ProcessHangMonitorIPC.h"
+#include "mozilla/Unused.h"
+#include "mozilla/devtools/HeapSnapshotTempFileHelperChild.h"
+#include "mozilla/docshell/OfflineCacheUpdateChild.h"
+#include "mozilla/dom/ContentBridgeChild.h"
+#include "mozilla/dom/ContentBridgeParent.h"
+#include "mozilla/dom/VideoDecoderManagerChild.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/DataTransfer.h"
+#include "mozilla/dom/DOMStorageIPC.h"
+#include "mozilla/dom/ExternalHelperAppChild.h"
+#include "mozilla/dom/FlyWebPublishedServerIPC.h"
+#include "mozilla/dom/GetFilesHelper.h"
+#include "mozilla/dom/PCrashReporterChild.h"
+#include "mozilla/dom/ProcessGlobal.h"
+#include "mozilla/dom/PushNotifier.h"
+#include "mozilla/dom/workers/ServiceWorkerManager.h"
+#include "mozilla/dom/nsIContentChild.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/psm/PSMContentListener.h"
+#include "mozilla/hal_sandbox/PHalChild.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/FileDescriptorSetChild.h"
+#include "mozilla/ipc/FileDescriptorUtils.h"
+#include "mozilla/ipc/GeckoChildProcessHost.h"
+#include "mozilla/ipc/ProcessChild.h"
+#include "mozilla/ipc/PSendStreamChild.h"
+#include "mozilla/ipc/TestShellChild.h"
+#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
+#include "mozilla/layers/APZChild.h"
+#include "mozilla/layers/CompositorBridgeChild.h"
+#include "mozilla/layers/ContentProcessController.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+#include "mozilla/layout/RenderFrameChild.h"
+#include "mozilla/net/NeckoChild.h"
+#include "mozilla/net/CaptivePortalService.h"
+#include "mozilla/plugins/PluginInstanceParent.h"
+#include "mozilla/plugins/PluginModuleParent.h"
+#include "mozilla/widget/WidgetMessageUtils.h"
+#include "nsBaseDragService.h"
+#include "mozilla/media/MediaChild.h"
+#include "mozilla/BasePrincipal.h"
+#include "mozilla/WebBrowserPersistDocumentChild.h"
+#include "imgLoader.h"
+#include "GMPServiceChild.h"
+
+#if defined(MOZ_CONTENT_SANDBOX)
+#if defined(XP_WIN)
+#define TARGET_SANDBOX_EXPORTS
+#include "mozilla/sandboxTarget.h"
+#elif defined(XP_LINUX)
+#include "mozilla/Sandbox.h"
+#include "mozilla/SandboxInfo.h"
+
+// Remove this include with Bug 1104619
+#include "CubebUtils.h"
+#elif defined(XP_MACOSX)
+#include "mozilla/Sandbox.h"
+#endif
+#endif
+
+#include "mozilla/Unused.h"
+
+#include "mozInlineSpellChecker.h"
+#include "nsDocShell.h"
+#include "nsIConsoleListener.h"
+#include "nsICycleCollectorListener.h"
+#include "nsIIdlePeriod.h"
+#include "nsIDragService.h"
+#include "nsIIPCBackgroundChildCreateCallback.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIMemoryReporter.h"
+#include "nsIMemoryInfoDumper.h"
+#include "nsIMutable.h"
+#include "nsIObserverService.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsScreenManagerProxy.h"
+#include "nsMemoryInfoDumper.h"
+#include "nsServiceManagerUtils.h"
+#include "nsStyleSheetService.h"
+#include "nsVariant.h"
+#include "nsXULAppAPI.h"
+#include "nsIScriptError.h"
+#include "nsIConsoleService.h"
+#include "nsJSEnvironment.h"
+#include "SandboxHal.h"
+#include "nsDebugImpl.h"
+#include "nsHashPropertyBag.h"
+#include "nsLayoutStylesheetCache.h"
+#include "nsThreadManager.h"
+#include "nsAnonymousTemporaryFile.h"
+#include "nsISpellChecker.h"
+#include "nsClipboardProxy.h"
+#include "nsDirectoryService.h"
+#include "nsDirectoryServiceUtils.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsContentPermissionHelper.h"
+#ifdef NS_PRINTING
+#include "nsPrintingProxy.h"
+#endif
+
+#include "IHistory.h"
+#include "nsNetUtil.h"
+
+#include "base/message_loop.h"
+#include "base/process_util.h"
+#include "base/task.h"
+
+#include "nsChromeRegistryContent.h"
+#include "nsFrameMessageManager.h"
+
+#include "nsIGeolocationProvider.h"
+#include "mozilla/dom/PMemoryReportRequestChild.h"
+#include "mozilla/dom/PCycleCollectWithLogsChild.h"
+
+#include "nsIScriptSecurityManager.h"
+#include "nsHostObjectProtocolHandler.h"
+
+#ifdef MOZ_WEBRTC
+#include "signaling/src/peerconnection/WebrtcGlobalChild.h"
+#endif
+
+#ifdef MOZ_PERMISSIONS
+#include "nsPermission.h"
+#include "nsPermissionManager.h"
+#endif
+
+#include "PermissionMessageUtils.h"
+
+#if defined(MOZ_WIDGET_ANDROID)
+#include "APKOpen.h"
+#endif
+
+#if defined(MOZ_WIDGET_GONK)
+#include "nsVolume.h"
+#include "nsVolumeService.h"
+#include "SpeakerManagerService.h"
+#endif
+
+#ifdef XP_WIN
+#include <process.h>
+#define getpid _getpid
+#include "mozilla/widget/AudioSession.h"
+#endif
+
+#ifdef MOZ_X11
+#include "mozilla/X11Util.h"
+#endif
+
+#ifdef ACCESSIBILITY
+#include "nsAccessibilityService.h"
+#endif
+
+#include "mozilla/dom/File.h"
+#include "mozilla/dom/PPresentationChild.h"
+#include "mozilla/dom/PresentationIPCService.h"
+#include "mozilla/ipc/InputStreamUtils.h"
+
+#ifdef MOZ_WEBSPEECH
+#include "mozilla/dom/PSpeechSynthesisChild.h"
+#endif
+
+#include "ProcessUtils.h"
+#include "URIUtils.h"
+#include "nsContentUtils.h"
+#include "nsIPrincipal.h"
+#include "DomainPolicy.h"
+#include "mozilla/dom/ipc/StructuredCloneData.h"
+#include "mozilla/dom/time/DateCacheCleaner.h"
+#include "mozilla/net/NeckoMessageUtils.h"
+#include "mozilla/widget/PuppetBidiKeyboard.h"
+#include "mozilla/RemoteSpellCheckEngineChild.h"
+#include "GMPServiceChild.h"
+#include "gfxPlatform.h"
+#include "nscore.h" // for NS_FREE_PERMANENT_DATA
+#include "VRManagerChild.h"
+
+using namespace mozilla;
+using namespace mozilla::docshell;
+using namespace mozilla::dom::ipc;
+using namespace mozilla::dom::workers;
+using namespace mozilla::media;
+using namespace mozilla::embedding;
+using namespace mozilla::gmp;
+using namespace mozilla::hal_sandbox;
+using namespace mozilla::ipc;
+using namespace mozilla::layers;
+using namespace mozilla::layout;
+using namespace mozilla::net;
+using namespace mozilla::jsipc;
+using namespace mozilla::psm;
+using namespace mozilla::widget;
+#if defined(MOZ_WIDGET_GONK)
+using namespace mozilla::system;
+#endif
+using namespace mozilla::widget;
+
+namespace mozilla {
+namespace dom {
+
+class MemoryReportRequestChild : public PMemoryReportRequestChild,
+ public nsIRunnable
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ MemoryReportRequestChild(bool aAnonymize,
+ const MaybeFileDesc& aDMDFile);
+ NS_IMETHOD Run() override;
+
+private:
+ virtual ~MemoryReportRequestChild();
+
+ bool mAnonymize;
+ FileDescriptor mDMDFile;
+};
+
+NS_IMPL_ISUPPORTS(MemoryReportRequestChild, nsIRunnable)
+
+MemoryReportRequestChild::MemoryReportRequestChild(
+ bool aAnonymize, const MaybeFileDesc& aDMDFile)
+: mAnonymize(aAnonymize)
+{
+ MOZ_COUNT_CTOR(MemoryReportRequestChild);
+ if (aDMDFile.type() == MaybeFileDesc::TFileDescriptor) {
+ mDMDFile = aDMDFile.get_FileDescriptor();
+ }
+}
+
+MemoryReportRequestChild::~MemoryReportRequestChild()
+{
+ MOZ_COUNT_DTOR(MemoryReportRequestChild);
+}
+
+// IPC sender for remote GC/CC logging.
+class CycleCollectWithLogsChild final
+ : public PCycleCollectWithLogsChild
+ , public nsICycleCollectorLogSink
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ CycleCollectWithLogsChild(const FileDescriptor& aGCLog,
+ const FileDescriptor& aCCLog)
+ {
+ 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;
+ SendCloseGCLog();
+ return NS_OK;
+ }
+
+ NS_IMETHOD CloseCCLog() override
+ {
+ MOZ_ASSERT(mCCLog);
+ fclose(mCCLog);
+ mCCLog = nullptr;
+ 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:
+ ~CycleCollectWithLogsChild()
+ {
+ if (mGCLog) {
+ fclose(mGCLog);
+ mGCLog = nullptr;
+ }
+ if (mCCLog) {
+ fclose(mCCLog);
+ mCCLog = nullptr;
+ }
+ // The XPCOM refcount drives the IPC lifecycle; see also
+ // DeallocPCycleCollectWithLogsChild.
+ Unused << Send__delete__(this);
+ }
+
+ nsresult UnimplementedProperty()
+ {
+ MOZ_ASSERT(false, "This object is a remote GC/CC logger;"
+ " this property isn't meaningful.");
+ return NS_ERROR_UNEXPECTED;
+ }
+
+ FILE* mGCLog;
+ FILE* mCCLog;
+};
+
+NS_IMPL_ISUPPORTS(CycleCollectWithLogsChild, nsICycleCollectorLogSink);
+
+class AlertObserver
+{
+public:
+
+ AlertObserver(nsIObserver *aObserver, const nsString& aData)
+ : mObserver(aObserver)
+ , mData(aData)
+ {
+ }
+
+ ~AlertObserver() {}
+
+ bool ShouldRemoveFrom(nsIObserver* aObserver,
+ const nsString& aData) const
+ {
+ return (mObserver == aObserver && mData == aData);
+ }
+
+ bool Observes(const nsString& aData) const
+ {
+ return mData.Equals(aData);
+ }
+
+ bool Notify(const nsCString& aType) const
+ {
+ mObserver->Observe(nullptr, aType.get(), mData.get());
+ return true;
+ }
+
+private:
+ 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() {}
+
+ 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;
+ nsXPIDLCString category;
+ uint32_t lineNum, colNum, flags;
+
+ 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);
+
+ mChild->SendScriptError(msg, sourceName, sourceLine,
+ lineNum, colNum, flags, category);
+ return NS_OK;
+ }
+
+ nsXPIDLString msg;
+ nsresult rv = aMessage->GetMessageMoz(getter_Copies(msg));
+ NS_ENSURE_SUCCESS(rv, rv);
+ mChild->SendConsoleMessage(msg);
+ return NS_OK;
+}
+
+class BackgroundChildPrimer final :
+ public nsIIPCBackgroundChildCreateCallback
+{
+public:
+ BackgroundChildPrimer()
+ { }
+
+ NS_DECL_ISUPPORTS
+
+private:
+ ~BackgroundChildPrimer()
+ { }
+
+ virtual void
+ ActorCreated(PBackgroundChild* aActor) override
+ {
+ MOZ_ASSERT(aActor, "Failed to create a PBackgroundChild actor!");
+ }
+
+ virtual void
+ ActorFailed() override
+ {
+ MOZ_CRASH("Failed to create a PBackgroundChild actor!");
+ }
+};
+
+NS_IMPL_ISUPPORTS(BackgroundChildPrimer, nsIIPCBackgroundChildCreateCallback)
+
+ContentChild* ContentChild::sSingleton;
+
+ContentChild::ContentChild()
+ : mID(uint64_t(-1))
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+ , mMsaaID(0)
+#endif
+ , mCanOverrideProcessName(true)
+ , mIsAlive(true)
+ , mShuttingDown(false)
+{
+ // This process is a content process, so it's clearly running in
+ // multiprocess mode!
+ nsDebugImpl::SetMultiprocessMode("Child");
+}
+
+ContentChild::~ContentChild()
+{
+#ifndef NS_FREE_PERMANENT_DATA
+ NS_RUNTIMEABORT("Content Child shouldn't be destroyed.");
+#endif
+}
+
+NS_INTERFACE_MAP_BEGIN(ContentChild)
+ NS_INTERFACE_MAP_ENTRY(nsIContentChild)
+ NS_INTERFACE_MAP_ENTRY(nsIWindowProvider)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentChild)
+NS_INTERFACE_MAP_END
+
+bool
+ContentChild::Init(MessageLoop* aIOLoop,
+ base::ProcessId aParentPid,
+ IPC::Channel* aChannel)
+{
+#ifdef MOZ_WIDGET_GTK
+ // 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.
+ char* 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,
+ display_name,
+ nullptr
+ };
+ char** argvp = argv;
+ gtk_init(&argc, &argvp);
+ } else {
+ gtk_init(nullptr, nullptr);
+ }
+#endif
+
+#ifdef MOZ_X11
+ // 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(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
+
+#ifdef MOZ_X11
+ // Send the parent our X socket to act as a proxy reference for our X
+ // resources.
+ int xSocketFd = ConnectionNumber(DefaultXDisplay());
+ SendBackUpXResources(FileDescriptor(xSocketFd));
+#endif
+
+#ifdef MOZ_CRASHREPORTER
+ SendPCrashReporterConstructor(CrashReporter::CurrentThreadId(),
+ XRE_GetProcessType());
+#endif
+
+ SendGetProcessAttributes(&mID, &mIsForApp, &mIsForBrowser);
+ InitProcessAttributes();
+
+#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
+
+ return true;
+}
+
+void
+ContentChild::InitProcessAttributes()
+{
+#ifdef MOZ_WIDGET_GONK
+ if (mIsForApp && !mIsForBrowser) {
+ SetProcessName(NS_LITERAL_STRING("(Preallocated app)"), false);
+ } else {
+ SetProcessName(NS_LITERAL_STRING("Browser"), false);
+ }
+#else
+ SetProcessName(NS_LITERAL_STRING("Web Content"), true);
+#endif
+}
+
+void
+ContentChild::SetProcessName(const nsAString& aName, bool aDontOverride)
+{
+ if (!mCanOverrideProcessName) {
+ return;
+ }
+
+ 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;
+ mozilla::ipc::SetThisProcessName(NS_LossyConvertUTF16toASCII(aName).get());
+
+ if (aDontOverride) {
+ mCanOverrideProcessName = false;
+ }
+}
+
+NS_IMETHODIMP
+ContentChild::ProvideWindow(mozIDOMWindowProxy* aParent,
+ uint32_t aChromeFlags,
+ bool aCalledFromJS,
+ bool aPositionSpecified,
+ bool aSizeSpecified,
+ nsIURI* aURI,
+ const nsAString& aName,
+ const nsACString& aFeatures,
+ bool aForceNoOpener,
+ bool* aWindowIsNew,
+ mozIDOMWindowProxy** aReturn)
+{
+ return ProvideWindowCommon(nullptr, aParent, false, aChromeFlags,
+ aCalledFromJS, aPositionSpecified,
+ aSizeSpecified, aURI, aName, aFeatures,
+ aForceNoOpener, aWindowIsNew, aReturn);
+}
+
+nsresult
+ContentChild::ProvideWindowCommon(TabChild* aTabOpener,
+ mozIDOMWindowProxy* aParent,
+ bool aIframeMoz,
+ uint32_t aChromeFlags,
+ bool aCalledFromJS,
+ bool aPositionSpecified,
+ bool aSizeSpecified,
+ nsIURI* aURI,
+ const nsAString& aName,
+ const nsACString& aFeatures,
+ bool aForceNoOpener,
+ bool* aWindowIsNew,
+ mozIDOMWindowProxy** aReturn)
+{
+ *aReturn = nullptr;
+
+ nsAutoPtr<IPCTabContext> ipcContext;
+ TabId openerTabId = TabId(0);
+
+ if (aTabOpener) {
+ PopupIPCTabContext context;
+ openerTabId = aTabOpener->GetTabId();
+ context.opener() = openerTabId;
+ context.isMozBrowserElement() = aTabOpener->IsMozBrowserElement();
+ ipcContext = new IPCTabContext(context);
+ } else {
+ // It's possible to not have a TabChild opener in the case
+ // of ServiceWorker::OpenWindow.
+ UnsafeIPCTabContext unsafeTabContext;
+ ipcContext = new IPCTabContext(unsafeTabContext);
+ }
+
+ MOZ_ASSERT(ipcContext);
+ TabId tabId;
+ SendAllocateTabId(openerTabId,
+ *ipcContext,
+ GetID(),
+ &tabId);
+
+ TabContext newTabContext = aTabOpener ? *aTabOpener : TabContext();
+ RefPtr<TabChild> newChild = new TabChild(this, tabId,
+ newTabContext, aChromeFlags);
+ if (NS_FAILED(newChild->Init())) {
+ return NS_ERROR_ABORT;
+ }
+
+ if (aTabOpener) {
+ MOZ_ASSERT(ipcContext->type() == IPCTabContext::TPopupIPCTabContext);
+ ipcContext->get_PopupIPCTabContext().opener() = aTabOpener;
+ }
+
+ Unused << SendPBrowserConstructor(
+ // We release this ref in DeallocPBrowserChild
+ RefPtr<TabChild>(newChild).forget().take(),
+ tabId, *ipcContext, aChromeFlags,
+ GetID(), IsForApp(), IsForBrowser());
+
+ nsString name(aName);
+ nsAutoCString features(aFeatures);
+ nsTArray<FrameScriptInfo> frameScripts;
+ nsCString urlToLoad;
+
+ PRenderFrameChild* renderFrame = newChild->SendPRenderFrameConstructor();
+ TextureFactoryIdentifier textureFactoryIdentifier;
+ uint64_t layersId = 0;
+
+ if (aIframeMoz) {
+ MOZ_ASSERT(aTabOpener);
+ nsAutoCString url;
+ if (aURI) {
+ aURI->GetSpec(url);
+ } else {
+ // We can't actually send a nullptr up as the URI, since IPDL doesn't let us
+ // send nullptr's for primitives. We indicate that the nsString for the URI
+ // should be converted to a nullptr by voiding the string.
+ url.SetIsVoid(true);
+ }
+
+ newChild->SendBrowserFrameOpenWindow(aTabOpener, renderFrame, NS_ConvertUTF8toUTF16(url),
+ name, NS_ConvertUTF8toUTF16(features),
+ aWindowIsNew, &textureFactoryIdentifier,
+ &layersId);
+ } else {
+ nsAutoCString baseURIString;
+ if (aTabOpener) {
+ auto* opener = nsPIDOMWindowOuter::From(aParent);
+ nsCOMPtr<nsIDocument> doc = opener->GetDoc();
+ nsCOMPtr<nsIURI> baseURI = doc->GetDocBaseURI();
+ if (!baseURI) {
+ NS_ERROR("nsIDocument didn't return a base URI");
+ return NS_ERROR_FAILURE;
+ }
+
+ baseURI->GetSpec(baseURIString);
+ }
+
+ auto* opener = nsPIDOMWindowOuter::From(aParent);
+ nsIDocShell* openerShell;
+ RefPtr<nsDocShell> openerDocShell;
+ float fullZoom = 1.0f;
+ if (opener && (openerShell = opener->GetDocShell())) {
+ openerDocShell = static_cast<nsDocShell*>(openerShell);
+ nsCOMPtr<nsIContentViewer> cv;
+ openerDocShell->GetContentViewer(getter_AddRefs(cv));
+ if (cv) {
+ cv->GetFullZoom(&fullZoom);
+ }
+ }
+
+ nsresult rv;
+ if (!SendCreateWindow(aTabOpener, newChild, renderFrame,
+ aChromeFlags, aCalledFromJS, aPositionSpecified,
+ aSizeSpecified,
+ features,
+ baseURIString,
+ openerDocShell
+ ? openerDocShell->GetOriginAttributes()
+ : DocShellOriginAttributes(),
+ fullZoom,
+ &rv,
+ aWindowIsNew,
+ &frameScripts,
+ &urlToLoad,
+ &textureFactoryIdentifier,
+ &layersId)) {
+ PRenderFrameChild::Send__delete__(renderFrame);
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ if (NS_FAILED(rv)) {
+ PRenderFrameChild::Send__delete__(renderFrame);
+ return rv;
+ }
+ }
+ if (!*aWindowIsNew) {
+ PRenderFrameChild::Send__delete__(renderFrame);
+ return NS_ERROR_ABORT;
+ }
+
+ if (layersId == 0) { // if renderFrame is invalid.
+ PRenderFrameChild::Send__delete__(renderFrame);
+ renderFrame = nullptr;
+ }
+
+ ShowInfo showInfo(EmptyString(), false, false, true, false, 0, 0, 0);
+ auto* opener = nsPIDOMWindowOuter::From(aParent);
+ nsIDocShell* openerShell;
+ if (opener && (openerShell = opener->GetDocShell())) {
+ nsCOMPtr<nsILoadContext> context = do_QueryInterface(openerShell);
+ showInfo = ShowInfo(EmptyString(), false,
+ context->UsePrivateBrowsing(), true, false,
+ aTabOpener->mDPI, aTabOpener->mRounding,
+ aTabOpener->mDefaultScale);
+ }
+
+ // Set the opener window for this window before we start loading the document
+ // inside of it. We have to do this before loading the remote scripts, because
+ // they can poke at the document and cause the nsDocument to be created before
+ // the openerwindow
+ nsCOMPtr<mozIDOMWindowProxy> windowProxy = do_GetInterface(newChild->WebNavigation());
+ if (!aForceNoOpener && windowProxy && aParent) {
+ nsPIDOMWindowOuter* outer = nsPIDOMWindowOuter::From(windowProxy);
+ nsPIDOMWindowOuter* parent = nsPIDOMWindowOuter::From(aParent);
+ outer->SetOpenerWindow(parent, *aWindowIsNew);
+ }
+
+ // Unfortunately we don't get a window unless we've shown the frame. That's
+ // pretty bogus; see bug 763602.
+ newChild->DoFakeShow(textureFactoryIdentifier, layersId, renderFrame,
+ showInfo);
+
+ for (size_t i = 0; i < frameScripts.Length(); i++) {
+ FrameScriptInfo& info = frameScripts[i];
+ if (!newChild->RecvLoadRemoteScript(info.url(), info.runInGlobalScope())) {
+ MOZ_CRASH();
+ }
+ }
+
+ if (!urlToLoad.IsEmpty()) {
+ newChild->RecvLoadURL(urlToLoad, showInfo);
+ }
+
+ nsCOMPtr<mozIDOMWindowProxy> win = do_GetInterface(newChild->WebNavigation());
+ win.forget(aReturn);
+ return NS_OK;
+}
+
+void
+ContentChild::GetProcessName(nsAString& aName) const
+{
+ aName.Assign(mProcessName);
+}
+
+bool
+ContentChild::IsAlive() const
+{
+ return mIsAlive;
+}
+
+bool
+ContentChild::IsShuttingDown() const
+{
+ return mShuttingDown;
+}
+
+void
+ContentChild::GetProcessName(nsACString& aName) const
+{
+ aName.Assign(NS_ConvertUTF16toUTF8(mProcessName));
+}
+
+/* static */ void
+ContentChild::AppendProcessId(nsACString& aName)
+{
+ if (!aName.IsEmpty()) {
+ aName.Append(' ');
+ }
+ unsigned pid = getpid();
+ aName.Append(nsPrintfCString("(pid %u)", pid));
+}
+
+void
+ContentChild::InitGraphicsDeviceData()
+{
+ // Initialize the graphics platform. This may contact the parent process
+ // to read device preferences.
+ gfxPlatform::GetPlatform();
+}
+
+void
+ContentChild::InitXPCOM()
+{
+ // 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();
+
+ nsCOMPtr<nsIIPCBackgroundChildCreateCallback> callback =
+ new BackgroundChildPrimer();
+ if (!BackgroundChild::GetOrCreateForCurrentThread(callback)) {
+ MOZ_CRASH("Failed to create PBackgroundChild!");
+ }
+
+ BlobChild::Startup(BlobChild::FriendKey());
+
+ 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");
+
+ bool isOffline, isLangRTL, haveBidiKeyboards;
+ bool isConnected;
+ ClipboardCapabilities clipboardCaps;
+ DomainPolicyClone domainPolicy;
+ StructuredCloneData initialData;
+ OptionalURIParams userContentSheetURL;
+
+ int32_t captivePortalState;
+ SendGetXPCOMProcessAttributes(&isOffline, &isConnected, &captivePortalState,
+ &isLangRTL, &haveBidiKeyboards,
+ &mAvailableDictionaries,
+ &clipboardCaps, &domainPolicy, &initialData,
+ &userContentSheetURL);
+ RecvSetOffline(isOffline);
+ RecvSetConnectivity(isConnected);
+ RecvSetCaptivePortalState(captivePortalState);
+ RecvBidiKeyboardNotify(isLangRTL, haveBidiKeyboards);
+
+ // Create the CPOW manager as soon as possible.
+ SendPJavaScriptConstructor();
+
+ if (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(&domainPolicy);
+ }
+
+ nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1"));
+ if (nsCOMPtr<nsIClipboardProxy> clipboardProxy = do_QueryInterface(clipboard)) {
+ clipboardProxy->SetCapabilities(clipboardCaps);
+ }
+
+ {
+ AutoJSAPI jsapi;
+ if (NS_WARN_IF(!jsapi.Init(xpc::PrivilegedJunkScope()))) {
+ MOZ_CRASH();
+ }
+ ErrorResult rv;
+ JS::RootedValue data(jsapi.cx());
+ initialData.Read(jsapi.cx(), &data, rv);
+ if (NS_WARN_IF(rv.Failed())) {
+ MOZ_CRASH();
+ }
+ ProcessGlobal* global = ProcessGlobal::Get();
+ global->SetInitialProcessData(data);
+ }
+
+ // The stylesheet cache is not ready yet. Store this URL for future use.
+ nsCOMPtr<nsIURI> ucsURL = DeserializeURI(userContentSheetURL);
+ nsLayoutStylesheetCache::SetUserContentCSSURL(ucsURL);
+
+ // This will register cross-process observer.
+ mozilla::dom::time::InitializeDateCacheCleaner();
+}
+
+PMemoryReportRequestChild*
+ContentChild::AllocPMemoryReportRequestChild(const uint32_t& aGeneration,
+ const bool &aAnonymize,
+ const bool &aMinimizeMemoryUsage,
+ const MaybeFileDesc& aDMDFile)
+{
+ MemoryReportRequestChild *actor =
+ new MemoryReportRequestChild(aAnonymize, aDMDFile);
+ actor->AddRef();
+ return actor;
+}
+
+class HandleReportCallback final : public nsIHandleReportCallback
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ explicit HandleReportCallback(MemoryReportRequestChild* aActor,
+ const nsACString& aProcess)
+ : mActor(aActor)
+ , mProcess(aProcess)
+ { }
+
+ 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, nsCString(aDescription));
+ mActor->SendReport(memreport);
+ return NS_OK;
+ }
+private:
+ ~HandleReportCallback() {}
+
+ RefPtr<MemoryReportRequestChild> mActor;
+ const nsCString mProcess;
+};
+
+NS_IMPL_ISUPPORTS(
+ HandleReportCallback
+, nsIHandleReportCallback
+)
+
+class FinishReportingCallback final : public nsIFinishReportingCallback
+{
+public:
+ NS_DECL_ISUPPORTS
+
+ explicit FinishReportingCallback(MemoryReportRequestChild* aActor)
+ : mActor(aActor)
+ {
+ }
+
+ NS_IMETHOD Callback(nsISupports* aUnused) override
+ {
+ bool sent = PMemoryReportRequestChild::Send__delete__(mActor);
+ return sent ? NS_OK : NS_ERROR_FAILURE;
+ }
+
+private:
+ ~FinishReportingCallback() {}
+
+ RefPtr<MemoryReportRequestChild> mActor;
+};
+
+NS_IMPL_ISUPPORTS(
+ FinishReportingCallback
+, nsIFinishReportingCallback
+)
+
+bool
+ContentChild::RecvPMemoryReportRequestConstructor(
+ PMemoryReportRequestChild* aChild,
+ const uint32_t& aGeneration,
+ const bool& aAnonymize,
+ const bool& aMinimizeMemoryUsage,
+ const MaybeFileDesc& aDMDFile)
+{
+ MemoryReportRequestChild *actor =
+ static_cast<MemoryReportRequestChild*>(aChild);
+ DebugOnly<nsresult> rv;
+
+ if (aMinimizeMemoryUsage) {
+ nsCOMPtr<nsIMemoryReporterManager> mgr =
+ do_GetService("@mozilla.org/memory-reporter-manager;1");
+ rv = mgr->MinimizeMemoryUsage(actor);
+ // mgr will eventually call actor->Run()
+ } else {
+ rv = actor->Run();
+ }
+
+ // Bug 1295622: don't kill the process just because this failed.
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "actor operation failed");
+ return true;
+}
+
+NS_IMETHODIMP MemoryReportRequestChild::Run()
+{
+ ContentChild *child = static_cast<ContentChild*>(Manager());
+ nsCOMPtr<nsIMemoryReporterManager> mgr =
+ do_GetService("@mozilla.org/memory-reporter-manager;1");
+
+ nsCString process;
+ child->GetProcessName(process);
+ child->AppendProcessId(process);
+
+ // Run the reporters. The callback will turn each measurement into a
+ // MemoryReport.
+ RefPtr<HandleReportCallback> handleReport =
+ new HandleReportCallback(this, process);
+ RefPtr<FinishReportingCallback> finishReporting =
+ new FinishReportingCallback(this);
+
+ nsresult rv =
+ mgr->GetReportsForThisProcessExtended(handleReport, nullptr, mAnonymize,
+ FileDescriptorToFILE(mDMDFile, "wb"),
+ finishReporting, nullptr);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+ "GetReportsForThisProcessExtended failed");
+ return rv;
+}
+
+bool
+ContentChild::DeallocPMemoryReportRequestChild(PMemoryReportRequestChild* actor)
+{
+ static_cast<MemoryReportRequestChild*>(actor)->Release();
+ return true;
+}
+
+PCycleCollectWithLogsChild*
+ContentChild::AllocPCycleCollectWithLogsChild(const bool& aDumpAllTraces,
+ const FileDescriptor& aGCLog,
+ const FileDescriptor& aCCLog)
+{
+ CycleCollectWithLogsChild* actor = new CycleCollectWithLogsChild(aGCLog, aCCLog);
+ // Return actor with refcount 0, which is safe because it has a non-XPCOM type.
+ return actor;
+}
+
+bool
+ContentChild::RecvPCycleCollectWithLogsConstructor(PCycleCollectWithLogsChild* aActor,
+ const bool& aDumpAllTraces,
+ const FileDescriptor& aGCLog,
+ const FileDescriptor& aCCLog)
+{
+ // Take a reference here, where the XPCOM type is regained.
+ RefPtr<CycleCollectWithLogsChild> sink = static_cast<CycleCollectWithLogsChild*>(aActor);
+ nsCOMPtr<nsIMemoryInfoDumper> dumper = do_GetService("@mozilla.org/memory-info-dumper;1");
+
+ dumper->DumpGCAndCCLogsToSink(aDumpAllTraces, sink);
+
+ // The actor's destructor is called when the last reference goes away...
+ return true;
+}
+
+bool
+ContentChild::DeallocPCycleCollectWithLogsChild(PCycleCollectWithLogsChild* /* aActor */)
+{
+ // ...so when we get here, there's nothing for us to do.
+ //
+ // Also, we're already in ~CycleCollectWithLogsChild (q.v.) at
+ // this point, so we shouldn't touch the actor in any case.
+ return true;
+}
+
+mozilla::plugins::PPluginModuleParent*
+ContentChild::AllocPPluginModuleParent(mozilla::ipc::Transport* aTransport,
+ base::ProcessId aOtherProcess)
+{
+ return plugins::PluginModuleContentParent::Initialize(aTransport, aOtherProcess);
+}
+
+PContentBridgeChild*
+ContentChild::AllocPContentBridgeChild(mozilla::ipc::Transport* aTransport,
+ base::ProcessId aOtherProcess)
+{
+ return ContentBridgeChild::Create(aTransport, aOtherProcess);
+}
+
+PContentBridgeParent*
+ContentChild::AllocPContentBridgeParent(mozilla::ipc::Transport* aTransport,
+ base::ProcessId aOtherProcess)
+{
+ MOZ_ASSERT(!mLastBridge);
+ mLastBridge = static_cast<ContentBridgeParent*>(
+ ContentBridgeParent::Create(aTransport, aOtherProcess));
+ return mLastBridge;
+}
+
+PGMPServiceChild*
+ContentChild::AllocPGMPServiceChild(mozilla::ipc::Transport* aTransport,
+ base::ProcessId aOtherProcess)
+{
+ return GMPServiceChild::Create(aTransport, aOtherProcess);
+}
+
+bool
+ContentChild::RecvGMPsChanged(nsTArray<GMPCapabilityData>&& capabilities)
+{
+ GeckoMediaPluginServiceChild::UpdateGMPCapabilities(Move(capabilities));
+ return true;
+}
+
+bool
+ContentChild::RecvInitRendering(Endpoint<PCompositorBridgeChild>&& aCompositor,
+ Endpoint<PImageBridgeChild>&& aImageBridge,
+ Endpoint<PVRManagerChild>&& aVRBridge,
+ Endpoint<PVideoDecoderManagerChild>&& aVideoManager)
+{
+ if (!CompositorBridgeChild::InitForContent(Move(aCompositor))) {
+ return false;
+ }
+ if (!ImageBridgeChild::InitForContent(Move(aImageBridge))) {
+ return false;
+ }
+ if (!gfx::VRManagerChild::InitForContent(Move(aVRBridge))) {
+ return false;
+ }
+ VideoDecoderManagerChild::InitForContent(Move(aVideoManager));
+ return true;
+}
+
+bool
+ContentChild::RecvReinitRendering(Endpoint<PCompositorBridgeChild>&& aCompositor,
+ Endpoint<PImageBridgeChild>&& aImageBridge,
+ Endpoint<PVRManagerChild>&& aVRBridge,
+ Endpoint<PVideoDecoderManagerChild>&& aVideoManager)
+{
+ nsTArray<RefPtr<TabChild>> tabs = TabChild::GetAll();
+
+ // Zap all the old layer managers we have lying around.
+ for (const auto& tabChild : tabs) {
+ if (tabChild->LayersId()) {
+ tabChild->InvalidateLayers();
+ }
+ }
+
+ // Re-establish singleton bridges to the compositor.
+ if (!CompositorBridgeChild::ReinitForContent(Move(aCompositor))) {
+ return false;
+ }
+ if (!ImageBridgeChild::ReinitForContent(Move(aImageBridge))) {
+ return false;
+ }
+ if (!gfx::VRManagerChild::ReinitForContent(Move(aVRBridge))) {
+ return false;
+ }
+
+ // Establish new PLayerTransactions.
+ for (const auto& tabChild : tabs) {
+ if (tabChild->LayersId()) {
+ tabChild->ReinitRendering();
+ }
+ }
+
+ VideoDecoderManagerChild::InitForContent(Move(aVideoManager));
+ return true;
+}
+
+PBackgroundChild*
+ContentChild::AllocPBackgroundChild(Transport* aTransport,
+ ProcessId aOtherProcess)
+{
+ return BackgroundChild::Alloc(aTransport, aOtherProcess);
+}
+
+PProcessHangMonitorChild*
+ContentChild::AllocPProcessHangMonitorChild(Transport* aTransport,
+ ProcessId aOtherProcess)
+{
+ return CreateHangMonitorChild(aTransport, aOtherProcess);
+}
+
+#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
+
+#include <stdlib.h>
+
+static bool
+GetAppPaths(nsCString &aAppPath, nsCString &aAppBinaryPath, nsCString &aAppDir)
+{
+ nsAutoCString appPath;
+ nsAutoCString appBinaryPath(
+ (CommandLine::ForCurrentProcess()->argv()[0]).c_str());
+
+ nsAutoCString::const_iterator start, end;
+ appBinaryPath.BeginReading(start);
+ appBinaryPath.EndReading(end);
+ if (RFindInReadable(NS_LITERAL_CSTRING(".app/Contents/MacOS/"), start, end)) {
+ end = start;
+ ++end; ++end; ++end; ++end;
+ appBinaryPath.BeginReading(start);
+ appPath.Assign(Substring(start, end));
+ } else {
+ return false;
+ }
+
+ nsCOMPtr<nsIFile> app, appBinary;
+ nsresult rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(appPath),
+ true, getter_AddRefs(app));
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+ rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(appBinaryPath),
+ true, getter_AddRefs(appBinary));
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+
+ nsCOMPtr<nsIFile> appDir;
+ nsCOMPtr<nsIProperties> dirSvc =
+ do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID);
+ if (!dirSvc) {
+ return false;
+ }
+ rv = dirSvc->Get(NS_XPCOM_CURRENT_PROCESS_DIR,
+ NS_GET_IID(nsIFile), getter_AddRefs(appDir));
+ if (NS_FAILED(rv)) {
+ return false;
+ }
+ bool exists;
+ rv = appDir->Exists(&exists);
+ if (NS_FAILED(rv) || !exists) {
+ return false;
+ }
+
+ bool isLink;
+ app->IsSymlink(&isLink);
+ if (isLink) {
+ app->GetNativeTarget(aAppPath);
+ } else {
+ app->GetNativePath(aAppPath);
+ }
+ appBinary->IsSymlink(&isLink);
+ if (isLink) {
+ appBinary->GetNativeTarget(aAppBinaryPath);
+ } else {
+ appBinary->GetNativePath(aAppBinaryPath);
+ }
+ appDir->IsSymlink(&isLink);
+ if (isLink) {
+ appDir->GetNativeTarget(aAppDir);
+ } else {
+ appDir->GetNativePath(aAppDir);
+ }
+
+ return true;
+}
+
+static bool
+StartMacOSContentSandbox()
+{
+ int sandboxLevel = Preferences::GetInt("security.sandbox.content.level");
+ if (sandboxLevel < 1) {
+ return false;
+ }
+
+ nsAutoCString appPath, appBinaryPath, appDir;
+ if (!GetAppPaths(appPath, appBinaryPath, appDir)) {
+ MOZ_CRASH("Error resolving child process path");
+ }
+
+ // During sandboxed content process startup, before reaching
+ // this point, NS_OS_TEMP_DIR is modified to refer to a sandbox-
+ // writable temporary directory
+ nsCOMPtr<nsIFile> tempDir;
+ nsresult rv = nsDirectoryService::gService->Get(NS_OS_TEMP_DIR,
+ NS_GET_IID(nsIFile), getter_AddRefs(tempDir));
+ if (NS_FAILED(rv)) {
+ MOZ_CRASH("Failed to get NS_OS_TEMP_DIR");
+ }
+
+ nsAutoCString tempDirPath;
+ tempDir->Normalize();
+ rv = tempDir->GetNativePath(tempDirPath);
+ if (NS_FAILED(rv)) {
+ MOZ_CRASH("Failed to get NS_OS_TEMP_DIR path");
+ }
+
+ nsCOMPtr<nsIFile> profileDir;
+ ContentChild::GetSingleton()->GetProfileDir(getter_AddRefs(profileDir));
+ nsCString profileDirPath;
+ if (profileDir) {
+ rv = profileDir->GetNativePath(profileDirPath);
+ if (NS_FAILED(rv) || profileDirPath.IsEmpty()) {
+ MOZ_CRASH("Failed to get profile path");
+ }
+ }
+
+ MacSandboxInfo info;
+ info.type = MacSandboxType_Content;
+ info.level = info.level = sandboxLevel;
+ info.appPath.assign(appPath.get());
+ info.appBinaryPath.assign(appBinaryPath.get());
+ info.appDir.assign(appDir.get());
+ info.appTempDir.assign(tempDirPath.get());
+
+ if (profileDir) {
+ info.hasSandboxedProfile = true;
+ info.profileDir.assign(profileDirPath.get());
+ } else {
+ info.hasSandboxedProfile = false;
+ }
+
+ std::string err;
+ if (!mozilla::StartMacSandbox(info, err)) {
+ NS_WARNING(err.c_str());
+ MOZ_CRASH("sandbox_init() failed");
+ }
+
+ return true;
+}
+#endif
+
+bool
+ContentChild::RecvSetProcessSandbox(const MaybeFileDesc& aBroker)
+{
+ // We may want to move the sandbox initialization somewhere else
+ // at some point; see bug 880808.
+#if defined(MOZ_CONTENT_SANDBOX)
+ bool sandboxEnabled = true;
+#if defined(XP_LINUX)
+#if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 19
+ // For B2G >= KitKat, sandboxing is mandatory; this has already
+ // been enforced by ContentParent::StartUp().
+ MOZ_ASSERT(SandboxInfo::Get().CanSandboxContent());
+#else
+ // Otherwise, sandboxing is best-effort.
+ if (!SandboxInfo::Get().CanSandboxContent()) {
+ sandboxEnabled = false;
+ } else {
+ // This triggers the initialization of cubeb, which needs to happen
+ // before seccomp is enabled (Bug 1259508). It also increases the startup
+ // time of the content process, because cubeb is usually initialized
+ // when it is actually needed. This call here is no longer required
+ // once Bug 1104619 (remoting audio) is resolved.
+ Unused << CubebUtils::GetCubebContext();
+ }
+
+#endif /* MOZ_WIDGET_GONK && ANDROID_VERSION >= 19 */
+ if (sandboxEnabled) {
+ int brokerFd = -1;
+ if (aBroker.type() == MaybeFileDesc::TFileDescriptor) {
+ auto fd = aBroker.get_FileDescriptor().ClonePlatformHandle();
+ brokerFd = fd.release();
+ // brokerFd < 0 means to allow direct filesystem access, so
+ // make absolutely sure that doesn't happen if the parent
+ // didn't intend it.
+ MOZ_RELEASE_ASSERT(brokerFd >= 0);
+ }
+ sandboxEnabled = SetContentProcessSandbox(brokerFd);
+ }
+#elif defined(XP_WIN)
+ mozilla::SandboxTarget::Instance()->StartSandbox();
+#elif defined(XP_MACOSX)
+ sandboxEnabled = StartMacOSContentSandbox();
+#endif
+
+#if defined(MOZ_CRASHREPORTER)
+ CrashReporter::AnnotateCrashReport(
+ NS_LITERAL_CSTRING("ContentSandboxEnabled"),
+ sandboxEnabled? NS_LITERAL_CSTRING("1") : NS_LITERAL_CSTRING("0"));
+#if defined(XP_LINUX) && !defined(OS_ANDROID)
+ nsAutoCString flagsString;
+ flagsString.AppendInt(SandboxInfo::Get().AsInteger());
+
+ CrashReporter::AnnotateCrashReport(
+ NS_LITERAL_CSTRING("ContentSandboxCapabilities"), flagsString);
+#endif /* XP_LINUX && !OS_ANDROID */
+#endif /* MOZ_CRASHREPORTER */
+#endif /* MOZ_CONTENT_SANDBOX */
+
+ return true;
+}
+
+bool
+ContentChild::RecvNotifyLayerAllocated(const dom::TabId& aTabId, const uint64_t& aLayersId)
+{
+ if (!CompositorBridgeChild::Get()->IPCOpen()) {
+ return true;
+ }
+
+ APZChild* apz = ContentProcessController::Create(aTabId);
+ return CompositorBridgeChild::Get()->SendPAPZConstructor(apz, aLayersId);
+}
+
+bool
+ContentChild::RecvSpeakerManagerNotify()
+{
+#ifdef MOZ_WIDGET_GONK
+ // Only notify the process which has the SpeakerManager instance.
+ RefPtr<SpeakerManagerService> service =
+ SpeakerManagerService::GetSpeakerManagerService();
+ if (service) {
+ service->Notify();
+ }
+ return true;
+#endif
+ return false;
+}
+
+bool
+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 true;
+}
+
+static CancelableRunnable* sFirstIdleTask;
+
+static void FirstIdle(void)
+{
+ MOZ_ASSERT(sFirstIdleTask);
+ sFirstIdleTask = nullptr;
+ ContentChild::GetSingleton()->SendFirstIdle();
+}
+
+mozilla::jsipc::PJavaScriptChild *
+ContentChild::AllocPJavaScriptChild()
+{
+ MOZ_ASSERT(ManagedPJavaScriptChild().IsEmpty());
+
+ return nsIContentChild::AllocPJavaScriptChild();
+}
+
+bool
+ContentChild::DeallocPJavaScriptChild(PJavaScriptChild *aChild)
+{
+ return nsIContentChild::DeallocPJavaScriptChild(aChild);
+}
+
+PBrowserChild*
+ContentChild::AllocPBrowserChild(const TabId& aTabId,
+ const IPCTabContext& aContext,
+ const uint32_t& aChromeFlags,
+ const ContentParentId& aCpID,
+ const bool& aIsForApp,
+ const bool& aIsForBrowser)
+{
+ return nsIContentChild::AllocPBrowserChild(aTabId,
+ aContext,
+ aChromeFlags,
+ aCpID,
+ aIsForApp,
+ aIsForBrowser);
+}
+
+bool
+ContentChild::SendPBrowserConstructor(PBrowserChild* aActor,
+ const TabId& aTabId,
+ const IPCTabContext& aContext,
+ const uint32_t& aChromeFlags,
+ const ContentParentId& aCpID,
+ const bool& aIsForApp,
+ const bool& aIsForBrowser)
+{
+ if (IsShuttingDown()) {
+ return false;
+ }
+
+ return PContentChild::SendPBrowserConstructor(aActor,
+ aTabId,
+ aContext,
+ aChromeFlags,
+ aCpID,
+ aIsForApp,
+ aIsForBrowser);
+}
+
+bool
+ContentChild::RecvPBrowserConstructor(PBrowserChild* aActor,
+ const TabId& aTabId,
+ const IPCTabContext& aContext,
+ const uint32_t& aChromeFlags,
+ const ContentParentId& aCpID,
+ const bool& aIsForApp,
+ const bool& aIsForBrowser)
+{
+ MOZ_ASSERT(!IsShuttingDown());
+
+ // This runs after AllocPBrowserChild() returns and the IPC machinery for this
+ // PBrowserChild has been set up.
+
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ if (os) {
+ nsITabChild* tc =
+ static_cast<nsITabChild*>(static_cast<TabChild*>(aActor));
+ os->NotifyObservers(tc, "tab-child-created", nullptr);
+ }
+
+ static bool hasRunOnce = false;
+ if (!hasRunOnce) {
+ hasRunOnce = true;
+
+ MOZ_ASSERT(!sFirstIdleTask);
+ RefPtr<CancelableRunnable> firstIdleTask = NewCancelableRunnableFunction(FirstIdle);
+ sFirstIdleTask = firstIdleTask;
+ MessageLoop::current()->PostIdleTask(firstIdleTask.forget());
+
+ // Redo InitProcessAttributes() when the app or browser is really
+ // launching so the attributes will be correct.
+ mID = aCpID;
+ mIsForApp = aIsForApp;
+ mIsForBrowser = aIsForBrowser;
+ InitProcessAttributes();
+ }
+
+ return true;
+}
+
+void
+ContentChild::GetAvailableDictionaries(InfallibleTArray<nsString>& aDictionaries)
+{
+ aDictionaries = mAvailableDictionaries;
+}
+
+PFileDescriptorSetChild*
+ContentChild::SendPFileDescriptorSetConstructor(const FileDescriptor& aFD)
+{
+ if (IsShuttingDown()) {
+ return nullptr;
+ }
+
+ return PContentChild::SendPFileDescriptorSetConstructor(aFD);
+}
+
+PFileDescriptorSetChild*
+ContentChild::AllocPFileDescriptorSetChild(const FileDescriptor& aFD)
+{
+ return nsIContentChild::AllocPFileDescriptorSetChild(aFD);
+}
+
+bool
+ContentChild::DeallocPFileDescriptorSetChild(PFileDescriptorSetChild* aActor)
+{
+ return nsIContentChild::DeallocPFileDescriptorSetChild(aActor);
+}
+
+bool
+ContentChild::DeallocPBrowserChild(PBrowserChild* aIframe)
+{
+ return nsIContentChild::DeallocPBrowserChild(aIframe);
+}
+
+PBlobChild*
+ContentChild::AllocPBlobChild(const BlobConstructorParams& aParams)
+{
+ return nsIContentChild::AllocPBlobChild(aParams);
+}
+
+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;
+}
+
+bool
+ContentChild::DeallocPBlobChild(PBlobChild* aActor)
+{
+ return nsIContentChild::DeallocPBlobChild(aActor);
+}
+
+PBlobChild*
+ContentChild::SendPBlobConstructor(PBlobChild* aActor,
+ const BlobConstructorParams& aParams)
+{
+ if (IsShuttingDown()) {
+ return nullptr;
+ }
+
+ return PContentChild::SendPBlobConstructor(aActor, aParams);
+}
+
+PPresentationChild*
+ContentChild::AllocPPresentationChild()
+{
+ MOZ_CRASH("We should never be manually allocating PPresentationChild actors");
+ return nullptr;
+}
+
+bool
+ContentChild::DeallocPPresentationChild(PPresentationChild* aActor)
+{
+ delete aActor;
+ return true;
+}
+
+PFlyWebPublishedServerChild*
+ContentChild::AllocPFlyWebPublishedServerChild(const nsString& name,
+ const FlyWebPublishOptions& params)
+{
+ MOZ_CRASH("We should never be manually allocating PFlyWebPublishedServerChild actors");
+ return nullptr;
+}
+
+bool
+ContentChild::DeallocPFlyWebPublishedServerChild(PFlyWebPublishedServerChild* aActor)
+{
+ RefPtr<FlyWebPublishedServerChild> actor =
+ dont_AddRef(static_cast<FlyWebPublishedServerChild*>(aActor));
+ return true;
+}
+
+bool
+ContentChild::RecvNotifyPresentationReceiverLaunched(PBrowserChild* aIframe,
+ const nsString& aSessionId)
+{
+ nsCOMPtr<nsIDocShell> docShell =
+ do_GetInterface(static_cast<TabChild*>(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 true;
+}
+
+bool
+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 true;
+}
+
+bool
+ContentChild::RecvNotifyEmptyHTTPCache()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ obs->NotifyObservers(nullptr, "cacheservice:empty-cache", nullptr);
+ return true;
+}
+
+PCrashReporterChild*
+ContentChild::AllocPCrashReporterChild(const mozilla::dom::NativeThreadId& id,
+ const uint32_t& processType)
+{
+#ifdef MOZ_CRASHREPORTER
+ return new CrashReporterChild();
+#else
+ return nullptr;
+#endif
+}
+
+bool
+ContentChild::DeallocPCrashReporterChild(PCrashReporterChild* crashreporter)
+{
+ delete crashreporter;
+ return true;
+}
+
+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;
+}
+
+jsipc::CPOWManager*
+ContentChild::GetCPOWManager()
+{
+ if (PJavaScriptChild* c = LoneManagedOrNullAsserts(ManagedPJavaScriptChild())) {
+ return CPOWManagerFor(c);
+ }
+ return CPOWManagerFor(SendPJavaScriptConstructor());
+}
+
+bool
+ContentChild::RecvPTestShellConstructor(PTestShellChild* actor)
+{
+ return true;
+}
+
+PNeckoChild*
+ContentChild::AllocPNeckoChild()
+{
+ return new NeckoChild();
+}
+
+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;
+}
+
+PSendStreamChild*
+ContentChild::SendPSendStreamConstructor(PSendStreamChild* aActor)
+{
+ if (IsShuttingDown()) {
+ return nullptr;
+ }
+
+ return PContentChild::SendPSendStreamConstructor(aActor);
+}
+
+PSendStreamChild*
+ContentChild::AllocPSendStreamChild()
+{
+ return nsIContentChild::AllocPSendStreamChild();
+}
+
+bool
+ContentChild::DeallocPSendStreamChild(PSendStreamChild* aActor)
+{
+ return nsIContentChild::DeallocPSendStreamChild(aActor);
+}
+
+PScreenManagerChild*
+ContentChild::AllocPScreenManagerChild(uint32_t* aNumberOfScreens,
+ float* aSystemDefaultScale,
+ bool* aSuccess)
+{
+ // The ContentParent should never attempt to allocate the
+ // nsScreenManagerProxy. Instead, the nsScreenManagerProxy
+ // service is requested and instantiated via XPCOM, and the
+ // constructor of nsScreenManagerProxy sets up the IPC connection.
+ MOZ_CRASH("Should never get here!");
+ return nullptr;
+}
+
+bool
+ContentChild::DeallocPScreenManagerChild(PScreenManagerChild* aService)
+{
+ // nsScreenManagerProxy is AddRef'd in its constructor.
+ nsScreenManagerProxy *child = static_cast<nsScreenManagerProxy*>(aService);
+ child->Release();
+ return true;
+}
+
+PPSMContentDownloaderChild*
+ContentChild::AllocPPSMContentDownloaderChild(const uint32_t& aCertType)
+{
+ // NB: We don't need aCertType in the child actor.
+ RefPtr<PSMContentDownloaderChild> child = new PSMContentDownloaderChild();
+ return child.forget().take();
+}
+
+bool
+ContentChild::DeallocPPSMContentDownloaderChild(PPSMContentDownloaderChild* aListener)
+{
+ auto* listener = static_cast<PSMContentDownloaderChild*>(aListener);
+ RefPtr<PSMContentDownloaderChild> child = dont_AddRef(listener);
+ return true;
+}
+
+PExternalHelperAppChild*
+ContentChild::AllocPExternalHelperAppChild(const OptionalURIParams& uri,
+ const nsCString& aMimeContentType,
+ const nsCString& aContentDisposition,
+ const uint32_t& aContentDispositionHint,
+ const nsString& aContentDispositionFilename,
+ const bool& aForceSave,
+ const int64_t& aContentLength,
+ const bool& aWasFileChannel,
+ const OptionalURIParams& aReferrer,
+ PBrowserChild* aBrowser)
+{
+ ExternalHelperAppChild *child = new ExternalHelperAppChild();
+ child->AddRef();
+ return child;
+}
+
+bool
+ContentChild::DeallocPExternalHelperAppChild(PExternalHelperAppChild* aService)
+{
+ ExternalHelperAppChild *child = static_cast<ExternalHelperAppChild*>(aService);
+ child->Release();
+ return true;
+}
+
+PHandlerServiceChild*
+ContentChild::AllocPHandlerServiceChild()
+{
+ HandlerServiceChild* actor = new HandlerServiceChild();
+ actor->AddRef();
+ return actor;
+}
+
+bool ContentChild::DeallocPHandlerServiceChild(PHandlerServiceChild* aHandlerServiceChild)
+{
+ static_cast<HandlerServiceChild*>(aHandlerServiceChild)->Release();
+ return true;
+}
+
+media::PMediaChild*
+ContentChild::AllocPMediaChild()
+{
+ return media::AllocPMediaChild();
+}
+
+bool
+ContentChild::DeallocPMediaChild(media::PMediaChild *aActor)
+{
+ return media::DeallocPMediaChild(aActor);
+}
+
+PStorageChild*
+ContentChild::AllocPStorageChild()
+{
+ MOZ_CRASH("We should never be manually allocating PStorageChild actors");
+ return nullptr;
+}
+
+bool
+ContentChild::DeallocPStorageChild(PStorageChild* aActor)
+{
+ DOMStorageDBChild* child = static_cast<DOMStorageDBChild*>(aActor);
+ child->ReleaseIPDLReference();
+ return true;
+}
+
+PSpeechSynthesisChild*
+ContentChild::AllocPSpeechSynthesisChild()
+{
+#ifdef MOZ_WEBSPEECH
+ MOZ_CRASH("No one should be allocating PSpeechSynthesisChild actors");
+#else
+ return nullptr;
+#endif
+}
+
+bool
+ContentChild::DeallocPSpeechSynthesisChild(PSpeechSynthesisChild* aActor)
+{
+#ifdef MOZ_WEBSPEECH
+ delete aActor;
+ return true;
+#else
+ return false;
+#endif
+}
+
+PWebrtcGlobalChild *
+ContentChild::AllocPWebrtcGlobalChild()
+{
+#ifdef MOZ_WEBRTC
+ WebrtcGlobalChild *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
+}
+
+
+bool
+ContentChild::RecvRegisterChrome(InfallibleTArray<ChromePackage>&& packages,
+ InfallibleTArray<SubstitutionMapping>&& resources,
+ InfallibleTArray<OverrideMapping>&& overrides,
+ const nsCString& locale,
+ const bool& reset)
+{
+ nsCOMPtr<nsIChromeRegistry> registrySvc = nsChromeRegistry::GetService();
+ nsChromeRegistryContent* chromeRegistry =
+ static_cast<nsChromeRegistryContent*>(registrySvc.get());
+ chromeRegistry->RegisterRemoteChrome(packages, resources, overrides,
+ locale, reset);
+ return true;
+}
+
+bool
+ContentChild::RecvRegisterChromeItem(const ChromeRegistryItem& item)
+{
+ nsCOMPtr<nsIChromeRegistry> registrySvc = nsChromeRegistry::GetService();
+ nsChromeRegistryContent* chromeRegistry =
+ static_cast<nsChromeRegistryContent*>(registrySvc.get());
+ 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 false;
+ }
+
+ return true;
+}
+
+bool
+ContentChild::RecvClearImageCache(const bool& privateLoader, const bool& chrome)
+{
+ imgLoader* loader = privateLoader ? imgLoader::PrivateBrowsingLoader() :
+ imgLoader::NormalLoader();
+
+ loader->ClearCache(chrome);
+ return true;
+}
+
+bool
+ContentChild::RecvSetOffline(const bool& offline)
+{
+ nsCOMPtr<nsIIOService> io (do_GetIOService());
+ NS_ASSERTION(io, "IO Service can not be null");
+
+ io->SetOffline(offline);
+
+ return true;
+}
+
+bool
+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 true;
+}
+
+bool
+ContentChild::RecvSetCaptivePortalState(const int32_t& aState)
+{
+ nsCOMPtr<nsICaptivePortalService> cps = do_GetService(NS_CAPTIVEPORTAL_CID);
+ if (!cps) {
+ return true;
+ }
+
+ mozilla::net::CaptivePortalService *portal =
+ static_cast<mozilla::net::CaptivePortalService*>(cps.get());
+ portal->SetStateInChild(aState);
+
+ return true;
+}
+
+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
+ if (sFirstIdleTask) {
+ sFirstIdleTask->Cancel();
+ }
+
+ nsHostObjectProtocolHandler::RemoveDataEntries();
+
+ mAlertObservers.Clear();
+
+ mIdleObservers.Clear();
+
+ nsCOMPtr<nsIConsoleService> svc(do_GetService(NS_CONSOLESERVICE_CONTRACTID));
+ if (svc) {
+ svc->UnregisterListener(mConsoleListener);
+ mConsoleListener->mChild = nullptr;
+ }
+ mIsAlive = false;
+
+ 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:
+ NS_RUNTIMEABORT("not reached");
+ }
+
+#if defined(MOZ_CRASHREPORTER) && !defined(MOZ_B2G)
+ if (PCrashReporterChild* c = LoneManagedOrNullAsserts(ManagedPCrashReporterChild())) {
+ CrashReporterChild* crashReporter =
+ static_cast<CrashReporterChild*>(c);
+ nsDependentCString reason(aReason);
+ crashReporter->SendAnnotateCrashReport(
+ NS_LITERAL_CSTRING("ipc_channel_error"),
+ reason);
+ }
+#endif
+ NS_RUNTIMEABORT("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;
+}
+
+bool
+ContentChild::RecvPreferenceUpdate(const PrefSetting& aPref)
+{
+ Preferences::SetPreference(aPref);
+ return true;
+}
+
+bool
+ContentChild::RecvVarUpdate(const GfxVarUpdate& aVar)
+{
+ gfx::gfxVars::ApplyUpdate(aVar);
+ return true;
+}
+
+bool
+ContentChild::RecvDataStoragePut(const nsString& aFilename,
+ const DataStorageItem& aItem)
+{
+ RefPtr<DataStorage> storage = DataStorage::GetIfExists(aFilename);
+ if (storage) {
+ storage->Put(aItem.key(), aItem.value(), aItem.type());
+ }
+ return true;
+}
+
+bool
+ContentChild::RecvDataStorageRemove(const nsString& aFilename,
+ const nsCString& aKey,
+ const DataStorageType& aType)
+{
+ RefPtr<DataStorage> storage = DataStorage::GetIfExists(aFilename);
+ if (storage) {
+ storage->Remove(aKey, aType);
+ }
+ return true;
+}
+
+bool
+ContentChild::RecvDataStorageClear(const nsString& aFilename)
+{
+ RefPtr<DataStorage> storage = DataStorage::GetIfExists(aFilename);
+ if (storage) {
+ storage->Clear();
+ }
+ return true;
+}
+
+bool
+ContentChild::RecvNotifyAlertsObserver(const nsCString& aType, const nsString& aData)
+{
+ for (uint32_t i = 0; i < mAlertObservers.Length();
+ /*we mutate the array during the loop; ++i iff no mutation*/) {
+ AlertObserver* observer = mAlertObservers[i];
+ if (observer->Observes(aData) && observer->Notify(aType)) {
+ // if aType == alertfinished, this alert is done. we can
+ // remove the observer.
+ if (aType.Equals(nsDependentCString("alertfinished"))) {
+ mAlertObservers.RemoveElementAt(i);
+ continue;
+ }
+ }
+ ++i;
+ }
+ return true;
+}
+
+bool
+ContentChild::RecvNotifyVisited(const URIParams& aURI)
+{
+ nsCOMPtr<nsIURI> newURI = DeserializeURI(aURI);
+ if (!newURI) {
+ return false;
+ }
+ nsCOMPtr<IHistory> history = services::GetHistoryService();
+ if (history) {
+ history->NotifyVisited(newURI);
+ }
+ return true;
+}
+
+bool
+ContentChild::RecvLoadProcessScript(const nsString& aURL)
+{
+ ProcessGlobal* global = ProcessGlobal::Get();
+ global->LoadScript(aURL);
+ return true;
+}
+
+bool
+ContentChild::RecvAsyncMessage(const nsString& aMsg,
+ InfallibleTArray<CpowEntry>&& aCpows,
+ const IPC::Principal& aPrincipal,
+ const ClonedMessageData& aData)
+{
+ RefPtr<nsFrameMessageManager> cpm =
+ nsFrameMessageManager::GetChildProcessManager();
+ if (cpm) {
+ StructuredCloneData data;
+ ipc::UnpackClonedMessageDataForChild(aData, data);
+ CrossProcessCpowHolder cpows(this, aCpows);
+ cpm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(cpm.get()),
+ nullptr, aMsg, false, &data, &cpows, aPrincipal,
+ nullptr);
+ }
+ return true;
+}
+
+bool
+ContentChild::RecvGeolocationUpdate(const GeoPosition& somewhere)
+{
+ nsCOMPtr<nsIGeolocationUpdate> gs =
+ do_GetService("@mozilla.org/geolocation/service;1");
+ if (!gs) {
+ return true;
+ }
+ nsCOMPtr<nsIDOMGeoPosition> position = somewhere;
+ gs->Update(position);
+ return true;
+}
+
+bool
+ContentChild::RecvGeolocationError(const uint16_t& errorCode)
+{
+ nsCOMPtr<nsIGeolocationUpdate> gs =
+ do_GetService("@mozilla.org/geolocation/service;1");
+ if (!gs) {
+ return true;
+ }
+ gs->NotifyError(errorCode);
+ return true;
+}
+
+bool
+ContentChild::RecvUpdateDictionaryList(InfallibleTArray<nsString>&& aDictionaries)
+{
+ mAvailableDictionaries = aDictionaries;
+ mozInlineSpellChecker::UpdateCanEnableInlineSpellChecking();
+ return true;
+}
+
+bool
+ContentChild::RecvAddPermission(const IPC::Permission& permission)
+{
+#if MOZ_PERMISSIONS
+ nsCOMPtr<nsIPermissionManager> permissionManagerIface =
+ services::GetPermissionManager();
+ nsPermissionManager* permissionManager =
+ static_cast<nsPermissionManager*>(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;
+ PrincipalOriginAttributes attrs;
+ bool success = attrs.PopulateFromOrigin(permission.origin, originNoSuffix);
+ NS_ENSURE_TRUE(success, false);
+
+ nsCOMPtr<nsIURI> uri;
+ nsresult rv = NS_NewURI(getter_AddRefs(uri), originNoSuffix);
+ NS_ENSURE_SUCCESS(rv, true);
+
+ nsCOMPtr<nsIPrincipal> principal = mozilla::BasePrincipal::CreateCodebasePrincipal(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,
+ nsPermissionManager::eNotify,
+ nsPermissionManager::eNoDBOperation);
+#endif
+
+ return true;
+}
+
+bool
+ContentChild::RecvFlushMemory(const nsString& reason)
+{
+ nsCOMPtr<nsIObserverService> os =
+ mozilla::services::GetObserverService();
+ if (os) {
+ os->NotifyObservers(nullptr, "memory-pressure", reason.get());
+ }
+ return true;
+}
+
+bool
+ContentChild::RecvActivateA11y(const uint32_t& aMsaaID)
+{
+#ifdef ACCESSIBILITY
+#ifdef XP_WIN
+ 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 true;
+}
+
+bool
+ContentChild::RecvShutdownA11y()
+{
+#ifdef ACCESSIBILITY
+ // Try to shutdown accessibility in content process if it's shutting down in
+ // chrome process.
+ MaybeShutdownAccService(nsAccessibilityService::eMainProcess);
+#endif
+ return true;
+}
+
+bool
+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 true;
+}
+
+bool
+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 true;
+}
+
+static void
+PreloadSlowThings()
+{
+ // This fetches and creates all the built-in stylesheets.
+ //
+ // XXXheycam In the future we might want to preload the Servo-flavoured
+ // UA sheets too, but for now that will be a waste of time.
+ nsLayoutStylesheetCache::For(StyleBackendType::Gecko)->UserContentSheet();
+
+ TabChild::PreloadSlowThings();
+
+}
+
+bool
+ContentChild::RecvAppInfo(const nsCString& version, const nsCString& buildID,
+ const nsCString& name, const nsCString& UAName,
+ const nsCString& ID, const nsCString& vendor)
+{
+ mAppInfo.version.Assign(version);
+ mAppInfo.buildID.Assign(buildID);
+ mAppInfo.name.Assign(name);
+ mAppInfo.UAName.Assign(UAName);
+ mAppInfo.ID.Assign(ID);
+ mAppInfo.vendor.Assign(vendor);
+
+ return true;
+}
+
+bool
+ContentChild::RecvAppInit()
+{
+ if (!Preferences::GetBool("dom.ipc.processPrelaunch.enabled", false)) {
+ return true;
+ }
+
+ // If we're part of the mozbrowser machinery, go ahead and start
+ // preloading things. We can only do this for mozbrowser because
+ // PreloadSlowThings() may set the docshell of the first TabChild
+ // inactive, and we can only safely restore it to active from
+ // BrowserElementChild.js.
+ if (mIsForApp || mIsForBrowser) {
+ PreloadSlowThings();
+ }
+
+ return true;
+}
+
+bool
+ContentChild::RecvInitServiceWorkers(const ServiceWorkerConfiguration& aConfig)
+{
+ RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
+ if (!swm) {
+ // browser shutdown began
+ return true;
+ }
+ swm->LoadRegistrations(aConfig.serviceWorkerRegistrations());
+ return true;
+}
+
+bool
+ContentChild::RecvInitBlobURLs(nsTArray<BlobURLRegistrationData>&& aRegistrations)
+{
+ for (uint32_t i = 0; i < aRegistrations.Length(); ++i) {
+ BlobURLRegistrationData& registration = aRegistrations[i];
+ RefPtr<BlobImpl> blobImpl =
+ static_cast<BlobChild*>(registration.blobChild())->GetBlobImpl();
+ MOZ_ASSERT(blobImpl);
+
+ nsHostObjectProtocolHandler::AddDataEntry(registration.url(),
+ registration.principal(),
+ blobImpl);
+ }
+
+ return true;
+}
+
+bool
+ContentChild::RecvLastPrivateDocShellDestroyed()
+{
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ obs->NotifyObservers(nullptr, "last-pb-context-exited", nullptr);
+ return true;
+}
+
+bool
+ContentChild::RecvVolumes(nsTArray<VolumeInfo>&& aVolumes)
+{
+#ifdef MOZ_WIDGET_GONK
+ RefPtr<nsVolumeService> vs = nsVolumeService::GetSingleton();
+ if (vs) {
+ vs->RecvVolumesFromParent(aVolumes);
+ }
+#endif
+ return true;
+}
+
+bool
+ContentChild::RecvFileSystemUpdate(const nsString& aFsName,
+ const nsString& aVolumeName,
+ const int32_t& aState,
+ const int32_t& aMountGeneration,
+ const bool& aIsMediaPresent,
+ const bool& aIsSharing,
+ const bool& aIsFormatting,
+ const bool& aIsFake,
+ const bool& aIsUnmounting,
+ const bool& aIsRemovable,
+ const bool& aIsHotSwappable)
+{
+#ifdef MOZ_WIDGET_GONK
+ RefPtr<nsVolume> volume = new nsVolume(aFsName, aVolumeName, aState,
+ aMountGeneration, aIsMediaPresent,
+ aIsSharing, aIsFormatting, aIsFake,
+ aIsUnmounting, aIsRemovable, aIsHotSwappable);
+
+ RefPtr<nsVolumeService> vs = nsVolumeService::GetSingleton();
+ if (vs) {
+ vs->UpdateVolume(volume);
+ }
+#else
+ // Remove warnings about unused arguments
+ Unused << aFsName;
+ Unused << aVolumeName;
+ Unused << aState;
+ Unused << aMountGeneration;
+ Unused << aIsMediaPresent;
+ Unused << aIsSharing;
+ Unused << aIsFormatting;
+ Unused << aIsFake;
+ Unused << aIsUnmounting;
+ Unused << aIsRemovable;
+ Unused << aIsHotSwappable;
+#endif
+ return true;
+}
+
+bool
+ContentChild::RecvVolumeRemoved(const nsString& aFsName)
+{
+#ifdef MOZ_WIDGET_GONK
+ RefPtr<nsVolumeService> vs = nsVolumeService::GetSingleton();
+ if (vs) {
+ vs->RemoveVolumeByName(aFsName);
+ }
+#else
+ // Remove warnings about unused arguments
+ Unused << aFsName;
+#endif
+ return true;
+}
+
+bool
+ContentChild::RecvNotifyProcessPriorityChanged(
+ const hal::ProcessPriority& aPriority)
+{
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ NS_ENSURE_TRUE(os, true);
+
+ RefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();
+ props->SetPropertyAsInt32(NS_LITERAL_STRING("priority"),
+ static_cast<int32_t>(aPriority));
+
+ os->NotifyObservers(static_cast<nsIPropertyBag2*>(props),
+ "ipc:process-priority-changed", nullptr);
+ return true;
+}
+
+bool
+ContentChild::RecvMinimizeMemoryUsage()
+{
+ nsCOMPtr<nsIMemoryReporterManager> mgr =
+ do_GetService("@mozilla.org/memory-reporter-manager;1");
+ NS_ENSURE_TRUE(mgr, true);
+
+ Unused << mgr->MinimizeMemoryUsage(/* callback = */ nullptr);
+ return true;
+}
+
+bool
+ContentChild::RecvNotifyPhoneStateChange(const nsString& aState)
+{
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ if (os) {
+ os->NotifyObservers(nullptr, "phone-state-changed", aState.get());
+ }
+ return true;
+}
+
+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);
+}
+
+bool
+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 true;
+}
+
+bool
+ContentChild::RecvLoadAndRegisterSheet(const URIParams& aURI, const uint32_t& aType)
+{
+ nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
+ if (!uri) {
+ return true;
+ }
+
+ nsStyleSheetService *sheetService = nsStyleSheetService::GetInstance();
+ if (sheetService) {
+ sheetService->LoadAndRegisterSheet(uri, aType);
+ }
+
+ return true;
+}
+
+bool
+ContentChild::RecvUnregisterSheet(const URIParams& aURI, const uint32_t& aType)
+{
+ nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
+ if (!uri) {
+ return true;
+ }
+
+ nsStyleSheetService *sheetService = nsStyleSheetService::GetInstance();
+ if (sheetService) {
+ sheetService->UnregisterSheet(uri, aType);
+ }
+
+ return true;
+}
+
+POfflineCacheUpdateChild*
+ContentChild::AllocPOfflineCacheUpdateChild(const URIParams& manifestURI,
+ const URIParams& documentURI,
+ const PrincipalInfo& aLoadingPrincipalInfo,
+ const bool& stickDocument)
+{
+ NS_RUNTIMEABORT("unused");
+ return nullptr;
+}
+
+bool
+ContentChild::DeallocPOfflineCacheUpdateChild(POfflineCacheUpdateChild* actor)
+{
+ OfflineCacheUpdateChild* offlineCacheUpdate =
+ static_cast<OfflineCacheUpdateChild*>(actor);
+ NS_RELEASE(offlineCacheUpdate);
+ return true;
+}
+
+bool
+ContentChild::RecvStartProfiler(const ProfilerInitParams& params)
+{
+ nsTArray<const char*> featureArray;
+ for (size_t i = 0; i < params.features().Length(); ++i) {
+ featureArray.AppendElement(params.features()[i].get());
+ }
+
+ nsTArray<const char*> threadNameFilterArray;
+ for (size_t i = 0; i < params.threadFilters().Length(); ++i) {
+ threadNameFilterArray.AppendElement(params.threadFilters()[i].get());
+ }
+
+ profiler_start(params.entries(), params.interval(),
+ featureArray.Elements(), featureArray.Length(),
+ threadNameFilterArray.Elements(),
+ threadNameFilterArray.Length());
+
+ return true;
+}
+
+bool
+ContentChild::RecvStopProfiler()
+{
+ profiler_stop();
+ return true;
+}
+
+bool
+ContentChild::RecvPauseProfiler(const bool& aPause)
+{
+ if (aPause) {
+ profiler_pause();
+ } else {
+ profiler_resume();
+ }
+
+ return true;
+}
+
+bool
+ContentChild::RecvGatherProfile()
+{
+ nsCString profileCString;
+ UniquePtr<char[]> profile = profiler_get_profile();
+ if (profile) {
+ profileCString = nsCString(profile.get(), strlen(profile.get()));
+ } else {
+ profileCString = EmptyCString();
+ }
+
+ Unused << SendProfile(profileCString);
+ return true;
+}
+
+bool
+ContentChild::RecvLoadPluginResult(const uint32_t& aPluginId,
+ const bool& aResult)
+{
+ nsresult rv;
+ bool finalResult = aResult && SendConnectPluginBridge(aPluginId, &rv) &&
+ NS_SUCCEEDED(rv);
+ plugins::PluginModuleContentParent::OnLoadPluginResult(aPluginId,
+ finalResult);
+ return true;
+}
+
+bool
+ContentChild::RecvAssociatePluginId(const uint32_t& aPluginId,
+ const base::ProcessId& aProcessId)
+{
+ plugins::PluginModuleContentParent::AssociatePluginId(aPluginId, aProcessId);
+ return true;
+}
+
+bool
+ContentChild::RecvDomainSetChanged(const uint32_t& aSetType,
+ const uint32_t& aChangeType,
+ const OptionalURIParams& aDomain)
+{
+ if (aChangeType == ACTIVATE_POLICY) {
+ if (mPolicy) {
+ return true;
+ }
+ nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
+ MOZ_ASSERT(ssm);
+ ssm->ActivateDomainPolicyInternal(getter_AddRefs(mPolicy));
+ return !!mPolicy;
+ } else if (!mPolicy) {
+ MOZ_ASSERT_UNREACHABLE("If the domain policy is not active yet,"
+ " the first message should be ACTIVATE_POLICY");
+ return false;
+ }
+
+ NS_ENSURE_TRUE(mPolicy, false);
+
+ if (aChangeType == DEACTIVATE_POLICY) {
+ mPolicy->Deactivate();
+ mPolicy = nullptr;
+ return true;
+ }
+
+ nsCOMPtr<nsIDomainSet> set;
+ switch(aSetType) {
+ case BLACKLIST:
+ mPolicy->GetBlacklist(getter_AddRefs(set));
+ break;
+ case SUPER_BLACKLIST:
+ mPolicy->GetSuperBlacklist(getter_AddRefs(set));
+ break;
+ case WHITELIST:
+ mPolicy->GetWhitelist(getter_AddRefs(set));
+ break;
+ case SUPER_WHITELIST:
+ mPolicy->GetSuperWhitelist(getter_AddRefs(set));
+ break;
+ default:
+ NS_NOTREACHED("Unexpected setType");
+ return false;
+ }
+
+ MOZ_ASSERT(set);
+
+ nsCOMPtr<nsIURI> uri = DeserializeURI(aDomain);
+
+ switch(aChangeType) {
+ case ADD_DOMAIN:
+ NS_ENSURE_TRUE(uri, false);
+ set->Add(uri);
+ break;
+ case REMOVE_DOMAIN:
+ NS_ENSURE_TRUE(uri, false);
+ set->Remove(uri);
+ break;
+ case CLEAR_DOMAINS:
+ set->Clear();
+ break;
+ default:
+ NS_NOTREACHED("Unexpected changeType");
+ return false;
+ }
+
+ return true;
+}
+
+void
+ContentChild::StartForceKillTimer()
+{
+ if (mForceKillTimer) {
+ return;
+ }
+
+ int32_t timeoutSecs = Preferences::GetInt("dom.ipc.tabs.shutdownTimeoutSecs", 5);
+ if (timeoutSecs > 0) {
+ mForceKillTimer = do_CreateInstance("@mozilla.org/timer;1");
+ MOZ_ASSERT(mForceKillTimer);
+ mForceKillTimer->InitWithFuncCallback(ContentChild::ForceKillTimerCallback,
+ this,
+ timeoutSecs * 1000,
+ nsITimer::TYPE_ONE_SHOT);
+ }
+}
+
+/* static */ void
+ContentChild::ForceKillTimerCallback(nsITimer* aTimer, void* aClosure)
+{
+ ProcessChild::QuickExit();
+}
+
+bool
+ContentChild::RecvShutdown()
+{
+ // 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).
+#if defined(MOZ_CRASHREPORTER)
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("IPCShutdownState"),
+ NS_LITERAL_CSTRING("RecvShutdown"));
+#endif
+ nsCOMPtr<nsIThread> thread;
+ nsresult rv = NS_GetMainThread(getter_AddRefs(thread));
+ if (NS_SUCCEEDED(rv) && thread) {
+ RefPtr<nsThread> mainThread(thread.forget().downcast<nsThread>());
+ if (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.
+ MessageLoop::current()->PostDelayedTask(
+ NewRunnableMethod(this, &ContentChild::RecvShutdown), 100);
+ return true;
+ }
+ }
+
+ mShuttingDown = true;
+
+ if (mPolicy) {
+ mPolicy->Deactivate();
+ mPolicy = nullptr;
+ }
+
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ if (os) {
+ os->NotifyObservers(static_cast<nsIContentChild*>(this),
+ "content-child-shutdown", nullptr);
+ }
+
+#if defined(XP_WIN)
+ mozilla::widget::StopAudioSession();
+#endif
+
+ GetIPCChannel()->SetAbortOnError(false);
+
+#ifdef MOZ_ENABLE_PROFILER_SPS
+ if (profiler_is_active()) {
+ // We're shutting down while we were profiling. Send the
+ // profile up to the parent so that we don't lose this
+ // information.
+ Unused << RecvGatherProfile();
+ }
+#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();
+
+#if defined(MOZ_CRASHREPORTER)
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("IPCShutdownState"),
+ NS_LITERAL_CSTRING("SendFinishShutdown"));
+#endif
+ // Ignore errors here. If this fails, the parent will kill us after a
+ // timeout.
+ Unused << SendFinishShutdown();
+ return true;
+}
+
+PBrowserOrId
+ContentChild::GetBrowserOrId(TabChild* aTabChild)
+{
+ if (!aTabChild ||
+ this == aTabChild->Manager()) {
+ return PBrowserOrId(aTabChild);
+ }
+ else {
+ return PBrowserOrId(aTabChild->GetTabId());
+ }
+}
+
+bool
+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.
+ parentInstance->CallUpdateWindow();
+ }
+ return true;
+#else
+ MOZ_ASSERT(false, "ContentChild::RecvUpdateWindow calls unexpected on this platform.");
+ return false;
+#endif
+}
+
+PContentPermissionRequestChild*
+ContentChild::AllocPContentPermissionRequestChild(const InfallibleTArray<PermissionRequest>& aRequests,
+ const IPC::Principal& aPrincipal,
+ const TabId& aTabId)
+{
+ NS_RUNTIMEABORT("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 uint64_t& aOuterWindowID)
+{
+ return new WebBrowserPersistDocumentChild();
+}
+
+bool
+ContentChild::RecvPWebBrowserPersistDocumentConstructor(PWebBrowserPersistDocumentChild *aActor,
+ PBrowserChild* aBrowser,
+ const uint64_t& aOuterWindowID)
+{
+ if (NS_WARN_IF(!aBrowser)) {
+ return false;
+ }
+ nsCOMPtr<nsIDocument> rootDoc =
+ static_cast<TabChild*>(aBrowser)->GetDocument();
+ nsCOMPtr<nsIDocument> foundDoc;
+ if (aOuterWindowID) {
+ foundDoc = nsContentUtils::GetSubdocumentWithOuterWindowId(rootDoc, aOuterWindowID);
+ } else {
+ foundDoc = rootDoc;
+ }
+
+ if (!foundDoc) {
+ aActor->SendInitFailure(NS_ERROR_NO_CONTENT);
+ } else {
+ static_cast<WebBrowserPersistDocumentChild*>(aActor)->Start(foundDoc);
+ }
+ return true;
+}
+
+bool
+ContentChild::DeallocPWebBrowserPersistDocumentChild(PWebBrowserPersistDocumentChild* aActor)
+{
+ delete aActor;
+ return true;
+}
+
+bool
+ContentChild::RecvSetAudioSessionData(const nsID& aId,
+ const nsString& aDisplayName,
+ const nsString& aIconPath)
+{
+#if defined(XP_WIN)
+ if (NS_FAILED(mozilla::widget::RecvAudioSessionData(aId, aDisplayName,
+ aIconPath))) {
+ return true;
+ }
+
+ // Ignore failures here; we can't really do anything about them
+ mozilla::widget::StartAudioSession();
+ return true;
+#else
+ NS_RUNTIMEABORT("Not Reached!");
+ return false;
+#endif
+}
+
+// This code goes here rather than nsGlobalWindow.cpp because nsGlobalWindow.cpp
+// can't include ContentChild.h since it includes windows.h.
+
+static uint64_t gNextWindowID = 0;
+
+// We use only 53 bits for the window ID so that it can be converted to and from
+// a JS value without loss of precision. The upper bits of the window ID hold the
+// process ID. The lower bits identify the window.
+static const uint64_t kWindowIDTotalBits = 53;
+static const uint64_t kWindowIDProcessBits = 22;
+static const uint64_t kWindowIDWindowBits = kWindowIDTotalBits - kWindowIDProcessBits;
+
+// Try to return a window ID that is unique across processes and that will never
+// be recycled.
+uint64_t
+NextWindowID()
+{
+ uint64_t processID = 0;
+ if (XRE_IsContentProcess()) {
+ ContentChild* cc = ContentChild::GetSingleton();
+ processID = cc->GetID();
+ }
+
+ MOZ_RELEASE_ASSERT(processID < (uint64_t(1) << kWindowIDProcessBits));
+ uint64_t processBits = processID & ((uint64_t(1) << kWindowIDProcessBits) - 1);
+
+ // Make sure no actual window ends up with mWindowID == 0.
+ uint64_t windowID = ++gNextWindowID;
+
+ MOZ_RELEASE_ASSERT(windowID < (uint64_t(1) << kWindowIDWindowBits));
+ uint64_t windowBits = windowID & ((uint64_t(1) << kWindowIDWindowBits) - 1);
+
+ return (processBits << kWindowIDWindowBits) | windowBits;
+}
+
+bool
+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::TPBlobChild) {
+ 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(nsDependentCString(data.get<char>(), data.Size<char>()));
+ Unused << DeallocShmem(data);
+ } else if (item.data().type() == IPCDataTransferData::TPBlobChild) {
+ BlobChild* blob = static_cast<BlobChild*>(item.data().get_PBlobChild());
+ RefPtr<BlobImpl> blobImpl = blob->GetBlobImpl();
+ 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::TPBlobChild;
+ dataTransfer->SetDataWithPrincipalFromOtherProcess(
+ NS_ConvertUTF8toUTF16(item.flavor()), variant, i,
+ nsContentUtils::GetSystemPrincipal(), hidden);
+ }
+ }
+ session->SetDataTransfer(dataTransfer);
+ }
+ }
+ return true;
+}
+
+bool
+ContentChild::RecvEndDragSession(const bool& aDoneDrag,
+ const bool& aUserCancelled,
+ const LayoutDeviceIntPoint& aDragEndPoint)
+{
+ 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);
+ }
+ return true;
+}
+
+bool
+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 true;
+}
+
+bool
+ContentChild::RecvPushWithData(const nsCString& aScope,
+ const IPC::Principal& aPrincipal,
+ const nsString& aMessageId,
+ InfallibleTArray<uint8_t>&& aData)
+{
+ PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId, Some(aData));
+ Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers()));
+ return true;
+}
+
+bool
+ContentChild::RecvPushSubscriptionChange(const nsCString& aScope,
+ const IPC::Principal& aPrincipal)
+{
+ PushSubscriptionChangeDispatcher dispatcher(aScope, aPrincipal);
+ Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObserversAndWorkers()));
+ return true;
+}
+
+bool
+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 true;
+}
+
+bool
+ContentChild::RecvNotifyPushSubscriptionModifiedObservers(const nsCString& aScope,
+ const IPC::Principal& aPrincipal)
+{
+ PushSubscriptionModifiedDispatcher dispatcher(aScope, aPrincipal);
+ Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObservers()));
+ return true;
+}
+
+bool
+ContentChild::RecvBlobURLRegistration(const nsCString& aURI, PBlobChild* aBlobChild,
+ const IPC::Principal& aPrincipal)
+{
+ RefPtr<BlobImpl> blobImpl = static_cast<BlobChild*>(aBlobChild)->GetBlobImpl();
+ MOZ_ASSERT(blobImpl);
+
+ nsHostObjectProtocolHandler::AddDataEntry(aURI, aPrincipal, blobImpl);
+ return true;
+}
+
+bool
+ContentChild::RecvBlobURLUnregistration(const nsCString& aURI)
+{
+ nsHostObjectProtocolHandler::RemoveDataEntry(aURI);
+ return true;
+}
+
+#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, aChild);
+}
+
+void
+ContentChild::DeleteGetFilesRequest(nsID& aUUID, GetFilesHelperChild* aChild)
+{
+ MOZ_ASSERT(aChild);
+ MOZ_ASSERT(mGetFilesPendingRequests.GetWeak(aUUID));
+
+ Unused << SendDeleteGetFilesRequest(aUUID);
+ mGetFilesPendingRequests.Remove(aUUID);
+}
+
+bool
+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 true;
+ }
+
+ if (aResult.type() == GetFilesResponseResult::TGetFilesResponseFailure) {
+ child->Finished(aResult.get_GetFilesResponseFailure().errorCode());
+ } else {
+ MOZ_ASSERT(aResult.type() == GetFilesResponseResult::TGetFilesResponseSuccess);
+
+ const nsTArray<PBlobChild*>& blobs =
+ aResult.get_GetFilesResponseSuccess().blobsChild();
+
+ bool succeeded = true;
+ for (uint32_t i = 0; succeeded && i < blobs.Length(); ++i) {
+ RefPtr<BlobImpl> impl = static_cast<BlobChild*>(blobs[i])->GetBlobImpl();
+ succeeded = child->AppendBlobImpl(impl);
+ }
+
+ child->Finished(succeeded ? NS_OK : NS_ERROR_OUT_OF_MEMORY);
+ }
+
+ mGetFilesPendingRequests.Remove(aUUID);
+ return true;
+}
+
+/* static */ void
+ContentChild::FatalErrorIfNotUsingGPUProcess(const char* const aProtocolName,
+ 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(aProtocolName, aErrorMsg, false);
+ } else {
+ nsAutoCString formattedMessage("IPDL error [");
+ formattedMessage.AppendASCII(aProtocolName);
+ formattedMessage.AppendLiteral("]: \"");
+ formattedMessage.AppendASCII(aErrorMsg);
+ formattedMessage.AppendLiteral("\".");
+ NS_WARNING(formattedMessage.get());
+ }
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h
new file mode 100644
index 000000000..cb718aff9
--- /dev/null
+++ b/dom/ipc/ContentChild.h
@@ -0,0 +1,702 @@
+/* -*- 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/Attributes.h"
+#include "mozilla/dom/ContentBridgeParent.h"
+#include "mozilla/dom/nsIContentChild.h"
+#include "mozilla/dom/PBrowserOrId.h"
+#include "mozilla/dom/PContentChild.h"
+#include "nsAutoPtr.h"
+#include "nsHashKeys.h"
+#include "nsIObserver.h"
+#include "nsTHashtable.h"
+#include "nsRefPtrHashtable.h"
+
+#include "nsWeakPtr.h"
+#include "nsIWindowProvider.h"
+
+#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
+#include "nsIFile.h"
+#endif
+
+struct ChromePackage;
+class nsIObserver;
+struct SubstitutionMapping;
+struct OverrideMapping;
+class nsIDomainPolicy;
+
+namespace mozilla {
+class RemoteSpellcheckEngineChild;
+
+namespace ipc {
+class OptionalURIParams;
+class URIParams;
+}// namespace ipc
+
+namespace dom {
+
+class AlertObserver;
+class ConsoleListener;
+class PStorageChild;
+class ClonedMessageData;
+class TabChild;
+class GetFilesHelperChild;
+
+class ContentChild final : public PContentChild
+ , public nsIWindowProvider
+ , public nsIContentChild
+{
+ typedef mozilla::dom::ClonedMessageData ClonedMessageData;
+ typedef mozilla::ipc::FileDescriptor FileDescriptor;
+ typedef mozilla::ipc::OptionalURIParams OptionalURIParams;
+ typedef mozilla::ipc::PFileDescriptorSetChild PFileDescriptorSetChild;
+ typedef mozilla::ipc::URIParams URIParams;
+
+public:
+ NS_DECL_NSIWINDOWPROVIDER
+
+ 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;
+ };
+
+ nsresult
+ ProvideWindowCommon(TabChild* aTabOpener,
+ mozIDOMWindowProxy* aOpener,
+ bool aIframeMoz,
+ uint32_t aChromeFlags,
+ bool aCalledFromJS,
+ bool aPositionSpecified,
+ bool aSizeSpecified,
+ nsIURI* aURI,
+ const nsAString& aName,
+ const nsACString& aFeatures,
+ bool aForceNoOpener,
+ bool* aWindowIsNew,
+ mozIDOMWindowProxy** aReturn);
+
+ bool Init(MessageLoop* aIOLoop,
+ base::ProcessId aParentPid,
+ IPC::Channel* aChannel);
+
+ void InitProcessAttributes();
+
+ void InitXPCOM();
+
+ void InitGraphicsDeviceData();
+
+ static ContentChild* GetSingleton()
+ {
+ return sSingleton;
+ }
+
+ const AppInfo& GetAppInfo()
+ {
+ return mAppInfo;
+ }
+
+ void SetProcessName(const nsAString& aName, bool aDontOverride = false);
+
+ void GetProcessName(nsAString& aName) const;
+
+ void GetProcessName(nsACString& aName) const;
+
+#if defined(XP_MACOSX) && defined(MOZ_CONTENT_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;
+
+ static void AppendProcessId(nsACString& aName);
+
+ ContentBridgeParent* GetLastBridge()
+ {
+ MOZ_ASSERT(mLastBridge);
+ ContentBridgeParent* parent = mLastBridge;
+ mLastBridge = nullptr;
+ return parent;
+ }
+
+ RefPtr<ContentBridgeParent> mLastBridge;
+
+ PPluginModuleParent *
+ AllocPPluginModuleParent(mozilla::ipc::Transport* transport,
+ base::ProcessId otherProcess) override;
+
+ PContentBridgeParent*
+ AllocPContentBridgeParent(mozilla::ipc::Transport* transport,
+ base::ProcessId otherProcess) override;
+ PContentBridgeChild*
+ AllocPContentBridgeChild(mozilla::ipc::Transport* transport,
+ base::ProcessId otherProcess) override;
+
+ PGMPServiceChild*
+ AllocPGMPServiceChild(mozilla::ipc::Transport* transport,
+ base::ProcessId otherProcess) override;
+
+ bool
+ RecvGMPsChanged(nsTArray<GMPCapabilityData>&& capabilities) override;
+
+ bool
+ RecvInitRendering(
+ Endpoint<PCompositorBridgeChild>&& aCompositor,
+ Endpoint<PImageBridgeChild>&& aImageBridge,
+ Endpoint<PVRManagerChild>&& aVRBridge,
+ Endpoint<PVideoDecoderManagerChild>&& aVideoManager) override;
+
+ bool
+ RecvReinitRendering(
+ Endpoint<PCompositorBridgeChild>&& aCompositor,
+ Endpoint<PImageBridgeChild>&& aImageBridge,
+ Endpoint<PVRManagerChild>&& aVRBridge,
+ Endpoint<PVideoDecoderManagerChild>&& aVideoManager) override;
+
+ PProcessHangMonitorChild*
+ AllocPProcessHangMonitorChild(Transport* aTransport,
+ ProcessId aOtherProcess) override;
+
+ virtual bool RecvSetProcessSandbox(const MaybeFileDesc& aBroker) override;
+
+ PBackgroundChild*
+ AllocPBackgroundChild(Transport* aTransport, ProcessId aOtherProcess)
+ override;
+
+ virtual PBrowserChild* AllocPBrowserChild(const TabId& aTabId,
+ const IPCTabContext& aContext,
+ const uint32_t& aChromeFlags,
+ const ContentParentId& aCpID,
+ const bool& aIsForApp,
+ const bool& aIsForBrowser) override;
+
+ virtual bool DeallocPBrowserChild(PBrowserChild*) override;
+
+ virtual PBlobChild*
+ AllocPBlobChild(const BlobConstructorParams& aParams) override;
+
+ virtual bool DeallocPBlobChild(PBlobChild* aActor) override;
+
+ virtual PCrashReporterChild*
+ AllocPCrashReporterChild(const mozilla::dom::NativeThreadId& id,
+ const uint32_t& processType) override;
+
+ virtual bool
+ DeallocPCrashReporterChild(PCrashReporterChild*) override;
+
+ virtual PHalChild* AllocPHalChild() override;
+ virtual bool DeallocPHalChild(PHalChild*) override;
+
+ virtual PHeapSnapshotTempFileHelperChild*
+ AllocPHeapSnapshotTempFileHelperChild() override;
+
+ virtual bool
+ DeallocPHeapSnapshotTempFileHelperChild(PHeapSnapshotTempFileHelperChild*) override;
+
+ virtual PMemoryReportRequestChild*
+ AllocPMemoryReportRequestChild(const uint32_t& aGeneration,
+ const bool& aAnonymize,
+ const bool& aMinimizeMemoryUsage,
+ const MaybeFileDesc& aDMDFile) override;
+
+ virtual bool
+ DeallocPMemoryReportRequestChild(PMemoryReportRequestChild* actor) override;
+
+ virtual bool
+ RecvPMemoryReportRequestConstructor(PMemoryReportRequestChild* aChild,
+ const uint32_t& aGeneration,
+ const bool& aAnonymize,
+ const bool &aMinimizeMemoryUsage,
+ const MaybeFileDesc &aDMDFile) override;
+
+ virtual PCycleCollectWithLogsChild*
+ AllocPCycleCollectWithLogsChild(const bool& aDumpAllTraces,
+ const FileDescriptor& aGCLog,
+ const FileDescriptor& aCCLog) override;
+
+ virtual bool
+ DeallocPCycleCollectWithLogsChild(PCycleCollectWithLogsChild* aActor) override;
+
+ virtual bool
+ RecvPCycleCollectWithLogsConstructor(PCycleCollectWithLogsChild* aChild,
+ const bool& aDumpAllTraces,
+ const FileDescriptor& aGCLog,
+ const FileDescriptor& aCCLog) override;
+
+ virtual PWebBrowserPersistDocumentChild*
+ AllocPWebBrowserPersistDocumentChild(PBrowserChild* aBrowser,
+ const uint64_t& aOuterWindowID) override;
+
+ virtual bool
+ RecvPWebBrowserPersistDocumentConstructor(PWebBrowserPersistDocumentChild *aActor,
+ PBrowserChild *aBrowser,
+ const uint64_t& aOuterWindowID) override;
+
+ virtual bool
+ DeallocPWebBrowserPersistDocumentChild(PWebBrowserPersistDocumentChild* aActor) override;
+
+ virtual PTestShellChild* AllocPTestShellChild() override;
+
+ virtual bool DeallocPTestShellChild(PTestShellChild*) override;
+
+ virtual bool RecvPTestShellConstructor(PTestShellChild*) override;
+
+ jsipc::CPOWManager* GetCPOWManager() override;
+
+ virtual PNeckoChild* AllocPNeckoChild() override;
+
+ virtual bool DeallocPNeckoChild(PNeckoChild*) override;
+
+ virtual PPrintingChild* AllocPPrintingChild() override;
+
+ virtual bool DeallocPPrintingChild(PPrintingChild*) override;
+
+ virtual PSendStreamChild*
+ SendPSendStreamConstructor(PSendStreamChild*) override;
+
+ virtual PSendStreamChild* AllocPSendStreamChild() override;
+ virtual bool DeallocPSendStreamChild(PSendStreamChild*) override;
+
+ virtual PScreenManagerChild*
+ AllocPScreenManagerChild(uint32_t* aNumberOfScreens,
+ float* aSystemDefaultScale,
+ bool* aSuccess) override;
+
+ virtual bool DeallocPScreenManagerChild(PScreenManagerChild*) override;
+
+ virtual PPSMContentDownloaderChild*
+ AllocPPSMContentDownloaderChild( const uint32_t& aCertType) override;
+
+ virtual bool
+ DeallocPPSMContentDownloaderChild(PPSMContentDownloaderChild* aDownloader) override;
+
+ virtual PExternalHelperAppChild*
+ AllocPExternalHelperAppChild(const OptionalURIParams& uri,
+ const nsCString& aMimeContentType,
+ const nsCString& aContentDisposition,
+ const uint32_t& aContentDispositionHint,
+ const nsString& aContentDispositionFilename,
+ const bool& aForceSave,
+ const int64_t& aContentLength,
+ const bool& aWasFileChannel,
+ const OptionalURIParams& aReferrer,
+ PBrowserChild* aBrowser) override;
+
+ virtual bool
+ DeallocPExternalHelperAppChild(PExternalHelperAppChild *aService) override;
+
+ virtual PHandlerServiceChild* AllocPHandlerServiceChild() override;
+
+ virtual bool DeallocPHandlerServiceChild(PHandlerServiceChild*) override;
+
+ virtual PMediaChild* AllocPMediaChild() override;
+
+ virtual bool DeallocPMediaChild(PMediaChild* aActor) override;
+
+ virtual PStorageChild* AllocPStorageChild() override;
+
+ virtual bool DeallocPStorageChild(PStorageChild* aActor) override;
+
+ virtual PPresentationChild* AllocPPresentationChild() override;
+
+ virtual bool DeallocPPresentationChild(PPresentationChild* aActor) override;
+
+ virtual PFlyWebPublishedServerChild*
+ AllocPFlyWebPublishedServerChild(const nsString& name,
+ const FlyWebPublishOptions& params) override;
+
+ virtual bool DeallocPFlyWebPublishedServerChild(PFlyWebPublishedServerChild* aActor) override;
+
+ virtual bool
+ RecvNotifyPresentationReceiverLaunched(PBrowserChild* aIframe,
+ const nsString& aSessionId) override;
+
+ virtual bool
+ RecvNotifyPresentationReceiverCleanUp(const nsString& aSessionId) override;
+
+ virtual bool RecvNotifyEmptyHTTPCache() override;
+
+ virtual PSpeechSynthesisChild* AllocPSpeechSynthesisChild() override;
+
+ virtual bool DeallocPSpeechSynthesisChild(PSpeechSynthesisChild* aActor) override;
+
+ virtual bool RecvRegisterChrome(InfallibleTArray<ChromePackage>&& packages,
+ InfallibleTArray<SubstitutionMapping>&& resources,
+ InfallibleTArray<OverrideMapping>&& overrides,
+ const nsCString& locale,
+ const bool& reset) override;
+ virtual bool RecvRegisterChromeItem(const ChromeRegistryItem& item) override;
+
+ virtual bool RecvClearImageCache(const bool& privateLoader,
+ const bool& chrome) override;
+
+ virtual mozilla::jsipc::PJavaScriptChild* AllocPJavaScriptChild() override;
+
+ virtual bool DeallocPJavaScriptChild(mozilla::jsipc::PJavaScriptChild*) override;
+
+ virtual PRemoteSpellcheckEngineChild* AllocPRemoteSpellcheckEngineChild() override;
+
+ virtual bool DeallocPRemoteSpellcheckEngineChild(PRemoteSpellcheckEngineChild*) override;
+
+ virtual bool RecvSetOffline(const bool& offline) override;
+
+ virtual bool RecvSetConnectivity(const bool& connectivity) override;
+ virtual bool RecvSetCaptivePortalState(const int32_t& state) override;
+
+ virtual bool RecvNotifyLayerAllocated(const dom::TabId& aTabId, const uint64_t& aLayersId) override;
+
+ virtual bool RecvSpeakerManagerNotify() override;
+
+ virtual bool RecvBidiKeyboardNotify(const bool& isLangRTL,
+ const bool& haveBidiKeyboards) override;
+
+ virtual bool RecvNotifyVisited(const URIParams& aURI) override;
+
+ // auto remove when alertfinished is received.
+ nsresult AddRemoteAlertObserver(const nsString& aData, nsIObserver* aObserver);
+
+ virtual bool RecvPreferenceUpdate(const PrefSetting& aPref) override;
+ virtual bool RecvVarUpdate(const GfxVarUpdate& pref) override;
+
+ virtual bool RecvDataStoragePut(const nsString& aFilename,
+ const DataStorageItem& aItem) override;
+
+ virtual bool RecvDataStorageRemove(const nsString& aFilename,
+ const nsCString& aKey,
+ const DataStorageType& aType) override;
+
+ virtual bool RecvDataStorageClear(const nsString& aFilename) override;
+
+ virtual bool RecvNotifyAlertsObserver(const nsCString& aType,
+ const nsString& aData) override;
+
+ virtual bool RecvLoadProcessScript(const nsString& aURL) override;
+
+ virtual bool RecvAsyncMessage(const nsString& aMsg,
+ InfallibleTArray<CpowEntry>&& aCpows,
+ const IPC::Principal& aPrincipal,
+ const ClonedMessageData& aData) override;
+
+ virtual bool RecvGeolocationUpdate(const GeoPosition& somewhere) override;
+
+ virtual bool RecvGeolocationError(const uint16_t& errorCode) override;
+
+ virtual bool RecvUpdateDictionaryList(InfallibleTArray<nsString>&& aDictionaries) override;
+
+ virtual bool RecvAddPermission(const IPC::Permission& permission) override;
+
+ virtual bool RecvFlushMemory(const nsString& reason) override;
+
+ virtual bool RecvActivateA11y(const uint32_t& aMsaaID) override;
+ virtual bool RecvShutdownA11y() override;
+
+ virtual bool RecvGarbageCollect() override;
+ virtual bool RecvCycleCollect() override;
+
+ virtual bool RecvAppInfo(const nsCString& version, const nsCString& buildID,
+ const nsCString& name, const nsCString& UAName,
+ const nsCString& ID, const nsCString& vendor) override;
+
+ virtual bool RecvAppInit() override;
+
+ virtual bool
+ RecvInitServiceWorkers(const ServiceWorkerConfiguration& aConfig) override;
+
+ virtual bool
+ RecvInitBlobURLs(nsTArray<BlobURLRegistrationData>&& aRegistations) override;
+
+ virtual bool RecvLastPrivateDocShellDestroyed() override;
+
+ virtual bool RecvVolumes(InfallibleTArray<VolumeInfo>&& aVolumes) override;
+
+ virtual bool RecvFileSystemUpdate(const nsString& aFsName,
+ const nsString& aVolumeName,
+ const int32_t& aState,
+ const int32_t& aMountGeneration,
+ const bool& aIsMediaPresent,
+ const bool& aIsSharing,
+ const bool& aIsFormatting,
+ const bool& aIsFake,
+ const bool& aIsUnmounting,
+ const bool& aIsRemovable,
+ const bool& aIsHotSwappable) override;
+
+ virtual bool RecvVolumeRemoved(const nsString& aFsName) override;
+
+ virtual bool
+ RecvNotifyProcessPriorityChanged(const hal::ProcessPriority& aPriority) override;
+
+ virtual bool RecvMinimizeMemoryUsage() override;
+
+ virtual bool RecvLoadAndRegisterSheet(const URIParams& aURI,
+ const uint32_t& aType) override;
+
+ virtual bool RecvUnregisterSheet(const URIParams& aURI,
+ const uint32_t& aType) override;
+
+ virtual bool RecvNotifyPhoneStateChange(const nsString& aState) override;
+
+ void AddIdleObserver(nsIObserver* aObserver, uint32_t aIdleTimeInS);
+
+ void RemoveIdleObserver(nsIObserver* aObserver, uint32_t aIdleTimeInS);
+
+ virtual bool RecvNotifyIdleObserver(const uint64_t& aObserver,
+ const nsCString& aTopic,
+ const nsString& aData) override;
+
+ virtual bool RecvAssociatePluginId(const uint32_t& aPluginId,
+ const base::ProcessId& aProcessId) override;
+
+ virtual bool RecvLoadPluginResult(const uint32_t& aPluginId,
+ const bool& aResult) override;
+
+ virtual bool RecvUpdateWindow(const uintptr_t& aChildId) override;
+
+ virtual bool RecvStartProfiler(const ProfilerInitParams& params) override;
+
+ virtual bool RecvPauseProfiler(const bool& aPause) override;
+
+ virtual bool RecvStopProfiler() override;
+
+ virtual bool RecvGatherProfile() override;
+
+ virtual bool RecvDomainSetChanged(const uint32_t& aSetType,
+ const uint32_t& aChangeType,
+ const OptionalURIParams& aDomain) override;
+
+ virtual bool RecvShutdown() override;
+
+ virtual bool
+ RecvInvokeDragSession(nsTArray<IPCDataTransfer>&& aTransfers,
+ const uint32_t& aAction) override;
+
+ virtual bool RecvEndDragSession(const bool& aDoneDrag,
+ const bool& aUserCancelled,
+ const mozilla::LayoutDeviceIntPoint& aEndDragPoint) override;
+
+ virtual bool
+ RecvPush(const nsCString& aScope,
+ const IPC::Principal& aPrincipal,
+ const nsString& aMessageId) override;
+
+ virtual bool
+ RecvPushWithData(const nsCString& aScope,
+ const IPC::Principal& aPrincipal,
+ const nsString& aMessageId,
+ InfallibleTArray<uint8_t>&& aData) override;
+
+ virtual bool
+ RecvPushSubscriptionChange(const nsCString& aScope,
+ const IPC::Principal& aPrincipal) override;
+
+ virtual bool
+ RecvPushError(const nsCString& aScope, const IPC::Principal& aPrincipal,
+ const nsString& aMessage, const uint32_t& aFlags) override;
+
+ virtual bool
+ RecvNotifyPushSubscriptionModifiedObservers(const nsCString& aScope,
+ const IPC::Principal& aPrincipal) override;
+
+ // 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 GetMsaaID() const { return mMsaaID; }
+#endif
+
+ bool IsForApp() const { return mIsForApp; }
+ bool IsForBrowser() const { return mIsForBrowser; }
+
+ virtual PBlobChild*
+ SendPBlobConstructor(PBlobChild* actor,
+ const BlobConstructorParams& params) override;
+
+ virtual PFileDescriptorSetChild*
+ SendPFileDescriptorSetConstructor(const FileDescriptor&) override;
+
+ virtual PFileDescriptorSetChild*
+ AllocPFileDescriptorSetChild(const FileDescriptor&) override;
+
+ virtual bool
+ DeallocPFileDescriptorSetChild(PFileDescriptorSetChild*) override;
+
+ virtual bool SendPBrowserConstructor(PBrowserChild* actor,
+ const TabId& aTabId,
+ const IPCTabContext& context,
+ const uint32_t& chromeFlags,
+ const ContentParentId& aCpID,
+ const bool& aIsForApp,
+ const bool& aIsForBrowser) override;
+
+ virtual bool RecvPBrowserConstructor(PBrowserChild* aCctor,
+ const TabId& aTabId,
+ const IPCTabContext& aContext,
+ const uint32_t& aChromeFlags,
+ const ContentParentId& aCpID,
+ const bool& aIsForApp,
+ const bool& aIsForBrowser) override;
+
+ FORWARD_SHMEM_ALLOCATOR_TO(PContentChild)
+
+ void GetAvailableDictionaries(InfallibleTArray<nsString>& aDictionaries);
+
+ PBrowserOrId
+ GetBrowserOrId(TabChild* aTabChild);
+
+ virtual POfflineCacheUpdateChild*
+ AllocPOfflineCacheUpdateChild(const URIParams& manifestURI,
+ const URIParams& documentURI,
+ const PrincipalInfo& aLoadingPrincipalInfo,
+ const bool& stickDocument) override;
+
+ virtual bool
+ DeallocPOfflineCacheUpdateChild(POfflineCacheUpdateChild* offlineCacheUpdate) override;
+
+ virtual PWebrtcGlobalChild* AllocPWebrtcGlobalChild() override;
+
+ virtual bool DeallocPWebrtcGlobalChild(PWebrtcGlobalChild *aActor) override;
+
+ virtual PContentPermissionRequestChild*
+ AllocPContentPermissionRequestChild(const InfallibleTArray<PermissionRequest>& aRequests,
+ const IPC::Principal& aPrincipal,
+ const TabId& aTabId) override;
+ virtual bool
+ DeallocPContentPermissionRequestChild(PContentPermissionRequestChild* actor) override;
+
+ // Windows specific - set up audio session
+ virtual bool
+ RecvSetAudioSessionData(const nsID& aId,
+ const nsString& aDisplayName,
+ const nsString& aIconPath) override;
+
+
+ // 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);
+
+ virtual bool
+ RecvGetFilesResponse(const nsID& aUUID,
+ const GetFilesResponseResult& aResult) override;
+
+ virtual bool
+ RecvBlobURLRegistration(const nsCString& aURI, PBlobChild* aBlobChild,
+ const IPC::Principal& aPrincipal) override;
+
+ virtual bool
+ RecvBlobURLUnregistration(const nsCString& aURI) override;
+
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+ bool
+ SendGetA11yContentId();
+#endif // defined(XP_WIN) && defined(ACCESSIBILITY)
+
+ /**
+ * 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 aProtocolName,
+ const char* const aErrorMsg,
+ base::ProcessId aOtherPid);
+
+private:
+ static void ForceKillTimerCallback(nsITimer* aTimer, void* aClosure);
+ void StartForceKillTimer();
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ virtual void ProcessingError(Result aCode, const char* aReason) override;
+
+ InfallibleTArray<nsAutoPtr<AlertObserver> > mAlertObservers;
+ RefPtr<ConsoleListener> mConsoleListener;
+
+ nsTHashtable<nsPtrHashKey<nsIObserver>> mIdleObservers;
+
+ InfallibleTArray<nsString> mAvailableDictionaries;
+
+ /**
+ * 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)
+ /**
+ * This is an a11y-specific unique id for the content process that is
+ * generated by the chrome process.
+ */
+ uint32_t mMsaaID;
+#endif
+
+ AppInfo mAppInfo;
+
+ bool mIsForApp;
+ bool mIsForBrowser;
+ bool mCanOverrideProcessName;
+ bool mIsAlive;
+ nsString mProcessName;
+
+ static ContentChild* sSingleton;
+
+ nsCOMPtr<nsIDomainPolicy> mPolicy;
+ nsCOMPtr<nsITimer> mForceKillTimer;
+
+#if defined(XP_MACOSX) && defined(MOZ_CONTENT_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;
+
+ bool mShuttingDown;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ContentChild);
+};
+
+uint64_t
+NextWindowID();
+
+} // 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 000000000..ff40db8d7
--- /dev/null
+++ b/dom/ipc/ContentParent.cpp
@@ -0,0 +1,5185 @@
+/* -*- 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 "ContentParent.h"
+#include "TabParent.h"
+
+#if defined(ANDROID) || defined(LINUX)
+# include <sys/time.h>
+# include <sys/resource.h>
+#endif
+
+#ifdef MOZ_WIDGET_GONK
+#include <sys/types.h>
+#include <sys/wait.h>
+#endif
+
+#include "chrome/common/process_watcher.h"
+
+#include "mozilla/a11y/PDocAccessible.h"
+#include "AppProcessChecker.h"
+#include "AudioChannelService.h"
+#include "BlobParent.h"
+#include "CrashReporterParent.h"
+#include "GMPServiceParent.h"
+#include "HandlerServiceParent.h"
+#include "IHistory.h"
+#include "imgIContainer.h"
+#include "mozIApplication.h"
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+#include "mozilla/a11y/AccessibleWrap.h"
+#endif
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/StyleSheetInlines.h"
+#include "mozilla/DataStorage.h"
+#include "mozilla/devtools/HeapSnapshotTempFileHelperParent.h"
+#include "mozilla/docshell/OfflineCacheUpdateParent.h"
+#include "mozilla/dom/DataTransfer.h"
+#include "mozilla/dom/DOMStorageIPC.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/File.h"
+#include "mozilla/dom/FileSystemSecurity.h"
+#include "mozilla/dom/ExternalHelperAppParent.h"
+#include "mozilla/dom/GetFilesHelper.h"
+#include "mozilla/dom/GeolocationBinding.h"
+#include "mozilla/dom/Notification.h"
+#include "mozilla/dom/PContentBridgeParent.h"
+#include "mozilla/dom/PContentPermissionRequestParent.h"
+#include "mozilla/dom/PCycleCollectWithLogsParent.h"
+#include "mozilla/dom/PMemoryReportRequestParent.h"
+#include "mozilla/dom/ServiceWorkerRegistrar.h"
+#include "mozilla/dom/power/PowerManagerService.h"
+#include "mozilla/dom/Permissions.h"
+#include "mozilla/dom/PresentationParent.h"
+#include "mozilla/dom/PPresentationParent.h"
+#include "mozilla/dom/PushNotifier.h"
+#include "mozilla/dom/FlyWebPublishedServerIPC.h"
+#include "mozilla/dom/quota/QuotaManagerService.h"
+#include "mozilla/dom/time/DateCacheCleaner.h"
+#include "mozilla/embedding/printingui/PrintingParent.h"
+#include "mozilla/gfx/gfxVars.h"
+#include "mozilla/gfx/GPUProcessManager.h"
+#include "mozilla/hal_sandbox/PHalParent.h"
+#include "mozilla/ipc/BackgroundChild.h"
+#include "mozilla/ipc/BackgroundParent.h"
+#include "mozilla/ipc/FileDescriptorUtils.h"
+#include "mozilla/ipc/PSendStreamParent.h"
+#include "mozilla/ipc/TestShellParent.h"
+#include "mozilla/ipc/InputStreamUtils.h"
+#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
+#include "mozilla/layers/PAPZParent.h"
+#include "mozilla/layers/CompositorThread.h"
+#include "mozilla/layers/ImageBridgeParent.h"
+#include "mozilla/layers/LayerTreeOwnerTracker.h"
+#include "mozilla/layout/RenderFrameParent.h"
+#include "mozilla/LookAndFeel.h"
+#include "mozilla/media/MediaParent.h"
+#include "mozilla/Move.h"
+#include "mozilla/net/NeckoParent.h"
+#include "mozilla/plugins/PluginBridge.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/ProcessHangMonitor.h"
+#include "mozilla/ProcessHangMonitorIPC.h"
+#ifdef MOZ_ENABLE_PROFILER_SPS
+#include "mozilla/ProfileGatherer.h"
+#endif
+#include "mozilla/ScopeExit.h"
+#include "mozilla/Services.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/WebBrowserPersistDocumentParent.h"
+#include "mozilla/Unused.h"
+#include "nsAnonymousTemporaryFile.h"
+#include "nsAppRunner.h"
+#include "nsCDefaultURIFixup.h"
+#include "nsCExternalHandlerService.h"
+#include "nsCOMPtr.h"
+#include "nsChromeRegistryChrome.h"
+#include "nsConsoleMessage.h"
+#include "nsConsoleService.h"
+#include "nsContentUtils.h"
+#include "nsDebugImpl.h"
+#include "nsFrameMessageManager.h"
+#include "nsHashPropertyBag.h"
+#include "nsIAlertsService.h"
+#include "nsIAppsService.h"
+#include "nsIClipboard.h"
+#include "nsContentPermissionHelper.h"
+#include "nsICycleCollectorListener.h"
+#include "nsIDocShellTreeOwner.h"
+#include "nsIDocument.h"
+#include "nsIDOMGeoGeolocation.h"
+#include "nsIDOMGeoPositionError.h"
+#include "nsIDragService.h"
+#include "mozilla/dom/WakeLock.h"
+#include "nsIDOMWindow.h"
+#include "nsIExternalProtocolService.h"
+#include "nsIFormProcessor.h"
+#include "nsIGfxInfo.h"
+#include "nsIIdleService.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIMemoryInfoDumper.h"
+#include "nsIMemoryReporter.h"
+#include "nsIMozBrowserFrame.h"
+#include "nsIMutable.h"
+#include "nsINSSU2FToken.h"
+#include "nsIObserverService.h"
+#include "nsIParentChannel.h"
+#include "nsIPresShell.h"
+#include "nsIRemoteWindowContext.h"
+#include "nsIScriptError.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsISiteSecurityService.h"
+#include "nsISpellChecker.h"
+#include "nsISupportsPrimitives.h"
+#include "nsITimer.h"
+#include "nsIURIFixup.h"
+#include "nsIDocShellTreeOwner.h"
+#include "nsIXULWindow.h"
+#include "nsIDOMChromeWindow.h"
+#include "nsIWindowWatcher.h"
+#include "nsPIWindowWatcher.h"
+#include "nsWindowWatcher.h"
+#include "nsIXULRuntime.h"
+#include "mozilla/dom/nsMixedContentBlocker.h"
+#include "nsMemoryInfoDumper.h"
+#include "nsMemoryReporterManager.h"
+#include "nsServiceManagerUtils.h"
+#include "nsStyleSheetService.h"
+#include "nsThreadUtils.h"
+#include "nsToolkitCompsCID.h"
+#include "nsWidgetsCID.h"
+#include "PreallocatedProcessManager.h"
+#include "ProcessPriorityManager.h"
+#include "SandboxHal.h"
+#include "ScreenManagerParent.h"
+#include "SourceSurfaceRawData.h"
+#include "TabParent.h"
+#include "URIUtils.h"
+#include "nsIWebBrowserChrome.h"
+#include "nsIDocShell.h"
+#include "nsDocShell.h"
+#include "nsOpenURIInFrameParams.h"
+#include "mozilla/net/NeckoMessageUtils.h"
+#include "gfxPrefs.h"
+#include "prio.h"
+#include "private/pprio.h"
+#include "ContentProcessManager.h"
+#include "mozilla/dom/ipc/StructuredCloneData.h"
+#include "mozilla/psm/PSMContentListener.h"
+#include "nsPluginHost.h"
+#include "nsPluginTags.h"
+#include "nsIBlocklistService.h"
+#include "mozilla/StyleSheet.h"
+#include "mozilla/StyleSheetInlines.h"
+#include "nsHostObjectProtocolHandler.h"
+#include "nsICaptivePortalService.h"
+
+#include "nsIBidiKeyboard.h"
+
+#include "nsLayoutStylesheetCache.h"
+
+#ifdef MOZ_WEBRTC
+#include "signaling/src/peerconnection/WebrtcGlobalParent.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
+
+#ifdef MOZ_PERMISSIONS
+# include "nsPermissionManager.h"
+#endif
+
+#ifdef MOZ_WIDGET_ANDROID
+# include "AndroidBridge.h"
+#endif
+
+#ifdef MOZ_WIDGET_GONK
+#include "nsIVolume.h"
+#include "nsVolumeService.h"
+#include "nsIVolumeService.h"
+#include "SpeakerManagerService.h"
+using namespace mozilla::system;
+#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_CONTENT_SANDBOX) && defined(XP_LINUX)
+#include "mozilla/SandboxInfo.h"
+#include "mozilla/SandboxBroker.h"
+#include "mozilla/SandboxBrokerPolicyFactory.h"
+#endif
+
+#ifdef MOZ_TOOLKIT_SEARCH
+#include "nsIBrowserSearchService.h"
+#endif
+
+#ifdef MOZ_ENABLE_PROFILER_SPS
+#include "nsIProfiler.h"
+#include "nsIProfileSaveEvent.h"
+#endif
+
+#ifdef XP_WIN
+#include "mozilla/widget/AudioSession.h"
+#endif
+
+#ifdef MOZ_CRASHREPORTER
+#include "nsThread.h"
+#endif
+
+#ifdef ACCESSIBILITY
+#include "nsAccessibilityService.h"
+#endif
+
+// For VP9Benchmark::sBenchmarkFpsPref
+#include "Benchmark.h"
+
+static NS_DEFINE_CID(kCClipboardCID, NS_CLIPBOARD_CID);
+
+#if defined(XP_WIN)
+// e10s forced enable pref, defined in nsAppRunner.cpp
+extern const char* kForceEnableE10sPref;
+#endif
+
+using base::ChildPrivileges;
+using base::KillProcess;
+#ifdef MOZ_ENABLE_PROFILER_SPS
+using mozilla::ProfileGatherer;
+#endif
+
+#ifdef MOZ_CRASHREPORTER
+using namespace CrashReporter;
+#endif
+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::layers;
+using namespace mozilla::layout;
+using namespace mozilla::net;
+using namespace mozilla::jsipc;
+using namespace mozilla::psm;
+using namespace mozilla::widget;
+
+// 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 dom {
+
+#define NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC "ipc:network:set-offline"
+#define NS_IPC_IOSERVICE_SET_CONNECTIVITY_TOPIC "ipc:network:set-connectivity"
+
+class MemoryReportRequestParent : public PMemoryReportRequestParent
+{
+public:
+ explicit MemoryReportRequestParent(uint32_t aGeneration);
+
+ virtual ~MemoryReportRequestParent();
+
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ virtual bool RecvReport(const MemoryReport& aReport) override;
+ virtual bool Recv__delete__() override;
+
+private:
+ const uint32_t mGeneration;
+ // Non-null if we haven't yet called EndProcessReport() on it.
+ RefPtr<nsMemoryReporterManager> mReporterManager;
+
+ ContentParent* Owner()
+ {
+ return static_cast<ContentParent*>(Manager());
+ }
+};
+
+MemoryReportRequestParent::MemoryReportRequestParent(uint32_t aGeneration)
+ : mGeneration(aGeneration)
+{
+ MOZ_COUNT_CTOR(MemoryReportRequestParent);
+ mReporterManager = nsMemoryReporterManager::GetOrCreate();
+ NS_WARNING_ASSERTION(mReporterManager, "GetOrCreate failed");
+}
+
+bool
+MemoryReportRequestParent::RecvReport(const MemoryReport& aReport)
+{
+ if (mReporterManager) {
+ mReporterManager->HandleChildReport(mGeneration, aReport);
+ }
+ return true;
+}
+
+bool
+MemoryReportRequestParent::Recv__delete__()
+{
+ // Notifying the reporter manager is done in ActorDestroy, because
+ // it needs to happen even if the child process exits mid-report.
+ // (The reporter manager will time out eventually, but let's avoid
+ // that if possible.)
+ return true;
+}
+
+void
+MemoryReportRequestParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+ if (mReporterManager) {
+ mReporterManager->EndProcessReport(mGeneration, aWhy == Deletion);
+ mReporterManager = nullptr;
+ }
+}
+
+MemoryReportRequestParent::~MemoryReportRequestParent()
+{
+ MOZ_ASSERT(!mReporterManager);
+ MOZ_COUNT_DTOR(MemoryReportRequestParent);
+}
+
+// IPC receiver for remote GC/CC logging.
+class CycleCollectWithLogsParent final : public PCycleCollectWithLogsParent
+{
+public:
+ ~CycleCollectWithLogsParent()
+ {
+ MOZ_COUNT_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 bool RecvCloseGCLog() override
+ {
+ Unused << mSink->CloseGCLog();
+ return true;
+ }
+
+ virtual bool RecvCloseCCLog() override
+ {
+ Unused << mSink->CloseCCLog();
+ return true;
+ }
+
+ virtual bool 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 true;
+ }
+
+ 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() {}
+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=%d)",
+ NS_ConvertUTF16toUTF8(friendlyName).get(),
+ cp->Pid(), channelStr,
+ static_cast<nsIContentParent*>(cp), refcnt);
+
+ NS_NAMED_LITERAL_CSTRING(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.");
+
+ aHandleReport->Callback(/* process */ EmptyCString(), path,
+ KIND_OTHER, UNITS_COUNT,
+ numQueuedMessages, desc, aData);
+ }
+
+ return NS_OK;
+}
+
+nsDataHashtable<nsStringHashKey, ContentParent*>* ContentParent::sAppContentParents;
+nsTArray<ContentParent*>* ContentParent::sNonAppContentParents;
+nsTArray<ContentParent*>* ContentParent::sLargeAllocationContentParents;
+nsTArray<ContentParent*>* ContentParent::sPrivateContent;
+StaticAutoPtr<LinkedList<ContentParent> > ContentParent::sContentParents;
+#if defined(XP_LINUX) && defined(MOZ_CONTENT_SANDBOX)
+UniquePtr<SandboxBrokerPolicyFactory> ContentParent::sSandboxBrokerPolicyFactory;
+#endif
+
+// This is true when subprocess launching is enabled. This is the
+// case between StartUp() and ShutDown() or JoinAllSubprocesses().
+static bool sCanLaunchSubprocesses;
+
+// Set to true if the DISABLE_UNSAFE_CPOW_WARNINGS environment variable is
+// set.
+static bool sDisableUnsafeCPOWWarnings = false;
+
+// The first content child has ID 1, so the chrome process can have ID 0.
+static uint64_t gContentChildID = 1;
+
+// We want the prelaunched process to know that it's for apps, but not
+// actually for any app in particular. Use a magic manifest URL.
+// Can't be a static constant.
+#define MAGIC_PREALLOCATED_APP_MANIFEST_URL NS_LITERAL_STRING("{{template}}")
+
+static const char* sObserverTopics[] = {
+ "xpcom-shutdown",
+ "profile-before-change",
+ NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC,
+ NS_IPC_IOSERVICE_SET_CONNECTIVITY_TOPIC,
+ NS_IPC_CAPTIVE_PORTAL_SET_STATE,
+ "memory-pressure",
+ "child-gc-request",
+ "child-cc-request",
+ "child-mmu-request",
+ "last-pb-context-exited",
+ "file-watcher-update",
+#ifdef MOZ_WIDGET_GONK
+ NS_VOLUME_STATE_CHANGED,
+ NS_VOLUME_REMOVED,
+ "phone-state-changed",
+#endif
+#ifdef ACCESSIBILITY
+ "a11y-init-or-shutdown",
+#endif
+#ifdef MOZ_ENABLE_PROFILER_SPS
+ "profiler-started",
+ "profiler-stopped",
+ "profiler-paused",
+ "profiler-resumed",
+ "profiler-subprocess-gather",
+ "profiler-subprocess",
+#endif
+ "cacheservice:empty-cache",
+};
+
+// PreallocateAppProcess is called by the PreallocatedProcessManager.
+// ContentParent then takes this process back within
+// GetNewOrPreallocatedAppProcess.
+/*static*/ already_AddRefed<ContentParent>
+ContentParent::PreallocateAppProcess()
+{
+ RefPtr<ContentParent> process =
+ new ContentParent(/* app = */ nullptr,
+ /* aOpener = */ nullptr,
+ /* isForBrowserElement = */ false,
+ /* isForPreallocated = */ true);
+
+ if (!process->LaunchSubprocess(PROCESS_PRIORITY_PREALLOC)) {
+ return nullptr;
+ }
+
+ process->Init();
+ return process.forget();
+}
+
+/*static*/ already_AddRefed<ContentParent>
+ContentParent::GetNewOrPreallocatedAppProcess(mozIApplication* aApp,
+ ProcessPriority aInitialPriority,
+ ContentParent* aOpener,
+ /*out*/ bool* aTookPreAllocated)
+{
+ MOZ_ASSERT(aApp);
+ RefPtr<ContentParent> process = PreallocatedProcessManager::Take();
+
+ if (process) {
+ if (!process->SetPriorityAndCheckIsAlive(aInitialPriority)) {
+ // Kill the process just in case it's not actually dead; we don't want
+ // to "leak" this process!
+ process->KillHard("GetNewOrPreallocatedAppProcess");
+ }
+ else {
+ nsAutoString manifestURL;
+ if (NS_FAILED(aApp->GetManifestURL(manifestURL))) {
+ NS_ERROR("Failed to get manifest URL");
+ return nullptr;
+ }
+ process->TransformPreallocatedIntoApp(aOpener, manifestURL);
+ process->ForwardKnownInfo();
+
+ if (aTookPreAllocated) {
+ *aTookPreAllocated = true;
+ }
+ return process.forget();
+ }
+ }
+
+ NS_WARNING("Unable to use pre-allocated app process");
+ process = new ContentParent(aApp,
+ /* aOpener = */ aOpener,
+ /* isForBrowserElement = */ false,
+ /* isForPreallocated = */ false);
+
+ if (!process->LaunchSubprocess(aInitialPriority)) {
+ return nullptr;
+ }
+
+ process->Init();
+ process->ForwardKnownInfo();
+
+ if (aTookPreAllocated) {
+ *aTookPreAllocated = false;
+ }
+
+ return process.forget();
+}
+
+/*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;
+ }
+
+#if defined(MOZ_CONTENT_SANDBOX) && defined(MOZ_WIDGET_GONK) && ANDROID_VERSION >= 19
+ // Require sandboxing on B2G >= KitKat. This condition must stay
+ // in sync with ContentChild::RecvSetProcessSandbox.
+ if (!SandboxInfo::Get().CanSandboxContent()) {
+ // MOZ_CRASH strings are only for debug builds; make sure the
+ // message is clear on non-debug builds as well:
+ printf_stderr("Sandboxing support is required on this platform. "
+ "Recompile kernel with CONFIG_SECCOMP_FILTER=y\n");
+ MOZ_CRASH("Sandboxing support is required on this platform.");
+ }
+#endif
+
+ // Note: This reporter measures all ContentParents.
+ RegisterStrongMemoryReporter(new ContentParentsMemoryReporter());
+
+ mozilla::dom::time::InitializeDateCacheCleaner();
+
+ BlobParent::Startup(BlobParent::FriendKey());
+
+ BackgroundChild::Startup();
+
+ // Try to preallocate a process that we can transform into an app later.
+ PreallocatedProcessManager::AllocateAfterDelay();
+
+ sDisableUnsafeCPOWWarnings = PR_GetEnv("DISABLE_UNSAFE_CPOW_WARNINGS");
+
+#if defined(XP_LINUX) && defined(MOZ_CONTENT_SANDBOX)
+ sSandboxBrokerPolicyFactory = MakeUnique<SandboxBrokerPolicyFactory>();
+#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_CONTENT_SANDBOX)
+ sSandboxBrokerPolicyFactory = nullptr;
+#endif
+}
+
+/*static*/ void
+ContentParent::JoinProcessesIOThread(const nsTArray<ContentParent*>* aProcesses,
+ Monitor* aMonitor, bool* aDone)
+{
+ const nsTArray<ContentParent*>& processes = *aProcesses;
+ for (uint32_t i = 0; i < processes.Length(); ++i) {
+ if (GeckoChildProcessHost* process = processes[i]->mSubprocess) {
+ process->Join();
+ }
+ }
+ {
+ MonitorAutoLock lock(*aMonitor);
+ *aDone = true;
+ lock.Notify();
+ }
+ // Don't touch any arguments to this function from now on.
+}
+
+/*static*/ void
+ContentParent::JoinAllSubprocesses()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ AutoTArray<ContentParent*, 8> processes;
+ GetAll(processes);
+ if (processes.IsEmpty()) {
+ printf_stderr("There are no live subprocesses.");
+ return;
+ }
+
+ printf_stderr("Subprocesses are still alive. Doing emergency join.\n");
+
+ bool done = false;
+ Monitor monitor("mozilla.dom.ContentParent.JoinAllSubprocesses");
+ XRE_GetIOMessageLoop()->PostTask(NewRunnableFunction(
+ &ContentParent::JoinProcessesIOThread,
+ &processes, &monitor, &done));
+ {
+ MonitorAutoLock lock(monitor);
+ while (!done) {
+ lock.Wait();
+ }
+ }
+
+ sCanLaunchSubprocesses = false;
+}
+
+/*static*/ already_AddRefed<ContentParent>
+ContentParent::GetNewOrUsedBrowserProcess(bool aForBrowserElement,
+ ProcessPriority aPriority,
+ ContentParent* aOpener,
+ bool aLargeAllocationProcess)
+{
+ nsTArray<ContentParent*>* contentParents;
+ int32_t maxContentParents;
+
+ // Decide which pool of content parents we are going to be pulling from based
+ // on the aLargeAllocationProcess flag.
+ if (aLargeAllocationProcess) {
+ if (!sLargeAllocationContentParents) {
+ sLargeAllocationContentParents = new nsTArray<ContentParent*>();
+ }
+ contentParents = sLargeAllocationContentParents;
+
+ maxContentParents = Preferences::GetInt("dom.ipc.dedicatedProcessCount", 2);
+ } else {
+ if (!sNonAppContentParents) {
+ sNonAppContentParents = new nsTArray<ContentParent*>();
+ }
+ contentParents = sNonAppContentParents;
+
+ maxContentParents = Preferences::GetInt("dom.ipc.processCount", 1);
+ }
+
+ if (maxContentParents < 1) {
+ maxContentParents = 1;
+ }
+
+ if (contentParents->Length() >= uint32_t(maxContentParents)) {
+ uint32_t maxSelectable = std::min(static_cast<uint32_t>(contentParents->Length()),
+ static_cast<uint32_t>(maxContentParents));
+ uint32_t startIdx = rand() % maxSelectable;
+ uint32_t currIdx = startIdx;
+ do {
+ RefPtr<ContentParent> p = (*contentParents)[currIdx];
+ NS_ASSERTION(p->IsAlive(), "Non-alive contentparent in sNonAppContntParents?");
+ if (p->mOpener == aOpener) {
+ return p.forget();
+ }
+ currIdx = (currIdx + 1) % maxSelectable;
+ } while (currIdx != startIdx);
+ }
+
+ // Try to take and transform the preallocated process into browser.
+ RefPtr<ContentParent> p = PreallocatedProcessManager::Take();
+ if (p) {
+ p->TransformPreallocatedIntoBrowser(aOpener);
+ } else {
+ // Failed in using the preallocated process: fork from the chrome process.
+ p = new ContentParent(/* app = */ nullptr,
+ aOpener,
+ aForBrowserElement,
+ /* isForPreallocated = */ false);
+
+ if (!p->LaunchSubprocess(aPriority)) {
+ return nullptr;
+ }
+
+ p->Init();
+ }
+
+ p->mLargeAllocationProcess = aLargeAllocationProcess;
+
+ p->ForwardKnownInfo();
+
+ contentParents->AppendElement(p);
+ return p.forget();
+}
+
+/*static*/ ProcessPriority
+ContentParent::GetInitialProcessPriority(Element* aFrameElement)
+{
+ // Frames with mozapptype == critical which are expecting a system message
+ // get FOREGROUND_HIGH priority.
+
+ if (!aFrameElement) {
+ return PROCESS_PRIORITY_FOREGROUND;
+ }
+
+ if (aFrameElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::mozapptype,
+ NS_LITERAL_STRING("inputmethod"), eCaseMatters)) {
+ return PROCESS_PRIORITY_FOREGROUND_KEYBOARD;
+ } else if (!aFrameElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::mozapptype,
+ NS_LITERAL_STRING("critical"), eCaseMatters)) {
+ return PROCESS_PRIORITY_FOREGROUND;
+ }
+
+ nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(aFrameElement);
+ if (!browserFrame) {
+ return PROCESS_PRIORITY_FOREGROUND;
+ }
+
+ return PROCESS_PRIORITY_FOREGROUND;
+}
+
+#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->IsDestroyed()) {
+ Unused << cp->SendUpdateWindow((uintptr_t)hwnd);
+ }
+}
+#endif // defined(XP_WIN)
+
+bool
+ContentParent::PreallocatedProcessReady()
+{
+ return true;
+}
+
+bool
+ContentParent::RecvCreateChildProcess(const IPCTabContext& aContext,
+ const hal::ProcessPriority& aPriority,
+ const TabId& aOpenerTabId,
+ ContentParentId* aCpId,
+ bool* aIsForApp,
+ bool* aIsForBrowser,
+ TabId* aTabId)
+{
+#if 0
+ if (!CanOpenBrowser(aContext)) {
+ return false;
+ }
+#endif
+ RefPtr<ContentParent> cp;
+ MaybeInvalidTabContext tc(aContext);
+ if (!tc.IsValid()) {
+ NS_ERROR(nsPrintfCString("Received an invalid TabContext from "
+ "the child process. (%s)",
+ tc.GetInvalidReason()).get());
+ return false;
+ }
+
+ nsCOMPtr<mozIApplication> ownApp = tc.GetTabContext().GetOwnApp();
+ if (ownApp) {
+ cp = GetNewOrPreallocatedAppProcess(ownApp, aPriority, this);
+ }
+ else {
+ cp = GetNewOrUsedBrowserProcess(/* isBrowserElement = */ true,
+ aPriority, this);
+ }
+
+ if (!cp) {
+ *aCpId = 0;
+ *aIsForApp = false;
+ *aIsForBrowser = false;
+ return true;
+ }
+
+ *aCpId = cp->ChildID();
+ *aIsForApp = cp->IsForApp();
+ *aIsForBrowser = cp->IsForBrowser();
+
+ ContentProcessManager *cpm = ContentProcessManager::GetSingleton();
+ cpm->AddContentProcess(cp, this->ChildID());
+
+ if (cpm->AddGrandchildProcess(this->ChildID(), cp->ChildID())) {
+ // Pre-allocate a TabId here to save one time IPC call at app startup.
+ *aTabId = AllocateTabId(aOpenerTabId, aContext, cp->ChildID());
+ return (*aTabId != 0);
+ }
+
+ return false;
+}
+
+bool
+ContentParent::RecvBridgeToChildProcess(const ContentParentId& aCpId)
+{
+ ContentProcessManager *cpm = ContentProcessManager::GetSingleton();
+ ContentParent* cp = cpm->GetContentProcessById(aCpId);
+
+ if (cp) {
+ ContentParentId parentId;
+ if (cpm->GetParentProcessId(cp->ChildID(), &parentId) &&
+ parentId == this->ChildID()) {
+ return NS_SUCCEEDED(PContentBridge::Bridge(this, cp));
+ }
+ }
+
+ // You can't bridge to a process you didn't open!
+ KillHard("BridgeToChildProcess");
+ return false;
+}
+
+static nsIDocShell* GetOpenerDocShellHelper(Element* aFrameElement)
+{
+ // Propagate the private-browsing status of the element's parent
+ // docshell to the remote docshell, via the chrome flags.
+ nsCOMPtr<Element> frameElement = do_QueryInterface(aFrameElement);
+ MOZ_ASSERT(frameElement);
+ nsPIDOMWindowOuter* win = frameElement->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;
+}
+
+bool
+ContentParent::RecvCreateGMPService()
+{
+ return PGMPService::Open(this);
+}
+
+bool
+ContentParent::RecvLoadPlugin(const uint32_t& aPluginId, nsresult* aRv, uint32_t* aRunID)
+{
+ *aRv = NS_OK;
+ return mozilla::plugins::SetupBridge(aPluginId, this, false, aRv, aRunID);
+}
+
+bool
+ContentParent::RecvUngrabPointer(const uint32_t& aTime)
+{
+#if !defined(MOZ_WIDGET_GTK)
+ NS_RUNTIMEABORT("This message only makes sense on GTK platforms");
+ return false;
+#else
+ gdk_pointer_ungrab(aTime);
+ return true;
+#endif
+}
+
+bool
+ContentParent::RecvRemovePermission(const IPC::Principal& aPrincipal,
+ const nsCString& aPermissionType,
+ nsresult* aRv) {
+ *aRv = Permissions::RemovePermission(aPrincipal, aPermissionType.get());
+ return true;
+}
+
+bool
+ContentParent::RecvConnectPluginBridge(const uint32_t& aPluginId, nsresult* aRv)
+{
+ *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;
+ return mozilla::plugins::SetupBridge(aPluginId, this, true, aRv, &dummy);
+}
+
+bool
+ContentParent::RecvGetBlocklistState(const uint32_t& aPluginId,
+ uint32_t* aState)
+{
+ *aState = nsIBlocklistService::STATE_BLOCKED;
+
+ RefPtr<nsPluginHost> pluginHost = nsPluginHost::GetInst();
+ if (!pluginHost) {
+ NS_WARNING("Plugin host not found");
+ return false;
+ }
+ nsPluginTag* tag = pluginHost->PluginWithId(aPluginId);
+
+ if (!tag) {
+ // Default state is blocked anyway
+ NS_WARNING("Plugin tag not found. This should never happen, but to avoid a crash we're forcibly blocking it");
+ return true;
+ }
+
+ return NS_SUCCEEDED(tag->GetBlocklistState(aState));
+}
+
+bool
+ContentParent::RecvFindPlugins(const uint32_t& aPluginEpoch,
+ nsresult* aRv,
+ nsTArray<PluginTag>* aPlugins,
+ uint32_t* aNewPluginEpoch)
+{
+ *aRv = mozilla::plugins::FindPluginsForContent(aPluginEpoch, aPlugins, aNewPluginEpoch);
+ return true;
+}
+
+/*static*/ TabParent*
+ContentParent::CreateBrowserOrApp(const TabContext& aContext,
+ Element* aFrameElement,
+ ContentParent* aOpenerContentParent,
+ bool aFreshProcess)
+{
+ PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
+
+ if (!sCanLaunchSubprocesses) {
+ return nullptr;
+ }
+
+ if (TabParent* parent = TabParent::GetNextTabParent()) {
+ parent->SetOwnerElement(aFrameElement);
+ return parent;
+ }
+
+ ProcessPriority initialPriority = GetInitialProcessPriority(aFrameElement);
+ bool isInContentProcess = !XRE_IsParentProcess();
+ TabId tabId;
+
+ nsIDocShell* docShell = GetOpenerDocShellHelper(aFrameElement);
+ TabId openerTabId;
+ if (docShell) {
+ openerTabId = TabParent::GetTabIdFrom(docShell);
+ }
+
+ if (aContext.IsMozBrowserElement() || !aContext.HasOwnApp()) {
+ RefPtr<nsIContentParent> constructorSender;
+ if (isInContentProcess) {
+ MOZ_ASSERT(aContext.IsMozBrowserElement());
+ constructorSender = CreateContentBridgeParent(aContext, initialPriority,
+ openerTabId, &tabId);
+ } else {
+ if (aOpenerContentParent) {
+ constructorSender = aOpenerContentParent;
+ } else {
+ constructorSender =
+ GetNewOrUsedBrowserProcess(aContext.IsMozBrowserElement(),
+ initialPriority,
+ nullptr,
+ aFreshProcess);
+ if (!constructorSender) {
+ return nullptr;
+ }
+ }
+ tabId = AllocateTabId(openerTabId,
+ aContext.AsIPCTabContext(),
+ constructorSender->ChildID());
+ }
+ if (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 (docShell->GetAffectPrivateSessionLifetime()) {
+ chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME;
+ }
+
+ if (tabId == 0) {
+ return nullptr;
+ }
+ RefPtr<TabParent> tp(new TabParent(constructorSender, tabId,
+ aContext, chromeFlags));
+ tp->SetInitedByParent();
+
+ PBrowserParent* browser =
+ constructorSender->SendPBrowserConstructor(
+ // DeallocPBrowserParent() releases this ref.
+ tp.forget().take(), tabId,
+ aContext.AsIPCTabContext(),
+ chromeFlags,
+ constructorSender->ChildID(),
+ constructorSender->IsForApp(),
+ constructorSender->IsForBrowser());
+
+ if (aFreshProcess) {
+ Unused << browser->SendSetFreshProcess();
+ }
+
+ if (browser) {
+ RefPtr<TabParent> constructedTabParent = TabParent::GetFrom(browser);
+ constructedTabParent->SetOwnerElement(aFrameElement);
+ return constructedTabParent;
+ }
+ }
+ return nullptr;
+ }
+
+ // If we got here, we have an app and we're not a browser element. ownApp
+ // shouldn't be null, because we otherwise would have gone into the
+ // !HasOwnApp() branch above.
+ RefPtr<nsIContentParent> parent;
+ bool reused = false;
+ bool tookPreallocated = false;
+ nsAutoString manifestURL;
+
+ if (isInContentProcess) {
+ parent = CreateContentBridgeParent(aContext,
+ initialPriority,
+ openerTabId,
+ &tabId);
+ }
+ else {
+ nsCOMPtr<mozIApplication> ownApp = aContext.GetOwnApp();
+
+ if (!sAppContentParents) {
+ sAppContentParents =
+ new nsDataHashtable<nsStringHashKey, ContentParent*>();
+ }
+
+ // Each app gets its own ContentParent instance unless it shares it with
+ // a parent app.
+ if (NS_FAILED(ownApp->GetManifestURL(manifestURL))) {
+ NS_ERROR("Failed to get manifest URL");
+ return nullptr;
+ }
+
+ RefPtr<ContentParent> p = sAppContentParents->Get(manifestURL);
+
+ if (!p && Preferences::GetBool("dom.ipc.reuse_parent_app")) {
+ nsAutoString parentAppManifestURL;
+ aFrameElement->GetAttr(kNameSpaceID_None,
+ nsGkAtoms::parentapp, parentAppManifestURL);
+ nsAdoptingString systemAppManifestURL =
+ Preferences::GetString("b2g.system_manifest_url");
+ nsCOMPtr<nsIAppsService> appsService =
+ do_GetService(APPS_SERVICE_CONTRACTID);
+ if (!parentAppManifestURL.IsEmpty() &&
+ !parentAppManifestURL.Equals(systemAppManifestURL) &&
+ appsService) {
+ nsCOMPtr<mozIApplication> parentApp;
+ nsCOMPtr<mozIApplication> app;
+ appsService->GetAppByManifestURL(parentAppManifestURL,
+ getter_AddRefs(parentApp));
+ appsService->GetAppByManifestURL(manifestURL,
+ getter_AddRefs(app));
+
+ // Only let certified apps re-use the same process.
+ unsigned short parentAppStatus = 0;
+ unsigned short appStatus = 0;
+ if (app &&
+ NS_SUCCEEDED(app->GetAppStatus(&appStatus)) &&
+ appStatus == nsIPrincipal::APP_STATUS_CERTIFIED &&
+ parentApp &&
+ NS_SUCCEEDED(parentApp->GetAppStatus(&parentAppStatus)) &&
+ parentAppStatus == nsIPrincipal::APP_STATUS_CERTIFIED) {
+ // Check if we can re-use the process of the parent app.
+ p = sAppContentParents->Get(parentAppManifestURL);
+ }
+ }
+ }
+
+ if (p) {
+ // Check that the process is still alive and set its priority.
+ // Hopefully the process won't die after this point, if this call
+ // succeeds.
+ if (!p->SetPriorityAndCheckIsAlive(initialPriority)) {
+ p = nullptr;
+ }
+ }
+
+ reused = !!p;
+ if (!p) {
+ p = GetNewOrPreallocatedAppProcess(ownApp, initialPriority, nullptr,
+ &tookPreallocated);
+ MOZ_ASSERT(p);
+ sAppContentParents->Put(manifestURL, p);
+ }
+ tabId = AllocateTabId(openerTabId, aContext.AsIPCTabContext(),
+ p->ChildID());
+ parent = static_cast<nsIContentParent*>(p);
+ }
+
+ if (!parent || (tabId == 0)) {
+ return nullptr;
+ }
+
+ uint32_t chromeFlags = 0;
+
+ RefPtr<TabParent> tp = new TabParent(parent, tabId, aContext, chromeFlags);
+ tp->SetInitedByParent();
+ PBrowserParent* browser = parent->SendPBrowserConstructor(
+ // DeallocPBrowserParent() releases this ref.
+ RefPtr<TabParent>(tp).forget().take(),
+ tabId,
+ aContext.AsIPCTabContext(),
+ chromeFlags,
+ parent->ChildID(),
+ parent->IsForApp(),
+ parent->IsForBrowser());
+
+ if (aFreshProcess) {
+ Unused << browser->SendSetFreshProcess();
+ }
+
+ if (browser) {
+ RefPtr<TabParent> constructedTabParent = TabParent::GetFrom(browser);
+ constructedTabParent->SetOwnerElement(aFrameElement);
+ }
+
+ if (isInContentProcess) {
+ // Just return directly without the following check in content process.
+ return TabParent::GetFrom(browser);
+ }
+
+ if (!browser) {
+ // We failed to actually start the PBrowser. This can happen if the
+ // other process has already died.
+ if (!reused) {
+ // Don't leave a broken ContentParent in the hashtable.
+ parent->AsContentParent()->KillHard("CreateBrowserOrApp");
+ sAppContentParents->Remove(manifestURL);
+ parent = nullptr;
+ }
+
+ // If we took the preallocated process and it was already dead, try
+ // again with a non-preallocated process. We can be sure this won't
+ // loop forever, because the next time through there will be no
+ // preallocated process to take.
+ if (tookPreallocated) {
+ return ContentParent::CreateBrowserOrApp(aContext, aFrameElement,
+ aOpenerContentParent);
+ }
+
+ // Otherwise just give up.
+ return nullptr;
+ }
+
+ return TabParent::GetFrom(browser);
+}
+
+/*static*/ ContentBridgeParent*
+ContentParent::CreateContentBridgeParent(const TabContext& aContext,
+ const hal::ProcessPriority& aPriority,
+ const TabId& aOpenerTabId,
+ /*out*/ TabId* aTabId)
+{
+ MOZ_ASSERT(aTabId);
+
+ ContentChild* child = ContentChild::GetSingleton();
+ ContentParentId cpId;
+ bool isForApp;
+ bool isForBrowser;
+ if (!child->SendCreateChildProcess(aContext.AsIPCTabContext(),
+ aPriority,
+ aOpenerTabId,
+ &cpId,
+ &isForApp,
+ &isForBrowser,
+ aTabId)) {
+ return nullptr;
+ }
+ if (cpId == 0) {
+ return nullptr;
+ }
+ if (!child->SendBridgeToChildProcess(cpId)) {
+ return nullptr;
+ }
+ ContentBridgeParent* parent = child->GetLastBridge();
+ parent->SetChildID(cpId);
+ parent->SetIsForApp(isForApp);
+ parent->SetIsForBrowser(isForBrowser);
+ return parent;
+}
+
+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::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);
+ }
+ }
+ Preferences::AddStrongObserver(this, "");
+ 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 (nsIPresShell::IsAccessibilityActive()) {
+#if !defined(XP_WIN)
+ Unused << SendActivateA11y(0);
+#else
+ // On Windows we currently only enable a11y in the content process
+ // for testing purposes.
+ if (Preferences::GetBool(kForceEnableE10sPref, false)) {
+ Unused << SendActivateA11y(a11y::AccessibleWrap::GetContentProcessIdFor(ChildID()));
+ }
+#endif
+ }
+#endif
+
+#ifdef MOZ_ENABLE_PROFILER_SPS
+ nsCOMPtr<nsIProfiler> profiler(do_GetService("@mozilla.org/tools/profiler;1"));
+ bool profilerActive = false;
+ DebugOnly<nsresult> rv = profiler->IsActive(&profilerActive);
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+ if (profilerActive) {
+ nsCOMPtr<nsIProfilerStartParams> currentProfilerParams;
+ rv = profiler->GetStartParams(getter_AddRefs(currentProfilerParams));
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+ nsCOMPtr<nsISupports> gatherer;
+ rv = profiler->GetProfileGatherer(getter_AddRefs(gatherer));
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+ mGatherer = static_cast<ProfileGatherer*>(gatherer.get());
+
+ StartProfiler(currentProfilerParams);
+ }
+#endif
+
+ RefPtr<GeckoMediaPluginServiceParent> gmps(GeckoMediaPluginServiceParent::GetSingleton());
+ gmps->UpdateContentProcessGMPCapabilities();
+}
+
+void
+ContentParent::ForwardKnownInfo()
+{
+ MOZ_ASSERT(mMetamorphosed);
+ if (!mMetamorphosed) {
+ return;
+ }
+#ifdef MOZ_WIDGET_GONK
+ InfallibleTArray<VolumeInfo> volumeInfo;
+ RefPtr<nsVolumeService> vs = nsVolumeService::GetSingleton();
+ if (vs) {
+ vs->GetVolumesForIPC(&volumeInfo);
+ Unused << SendVolumes(volumeInfo);
+ }
+#endif /* MOZ_WIDGET_GONK */
+}
+
+namespace {
+
+class RemoteWindowContext final : public nsIRemoteWindowContext
+ , public nsIInterfaceRequestor
+{
+public:
+ explicit RemoteWindowContext(TabParent* aTabParent)
+ : mTabParent(aTabParent)
+ {
+ }
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIINTERFACEREQUESTOR
+ NS_DECL_NSIREMOTEWINDOWCONTEXT
+
+private:
+ ~RemoteWindowContext();
+ RefPtr<TabParent> mTabParent;
+};
+
+NS_IMPL_ISUPPORTS(RemoteWindowContext, nsIRemoteWindowContext, nsIInterfaceRequestor)
+
+RemoteWindowContext::~RemoteWindowContext()
+{
+}
+
+NS_IMETHODIMP
+RemoteWindowContext::GetInterface(const nsIID& aIID, void** aSink)
+{
+ return QueryInterface(aIID, aSink);
+}
+
+NS_IMETHODIMP
+RemoteWindowContext::OpenURI(nsIURI* aURI)
+{
+ mTabParent->LoadURL(aURI);
+ return NS_OK;
+}
+
+} // namespace
+
+bool
+ContentParent::SetPriorityAndCheckIsAlive(ProcessPriority aPriority)
+{
+ ProcessPriorityManager::SetProcessPriority(this, aPriority);
+
+ // Now that we've set this process's priority, check whether the process is
+ // still alive. Hopefully we've set the priority to FOREGROUND*, so the
+ // process won't unexpectedly crash after this point!
+ //
+ // Bug 943174: use waitid() with WNOWAIT so that, if the process
+ // did exit, we won't consume its zombie and confuse the
+ // GeckoChildProcessHost dtor.
+#ifdef MOZ_WIDGET_GONK
+ siginfo_t info;
+ info.si_pid = 0;
+ if (waitid(P_PID, Pid(), &info, WNOWAIT | WNOHANG | WEXITED) == 0
+ && info.si_pid != 0) {
+ return false;
+ }
+#endif
+
+ return true;
+}
+
+// Helper for ContentParent::TransformPreallocatedIntoApp.
+static void
+TryGetNameFromManifestURL(const nsAString& aManifestURL,
+ nsAString& aName)
+{
+ aName.Truncate();
+ if (aManifestURL.IsEmpty() ||
+ aManifestURL == MAGIC_PREALLOCATED_APP_MANIFEST_URL) {
+ return;
+ }
+
+ nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
+ NS_ENSURE_TRUE_VOID(appsService);
+
+ nsCOMPtr<mozIApplication> app;
+ appsService->GetAppByManifestURL(aManifestURL, getter_AddRefs(app));
+
+ if (!app) {
+ return;
+ }
+
+ app->GetName(aName);
+}
+
+void
+ContentParent::TransformPreallocatedIntoApp(ContentParent* aOpener,
+ const nsAString& aAppManifestURL)
+{
+ MOZ_ASSERT(IsPreallocated());
+ mMetamorphosed = true;
+ mOpener = aOpener;
+ mAppManifestURL = aAppManifestURL;
+ TryGetNameFromManifestURL(aAppManifestURL, mAppName);
+}
+
+void
+ContentParent::TransformPreallocatedIntoBrowser(ContentParent* aOpener)
+{
+ // Reset mAppManifestURL, mIsForBrowser and mOSPrivileges for browser.
+ mMetamorphosed = true;
+ mOpener = aOpener;
+ mAppManifestURL.Truncate();
+ mIsForBrowser = true;
+}
+
+void
+ContentParent::ShutDownProcess(ShutDownMethod aMethod)
+{
+ // 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 (mIPCOpen && !mShutdownPending && 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* quotaManagerService = QuotaManagerService::Get()) {
+ quotaManagerService->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();
+ }
+
+ // NB: must MarkAsDead() here so that this isn't accidentally
+ // returned from Get*() while in the midst of shutdown.
+ MarkAsDead();
+
+ // 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();
+}
+
+bool
+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 true;
+}
+
+void
+ContentParent::ShutDownMessageManager()
+{
+ if (!mMessageManager) {
+ return;
+ }
+
+ mMessageManager->ReceiveMessage(
+ static_cast<nsIContentFrameMessageManager*>(mMessageManager.get()), nullptr,
+ CHILD_PROCESS_SHUTDOWN_MESSAGE, false,
+ nullptr, nullptr, nullptr, nullptr);
+
+ mMessageManager->Disconnect();
+ mMessageManager = nullptr;
+}
+
+void
+ContentParent::MarkAsDead()
+{
+ if (!mAppManifestURL.IsEmpty()) {
+ if (sAppContentParents) {
+ sAppContentParents->Remove(mAppManifestURL);
+ if (!sAppContentParents->Count()) {
+ delete sAppContentParents;
+ sAppContentParents = nullptr;
+ }
+ }
+ } else {
+ if (sNonAppContentParents) {
+ sNonAppContentParents->RemoveElement(this);
+ if (!sNonAppContentParents->Length()) {
+ delete sNonAppContentParents;
+ sNonAppContentParents = nullptr;
+ }
+ }
+
+ if (sLargeAllocationContentParents) {
+ sLargeAllocationContentParents->RemoveElement(this);
+ if (!sLargeAllocationContentParents->Length()) {
+ delete sLargeAllocationContentParents;
+ sLargeAllocationContentParents = nullptr;
+ }
+ }
+ }
+
+ if (sPrivateContent) {
+ sPrivateContent->RemoveElement(this);
+ if (!sPrivateContent->Length()) {
+ delete sPrivateContent;
+ sPrivateContent = nullptr;
+ }
+ }
+
+ mIsAlive = false;
+}
+
+void
+ContentParent::OnChannelError()
+{
+ RefPtr<ContentParent> content(this);
+ PContentParent::OnChannelError();
+}
+
+void
+ContentParent::OnChannelConnected(int32_t pid)
+{
+ SetOtherProcessId(pid);
+
+#if defined(ANDROID) || defined(LINUX)
+ // Check nice preference
+ int32_t nice = Preferences::GetInt("dom.ipc.content.nice", 0);
+
+ // Environment variable overrides preference
+ char* relativeNicenessStr = getenv("MOZ_CHILD_PROCESS_RELATIVE_NICENESS");
+ if (relativeNicenessStr) {
+ nice = atoi(relativeNicenessStr);
+ }
+
+ /* make the GUI thread have higher priority on single-cpu devices */
+ nsCOMPtr<nsIPropertyBag2> infoService = do_GetService(NS_SYSTEMINFO_CONTRACTID);
+ if (infoService) {
+ int32_t cpus;
+ nsresult rv = infoService->GetPropertyAsInt32(NS_LITERAL_STRING("cpucount"), &cpus);
+ if (NS_FAILED(rv)) {
+ cpus = 1;
+ }
+ if (nice != 0 && cpus == 1) {
+ setpriority(PRIO_PROCESS, pid, getpriority(PRIO_PROCESS, pid) + nice);
+ }
+ }
+#endif
+}
+
+void
+ContentParent::ProcessingError(Result aCode, const char* aReason)
+{
+ if (MsgDropped == aCode) {
+ return;
+ }
+ // Other errors are big deals.
+ KillHard(aReason);
+}
+
+/* static */
+bool
+ContentParent::AllocateLayerTreeId(TabParent* aTabParent, uint64_t* aId)
+{
+ return AllocateLayerTreeId(aTabParent->Manager()->AsContentParent(),
+ aTabParent, aTabParent->GetTabId(), aId);
+}
+
+/* static */
+bool
+ContentParent::AllocateLayerTreeId(ContentParent* aContent,
+ TabParent* aTopLevel, const TabId& aTabId,
+ uint64_t* aId)
+{
+ GPUProcessManager* gpu = GPUProcessManager::Get();
+
+ *aId = gpu->AllocateLayerTreeId();
+
+ if (!aContent || !aTopLevel) {
+ return false;
+ }
+
+ gpu->MapLayerTreeId(*aId, aContent->OtherPid());
+
+ if (!gfxPlatform::AsyncPanZoomEnabled()) {
+ return true;
+ }
+
+ return aContent->SendNotifyLayerAllocated(aTabId, *aId);
+}
+
+bool
+ContentParent::RecvAllocateLayerTreeId(const ContentParentId& aCpId,
+ const TabId& aTabId, uint64_t* aId)
+{
+ // Protect against spoofing by a compromised child. aCpId must either
+ // correspond to the process that this ContentParent represents or be a
+ // child of it.
+ ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+ if (ChildID() != aCpId) {
+ ContentParentId parent;
+ if (!cpm->GetParentProcessId(aCpId, &parent) ||
+ ChildID() != parent) {
+ return false;
+ }
+ }
+
+ // GetTopLevelTabParentByProcessAndTabId will make sure that aTabId
+ // lives in the process for aCpId.
+ RefPtr<ContentParent> contentParent = cpm->GetContentProcessById(aCpId);
+ RefPtr<TabParent> browserParent =
+ cpm->GetTopLevelTabParentByProcessAndTabId(aCpId, aTabId);
+ MOZ_ASSERT(contentParent && browserParent);
+
+ return AllocateLayerTreeId(contentParent, browserParent, aTabId, aId);
+}
+
+bool
+ContentParent::RecvDeallocateLayerTreeId(const uint64_t& aId)
+{
+ GPUProcessManager* gpu = GPUProcessManager::Get();
+
+ if (!gpu->IsLayerTreeIdMapped(aId, OtherPid()))
+ {
+ // You can't deallocate layer tree ids that you didn't allocate
+ KillHard("DeallocateLayerTreeId");
+ }
+
+ gpu->UnmapLayerTreeId(aId, OtherPid());
+
+ return true;
+}
+
+namespace {
+
+void
+DelayedDeleteSubprocess(GeckoChildProcessHost* aSubprocess)
+{
+ RefPtr<DeleteTask<GeckoChildProcessHost>> task = new DeleteTask<GeckoChildProcessHost>(aSubprocess);
+ XRE_GetIOMessageLoop()->PostTask(task.forget());
+}
+
+// This runnable only exists to delegate ownership of the
+// ContentParent to this runnable, until it's deleted by the event
+// system.
+struct DelayedDeleteContentParentTask : public Runnable
+{
+ explicit DelayedDeleteContentParentTask(ContentParent* aObj) : mObj(aObj) { }
+
+ // No-op
+ NS_IMETHOD Run() override { return NS_OK; }
+
+ RefPtr<ContentParent> mObj;
+};
+
+} // namespace
+
+void
+ContentParent::ActorDestroy(ActorDestroyReason why)
+{
+ 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.
+ mIPCOpen = false;
+
+ 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);
+
+ RefPtr<ContentParent> kungFuDeathGrip(this);
+ 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;
+
+#ifdef MOZ_ENABLE_PROFILER_SPS
+ if (mGatherer && !mProfile.IsEmpty()) {
+ mGatherer->OOPExitProfile(mProfile);
+ }
+#endif
+
+ if (obs) {
+ RefPtr<nsHashPropertyBag> props = new nsHashPropertyBag();
+
+ props->SetPropertyAsUint64(NS_LITERAL_STRING("childID"), mChildID);
+
+ if (AbnormalShutdown == why) {
+ Telemetry::Accumulate(Telemetry::SUBPROCESS_ABNORMAL_ABORT,
+ NS_LITERAL_CSTRING("content"), 1);
+
+ props->SetPropertyAsBool(NS_LITERAL_STRING("abnormal"), true);
+
+#ifdef MOZ_CRASHREPORTER
+ // There's a window in which child processes can crash
+ // after IPC is established, but before a crash reporter
+ // is created.
+ if (PCrashReporterParent* p = LoneManagedOrNullAsserts(ManagedPCrashReporterParent())) {
+ CrashReporterParent* crashReporter =
+ static_cast<CrashReporterParent*>(p);
+
+ // If we're an app process, always stomp the latest URI
+ // loaded in the child process with our manifest URL. We
+ // would rather associate the crashes with apps than
+ // random child windows loaded in them.
+ //
+ // XXX would be nice if we could get both ...
+ if (!mAppManifestURL.IsEmpty()) {
+ crashReporter->AnnotateCrashReport(NS_LITERAL_CSTRING("URL"),
+ NS_ConvertUTF16toUTF8(mAppManifestURL));
+ }
+
+ // if mCreatedPairedMinidumps is true, we've already generated
+ // parent/child dumps for dekstop crashes.
+ if (!mCreatedPairedMinidumps) {
+ crashReporter->GenerateCrashReport(this, nullptr);
+ }
+
+ nsAutoString dumpID(crashReporter->ChildDumpID());
+ props->SetPropertyAsAString(NS_LITERAL_STRING("dumpID"), dumpID);
+ }
+#endif
+ }
+ 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<nsIIdleService> idleService =
+ do_GetService("@mozilla.org/widget/idleservice;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();
+
+ MessageLoop::current()->
+ PostTask(NewRunnableFunction(DelayedDeleteSubprocess, mSubprocess));
+ mSubprocess = nullptr;
+
+ // IPDL rules require actors to live on past ActorDestroy, but it
+ // may be that the kungFuDeathGrip above is the last reference to
+ // |this|. If so, when we go out of scope here, we're deleted and
+ // all hell breaks loose.
+ //
+ // This runnable ensures that a reference to |this| lives on at
+ // least until after the current task finishes running.
+ NS_DispatchToCurrentThread(new DelayedDeleteContentParentTask(this));
+
+ ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+ nsTArray<ContentParentId> childIDArray =
+ cpm->GetAllChildProcessById(this->ChildID());
+
+ // Destroy any processes created by this ContentParent
+ for(uint32_t i = 0; i < childIDArray.Length(); i++) {
+ ContentParent* cp = cpm->GetContentProcessById(childIDArray[i]);
+ MessageLoop::current()->PostTask(NewRunnableMethod
+ <ShutDownMethod>(cp,
+ &ContentParent::ShutDownProcess,
+ SEND_SHUTDOWN_MESSAGE));
+ }
+ 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) {
+ nsHostObjectProtocolHandler::RemoveDataEntry(mBlobURLs[i]);
+ }
+
+ mBlobURLs.Clear();
+
+#if defined(XP_WIN32) && defined(ACCESSIBILITY)
+ a11y::AccessibleWrap::ReleaseContentProcessIdFor(ChildID());
+#endif
+}
+
+void
+ContentParent::NotifyTabDestroying(const TabId& aTabId,
+ const ContentParentId& aCpId)
+{
+ if (XRE_IsParentProcess()) {
+ // 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.
+ ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+ ContentParent* cp = cpm->GetContentProcessById(aCpId);
+ if (!cp) {
+ return;
+ }
+ ++cp->mNumDestroyingTabs;
+ nsTArray<TabId> tabIds = cpm->GetTabParentsByProcessId(aCpId);
+ if (static_cast<size_t>(cp->mNumDestroyingTabs) != tabIds.Length()) {
+ return;
+ }
+
+ uint32_t numberOfParents = sNonAppContentParents ? sNonAppContentParents->Length() : 0;
+ int32_t processesToKeepAlive = Preferences::GetInt("dom.ipc.keepProcessesAlive", 0);
+ if (!cp->mLargeAllocationProcess && static_cast<int32_t>(numberOfParents) <= processesToKeepAlive) {
+ return;
+ }
+
+ // We're dying now, so prevent this content process from being
+ // recycled during its shutdown procedure.
+ cp->MarkAsDead();
+ cp->StartForceKillTimer();
+ } else {
+ ContentChild::GetSingleton()->SendNotifyTabDestroying(aTabId, aCpId);
+ }
+}
+
+void
+ContentParent::StartForceKillTimer()
+{
+ if (mForceKillTimer || !mIPCOpen) {
+ return;
+ }
+
+ int32_t timeoutSecs = Preferences::GetInt("dom.ipc.tabs.shutdownTimeoutSecs", 5);
+ if (timeoutSecs > 0) {
+ mForceKillTimer = do_CreateInstance("@mozilla.org/timer;1");
+ MOZ_ASSERT(mForceKillTimer);
+ mForceKillTimer->InitWithFuncCallback(ContentParent::ForceKillTimerCallback,
+ this,
+ timeoutSecs * 1000,
+ nsITimer::TYPE_ONE_SHOT);
+ }
+}
+
+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.
+ ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+ nsTArray<TabId> tabIds = cpm->GetTabParentsByProcessId(this->ChildID());
+
+ // We might want to keep alive some content processes for testing, because of performance
+ // reasons, but we don't want to alter behavior if the pref is not set.
+ uint32_t numberOfParents = sNonAppContentParents ? sNonAppContentParents->Length() : 0;
+ int32_t processesToKeepAlive = Preferences::GetInt("dom.ipc.keepProcessesAlive", 0);
+ bool shouldKeepAliveAny = !mLargeAllocationProcess && processesToKeepAlive > 0;
+ bool shouldKeepAliveThis = shouldKeepAliveAny && static_cast<int32_t>(numberOfParents) <= processesToKeepAlive;
+
+ if (tabIds.Length() == 1 && !shouldKeepAliveThis) {
+ // In the case of normal shutdown, send a shutdown message to child to
+ // allow it to perform shutdown tasks.
+ MessageLoop::current()->PostTask(NewRunnableMethod
+ <ShutDownMethod>(this,
+ &ContentParent::ShutDownProcess,
+ SEND_SHUTDOWN_MESSAGE));
+ }
+}
+
+jsipc::CPOWManager*
+ContentParent::GetCPOWManager()
+{
+ if (PJavaScriptParent* p = LoneManagedOrNullAsserts(ManagedPJavaScriptParent())) {
+ return CPOWManagerFor(p);
+ }
+ return nullptr;
+}
+
+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);
+}
+
+void
+ContentParent::InitializeMembers()
+{
+ mSubprocess = nullptr;
+ mChildID = gContentChildID++;
+ mGeolocationWatchID = -1;
+ mNumDestroyingTabs = 0;
+ mIsAlive = true;
+ mMetamorphosed = false;
+ mSendPermissionUpdates = false;
+ mCalledClose = false;
+ mCalledKillHard = false;
+ mCreatedPairedMinidumps = false;
+ mShutdownPending = false;
+ mIPCOpen = true;
+ mHangMonitorActor = nullptr;
+}
+
+bool
+ContentParent::LaunchSubprocess(ProcessPriority aInitialPriority /* = PROCESS_PRIORITY_FOREGROUND */)
+{
+ PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
+
+ std::vector<std::string> extraArgs;
+
+ if (gSafeMode) {
+ extraArgs.push_back("-safeMode");
+ }
+
+ if (!mSubprocess->LaunchAndWaitForProcessHandle(extraArgs)) {
+ MarkAsDead();
+ return false;
+ }
+
+ Open(mSubprocess->GetChannel(),
+ base::GetProcId(mSubprocess->GetChildProcessHandle()));
+
+ InitInternal(aInitialPriority,
+ true, /* Setup off-main thread compositing */
+ true /* Send registered chrome */);
+
+ ContentProcessManager::GetSingleton()->AddContentProcess(this);
+
+ ProcessHangMonitor::AddProcess(this);
+
+ // Set a reply timeout for CPOWs.
+ SetReplyTimeoutMs(Preferences::GetInt("dom.ipc.cpow.timeout", 0));
+
+ return true;
+}
+
+ContentParent::ContentParent(mozIApplication* aApp,
+ ContentParent* aOpener,
+ bool aIsForBrowser,
+ bool aIsForPreallocated)
+ : nsIContentParent()
+ , mOpener(aOpener)
+ , mIsForBrowser(aIsForBrowser)
+ , mLargeAllocationProcess(false)
+{
+ InitializeMembers(); // Perform common initialization.
+
+ // No more than one of !!aApp, aIsForBrowser, aIsForPreallocated should be
+ // true.
+ MOZ_ASSERT(!!aApp + aIsForBrowser + aIsForPreallocated <= 1);
+
+ mMetamorphosed = true;
+
+ // Insert ourselves into the global linked list of ContentParent objects.
+ if (!sContentParents) {
+ sContentParents = new LinkedList<ContentParent>();
+ }
+ sContentParents->insertBack(this);
+
+ if (aApp) {
+ aApp->GetManifestURL(mAppManifestURL);
+ aApp->GetName(mAppName);
+ } else if (aIsForPreallocated) {
+ mAppManifestURL = MAGIC_PREALLOCATED_APP_MANIFEST_URL;
+ }
+
+ // From this point on, NS_WARNING, NS_ASSERTION, etc. should print out the
+ // PID along with the warning.
+ nsDebugImpl::SetMultiprocessMode("Parent");
+
+#if defined(XP_WIN) && !defined(MOZ_B2G)
+ // 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!");
+ ChildPrivileges privs = base::PRIVILEGES_DEFAULT;
+ mSubprocess = new GeckoChildProcessHost(GeckoProcessType_Content, privs);
+}
+
+ContentParent::~ContentParent()
+{
+ if (mForceKillTimer) {
+ mForceKillTimer->Cancel();
+ }
+
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+
+ // We should be removed from all these lists in ActorDestroy.
+ MOZ_ASSERT(!sPrivateContent || !sPrivateContent->Contains(this));
+ if (mAppManifestURL.IsEmpty()) {
+ MOZ_ASSERT((!sNonAppContentParents ||
+ !sNonAppContentParents->Contains(this)) &&
+ (!sLargeAllocationContentParents ||
+ !sLargeAllocationContentParents->Contains(this)));
+ } else {
+ // In general, we expect sAppContentParents->Get(mAppManifestURL) to be
+ // nullptr. But it could be that we created another ContentParent for
+ // this app after we did this->ActorDestroy(), so the right check is
+ // that sAppContentParents->Get(mAppManifestURL) != this.
+ MOZ_ASSERT(!sAppContentParents ||
+ sAppContentParents->Get(mAppManifestURL) != this);
+ }
+}
+
+void
+ContentParent::InitInternal(ProcessPriority aInitialPriority,
+ bool aSetupOffMainThreadCompositing,
+ bool aSendRegisteredChrome)
+{
+ if (aSendRegisteredChrome) {
+ nsCOMPtr<nsIChromeRegistry> registrySvc = nsChromeRegistry::GetService();
+ nsChromeRegistryChrome* chromeRegistry =
+ static_cast<nsChromeRegistryChrome*>(registrySvc.get());
+ chromeRegistry->SendRegisteredChrome(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);
+
+ // Sending all information to content process.
+ Unused << SendAppInfo(version, buildID, name, UAName, ID, vendor);
+ }
+
+ // Initialize the message manager (and load delayed scripts) now that we
+ // have established communications with the child.
+ mMessageManager->InitWithCallback(this);
+
+ // 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);
+
+ if (aSetupOffMainThreadCompositing) {
+ // 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.)
+ bool useOffMainThreadCompositing = !!CompositorThreadHolder::Loop();
+ if (useOffMainThreadCompositing) {
+ GPUProcessManager* gpm = GPUProcessManager::Get();
+
+ Endpoint<PCompositorBridgeChild> compositor;
+ Endpoint<PImageBridgeChild> imageBridge;
+ Endpoint<PVRManagerChild> vrBridge;
+ Endpoint<PVideoDecoderManagerChild> videoManager;
+
+ DebugOnly<bool> opened = gpm->CreateContentBridges(
+ OtherPid(),
+ &compositor,
+ &imageBridge,
+ &vrBridge,
+ &videoManager);
+ MOZ_ASSERT(opened);
+
+ Unused << SendInitRendering(
+ Move(compositor),
+ Move(imageBridge),
+ Move(vrBridge),
+ Move(videoManager));
+
+ gpm->AddListener(this);
+ }
+ }
+
+ if (gAppData) {
+ // Sending all information to content process.
+ Unused << SendAppInit();
+ }
+
+ nsStyleSheetService *sheetService = nsStyleSheetService::GetInstance();
+ if (sheetService) {
+ // This looks like a lot of work, but in a normal browser session we just
+ // send two loads.
+
+ for (StyleSheet* sheet : *sheetService->AgentStyleSheets()) {
+ URIParams uri;
+ SerializeURI(sheet->GetSheetURI(), uri);
+ Unused << SendLoadAndRegisterSheet(uri, nsIStyleSheetService::AGENT_SHEET);
+ }
+
+ for (StyleSheet* sheet : *sheetService->UserStyleSheets()) {
+ URIParams uri;
+ SerializeURI(sheet->GetSheetURI(), uri);
+ Unused << SendLoadAndRegisterSheet(uri, nsIStyleSheetService::USER_SHEET);
+ }
+
+ for (StyleSheet* sheet : *sheetService->AuthorStyleSheets()) {
+ URIParams uri;
+ SerializeURI(sheet->GetSheetURI(), uri);
+ Unused << SendLoadAndRegisterSheet(uri, nsIStyleSheetService::AUTHOR_SHEET);
+ }
+ }
+
+#ifdef MOZ_CONTENT_SANDBOX
+ bool shouldSandbox = true;
+ MaybeFileDesc brokerFd = void_t();
+#ifdef XP_LINUX
+ // 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.
+ shouldSandbox = (Preferences::GetInt("security.sandbox.content.level") > 0) &&
+ !PR_GetEnv("MOZ_DISABLE_CONTENT_SANDBOX");
+
+ if (shouldSandbox) {
+ MOZ_ASSERT(!mSandboxBroker);
+ UniquePtr<SandboxBroker::Policy> policy =
+ sSandboxBrokerPolicyFactory->GetContentPolicy(Pid());
+ if (policy) {
+ brokerFd = FileDescriptor();
+ mSandboxBroker = SandboxBroker::Create(Move(policy), Pid(), brokerFd);
+ if (!mSandboxBroker) {
+ KillHard("SandboxBroker::Create failed");
+ return;
+ }
+ MOZ_ASSERT(static_cast<const FileDescriptor&>(brokerFd).IsValid());
+ }
+ }
+#endif
+ if (shouldSandbox && !SendSetProcessSandbox(brokerFd)) {
+ KillHard("SandboxInitFailed");
+ }
+#endif
+#if defined(XP_WIN)
+ // Send the info needed to join the browser process's audio session.
+ nsID id;
+ nsString sessionName;
+ nsString iconPath;
+ if (NS_SUCCEEDED(mozilla::widget::GetAudioSessionData(id, sessionName,
+ iconPath))) {
+ Unused << SendSetAudioSessionData(id, sessionName, iconPath);
+ }
+#endif
+
+ {
+ RefPtr<ServiceWorkerRegistrar> swr = ServiceWorkerRegistrar::Get();
+ MOZ_ASSERT(swr);
+
+ nsTArray<ServiceWorkerRegistrationData> registrations;
+ swr->GetRegistrations(registrations);
+ Unused << SendInitServiceWorkers(ServiceWorkerConfiguration(registrations));
+ }
+
+ {
+ nsTArray<BlobURLRegistrationData> registrations;
+ if (nsHostObjectProtocolHandler::GetAllBlobURLEntries(registrations,
+ this)) {
+ Unused << SendInitBlobURLs(registrations);
+ }
+ }
+}
+
+bool
+ContentParent::IsAlive() const
+{
+ return mIsAlive;
+}
+
+bool
+ContentParent::IsForApp() const
+{
+ return !mAppManifestURL.IsEmpty();
+}
+
+int32_t
+ContentParent::Pid() const
+{
+ if (!mSubprocess || !mSubprocess->GetChildProcessHandle()) {
+ return -1;
+ }
+ return base::GetProcId(mSubprocess->GetChildProcessHandle());
+}
+
+bool
+ContentParent::RecvReadPrefsArray(InfallibleTArray<PrefSetting>* aPrefs)
+{
+ Preferences::GetPreferences(aPrefs);
+ return true;
+}
+
+bool
+ContentParent::RecvGetGfxVars(InfallibleTArray<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 true;
+}
+
+void
+ContentParent::OnCompositorUnexpectedShutdown()
+{
+ GPUProcessManager* gpm = GPUProcessManager::Get();
+
+ Endpoint<PCompositorBridgeChild> compositor;
+ Endpoint<PImageBridgeChild> imageBridge;
+ Endpoint<PVRManagerChild> vrBridge;
+ Endpoint<PVideoDecoderManagerChild> videoManager;
+
+ DebugOnly<bool> opened = gpm->CreateContentBridges(
+ OtherPid(),
+ &compositor,
+ &imageBridge,
+ &vrBridge,
+ &videoManager);
+ MOZ_ASSERT(opened);
+
+ Unused << SendReinitRendering(
+ Move(compositor),
+ Move(imageBridge),
+ Move(vrBridge),
+ Move(videoManager));
+}
+
+void
+ContentParent::OnVarChanged(const GfxVarUpdate& aVar)
+{
+ if (!mIPCOpen) {
+ return;
+ }
+ Unused << SendVarUpdate(aVar);
+}
+
+bool
+ContentParent::RecvReadFontList(InfallibleTArray<FontListEntry>* retValue)
+{
+#ifdef ANDROID
+ gfxAndroidPlatform::GetPlatform()->GetSystemFontList(retValue);
+#endif
+ return true;
+}
+
+bool
+ContentParent::RecvReadDataStorageArray(const nsString& aFilename,
+ InfallibleTArray<DataStorageItem>* aValues)
+{
+ // If we're shutting down, the DataStorage object may have been cleared
+ // already, and setting it up is pointless anyways since we're about to die.
+ if (mShutdownPending) {
+ return true;
+ }
+
+ // Ensure the SSS is initialized before we try to use its storage.
+ nsCOMPtr<nsISiteSecurityService> sss = do_GetService("@mozilla.org/ssservice;1");
+
+ RefPtr<DataStorage> storage = DataStorage::Get(aFilename);
+ storage->GetAll(aValues);
+ return true;
+}
+
+bool
+ContentParent::RecvReadPermissions(InfallibleTArray<IPC::Permission>* aPermissions)
+{
+#ifdef MOZ_PERMISSIONS
+ nsCOMPtr<nsIPermissionManager> permissionManagerIface =
+ services::GetPermissionManager();
+ nsPermissionManager* permissionManager =
+ static_cast<nsPermissionManager*>(permissionManagerIface.get());
+ MOZ_ASSERT(permissionManager,
+ "We have no permissionManager in the Chrome process !");
+
+ nsCOMPtr<nsISimpleEnumerator> enumerator;
+ DebugOnly<nsresult> rv = permissionManager->GetEnumerator(getter_AddRefs(enumerator));
+ MOZ_ASSERT(NS_SUCCEEDED(rv), "Could not get enumerator!");
+ while(1) {
+ bool hasMore;
+ enumerator->HasMoreElements(&hasMore);
+ if (!hasMore)
+ break;
+
+ nsCOMPtr<nsISupports> supp;
+ enumerator->GetNext(getter_AddRefs(supp));
+ nsCOMPtr<nsIPermission> perm = do_QueryInterface(supp);
+
+ nsCOMPtr<nsIPrincipal> principal;
+ perm->GetPrincipal(getter_AddRefs(principal));
+ nsCString origin;
+ if (principal) {
+ principal->GetOrigin(origin);
+ }
+ nsCString type;
+ perm->GetType(type);
+ uint32_t capability;
+ perm->GetCapability(&capability);
+ uint32_t expireType;
+ perm->GetExpireType(&expireType);
+ int64_t expireTime;
+ perm->GetExpireTime(&expireTime);
+
+ aPermissions->AppendElement(IPC::Permission(origin, type,
+ capability, expireType,
+ expireTime));
+ }
+
+ // Ask for future changes
+ mSendPermissionUpdates = true;
+#endif
+
+ return true;
+}
+
+bool
+ContentParent::RecvSetClipboard(const IPCDataTransfer& aDataTransfer,
+ const bool& aIsPrivateData,
+ const IPC::Principal& aRequestingPrincipal,
+ const int32_t& aWhichClipboard)
+{
+ nsresult rv;
+ nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
+ NS_ENSURE_SUCCESS(rv, true);
+
+ nsCOMPtr<nsITransferable> trans =
+ do_CreateInstance("@mozilla.org/widget/transferable;1", &rv);
+ NS_ENSURE_SUCCESS(rv, true);
+ trans->Init(nullptr);
+
+ rv = nsContentUtils::IPCTransferableToTransferable(aDataTransfer,
+ aIsPrivateData,
+ aRequestingPrincipal,
+ trans, this, nullptr);
+ NS_ENSURE_SUCCESS(rv, true);
+
+ clipboard->SetData(trans, nullptr, aWhichClipboard);
+ return true;
+}
+
+bool
+ContentParent::RecvGetClipboard(nsTArray<nsCString>&& aTypes,
+ const int32_t& aWhichClipboard,
+ IPCDataTransfer* aDataTransfer)
+{
+ nsresult rv;
+ nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
+ NS_ENSURE_SUCCESS(rv, true);
+
+ nsCOMPtr<nsITransferable> trans = do_CreateInstance("@mozilla.org/widget/transferable;1", &rv);
+ NS_ENSURE_SUCCESS(rv, true);
+ trans->Init(nullptr);
+
+ 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 true;
+}
+
+bool
+ContentParent::RecvEmptyClipboard(const int32_t& aWhichClipboard)
+{
+ nsresult rv;
+ nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
+ NS_ENSURE_SUCCESS(rv, true);
+
+ clipboard->EmptyClipboard(aWhichClipboard);
+
+ return true;
+}
+
+bool
+ContentParent::RecvClipboardHasType(nsTArray<nsCString>&& aTypes,
+ const int32_t& aWhichClipboard,
+ bool* aHasType)
+{
+ nsresult rv;
+ nsCOMPtr<nsIClipboard> clipboard(do_GetService(kCClipboardCID, &rv));
+ NS_ENSURE_SUCCESS(rv, true);
+
+ const char** typesChrs = new const char *[aTypes.Length()];
+ for (uint32_t t = 0; t < aTypes.Length(); t++) {
+ typesChrs[t] = aTypes[t].get();
+ }
+
+ clipboard->HasDataMatchingFlavors(typesChrs, aTypes.Length(),
+ aWhichClipboard, aHasType);
+
+ delete [] typesChrs;
+ return true;
+}
+
+bool
+ContentParent::RecvGetSystemColors(const uint32_t& colorsCount,
+ InfallibleTArray<uint32_t>* colors)
+{
+#ifdef MOZ_WIDGET_ANDROID
+ NS_ASSERTION(AndroidBridge::Bridge() != nullptr, "AndroidBridge is not available");
+ if (AndroidBridge::Bridge() == nullptr) {
+ // Do not fail - the colors won't be right, but it's not critical
+ return true;
+ }
+
+ colors->AppendElements(colorsCount);
+
+ // The array elements correspond to the members of AndroidSystemColors structure,
+ // so just pass the pointer to the elements buffer
+ AndroidBridge::Bridge()->GetSystemColors((AndroidSystemColors*)colors->Elements());
+#endif
+ return true;
+}
+
+bool
+ContentParent::RecvGetIconForExtension(const nsCString& aFileExt,
+ const uint32_t& aIconSize,
+ InfallibleTArray<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 true;
+ }
+
+ bits->AppendElements(aIconSize * aIconSize * 4);
+
+ AndroidBridge::Bridge()->GetIconForExtension(aFileExt, aIconSize, bits->Elements());
+#endif
+ return true;
+}
+
+bool
+ContentParent::RecvGetShowPasswordSetting(bool* showPassword)
+{
+ // default behavior is to show the last password character
+ *showPassword = true;
+#ifdef MOZ_WIDGET_ANDROID
+ NS_ASSERTION(AndroidBridge::Bridge() != nullptr, "AndroidBridge is not available");
+
+ *showPassword = java::GeckoAppShell::GetShowPasswordSetting();
+#endif
+ return true;
+}
+
+bool
+ContentParent::RecvFirstIdle()
+{
+ // When the ContentChild goes idle, it sends us a FirstIdle message
+ // which we use as a good time to prelaunch another process. If we
+ // prelaunch any sooner than this, then we'll be competing with the
+ // child process and slowing it down.
+ PreallocatedProcessManager::AllocateAfterDelay();
+ return true;
+}
+
+bool
+ContentParent::RecvAudioChannelChangeDefVolChannel(const int32_t& aChannel,
+ const bool& aHidden)
+{
+ RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
+ MOZ_ASSERT(service);
+ service->SetDefaultVolumeControlChannelInternal(aChannel, aHidden, mChildID);
+ return true;
+}
+
+bool
+ContentParent::RecvAudioChannelServiceStatus(
+ const bool& aTelephonyChannel,
+ const bool& aContentOrNormalChannel,
+ const bool& aAnyChannel)
+{
+ RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
+ MOZ_ASSERT(service);
+
+ service->ChildStatusReceived(mChildID, aTelephonyChannel,
+ aContentOrNormalChannel, aAnyChannel);
+ return true;
+}
+
+// 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(nsIContentParent)
+ NS_INTERFACE_MAP_ENTRY(nsIObserver)
+ NS_INTERFACE_MAP_ENTRY(nsIDOMGeoPositionCallback)
+ NS_INTERFACE_MAP_ENTRY(nsIDOMGeoPositionErrorCallback)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
+NS_INTERFACE_MAP_END
+
+NS_IMETHODIMP
+ContentParent::Observe(nsISupports* aSubject,
+ const char* aTopic,
+ const char16_t* aData)
+{
+ if (mSubprocess && (!strcmp(aTopic, "profile-before-change") ||
+ !strcmp(aTopic, "xpcom-shutdown"))) {
+ // Okay to call ShutDownProcess multiple times.
+ ShutDownProcess(SEND_SHUTDOWN_MESSAGE);
+
+ // Wait for shutdown to complete, so that we receive any shutdown
+ // data (e.g. telemetry) from the child before we quit.
+ // This loop terminate prematurely based on mForceKillTimer.
+ while (mIPCOpen && !mCalledKillHard) {
+ NS_ProcessNextEvent(nullptr, true);
+ }
+ NS_ASSERTION(!mSubprocess, "Close should have nulled mSubprocess");
+ }
+
+#ifdef MOZ_ENABLE_PROFILER_SPS
+ // Need to do this before the mIsAlive check to avoid missing profiles.
+ if (!strcmp(aTopic, "profiler-subprocess-gather")) {
+ if (mGatherer) {
+ mGatherer->WillGatherOOPProfile();
+ if (mIsAlive && mSubprocess) {
+ Unused << SendGatherProfile();
+ }
+ }
+ }
+ else if (!strcmp(aTopic, "profiler-subprocess")) {
+ nsCOMPtr<nsIProfileSaveEvent> pse = do_QueryInterface(aSubject);
+ if (pse) {
+ if (!mProfile.IsEmpty()) {
+ pse->AddSubProfile(mProfile.get());
+ mProfile.Truncate();
+ }
+ }
+ }
+#endif
+
+ if (!mIsAlive || !mSubprocess)
+ return NS_OK;
+
+ // listening for memory pressure event
+ if (!strcmp(aTopic, "memory-pressure") &&
+ !StringEndsWith(nsDependentString(aData),
+ NS_LITERAL_STRING("-no-forward"))) {
+ Unused << SendFlushMemory(nsDependentString(aData));
+ }
+ // listening for remotePrefs...
+ else if (!strcmp(aTopic, "nsPref:changed")) {
+ // We know prefs are ASCII here.
+ NS_LossyConvertUTF16toASCII strData(aData);
+
+ PrefSetting pref(strData, null_t(), null_t());
+ Preferences::GetPreference(&pref);
+ if (!SendPreferenceUpdate(pref)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ }
+ else if (!strcmp(aTopic, NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC)) {
+ NS_ConvertUTF16toUTF8 dataStr(aData);
+ const char *offline = dataStr.get();
+ if (!SendSetOffline(!strcmp(offline, "true") ? true : false)) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ }
+ else if (!strcmp(aTopic, NS_IPC_IOSERVICE_SET_CONNECTIVITY_TOPIC)) {
+ if (!SendSetConnectivity(NS_LITERAL_STRING("true").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, "last-pb-context-exited")) {
+ Unused << SendLastPrivateDocShellDestroyed();
+ }
+#ifdef MOZ_WIDGET_GONK
+ else if(!strcmp(aTopic, NS_VOLUME_STATE_CHANGED)) {
+ nsCOMPtr<nsIVolume> vol = do_QueryInterface(aSubject);
+ if (!vol) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ nsString volName;
+ nsString mountPoint;
+ int32_t state;
+ int32_t mountGeneration;
+ bool isMediaPresent;
+ bool isSharing;
+ bool isFormatting;
+ bool isFake;
+ bool isUnmounting;
+ bool isRemovable;
+ bool isHotSwappable;
+
+ vol->GetName(volName);
+ vol->GetMountPoint(mountPoint);
+ vol->GetState(&state);
+ vol->GetMountGeneration(&mountGeneration);
+ vol->GetIsMediaPresent(&isMediaPresent);
+ vol->GetIsSharing(&isSharing);
+ vol->GetIsFormatting(&isFormatting);
+ vol->GetIsFake(&isFake);
+ vol->GetIsUnmounting(&isUnmounting);
+ vol->GetIsRemovable(&isRemovable);
+ vol->GetIsHotSwappable(&isHotSwappable);
+
+ Unused << SendFileSystemUpdate(volName, mountPoint, state,
+ mountGeneration, isMediaPresent,
+ isSharing, isFormatting, isFake,
+ isUnmounting, isRemovable, isHotSwappable);
+ } else if (!strcmp(aTopic, "phone-state-changed")) {
+ nsString state(aData);
+ Unused << SendNotifyPhoneStateChange(state);
+ }
+ else if(!strcmp(aTopic, NS_VOLUME_REMOVED)) {
+ nsString volName(aData);
+ Unused << SendVolumeRemoved(volName);
+ }
+#endif
+#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)
+ Unused << SendActivateA11y(0);
+#else
+ // On Windows we currently only enable a11y in the content process
+ // for testing purposes.
+ if (Preferences::GetBool(kForceEnableE10sPref, false)) {
+ Unused << SendActivateA11y(a11y::AccessibleWrap::GetContentProcessIdFor(ChildID()));
+ }
+#endif
+ } else {
+ // If possible, shut down accessibility in content process when
+ // accessibility gets shutdown in chrome process.
+ Unused << SendShutdownA11y();
+ }
+ }
+#endif
+#ifdef MOZ_ENABLE_PROFILER_SPS
+ else if (!strcmp(aTopic, "profiler-started")) {
+ nsCOMPtr<nsIProfilerStartParams> params(do_QueryInterface(aSubject));
+ StartProfiler(params);
+ }
+ else if (!strcmp(aTopic, "profiler-stopped")) {
+ mGatherer = nullptr;
+ Unused << SendStopProfiler();
+ }
+ else if (!strcmp(aTopic, "profiler-paused")) {
+ Unused << SendPauseProfiler(true);
+ }
+ else if (!strcmp(aTopic, "profiler-resumed")) {
+ Unused << SendPauseProfiler(false);
+ }
+#endif
+ else if (!strcmp(aTopic, "cacheservice:empty-cache")) {
+ Unused << SendNotifyEmptyHTTPCache();
+ }
+ return NS_OK;
+}
+
+PGMPServiceParent*
+ContentParent::AllocPGMPServiceParent(mozilla::ipc::Transport* aTransport,
+ base::ProcessId aOtherProcess)
+{
+ return GMPServiceParent::Create(aTransport, aOtherProcess);
+}
+
+PBackgroundParent*
+ContentParent::AllocPBackgroundParent(Transport* aTransport,
+ ProcessId aOtherProcess)
+{
+ return BackgroundParent::Alloc(this, aTransport, aOtherProcess);
+}
+
+PProcessHangMonitorParent*
+ContentParent::AllocPProcessHangMonitorParent(Transport* aTransport,
+ ProcessId aOtherProcess)
+{
+ mHangMonitorActor = CreateHangMonitorParent(this, aTransport, aOtherProcess);
+ return mHangMonitorActor;
+}
+
+bool
+ContentParent::RecvGetProcessAttributes(ContentParentId* aCpId,
+ bool* aIsForApp, bool* aIsForBrowser)
+{
+ *aCpId = mChildID;
+ *aIsForApp = IsForApp();
+ *aIsForBrowser = mIsForBrowser;
+
+ return true;
+}
+
+bool
+ContentParent::RecvGetXPCOMProcessAttributes(bool* aIsOffline,
+ bool* aIsConnected,
+ int32_t* aCaptivePortalState,
+ bool* aIsLangRTL,
+ bool* aHaveBidiKeyboards,
+ InfallibleTArray<nsString>* dictionaries,
+ ClipboardCapabilities* clipboardCaps,
+ DomainPolicyClone* domainPolicy,
+ StructuredCloneData* aInitialData,
+ OptionalURIParams* aUserContentCSSURL)
+{
+ nsCOMPtr<nsIIOService> io(do_GetIOService());
+ MOZ_ASSERT(io, "No IO service?");
+ DebugOnly<nsresult> rv = io->GetOffline(aIsOffline);
+ MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed getting offline?");
+
+ rv = io->GetConnectivity(aIsConnected);
+ MOZ_ASSERT(NS_SUCCEEDED(rv), "Failed getting connectivity?");
+
+ *aCaptivePortalState = nsICaptivePortalService::UNKNOWN;
+ nsCOMPtr<nsICaptivePortalService> cps = do_GetService(NS_CAPTIVEPORTAL_CONTRACTID);
+ if (cps) {
+ cps->GetState(aCaptivePortalState);
+ }
+
+ nsIBidiKeyboard* bidi = nsContentUtils::GetBidiKeyboard();
+
+ *aIsLangRTL = false;
+ *aHaveBidiKeyboards = false;
+ if (bidi) {
+ bidi->IsLangRTL(aIsLangRTL);
+ bidi->GetHaveBidiKeyboards(aHaveBidiKeyboards);
+ }
+
+ nsCOMPtr<nsISpellChecker> spellChecker(do_GetService(NS_SPELLCHECKER_CONTRACTID));
+ MOZ_ASSERT(spellChecker, "No spell checker?");
+
+ spellChecker->GetDictionaryList(dictionaries);
+
+ nsCOMPtr<nsIClipboard> clipboard(do_GetService("@mozilla.org/widget/clipboard;1"));
+ MOZ_ASSERT(clipboard, "No clipboard?");
+
+ rv = clipboard->SupportsSelectionClipboard(&clipboardCaps->supportsSelectionClipboard());
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+ rv = clipboard->SupportsFindClipboard(&clipboardCaps->supportsFindClipboard());
+ MOZ_ASSERT(NS_SUCCEEDED(rv));
+
+ // Let's copy the domain policy from the parent to the child (if it's active).
+ nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
+ NS_ENSURE_TRUE(ssm, false);
+ ssm->CloneDomainPolicy(domainPolicy);
+
+ if (nsFrameMessageManager* mm = nsFrameMessageManager::sParentProcessManager) {
+ AutoJSAPI jsapi;
+ if (NS_WARN_IF(!jsapi.Init(xpc::PrivilegedJunkScope()))) {
+ return false;
+ }
+ JS::RootedValue init(jsapi.cx());
+ nsresult result = mm->GetInitialProcessData(jsapi.cx(), &init);
+ if (NS_FAILED(result)) {
+ return false;
+ }
+
+ ErrorResult rv;
+ aInitialData->Write(jsapi.cx(), init, rv);
+ if (NS_WARN_IF(rv.Failed())) {
+ rv.SuppressException();
+ return false;
+ }
+ }
+
+ // Content processes have no permission to access profile directory, so we
+ // send the file URL instead.
+ StyleSheet* ucs = nsLayoutStylesheetCache::For(StyleBackendType::Gecko)->UserContentSheet();
+ if (ucs) {
+ SerializeURI(ucs->GetSheetURI(), *aUserContentCSSURL);
+ } else {
+ SerializeURI(nullptr, *aUserContentCSSURL);
+ }
+
+ return true;
+}
+
+mozilla::jsipc::PJavaScriptParent *
+ContentParent::AllocPJavaScriptParent()
+{
+ MOZ_ASSERT(ManagedPJavaScriptParent().IsEmpty());
+ return nsIContentParent::AllocPJavaScriptParent();
+}
+
+bool
+ContentParent::DeallocPJavaScriptParent(PJavaScriptParent *parent)
+{
+ return nsIContentParent::DeallocPJavaScriptParent(parent);
+}
+
+PBrowserParent*
+ContentParent::AllocPBrowserParent(const TabId& aTabId,
+ const IPCTabContext& aContext,
+ const uint32_t& aChromeFlags,
+ const ContentParentId& aCpId,
+ const bool& aIsForApp,
+ const bool& aIsForBrowser)
+{
+ return nsIContentParent::AllocPBrowserParent(aTabId,
+ aContext,
+ aChromeFlags,
+ aCpId,
+ aIsForApp,
+ aIsForBrowser);
+}
+
+bool
+ContentParent::DeallocPBrowserParent(PBrowserParent* frame)
+{
+ return nsIContentParent::DeallocPBrowserParent(frame);
+}
+
+PBlobParent*
+ContentParent::AllocPBlobParent(const BlobConstructorParams& aParams)
+{
+ return nsIContentParent::AllocPBlobParent(aParams);
+}
+
+bool
+ContentParent::DeallocPBlobParent(PBlobParent* aActor)
+{
+ return nsIContentParent::DeallocPBlobParent(aActor);
+}
+
+bool
+ContentParent::RecvPBlobConstructor(PBlobParent* aActor,
+ const BlobConstructorParams& aParams)
+{
+ const ParentBlobConstructorParams& params = aParams.get_ParentBlobConstructorParams();
+ if (params.blobParams().type() == AnyBlobConstructorParams::TKnownBlobConstructorParams) {
+ return aActor->SendCreatedFromKnownBlob();
+ }
+
+ return true;
+}
+
+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");
+}
+
+// WARNING: aReason appears in telemetry, so any new value passed in requires
+// data review.
+void
+ContentParent::KillHard(const char* aReason)
+{
+ PROFILER_LABEL_FUNC(js::ProfileEntry::Category::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;
+
+#if defined(MOZ_CRASHREPORTER) && !defined(MOZ_B2G)
+ // 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.
+ if (PCrashReporterParent* p = LoneManagedOrNullAsserts(ManagedPCrashReporterParent())) {
+ CrashReporterParent* crashReporter =
+ static_cast<CrashReporterParent*>(p);
+ // 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");
+ crashReporter->AnnotateCrashReport(
+ NS_LITERAL_CSTRING("additional_minidumps"),
+ additionalDumps);
+ nsDependentCString reason(aReason);
+ crashReporter->AnnotateCrashReport(
+ NS_LITERAL_CSTRING("ipc_channel_error"),
+ reason);
+
+ // Generate the report and insert into the queue for submittal.
+ mCreatedPairedMinidumps = crashReporter->GenerateCompleteMinidump(this);
+
+ Telemetry::Accumulate(Telemetry::SUBPROCESS_KILL_HARD, reason, 1);
+ }
+#endif
+ 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) {
+ mSubprocess->SetAlreadyDead();
+ }
+
+ // EnsureProcessTerminated has responsibilty for closing otherProcessHandle.
+ XRE_GetIOMessageLoop()->PostTask(
+ NewRunnableFunction(&ProcessWatcher::EnsureProcessTerminated,
+ otherProcessHandle, /*force=*/true));
+}
+
+bool
+ContentParent::IsPreallocated() const
+{
+ return mAppManifestURL == MAGIC_PREALLOCATED_APP_MANIFEST_URL;
+}
+
+void
+ContentParent::FriendlyName(nsAString& aName, bool aAnonymize)
+{
+ aName.Truncate();
+ if (IsPreallocated()) {
+ aName.AssignLiteral("(Preallocated)");
+ } else if (mIsForBrowser) {
+ aName.AssignLiteral("Browser");
+ } else if (aAnonymize) {
+ aName.AssignLiteral("<anonymized-name>");
+ } else if (!mAppName.IsEmpty()) {
+ aName = mAppName;
+ } else if (!mAppManifestURL.IsEmpty()) {
+ aName.AssignLiteral("Unknown app: ");
+ aName.Append(mAppManifestURL);
+ } else {
+ aName.AssignLiteral("???");
+ }
+}
+
+PCrashReporterParent*
+ContentParent::AllocPCrashReporterParent(const NativeThreadId& tid,
+ const uint32_t& processType)
+{
+#ifdef MOZ_CRASHREPORTER
+ return new CrashReporterParent();
+#else
+ return nullptr;
+#endif
+}
+
+bool
+ContentParent::RecvPCrashReporterConstructor(PCrashReporterParent* actor,
+ const NativeThreadId& tid,
+ const uint32_t& processType)
+{
+ static_cast<CrashReporterParent*>(actor)->SetChildData(tid, processType);
+ return true;
+}
+
+bool
+ContentParent::DeallocPCrashReporterParent(PCrashReporterParent* crashreporter)
+{
+ delete crashreporter;
+ return true;
+}
+
+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;
+}
+
+PMemoryReportRequestParent*
+ContentParent::AllocPMemoryReportRequestParent(const uint32_t& aGeneration,
+ const bool &aAnonymize,
+ const bool &aMinimizeMemoryUsage,
+ const MaybeFileDesc &aDMDFile)
+{
+ MemoryReportRequestParent* parent =
+ new MemoryReportRequestParent(aGeneration);
+ return parent;
+}
+
+bool
+ContentParent::DeallocPMemoryReportRequestParent(PMemoryReportRequestParent* actor)
+{
+ delete actor;
+ return true;
+}
+
+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;
+}
+
+PNeckoParent*
+ContentParent::AllocPNeckoParent()
+{
+ return new NeckoParent();
+}
+
+bool
+ContentParent::DeallocPNeckoParent(PNeckoParent* necko)
+{
+ delete necko;
+ return true;
+}
+
+PPrintingParent*
+ContentParent::AllocPPrintingParent()
+{
+#ifdef NS_PRINTING
+ MOZ_ASSERT(!mPrintingParent,
+ "Only one PrintingParent should be created per process.");
+
+ // Create the printing singleton for this process.
+ mPrintingParent = new PrintingParent();
+ 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_ASSERT(mPrintingParent == printing,
+ "Only one PrintingParent should have been created per process.");
+
+ 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
+
+PSendStreamParent*
+ContentParent::AllocPSendStreamParent()
+{
+ return nsIContentParent::AllocPSendStreamParent();
+}
+
+bool
+ContentParent::DeallocPSendStreamParent(PSendStreamParent* aActor)
+{
+ return nsIContentParent::DeallocPSendStreamParent(aActor);
+}
+
+PScreenManagerParent*
+ContentParent::AllocPScreenManagerParent(uint32_t* aNumberOfScreens,
+ float* aSystemDefaultScale,
+ bool* aSuccess)
+{
+ return new ScreenManagerParent(aNumberOfScreens, aSystemDefaultScale, aSuccess);
+}
+
+bool
+ContentParent::DeallocPScreenManagerParent(PScreenManagerParent* aActor)
+{
+ delete aActor;
+ return true;
+}
+
+PPSMContentDownloaderParent*
+ContentParent::AllocPPSMContentDownloaderParent(const uint32_t& aCertType)
+{
+ RefPtr<PSMContentDownloaderParent> downloader =
+ new PSMContentDownloaderParent(aCertType);
+ return downloader.forget().take();
+}
+
+bool
+ContentParent::DeallocPPSMContentDownloaderParent(PPSMContentDownloaderParent* aListener)
+{
+ auto* listener = static_cast<PSMContentDownloaderParent*>(aListener);
+ RefPtr<PSMContentDownloaderParent> downloader = dont_AddRef(listener);
+ return true;
+}
+
+PExternalHelperAppParent*
+ContentParent::AllocPExternalHelperAppParent(const OptionalURIParams& uri,
+ const nsCString& aMimeContentType,
+ const nsCString& aContentDisposition,
+ const uint32_t& aContentDispositionHint,
+ const nsString& aContentDispositionFilename,
+ const bool& aForceSave,
+ const int64_t& aContentLength,
+ const bool& aWasFileChannel,
+ const OptionalURIParams& aReferrer,
+ PBrowserParent* aBrowser)
+{
+ ExternalHelperAppParent *parent =
+ new ExternalHelperAppParent(uri, aContentLength, aWasFileChannel);
+ parent->AddRef();
+ parent->Init(this,
+ aMimeContentType,
+ aContentDisposition,
+ aContentDispositionHint,
+ aContentDispositionFilename,
+ aForceSave,
+ aReferrer,
+ aBrowser);
+ return parent;
+}
+
+bool
+ContentParent::DeallocPExternalHelperAppParent(PExternalHelperAppParent* aService)
+{
+ ExternalHelperAppParent *parent = static_cast<ExternalHelperAppParent *>(aService);
+ parent->Release();
+ return true;
+}
+
+PHandlerServiceParent*
+ContentParent::AllocPHandlerServiceParent()
+{
+ HandlerServiceParent* actor = new HandlerServiceParent();
+ actor->AddRef();
+ return actor;
+}
+
+bool
+ContentParent::DeallocPHandlerServiceParent(PHandlerServiceParent* aHandlerServiceParent)
+{
+ static_cast<HandlerServiceParent*>(aHandlerServiceParent)->Release();
+ return true;
+}
+
+media::PMediaParent*
+ContentParent::AllocPMediaParent()
+{
+ return media::AllocPMediaParent();
+}
+
+bool
+ContentParent::DeallocPMediaParent(media::PMediaParent *aActor)
+{
+ return media::DeallocPMediaParent(aActor);
+}
+
+PStorageParent*
+ContentParent::AllocPStorageParent()
+{
+ return new DOMStorageDBParent();
+}
+
+bool
+ContentParent::DeallocPStorageParent(PStorageParent* aActor)
+{
+ DOMStorageDBParent* child = static_cast<DOMStorageDBParent*>(aActor);
+ child->ReleaseIPDLReference();
+ 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;
+}
+
+bool
+ContentParent::RecvPPresentationConstructor(PPresentationParent* aActor)
+{
+ return static_cast<PresentationParent*>(aActor)->Init(mChildID);
+}
+
+PFlyWebPublishedServerParent*
+ContentParent::AllocPFlyWebPublishedServerParent(const nsString& name,
+ const FlyWebPublishOptions& params)
+{
+ RefPtr<FlyWebPublishedServerParent> actor =
+ new FlyWebPublishedServerParent(name, params);
+ return actor.forget().take();
+}
+
+bool
+ContentParent::DeallocPFlyWebPublishedServerParent(PFlyWebPublishedServerParent* aActor)
+{
+ RefPtr<FlyWebPublishedServerParent> actor =
+ dont_AddRef(static_cast<FlyWebPublishedServerParent*>(aActor));
+ return true;
+}
+
+PSpeechSynthesisParent*
+ContentParent::AllocPSpeechSynthesisParent()
+{
+#ifdef MOZ_WEBSPEECH
+ return new mozilla::dom::SpeechSynthesisParent();
+#else
+ return nullptr;
+#endif
+}
+
+bool
+ContentParent::DeallocPSpeechSynthesisParent(PSpeechSynthesisParent* aActor)
+{
+#ifdef MOZ_WEBSPEECH
+ delete aActor;
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool
+ContentParent::RecvPSpeechSynthesisConstructor(PSpeechSynthesisParent* aActor)
+{
+#ifdef MOZ_WEBSPEECH
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool
+ContentParent::RecvSpeakerManagerGetSpeakerStatus(bool* aValue)
+{
+#ifdef MOZ_WIDGET_GONK
+ *aValue = false;
+ RefPtr<SpeakerManagerService> service =
+ SpeakerManagerService::GetOrCreateSpeakerManagerService();
+ MOZ_ASSERT(service);
+
+ *aValue = service->GetSpeakerStatus();
+ return true;
+#endif
+ return false;
+}
+
+bool
+ContentParent::RecvSpeakerManagerForceSpeaker(const bool& aEnable)
+{
+#ifdef MOZ_WIDGET_GONK
+ RefPtr<SpeakerManagerService> service =
+ SpeakerManagerService::GetOrCreateSpeakerManagerService();
+ MOZ_ASSERT(service);
+ service->ForceSpeaker(aEnable, mChildID);
+
+ return true;
+#endif
+ return false;
+}
+
+bool
+ContentParent::RecvStartVisitedQuery(const URIParams& aURI)
+{
+ nsCOMPtr<nsIURI> newURI = DeserializeURI(aURI);
+ if (!newURI) {
+ return false;
+ }
+ nsCOMPtr<IHistory> history = services::GetHistoryService();
+ if (history) {
+ history->RegisterVisitedCallback(newURI, nullptr);
+ }
+ return true;
+}
+
+
+bool
+ContentParent::RecvVisitURI(const URIParams& uri,
+ const OptionalURIParams& referrer,
+ const uint32_t& flags)
+{
+ nsCOMPtr<nsIURI> ourURI = DeserializeURI(uri);
+ if (!ourURI) {
+ return false;
+ }
+ nsCOMPtr<nsIURI> ourReferrer = DeserializeURI(referrer);
+ nsCOMPtr<IHistory> history = services::GetHistoryService();
+ if (history) {
+ history->VisitURI(ourURI, ourReferrer, flags);
+ }
+ return true;
+}
+
+
+bool
+ContentParent::RecvSetURITitle(const URIParams& uri,
+ const nsString& title)
+{
+ nsCOMPtr<nsIURI> ourURI = DeserializeURI(uri);
+ if (!ourURI) {
+ return false;
+ }
+ nsCOMPtr<IHistory> history = services::GetHistoryService();
+ if (history) {
+ history->SetURITitle(ourURI, title);
+ }
+ return true;
+}
+
+bool
+ContentParent::RecvNSSU2FTokenIsCompatibleVersion(const nsString& aVersion,
+ bool* aIsCompatible)
+{
+ MOZ_ASSERT(aIsCompatible);
+
+ nsCOMPtr<nsINSSU2FToken> nssToken(do_GetService(NS_NSSU2FTOKEN_CONTRACTID));
+ if (NS_WARN_IF(!nssToken)) {
+ return false;
+ }
+
+ nsresult rv = nssToken->IsCompatibleVersion(aVersion, aIsCompatible);
+ return NS_SUCCEEDED(rv);
+}
+
+bool
+ContentParent::RecvNSSU2FTokenIsRegistered(nsTArray<uint8_t>&& aKeyHandle,
+ bool* aIsValidKeyHandle)
+{
+ MOZ_ASSERT(aIsValidKeyHandle);
+
+ nsCOMPtr<nsINSSU2FToken> nssToken(do_GetService(NS_NSSU2FTOKEN_CONTRACTID));
+ if (NS_WARN_IF(!nssToken)) {
+ return false;
+ }
+
+ nsresult rv = nssToken->IsRegistered(aKeyHandle.Elements(), aKeyHandle.Length(),
+ aIsValidKeyHandle);
+ return NS_SUCCEEDED(rv);
+}
+
+bool
+ContentParent::RecvNSSU2FTokenRegister(nsTArray<uint8_t>&& aApplication,
+ nsTArray<uint8_t>&& aChallenge,
+ nsTArray<uint8_t>* aRegistration)
+{
+ MOZ_ASSERT(aRegistration);
+
+ nsCOMPtr<nsINSSU2FToken> nssToken(do_GetService(NS_NSSU2FTOKEN_CONTRACTID));
+ if (NS_WARN_IF(!nssToken)) {
+ return false;
+ }
+ uint8_t* buffer;
+ uint32_t bufferlen;
+ nsresult rv = nssToken->Register(aApplication.Elements(), aApplication.Length(),
+ aChallenge.Elements(), aChallenge.Length(),
+ &buffer, &bufferlen);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return false;
+ }
+
+ MOZ_ASSERT(buffer);
+ aRegistration->ReplaceElementsAt(0, aRegistration->Length(), buffer, bufferlen);
+ free(buffer);
+ return NS_SUCCEEDED(rv);
+}
+
+bool
+ContentParent::RecvNSSU2FTokenSign(nsTArray<uint8_t>&& aApplication,
+ nsTArray<uint8_t>&& aChallenge,
+ nsTArray<uint8_t>&& aKeyHandle,
+ nsTArray<uint8_t>* aSignature)
+{
+ MOZ_ASSERT(aSignature);
+
+ nsCOMPtr<nsINSSU2FToken> nssToken(do_GetService(NS_NSSU2FTOKEN_CONTRACTID));
+ if (NS_WARN_IF(!nssToken)) {
+ return false;
+ }
+ uint8_t* buffer;
+ uint32_t bufferlen;
+ nsresult rv = nssToken->Sign(aApplication.Elements(), aApplication.Length(),
+ aChallenge.Elements(), aChallenge.Length(),
+ aKeyHandle.Elements(), aKeyHandle.Length(),
+ &buffer, &bufferlen);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return false;
+ }
+
+ MOZ_ASSERT(buffer);
+ aSignature->ReplaceElementsAt(0, aSignature->Length(), buffer, bufferlen);
+ free(buffer);
+ return NS_SUCCEEDED(rv);
+}
+
+bool
+ContentParent::RecvGetLookAndFeelCache(nsTArray<LookAndFeelInt>* aLookAndFeelIntCache)
+{
+ *aLookAndFeelIntCache = LookAndFeel::GetIntCache();
+ return true;
+}
+
+bool
+ContentParent::RecvIsSecureURI(const uint32_t& type,
+ const URIParams& uri,
+ const uint32_t& flags,
+ bool* isSecureURI)
+{
+ nsCOMPtr<nsISiteSecurityService> sss(do_GetService(NS_SSSERVICE_CONTRACTID));
+ if (!sss) {
+ return false;
+ }
+ nsCOMPtr<nsIURI> ourURI = DeserializeURI(uri);
+ if (!ourURI) {
+ return false;
+ }
+ nsresult rv = sss->IsSecureURI(type, ourURI, flags, nullptr, isSecureURI);
+ return NS_SUCCEEDED(rv);
+}
+
+bool
+ContentParent::RecvAccumulateMixedContentHSTS(const URIParams& aURI, const bool& aActive, const bool& aHSTSPriming)
+{
+ nsCOMPtr<nsIURI> ourURI = DeserializeURI(aURI);
+ if (!ourURI) {
+ return false;
+ }
+ nsMixedContentBlocker::AccumulateMixedContentHSTS(ourURI, aActive, aHSTSPriming);
+ return true;
+}
+
+bool
+ContentParent::RecvLoadURIExternal(const URIParams& uri,
+ PBrowserParent* windowContext)
+{
+ nsCOMPtr<nsIExternalProtocolService> extProtService(do_GetService(NS_EXTERNALPROTOCOLSERVICE_CONTRACTID));
+ if (!extProtService) {
+ return true;
+ }
+ nsCOMPtr<nsIURI> ourURI = DeserializeURI(uri);
+ if (!ourURI) {
+ return false;
+ }
+
+ RefPtr<RemoteWindowContext> context =
+ new RemoteWindowContext(static_cast<TabParent*>(windowContext));
+ extProtService->LoadURI(ourURI, context);
+ return true;
+}
+
+bool
+ContentParent::RecvExtProtocolChannelConnectParent(const uint32_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, true);
+
+ nsCOMPtr<nsIParentChannel> parent = do_QueryInterface(channel, &rv);
+ NS_ENSURE_SUCCESS(rv, true);
+
+ // The channel itself is its own (faked) parent, link it.
+ rv = NS_LinkRedirectChannels(registrarId, parent, getter_AddRefs(channel));
+ NS_ENSURE_SUCCESS(rv, true);
+
+ // 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 true;
+}
+
+bool
+ContentParent::HasNotificationPermission(const IPC::Principal& aPrincipal)
+{
+ return true;
+}
+
+bool
+ContentParent::RecvShowAlert(const AlertNotificationType& aAlert)
+{
+ nsCOMPtr<nsIAlertNotification> alert(dont_AddRef(aAlert));
+ if (NS_WARN_IF(!alert)) {
+ return true;
+ }
+
+ nsCOMPtr<nsIPrincipal> principal;
+ nsresult rv = alert->GetPrincipal(getter_AddRefs(principal));
+ if (NS_WARN_IF(NS_FAILED(rv)) ||
+ !HasNotificationPermission(IPC::Principal(principal))) {
+
+ return true;
+ }
+
+ nsCOMPtr<nsIAlertsService> sysAlerts(do_GetService(NS_ALERTSERVICE_CONTRACTID));
+ if (sysAlerts) {
+ sysAlerts->ShowAlert(alert, this);
+ }
+ return true;
+}
+
+bool
+ContentParent::RecvCloseAlert(const nsString& aName,
+ const IPC::Principal& aPrincipal)
+{
+ if (!HasNotificationPermission(aPrincipal)) {
+ return true;
+ }
+
+ nsCOMPtr<nsIAlertsService> sysAlerts(do_GetService(NS_ALERTSERVICE_CONTRACTID));
+ if (sysAlerts) {
+ sysAlerts->CloseAlert(aName, aPrincipal);
+ }
+
+ return true;
+}
+
+bool
+ContentParent::RecvDisableNotifications(const IPC::Principal& aPrincipal)
+{
+ if (HasNotificationPermission(aPrincipal)) {
+ Unused << Notification::RemovePermission(aPrincipal);
+ }
+ return true;
+}
+
+bool
+ContentParent::RecvOpenNotificationSettings(const IPC::Principal& aPrincipal)
+{
+ if (HasNotificationPermission(aPrincipal)) {
+ Unused << Notification::OpenSettings(aPrincipal);
+ }
+ return true;
+}
+
+bool
+ContentParent::RecvSyncMessage(const nsString& aMsg,
+ const ClonedMessageData& aData,
+ InfallibleTArray<CpowEntry>&& aCpows,
+ const IPC::Principal& aPrincipal,
+ nsTArray<StructuredCloneData>* aRetvals)
+{
+ return nsIContentParent::RecvSyncMessage(aMsg, aData, Move(aCpows),
+ aPrincipal, aRetvals);
+}
+
+bool
+ContentParent::RecvRpcMessage(const nsString& aMsg,
+ const ClonedMessageData& aData,
+ InfallibleTArray<CpowEntry>&& aCpows,
+ const IPC::Principal& aPrincipal,
+ nsTArray<StructuredCloneData>* aRetvals)
+{
+ return nsIContentParent::RecvRpcMessage(aMsg, aData, Move(aCpows), aPrincipal,
+ aRetvals);
+}
+
+bool
+ContentParent::RecvAsyncMessage(const nsString& aMsg,
+ InfallibleTArray<CpowEntry>&& aCpows,
+ const IPC::Principal& aPrincipal,
+ const ClonedMessageData& aData)
+{
+ return nsIContentParent::RecvAsyncMessage(aMsg, Move(aCpows), aPrincipal,
+ aData);
+}
+
+static int32_t
+AddGeolocationListener(nsIDOMGeoPositionCallback* watcher,
+ nsIDOMGeoPositionErrorCallback* errorCallBack,
+ bool highAccuracy)
+{
+ nsCOMPtr<nsIDOMGeoGeolocation> geo = do_GetService("@mozilla.org/geolocation;1");
+ if (!geo) {
+ return -1;
+ }
+
+ UniquePtr<PositionOptions> options = MakeUnique<PositionOptions>();
+ options->mTimeout = 0;
+ options->mMaximumAge = 0;
+ options->mEnableHighAccuracy = highAccuracy;
+ int32_t retval = 1;
+ geo->WatchPosition(watcher, errorCallBack, Move(options), &retval);
+ return retval;
+}
+
+bool
+ContentParent::RecvAddGeolocationListener(const IPC::Principal& aPrincipal,
+ 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 true;
+}
+
+bool
+ContentParent::RecvRemoveGeolocationListener()
+{
+ if (mGeolocationWatchID != -1) {
+ nsCOMPtr<nsIDOMGeoGeolocation> geo = do_GetService("@mozilla.org/geolocation;1");
+ if (!geo) {
+ return true;
+ }
+ geo->ClearWatch(mGeolocationWatchID);
+ mGeolocationWatchID = -1;
+ }
+ return true;
+}
+
+bool
+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 true;
+}
+
+NS_IMETHODIMP
+ContentParent::HandleEvent(nsIDOMGeoPosition* postion)
+{
+ Unused << SendGeolocationUpdate(GeoPosition(postion));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+ContentParent::HandleEvent(nsIDOMGeoPositionError* postionError)
+{
+ int16_t errorCode;
+ nsresult rv;
+ rv = postionError->GetCode(&errorCode);
+ NS_ENSURE_SUCCESS(rv,rv);
+ Unused << SendGeolocationError(errorCode);
+ 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();
+}
+
+bool
+ContentParent::RecvConsoleMessage(const nsString& aMessage)
+{
+ RefPtr<nsConsoleService> consoleService = GetConsoleService();
+ if (!consoleService) {
+ return true;
+ }
+
+ RefPtr<nsConsoleMessage> msg(new nsConsoleMessage(aMessage.get()));
+ consoleService->LogMessageWithMode(msg, nsConsoleService::SuppressLog);
+ return true;
+}
+
+bool
+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)
+{
+ RefPtr<nsConsoleService> consoleService = GetConsoleService();
+ if (!consoleService) {
+ return true;
+ }
+
+ nsCOMPtr<nsIScriptError> msg(do_CreateInstance(NS_SCRIPTERROR_CONTRACTID));
+ nsresult rv = msg->Init(aMessage, aSourceName, aSourceLine,
+ aLineNumber, aColNumber, aFlags, aCategory.get());
+ if (NS_FAILED(rv))
+ return true;
+
+ consoleService->LogMessageWithMode(msg, nsConsoleService::SuppressLog);
+ return true;
+}
+
+bool
+ContentParent::RecvPrivateDocShellsExist(const bool& aExist)
+{
+ if (!sPrivateContent)
+ sPrivateContent = new nsTArray<ContentParent*>();
+ 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);
+ delete sPrivateContent;
+ sPrivateContent = nullptr;
+ }
+ }
+ return true;
+}
+
+bool
+ContentParent::DoLoadMessageManagerScript(const nsAString& aURL,
+ bool aRunInGlobalScope)
+{
+ MOZ_ASSERT(!aRunInGlobalScope);
+ return SendLoadProcessScript(nsString(aURL));
+}
+
+nsresult
+ContentParent::DoSendAsyncMessage(JSContext* aCx,
+ const nsAString& aMessage,
+ StructuredCloneData& aHelper,
+ JS::Handle<JSObject *> aCpows,
+ nsIPrincipal* aPrincipal)
+{
+ ClonedMessageData data;
+ if (!BuildClonedMessageDataForParent(this, aHelper, data)) {
+ return NS_ERROR_DOM_DATA_CLONE_ERR;
+ }
+ InfallibleTArray<CpowEntry> cpows;
+ jsipc::CPOWManager* mgr = GetCPOWManager();
+ if (aCpows && (!mgr || !mgr->Wrap(aCx, aCpows, &cpows))) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ if (!SendAsyncMessage(nsString(aMessage), cpows, Principal(aPrincipal), data)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ return NS_OK;
+}
+
+bool
+ContentParent::CheckPermission(const nsAString& aPermission)
+{
+ return AssertAppProcessPermission(this, NS_ConvertUTF16toUTF8(aPermission).get());
+}
+
+bool
+ContentParent::CheckManifestURL(const nsAString& aManifestURL)
+{
+ return AssertAppProcessManifestURL(this, NS_ConvertUTF16toUTF8(aManifestURL).get());
+}
+
+bool
+ContentParent::CheckAppHasPermission(const nsAString& aPermission)
+{
+ return AssertAppHasPermission(this, NS_ConvertUTF16toUTF8(aPermission).get());
+}
+
+bool
+ContentParent::CheckAppHasStatus(unsigned short aStatus)
+{
+ return AssertAppHasStatus(this, aStatus);
+}
+
+bool
+ContentParent::KillChild()
+{
+ KillHard("KillChild");
+ return true;
+}
+
+PBlobParent*
+ContentParent::SendPBlobConstructor(PBlobParent* aActor,
+ const BlobConstructorParams& aParams)
+{
+ return PContentParent::SendPBlobConstructor(aActor, aParams);
+}
+
+PBrowserParent*
+ContentParent::SendPBrowserConstructor(PBrowserParent* aActor,
+ const TabId& aTabId,
+ const IPCTabContext& aContext,
+ const uint32_t& aChromeFlags,
+ const ContentParentId& aCpId,
+ const bool& aIsForApp,
+ const bool& aIsForBrowser)
+{
+ return PContentParent::SendPBrowserConstructor(aActor,
+ aTabId,
+ aContext,
+ aChromeFlags,
+ aCpId,
+ aIsForApp,
+ aIsForBrowser);
+}
+
+bool
+ContentParent::RecvCreateFakeVolume(const nsString& fsName,
+ const nsString& mountPoint)
+{
+#ifdef MOZ_WIDGET_GONK
+ nsresult rv;
+ nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID, &rv);
+ if (vs) {
+ vs->CreateFakeVolume(fsName, mountPoint);
+ }
+ return true;
+#else
+ NS_WARNING("ContentParent::RecvCreateFakeVolume shouldn't be called when MOZ_WIDGET_GONK is not defined");
+ return false;
+#endif
+}
+
+bool
+ContentParent::RecvSetFakeVolumeState(const nsString& fsName, const int32_t& fsState)
+{
+#ifdef MOZ_WIDGET_GONK
+ nsresult rv;
+ nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID, &rv);
+ if (vs) {
+ vs->SetFakeVolumeState(fsName, fsState);
+ }
+ return true;
+#else
+ NS_WARNING("ContentParent::RecvSetFakeVolumeState shouldn't be called when MOZ_WIDGET_GONK is not defined");
+ return false;
+#endif
+}
+
+bool
+ContentParent::RecvRemoveFakeVolume(const nsString& fsName)
+{
+#ifdef MOZ_WIDGET_GONK
+ nsresult rv;
+ nsCOMPtr<nsIVolumeService> vs = do_GetService(NS_VOLUMESERVICE_CONTRACTID, &rv);
+ if (vs) {
+ vs->RemoveFakeVolume(fsName);
+ }
+ return true;
+#else
+ NS_WARNING("ContentParent::RecvRemoveFakeVolume shouldn't be called when MOZ_WIDGET_GONK is not defined");
+ return false;
+#endif
+}
+
+bool
+ContentParent::RecvKeywordToURI(const nsCString& aKeyword,
+ nsString* aProviderName,
+ OptionalInputStreamParams* aPostData,
+ OptionalURIParams* aURI)
+{
+ *aPostData = void_t();
+ *aURI = void_t();
+
+ nsCOMPtr<nsIURIFixup> fixup = do_GetService(NS_URIFIXUP_CONTRACTID);
+ if (!fixup) {
+ return true;
+ }
+
+ nsCOMPtr<nsIInputStream> postData;
+ nsCOMPtr<nsIURIFixupInfo> info;
+
+ if (NS_FAILED(fixup->KeywordToURI(aKeyword, getter_AddRefs(postData),
+ getter_AddRefs(info)))) {
+ return true;
+ }
+ info->GetKeywordProviderName(*aProviderName);
+
+ nsTArray<mozilla::ipc::FileDescriptor> fds;
+ SerializeInputStream(postData, *aPostData, fds);
+ MOZ_ASSERT(fds.IsEmpty());
+
+ nsCOMPtr<nsIURI> uri;
+ info->GetPreferredURI(getter_AddRefs(uri));
+ SerializeURI(uri, *aURI);
+ return true;
+}
+
+bool
+ContentParent::RecvNotifyKeywordSearchLoading(const nsString &aProvider,
+ const nsString &aKeyword)
+{
+#ifdef MOZ_TOOLKIT_SEARCH
+ nsCOMPtr<nsIBrowserSearchService> searchSvc = do_GetService("@mozilla.org/browser/search-service;1");
+ if (searchSvc) {
+ nsCOMPtr<nsISearchEngine> searchEngine;
+ searchSvc->GetEngineByName(aProvider, getter_AddRefs(searchEngine));
+ if (searchEngine) {
+ nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
+ if (obsSvc) {
+ // Note that "keyword-search" refers to a search via the url
+ // bar, not a bookmarks keyword search.
+ obsSvc->NotifyObservers(searchEngine, "keyword-search", aKeyword.get());
+ }
+ }
+ }
+#endif
+ return true;
+}
+
+bool
+ContentParent::RecvCopyFavicon(const URIParams& aOldURI,
+ const URIParams& aNewURI,
+ const IPC::Principal& aLoadingPrincipal,
+ const bool& aInPrivateBrowsing)
+{
+ nsCOMPtr<nsIURI> oldURI = DeserializeURI(aOldURI);
+ if (!oldURI) {
+ return true;
+ }
+ nsCOMPtr<nsIURI> newURI = DeserializeURI(aNewURI);
+ if (!newURI) {
+ return true;
+ }
+
+ nsDocShell::CopyFavicon(oldURI, newURI, aLoadingPrincipal, aInPrivateBrowsing);
+ return true;
+}
+
+bool
+ContentParent::ShouldContinueFromReplyTimeout()
+{
+ RefPtr<ProcessHangMonitor> monitor = ProcessHangMonitor::Get();
+ return !monitor || !monitor->ShouldTimeOutCPOWs();
+}
+
+bool
+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(NS_LITERAL_STRING("childID"), ChildID());
+ props->SetPropertyAsBool(NS_LITERAL_STRING("isApp"), IsForApp());
+ props->SetPropertyAsBool(NS_LITERAL_STRING("isAudio"), aIsAudio);
+ props->SetPropertyAsBool(NS_LITERAL_STRING("isVideo"), aIsVideo);
+
+ nsString requestURL = IsForApp() ? AppManifestURL() : aPageURL;
+ props->SetPropertyAsAString(NS_LITERAL_STRING("requestURL"), requestURL);
+
+ obs->NotifyObservers((nsIPropertyBag2*) props,
+ "recording-device-ipc-events",
+ aRecordingStatus.get());
+ } else {
+ NS_WARNING("Could not get the Observer service for ContentParent::RecvRecordingDeviceEvents.");
+ }
+ return true;
+}
+
+bool
+ContentParent::RecvGetGraphicsFeatureStatus(const int32_t& aFeature,
+ int32_t* aStatus,
+ nsCString* aFailureId,
+ bool* aSuccess)
+{
+ nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo();
+ if (!gfxInfo) {
+ *aSuccess = false;
+ return true;
+ }
+
+ *aSuccess = NS_SUCCEEDED(gfxInfo->GetFeatureStatus(aFeature, *aFailureId, aStatus));
+ return true;
+}
+
+bool
+ContentParent::RecvAddIdleObserver(const uint64_t& aObserver,
+ const uint32_t& aIdleTimeInS)
+{
+ nsresult rv;
+ nsCOMPtr<nsIIdleService> idleService =
+ do_GetService("@mozilla.org/widget/idleservice;1", &rv);
+ NS_ENSURE_SUCCESS(rv, false);
+
+ RefPtr<ParentIdleListener> listener =
+ new ParentIdleListener(this, aObserver, aIdleTimeInS);
+ rv = idleService->AddIdleObserver(listener, aIdleTimeInS);
+ NS_ENSURE_SUCCESS(rv, false);
+ mIdleListeners.AppendElement(listener);
+ return true;
+}
+
+bool
+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<nsIIdleService> idleService =
+ do_GetService("@mozilla.org/widget/idleservice;1", &rv);
+ NS_ENSURE_SUCCESS(rv, false);
+ idleService->RemoveIdleObserver(listener, aIdleTimeInS);
+ mIdleListeners.RemoveElementAt(i);
+ break;
+ }
+ }
+ return true;
+}
+
+bool
+ContentParent::RecvBackUpXResources(const FileDescriptor& aXSocketFd)
+{
+#ifndef MOZ_X11
+ NS_RUNTIMEABORT("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 true;
+}
+
+bool
+ContentParent::RecvOpenAnonymousTemporaryFile(FileDescOrError *aFD)
+{
+ PRFileDesc *prfd;
+ nsresult rv = NS_OpenAnonymousTemporaryFile(&prfd);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ // Returning false will kill the child process; instead
+ // propagate the error and let the child handle it.
+ *aFD = rv;
+ return true;
+ }
+ *aFD = FileDescriptor(FileDescriptor::PlatformHandleType(PR_FileDesc2NativeHandle(prfd)));
+ // The FileDescriptor object owns a duplicate of the file handle; we
+ // must close the original (and clean up the NSPR descriptor).
+ PR_Close(prfd);
+ return true;
+}
+
+static NS_DEFINE_CID(kFormProcessorCID, NS_FORMPROCESSOR_CID);
+
+bool
+ContentParent::RecvKeygenProcessValue(const nsString& oldValue,
+ const nsString& challenge,
+ const nsString& keytype,
+ const nsString& keyparams,
+ nsString* newValue)
+{
+ nsCOMPtr<nsIFormProcessor> formProcessor =
+ do_GetService(kFormProcessorCID);
+ if (!formProcessor) {
+ newValue->Truncate();
+ return true;
+ }
+
+ formProcessor->ProcessValueIPC(oldValue, challenge, keytype, keyparams,
+ *newValue);
+ return true;
+}
+
+bool
+ContentParent::RecvKeygenProvideContent(nsString* aAttribute,
+ nsTArray<nsString>* aContent)
+{
+ nsCOMPtr<nsIFormProcessor> formProcessor =
+ do_GetService(kFormProcessorCID);
+ if (!formProcessor) {
+ return true;
+ }
+
+ formProcessor->ProvideContent(NS_LITERAL_STRING("SELECT"), *aContent,
+ *aAttribute);
+ return true;
+}
+
+PFileDescriptorSetParent*
+ContentParent::AllocPFileDescriptorSetParent(const FileDescriptor& aFD)
+{
+ return nsIContentParent::AllocPFileDescriptorSetParent(aFD);
+}
+
+bool
+ContentParent::DeallocPFileDescriptorSetParent(PFileDescriptorSetParent* aActor)
+{
+ return nsIContentParent::DeallocPFileDescriptorSetParent(aActor);
+}
+
+bool
+ContentParent::IgnoreIPCPrincipal()
+{
+ static bool sDidAddVarCache = false;
+ static bool sIgnoreIPCPrincipal = false;
+ if (!sDidAddVarCache) {
+ sDidAddVarCache = true;
+ Preferences::AddBoolVarCache(&sIgnoreIPCPrincipal,
+ "dom.testing.ignore_ipc_principal", false);
+ }
+ return sIgnoreIPCPrincipal;
+}
+
+void
+ContentParent::NotifyUpdatedDictionaries()
+{
+ nsCOMPtr<nsISpellChecker> spellChecker(do_GetService(NS_SPELLCHECKER_CONTRACTID));
+ MOZ_ASSERT(spellChecker, "No spell checker?");
+
+ InfallibleTArray<nsString> dictionaries;
+ spellChecker->GetDictionaryList(&dictionaries);
+
+ for (auto* cp : AllProcesses(eLive)) {
+ Unused << cp->SendUpdateDictionaryList(dictionaries);
+ }
+}
+
+/*static*/ TabId
+ContentParent::AllocateTabId(const TabId& aOpenerTabId,
+ const IPCTabContext& aContext,
+ const ContentParentId& aCpId)
+{
+ TabId tabId;
+ if (XRE_IsParentProcess()) {
+ ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+ tabId = cpm->AllocateTabId(aOpenerTabId, aContext, aCpId);
+ }
+ else {
+ ContentChild::GetSingleton()->SendAllocateTabId(aOpenerTabId,
+ aContext,
+ aCpId,
+ &tabId);
+ }
+ return tabId;
+}
+
+/*static*/ void
+ContentParent::DeallocateTabId(const TabId& aTabId,
+ const ContentParentId& aCpId,
+ bool aMarkedDestroying)
+{
+ if (XRE_IsParentProcess()) {
+ ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+ ContentParent* cp = cpm->GetContentProcessById(aCpId);
+
+ cp->NotifyTabDestroyed(aTabId, aMarkedDestroying);
+
+ ContentProcessManager::GetSingleton()->DeallocateTabId(aCpId, aTabId);
+ } else {
+ ContentChild::GetSingleton()->SendDeallocateTabId(aTabId, aCpId,
+ aMarkedDestroying);
+ }
+}
+
+bool
+ContentParent::RecvAllocateTabId(const TabId& aOpenerTabId,
+ const IPCTabContext& aContext,
+ const ContentParentId& aCpId,
+ TabId* aTabId)
+{
+ *aTabId = AllocateTabId(aOpenerTabId, aContext, aCpId);
+ if (!(*aTabId)) {
+ return false;
+ }
+ return true;
+}
+
+bool
+ContentParent::RecvDeallocateTabId(const TabId& aTabId,
+ const ContentParentId& aCpId,
+ const bool& aMarkedDestroying)
+{
+ DeallocateTabId(aTabId, aCpId, aMarkedDestroying);
+ return true;
+}
+
+bool
+ContentParent::RecvNotifyTabDestroying(const TabId& aTabId,
+ const ContentParentId& aCpId)
+{
+ NotifyTabDestroying(aTabId, aCpId);
+ return true;
+}
+
+nsTArray<TabContext>
+ContentParent::GetManagedTabContext()
+{
+ return Move(ContentProcessManager::GetSingleton()->
+ GetTabContextByContentProcess(this->ChildID()));
+}
+
+mozilla::docshell::POfflineCacheUpdateParent*
+ContentParent::AllocPOfflineCacheUpdateParent(const URIParams& aManifestURI,
+ const URIParams& aDocumentURI,
+ const PrincipalInfo& aLoadingPrincipalInfo,
+ const bool& aStickDocument)
+{
+ RefPtr<mozilla::docshell::OfflineCacheUpdateParent> update =
+ new mozilla::docshell::OfflineCacheUpdateParent();
+ // Use this reference as the IPDL reference.
+ return update.forget().take();
+}
+
+bool
+ContentParent::RecvPOfflineCacheUpdateConstructor(POfflineCacheUpdateParent* aActor,
+ const URIParams& aManifestURI,
+ const URIParams& aDocumentURI,
+ const PrincipalInfo& aLoadingPrincipal,
+ const bool& aStickDocument)
+{
+ MOZ_ASSERT(aActor);
+
+ RefPtr<mozilla::docshell::OfflineCacheUpdateParent> update =
+ static_cast<mozilla::docshell::OfflineCacheUpdateParent*>(aActor);
+
+ nsresult rv = update->Schedule(aManifestURI, aDocumentURI, aLoadingPrincipal, aStickDocument);
+ if (NS_FAILED(rv) && IsAlive()) {
+ // Inform the child of failure.
+ Unused << update->SendFinish(false, false);
+ }
+
+ return true;
+}
+
+bool
+ContentParent::DeallocPOfflineCacheUpdateParent(POfflineCacheUpdateParent* aActor)
+{
+ // Reclaim the IPDL reference.
+ RefPtr<mozilla::docshell::OfflineCacheUpdateParent> update =
+ dont_AddRef(static_cast<mozilla::docshell::OfflineCacheUpdateParent*>(aActor));
+ return true;
+}
+
+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
+}
+
+bool
+ContentParent::RecvSetOfflinePermission(const Principal& aPrincipal)
+{
+ nsIPrincipal* principal = aPrincipal;
+ nsContentUtils::MaybeAllowOfflineAppByDefault(principal);
+ return true;
+}
+
+void
+ContentParent::MaybeInvokeDragSession(TabParent* aParent)
+{
+ 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;
+ nsCOMPtr<nsIDOMDataTransfer> domTransfer;
+ session->GetDataTransfer(getter_AddRefs(domTransfer));
+ nsCOMPtr<DataTransfer> transfer = do_QueryInterface(domTransfer);
+ 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);
+ }
+ }
+}
+
+bool
+ContentParent::RecvUpdateDropEffect(const uint32_t& aDragAction,
+ const uint32_t& aDropEffect)
+{
+ nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
+ if (dragSession) {
+ dragSession->SetDragAction(aDragAction);
+ nsCOMPtr<nsIDOMDataTransfer> dt;
+ dragSession->GetDataTransfer(getter_AddRefs(dt));
+ if (dt) {
+ dt->SetDropEffectInt(aDropEffect);
+ }
+ dragSession->UpdateDragEffect();
+ }
+ return true;
+}
+
+PContentPermissionRequestParent*
+ContentParent::AllocPContentPermissionRequestParent(const InfallibleTArray<PermissionRequest>& aRequests,
+ const IPC::Principal& aPrincipal,
+ const TabId& aTabId)
+{
+ ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+ RefPtr<TabParent> tp =
+ cpm->GetTopLevelTabParentByProcessAndTabId(this->ChildID(), aTabId);
+ if (!tp) {
+ return nullptr;
+ }
+
+ return nsContentPermissionUtils::CreateContentPermissionRequestParent(aRequests,
+ tp->GetOwnerElement(),
+ aPrincipal,
+ aTabId);
+}
+
+bool
+ContentParent::DeallocPContentPermissionRequestParent(PContentPermissionRequestParent* actor)
+{
+ nsContentPermissionUtils::NotifyRemoveContentPermissionRequestParent(actor);
+ delete actor;
+ return true;
+}
+
+PWebBrowserPersistDocumentParent*
+ContentParent::AllocPWebBrowserPersistDocumentParent(PBrowserParent* aBrowser,
+ const uint64_t& aOuterWindowID)
+{
+ return new WebBrowserPersistDocumentParent();
+}
+
+bool
+ContentParent::DeallocPWebBrowserPersistDocumentParent(PWebBrowserPersistDocumentParent* aActor)
+{
+ delete aActor;
+ return true;
+}
+
+bool
+ContentParent::RecvCreateWindow(PBrowserParent* aThisTab,
+ PBrowserParent* aNewTab,
+ PRenderFrameParent* aRenderFrame,
+ const uint32_t& aChromeFlags,
+ const bool& aCalledFromJS,
+ const bool& aPositionSpecified,
+ const bool& aSizeSpecified,
+ const nsCString& aFeatures,
+ const nsCString& aBaseURI,
+ const DocShellOriginAttributes& aOpenerOriginAttributes,
+ const float& aFullZoom,
+ nsresult* aResult,
+ bool* aWindowIsNew,
+ InfallibleTArray<FrameScriptInfo>* aFrameScripts,
+ nsCString* aURLToLoad,
+ TextureFactoryIdentifier* aTextureFactoryIdentifier,
+ uint64_t* aLayersId)
+{
+ // We always expect to open a new window here. If we don't, it's an error.
+ *aWindowIsNew = true;
+ *aResult = NS_OK;
+
+ // The content process should never be in charge of computing whether or
+ // not a window should be private or remote - the parent will do that.
+ const uint32_t badFlags =
+ nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW
+ | nsIWebBrowserChrome::CHROME_NON_PRIVATE_WINDOW
+ | nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME
+ | nsIWebBrowserChrome::CHROME_REMOTE_WINDOW;
+ if (!!(aChromeFlags & badFlags)) {
+ return false;
+ }
+
+ TabParent* thisTabParent = nullptr;
+ if (aThisTab) {
+ thisTabParent = TabParent::GetFrom(aThisTab);
+ }
+
+ if (NS_WARN_IF(thisTabParent && thisTabParent->IsMozBrowserOrApp())) {
+ return false;
+ }
+
+ TabParent* newTab = TabParent::GetFrom(aNewTab);
+ MOZ_ASSERT(newTab);
+
+ auto destroyNewTabOnError = MakeScopeExit([&] {
+ if (!*aWindowIsNew || NS_FAILED(*aResult)) {
+ if (newTab) {
+ newTab->Destroy();
+ }
+ }
+ });
+
+ // Content has requested that we open this new content window, so
+ // we must have an opener.
+ newTab->SetHasContentOpener(true);
+
+ nsCOMPtr<nsIContent> frame;
+ if (thisTabParent) {
+ frame = do_QueryInterface(thisTabParent->GetOwnerElement());
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> parent;
+ if (frame) {
+ parent = frame->OwnerDoc()->GetWindow();
+
+ // If our chrome window is in the process of closing, don't try to open a
+ // new tab in it.
+ if (parent && parent->Closed()) {
+ parent = nullptr;
+ }
+ }
+
+ nsCOMPtr<nsIBrowserDOMWindow> browserDOMWin;
+ if (thisTabParent) {
+ browserDOMWin = thisTabParent->GetBrowserDOMWindow();
+ }
+
+ // If we haven't found a chrome window to open in, just use the most recently
+ // opened one.
+ if (!parent) {
+ parent = nsContentUtils::GetMostRecentNonPBWindow();
+ if (NS_WARN_IF(!parent)) {
+ *aResult = NS_ERROR_FAILURE;
+ return true;
+ }
+
+ nsCOMPtr<nsIDOMChromeWindow> rootChromeWin = do_QueryInterface(parent);
+ if (rootChromeWin) {
+ rootChromeWin->GetBrowserDOMWindow(getter_AddRefs(browserDOMWin));
+ }
+ }
+
+ int32_t openLocation =
+ nsWindowWatcher::GetWindowOpenLocation(parent, aChromeFlags, aCalledFromJS,
+ aPositionSpecified, aSizeSpecified);
+
+ MOZ_ASSERT(openLocation == nsIBrowserDOMWindow::OPEN_NEWTAB ||
+ openLocation == nsIBrowserDOMWindow::OPEN_NEWWINDOW);
+
+ // Opening new tabs is the easy case...
+ if (openLocation == nsIBrowserDOMWindow::OPEN_NEWTAB) {
+ if (NS_WARN_IF(!browserDOMWin)) {
+ *aResult = NS_ERROR_ABORT;
+ return true;
+ }
+
+ bool isPrivate = false;
+ if (thisTabParent) {
+ nsCOMPtr<nsILoadContext> loadContext = thisTabParent->GetLoadContext();
+ loadContext->GetUsePrivateBrowsing(&isPrivate);
+ }
+
+ nsCOMPtr<nsIOpenURIInFrameParams> params =
+ new nsOpenURIInFrameParams(aOpenerOriginAttributes);
+ params->SetReferrer(NS_ConvertUTF8toUTF16(aBaseURI));
+ params->SetIsPrivate(isPrivate);
+
+ TabParent::AutoUseNewTab aunt(newTab, aWindowIsNew, aURLToLoad);
+
+ nsCOMPtr<nsIFrameLoaderOwner> frameLoaderOwner;
+ browserDOMWin->OpenURIInFrame(nullptr, params,
+ openLocation,
+ nsIBrowserDOMWindow::OPEN_NEW,
+ getter_AddRefs(frameLoaderOwner));
+ if (!frameLoaderOwner) {
+ *aWindowIsNew = false;
+ }
+
+ newTab->SwapFrameScriptsFrom(*aFrameScripts);
+
+ RenderFrameParent* rfp = static_cast<RenderFrameParent*>(aRenderFrame);
+ if (!newTab->SetRenderFrame(rfp) ||
+ !newTab->GetRenderFrameInfo(aTextureFactoryIdentifier, aLayersId)) {
+ *aResult = NS_ERROR_FAILURE;
+ }
+
+ return true;
+ }
+
+ TabParent::AutoUseNewTab aunt(newTab, aWindowIsNew, aURLToLoad);
+
+ nsCOMPtr<nsPIWindowWatcher> pwwatch =
+ do_GetService(NS_WINDOWWATCHER_CONTRACTID, aResult);
+
+ if (NS_WARN_IF(NS_FAILED(*aResult))) {
+ return true;
+ }
+
+ nsCOMPtr<nsITabParent> newRemoteTab;
+ if (!thisTabParent) {
+ // Because we weren't passed an opener tab, the content process has asked us
+ // to open a new window that is unrelated to a pre-existing tab.
+ *aResult = pwwatch->OpenWindowWithoutParent(getter_AddRefs(newRemoteTab));
+ } else {
+ *aResult = pwwatch->OpenWindowWithTabParent(thisTabParent, aFeatures, aCalledFromJS,
+ aFullZoom, getter_AddRefs(newRemoteTab));
+ }
+
+ if (NS_WARN_IF(NS_FAILED(*aResult))) {
+ return true;
+ }
+
+ MOZ_ASSERT(TabParent::GetFrom(newRemoteTab) == newTab);
+
+ newTab->SwapFrameScriptsFrom(*aFrameScripts);
+
+ RenderFrameParent* rfp = static_cast<RenderFrameParent*>(aRenderFrame);
+ if (!newTab->SetRenderFrame(rfp) ||
+ !newTab->GetRenderFrameInfo(aTextureFactoryIdentifier, aLayersId)) {
+ *aResult = NS_ERROR_FAILURE;
+ }
+
+ return true;
+}
+
+bool
+ContentParent::RecvProfile(const nsCString& aProfile)
+{
+#ifdef MOZ_ENABLE_PROFILER_SPS
+ if (NS_WARN_IF(!mGatherer)) {
+ return true;
+ }
+ mProfile = aProfile;
+ mGatherer->GatheredOOPProfile();
+#endif
+ return true;
+}
+
+bool
+ContentParent::RecvGetGraphicsDeviceInitData(ContentDeviceData* aOut)
+{
+ gfxPlatform::GetPlatform()->BuildContentDeviceData(aOut);
+ return true;
+}
+
+bool
+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 true;
+}
+
+bool
+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::D3D9Video:
+ guard = MakeUnique<gfx::D3D9VideoCrashGuard>(this);
+ break;
+ case gfx::CrashGuardType::GLContext:
+ guard = MakeUnique<gfx::GLContextCrashGuard>(this);
+ break;
+ case gfx::CrashGuardType::D3D11Video:
+ guard = MakeUnique<gfx::D3D11VideoCrashGuard>(this);
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("unknown crash guard type");
+ return false;
+ }
+
+ if (guard->Crashed()) {
+ *aOutCrashed = true;
+ return true;
+ }
+
+ *aOutCrashed = false;
+ mDriverCrashGuard = Move(guard);
+ return true;
+}
+
+bool
+ContentParent::RecvEndDriverCrashGuard(const uint32_t& aGuardType)
+{
+ mDriverCrashGuard = nullptr;
+ return true;
+}
+
+bool
+ContentParent::RecvGetAndroidSystemInfo(AndroidSystemInfo* aInfo)
+{
+#ifdef MOZ_WIDGET_ANDROID
+ nsSystemInfo::GetAndroidSystemInfo(aInfo);
+ return true;
+#else
+ MOZ_CRASH("wrong platform!");
+ return false;
+#endif
+}
+
+bool
+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 true;
+}
+
+void
+ContentParent::StartProfiler(nsIProfilerStartParams* aParams)
+{
+#ifdef MOZ_ENABLE_PROFILER_SPS
+ if (NS_WARN_IF(!aParams)) {
+ return;
+ }
+
+ ProfilerInitParams ipcParams;
+
+ ipcParams.enabled() = true;
+ aParams->GetEntries(&ipcParams.entries());
+ aParams->GetInterval(&ipcParams.interval());
+ ipcParams.features() = aParams->GetFeatures();
+ ipcParams.threadFilters() = aParams->GetThreadFilterNames();
+
+ Unused << SendStartProfiler(ipcParams);
+
+ nsCOMPtr<nsIProfiler> profiler(do_GetService("@mozilla.org/tools/profiler;1"));
+ if (NS_WARN_IF(!profiler)) {
+ return;
+ }
+ nsCOMPtr<nsISupports> gatherer;
+ profiler->GetProfileGatherer(getter_AddRefs(gatherer));
+ mGatherer = static_cast<ProfileGatherer*>(gatherer.get());
+#endif
+}
+
+bool
+ContentParent::RecvNotifyPushObservers(const nsCString& aScope,
+ const IPC::Principal& aPrincipal,
+ const nsString& aMessageId)
+{
+ PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId, Nothing());
+ Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObservers()));
+ return true;
+}
+
+bool
+ContentParent::RecvNotifyPushObserversWithData(const nsCString& aScope,
+ const IPC::Principal& aPrincipal,
+ const nsString& aMessageId,
+ InfallibleTArray<uint8_t>&& aData)
+{
+ PushMessageDispatcher dispatcher(aScope, aPrincipal, aMessageId, Some(aData));
+ Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObservers()));
+ return true;
+}
+
+bool
+ContentParent::RecvNotifyPushSubscriptionChangeObservers(const nsCString& aScope,
+ const IPC::Principal& aPrincipal)
+{
+ PushSubscriptionChangeDispatcher dispatcher(aScope, aPrincipal);
+ Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObservers()));
+ return true;
+}
+
+bool
+ContentParent::RecvNotifyPushSubscriptionModifiedObservers(const nsCString& aScope,
+ const IPC::Principal& aPrincipal)
+{
+ PushSubscriptionModifiedDispatcher dispatcher(aScope, aPrincipal);
+ Unused << NS_WARN_IF(NS_FAILED(dispatcher.NotifyObservers()));
+ return true;
+}
+
+bool
+ContentParent::RecvNotifyLowMemory()
+{
+#ifdef MOZ_CRASHREPORTER
+ nsThread::SaveMemoryReportNearOOM(nsThread::ShouldSaveMemoryReport::kForceReport);
+#endif
+ return true;
+}
+
+/* static */ void
+ContentParent::BroadcastBlobURLRegistration(const nsACString& aURI,
+ BlobImpl* aBlobImpl,
+ nsIPrincipal* aPrincipal,
+ ContentParent* aIgnoreThisCP)
+{
+ nsCString uri(aURI);
+ IPC::Principal principal(aPrincipal);
+
+ for (auto* cp : AllProcesses(eLive)) {
+ if (cp != aIgnoreThisCP) {
+ PBlobParent* blobParent = cp->GetOrCreateActorForBlobImpl(aBlobImpl);
+ if (blobParent) {
+ Unused << cp->SendBlobURLRegistration(uri, blobParent, principal);
+ }
+ }
+ }
+}
+
+/* static */ void
+ContentParent::BroadcastBlobURLUnregistration(const nsACString& aURI,
+ ContentParent* aIgnoreThisCP)
+{
+ nsCString uri(aURI);
+
+ for (auto* cp : AllProcesses(eLive)) {
+ if (cp != aIgnoreThisCP) {
+ Unused << cp->SendBlobURLUnregistration(uri);
+ }
+ }
+}
+
+bool
+ContentParent::RecvStoreAndBroadcastBlobURLRegistration(const nsCString& aURI,
+ PBlobParent* aBlobParent,
+ const Principal& aPrincipal)
+{
+ RefPtr<BlobImpl> blobImpl =
+ static_cast<BlobParent*>(aBlobParent)->GetBlobImpl();
+ if (NS_WARN_IF(!blobImpl)) {
+ return false;
+ }
+
+ if (NS_SUCCEEDED(nsHostObjectProtocolHandler::AddDataEntry(aURI, aPrincipal,
+ blobImpl))) {
+ BroadcastBlobURLRegistration(aURI, blobImpl, aPrincipal, this);
+
+ // We want to store this blobURL, so we can unregister it if the child
+ // crashes.
+ mBlobURLs.AppendElement(aURI);
+ }
+
+ BroadcastBlobURLRegistration(aURI, blobImpl, aPrincipal, this);
+ return true;
+}
+
+bool
+ContentParent::RecvUnstoreAndBroadcastBlobURLUnregistration(const nsCString& aURI)
+{
+ nsHostObjectProtocolHandler::RemoveDataEntry(aURI,
+ false /* Don't broadcast */);
+ BroadcastBlobURLUnregistration(aURI, this);
+ mBlobURLs.RemoveElement(aURI);
+
+ return true;
+}
+
+bool
+ContentParent::RecvGetA11yContentId(uint32_t* aContentId)
+{
+#if defined(XP_WIN32) && defined(ACCESSIBILITY)
+ *aContentId = a11y::AccessibleWrap::GetContentProcessIdFor(ChildID());
+ MOZ_ASSERT(*aContentId);
+ return true;
+#else
+ return false;
+#endif
+}
+
+} // 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;
+}
+
+bool
+ContentParent::HandleWindowsMessages(const Message& aMsg) const
+{
+ MOZ_ASSERT(aMsg.is_sync());
+
+ // 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;
+ }
+
+ return true;
+}
+
+bool
+ContentParent::RecvGetFilesRequest(const nsID& aUUID,
+ const nsString& aDirectoryPath,
+ const bool& aRecursiveFlag)
+{
+ MOZ_ASSERT(!mGetFilesPendingRequests.GetWeak(aUUID));
+
+ ErrorResult rv;
+ RefPtr<GetFilesHelper> helper =
+ GetFilesHelperParent::Create(aUUID, aDirectoryPath, aRecursiveFlag, this,
+ rv);
+
+ if (NS_WARN_IF(rv.Failed())) {
+ return SendGetFilesResponse(aUUID,
+ GetFilesResponseFailure(rv.StealNSResult()));
+ }
+
+ mGetFilesPendingRequests.Put(aUUID, helper);
+ return true;
+}
+
+bool
+ContentParent::RecvDeleteGetFilesRequest(const nsID& aUUID)
+{
+ GetFilesHelper* helper = mGetFilesPendingRequests.GetWeak(aUUID);
+ if (helper) {
+ mGetFilesPendingRequests.Remove(aUUID);
+ }
+
+ return true;
+}
+
+void
+ContentParent::SendGetFilesResponseAndForget(const nsID& aUUID,
+ const GetFilesResponseResult& aResult)
+{
+ GetFilesHelper* helper = mGetFilesPendingRequests.GetWeak(aUUID);
+ if (helper) {
+ mGetFilesPendingRequests.Remove(aUUID);
+ Unused << SendGetFilesResponse(aUUID, aResult);
+ }
+}
+
+void
+ContentParent::ForceTabPaint(TabParent* aTabParent, uint64_t aLayerObserverEpoch)
+{
+ if (!mHangMonitorActor) {
+ return;
+ }
+ ProcessHangMonitor::ForcePaint(mHangMonitorActor, aTabParent, aLayerObserverEpoch);
+}
+
+bool
+ContentParent::RecvAccumulateChildHistogram(
+ InfallibleTArray<Accumulation>&& aAccumulations)
+{
+ Telemetry::AccumulateChild(GeckoProcessType_Content, aAccumulations);
+ return true;
+}
+
+bool
+ContentParent::RecvAccumulateChildKeyedHistogram(
+ InfallibleTArray<KeyedAccumulation>&& aAccumulations)
+{
+ Telemetry::AccumulateChildKeyed(GeckoProcessType_Content, aAccumulations);
+ return true;
+}
diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h
new file mode 100644
index 000000000..a3750de1a
--- /dev/null
+++ b/dom/ipc/ContentParent.h
@@ -0,0 +1,1197 @@
+/* -*- 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/nsIContentParent.h"
+#include "mozilla/gfx/gfxVarReceiver.h"
+#include "mozilla/gfx/GPUProcessListener.h"
+#include "mozilla/ipc/GeckoChildProcessHost.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/FileUtils.h"
+#include "mozilla/HalTypes.h"
+#include "mozilla/LinkedList.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/UniquePtr.h"
+
+#include "nsDataHashtable.h"
+#include "nsFrameMessageManager.h"
+#include "nsHashKeys.h"
+#include "nsIObserver.h"
+#include "nsIThreadInternal.h"
+#include "nsIDOMGeoPositionCallback.h"
+#include "nsIDOMGeoPositionErrorCallback.h"
+#include "nsRefPtrHashtable.h"
+#include "PermissionMessageUtils.h"
+#include "DriverCrashGuard.h"
+
+#define CHILD_PROCESS_SHUTDOWN_MESSAGE NS_LITERAL_STRING("child-process-shutdown")
+
+class mozIApplication;
+class nsConsoleService;
+class nsICycleCollectorLogSink;
+class nsIDumpGCAndCCLogsCallback;
+class nsITimer;
+class ParentIdleListener;
+class nsIWidget;
+
+namespace mozilla {
+class PRemoteSpellcheckEngineParent;
+#ifdef MOZ_ENABLE_PROFILER_SPS
+class ProfileGatherer;
+#endif
+
+#if defined(XP_LINUX) && defined(MOZ_CONTENT_SANDBOX)
+class SandboxBroker;
+class SandboxBrokerPolicyFactory;
+#endif
+
+namespace embedding {
+class PrintingParent;
+}
+
+namespace ipc {
+class OptionalURIParams;
+class PFileDescriptorSetParent;
+class URIParams;
+class TestShellParent;
+} // namespace ipc
+
+namespace jsipc {
+class PJavaScriptParent;
+} // namespace jsipc
+
+namespace layers {
+struct TextureFactoryIdentifier;
+} // namespace layers
+
+namespace layout {
+class PRenderFrameParent;
+} // namespace layout
+
+namespace dom {
+
+class Element;
+class TabParent;
+class PStorageParent;
+class ClonedMessageData;
+class MemoryReport;
+class TabContext;
+class ContentBridgeParent;
+class GetFilesHelper;
+
+class ContentParent final : public PContentParent
+ , public nsIContentParent
+ , public nsIObserver
+ , public nsIDOMGeoPositionCallback
+ , public nsIDOMGeoPositionErrorCallback
+ , public gfx::gfxVarReceiver
+ , public mozilla::LinkedListElement<ContentParent>
+ , public gfx::GPUProcessListener
+{
+ typedef mozilla::ipc::GeckoChildProcessHost GeckoChildProcessHost;
+ typedef mozilla::ipc::OptionalURIParams OptionalURIParams;
+ typedef mozilla::ipc::PFileDescriptorSetParent PFileDescriptorSetParent;
+ typedef mozilla::ipc::TestShellParent TestShellParent;
+ typedef mozilla::ipc::URIParams URIParams;
+ typedef mozilla::ipc::PrincipalInfo PrincipalInfo;
+ typedef mozilla::dom::ClonedMessageData ClonedMessageData;
+
+public:
+
+ virtual bool IsContentParent() const override { return true; }
+
+ /**
+ * 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();
+
+ /**
+ * Ensure that all subprocesses are terminated and their OS
+ * resources have been reaped. This is synchronous and can be
+ * very expensive in general. It also bypasses the normal
+ * shutdown process.
+ */
+ static void JoinAllSubprocesses();
+
+ static bool PreallocatedProcessReady();
+
+ /**
+ * Get or create a content process for:
+ * 1. browser iframe
+ * 2. remote xul <browser>
+ * 3. normal iframe
+ */
+ static already_AddRefed<ContentParent>
+ GetNewOrUsedBrowserProcess(bool aForBrowserElement = false,
+ hal::ProcessPriority aPriority =
+ hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND,
+ ContentParent* aOpener = nullptr,
+ bool aLargeAllocationProcess = false);
+
+ /**
+ * Create a subprocess suitable for use as a preallocated app process.
+ */
+ static already_AddRefed<ContentParent> PreallocateAppProcess();
+
+ /**
+ * Get or create a content process for the given TabContext. aFrameElement
+ * should be the frame/iframe element with which this process will
+ * associated.
+ */
+ static TabParent*
+ CreateBrowserOrApp(const TabContext& aContext,
+ Element* aFrameElement,
+ ContentParent* aOpenerContentParent,
+ bool aFreshProcess = false);
+
+ static void GetAll(nsTArray<ContentParent*>& aArray);
+
+ static void GetAllEvenIfDead(nsTArray<ContentParent*>& aArray);
+
+ 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->mIsAlive) {
+ 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->mIsAlive);
+
+ 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 bool IgnoreIPCPrincipal();
+
+ static void NotifyUpdatedDictionaries();
+
+#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
+
+ // Let managees query if it is safe to send messages.
+ bool IsDestroyed() const { return !mIPCOpen; }
+
+ virtual bool RecvCreateChildProcess(const IPCTabContext& aContext,
+ const hal::ProcessPriority& aPriority,
+ const TabId& aOpenerTabId,
+ ContentParentId* aCpId,
+ bool* aIsForApp,
+ bool* aIsForBrowser,
+ TabId* aTabId) override;
+
+ virtual bool RecvBridgeToChildProcess(const ContentParentId& aCpId) override;
+
+ virtual bool RecvCreateGMPService() override;
+
+ virtual bool RecvLoadPlugin(const uint32_t& aPluginId, nsresult* aRv,
+ uint32_t* aRunID) override;
+
+ virtual bool RecvConnectPluginBridge(const uint32_t& aPluginId,
+ nsresult* aRv) override;
+
+ virtual bool RecvGetBlocklistState(const uint32_t& aPluginId,
+ uint32_t* aIsBlocklisted) override;
+
+ virtual bool RecvFindPlugins(const uint32_t& aPluginEpoch,
+ nsresult* aRv,
+ nsTArray<PluginTag>* aPlugins,
+ uint32_t* aNewPluginEpoch) override;
+
+ virtual bool RecvUngrabPointer(const uint32_t& aTime) override;
+
+ virtual bool RecvRemovePermission(const IPC::Principal& aPrincipal,
+ const nsCString& aPermissionType,
+ nsresult* aRv) override;
+
+ NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(ContentParent, nsIObserver)
+
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+ NS_DECL_NSIDOMGEOPOSITIONCALLBACK
+ NS_DECL_NSIDOMGEOPOSITIONERRORCALLBACK
+
+ /**
+ * MessageManagerCallback methods that we override.
+ */
+ virtual bool DoLoadMessageManagerScript(const nsAString& aURL,
+ bool aRunInGlobalScope) override;
+
+ virtual nsresult DoSendAsyncMessage(JSContext* aCx,
+ const nsAString& aMessage,
+ StructuredCloneData& aData,
+ JS::Handle<JSObject *> aCpows,
+ nsIPrincipal* aPrincipal) override;
+
+ virtual bool CheckPermission(const nsAString& aPermission) override;
+
+ virtual bool CheckManifestURL(const nsAString& aManifestURL) override;
+
+ virtual bool CheckAppHasPermission(const nsAString& aPermission) override;
+
+ virtual bool CheckAppHasStatus(unsigned short aStatus) override;
+
+ virtual bool KillChild() override;
+
+ /** Notify that a tab is beginning its destruction sequence. */
+ static void NotifyTabDestroying(const TabId& aTabId,
+ const ContentParentId& aCpId);
+
+ /** Notify that a tab was destroyed during normal operation. */
+ void NotifyTabDestroyed(const TabId& aTabId,
+ bool aNotifiedDestroying);
+
+ TestShellParent* CreateTestShell();
+
+ bool DestroyTestShell(TestShellParent* aTestShell);
+
+ TestShellParent* GetTestShellSingleton();
+
+ jsipc::CPOWManager* GetCPOWManager() override;
+
+ static TabId
+ AllocateTabId(const TabId& aOpenerTabId,
+ const IPCTabContext& aContext,
+ const ContentParentId& aCpId);
+
+ static void
+ DeallocateTabId(const TabId& aTabId,
+ const ContentParentId& aCpId,
+ bool aMarkedDestroying);
+
+ void ReportChildAlreadyBlocked();
+
+ bool RequestRunToCompletion();
+
+ bool IsAlive() const;
+
+ virtual bool IsForApp() const override;
+
+ virtual bool IsForBrowser() const override
+ {
+ return mIsForBrowser;
+ }
+
+ GeckoChildProcessHost* Process() const
+ {
+ return mSubprocess;
+ }
+
+ ContentParent* Opener() const
+ {
+ return mOpener;
+ }
+
+ bool NeedsPermissionsUpdate() const
+ {
+ return mSendPermissionUpdates;
+ }
+
+ /**
+ * 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 override { return mChildID; }
+
+ const nsString& AppManifestURL() const { return mAppManifestURL; }
+
+ bool IsPreallocated() const;
+
+ /**
+ * 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;
+
+ virtual PCrashReporterParent*
+ AllocPCrashReporterParent(const NativeThreadId& tid,
+ const uint32_t& processType) override;
+
+ virtual bool
+ RecvPCrashReporterConstructor(PCrashReporterParent* actor,
+ const NativeThreadId& tid,
+ const uint32_t& processType) override;
+
+ virtual PNeckoParent* AllocPNeckoParent() override;
+
+ virtual bool RecvPNeckoConstructor(PNeckoParent* aActor) override
+ {
+ return PContentParent::RecvPNeckoConstructor(aActor);
+ }
+
+ virtual PPrintingParent* AllocPPrintingParent() override;
+
+ virtual bool DeallocPPrintingParent(PPrintingParent* aActor) override;
+
+#if defined(NS_PRINTING)
+ /**
+ * @return the PrintingParent for this ContentParent.
+ */
+ already_AddRefed<embedding::PrintingParent> GetPrintingParent();
+#endif
+
+ virtual PSendStreamParent* AllocPSendStreamParent() override;
+ virtual bool DeallocPSendStreamParent(PSendStreamParent* aActor) override;
+
+ virtual PScreenManagerParent*
+ AllocPScreenManagerParent(uint32_t* aNumberOfScreens,
+ float* aSystemDefaultScale,
+ bool* aSuccess) override;
+
+ virtual bool
+ DeallocPScreenManagerParent(PScreenManagerParent* aActor) override;
+
+ virtual PHalParent* AllocPHalParent() override;
+
+ virtual bool RecvPHalConstructor(PHalParent* aActor) override
+ {
+ return PContentParent::RecvPHalConstructor(aActor);
+ }
+
+ virtual PHeapSnapshotTempFileHelperParent*
+ AllocPHeapSnapshotTempFileHelperParent() override;
+
+ virtual PStorageParent* AllocPStorageParent() override;
+
+ virtual bool RecvPStorageConstructor(PStorageParent* aActor) override
+ {
+ return PContentParent::RecvPStorageConstructor(aActor);
+ }
+
+ virtual PJavaScriptParent*
+ AllocPJavaScriptParent() override;
+
+ virtual bool
+ RecvPJavaScriptConstructor(PJavaScriptParent* aActor) override
+ {
+ return PContentParent::RecvPJavaScriptConstructor(aActor);
+ }
+
+ virtual PRemoteSpellcheckEngineParent* AllocPRemoteSpellcheckEngineParent() override;
+
+ virtual bool RecvRecordingDeviceEvents(const nsString& aRecordingStatus,
+ const nsString& aPageURL,
+ const bool& aIsAudio,
+ const bool& aIsVideo) override;
+
+ bool CycleCollectWithLogs(bool aDumpAllTraces,
+ nsICycleCollectorLogSink* aSink,
+ nsIDumpGCAndCCLogsCallback* aCallback);
+
+ virtual PBlobParent*
+ SendPBlobConstructor(PBlobParent* aActor,
+ const BlobConstructorParams& aParams) override;
+
+ virtual bool RecvAllocateTabId(const TabId& aOpenerTabId,
+ const IPCTabContext& aContext,
+ const ContentParentId& aCpId,
+ TabId* aTabId) override;
+
+ virtual bool RecvDeallocateTabId(const TabId& aTabId,
+ const ContentParentId& aCpId,
+ const bool& aMarkedDestroying) override;
+
+ virtual bool RecvNotifyTabDestroying(const TabId& aTabId,
+ const ContentParentId& aCpId) override;
+
+ nsTArray<TabContext> GetManagedTabContext();
+
+ virtual POfflineCacheUpdateParent*
+ AllocPOfflineCacheUpdateParent(const URIParams& aManifestURI,
+ const URIParams& aDocumentURI,
+ const PrincipalInfo& aLoadingPrincipalInfo,
+ const bool& aStickDocument) override;
+
+ virtual bool
+ RecvPOfflineCacheUpdateConstructor(POfflineCacheUpdateParent* aActor,
+ const URIParams& aManifestURI,
+ const URIParams& aDocumentURI,
+ const PrincipalInfo& aLoadingPrincipal,
+ const bool& stickDocument) override;
+
+ virtual bool
+ DeallocPOfflineCacheUpdateParent(POfflineCacheUpdateParent* aActor) override;
+
+ virtual bool RecvSetOfflinePermission(const IPC::Principal& principal) override;
+
+ virtual bool RecvFinishShutdown() override;
+
+ void MaybeInvokeDragSession(TabParent* aParent);
+
+ virtual PContentPermissionRequestParent*
+ AllocPContentPermissionRequestParent(const InfallibleTArray<PermissionRequest>& aRequests,
+ const IPC::Principal& aPrincipal,
+ const TabId& aTabId) override;
+
+ virtual bool
+ DeallocPContentPermissionRequestParent(PContentPermissionRequestParent* actor) override;
+
+ virtual bool HandleWindowsMessages(const Message& aMsg) const override;
+
+ void ForkNewProcess(bool aBlocking);
+
+ virtual bool RecvCreateWindow(PBrowserParent* aThisTabParent,
+ PBrowserParent* aOpener,
+ layout::PRenderFrameParent* aRenderFrame,
+ const uint32_t& aChromeFlags,
+ const bool& aCalledFromJS,
+ const bool& aPositionSpecified,
+ const bool& aSizeSpecified,
+ const nsCString& aFeatures,
+ const nsCString& aBaseURI,
+ const DocShellOriginAttributes& aOpenerOriginAttributes,
+ const float& aFullZoom,
+ nsresult* aResult,
+ bool* aWindowIsNew,
+ InfallibleTArray<FrameScriptInfo>* aFrameScripts,
+ nsCString* aURLToLoad,
+ layers::TextureFactoryIdentifier* aTextureFactoryIdentifier,
+ uint64_t* aLayersId) override;
+
+ static bool AllocateLayerTreeId(TabParent* aTabParent, uint64_t* aId);
+
+ static void
+ BroadcastBlobURLRegistration(const nsACString& aURI,
+ BlobImpl* aBlobImpl,
+ nsIPrincipal* aPrincipal,
+ ContentParent* aIgnoreThisCP = nullptr);
+
+ static void
+ BroadcastBlobURLUnregistration(const nsACString& aURI,
+ ContentParent* aIgnoreThisCP = nullptr);
+
+ virtual bool
+ RecvStoreAndBroadcastBlobURLRegistration(const nsCString& aURI,
+ PBlobParent* aBlobParent,
+ const Principal& aPrincipal) override;
+
+ virtual bool
+ RecvUnstoreAndBroadcastBlobURLUnregistration(const nsCString& aURI) override;
+
+ virtual bool
+ RecvGetA11yContentId(uint32_t* aContentId) override;
+
+ virtual int32_t Pid() const override;
+
+ // Use the PHangMonitor channel to ask the child to repaint a tab.
+ void ForceTabPaint(TabParent* aTabParent, uint64_t aLayerObserverEpoch);
+
+protected:
+ void OnChannelConnected(int32_t pid) override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ bool ShouldContinueFromReplyTimeout() override;
+
+ void OnVarChanged(const GfxVarUpdate& aVar) override;
+ void OnCompositorUnexpectedShutdown() override;
+
+private:
+ static nsDataHashtable<nsStringHashKey, ContentParent*> *sAppContentParents;
+ static nsTArray<ContentParent*>* sNonAppContentParents;
+ static nsTArray<ContentParent*>* sLargeAllocationContentParents;
+ static nsTArray<ContentParent*>* sPrivateContent;
+ static StaticAutoPtr<LinkedList<ContentParent> > sContentParents;
+
+ static void JoinProcessesIOThread(const nsTArray<ContentParent*>* aProcesses,
+ Monitor* aMonitor, bool* aDone);
+
+ // Take the preallocated process and transform it into a "real" app process,
+ // for the specified manifest URL. If there is no preallocated process (or
+ // if it's dead), create a new one and set aTookPreAllocated to false.
+ static already_AddRefed<ContentParent>
+ GetNewOrPreallocatedAppProcess(mozIApplication* aApp,
+ hal::ProcessPriority aInitialPriority,
+ ContentParent* aOpener,
+ /*out*/ bool* aTookPreAllocated = nullptr);
+
+ static hal::ProcessPriority GetInitialProcessPriority(Element* aFrameElement);
+
+ static ContentBridgeParent* CreateContentBridgeParent(const TabContext& aContext,
+ const hal::ProcessPriority& aPriority,
+ const TabId& aOpenerTabId,
+ /*out*/ TabId* aTabId);
+
+ // Hide the raw constructor methods since we don't want client code
+ // using them.
+ virtual PBrowserParent* SendPBrowserConstructor(
+ PBrowserParent* actor,
+ const TabId& aTabId,
+ const IPCTabContext& context,
+ const uint32_t& chromeFlags,
+ const ContentParentId& aCpId,
+ const bool& aIsForApp,
+ const bool& aIsForBrowser) override;
+ using PContentParent::SendPTestShellConstructor;
+
+ FORWARD_SHMEM_ALLOCATOR_TO(PContentParent)
+
+ // No more than one of !!aApp, aIsForBrowser, and aIsForPreallocated may be
+ // true.
+ ContentParent(mozIApplication* aApp,
+ ContentParent* aOpener,
+ bool aIsForBrowser,
+ bool aIsForPreallocated);
+
+ // The common initialization for the constructors.
+ void InitializeMembers();
+
+ // Launch the subprocess and associated initialization.
+ // Returns false if the process fails to start.
+ bool LaunchSubprocess(hal::ProcessPriority aInitialPriority = hal::PROCESS_PRIORITY_FOREGROUND);
+
+ // Common initialization after sub process launch or adoption.
+ void InitInternal(ProcessPriority aPriority,
+ bool aSetupOffMainThreadCompositing,
+ bool aSendRegisteredChrome);
+
+ 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 app or browser.
+ void ForwardKnownInfo();
+
+ // Set the child process's priority and then check whether the child is
+ // still alive. Returns true if the process is still alive, and false
+ // otherwise. If you pass a FOREGROUND* priority here, it's (hopefully)
+ // unlikely that the process will be killed after this point.
+ bool SetPriorityAndCheckIsAlive(hal::ProcessPriority aPriority);
+
+ // Transform a pre-allocated app process into a "real" app
+ // process, for the specified manifest URL.
+ void TransformPreallocatedIntoApp(ContentParent* aOpener,
+ const nsAString& aAppManifestURL);
+
+ // Transform a pre-allocated app process into a browser process. If this
+ // returns false, the child process has died.
+ void TransformPreallocatedIntoBrowser(ContentParent* aOpener);
+
+ /**
+ * Mark this ContentParent as dead for the purposes of Get*().
+ * This method is idempotent.
+ */
+ void MarkAsDead();
+
+ /**
+ * 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,
+ };
+
+ /**
+ * 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();
+
+ static void ForceKillTimerCallback(nsITimer* aTimer, void* aClosure);
+
+ static bool AllocateLayerTreeId(ContentParent* aContent,
+ TabParent* aTopLevel, const TabId& aTabId,
+ uint64_t* aId);
+
+ PGMPServiceParent*
+ AllocPGMPServiceParent(mozilla::ipc::Transport* aTransport,
+ base::ProcessId aOtherProcess) override;
+
+ PBackgroundParent*
+ AllocPBackgroundParent(Transport* aTransport, ProcessId aOtherProcess)
+ override;
+
+ PProcessHangMonitorParent*
+ AllocPProcessHangMonitorParent(Transport* aTransport,
+ ProcessId aOtherProcess) override;
+
+ virtual bool RecvGetProcessAttributes(ContentParentId* aCpId,
+ bool* aIsForApp,
+ bool* aIsForBrowser) override;
+
+ virtual bool
+ RecvGetXPCOMProcessAttributes(bool* aIsOffline,
+ bool* aIsConnected,
+ int32_t* aCaptivePortalState,
+ bool* aIsLangRTL,
+ bool* aHaveBidiKeyboards,
+ InfallibleTArray<nsString>* dictionaries,
+ ClipboardCapabilities* clipboardCaps,
+ DomainPolicyClone* domainPolicy,
+ StructuredCloneData* initialData,
+ OptionalURIParams* aUserContentSheetURL) override;
+
+ virtual bool
+ DeallocPJavaScriptParent(mozilla::jsipc::PJavaScriptParent*) override;
+
+ virtual bool
+ DeallocPRemoteSpellcheckEngineParent(PRemoteSpellcheckEngineParent*) override;
+
+ virtual PBrowserParent* AllocPBrowserParent(const TabId& aTabId,
+ const IPCTabContext& aContext,
+ const uint32_t& aChromeFlags,
+ const ContentParentId& aCpId,
+ const bool& aIsForApp,
+ const bool& aIsForBrowser) override;
+
+ virtual bool DeallocPBrowserParent(PBrowserParent* frame) override;
+
+ virtual PBlobParent*
+ AllocPBlobParent(const BlobConstructorParams& aParams) override;
+
+ virtual bool DeallocPBlobParent(PBlobParent* aActor) override;
+
+ virtual bool
+ RecvPBlobConstructor(PBlobParent* aActor,
+ const BlobConstructorParams& params) override;
+
+ virtual bool
+ DeallocPCrashReporterParent(PCrashReporterParent* crashreporter) override;
+
+ virtual bool RecvNSSU2FTokenIsCompatibleVersion(const nsString& aVersion,
+ bool* aIsCompatible) override;
+
+ virtual bool RecvNSSU2FTokenIsRegistered(nsTArray<uint8_t>&& aKeyHandle,
+ bool* aIsValidKeyHandle) override;
+
+ virtual bool RecvNSSU2FTokenRegister(nsTArray<uint8_t>&& aApplication,
+ nsTArray<uint8_t>&& aChallenge,
+ nsTArray<uint8_t>* aRegistration) override;
+
+ virtual bool RecvNSSU2FTokenSign(nsTArray<uint8_t>&& aApplication,
+ nsTArray<uint8_t>&& aChallenge,
+ nsTArray<uint8_t>&& aKeyHandle,
+ nsTArray<uint8_t>* aSignature) override;
+
+ virtual bool RecvIsSecureURI(const uint32_t& aType, const URIParams& aURI,
+ const uint32_t& aFlags, bool* aIsSecureURI) override;
+
+ virtual bool RecvAccumulateMixedContentHSTS(const URIParams& aURI,
+ const bool& aActive,
+ const bool& aHSTSPriming) override;
+
+ virtual bool DeallocPHalParent(PHalParent*) override;
+
+ virtual bool
+ DeallocPHeapSnapshotTempFileHelperParent(PHeapSnapshotTempFileHelperParent*) override;
+
+ virtual PMemoryReportRequestParent*
+ AllocPMemoryReportRequestParent(const uint32_t& aGeneration,
+ const bool &aAnonymize,
+ const bool &aMinimizeMemoryUsage,
+ const MaybeFileDesc &aDMDFile) override;
+
+ virtual bool
+ DeallocPMemoryReportRequestParent(PMemoryReportRequestParent* actor) override;
+
+ virtual PCycleCollectWithLogsParent*
+ AllocPCycleCollectWithLogsParent(const bool& aDumpAllTraces,
+ const FileDescriptor& aGCLog,
+ const FileDescriptor& aCCLog) override;
+
+ virtual bool
+ DeallocPCycleCollectWithLogsParent(PCycleCollectWithLogsParent* aActor) override;
+
+ virtual PTestShellParent* AllocPTestShellParent() override;
+
+ virtual bool DeallocPTestShellParent(PTestShellParent* shell) override;
+
+ virtual bool DeallocPNeckoParent(PNeckoParent* necko) override;
+
+ virtual PPSMContentDownloaderParent*
+ AllocPPSMContentDownloaderParent(const uint32_t& aCertType) override;
+
+ virtual bool
+ DeallocPPSMContentDownloaderParent(PPSMContentDownloaderParent* aDownloader) override;
+
+ virtual PExternalHelperAppParent*
+ AllocPExternalHelperAppParent(const OptionalURIParams& aUri,
+ const nsCString& aMimeContentType,
+ const nsCString& aContentDisposition,
+ const uint32_t& aContentDispositionHint,
+ const nsString& aContentDispositionFilename,
+ const bool& aForceSave,
+ const int64_t& aContentLength,
+ const bool& aWasFileChannel,
+ const OptionalURIParams& aReferrer,
+ PBrowserParent* aBrowser) override;
+
+ virtual bool
+ DeallocPExternalHelperAppParent(PExternalHelperAppParent* aService) override;
+
+ virtual PHandlerServiceParent* AllocPHandlerServiceParent() override;
+
+ virtual bool DeallocPHandlerServiceParent(PHandlerServiceParent*) override;
+
+ virtual PMediaParent* AllocPMediaParent() override;
+
+ virtual bool DeallocPMediaParent(PMediaParent* aActor) override;
+
+ virtual bool DeallocPStorageParent(PStorageParent* aActor) override;
+
+ virtual PPresentationParent* AllocPPresentationParent() override;
+
+ virtual bool DeallocPPresentationParent(PPresentationParent* aActor) override;
+
+ virtual bool RecvPPresentationConstructor(PPresentationParent* aActor) override;
+
+ virtual PFlyWebPublishedServerParent*
+ AllocPFlyWebPublishedServerParent(const nsString& name,
+ const FlyWebPublishOptions& params) override;
+
+ virtual bool DeallocPFlyWebPublishedServerParent(PFlyWebPublishedServerParent* aActor) override;
+
+ virtual PSpeechSynthesisParent* AllocPSpeechSynthesisParent() override;
+
+ virtual bool
+ DeallocPSpeechSynthesisParent(PSpeechSynthesisParent* aActor) override;
+
+ virtual bool
+ RecvPSpeechSynthesisConstructor(PSpeechSynthesisParent* aActor) override;
+
+ virtual PWebBrowserPersistDocumentParent*
+ AllocPWebBrowserPersistDocumentParent(PBrowserParent* aBrowser,
+ const uint64_t& aOuterWindowID) override;
+
+ virtual bool
+ DeallocPWebBrowserPersistDocumentParent(PWebBrowserPersistDocumentParent* aActor) override;
+
+ virtual bool RecvReadPrefsArray(InfallibleTArray<PrefSetting>* aPrefs) override;
+ virtual bool RecvGetGfxVars(InfallibleTArray<GfxVarUpdate>* aVars) override;
+
+ virtual bool RecvReadFontList(InfallibleTArray<FontListEntry>* retValue) override;
+
+ virtual bool RecvReadDataStorageArray(const nsString& aFilename,
+ InfallibleTArray<DataStorageItem>* aValues) override;
+
+ virtual bool RecvReadPermissions(InfallibleTArray<IPC::Permission>* aPermissions) override;
+
+ virtual bool RecvSetClipboard(const IPCDataTransfer& aDataTransfer,
+ const bool& aIsPrivateData,
+ const IPC::Principal& aRequestingPrincipal,
+ const int32_t& aWhichClipboard) override;
+
+ virtual bool RecvGetClipboard(nsTArray<nsCString>&& aTypes,
+ const int32_t& aWhichClipboard,
+ IPCDataTransfer* aDataTransfer) override;
+
+ virtual bool RecvEmptyClipboard(const int32_t& aWhichClipboard) override;
+
+ virtual bool RecvClipboardHasType(nsTArray<nsCString>&& aTypes,
+ const int32_t& aWhichClipboard,
+ bool* aHasType) override;
+
+ virtual bool RecvGetSystemColors(const uint32_t& colorsCount,
+ InfallibleTArray<uint32_t>* colors) override;
+
+ virtual bool RecvGetIconForExtension(const nsCString& aFileExt,
+ const uint32_t& aIconSize,
+ InfallibleTArray<uint8_t>* bits) override;
+
+ virtual bool RecvGetShowPasswordSetting(bool* showPassword) override;
+
+ virtual bool RecvStartVisitedQuery(const URIParams& uri) override;
+
+ virtual bool RecvVisitURI(const URIParams& uri,
+ const OptionalURIParams& referrer,
+ const uint32_t& flags) override;
+
+ virtual bool RecvSetURITitle(const URIParams& uri,
+ const nsString& title) override;
+
+ bool HasNotificationPermission(const IPC::Principal& aPrincipal);
+
+ virtual bool RecvShowAlert(const AlertNotificationType& aAlert) override;
+
+ virtual bool RecvCloseAlert(const nsString& aName,
+ const IPC::Principal& aPrincipal) override;
+
+ virtual bool RecvDisableNotifications(const IPC::Principal& aPrincipal) override;
+
+ virtual bool RecvOpenNotificationSettings(const IPC::Principal& aPrincipal) override;
+
+ virtual bool RecvLoadURIExternal(const URIParams& uri,
+ PBrowserParent* windowContext) override;
+ virtual bool RecvExtProtocolChannelConnectParent(const uint32_t& registrarId) override;
+
+ virtual bool RecvSyncMessage(const nsString& aMsg,
+ const ClonedMessageData& aData,
+ InfallibleTArray<CpowEntry>&& aCpows,
+ const IPC::Principal& aPrincipal,
+ nsTArray<StructuredCloneData>* aRetvals) override;
+
+ virtual bool RecvRpcMessage(const nsString& aMsg,
+ const ClonedMessageData& aData,
+ InfallibleTArray<CpowEntry>&& aCpows,
+ const IPC::Principal& aPrincipal,
+ nsTArray<StructuredCloneData>* aRetvals) override;
+
+ virtual bool RecvAsyncMessage(const nsString& aMsg,
+ InfallibleTArray<CpowEntry>&& aCpows,
+ const IPC::Principal& aPrincipal,
+ const ClonedMessageData& aData) override;
+
+ virtual bool RecvAddGeolocationListener(const IPC::Principal& aPrincipal,
+ const bool& aHighAccuracy) override;
+ virtual bool RecvRemoveGeolocationListener() override;
+
+ virtual bool RecvSetGeolocationHigherAccuracy(const bool& aEnable) override;
+
+ virtual bool RecvConsoleMessage(const nsString& aMessage) override;
+
+ virtual bool 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) override;
+
+ virtual bool RecvPrivateDocShellsExist(const bool& aExist) override;
+
+ virtual bool RecvFirstIdle() override;
+
+ virtual bool RecvAudioChannelChangeDefVolChannel(const int32_t& aChannel,
+ const bool& aHidden) override;
+
+ virtual bool RecvAudioChannelServiceStatus(const bool& aTelephonyChannel,
+ const bool& aContentOrNormalChannel,
+ const bool& aAnyChannel) override;
+
+ virtual bool RecvGetLookAndFeelCache(nsTArray<LookAndFeelInt>* aLookAndFeelIntCache) override;
+
+ virtual bool RecvSpeakerManagerGetSpeakerStatus(bool* aValue) override;
+
+ virtual bool RecvSpeakerManagerForceSpeaker(const bool& aEnable) override;
+
+ virtual bool RecvCreateFakeVolume(const nsString& aFsName,
+ const nsString& aMountPoint) override;
+
+ virtual bool RecvSetFakeVolumeState(const nsString& aFsName,
+ const int32_t& aFsState) override;
+
+ virtual bool RecvRemoveFakeVolume(const nsString& fsName) override;
+
+ virtual bool RecvKeywordToURI(const nsCString& aKeyword,
+ nsString* aProviderName,
+ OptionalInputStreamParams* aPostData,
+ OptionalURIParams* aURI) override;
+
+ virtual bool RecvNotifyKeywordSearchLoading(const nsString &aProvider,
+ const nsString &aKeyword) override;
+
+ virtual bool RecvCopyFavicon(const URIParams& aOldURI,
+ const URIParams& aNewURI,
+ const IPC::Principal& aLoadingPrincipal,
+ const bool& aInPrivateBrowsing) override;
+
+ virtual void ProcessingError(Result aCode, const char* aMsgName) override;
+
+ virtual bool RecvAllocateLayerTreeId(const ContentParentId& aCpId,
+ const TabId& aTabId,
+ uint64_t* aId) override;
+
+ virtual bool RecvDeallocateLayerTreeId(const uint64_t& aId) override;
+
+ virtual bool RecvGetGraphicsFeatureStatus(const int32_t& aFeature,
+ int32_t* aStatus,
+ nsCString* aFailureId,
+ bool* aSuccess) override;
+
+ virtual bool RecvGraphicsError(const nsCString& aError) override;
+
+ virtual bool
+ RecvBeginDriverCrashGuard(const uint32_t& aGuardType,
+ bool* aOutCrashed) override;
+
+ virtual bool RecvEndDriverCrashGuard(const uint32_t& aGuardType) override;
+
+ virtual bool RecvAddIdleObserver(const uint64_t& observerId,
+ const uint32_t& aIdleTimeInS) override;
+
+ virtual bool RecvRemoveIdleObserver(const uint64_t& observerId,
+ const uint32_t& aIdleTimeInS) override;
+
+ virtual bool
+ RecvBackUpXResources(const FileDescriptor& aXSocketFd) override;
+
+ virtual bool
+ RecvOpenAnonymousTemporaryFile(FileDescOrError* aFD) override;
+
+ virtual bool
+ RecvKeygenProcessValue(const nsString& oldValue, const nsString& challenge,
+ const nsString& keytype, const nsString& keyparams,
+ nsString* newValue) override;
+
+ virtual bool
+ RecvKeygenProvideContent(nsString* aAttribute,
+ nsTArray<nsString>* aContent) override;
+
+ virtual PFileDescriptorSetParent*
+ AllocPFileDescriptorSetParent(const mozilla::ipc::FileDescriptor&) override;
+
+ virtual bool
+ DeallocPFileDescriptorSetParent(PFileDescriptorSetParent*) override;
+
+ virtual PWebrtcGlobalParent* AllocPWebrtcGlobalParent() override;
+ virtual bool DeallocPWebrtcGlobalParent(PWebrtcGlobalParent *aActor) override;
+
+
+ virtual bool RecvUpdateDropEffect(const uint32_t& aDragAction,
+ const uint32_t& aDropEffect) override;
+
+ virtual bool RecvProfile(const nsCString& aProfile) override;
+
+ virtual bool RecvGetGraphicsDeviceInitData(ContentDeviceData* aOut) override;
+
+ void StartProfiler(nsIProfilerStartParams* aParams);
+
+ virtual bool RecvGetAndroidSystemInfo(AndroidSystemInfo* aInfo) override;
+
+ virtual bool RecvNotifyBenchmarkResult(const nsString& aCodecName,
+ const uint32_t& aDecodeFPS) override;
+
+ virtual bool RecvNotifyPushObservers(const nsCString& aScope,
+ const IPC::Principal& aPrincipal,
+ const nsString& aMessageId) override;
+
+ virtual bool RecvNotifyPushObserversWithData(const nsCString& aScope,
+ const IPC::Principal& aPrincipal,
+ const nsString& aMessageId,
+ InfallibleTArray<uint8_t>&& aData) override;
+
+ virtual bool RecvNotifyPushSubscriptionChangeObservers(const nsCString& aScope,
+ const IPC::Principal& aPrincipal) override;
+
+ virtual bool RecvNotifyPushSubscriptionModifiedObservers(const nsCString& aScope,
+ const IPC::Principal& aPrincipal) override;
+
+ virtual bool RecvNotifyLowMemory() override;
+
+ virtual bool RecvGetFilesRequest(const nsID& aID,
+ const nsString& aDirectoryPath,
+ const bool& aRecursiveFlag) override;
+
+ virtual bool RecvDeleteGetFilesRequest(const nsID& aID) override;
+
+ virtual bool RecvAccumulateChildHistogram(
+ InfallibleTArray<Accumulation>&& aAccumulations) override;
+ virtual bool RecvAccumulateChildKeyedHistogram(
+ InfallibleTArray<KeyedAccumulation>&& aAccumulations) override;
+public:
+ void SendGetFilesResponseAndForget(const nsID& aID,
+ const GetFilesResponseResult& aResult);
+
+private:
+
+ // If you add strong pointers to cycle collected objects here, be sure to
+ // release these objects in ShutDownProcess. See the comment there for more
+ // details.
+
+ GeckoChildProcessHost* mSubprocess;
+ ContentParent* mOpener;
+
+ ContentParentId mChildID;
+ int32_t mGeolocationWatchID;
+
+ nsString mAppManifestURL;
+
+ nsCString mKillHardAnnotation;
+
+ /**
+ * We cache mAppName instead of looking it up using mAppManifestURL when we
+ * need it because it turns out that getting an app from the apps service is
+ * expensive.
+ */
+ nsString mAppName;
+
+ // 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;
+ // How many tabs we're waiting to finish their destruction
+ // sequence. Precisely, how many TabParents have called
+ // NotifyTabDestroying() but not called NotifyTabDestroyed().
+ int32_t mNumDestroyingTabs;
+ // True only while this is ready to be used to host remote tabs.
+ // This must not be used for new purposes after mIsAlive goes to
+ // false, but some previously scheduled IPC traffic may still pass
+ // through.
+ bool mIsAlive;
+
+ // True only the if process is already a browser or app or has
+ // been transformed into one.
+ bool mMetamorphosed;
+
+ bool mSendPermissionUpdates;
+ bool mIsForBrowser;
+
+ // These variables track whether we've called Close() and KillHard() on our
+ // channel.
+ bool mCalledClose;
+ bool mCalledKillHard;
+ bool mCreatedPairedMinidumps;
+ bool mShutdownPending;
+ bool mIPCOpen;
+
+ friend class CrashReporterParent;
+
+ RefPtr<nsConsoleService> mConsoleService;
+ nsConsoleService* GetConsoleService();
+
+ 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;
+
+#ifdef MOZ_ENABLE_PROFILER_SPS
+ RefPtr<mozilla::ProfileGatherer> mGatherer;
+#endif
+ nsCString mProfile;
+
+ UniquePtr<gfx::DriverCrashGuard> mDriverCrashGuard;
+
+#if defined(XP_LINUX) && defined(MOZ_CONTENT_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;
+
+ nsTArray<nsCString> mBlobURLs;
+ bool mLargeAllocationProcess;
+};
+
+} // 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() {}
+
+ 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 000000000..66125f332
--- /dev/null
+++ b/dom/ipc/ContentProcess.cpp
@@ -0,0 +1,162 @@
+/* -*- 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"
+
+#if defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX)
+#include "mozilla/WindowsVersion.h"
+#endif
+
+#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
+#include <stdlib.h>
+#endif
+
+#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
+#include "mozilla/Preferences.h"
+#include "nsAppDirectoryServiceDefs.h"
+#include "nsDirectoryService.h"
+#include "nsDirectoryServiceDefs.h"
+#endif
+
+using mozilla::ipc::IOThreadChild;
+
+namespace mozilla {
+namespace dom {
+
+#if defined(XP_WIN) && defined(MOZ_CONTENT_SANDBOX)
+static bool
+IsSandboxTempDirRequired()
+{
+ // On Windows, a sandbox-writable temp directory is only used
+ // for Vista or later with sandbox pref level >= 1.
+ return (IsVistaOrLater() &&
+ (Preferences::GetInt("security.sandbox.content.level") >= 1));
+}
+
+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_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
+static bool
+IsSandboxTempDirRequired()
+{
+ // On OSX, use the sandbox-writable temp when the pref level >= 1.
+ return (Preferences::GetInt("security.sandbox.content.level") >= 1);
+}
+
+static void
+SetTmpEnvironmentVariable(nsIFile* aValue)
+{
+ nsAutoCString fullTmpPath;
+ nsresult rv = aValue->GetNativePath(fullTmpPath);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return;
+ }
+ Unused << NS_WARN_IF(setenv("TMPDIR", fullTmpPath.get(), 1) != 0);
+}
+#endif
+
+#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
+static void
+SetUpSandboxEnvironment()
+{
+ MOZ_ASSERT(nsDirectoryService::gService,
+ "SetUpSandboxEnvironment relies on nsDirectoryService being initialized");
+
+ if (!IsSandboxTempDirRequired()) {
+ 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
+
+void
+ContentProcess::SetAppDir(const nsACString& aPath)
+{
+ mXREEmbed.SetAppDir(aPath);
+}
+
+#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
+void
+ContentProcess::SetProfile(const nsACString& aProfile)
+{
+ bool flag;
+ nsresult rv =
+ XRE_GetFileFromPath(aProfile.BeginReading(), getter_AddRefs(mProfileDir));
+ if (NS_FAILED(rv) ||
+ NS_FAILED(mProfileDir->Exists(&flag)) || !flag) {
+ NS_WARNING("Invalid profile directory passed to content process.");
+ mProfileDir = nullptr;
+ }
+}
+#endif
+
+bool
+ContentProcess::Init()
+{
+ mContent.Init(IOThreadChild::message_loop(),
+ ParentPid(),
+ IOThreadChild::channel());
+ mXREEmbed.Start();
+ mContent.InitXPCOM();
+ mContent.InitGraphicsDeviceData();
+
+#if (defined(XP_MACOSX)) && defined(MOZ_CONTENT_SANDBOX)
+ mContent.SetProfileDir(mProfileDir);
+#endif
+
+#if (defined(XP_WIN) || defined(XP_MACOSX)) && defined(MOZ_CONTENT_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 dom
+} // namespace mozilla
diff --git a/dom/ipc/ContentProcess.h b/dom/ipc/ContentProcess.h
new file mode 100644
index 000000000..bf9968f8c
--- /dev/null
+++ b/dom/ipc/ContentProcess.h
@@ -0,0 +1,65 @@
+/* -*- 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/MainThreadRuntime.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()
+ { }
+
+ virtual bool Init() override;
+ virtual void CleanUp() override;
+
+ void SetAppDir(const nsACString& aPath);
+
+#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
+ void SetProfile(const nsACString& aProfile);
+#endif
+
+private:
+ ContentChild mContent;
+ mozilla::ipc::ScopedXREEmbed mXREEmbed;
+
+#if defined(XP_MACOSX) && defined(MOZ_CONTENT_SANDBOX)
+ nsCOMPtr<nsIFile> mProfileDir;
+#endif
+
+#if defined(XP_WIN)
+ // This object initializes and configures COM.
+ mozilla::mscom::MainThreadRuntime mCOMRuntime;
+#endif
+
+ DISALLOW_EVIL_CONSTRUCTORS(ContentProcess);
+};
+
+} // 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 000000000..02fdcba34
--- /dev/null
+++ b/dom/ipc/ContentProcessManager.cpp
@@ -0,0 +1,372 @@
+/* -*- 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/TabParent.h"
+
+#include "mozilla/StaticPtr.h"
+#include "mozilla/ClearOnShutdown.h"
+
+#include "nsPrintfCString.h"
+#include "nsIScriptSecurityManager.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
+
+namespace mozilla {
+namespace dom {
+
+static uint64_t gTabId = 0;
+
+/* 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,
+ const ContentParentId& aParentCpId)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aChildCp);
+
+ ContentProcessInfo info;
+ info.mCp = aChildCp;
+ info.mParentCpId = aParentCpId;
+ mContentParentMap[aChildCp->ChildID()] = info;
+}
+
+void
+ContentProcessManager::RemoveContentProcess(const ContentParentId& aChildCpId)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mContentParentMap.find(aChildCpId) != mContentParentMap.end());
+
+ mContentParentMap.erase(aChildCpId);
+ for (auto iter = mContentParentMap.begin();
+ iter != mContentParentMap.end();
+ ++iter) {
+ if (!iter->second.mChildrenCpId.empty()) {
+ iter->second.mChildrenCpId.erase(aChildCpId);
+ }
+ }
+}
+
+bool
+ContentProcessManager::AddGrandchildProcess(const ContentParentId& aParentCpId,
+ const ContentParentId& aChildCpId)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ auto iter = mContentParentMap.find(aParentCpId);
+ if (NS_WARN_IF(iter == mContentParentMap.end())) {
+ ASSERT_UNLESS_FUZZING("Parent process should be already in map!");
+ return false;
+ }
+ iter->second.mChildrenCpId.insert(aChildCpId);
+ return true;
+}
+
+bool
+ContentProcessManager::GetParentProcessId(const ContentParentId& aChildCpId,
+ /*out*/ ContentParentId* aParentCpId)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ auto iter = mContentParentMap.find(aChildCpId);
+ if (NS_WARN_IF(iter == mContentParentMap.end())) {
+ ASSERT_UNLESS_FUZZING();
+ return false;
+ }
+ *aParentCpId = iter->second.mParentCpId;
+ return true;
+}
+
+ContentParent*
+ContentProcessManager::GetContentProcessById(const ContentParentId& aChildCpId)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ auto iter = mContentParentMap.find(aChildCpId);
+ if (NS_WARN_IF(iter == mContentParentMap.end())) {
+ ASSERT_UNLESS_FUZZING();
+ return nullptr;
+ }
+ return iter->second.mCp;
+}
+
+nsTArray<ContentParentId>
+ContentProcessManager::GetAllChildProcessById(const ContentParentId& aParentCpId)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsTArray<ContentParentId> cpIdArray;
+ auto iter = mContentParentMap.find(aParentCpId);
+ if (NS_WARN_IF(iter == mContentParentMap.end())) {
+ ASSERT_UNLESS_FUZZING();
+ return Move(cpIdArray);
+ }
+
+ for (auto cpIter = iter->second.mChildrenCpId.begin();
+ cpIter != iter->second.mChildrenCpId.end();
+ ++cpIter) {
+ cpIdArray.AppendElement(*cpIter);
+ }
+
+ return Move(cpIdArray);
+}
+
+TabId
+ContentProcessManager::AllocateTabId(const TabId& aOpenerTabId,
+ const IPCTabContext& aContext,
+ const ContentParentId& aChildCpId)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ auto iter = mContentParentMap.find(aChildCpId);
+ if (NS_WARN_IF(iter == mContentParentMap.end())) {
+ ASSERT_UNLESS_FUZZING();
+ return TabId(0);
+ }
+
+ struct RemoteFrameInfo info;
+
+ // If it's a PopupIPCTabContext, it's the case that a TabChild want to
+ // open a new tab. aOpenerTabId has to be it's parent frame's opener id.
+ if (aContext.type() == IPCTabContext::TPopupIPCTabContext) {
+ auto remoteFrameIter = iter->second.mRemoteFrames.find(aOpenerTabId);
+ if (remoteFrameIter == iter->second.mRemoteFrames.end()) {
+ ASSERT_UNLESS_FUZZING("Failed to find parent frame's opener id.");
+ return TabId(0);
+ }
+
+ info.mOpenerTabId = remoteFrameIter->second.mOpenerTabId;
+
+ const PopupIPCTabContext &ipcContext = aContext.get_PopupIPCTabContext();
+ MOZ_ASSERT(ipcContext.opener().type() == PBrowserOrId::TTabId);
+
+ remoteFrameIter = iter->second.mRemoteFrames.find(ipcContext.opener().get_TabId());
+ if (remoteFrameIter == iter->second.mRemoteFrames.end()) {
+ ASSERT_UNLESS_FUZZING("Failed to find tab id.");
+ return TabId(0);
+ }
+
+ info.mContext = remoteFrameIter->second.mContext;
+ }
+ else {
+ MaybeInvalidTabContext tc(aContext);
+ if (!tc.IsValid()) {
+ NS_ERROR(nsPrintfCString("Received an invalid TabContext from "
+ "the child process. (%s)",
+ tc.GetInvalidReason()).get());
+ return TabId(0);
+ }
+ info.mOpenerTabId = aOpenerTabId;
+ info.mContext = tc.GetTabContext();
+ }
+
+ mUniqueId = ++gTabId;
+ iter->second.mRemoteFrames[mUniqueId] = info;
+
+ return mUniqueId;
+}
+
+void
+ContentProcessManager::DeallocateTabId(const ContentParentId& aChildCpId,
+ const TabId& aChildTabId)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ auto iter = mContentParentMap.find(aChildCpId);
+ if (NS_WARN_IF(iter == mContentParentMap.end())) {
+ ASSERT_UNLESS_FUZZING();
+ return;
+ }
+
+ auto remoteFrameIter = iter->second.mRemoteFrames.find(aChildTabId);
+ if (remoteFrameIter != iter->second.mRemoteFrames.end()) {
+ iter->second.mRemoteFrames.erase(aChildTabId);
+ }
+}
+
+bool
+ContentProcessManager::GetTabContextByProcessAndTabId(const ContentParentId& aChildCpId,
+ const TabId& aChildTabId,
+ /*out*/ TabContext* aTabContext)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aTabContext);
+
+ auto iter = mContentParentMap.find(aChildCpId);
+ if (NS_WARN_IF(iter == mContentParentMap.end())) {
+ ASSERT_UNLESS_FUZZING();
+ return false;
+ }
+
+ auto remoteFrameIter = iter->second.mRemoteFrames.find(aChildTabId);
+ if (NS_WARN_IF(remoteFrameIter == iter->second.mRemoteFrames.end())) {
+ ASSERT_UNLESS_FUZZING();
+ return false;
+ }
+
+ *aTabContext = remoteFrameIter->second.mContext;
+
+ return true;
+}
+
+nsTArray<TabContext>
+ContentProcessManager::GetTabContextByContentProcess(const ContentParentId& aChildCpId)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsTArray<TabContext> tabContextArray;
+ auto iter = mContentParentMap.find(aChildCpId);
+ if (NS_WARN_IF(iter == mContentParentMap.end())) {
+ ASSERT_UNLESS_FUZZING();
+ return Move(tabContextArray);
+ }
+
+ for (auto remoteFrameIter = iter->second.mRemoteFrames.begin();
+ remoteFrameIter != iter->second.mRemoteFrames.end();
+ ++remoteFrameIter) {
+ tabContextArray.AppendElement(remoteFrameIter->second.mContext);
+ }
+
+ return Move(tabContextArray);
+}
+
+bool
+ContentProcessManager::GetRemoteFrameOpenerTabId(const ContentParentId& aChildCpId,
+ const TabId& aChildTabId,
+ /*out*/TabId* aOpenerTabId)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ auto iter = mContentParentMap.find(aChildCpId);
+ if (NS_WARN_IF(iter == mContentParentMap.end())) {
+ ASSERT_UNLESS_FUZZING();
+ return false;
+ }
+
+ auto remoteFrameIter = iter->second.mRemoteFrames.find(aChildTabId);
+ if (NS_WARN_IF(remoteFrameIter == iter->second.mRemoteFrames.end())) {
+ ASSERT_UNLESS_FUZZING();
+ return false;
+ }
+
+ *aOpenerTabId = remoteFrameIter->second.mOpenerTabId;
+
+ return true;
+}
+
+already_AddRefed<TabParent>
+ContentProcessManager::GetTabParentByProcessAndTabId(const ContentParentId& aChildCpId,
+ const TabId& aChildTabId)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ auto iter = mContentParentMap.find(aChildCpId);
+ if (NS_WARN_IF(iter == mContentParentMap.end())) {
+ ASSERT_UNLESS_FUZZING();
+ return nullptr;
+ }
+
+ const ManagedContainer<PBrowserParent>& browsers = iter->second.mCp->ManagedPBrowserParent();
+ for (auto iter = browsers.ConstIter(); !iter.Done(); iter.Next()) {
+ RefPtr<TabParent> tab = TabParent::GetFrom(iter.Get()->GetKey());
+ if (tab->GetTabId() == aChildTabId) {
+ return tab.forget();
+ }
+ }
+
+ return nullptr;
+}
+
+already_AddRefed<TabParent>
+ContentProcessManager::GetTopLevelTabParentByProcessAndTabId(const ContentParentId& aChildCpId,
+ const TabId& aChildTabId)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // Used to keep the current ContentParentId and the current TabId
+ // in the iteration(do-while loop below)
+ ContentParentId currentCpId;
+ TabId currentTabId;
+
+ // To get the ContentParentId and the TabParentId on upper level
+ ContentParentId parentCpId = aChildCpId;
+ TabId openerTabId = aChildTabId;
+
+ // Stop this loop when the upper ContentParentId of
+ // the current ContentParentId is chrome(ContentParentId = 0).
+ do {
+ // Update the current ContentParentId and TabId in iteration
+ currentCpId = parentCpId;
+ currentTabId = openerTabId;
+
+ // Get the ContentParentId and TabId on upper level
+ if (!GetParentProcessId(currentCpId, &parentCpId) ||
+ !GetRemoteFrameOpenerTabId(currentCpId, currentTabId, &openerTabId)) {
+ return nullptr;
+ }
+ } while (parentCpId);
+
+ // Get the top level TabParent by the current ContentParentId and TabId
+ return GetTabParentByProcessAndTabId(currentCpId, currentTabId);
+}
+
+nsTArray<TabId>
+ContentProcessManager::GetTabParentsByProcessId(const ContentParentId& aChildCpId)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsTArray<TabId> tabIdList;
+ auto iter = mContentParentMap.find(aChildCpId);
+ if (NS_WARN_IF(iter == mContentParentMap.end())) {
+ ASSERT_UNLESS_FUZZING();
+ return Move(tabIdList);
+ }
+
+ for (auto remoteFrameIter = iter->second.mRemoteFrames.begin();
+ remoteFrameIter != iter->second.mRemoteFrames.end();
+ ++remoteFrameIter) {
+ tabIdList.AppendElement(remoteFrameIter->first);
+ }
+
+ return Move(tabIdList);
+}
+
+uint32_t
+ContentProcessManager::GetAppIdByProcessAndTabId(const ContentParentId& aChildCpId,
+ const TabId& aChildTabId)
+{
+ uint32_t appId = nsIScriptSecurityManager::NO_APP_ID;
+ if (aChildCpId && aChildTabId) {
+ TabContext tabContext;
+ if (GetTabContextByProcessAndTabId(aChildCpId, aChildTabId, &tabContext)) {
+ appId = tabContext.OwnOrContainingAppId();
+ }
+ }
+ return appId;
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/ContentProcessManager.h b/dom/ipc/ContentProcessManager.h
new file mode 100644
index 000000000..cc6d8bd8b
--- /dev/null
+++ b/dom/ipc/ContentProcessManager.h
@@ -0,0 +1,166 @@
+/* -*- 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 <map>
+#include <set>
+#include "mozilla/StaticPtr.h"
+#include "mozilla/dom/TabContext.h"
+#include "mozilla/dom/ipc/IdType.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace dom {
+class ContentParent;
+
+struct RemoteFrameInfo
+{
+ TabId mOpenerTabId;
+ TabContext mContext;
+};
+
+struct ContentProcessInfo
+{
+ ContentParent* mCp;
+ ContentParentId mParentCpId;
+ std::set<ContentParentId> mChildrenCpId;
+ std::map<TabId, RemoteFrameInfo> mRemoteFrames;
+};
+
+class ContentProcessManager final
+{
+public:
+ static ContentProcessManager* GetSingleton();
+ ~ContentProcessManager() {MOZ_COUNT_DTOR(ContentProcessManager);};
+
+ /**
+ * Add a new content process into the map.
+ * If aParentCpId is not 0, it's a nested content process.
+ */
+ void AddContentProcess(ContentParent* aChildCp,
+ const ContentParentId& aParentCpId = ContentParentId(0));
+ /**
+ * Remove the content process by id.
+ */
+ void RemoveContentProcess(const ContentParentId& aChildCpId);
+ /**
+ * Add a grandchild content process into the map.
+ * aParentCpId must be already added in the map by AddContentProcess().
+ */
+ bool AddGrandchildProcess(const ContentParentId& aParentCpId,
+ const ContentParentId& aChildCpId);
+ /**
+ * Get the parent process's id by child process's id.
+ * Used to check if a child really belongs to the parent.
+ */
+ bool GetParentProcessId(const ContentParentId& aChildCpId,
+ /*out*/ ContentParentId* aParentCpId);
+ /**
+ * Return the ContentParent pointer by id.
+ */
+ ContentParent* GetContentProcessById(const ContentParentId& aChildCpId);
+
+ /**
+ * Return a list of all child process's id.
+ */
+ nsTArray<ContentParentId>
+ GetAllChildProcessById(const ContentParentId& aParentCpId);
+
+ /**
+ * Allocate a tab id for the given content process's id.
+ * Used when a content process wants to create a new tab. aOpenerTabId and
+ * aContext are saved in RemoteFrameInfo, which is a part of
+ * ContentProcessInfo. We can use the tab id and process id to locate the
+ * TabContext for future use.
+ */
+ TabId AllocateTabId(const TabId& aOpenerTabId,
+ const IPCTabContext& aContext,
+ const ContentParentId& aChildCpId);
+
+ /**
+ * Remove the RemoteFrameInfo by the given process and tab id.
+ */
+ void DeallocateTabId(const ContentParentId& aChildCpId,
+ const TabId& aChildTabId);
+
+ /**
+ * Get the TabContext by the given content process and tab id.
+ */
+ bool
+ GetTabContextByProcessAndTabId(const ContentParentId& aChildCpId,
+ const TabId& aChildTabId,
+ /*out*/ TabContext* aTabContext);
+
+ /**
+ * Get all TabContext which are inside the given content process.
+ * Used for AppProcessChecker to cehck app status.
+ */
+ nsTArray<TabContext>
+ GetTabContextByContentProcess(const ContentParentId& aChildCpId);
+
+ /**
+ * Query a tab's opener id by the given process and tab id.
+ * XXX Currently not used. Plan to be used for bug 1020179.
+ */
+ bool GetRemoteFrameOpenerTabId(const ContentParentId& aChildCpId,
+ const TabId& aChildTabId,
+ /*out*/ TabId* aOpenerTabId);
+
+ /**
+ * Get all TabParents' Ids managed by the givent content process.
+ * Return empty array when TabParent couldn't be found via aChildCpId
+ */
+ nsTArray<TabId>
+ GetTabParentsByProcessId(const ContentParentId& aChildCpId);
+
+ /**
+ * Get the TabParent by the given content process and tab id.
+ * Return nullptr when TabParent couldn't be found via aChildCpId
+ * and aChildTabId.
+ * (or probably because the TabParent is not in the chrome process)
+ */
+ already_AddRefed<TabParent>
+ GetTabParentByProcessAndTabId(const ContentParentId& aChildCpId,
+ const TabId& aChildTabId);
+
+ /**
+ * Get the TabParent on top level by the given content process and tab id.
+ *
+ * This function return the TabParent belong to the chrome process,
+ * called top-level TabParent here, by given aChildCpId and aChildTabId.
+ * The given aChildCpId and aChildTabId are related to a content process
+ * and a tab respectively. In nested-oop, the top-level TabParent isn't
+ * always the opener tab of the given tab in content process. This function
+ * will call GetTabParentByProcessAndTabId iteratively until the Tab returned
+ * is belong to the chrome process.
+ */
+ already_AddRefed<TabParent>
+ GetTopLevelTabParentByProcessAndTabId(const ContentParentId& aChildCpId,
+ const TabId& aChildTabId);
+
+ /**
+ * Return appId by given TabId and ContentParentId.
+ * It will return nsIScriptSecurityManager::NO_APP_ID
+ * if the given tab is not an app.
+ */
+ uint32_t
+ GetAppIdByProcessAndTabId(const ContentParentId& aChildCpId,
+ const TabId& aChildTabId);
+
+private:
+ static StaticAutoPtr<ContentProcessManager> sSingleton;
+ TabId mUniqueId;
+ std::map<ContentParentId, ContentProcessInfo> mContentParentMap;
+
+ ContentProcessManager() {MOZ_COUNT_CTOR(ContentProcessManager);};
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_ContentProcessManager_h
diff --git a/dom/ipc/CrashReporterChild.cpp b/dom/ipc/CrashReporterChild.cpp
new file mode 100644
index 000000000..8174452e7
--- /dev/null
+++ b/dom/ipc/CrashReporterChild.cpp
@@ -0,0 +1,42 @@
+/* -*- 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/plugins/PluginModuleChild.h"
+#include "ContentChild.h"
+#include "CrashReporterChild.h"
+#include "nsXULAppAPI.h"
+
+using mozilla::plugins::PluginModuleChild;
+
+namespace mozilla {
+namespace dom {
+
+/*static*/
+PCrashReporterChild*
+CrashReporterChild::GetCrashReporter()
+{
+ const ManagedContainer<PCrashReporterChild>* reporters = nullptr;
+ switch (XRE_GetProcessType()) {
+ case GeckoProcessType_Content: {
+ ContentChild* child = ContentChild::GetSingleton();
+ reporters = &child->ManagedPCrashReporterChild();
+ break;
+ }
+ case GeckoProcessType_Plugin: {
+ PluginModuleChild* child = PluginModuleChild::GetChrome();
+ reporters = &child->ManagedPCrashReporterChild();
+ break;
+ }
+ default:
+ break;
+ }
+ if (!reporters) {
+ return nullptr;
+ }
+ return LoneManagedOrNullAsserts(*reporters);
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/CrashReporterChild.h b/dom/ipc/CrashReporterChild.h
new file mode 100644
index 000000000..96355ca11
--- /dev/null
+++ b/dom/ipc/CrashReporterChild.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_CrashReporterChild_h
+#define mozilla_dom_CrashReporterChild_h
+
+#include "mozilla/dom/PCrashReporterChild.h"
+
+namespace mozilla {
+namespace dom {
+
+class CrashReporterChild :
+ public PCrashReporterChild
+{
+public:
+ CrashReporterChild() {
+ MOZ_COUNT_CTOR(CrashReporterChild);
+ }
+ ~CrashReporterChild() {
+ MOZ_COUNT_DTOR(CrashReporterChild);
+ }
+
+ static PCrashReporterChild* GetCrashReporter();
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_CrashReporterChild_h
diff --git a/dom/ipc/CrashReporterParent.cpp b/dom/ipc/CrashReporterParent.cpp
new file mode 100644
index 000000000..fc627387f
--- /dev/null
+++ b/dom/ipc/CrashReporterParent.cpp
@@ -0,0 +1,146 @@
+/* -*- 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 "CrashReporterParent.h"
+#include "mozilla/Sprintf.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/ipc/CrashReporterHost.h"
+#include "nsAutoPtr.h"
+#include "nsXULAppAPI.h"
+#include <time.h>
+
+#include "mozilla/Telemetry.h"
+
+#ifdef MOZ_CRASHREPORTER
+#include "nsExceptionHandler.h"
+#include "nsICrashService.h"
+#include "mozilla/SyncRunnable.h"
+#include "nsThreadUtils.h"
+#endif
+
+namespace mozilla {
+namespace dom {
+
+using namespace mozilla::ipc;
+
+void
+CrashReporterParent::AnnotateCrashReport(const nsCString& key,
+ const nsCString& data)
+{
+#ifdef MOZ_CRASHREPORTER
+ mNotes.Put(key, data);
+#endif
+}
+
+void
+CrashReporterParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+ // Implement me! Bug 1005155
+}
+
+bool
+CrashReporterParent::RecvAppendAppNotes(const nsCString& data)
+{
+ mAppNotes.Append(data);
+ return true;
+}
+
+CrashReporterParent::CrashReporterParent()
+ :
+#ifdef MOZ_CRASHREPORTER
+ mNotes(4),
+#endif
+ mStartTime(::time(nullptr))
+ , mInitialized(false)
+{
+ MOZ_COUNT_CTOR(CrashReporterParent);
+}
+
+CrashReporterParent::~CrashReporterParent()
+{
+ MOZ_COUNT_DTOR(CrashReporterParent);
+}
+
+void
+CrashReporterParent::SetChildData(const NativeThreadId& tid,
+ const uint32_t& processType)
+{
+ mInitialized = true;
+ mMainThread = tid;
+ mProcessType = GeckoProcessType(processType);
+}
+
+#ifdef MOZ_CRASHREPORTER
+bool
+CrashReporterParent::GenerateCrashReportForMinidump(nsIFile* minidump,
+ const AnnotationTable* processNotes)
+{
+ if (!CrashReporter::GetIDFromMinidump(minidump, mChildDumpID)) {
+ return false;
+ }
+
+ bool result = GenerateChildData(processNotes);
+ FinalizeChildData();
+ return result;
+}
+
+bool
+CrashReporterParent::GenerateChildData(const AnnotationTable* processNotes)
+{
+ MOZ_ASSERT(mInitialized);
+
+ if (mChildDumpID.IsEmpty()) {
+ NS_WARNING("problem with GenerateChildData: no child dump id yet!");
+ return false;
+ }
+
+ nsAutoCString type;
+ switch (mProcessType) {
+ case GeckoProcessType_Content:
+ type = NS_LITERAL_CSTRING("content");
+ break;
+ case GeckoProcessType_Plugin:
+ case GeckoProcessType_GMPlugin:
+ type = NS_LITERAL_CSTRING("plugin");
+ break;
+ default:
+ NS_ERROR("unknown process type");
+ break;
+ }
+ mNotes.Put(NS_LITERAL_CSTRING("ProcessType"), type);
+
+ char startTime[32];
+ SprintfLiteral(startTime, "%lld", static_cast<long long>(mStartTime));
+ mNotes.Put(NS_LITERAL_CSTRING("StartupTime"), nsDependentCString(startTime));
+
+ if (!mAppNotes.IsEmpty()) {
+ mNotes.Put(NS_LITERAL_CSTRING("Notes"), mAppNotes);
+ }
+
+ // Append these notes to the end of the extra file based on the current
+ // dump id we obtained from CreatePairedMinidumps.
+ bool ret = CrashReporter::AppendExtraData(mChildDumpID, mNotes);
+ if (ret && processNotes) {
+ ret = CrashReporter::AppendExtraData(mChildDumpID, *processNotes);
+ }
+
+ if (!ret) {
+ NS_WARNING("problem appending child data to .extra");
+ }
+ return ret;
+}
+
+void
+CrashReporterParent::FinalizeChildData()
+{
+ MOZ_ASSERT(mInitialized);
+
+ CrashReporterHost::NotifyCrashService(mProcessType, mChildDumpID, &mNotes);
+ mNotes.Clear();
+}
+#endif
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/CrashReporterParent.h b/dom/ipc/CrashReporterParent.h
new file mode 100644
index 000000000..25824f279
--- /dev/null
+++ b/dom/ipc/CrashReporterParent.h
@@ -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/. */
+
+#ifndef mozilla_dom_CrashReporterParent_h
+#define mozilla_dom_CrashReporterParent_h
+
+#include "mozilla/dom/PCrashReporterParent.h"
+#include "mozilla/dom/TabMessageUtils.h"
+#include "nsIFile.h"
+#ifdef MOZ_CRASHREPORTER
+#include "nsExceptionHandler.h"
+#include "nsDataHashtable.h"
+#endif
+
+namespace mozilla {
+namespace dom {
+
+class CrashReporterParent : public PCrashReporterParent
+{
+#ifdef MOZ_CRASHREPORTER
+ typedef CrashReporter::AnnotationTable AnnotationTable;
+#endif
+public:
+ CrashReporterParent();
+ virtual ~CrashReporterParent();
+
+#ifdef MOZ_CRASHREPORTER
+
+ /*
+ * Attempt to create a bare-bones crash report, along with extra process-
+ * specific annotations present in the given AnnotationTable. Calls
+ * GenerateChildData and FinalizeChildData.
+ *
+ * @returns true if successful, false otherwise.
+ */
+ template<class Toplevel>
+ bool
+ GenerateCrashReport(Toplevel* t, const AnnotationTable* processNotes);
+
+ /*
+ * Attempt to generate a parent/child pair of minidumps from the given
+ * toplevel actor. This calls CrashReporter::CreateMinidumpsAndPair to
+ * generate the minidumps. Crash reporter annotations set prior to this
+ * call will be saved via PairedDumpCallbackExtra into an .extra file
+ * under the proper crash id. AnnotateCrashReport annotations are not
+ * set in this call and the report is not finalized.
+ *
+ * @returns true if successful, false otherwise.
+ */
+ template<class Toplevel>
+ bool
+ GeneratePairedMinidump(Toplevel* t);
+
+ /*
+ * Attempts to take a minidump of the current process and pair that with
+ * a named minidump handed in by the caller.
+ *
+ * @param aTopLevel - top level actor this reporter is associated with.
+ * @param aMinidump - the minidump to associate with.
+ * @param aPairName - the name of the additional minidump.
+ * @returns true if successful, false otherwise.
+ */
+ template<class Toplevel>
+ bool
+ GenerateMinidumpAndPair(Toplevel* aTopLevel, nsIFile* aMinidump,
+ const nsACString& aPairName);
+
+ /**
+ * Apply child process annotations to an existing paired mindump generated
+ * with GeneratePairedMinidump.
+ *
+ * Be careful about calling generate apis immediately after this call,
+ * see FinalizeChildData.
+ *
+ * @param processNotes (optional) - Additional notes to append. Annotations
+ * stored in mNotes will also be applied. processNotes can be null.
+ * @returns true if successful, false otherwise.
+ */
+ bool
+ GenerateChildData(const AnnotationTable* processNotes);
+
+ /**
+ * Handles main thread finalization tasks after a report has been
+ * generated. Does the following:
+ * - register the finished report with the crash service manager
+ * - records telemetry related data about crashes
+ *
+ * Be careful about calling generate apis immediately after this call,
+ * if this api is called on a non-main thread it will fire off a runnable
+ * to complete its work async.
+ */
+ void
+ FinalizeChildData();
+
+ /*
+ * Attempt to generate a full paired dump complete with any child
+ * annoations, and finalizes the report. Note this call is only valid
+ * on the main thread. Calling on a background thread will fail.
+ *
+ * @returns true if successful, false otherwise.
+ */
+ template<class Toplevel>
+ bool
+ GenerateCompleteMinidump(Toplevel* t);
+
+ /**
+ * Submits a raw minidump handed in, calls GenerateChildData and
+ * FinalizeChildData. Used by content plugins and gmp.
+ *
+ * @returns true if successful, false otherwise.
+ */
+ bool
+ GenerateCrashReportForMinidump(nsIFile* minidump,
+ const AnnotationTable* processNotes);
+
+ /*
+ * Instantiate a new crash reporter actor from a given parent that manages
+ * the protocol.
+ *
+ * @returns true if successful, false otherwise.
+ */
+ template<class Toplevel>
+ static bool CreateCrashReporter(Toplevel* actor);
+#endif // MOZ_CRASHREPORTER
+
+ /*
+ * Initialize this reporter with data from the child process.
+ */
+ void
+ SetChildData(const NativeThreadId& id, const uint32_t& processType);
+
+ /*
+ * Returns the ID of the child minidump.
+ * GeneratePairedMinidump or GenerateCrashReport must be called first.
+ */
+ const nsString& ChildDumpID() const {
+ return mChildDumpID;
+ }
+
+ /*
+ * Add an annotation to our internally tracked list of annotations.
+ * Callers must apply these notes using GenerateChildData otherwise
+ * the notes will get dropped.
+ */
+ void
+ AnnotateCrashReport(const nsCString& aKey, const nsCString& aData);
+
+ protected:
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ virtual bool RecvAnnotateCrashReport(const nsCString& aKey,
+ const nsCString& aData) override
+ {
+ AnnotateCrashReport(aKey, aData);
+ return true;
+ }
+
+ virtual bool RecvAppendAppNotes(const nsCString& aData) override;
+
+#ifdef MOZ_CRASHREPORTER
+ void
+ NotifyCrashService();
+#endif
+
+#ifdef MOZ_CRASHREPORTER
+ AnnotationTable mNotes;
+#endif
+ nsCString mAppNotes;
+ nsString mChildDumpID;
+ // stores the child main thread id
+ NativeThreadId mMainThread;
+ time_t mStartTime;
+ // stores the child process type
+ GeckoProcessType mProcessType;
+ bool mInitialized;
+};
+
+#ifdef MOZ_CRASHREPORTER
+template<class Toplevel>
+inline bool
+CrashReporterParent::GeneratePairedMinidump(Toplevel* t)
+{
+ mozilla::ipc::ScopedProcessHandle child;
+#ifdef XP_MACOSX
+ child = t->Process()->GetChildTask();
+#else
+ if (!base::OpenPrivilegedProcessHandle(t->OtherPid(), &child.rwget())) {
+ NS_WARNING("Failed to open child process handle.");
+ return false;
+ }
+#endif
+ nsCOMPtr<nsIFile> childDump;
+ if (CrashReporter::CreateMinidumpsAndPair(child,
+ mMainThread,
+ NS_LITERAL_CSTRING("browser"),
+ nullptr, // pair with a dump of this process and thread
+ getter_AddRefs(childDump)) &&
+ CrashReporter::GetIDFromMinidump(childDump, mChildDumpID)) {
+ return true;
+ }
+ return false;
+}
+
+template<class Toplevel>
+inline bool
+CrashReporterParent::GenerateMinidumpAndPair(Toplevel* aTopLevel,
+ nsIFile* aMinidumpToPair,
+ const nsACString& aPairName)
+{
+ mozilla::ipc::ScopedProcessHandle childHandle;
+#ifdef XP_MACOSX
+ childHandle = aTopLevel->Process()->GetChildTask();
+#else
+ if (!base::OpenPrivilegedProcessHandle(aTopLevel->OtherPid(),
+ &childHandle.rwget())) {
+ NS_WARNING("Failed to open child process handle.");
+ return false;
+ }
+#endif
+ nsCOMPtr<nsIFile> targetDump;
+ if (CrashReporter::CreateMinidumpsAndPair(childHandle,
+ mMainThread, // child thread id
+ aPairName,
+ aMinidumpToPair,
+ getter_AddRefs(targetDump)) &&
+ CrashReporter::GetIDFromMinidump(targetDump, mChildDumpID)) {
+ return true;
+ }
+ return false;
+}
+
+template<class Toplevel>
+inline bool
+CrashReporterParent::GenerateCrashReport(Toplevel* t,
+ const AnnotationTable* processNotes)
+{
+ nsCOMPtr<nsIFile> crashDump;
+ if (t->TakeMinidump(getter_AddRefs(crashDump), nullptr) &&
+ CrashReporter::GetIDFromMinidump(crashDump, mChildDumpID)) {
+ bool result = GenerateChildData(processNotes);
+ FinalizeChildData();
+ return result;
+ }
+ return false;
+}
+
+template<class Toplevel>
+inline bool
+CrashReporterParent::GenerateCompleteMinidump(Toplevel* t)
+{
+ mozilla::ipc::ScopedProcessHandle child;
+ if (!NS_IsMainThread()) {
+ NS_WARNING("GenerateCompleteMinidump can't be called on non-main thread.");
+ return false;
+ }
+
+#ifdef XP_MACOSX
+ child = t->Process()->GetChildTask();
+#else
+ if (!base::OpenPrivilegedProcessHandle(t->OtherPid(), &child.rwget())) {
+ NS_WARNING("Failed to open child process handle.");
+ return false;
+ }
+#endif
+ nsCOMPtr<nsIFile> childDump;
+ if (CrashReporter::CreateMinidumpsAndPair(child,
+ mMainThread,
+ NS_LITERAL_CSTRING("browser"),
+ nullptr, // pair with a dump of this process and thread
+ getter_AddRefs(childDump)) &&
+ CrashReporter::GetIDFromMinidump(childDump, mChildDumpID)) {
+ bool result = GenerateChildData(nullptr);
+ FinalizeChildData();
+ return result;
+ }
+ return false;
+}
+
+template<class Toplevel>
+/* static */ bool
+CrashReporterParent::CreateCrashReporter(Toplevel* actor)
+{
+#ifdef MOZ_CRASHREPORTER
+ NativeThreadId id;
+ uint32_t processType;
+ PCrashReporterParent* p =
+ actor->CallPCrashReporterConstructor(&id, &processType);
+ if (p) {
+ static_cast<CrashReporterParent*>(p)->SetChildData(id, processType);
+ } else {
+ NS_ERROR("Error creating crash reporter actor");
+ }
+ return !!p;
+#endif
+ return false;
+}
+
+#endif
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_CrashReporterParent_h
diff --git a/dom/ipc/DOMTypes.ipdlh b/dom/ipc/DOMTypes.ipdlh
new file mode 100644
index 000000000..e02760ada
--- /dev/null
+++ b/dom/ipc/DOMTypes.ipdlh
@@ -0,0 +1,181 @@
+/* -*- 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 PBlob;
+include protocol PSendStream;
+include IPCStream;
+include ProtocolTypes;
+
+using struct mozilla::void_t
+ from "ipc/IPCMessageUtils.h";
+
+using struct mozilla::SerializedStructuredCloneBuffer
+ from "ipc/IPCMessageUtils.h";
+
+namespace mozilla {
+namespace dom {
+
+union OptionalID
+{
+ nsID;
+ void_t;
+};
+
+struct MessagePortIdentifier
+{
+ nsID uuid;
+ nsID destinationUuid;
+ uint32_t sequenceId;
+ bool neutered;
+};
+
+struct ClonedMessageData
+{
+ SerializedStructuredCloneBuffer data;
+ PBlob[] blobs;
+ MessagePortIdentifier[] identfiers;
+};
+
+struct BlobDataStream
+{
+ IPCStream stream;
+ uint64_t length;
+};
+
+union BlobData
+{
+ // For remote blobs.
+ nsID;
+
+ // For memory-backed blobs.
+ BlobDataStream;
+
+ // For multiplex blobs.
+ BlobData[];
+};
+
+union OptionalBlobData
+{
+ BlobData;
+ void_t;
+};
+
+struct NormalBlobConstructorParams
+{
+ nsString contentType;
+ uint64_t length;
+
+ // This must be of type BlobData in a child->parent message, and will always
+ // be of type void_t in a parent->child message.
+ OptionalBlobData optionalBlobData;
+};
+
+struct FileBlobConstructorParams
+{
+ nsString name;
+ nsString contentType;
+ nsString path;
+ uint64_t length;
+ int64_t modDate;
+ bool isDirectory;
+
+ // This must be of type BlobData in a child->parent message, and will always
+ // be of type void_t in a parent->child message.
+ OptionalBlobData optionalBlobData;
+};
+
+struct SlicedBlobConstructorParams
+{
+ PBlob source;
+ nsID id;
+ uint64_t begin;
+ uint64_t end;
+ nsString contentType;
+};
+
+struct MysteryBlobConstructorParams
+{
+ // Nothing is known about this type of blob.
+};
+
+struct KnownBlobConstructorParams
+{
+ nsID id;
+};
+
+// This may only be used for same-process inter-thread communication!
+struct SameProcessBlobConstructorParams
+{
+ // This member should be reinterpret_cast'd to mozilla::dom::BlobImpl. It
+ // carries a reference.
+ intptr_t addRefedBlobImpl;
+};
+
+union AnyBlobConstructorParams
+{
+ // These types may be sent to/from parent and child.
+ NormalBlobConstructorParams;
+ FileBlobConstructorParams;
+ SameProcessBlobConstructorParams;
+
+ // This type may only be sent from parent to child.
+ MysteryBlobConstructorParams;
+
+ // These types may only be sent from child to parent.
+ SlicedBlobConstructorParams;
+ KnownBlobConstructorParams;
+};
+
+struct ChildBlobConstructorParams
+{
+ nsID id;
+
+ // May not be SlicedBlobConstructorParams or KnownBlobConstructorParams.
+ AnyBlobConstructorParams blobParams;
+};
+
+struct ParentBlobConstructorParams
+{
+ // May not be MysteryBlobConstructorParams.
+ AnyBlobConstructorParams blobParams;
+};
+
+union BlobConstructorParams
+{
+ ChildBlobConstructorParams;
+ ParentBlobConstructorParams;
+};
+
+union IPCDataTransferData
+{
+ nsString; // text
+ Shmem; // images using Shmem
+ PBlob; // files
+};
+
+struct IPCDataTransferImage
+{
+ uint32_t width;
+ uint32_t height;
+ uint32_t stride;
+ uint8_t format;
+};
+
+struct IPCDataTransferItem
+{
+ nsCString flavor;
+ // The image details are only used when transferring images.
+ IPCDataTransferImage imageDetails;
+ IPCDataTransferData data;
+};
+
+struct IPCDataTransfer
+{
+ IPCDataTransferItem[] items;
+};
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/DatePickerParent.cpp b/dom/ipc/DatePickerParent.cpp
new file mode 100644
index 000000000..509944ddd
--- /dev/null
+++ b/dom/ipc/DatePickerParent.cpp
@@ -0,0 +1,87 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DatePickerParent.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIDocument.h"
+#include "nsIDOMWindow.h"
+#include "mozilla/Unused.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/TabParent.h"
+
+using mozilla::Unused;
+using namespace mozilla::dom;
+
+NS_IMPL_ISUPPORTS(DatePickerParent::DatePickerShownCallback,
+ nsIDatePickerShownCallback);
+
+NS_IMETHODIMP
+DatePickerParent::DatePickerShownCallback::Cancel()
+{
+ if (mDatePickerParent) {
+ Unused << mDatePickerParent->SendCancel();
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+DatePickerParent::DatePickerShownCallback::Done(const nsAString& aDate)
+{
+ if (mDatePickerParent) {
+ Unused << mDatePickerParent->Send__delete__(mDatePickerParent,
+ nsString(aDate));
+ }
+ return NS_OK;
+}
+
+void
+DatePickerParent::DatePickerShownCallback::Destroy()
+{
+ mDatePickerParent = nullptr;
+}
+
+bool
+DatePickerParent::CreateDatePicker()
+{
+ mPicker = do_CreateInstance("@mozilla.org/datepicker;1");
+ if (!mPicker) {
+ return false;
+ }
+
+ Element* ownerElement = TabParent::GetFrom(Manager())->GetOwnerElement();
+ if (!ownerElement) {
+ return false;
+ }
+
+ nsCOMPtr<mozIDOMWindowProxy> window = do_QueryInterface(ownerElement->OwnerDoc()->GetWindow());
+ if (!window) {
+ return false;
+ }
+
+ return NS_SUCCEEDED(mPicker->Init(window, mTitle, mInitialDate));
+}
+
+bool
+DatePickerParent::RecvOpen()
+{
+ if (!CreateDatePicker()) {
+ Unused << Send__delete__(this, mInitialDate);
+ return true;
+ }
+
+ mCallback = new DatePickerShownCallback(this);
+
+ mPicker->Open(mCallback);
+ return true;
+};
+
+void
+DatePickerParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+ if (mCallback) {
+ mCallback->Destroy();
+ }
+}
diff --git a/dom/ipc/DatePickerParent.h b/dom/ipc/DatePickerParent.h
new file mode 100644
index 000000000..73b66f96c
--- /dev/null
+++ b/dom/ipc/DatePickerParent.h
@@ -0,0 +1,61 @@
+/* -*- 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_DatePickerParent_h
+#define mozilla_dom_DatePickerParent_h
+
+#include "mozilla/dom/PDatePickerParent.h"
+#include "nsIDatePicker.h"
+
+namespace mozilla {
+namespace dom {
+
+class DatePickerParent : public PDatePickerParent
+{
+ public:
+ DatePickerParent(const nsString& aTitle,
+ const nsString& aInitialDate)
+ : mTitle(aTitle)
+ , mInitialDate(aInitialDate)
+ {}
+
+ virtual bool RecvOpen() override;
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ class DatePickerShownCallback final
+ : public nsIDatePickerShownCallback
+ {
+ public:
+ explicit DatePickerShownCallback(DatePickerParent* aDatePickerParnet)
+ : mDatePickerParent(aDatePickerParnet)
+ {}
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIDATEPICKERSHOWNCALLBACK
+
+ void Destroy();
+
+ private:
+ ~DatePickerShownCallback() {}
+ DatePickerParent* mDatePickerParent;
+ };
+
+ private:
+ virtual ~DatePickerParent() {}
+
+ bool CreateDatePicker();
+
+ RefPtr<DatePickerShownCallback> mCallback;
+ nsCOMPtr<nsIDatePicker> mPicker;
+
+ nsString mTitle;
+ nsString mInitialDate;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_DatePickerParent_h
diff --git a/dom/ipc/FilePickerParent.cpp b/dom/ipc/FilePickerParent.cpp
new file mode 100644
index 000000000..0d4a17978
--- /dev/null
+++ b/dom/ipc/FilePickerParent.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 "FilePickerParent.h"
+#include "nsComponentManagerUtils.h"
+#include "nsNetCID.h"
+#include "nsIDocument.h"
+#include "nsIDOMWindow.h"
+#include "nsIFile.h"
+#include "nsISimpleEnumerator.h"
+#include "mozilla/Unused.h"
+#include "mozilla/dom/File.h"
+#include "mozilla/dom/FileSystemSecurity.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/TabParent.h"
+#include "mozilla/dom/ipc/BlobParent.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()
+{
+}
+
+// Before sending a blob to the child, we need to get its size and modification
+// date. Otherwise it will be sent as a "mystery blob" by
+// GetOrCreateActorForBlob, which will cause problems for the child
+// process. This runnable stat()s the file off the main thread.
+//
+// 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)
+ : mFilePickerParent(aFPParent)
+ , mIsDirectory(aIsDirectory)
+{
+ mFiles.SwapElements(aFiles);
+ 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 BlobImplFile(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)
+{
+ nsIContentParent* parent = TabParent::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;
+ }
+
+ InfallibleTArray<PBlobParent*> blobs;
+
+ for (unsigned i = 0; i < aData.Length(); i++) {
+ MOZ_ASSERT(aData[i].mType == BlobImplOrString::eBlobImpl);
+ BlobParent* blobParent = parent->GetOrCreateActorForBlobImpl(aData[i].mBlobImpl);
+ if (blobParent) {
+ blobs.AppendElement(blobParent);
+ }
+ }
+
+ InputBlobs inblobs;
+ inblobs.blobsParent().SwapElements(blobs);
+ 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, 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 = TabParent::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));
+}
+
+bool
+FilePickerParent::RecvOpen(const int16_t& aSelectedType,
+ const bool& aAddToRecentDocs,
+ const nsString& aDefaultFile,
+ const nsString& aDefaultExtension,
+ InfallibleTArray<nsString>&& aFilters,
+ InfallibleTArray<nsString>&& aFilterNames,
+ const nsString& aDisplayDirectory,
+ const nsString& aOkButtonLabel)
+{
+ if (!CreateFilePicker()) {
+ Unused << Send__delete__(this, void_t(), nsIFilePicker::returnCancel);
+ return true;
+ }
+
+ mFilePicker->SetAddToRecentDocs(aAddToRecentDocs);
+
+ for (uint32_t i = 0; i < aFilters.Length(); ++i) {
+ mFilePicker->AppendFilter(aFilterNames[i], aFilters[i]);
+ }
+
+ mFilePicker->SetDefaultString(aDefaultFile);
+ mFilePicker->SetDefaultExtension(aDefaultExtension);
+ mFilePicker->SetFilterIndex(aSelectedType);
+ mFilePicker->SetOkButtonLabel(aOkButtonLabel);
+
+ if (!aDisplayDirectory.IsEmpty()) {
+ nsCOMPtr<nsIFile> localFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
+ if (localFile) {
+ localFile->InitWithPath(aDisplayDirectory);
+ mFilePicker->SetDisplayDirectory(localFile);
+ }
+ }
+
+ mCallback = new FilePickerShownCallback(this);
+
+ mFilePicker->Open(mCallback);
+ return true;
+}
+
+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 000000000..599d118ff
--- /dev/null
+++ b/dom/ipc/FilePickerParent.h
@@ -0,0 +1,110 @@
+/* -*- 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)
+ {}
+
+ virtual ~FilePickerParent();
+
+ void Done(int16_t aResult);
+
+ struct BlobImplOrString
+ {
+ RefPtr<BlobImpl> mBlobImpl;
+ nsString mDirectoryPath;
+
+ enum {
+ eBlobImpl,
+ eDirectoryPath
+ } mType;
+ };
+
+ void SendFilesOrDirectories(const nsTArray<BlobImplOrString>& aData);
+
+ virtual bool RecvOpen(const int16_t& aSelectedType,
+ const bool& aAddToRecentDocs,
+ const nsString& aDefaultFile,
+ const nsString& aDefaultExtension,
+ InfallibleTArray<nsString>&& aFilters,
+ InfallibleTArray<nsString>&& aFilterNames,
+ const nsString& aDisplayDirectory,
+ const nsString& aOkButtonLabel) override;
+
+ 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() {}
+ 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();
+ 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 000000000..b683178eb
--- /dev/null
+++ b/dom/ipc/IdType.h
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_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 ContentParent;
+class TabParent;
+
+
+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<TabParent> 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/PBlob.ipdl b/dom/ipc/PBlob.ipdl
new file mode 100644
index 000000000..3e758fe92
--- /dev/null
+++ b/dom/ipc/PBlob.ipdl
@@ -0,0 +1,57 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 PBlobStream;
+include protocol PContent;
+include protocol PContentBridge;
+include protocol PFileDescriptorSet;
+include protocol PSendStream;
+
+include BlobTypes;
+include DOMTypes;
+include InputStreamParams;
+
+namespace mozilla {
+namespace dom {
+
+union ResolveMysteryParams
+{
+ NormalBlobConstructorParams;
+ FileBlobConstructorParams;
+};
+
+sync protocol PBlob
+{
+ manager PBackground or PContent or PContentBridge;
+ manages PBlobStream;
+
+both:
+ async __delete__();
+
+parent:
+ async PBlobStream(uint64_t begin, uint64_t length);
+
+ async ResolveMystery(ResolveMysteryParams params);
+
+ sync BlobStreamSync(uint64_t begin, uint64_t length)
+ returns (InputStreamParams params, OptionalFileDescriptorSet fds);
+
+ sync WaitForSliceCreation();
+
+ // Use only for testing!
+ sync GetFileId()
+ returns (int64_t fileId);
+
+ sync GetFilePath()
+ returns (nsString filePath);
+
+child:
+ // This method must be called by the parent when the PBlobParent is fully
+ // created in order to release the known blob.
+ async CreatedFromKnownBlob();
+};
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/PBlobStream.ipdl b/dom/ipc/PBlobStream.ipdl
new file mode 100644
index 000000000..f99399c8a
--- /dev/null
+++ b/dom/ipc/PBlobStream.ipdl
@@ -0,0 +1,22 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PBlob;
+include protocol PFileDescriptorSet;
+include BlobTypes;
+include InputStreamParams;
+
+namespace mozilla {
+namespace dom {
+
+protocol PBlobStream
+{
+ manager PBlob;
+
+child:
+ async __delete__(InputStreamParams params, OptionalFileDescriptorSet fds);
+};
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/PBrowser.ipdl b/dom/ipc/PBrowser.ipdl
new file mode 100644
index 000000000..249657c26
--- /dev/null
+++ b/dom/ipc/PBrowser.ipdl
@@ -0,0 +1,928 @@
+/* -*- 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 PBlob;
+include protocol PColorPicker;
+include protocol PContent;
+include protocol PContentBridge;
+include protocol PDatePicker;
+include protocol PDocAccessible;
+include protocol PDocumentRenderer;
+include protocol PFilePicker;
+include protocol PIndexedDBPermissionRequest;
+include protocol PRenderFrame;
+include protocol PPluginWidget;
+include protocol PRemotePrintJob;
+include DOMTypes;
+include JavaScriptTypes;
+include URIParams;
+include PPrintingTypes;
+include PTabContext;
+
+
+using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h";
+using class mozilla::gfx::Matrix from "mozilla/gfx/Matrix.h";
+using struct gfxSize from "gfxPoint.h";
+using CSSRect from "Units.h";
+using CSSSize from "Units.h";
+using mozilla::LayoutDeviceIntRect from "Units.h";
+using mozilla::LayoutDeviceIntPoint from "Units.h";
+using mozilla::LayoutDevicePoint from "Units.h";
+using mozilla::ScreenIntPoint from "Units.h";
+using ScreenIntSize from "Units.h";
+using struct mozilla::layers::FrameMetrics from "FrameMetrics.h";
+using struct mozilla::layers::ScrollableLayerGuid from "FrameMetrics.h";
+using struct mozilla::layers::ZoomConstraints from "FrameMetrics.h";
+using mozilla::layers::MaybeZoomConstraints from "FrameMetrics.h";
+using mozilla::layers::GeckoContentController::TapType from "mozilla/layers/GeckoContentController.h";
+using FrameMetrics::ViewID from "FrameMetrics.h";
+using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
+using mozilla::WindowsHandle from "ipc/IPCMessageUtils.h";
+using nscolor from "nsColor.h";
+using class mozilla::WidgetCompositionEvent from "ipc/nsGUIEventIPC.h";
+using struct mozilla::widget::IMENotification from "nsIWidget.h";
+using struct nsIMEUpdatePreference from "nsIWidget.h";
+using mozilla::gfx::IntSize from "mozilla/gfx/Point.h";
+using mozilla::gfx::IntPoint from "mozilla/gfx/Point.h";
+using mozilla::gfx::IntRect from "mozilla/gfx/Rect.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 class mozilla::WidgetPluginEvent from "ipc/nsGUIEventIPC.h";
+using struct mozilla::dom::RemoteDOMEvent from "mozilla/dom/TabMessageUtils.h";
+using mozilla::dom::ScreenOrientationInternal from "mozilla/dom/ScreenOrientation.h";
+using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
+using mozilla::CSSToScreenScale from "Units.h";
+using mozilla::CommandInt from "mozilla/EventForwards.h";
+using mozilla::WritingMode from "mozilla/WritingModes.h";
+using mozilla::layers::TouchBehaviorFlags from "mozilla/layers/APZUtils.h";
+using nsIWidget::TouchPointerState 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 nsSizeMode from "nsIWidgetListener.h";
+using mozilla::widget::CandidateWindowPosition from "ipc/nsGUIEventIPC.h";
+using class mozilla::NativeEventData from "ipc/nsGUIEventIPC.h";
+using mozilla::FontRange from "ipc/nsGUIEventIPC.h";
+using mozilla::a11y::IAccessibleHolder from "mozilla/a11y/IPCTypes.h";
+
+namespace mozilla {
+namespace dom {
+
+struct NativeKeyBinding
+{
+ CommandInt[] singleLineCommands;
+ CommandInt[] multiLineCommands;
+ CommandInt[] richTextCommands;
+};
+
+union MaybeNativeKeyBinding
+{
+ NativeKeyBinding;
+ void_t;
+};
+
+struct ShowInfo
+{
+ nsString name;
+ bool fullscreenAllowed;
+ bool isPrivate;
+ bool fakeShowInfo;
+ bool isTransparent;
+ float dpi;
+ int32_t widgetRounding;
+ double defaultScale;
+};
+
+union OptionalShmem
+{
+ void_t;
+ Shmem;
+};
+
+nested(upto inside_cpow) sync protocol PBrowser
+{
+ manager PContent or PContentBridge;
+
+ manages PColorPicker;
+ manages PDatePicker;
+ manages PDocAccessible;
+ manages PDocumentRenderer;
+ manages PFilePicker;
+ manages PIndexedDBPermissionRequest;
+ manages PRenderFrame;
+ manages PPluginWidget;
+
+both:
+ async AsyncMessage(nsString aMessage, CpowEntry[] aCpows,
+ Principal aPrincipal, ClonedMessageData aData);
+
+ /**
+ * Create a layout frame (encapsulating a remote layer tree) for
+ * the page that is currently loaded in the <browser>.
+ */
+ async PRenderFrame();
+
+parent:
+ /**
+ * 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);
+
+ /*
+ * 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();
+
+ /**
+ * Return native data of root widget
+ */
+ nested(inside_cpow) sync GetWidgetNativeData() returns (WindowsHandle value);
+
+ /**
+ * 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,
+ CpowEntry[] aCpows, Principal aPrincipal)
+ returns (StructuredCloneData[] retval);
+
+ nested(inside_sync) sync RpcMessage(nsString aMessage, ClonedMessageData aData,
+ CpowEntry[] aCpows, Principal aPrincipal)
+ 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
+ * preference Native widget preference for IME updates
+ */
+ nested(inside_cpow) sync NotifyIMEFocus(ContentCache contentCache,
+ IMENotification notification)
+ returns (nsIMEUpdatePreference preference);
+
+ /**
+ * 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 consumd 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);
+
+ /**
+ * Tells chrome to start plugin IME. If this results in a string getting
+ * committed, the result is in aCommitted (otherwise aCommitted is empty).
+ *
+ * aKeyboardEvent The event with which plugin IME is to be started
+ * panelX and panelY Location in screen coordinates of the IME input panel
+ * (should be just under the plugin)
+ * aCommitted The string committed during IME -- otherwise empty
+ */
+ nested(inside_cpow) sync StartPluginIME(WidgetKeyboardEvent aKeyboardEvent,
+ int32_t panelX, int32_t panelY)
+ returns (nsString aCommitted);
+
+ /**
+ * Tells chrome (and specifically the appropriate widget) whether or not
+ * a plugin (inside the widget) has the keyboard focus. Should be sent
+ * when the keyboard focus changes too or from a plugin.
+ *
+ * aFocused Whether or not a plugin is focused
+ */
+ nested(inside_cpow) async SetPluginFocused(bool aFocused);
+
+ /**
+ * Set IME candidate window by windowless plugin if plugin has focus.
+ */
+ async SetCandidateWindowForPlugin(CandidateWindowPosition aPosition);
+
+ /**
+ * 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);
+
+ /**
+ * When plugin event isn't consumed, call this
+ */
+ async DefaultProcOfPluginEvent(WidgetPluginEvent aEvent);
+
+ /**
+ * 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);
+
+ /**
+ * Indicate, based on the current state, that some commands are enabled and
+ * some are disabled.
+ */
+ async EnableDisableCommands(nsString action,
+ nsCString[] enabledCommands,
+ nsCString[] disabledCommands);
+
+ nested(inside_cpow) sync GetInputContext() returns (int32_t IMEEnabled,
+ int32_t IMEOpen);
+
+ nested(inside_cpow) async SetInputContext(int32_t IMEEnabled,
+ int32_t IMEOpen,
+ nsString type,
+ nsString inputmode,
+ nsString actionHint,
+ int32_t cause,
+ int32_t focusChange);
+
+ sync IsParentWindowMainWidgetVisible() returns (bool visible);
+
+ /**
+ * Gets the DPI of the screen corresponding to this browser.
+ */
+ sync GetDPI() returns (float value);
+
+ /**
+ * Gets the default scaling factor of the screen corresponding to this browser.
+ */
+ sync GetDefaultScale() returns (double value);
+
+ /**
+ * Gets the rounding of coordinates in the widget.
+ */
+ sync GetWidgetRounding() returns (int32_t value);
+
+ /**
+ * Gets maximum of touch points at current device.
+ */
+ sync GetMaxTouchPoints() returns (uint32_t value);
+
+ /**
+ * Set the native cursor.
+ * @param value
+ * The widget cursor to set.
+ * @param force
+ * Invalidate any locally cached cursor settings and force an
+ * update.
+ */
+ async SetCursor(uint32_t value, bool force);
+
+ /**
+ * Set the native cursor using a custom image.
+ * @param cursorData
+ * 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 SetCustomCursor(nsCString cursorData, uint32_t width, uint32_t height,
+ uint32_t stride, uint8_t format,
+ uint32_t hotspotX, uint32_t hotspotY, bool force);
+
+ /**
+ * Used to set the current text of the status tooltip.
+ * Nowadays this is mainly used for link locations on hover.
+ */
+ async SetStatus(uint32_t type, 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);
+
+ /**
+ * Create an asynchronous date picker on the parent side,
+ * but don't open it yet.
+ */
+ async PDatePicker(nsString title, nsString initialDate);
+
+ 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 PIndexedDBPermissionRequest(Principal principal);
+
+ /**
+ * window.open from inside <iframe mozbrowser> is special. When the child
+ * process calls window.open, it creates a new PBrowser (in its own
+ * process), then calls BrowserFrameOpenWindow on it.
+ *
+ * The parent process gets a chance to accept or reject the window.open
+ * call, and windowOpened is set to true if we ended up going through with
+ * the window.open.
+ *
+ * @param opener the PBrowser whose content called window.open.
+ */
+ sync BrowserFrameOpenWindow(PBrowser opener, PRenderFrame renderFrame,
+ nsString aURL, nsString aName, nsString aFeatures)
+ returns (bool windowOpened,
+ TextureFactoryIdentifier textureFactoryIdentifier,
+ uint64_t layersId);
+
+ /**
+ * 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);
+
+ async DispatchAfterKeyboardEvent(WidgetKeyboardEvent event);
+
+ sync RequestNativeKeyBindings(WidgetKeyboardEvent event)
+ returns (MaybeNativeKeyBinding bindings);
+
+ 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);
+
+ /**
+ * Returns the number of tabs in the window via the out parameter.
+ * If the number of tabs can't be determined, returns 0.
+ *
+ * @param aValue where to store the tab count
+ */
+ sync GetTabCount() returns (uint32_t value);
+
+ async AccessKeyNotHandled(WidgetKeyboardEvent event);
+
+child:
+ async NativeSynthesisResponse(uint64_t aObserverId, nsCString aResponse);
+
+
+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 layer tree is already available.
+ */
+ async ForcePaintNoOp(uint64_t aLayerObserverEpoch);
+
+ /**
+ * 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);
+
+ 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,
+ OptionalShmem visualData,
+ uint32_t stride, uint8_t format,
+ LayoutDeviceIntRect dragRect);
+
+ async AudioChannelActivityNotification(uint32_t aAudioChannel,
+ bool aActive);
+
+ // 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 TabChild attempts to use its cross-process compositor
+ // bridge.
+ sync EnsureLayersConnected();
+
+ /**
+ * Notify parent that one or more entries have been added / removed from
+ * the child session history.
+ *
+ * @param aCount the updated number of entries in child session history
+ */
+ async NotifySessionHistoryChange(uint32_t aCount);
+
+ /**
+ * When the session history is across multiple root docshells, this function
+ * is used to notify parent that it needs to navigate to an entry out of
+ * local index of the child.
+ *
+ * @param aGlobalIndex The global index of history entry to navigate to.
+ */
+ async RequestCrossBrowserNavigation(uint32_t aGlobalIndex);
+
+child:
+ /**
+ * Notify the remote browser that it has been Show()n on this
+ * side, with the given |visibleRect|. This message is expected
+ * to trigger creation of the remote browser's "widget".
+ *
+ * |Show()| and |Move()| take IntSizes rather than Rects because
+ * content processes always render to a virtual <0, 0> top-left
+ * point.
+ */
+ async Show(ScreenIntSize size,
+ ShowInfo info,
+ TextureFactoryIdentifier textureFactoryIdentifier,
+ uint64_t layersId,
+ nullable PRenderFrame renderFrame,
+ bool parentIsActive,
+ nsSizeMode sizeMode);
+
+ async LoadURL(nsCString uri, ShowInfo info);
+
+ async UpdateDimensions(CSSRect rect, CSSSize size,
+ ScreenOrientationInternal orientation,
+ LayoutDeviceIntPoint clientOffset,
+ LayoutDeviceIntPoint chromeDisp) compressall;
+
+ async SizeModeChanged(nsSizeMode sizeMode);
+
+ /**
+ * Sending an activate message moves focus to the child.
+ */
+ async Activate();
+
+ async Deactivate();
+
+ async ParentActivated(bool aActivated);
+
+ async SetKeyboardIndicators(UIStateChangeType showAccelerators,
+ UIStateChangeType showFocusRings);
+
+ /**
+ * StopIMEStateManagement() is called when the process loses focus and
+ * should stop managing IME state.
+ */
+ async StopIMEStateManagement();
+
+ /**
+ * MenuKeyboardListenerInstalled() is called when menu keyboard listener
+ * is installed in the parent process.
+ */
+ async MenuKeyboardListenerInstalled(bool aInstalled);
+
+ /**
+ * @see nsIDOMWindowUtils sendMouseEvent.
+ */
+ async MouseEvent(nsString aType,
+ float aX,
+ float aY,
+ int32_t aButton,
+ int32_t aClickCount,
+ int32_t aModifiers,
+ bool aIgnoreRootScrollFrame);
+
+ /**
+ * When two consecutive mouse move events would be added to the message queue,
+ * they are 'compressed' by dumping the oldest one.
+ */
+ async RealMouseMoveEvent(WidgetMouseEvent event, ScrollableLayerGuid aGuid, uint64_t aInputBlockId) compress;
+ /**
+ * 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.
+ */
+ async SynthMouseMoveEvent(WidgetMouseEvent event, ScrollableLayerGuid aGuid, uint64_t aInputBlockId);
+ async RealMouseButtonEvent(WidgetMouseEvent event, ScrollableLayerGuid aGuid, uint64_t aInputBlockId);
+ async RealKeyEvent(WidgetKeyboardEvent event, MaybeNativeKeyBinding keyBinding);
+ async MouseWheelEvent(WidgetWheelEvent event, ScrollableLayerGuid aGuid, uint64_t aInputBlockId);
+ async RealTouchEvent(WidgetTouchEvent aEvent,
+ ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId,
+ nsEventStatus aApzResponse);
+ async HandleTap(TapType aType, LayoutDevicePoint point, Modifiers aModifiers,
+ ScrollableLayerGuid aGuid, uint64_t aInputBlockId);
+ async RealTouchMoveEvent(WidgetTouchEvent aEvent,
+ ScrollableLayerGuid aGuid,
+ uint64_t aInputBlockId,
+ nsEventStatus aApzResponse);
+ async RealDragEvent(WidgetDragEvent aEvent, uint32_t aDragAction, uint32_t aDropEffect);
+ async PluginEvent(WidgetPluginEvent aEvent);
+
+ /**
+ * @see nsIDOMWindowUtils sendKeyEvent.
+ */
+ async KeyEvent(nsString aType,
+ int32_t aKeyCode,
+ int32_t aCharCode,
+ int32_t aModifiers,
+ bool aPreventDefault);
+
+ async CompositionEvent(WidgetCompositionEvent event);
+
+ async SelectionEvent(WidgetSelectionEvent event);
+
+ /**
+ * Call PasteTransferable via a controller on the content process
+ * to handle the command content event, "pasteTransferable".
+ */
+ async PasteTransferable(IPCDataTransfer aDataTransfer,
+ bool aIsPrivateData,
+ Principal aRequestingPrincipal);
+
+ /**
+ * Activate event forwarding from client to parent.
+ */
+ async ActivateFrameEvent(nsString aType, bool capture);
+
+ async LoadRemoteScript(nsString aURL, bool aRunInGlobalScope);
+
+ /**
+ * Create a asynchronous request to render whatever document is
+ * loaded in the child when this message arrives. When the
+ * request finishes, PDocumentRenderer:__delete__ is sent back to
+ * this side to notify completion.
+ *
+ * |documentRect| is the area of the remote document to draw,
+ * transformed by |transform|. The rendered area will have the
+ * default background color |bgcolor|. |renderFlags| are the
+ * nsIPresShell::RenderDocument() flags to use on the remote side,
+ * and if true, |flushLayout| will do just that before rendering
+ * the document. The rendered image will be of size |renderSize|.
+ */
+ async PDocumentRenderer(nsRect documentRect, Matrix transform,
+ nsString bgcolor,
+ uint32_t renderFlags, bool flushLayout,
+ IntSize renderSize);
+
+ /**
+ * 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();
+
+ /**
+ * Update the child side docShell active (resource use) state.
+ *
+ * @param aIsActive
+ * Whether to activate or deactivate the docshell.
+ * @param aPreserveLayers
+ * Whether layer trees should be preserved for inactive docshells.
+ * @param aLayerObserverEpoch
+ * The layer observer epoch for this activation. This message should be
+ * ignored if this epoch has already been observed (via ForcePaint).
+ */
+ async SetDocShellIsActive(bool aIsActive, bool aPreserveLayers, uint64_t aLayerObserverEpoch);
+
+ /**
+ * 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 system theme has changed, and that a repaint
+ * is necessary.
+ */
+ async ThemeChanged(LookAndFeelInt[] lookAndFeelIntCache);
+
+ /**
+ * 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
+ * @param modifierMask indicates which accesskey modifiers are pressed
+ */
+ async HandleAccessKey(WidgetKeyboardEvent event,
+ uint32_t[] charCodes, int32_t modifierMask);
+
+ /**
+ * Propagate a refresh to the child process
+ */
+ async AudioChannelChangeNotification(uint32_t aAudioChannel,
+ float aVolume,
+ bool aMuted);
+
+ /**
+ * Tells the root child docShell whether or not to use
+ * global history. This is sent right after the PBrowser
+ * is bound to a frameloader element.
+ */
+ async SetUseGlobalHistory(bool aUse);
+
+ /**
+ * 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 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);
+
+ /**
+ * Called when the session history of this particular PBrowser has been
+ * attached to a grouped session history.
+ *
+ * @param aOffset The number of entries in the grouped session
+ * history before this session history object.
+ */
+ async NotifyAttachGroupedSessionHistory(uint32_t aOffset);
+
+ /**
+ * Notify that the session history associated to this PBrowser has become
+ * the active history in the grouped session history.
+ *
+ * @param aGlobalLength The up-to-date number of entries in the grouped
+ * session history.
+ * @param aTargetLocalIndex The target local index to navigate to.
+ */
+ async NotifyPartialSessionHistoryActive(uint32_t aGlobalLength,
+ uint32_t aTargetLocalIndex);
+
+ /**
+ * Notify that the session history asssociates to this PBrowser has become
+ * an inactive history in the grouped session history.
+ */
+ async NotifyPartialSessionHistoryDeactive();
+
+ /**
+ * Tell the child that it is a fresh process created for a Large-Allocation
+ * load.
+ */
+ async SetFreshProcess();
+
+/*
+ * 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/PBrowserOrId.ipdlh b/dom/ipc/PBrowserOrId.ipdlh
new file mode 100644
index 000000000..8673f188a
--- /dev/null
+++ b/dom/ipc/PBrowserOrId.ipdlh
@@ -0,0 +1,21 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=c: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+* License, v. 2.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;
+
+using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
+
+namespace mozilla {
+namespace dom {
+
+union PBrowserOrId
+{
+ nullable PBrowser;
+ TabId;
+};
+
+} // namespace dom
+} // namespace mozilla \ No newline at end of file
diff --git a/dom/ipc/PColorPicker.ipdl b/dom/ipc/PColorPicker.ipdl
new file mode 100644
index 000000000..0e54ad598
--- /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 000000000..d436c19fe
--- /dev/null
+++ b/dom/ipc/PContent.ipdl
@@ -0,0 +1,1063 @@
+/* -*- 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 PBlob;
+include protocol PBrowser;
+include protocol PCompositorBridge;
+include protocol PContentBridge;
+include protocol PContentPermissionRequest;
+include protocol PCycleCollectWithLogs;
+include protocol PCrashReporter;
+include protocol PPSMContentDownloader;
+include protocol PExternalHelperApp;
+include protocol PHandlerService;
+include protocol PFileDescriptorSet;
+include protocol PHal;
+include protocol PHeapSnapshotTempFileHelper;
+include protocol PProcessHangMonitor;
+include protocol PImageBridge;
+include protocol PMedia;
+include protocol PMemoryReportRequest;
+include protocol PNecko;
+// FIXME This is pretty ridiculous, but we have to keep the order of the
+// following 4 includes, or the parser is confused about PGMPContent
+// bridging PContent and PGMP. As soon as it registers the bridge between
+// PContent and PPluginModule it seems to think that PContent's parent and
+// child live in the same process!
+include protocol PGMPContent;
+include protocol PGMPService;
+include protocol PPluginModule;
+include protocol PGMP;
+include protocol PPrinting;
+include protocol PSendStream;
+include protocol POfflineCacheUpdate;
+include protocol PRenderFrame;
+include protocol PScreenManager;
+include protocol PSpeechSynthesis;
+include protocol PStorage;
+include protocol PTestShell;
+include protocol PJavaScript;
+include protocol PRemoteSpellcheckEngine;
+include protocol PWebBrowserPersistDocument;
+include protocol PWebrtcGlobal;
+include protocol PPresentation;
+include protocol PVRManager;
+include protocol PVideoDecoderManager;
+include protocol PFlyWebPublishedServer;
+include DOMTypes;
+include JavaScriptTypes;
+include InputStreamParams;
+include PTabContext;
+include URIParams;
+include PluginTypes;
+include ProtocolTypes;
+include PBackgroundSharedTypes;
+include PContentPermission;
+include ServiceWorkerConfiguration;
+include GraphicsMessages;
+include ProfilerTypes;
+
+// Workaround to prevent error if PContentChild.cpp & PContentBridgeParent.cpp
+// are put into different UnifiedProtocolsXX.cpp files.
+// XXX Remove this once bug 1069073 is fixed
+include "mozilla/dom/PContentBridgeParent.h";
+
+using GeoPosition from "nsGeoPositionIPCSerialiser.h";
+using AlertNotificationType 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::ChildPrivileges from "base/process_util.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 struct mozilla::null_t from "ipc/IPCMessageUtils.h";
+using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
+using mozilla::dom::NativeThreadId from "mozilla/dom/TabMessageUtils.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 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::DataStorageType from "ipc/DataStorageIPCUtils.h";
+using mozilla::DocShellOriginAttributes from "mozilla/ipc/BackgroundUtils.h";
+using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
+using struct mozilla::dom::FlyWebPublishOptions from "mozilla/dom/FlyWebPublishOptionsIPCSerializer.h";
+using mozilla::Telemetry::Accumulation from "mozilla/TelemetryComms.h";
+using mozilla::Telemetry::KeyedAccumulation from "mozilla/TelemetryComms.h";
+
+union ChromeRegistryItem
+{
+ ChromePackage;
+ OverrideMapping;
+ SubstitutionMapping;
+};
+
+namespace mozilla {
+namespace dom {
+
+struct FontListEntry {
+ nsString familyName;
+ nsString faceName;
+ nsCString filepath;
+ uint16_t weight;
+ int16_t stretch;
+ uint8_t italic;
+ uint8_t index;
+ bool isHidden;
+};
+
+union PrefValue {
+ nsCString;
+ int32_t;
+ bool;
+};
+
+union MaybePrefValue {
+ PrefValue;
+ null_t;
+};
+
+struct PrefSetting {
+ nsCString name;
+ MaybePrefValue defaultValue;
+ MaybePrefValue userValue;
+};
+
+struct DataStorageItem {
+ nsCString key;
+ nsCString value;
+ DataStorageType type;
+};
+
+// Note: Any changes to this structure should also be changed in
+// FileSystemUpdate below.
+struct VolumeInfo {
+ nsString name;
+ nsString mountPoint;
+ int32_t volState;
+ int32_t mountGeneration;
+ bool isMediaPresent;
+ bool isSharing;
+ bool isFormatting;
+ bool isFake;
+ bool isUnmounting;
+ bool isRemovable;
+ bool isHotSwappable;
+};
+
+struct ClipboardCapabilities {
+ bool supportsSelectionClipboard;
+ bool supportsFindClipboard;
+};
+
+union MaybeFileDesc {
+ FileDescriptor;
+ void_t;
+};
+
+union FileDescOrError {
+ FileDescriptor;
+ nsresult;
+};
+
+struct DomainPolicyClone
+{
+ bool active;
+ URIParams[] blacklist;
+ URIParams[] whitelist;
+ URIParams[] superBlacklist;
+ URIParams[] superWhitelist;
+};
+
+
+
+struct FrameScriptInfo
+{
+ nsString url;
+ bool runInGlobalScope;
+};
+
+struct AndroidSystemInfo
+{
+ nsString device;
+ nsString manufacturer;
+ nsString release_version;
+ nsString hardware;
+ uint32_t sdk_version;
+ bool isTablet;
+};
+
+struct GetFilesResponseSuccess
+{
+ PBlob[] blobs;
+};
+
+struct GetFilesResponseFailure
+{
+ nsresult errorCode;
+};
+
+union GetFilesResponseResult
+{
+ GetFilesResponseSuccess;
+ GetFilesResponseFailure;
+};
+
+struct BlobURLRegistrationData
+{
+ nsCString url;
+ PBlob blob;
+ Principal principal;
+};
+
+struct GMPAPITags
+{
+ nsCString api;
+ nsCString[] tags;
+};
+
+struct GMPCapabilityData
+{
+ nsCString name;
+ nsCString version;
+ GMPAPITags[] capabilities;
+};
+
+nested(upto inside_cpow) sync protocol PContent
+{
+ parent spawns PPluginModule;
+
+ parent opens PProcessHangMonitor;
+ parent opens PGMPService;
+ child opens PBackground;
+
+ manages PBlob;
+ manages PBrowser;
+ manages PContentPermissionRequest;
+ manages PCrashReporter;
+ manages PCycleCollectWithLogs;
+ manages PPSMContentDownloader;
+ manages PExternalHelperApp;
+ manages PFileDescriptorSet;
+ manages PHal;
+ manages PHandlerService;
+ manages PHeapSnapshotTempFileHelper;
+ manages PMedia;
+ manages PMemoryReportRequest;
+ manages PNecko;
+ manages POfflineCacheUpdate;
+ manages PPrinting;
+ manages PSendStream;
+ manages PScreenManager;
+ manages PSpeechSynthesis;
+ manages PStorage;
+ manages PTestShell;
+ manages PJavaScript;
+ manages PRemoteSpellcheckEngine;
+ manages PWebBrowserPersistDocument;
+ manages PWebrtcGlobal;
+ manages PPresentation;
+ manages PFlyWebPublishedServer;
+
+both:
+ // 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
+ // TabChild::BrowserFrameProvideWindow (which happens when the child's
+ // content calls window.open()), and the parent creates the PBrowser as part
+ // of ContentParent::CreateBrowserOrApp.
+ //
+ // When the parent constructs a PBrowser, the child trusts the app token and
+ // other 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 app
+ // token it receives from the child. In this case, context must have type
+ // PopupIPCTabContext. The browser created using a PopupIPCTabContext has
+ // the opener PBrowser's app-id and containing-app-id. The parent checks
+ // that if the opener is a browser element, the context is also for a
+ // browser element.
+ //
+ // This allows the parent to prevent a malicious child from escalating its
+ // privileges by requesting a PBrowser corresponding to a highly-privileged
+ // app; the child can only request privileges for an app which the child has
+ // access to (in the form of a TabChild).
+ //
+ // Keep the last 3 attributes in sync with GetProcessAttributes!
+ async PBrowser(TabId tabId, IPCTabContext context, uint32_t chromeFlags,
+ ContentParentId cpId, bool isForApp, bool isForBrowser);
+
+ async PBlob(BlobConstructorParams params);
+
+ async PFileDescriptorSet(FileDescriptor fd);
+
+ // For parent->child, aBrowser must be non-null; aOuterWindowID can
+ // be 0 to indicate the browser's current root document, or nonzero
+ // to persist a subdocument. For child->parent, arguments are
+ // ignored and should be null/zero.
+ async PWebBrowserPersistDocument(nullable PBrowser aBrowser,
+ uint64_t aOuterWindowID);
+
+child:
+ // Give the content process its endpoints to the compositor.
+ async InitRendering(
+ Endpoint<PCompositorBridgeChild> compositor,
+ Endpoint<PImageBridgeChild> imageBridge,
+ Endpoint<PVRManagerChild> vr,
+ Endpoint<PVideoDecoderManagerChild> video);
+
+ // 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<PCompositorBridgeChild> compositor,
+ Endpoint<PImageBridgeChild> bridge,
+ Endpoint<PVRManagerChild> vr,
+ Endpoint<PVideoDecoderManagerChild> video);
+
+ /**
+ * 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(MaybeFileDesc aBroker);
+
+ async PMemoryReportRequest(uint32_t generation, bool anonymize,
+ bool minimizeMemoryUsage, MaybeFileDesc DMDFile);
+
+ /**
+ * Sent to notify that aTabId has been allocated aLayersId
+ */
+ async NotifyLayerAllocated(TabId aTabId, uint64_t aLayersId);
+
+ async SpeakerManagerNotify();
+
+ /**
+ * 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 RegisterChrome(ChromePackage[] packages, SubstitutionMapping[] substitutions,
+ OverrideMapping[] overrides, nsCString locale, bool reset);
+ async RegisterChromeItem(ChromeRegistryItem item);
+
+ async ClearImageCache(bool privateLoader, bool chrome);
+
+ async SetOffline(bool offline);
+ async SetConnectivity(bool connectivity);
+ async SetCaptivePortalState(int32_t aState);
+
+ async NotifyVisited(URIParams uri);
+
+ async PreferenceUpdate(PrefSetting pref);
+ async VarUpdate(GfxVarUpdate var);
+
+ 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(GeoPosition somewhere);
+
+ async GeolocationError(uint16_t errorCode);
+
+ async UpdateDictionaryList(nsString[] dictionaries);
+
+ // nsIPermissionManager messages
+ async AddPermission(Permission permission);
+
+ async Volumes(VolumeInfo[] volumes);
+
+ async FlushMemory(nsString reason);
+
+ async GarbageCollect();
+ async CycleCollect();
+
+ /**
+ * Start accessibility engine in content process.
+ * @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 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);
+ async AppInit();
+
+ /**
+ * Send ServiceWorkerRegistrationData to child process.
+ */
+ async InitServiceWorkers(ServiceWorkerConfiguration aConfig);
+
+ /**
+ * Send BlobURLRegistrationData to child process.
+ */
+ async InitBlobURLs(BlobURLRegistrationData[] registrations);
+
+ // Notify child that last-pb-context-exited notification was observed
+ async LastPrivateDocShellDestroyed();
+
+ // Note: Any changes to this structure should also be changed in
+ // VolumeInfo above.
+ async FileSystemUpdate(nsString fsName, nsString mountPoint, int32_t fsState,
+ int32_t mountGeneration, bool isMediaPresent,
+ bool isSharing, bool isFormatting, bool isFake,
+ bool isUnmounting, bool isRemovable, bool isHotSwappable);
+
+ // Notify volume is removed.
+ async VolumeRemoved(nsString fsName);
+
+ async NotifyProcessPriorityChanged(ProcessPriority priority);
+ async MinimizeMemoryUsage();
+
+ /**
+ * Used to manage nsIStyleSheetService across processes.
+ */
+ async LoadAndRegisterSheet(URIParams uri, uint32_t type);
+ async UnregisterSheet(URIParams uri, uint32_t type);
+
+ async NotifyPhoneStateChange(nsString newState);
+
+ /**
+ * Notify idle observers in the child
+ */
+ async NotifyIdleObserver(uint64_t observerId, nsCString topic, nsString str);
+
+ /**
+ * Called during plugin initialization to map a plugin id to a child process
+ * id.
+ */
+ async AssociatePluginId(uint32_t aPluginId, ProcessId aProcessId);
+
+ /**
+ * This call is used by async plugin initialization to notify the
+ * PluginModuleContentParent that the PluginModuleChromeParent's async
+ * init has completed.
+ */
+ async LoadPluginResult(uint32_t aPluginId, bool aResult);
+
+ /**
+ * Control the Gecko Profiler in the child process.
+ */
+ async StartProfiler(ProfilerInitParams params);
+ async StopProfiler();
+ async PauseProfiler(bool aPause);
+
+ async GatherProfile();
+
+ async InvokeDragSession(IPCDataTransfer[] transfers, uint32_t action);
+
+ async EndDragSession(bool aDoneDrag, bool aUserCancelled,
+ LayoutDeviceIntPoint aDragEndPoint);
+
+ async DomainSetChanged(uint32_t aSetType, uint32_t aChangeType, OptionalURIParams 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);
+
+ /**
+ * Send a Push error message to all service worker clients in the child.
+ */
+ async PushError(nsCString scope, Principal principal, nsString message,
+ uint32_t flags);
+
+ /**
+ * Windows specific: associate this content process with the browsers
+ * audio session.
+ */
+ async SetAudioSessionData(nsID aID,
+ nsString aDisplayName,
+ nsString aIconPath);
+
+ async GetFilesResponse(nsID aID, GetFilesResponseResult aResult);
+
+ async BlobURLRegistration(nsCString aURI, PBlob aBlob,
+ Principal aPrincipal);
+
+ async BlobURLUnregistration(nsCString aURI);
+
+
+ async GMPsChanged(GMPCapabilityData[] capabilities);
+
+parent:
+ /**
+ * Tell the content process some attributes of itself. This is
+ * among the first information queried by content processes after
+ * startup. (The message is sync to allow the content process to
+ * control when it receives the information.)
+ *
+ * |id| is a unique ID among all subprocesses. When |isForApp &&
+ * isForBrowser|, we're loading <browser> for an app. When
+ * |isForBrowser|, we're loading <browser>. When |!isForApp &&
+ * !isForBrowser|, we're probably loading <xul:browser remote>.
+ *
+ * Keep the return values in sync with PBrowser()!
+ */
+ sync GetProcessAttributes()
+ returns (ContentParentId cpId, bool isForApp, bool isForBrowser);
+ sync GetXPCOMProcessAttributes()
+ returns (bool isOffline, bool isConnected, int32_t captivePortalState,
+ bool isLangRTL,
+ bool haveBidiKeyboards, nsString[] dictionaries,
+ ClipboardCapabilities clipboardCaps,
+ DomainPolicyClone domainPolicy,
+ StructuredCloneData initialData,
+ OptionalURIParams userContentSheetURL);
+
+ sync CreateChildProcess(IPCTabContext context,
+ ProcessPriority priority,
+ TabId openerTabId)
+ returns (ContentParentId cpId, bool isForApp, bool isForBrowser, TabId tabId);
+ sync BridgeToChildProcess(ContentParentId cpId);
+
+ async CreateGMPService();
+
+ /**
+ * This call connects the content process to a plugin process. While this
+ * call runs, a new PluginModuleParent will be created in the ContentChild
+ * via bridging. The corresponding PluginModuleChild will live in the plugin
+ * process.
+ */
+ sync LoadPlugin(uint32_t aPluginId) returns (nsresult aResult, uint32_t aRunID);
+
+ /**
+ * 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. When this call returns, the requested bridge
+ * connection has been made.
+ */
+ sync ConnectPluginBridge(uint32_t aPluginId) returns (nsresult rv);
+
+ /**
+ * Return the current blocklist state for a particular plugin.
+ */
+ sync GetBlocklistState(uint32_t aPluginId) returns (uint32_t aState);
+
+ /**
+ * This call returns the set of plugins loaded in the chrome
+ * process. However, in many cases this set will not have changed since the
+ * last FindPlugins message. Consequently, the chrome process increments an
+ * epoch number every time the set of plugins changes. The content process
+ * sends up the last epoch it observed. If the epochs are the same, the
+ * chrome process returns no plugins. Otherwise it returns a complete list.
+ *
+ * |pluginEpoch| is the epoch last observed by the content
+ * process. |newPluginEpoch| is the current epoch in the chrome process. If
+ * |pluginEpoch == newPluginEpoch|, then |plugins| will be left empty.
+ */
+ sync FindPlugins(uint32_t pluginEpoch) returns (nsresult aResult, PluginTag[] plugins, uint32_t newPluginEpoch);
+
+ async PJavaScript();
+
+ async PRemoteSpellcheckEngine();
+
+ sync PCrashReporter(NativeThreadId tid, uint32_t processType);
+
+ /**
+ * Is this token compatible with the provided version?
+ *
+ * |version| The offered version to test
+ * Returns |True| if the offered version is compatible
+ */
+ sync NSSU2FTokenIsCompatibleVersion(nsString version)
+ returns (bool result);
+
+ /**
+ * Return whether the provided KeyHandle belongs to this Token
+ *
+ * |keyHandle| Key Handle to evaluate.
+ * Returns |True| if the Key Handle is ours.
+ */
+ sync NSSU2FTokenIsRegistered(uint8_t[] keyHandle)
+ returns (bool isValidKeyHandle);
+
+ /**
+ * Generates a public/private keypair for the provided application
+ * and challenge, returning the pubkey, challenge response, and
+ * key handle in the registration data.
+ *
+ * |application| The FIDO Application data to associate with the key.
+ * |challenge| The Challenge to satisfy in the response.
+ * |registration| An array containing the pubkey, challenge response,
+ * and key handle.
+ */
+ sync NSSU2FTokenRegister(uint8_t[] application, uint8_t[] challenge)
+ returns (uint8_t[] registration);
+
+ /**
+ * Creates a signature over the "param" arguments using the private key
+ * provided in the key handle argument.
+ *
+ * |application| The FIDO Application data to associate with the key.
+ * |challenge| The Challenge to satisfy in the response.
+ * |keyHandle| The Key Handle opaque object to use.
+ * |signature| The resulting signature.
+ */
+ sync NSSU2FTokenSign(uint8_t[] application, uint8_t[] challenge,
+ uint8_t[] keyHandle)
+ returns (uint8_t[] signature);
+
+ sync IsSecureURI(uint32_t type, URIParams uri, uint32_t flags)
+ returns (bool isSecureURI);
+
+ async AccumulateMixedContentHSTS(URIParams uri, bool active, bool hasHSTSPriming);
+
+ sync GetLookAndFeelCache()
+ returns (LookAndFeelInt[] lookAndFeelIntCache);
+
+ nested(inside_cpow) async PHal();
+
+ async PHeapSnapshotTempFileHelper();
+
+ async PNecko();
+
+ async PPrinting();
+
+ async PSendStream();
+
+ nested(inside_sync) sync PScreenManager()
+ returns (uint32_t numberOfScreens,
+ float systemDefaultScale,
+ bool success);
+
+ async PSpeechSynthesis();
+
+ nested(inside_cpow) async PStorage();
+
+ async PMedia();
+
+ async PWebrtcGlobal();
+
+ async PPresentation();
+
+ async PFlyWebPublishedServer(nsString name, FlyWebPublishOptions params);
+
+ // Services remoting
+
+ async StartVisitedQuery(URIParams uri);
+ async VisitURI(URIParams uri, OptionalURIParams referrer, uint32_t flags);
+ async SetURITitle(URIParams uri, nsString title);
+
+ async LoadURIExternal(URIParams uri, PBrowser windowContext);
+ async ExtProtocolChannelConnectParent(uint32_t registrarId);
+
+ // PrefService message
+ sync ReadPrefsArray() returns (PrefSetting[] prefs) verify;
+ sync GetGfxVars() returns (GfxVarUpdate[] vars);
+
+ sync ReadFontList() returns (FontListEntry[] retValue);
+
+ sync ReadDataStorageArray(nsString aFilename)
+ returns (DataStorageItem[] retValue);
+
+ sync SyncMessage(nsString aMessage, ClonedMessageData aData,
+ CpowEntry[] aCpows, Principal aPrincipal)
+ returns (StructuredCloneData[] retval);
+
+ nested(inside_sync) sync RpcMessage(nsString aMessage, ClonedMessageData aData,
+ CpowEntry[] aCpows, Principal aPrincipal)
+ returns (StructuredCloneData[] retval);
+
+ async ShowAlert(AlertNotificationType alert);
+
+ async CloseAlert(nsString name, Principal principal);
+
+ async DisableNotifications(Principal principal);
+
+ async OpenNotificationSettings(Principal principal);
+
+ async PPSMContentDownloader(uint32_t aCertType);
+
+ async PExternalHelperApp(OptionalURIParams uri,
+ nsCString aMimeContentType,
+ nsCString aContentDisposition,
+ uint32_t aContentDispositionHint,
+ nsString aContentDispositionFilename,
+ bool aForceSave,
+ int64_t aContentLength,
+ bool aWasFileChannel,
+ OptionalURIParams aReferrer,
+ nullable PBrowser aBrowser);
+
+ async PHandlerService();
+
+ async AddGeolocationListener(Principal principal, bool highAccuracy);
+ async RemoveGeolocationListener();
+ async SetGeolocationHigherAccuracy(bool enable);
+
+ async ConsoleMessage(nsString message);
+ async ScriptError(nsString message, nsString sourceName, nsString sourceLine,
+ uint32_t lineNumber, uint32_t colNumber, uint32_t flags,
+ nsCString category);
+
+ // nsIPermissionManager messages
+ sync ReadPermissions() returns (Permission[] permissions);
+
+ // Places the items within dataTransfer on the clipboard.
+ async SetClipboard(IPCDataTransfer aDataTransfer,
+ bool aIsPrivateData,
+ Principal aRequestingPrincipal,
+ 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);
+
+ // 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);
+
+ sync GetSystemColors(uint32_t colorsCount)
+ returns (uint32_t[] colors);
+
+ sync GetIconForExtension(nsCString aFileExt, uint32_t aIconSize)
+ returns (uint8_t[] bits);
+
+ sync GetShowPasswordSetting()
+ returns (bool showPassword);
+
+ // 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 AudioChannelServiceStatus(bool aActiveTelephonyChannel,
+ bool aContentOrNormalChannel,
+ bool aAnyActiveChannel);
+
+ async AudioChannelChangeDefVolChannel(int32_t aChannel, bool aHidden);
+
+ // called by the child (test code only) to propagate volume changes to the parent
+ async CreateFakeVolume(nsString fsName, nsString mountPoint);
+ async SetFakeVolumeState(nsString fsName, int32_t fsState);
+ async RemoveFakeVolume(nsString fsName);
+
+ sync KeywordToURI(nsCString keyword)
+ returns (nsString providerName, OptionalInputStreamParams postData, OptionalURIParams uri);
+
+ sync NotifyKeywordSearchLoading(nsString providerName, nsString keyword);
+
+ async CopyFavicon(URIParams oldURI, URIParams newURI, Principal aLoadingPrincipal, bool isPrivate);
+
+ // Tell the compositor to allocate a layer tree id for nested remote mozbrowsers.
+ sync AllocateLayerTreeId(ContentParentId cpId, TabId tabId)
+ returns (uint64_t id);
+ async DeallocateLayerTreeId(uint64_t id);
+
+ sync SpeakerManagerForceSpeaker(bool aEnable);
+
+ sync SpeakerManagerGetSpeakerStatus()
+ returns (bool value);
+
+ /**
+ * 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);
+
+ sync GetGraphicsFeatureStatus(int32_t aFeature) returns (int32_t aStatus, nsCString aFailureCode,
+ bool aSuccess);
+
+ // 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);
+
+ sync OpenAnonymousTemporaryFile() returns (FileDescOrError aFD);
+
+ /**
+ * Keygen requires us to call it after a <keygen> element is parsed and
+ * before one is submitted. This is urgent because an extension might use
+ * a CPOW to synchronously submit a keygen element.
+ */
+ nested(inside_cpow) sync KeygenProcessValue(nsString oldValue,
+ nsString challenge,
+ nsString keytype,
+ nsString keyparams)
+ returns (nsString newValue);
+
+ /**
+ * Called to provide the options for <keygen> elements.
+ */
+ sync KeygenProvideContent()
+ returns (nsString aAttribute, nsString[] aContent);
+
+ /**
+ * Tell the chrome process there is an creation of PBrowser.
+ * return a system-wise unique Id.
+ */
+ sync AllocateTabId(TabId openerTabId, IPCTabContext context, ContentParentId cpId)
+ returns (TabId tabId);
+ async DeallocateTabId(TabId tabId,
+ ContentParentId cpId,
+ bool aMarkedDestroying);
+
+ /**
+ * Tell the chrome process there is a destruction of PBrowser(Tab)
+ */
+ async NotifyTabDestroying(TabId tabId,
+ ContentParentId cpId);
+ /**
+ * 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(URIParams manifestURI, URIParams documentURI,
+ PrincipalInfo loadingPrincipal, bool stickDocument);
+
+ /**
+ * Sets "offline-app" permission for the principal. Called when we hit
+ * a web app with the manifest attribute in <html> and
+ * offline-apps.allow_by_default is set to true.
+ */
+ 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 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,
+ TabId tabId);
+
+ async Profile(nsCString aProfile);
+
+ /**
+ * Request graphics initialization information from the parent.
+ */
+ sync GetGraphicsDeviceInitData()
+ returns (ContentDeviceData aData);
+
+ sync CreateWindow(nullable PBrowser aThisTab,
+ PBrowser aNewTab,
+ PRenderFrame aRenderFrame,
+ uint32_t aChromeFlags,
+ bool aCalledFromJS,
+ bool aPositionSpecified,
+ bool aSizeSpecified,
+ nsCString aFeatures,
+ nsCString aBaseURI,
+ DocShellOriginAttributes aOpenerOriginAttributes,
+ float aFullZoom)
+ returns (nsresult rv,
+ bool windowOpened,
+ FrameScriptInfo[] frameScripts,
+ nsCString urlToLoad,
+ TextureFactoryIdentifier textureFactoryIdentifier,
+ uint64_t layersId);
+
+ sync GetAndroidSystemInfo()
+ returns (AndroidSystemInfo info);
+
+ /**
+ * 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);
+
+ /**
+ * Tell the parent process that the child process is low on memory. This
+ * allows the parent process to save a memory report that can potentially be
+ * sent with a crash report from the content process.
+ */
+ async NotifyLowMemory();
+
+ async GetFilesRequest(nsID aID, nsString aDirectory, bool aRecursiveFlag);
+ async DeleteGetFilesRequest(nsID aID);
+
+ async StoreAndBroadcastBlobURLRegistration(nsCString url, PBlob blob,
+ Principal principal);
+
+ async UnstoreAndBroadcastBlobURLUnregistration(nsCString url);
+
+ /**
+ * Messages for communicating child Telemetry to the parent process
+ */
+ async AccumulateChildHistogram(Accumulation[] accumulations);
+ async AccumulateChildKeyedHistogram(KeyedAccumulation[] accumulations);
+
+ sync GetA11yContentId() returns (uint32_t aContentId);
+
+both:
+ async AsyncMessage(nsString aMessage, CpowEntry[] aCpows,
+ Principal aPrincipal, ClonedMessageData aData);
+
+ /**
+ * Notify `push-subscription-modified` observers in the parent and child.
+ */
+ async NotifyPushSubscriptionModifiedObservers(nsCString scope,
+ Principal principal);
+};
+
+}
+}
diff --git a/dom/ipc/PContentBridge.ipdl b/dom/ipc/PContentBridge.ipdl
new file mode 100644
index 000000000..72f839178
--- /dev/null
+++ b/dom/ipc/PContentBridge.ipdl
@@ -0,0 +1,69 @@
+/* -*- 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 protocol PBlob;
+include protocol PBrowser;
+include protocol PContent;
+include protocol PJavaScript;
+include protocol PFileDescriptorSet;
+include protocol PSendStream;
+
+include DOMTypes;
+include JavaScriptTypes;
+include PTabContext;
+
+using class IPC::Principal from "mozilla/dom/PermissionMessageUtils.h";
+using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
+using mozilla::dom::ContentParentId from "mozilla/dom/ipc/IdType.h";
+using class mozilla::dom::MessagePort from "mozilla/dom/MessagePort.h";
+using class mozilla::dom::ipc::StructuredCloneData from "mozilla/dom/ipc/StructuredCloneData.h";
+
+namespace mozilla {
+namespace dom {
+
+/*
+ * PContentBridge allows us to represent a parent/child relationship between two
+ * child processes. When a child process wants to open its own child, it asks
+ * the root process to create a new process and then bridge them. The first
+ * child will allocate the PContentBridgeParent, and the newly opened child will
+ * allocate the PContentBridgeChild. This protocol allows these processes to
+ * share PBrowsers and send messages to each other.
+ */
+nested(upto inside_cpow) sync protocol PContentBridge
+{
+ bridges PContent, PContent;
+
+ manages PBlob;
+ manages PBrowser;
+ manages PFileDescriptorSet;
+ manages PJavaScript;
+ manages PSendStream;
+
+parent:
+ sync SyncMessage(nsString aMessage, ClonedMessageData aData,
+ CpowEntry[] aCpows, Principal aPrincipal)
+ returns (StructuredCloneData[] retval);
+
+ async PJavaScript();
+
+ async PSendStream();
+
+both:
+ // Both the parent and the child can construct the PBrowser.
+ // See the comment in PContent::PBrowser().
+ async PBrowser(TabId tabId, IPCTabContext context, uint32_t chromeFlags,
+ ContentParentId cpId, bool isForApp, bool isForBrowser);
+
+ async PBlob(BlobConstructorParams params);
+
+ async PFileDescriptorSet(FileDescriptor fd);
+
+ async AsyncMessage(nsString aMessage, CpowEntry[] aCpows,
+ Principal aPrincipal, ClonedMessageData aData);
+};
+
+}
+}
diff --git a/dom/ipc/PContentPermission.ipdlh b/dom/ipc/PContentPermission.ipdlh
new file mode 100644
index 000000000..1909c0ef8
--- /dev/null
+++ b/dom/ipc/PContentPermission.ipdlh
@@ -0,0 +1,20 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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;
+ nsCString access;
+ 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 000000000..8eb827862
--- /dev/null
+++ b/dom/ipc/PContentPermissionRequest.ipdl
@@ -0,0 +1,28 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 NotifyVisibility(bool visibility);
+ async Destroy();
+
+child:
+ async GetVisibility();
+ async NotifyResult(bool allow, PermissionChoice[] choices);
+ async __delete__();
+};
+
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/PCrashReporter.ipdl b/dom/ipc/PCrashReporter.ipdl
new file mode 100644
index 000000000..8f965f2ee
--- /dev/null
+++ b/dom/ipc/PCrashReporter.ipdl
@@ -0,0 +1,31 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set sw=4 ts=8 et tw=80 :
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PContent;
+include protocol PPluginModule;
+include protocol PGMP;
+
+namespace mozilla {
+namespace dom {
+
+struct Mapping {
+ nsCString library_name;
+ nsCString file_id;
+ uintptr_t start_address;
+ size_t mapping_length;
+ size_t file_offset;
+};
+
+async protocol PCrashReporter {
+ manager PContent or PPluginModule or PGMP;
+parent:
+ async AnnotateCrashReport(nsCString key, nsCString data);
+ async AppendAppNotes(nsCString data);
+ async __delete__();
+};
+
+}
+}
diff --git a/dom/ipc/PCycleCollectWithLogs.ipdl b/dom/ipc/PCycleCollectWithLogs.ipdl
new file mode 100644
index 000000000..f420c6dda
--- /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/PDatePicker.ipdl b/dom/ipc/PDatePicker.ipdl
new file mode 100644
index 000000000..90a2654bb
--- /dev/null
+++ b/dom/ipc/PDatePicker.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 PDatePicker
+{
+ manager PBrowser;
+
+parent:
+ async Open();
+
+child:
+ async Cancel();
+
+ async __delete__(nsString color);
+};
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/PDocumentRenderer.ipdl b/dom/ipc/PDocumentRenderer.ipdl
new file mode 100644
index 000000000..bdaed45d7
--- /dev/null
+++ b/dom/ipc/PDocumentRenderer.ipdl
@@ -0,0 +1,25 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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/GfxMessageUtils.h";
+
+using nsIntSize from "nsSize.h";
+
+namespace mozilla {
+namespace ipc {
+
+protocol PDocumentRenderer
+{
+ manager PBrowser;
+
+parent:
+ // Returns the width and height, in pixels, of the returned ARGB32 data.
+ async __delete__(nsIntSize renderedSize, nsCString data);
+};
+
+} // namespace ipc
+} // namespace mozilla
diff --git a/dom/ipc/PFilePicker.ipdl b/dom/ipc/PFilePicker.ipdl
new file mode 100644
index 000000000..234a055c6
--- /dev/null
+++ b/dom/ipc/PFilePicker.ipdl
@@ -0,0 +1,46 @@
+/* -*- 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 PBlob;
+include protocol PBrowser;
+
+using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
+
+namespace mozilla {
+namespace dom {
+
+struct InputBlobs
+{
+ PBlob[] 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 displayDirectory, nsString okButtonLabel);
+
+child:
+ async __delete__(MaybeInputData data, int16_t result);
+};
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/PMemoryReportRequest.ipdl b/dom/ipc/PMemoryReportRequest.ipdl
new file mode 100644
index 000000000..092d44250
--- /dev/null
+++ b/dom/ipc/PMemoryReportRequest.ipdl
@@ -0,0 +1,29 @@
+/* -*- 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/. */
+
+include protocol PContent;
+
+namespace mozilla {
+namespace dom {
+
+struct MemoryReport {
+ nsCString process;
+ nsCString path;
+ int32_t kind;
+ int32_t units;
+ int64_t amount;
+ nsCString desc;
+};
+
+protocol PMemoryReportRequest {
+ manager PContent;
+
+parent:
+ async Report(MemoryReport aReport);
+ async __delete__();
+};
+
+}
+}
diff --git a/dom/ipc/PPluginWidget.ipdl b/dom/ipc/PPluginWidget.ipdl
new file mode 100644
index 000000000..d96ea4ed8
--- /dev/null
+++ b/dom/ipc/PPluginWidget.ipdl
@@ -0,0 +1,61 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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/GfxMessageUtils.h";
+
+using nsIntRect from "nsRect.h";
+
+namespace mozilla {
+namespace plugins {
+
+/**
+ * PPluginWidget - a nsIWidget'ish protocol for windowed plugins in e10s.
+ * On windows and linux we create native widgets in chrome which we then manage
+ * from content. On the 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);
+
+ /**
+ * 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 000000000..07ee7a211
--- /dev/null
+++ b/dom/ipc/PProcessHangMonitor.ipdl
@@ -0,0 +1,47 @@
+/* -*- 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/. */
+
+using base::ProcessId from "base/process.h";
+using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
+
+namespace mozilla {
+
+struct SlowScriptData
+{
+ TabId tabId;
+ nsCString filename;
+ uint32_t lineno;
+};
+
+struct PluginHangData
+{
+ uint32_t pluginId;
+ ProcessId contentProcessId;
+};
+
+union HangData
+{
+ SlowScriptData;
+ PluginHangData;
+};
+
+protocol PProcessHangMonitor
+{
+parent:
+ async HangEvidence(HangData data);
+ async ClearHang();
+
+child:
+ async TerminateScript();
+
+ async BeginStartingDebugger();
+ async EndStartingDebugger();
+
+ async ForcePaint(TabId tabId, uint64_t aLayerObserverEpoch);
+};
+
+} // namespace mozilla
diff --git a/dom/ipc/PScreenManager.ipdl b/dom/ipc/PScreenManager.ipdl
new file mode 100644
index 000000000..a6ad26cfc
--- /dev/null
+++ b/dom/ipc/PScreenManager.ipdl
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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 PContent;
+
+include "mozilla/GfxMessageUtils.h";
+
+using nsIntRect from "nsRect.h";
+using mozilla::dom::TabId from "mozilla/dom/ipc/IdType.h";
+
+namespace mozilla {
+namespace dom {
+
+struct ScreenDetails {
+ uint32_t id;
+ nsIntRect rect;
+ nsIntRect rectDisplayPix;
+ nsIntRect availRect;
+ nsIntRect availRectDisplayPix;
+ int32_t pixelDepth;
+ int32_t colorDepth;
+ double contentsScaleFactor;
+};
+
+nested(upto inside_cpow) sync protocol PScreenManager
+{
+ manager PContent;
+
+parent:
+ nested(inside_sync) sync Refresh()
+ returns (uint32_t numberOfScreens,
+ float systemDefaultScale,
+ bool success);
+
+ nested(inside_cpow) sync ScreenRefresh(uint32_t aId)
+ returns (ScreenDetails screen,
+ bool success);
+
+ nested(inside_sync) sync GetPrimaryScreen()
+ returns (ScreenDetails screen,
+ bool success);
+
+ nested(inside_sync) sync ScreenForRect(int32_t aLeft,
+ int32_t aTop,
+ int32_t aWidth,
+ int32_t aHeight)
+ returns (ScreenDetails screen,
+ bool success);
+
+ nested(inside_cpow) sync ScreenForBrowser(TabId aTabId)
+ returns (ScreenDetails screen,
+ bool success);
+
+child:
+ async __delete__();
+};
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/PTabContext.ipdlh b/dom/ipc/PTabContext.ipdlh
new file mode 100644
index 000000000..507ef59d9
--- /dev/null
+++ b/dom/ipc/PTabContext.ipdlh
@@ -0,0 +1,84 @@
+/* -*- 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;
+include PBrowserOrId;
+
+using UIStateChangeType from "nsPIDOMWindow.h";
+using mozilla::DocShellOriginAttributes 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().
+//
+// If isMozBrowserElement is false, this PopupIPCTabContext is either a
+// <xul:browser> or an app frame. The frame's app-id and app-frame-owner-app-id
+// will be equal to the opener's values. For a <xul:browser>, those app IDs
+// will be NO_APP_ID.
+//
+// If isMozBrowserElement is true, the frame's browserFrameOwnerAppId will be
+// equal to the opener's app-id.
+//
+// It's an error to set isMozBrowserElement == false if opener is a mozbrowser
+// element. Such a PopupIPCTabContext should be rejected by code which receives
+// it.
+struct PopupIPCTabContext
+{
+ PBrowserOrId opener;
+ bool isMozBrowserElement;
+};
+
+// An IPCTabContext which corresponds to an app, browser, or normal frame.
+struct FrameIPCTabContext
+{
+ // The originAttributes dictionary.
+ DocShellOriginAttributes originAttributes;
+
+ // The ID of the app containing this app/browser frame, if applicable.
+ uint32_t frameOwnerAppId;
+
+ // Whether this is a mozbrowser frame. <iframe mozbrowser mozapp> and
+ // <xul:browser> are not considered to be mozbrowser frames.
+ bool isMozBrowserElement;
+
+ // Whether this TabContext should work in prerender mode.
+ bool isPrerendered;
+
+ // 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 showAccelerators;
+ UIStateChangeType showFocusRings;
+};
+
+// XXXcatalinb: This is only used by ServiceWorkerClients::OpenWindow.
+// Because service workers don't have an associated TabChild
+// we can't satisfy the security constraints on b2g. As such, the parent
+// process will accept this tab context only on desktop.
+struct UnsafeIPCTabContext
+{ };
+
+// 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;
+ UnsafeIPCTabContext;
+};
+
+}
+}
diff --git a/dom/ipc/PermissionMessageUtils.cpp b/dom/ipc/PermissionMessageUtils.cpp
new file mode 100644
index 000000000..445124b0a
--- /dev/null
+++ b/dom/ipc/PermissionMessageUtils.cpp
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/dom/PermissionMessageUtils.h"
+#include "nsISerializable.h"
+#include "nsSerializationHelper.h"
+
+namespace IPC {
+
+void
+ParamTraits<Principal>::Write(Message* aMsg, const paramType& aParam) {
+ bool isNull = !aParam.mPrincipal;
+ WriteParam(aMsg, isNull);
+ if (isNull) {
+ return;
+ }
+
+ bool isSerialized = false;
+ nsCString principalString;
+ nsCOMPtr<nsISerializable> serializable = do_QueryInterface(aParam.mPrincipal);
+ if (serializable) {
+ nsresult rv = NS_SerializeToString(serializable, principalString);
+ if (NS_SUCCEEDED(rv)) {
+ isSerialized = true;
+ }
+ }
+
+ if (!isSerialized) {
+ NS_RUNTIMEABORT("Unable to serialize principal.");
+ return;
+ }
+
+ WriteParam(aMsg, principalString);
+}
+
+bool
+ParamTraits<Principal>::Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+{
+ bool isNull;
+ if (!ReadParam(aMsg, aIter, &isNull)) {
+ return false;
+ }
+
+ if (isNull) {
+ aResult->mPrincipal = nullptr;
+ return true;
+ }
+
+ nsCString principalString;
+ if (!ReadParam(aMsg, aIter, &principalString)) {
+ return false;
+ }
+
+ nsCOMPtr<nsISupports> iSupports;
+ nsresult rv = NS_DeserializeObject(principalString, getter_AddRefs(iSupports));
+ NS_ENSURE_SUCCESS(rv, false);
+
+ nsCOMPtr<nsIPrincipal> principal = do_QueryInterface(iSupports);
+ NS_ENSURE_TRUE(principal, false);
+
+ principal.swap(aResult->mPrincipal);
+ return true;
+}
+
+} // namespace IPC
+
diff --git a/dom/ipc/PermissionMessageUtils.h b/dom/ipc/PermissionMessageUtils.h
new file mode 100644
index 000000000..3434c83b9
--- /dev/null
+++ b/dom/ipc/PermissionMessageUtils.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_permission_message_utils_h__
+#define mozilla_dom_permission_message_utils_h__
+
+#include "ipc/IPCMessageUtils.h"
+#include "nsCOMPtr.h"
+#include "nsIPrincipal.h"
+
+namespace IPC {
+
+class Principal
+{
+ friend struct ParamTraits<Principal>;
+
+public:
+ Principal()
+ : mPrincipal(nullptr)
+ {}
+
+ explicit Principal(nsIPrincipal* aPrincipal)
+ : mPrincipal(aPrincipal)
+ {}
+
+ operator nsIPrincipal*() const { return mPrincipal.get(); }
+
+ Principal& operator=(const Principal& aOther)
+ {
+ mPrincipal = aOther.mPrincipal;
+ return *this;
+ }
+
+private:
+ nsCOMPtr<nsIPrincipal> mPrincipal;
+};
+
+template <>
+struct ParamTraits<Principal>
+{
+ typedef Principal paramType;
+ static void Write(Message* aMsg, const paramType& aParam);
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult);
+};
+
+} // namespace IPC
+
+#endif // mozilla_dom_permission_message_utils_h__
+
diff --git a/dom/ipc/PreallocatedProcessManager.cpp b/dom/ipc/PreallocatedProcessManager.cpp
new file mode 100644
index 000000000..58ff84f21
--- /dev/null
+++ b/dom/ipc/PreallocatedProcessManager.cpp
@@ -0,0 +1,252 @@
+/* -*- 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/Unused.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "nsIPropertyBag2.h"
+#include "ProcessPriorityManager.h"
+#include "nsServiceManagerUtils.h"
+
+// This number is fairly arbitrary ... the intention is to put off
+// launching another app process until the last one has finished
+// loading its content, to reduce CPU/memory/IO contention.
+#define DEFAULT_ALLOCATE_DELAY 1000
+
+using namespace mozilla;
+using namespace mozilla::hal;
+using namespace mozilla::dom;
+
+namespace {
+
+/**
+ * This singleton class implements the static methods on
+ * PreallocatedProcessManager.
+ */
+class PreallocatedProcessManagerImpl final
+ : public nsIObserver
+{
+public:
+ static PreallocatedProcessManagerImpl* Singleton();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ // See comments on PreallocatedProcessManager for these methods.
+ void AllocateAfterDelay();
+ void AllocateOnIdle();
+ void AllocateNow();
+ already_AddRefed<ContentParent> Take();
+
+private:
+ static mozilla::StaticRefPtr<PreallocatedProcessManagerImpl> sSingleton;
+
+ PreallocatedProcessManagerImpl();
+ ~PreallocatedProcessManagerImpl() {}
+ DISALLOW_EVIL_CONSTRUCTORS(PreallocatedProcessManagerImpl);
+
+ void Init();
+
+ void RereadPrefs();
+ void Enable();
+ void Disable();
+
+ void ObserveProcessShutdown(nsISupports* aSubject);
+
+ bool mEnabled;
+ bool mShutdown;
+ RefPtr<ContentParent> mPreallocatedAppProcess;
+};
+
+/* static */ StaticRefPtr<PreallocatedProcessManagerImpl>
+PreallocatedProcessManagerImpl::sSingleton;
+
+/* static */ PreallocatedProcessManagerImpl*
+PreallocatedProcessManagerImpl::Singleton()
+{
+ if (!sSingleton) {
+ sSingleton = new PreallocatedProcessManagerImpl();
+ sSingleton->Init();
+ ClearOnShutdown(&sSingleton);
+ }
+
+ return sSingleton;
+}
+
+NS_IMPL_ISUPPORTS(PreallocatedProcessManagerImpl, nsIObserver)
+
+PreallocatedProcessManagerImpl::PreallocatedProcessManagerImpl()
+ :
+ mEnabled(false)
+ , mShutdown(false)
+{}
+
+void
+PreallocatedProcessManagerImpl::Init()
+{
+ Preferences::AddStrongObserver(this, "dom.ipc.processPrelaunch.enabled");
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ if (os) {
+ os->AddObserver(this, "ipc:content-shutdown",
+ /* weakRef = */ false);
+ os->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID,
+ /* weakRef = */ false);
+ }
+ {
+ RereadPrefs();
+ }
+}
+
+NS_IMETHODIMP
+PreallocatedProcessManagerImpl::Observe(nsISupports* aSubject,
+ const char* aTopic,
+ const char16_t* aData)
+{
+ if (!strcmp("ipc:content-shutdown", aTopic)) {
+ ObserveProcessShutdown(aSubject);
+ } else 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)) {
+ mShutdown = true;
+ } else {
+ MOZ_ASSERT(false);
+ }
+
+ return NS_OK;
+}
+
+void
+PreallocatedProcessManagerImpl::RereadPrefs()
+{
+ if (Preferences::GetBool("dom.ipc.processPrelaunch.enabled")) {
+ Enable();
+ } else {
+ Disable();
+ }
+}
+
+already_AddRefed<ContentParent>
+PreallocatedProcessManagerImpl::Take()
+{
+ return mPreallocatedAppProcess.forget();
+}
+
+void
+PreallocatedProcessManagerImpl::Enable()
+{
+ if (mEnabled) {
+ return;
+ }
+
+ mEnabled = true;
+ AllocateAfterDelay();
+}
+
+void
+PreallocatedProcessManagerImpl::AllocateAfterDelay()
+{
+ if (!mEnabled || mPreallocatedAppProcess) {
+ return;
+ }
+
+ MessageLoop::current()->PostDelayedTask(
+ NewRunnableMethod(this, &PreallocatedProcessManagerImpl::AllocateOnIdle),
+ Preferences::GetUint("dom.ipc.processPrelaunch.delayMs",
+ DEFAULT_ALLOCATE_DELAY));
+}
+
+void
+PreallocatedProcessManagerImpl::AllocateOnIdle()
+{
+ if (!mEnabled || mPreallocatedAppProcess) {
+ return;
+ }
+
+ MessageLoop::current()->PostIdleTask(NewRunnableMethod(this, &PreallocatedProcessManagerImpl::AllocateNow));
+}
+
+void
+PreallocatedProcessManagerImpl::AllocateNow()
+{
+ if (!mEnabled || mPreallocatedAppProcess) {
+ return;
+ }
+
+ mPreallocatedAppProcess = ContentParent::PreallocateAppProcess();
+}
+
+void
+PreallocatedProcessManagerImpl::Disable()
+{
+ if (!mEnabled) {
+ return;
+ }
+
+ mEnabled = false;
+
+ if (mPreallocatedAppProcess) {
+ mPreallocatedAppProcess->Close();
+ mPreallocatedAppProcess = nullptr;
+ }
+}
+
+void
+PreallocatedProcessManagerImpl::ObserveProcessShutdown(nsISupports* aSubject)
+{
+ if (!mPreallocatedAppProcess) {
+ return;
+ }
+
+ nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
+ NS_ENSURE_TRUE_VOID(props);
+
+ uint64_t childID = CONTENT_PROCESS_ID_UNKNOWN;
+ props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"), &childID);
+ NS_ENSURE_TRUE_VOID(childID != CONTENT_PROCESS_ID_UNKNOWN);
+
+ if (childID == mPreallocatedAppProcess->ChildID()) {
+ mPreallocatedAppProcess = nullptr;
+ }
+}
+
+inline PreallocatedProcessManagerImpl* GetPPMImpl()
+{
+ return PreallocatedProcessManagerImpl::Singleton();
+}
+
+} // namespace
+
+namespace mozilla {
+
+/* static */ void
+PreallocatedProcessManager::AllocateAfterDelay()
+{
+ GetPPMImpl()->AllocateAfterDelay();
+}
+
+/* static */ void
+PreallocatedProcessManager::AllocateOnIdle()
+{
+ GetPPMImpl()->AllocateOnIdle();
+}
+
+/* static */ void
+PreallocatedProcessManager::AllocateNow()
+{
+ GetPPMImpl()->AllocateNow();
+}
+
+/* static */ already_AddRefed<ContentParent>
+PreallocatedProcessManager::Take()
+{
+ return GetPPMImpl()->Take();
+}
+
+} // namespace mozilla
diff --git a/dom/ipc/PreallocatedProcessManager.h b/dom/ipc/PreallocatedProcessManager.h
new file mode 100644
index 000000000..94acd31fd
--- /dev/null
+++ b/dom/ipc/PreallocatedProcessManager.h
@@ -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/. */
+
+#ifndef mozilla_PreallocatedProcessManager_h
+#define mozilla_PreallocatedProcessManager_h
+
+#include "base/basictypes.h"
+#include "nsCOMPtr.h"
+#include "nsIObserver.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.
+ *
+ * The static methods here are implemented by forwarding calls on to a
+ * PreallocatedProcessManagerImpl singleton class, so if you add a new static
+ * method here, you'll need to write a corresponding public method on the
+ * singleton.
+ */
+class PreallocatedProcessManager final
+{
+ typedef mozilla::dom::ContentParent ContentParent;
+
+public:
+ /**
+ * Create a process after a delay. We wait for a period of time (specified
+ * by the dom.ipc.processPrelaunch.delayMs pref), then wait for this process
+ * to go idle, then allocate the new process.
+ *
+ * If the dom.ipc.processPrelaunch.enabled pref is false, or if we already
+ * have a preallocated process, this function does nothing.
+ */
+ static void AllocateAfterDelay();
+
+ /**
+ * Create a process once this process goes idle.
+ *
+ * If the dom.ipc.processPrelaunch.enabled pref is false, or if we already
+ * have a preallocated process, this function does nothing.
+ */
+ static void AllocateOnIdle();
+
+ /**
+ * Create a process right now.
+ *
+ * If the dom.ipc.processPrelaunch.enabled pref is false, or if we already
+ * have a preallocated process, this function does nothing.
+ */
+ static void AllocateNow();
+
+ /**
+ * Take the preallocated process, if we have one. If we don't have one, this
+ * returns null.
+ *
+ * If you call Take() twice in a row, the second call is guaranteed to return
+ * null.
+ *
+ * After you Take() the preallocated process, you need to call one of the
+ * Allocate* functions (or change the dom.ipc.processPrelaunch pref from
+ * false to true) before we'll create a new process.
+ */
+ static already_AddRefed<ContentParent> Take();
+
+private:
+ PreallocatedProcessManager();
+ DISALLOW_EVIL_CONSTRUCTORS(PreallocatedProcessManager);
+};
+
+} // namespace mozilla
+
+#endif // defined mozilla_PreallocatedProcessManager_h
diff --git a/dom/ipc/ProcessHangMonitor.cpp b/dom/ipc/ProcessHangMonitor.cpp
new file mode 100644
index 000000000..b574be61f
--- /dev/null
+++ b/dom/ipc/ProcessHangMonitor.cpp
@@ -0,0 +1,1278 @@
+/* -*- 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 "js/GCAPI.h"
+
+#include "mozilla/Atomics.h"
+#include "mozilla/BackgroundHangMonitor.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/ScriptSettings.h"
+#include "mozilla/dom/TabChild.h"
+#include "mozilla/dom/TabParent.h"
+#include "mozilla/Monitor.h"
+#include "mozilla/plugins/PluginBridge.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Unused.h"
+
+#include "nsIFrameLoader.h"
+#include "nsIHangReport.h"
+#include "nsITabParent.h"
+#include "nsPluginHost.h"
+#include "nsThreadUtils.h"
+#ifdef MOZ_CRASHREPORTER
+#include "nsExceptionHandler.h"
+#endif
+
+#include "base/task.h"
+#include "base/thread.h"
+
+#ifdef XP_WIN
+// For IsDebuggerPresent()
+#include <windows.h>
+#endif
+
+using namespace mozilla;
+using namespace mozilla::dom;
+
+/*
+ * 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:
+ explicit HangMonitorChild(ProcessHangMonitor* aMonitor);
+ virtual ~HangMonitorChild();
+
+ void Open(Transport* aTransport, ProcessId aOtherPid,
+ MessageLoop* aIOLoop);
+
+ typedef ProcessHangMonitor::SlowScriptAction SlowScriptAction;
+ SlowScriptAction NotifySlowScript(nsITabChild* aTabChild,
+ const char* aFileName,
+ unsigned aLineNo);
+ void NotifySlowScriptAsync(TabId aTabId,
+ const nsCString& aFileName,
+ unsigned aLineNo);
+
+ bool IsDebuggerStartupComplete();
+
+ void NotifyPluginHang(uint32_t aPluginId);
+ void NotifyPluginHangAsync(uint32_t aPluginId);
+
+ void ClearHang();
+ void ClearHangAsync();
+ void ClearForcePaint();
+
+ virtual bool RecvTerminateScript() override;
+ virtual bool RecvBeginStartingDebugger() override;
+ virtual bool RecvEndStartingDebugger() override;
+
+ virtual bool RecvForcePaint(const TabId& aTabId, const uint64_t& aLayerObserverEpoch) override;
+
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ void InterruptCallback();
+ void Shutdown();
+
+ static HangMonitorChild* Get() { return sInstance; }
+
+ MessageLoop* MonitorLoop() { return mHangMonitor->MonitorLoop(); }
+
+ private:
+ void ShutdownOnThread();
+
+ static Atomic<HangMonitorChild*> sInstance;
+ UniquePtr<BackgroundHangMonitor> mForcePaintMonitor;
+
+ const RefPtr<ProcessHangMonitor> mHangMonitor;
+ Monitor mMonitor;
+
+ // Main thread-only.
+ bool mSentReport;
+
+ // These fields must be accessed with mMonitor held.
+ bool mTerminateScript;
+ bool mStartDebugger;
+ bool mFinishedStartingDebugger;
+ bool mForcePaint;
+ TabId mForcePaintTab;
+ MOZ_INIT_OUTSIDE_CTOR uint64_t mForcePaintEpoch;
+ JSContext* mContext;
+ bool mShutdownDone;
+
+ // This field is only accessed on the hang thread.
+ bool mIPCOpen;
+};
+
+Atomic<HangMonitorChild*> HangMonitorChild::sInstance;
+
+/* Parent process objects */
+
+class HangMonitorParent;
+
+class HangMonitoredProcess final
+ : public nsIHangReport
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ HangMonitoredProcess(HangMonitorParent* aActor,
+ ContentParent* aContentParent)
+ : mActor(aActor), mContentParent(aContentParent) {}
+
+ NS_IMETHOD GetHangType(uint32_t* aHangType) override;
+ NS_IMETHOD GetScriptBrowser(nsIDOMElement** aBrowser) override;
+ NS_IMETHOD GetScriptFileName(nsACString& aFileName) override;
+ NS_IMETHOD GetScriptLineNo(uint32_t* aLineNo) override;
+
+ NS_IMETHOD GetPluginName(nsACString& aPluginName) override;
+
+ NS_IMETHOD TerminateScript() override;
+ NS_IMETHOD BeginStartingDebugger() override;
+ NS_IMETHOD EndStartingDebugger() override;
+ NS_IMETHOD TerminatePlugin() override;
+ NS_IMETHOD UserCanceled() override;
+
+ NS_IMETHOD IsReportForBrowser(nsIFrameLoader* aFrameLoader, bool* aResult) override;
+
+ // 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() {}
+
+ // Everything here is main thread-only.
+ HangMonitorParent* mActor;
+ ContentParent* mContentParent;
+ HangData mHangData;
+ nsAutoString mDumpId;
+};
+
+class HangMonitorParent
+ : public PProcessHangMonitorParent
+{
+public:
+ explicit HangMonitorParent(ProcessHangMonitor* aMonitor);
+ virtual ~HangMonitorParent();
+
+ void Open(Transport* aTransport, ProcessId aPid, MessageLoop* aIOLoop);
+
+ virtual bool RecvHangEvidence(const HangData& aHangData) override;
+ virtual bool RecvClearHang() override;
+
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ void SetProcess(HangMonitoredProcess* aProcess) { mProcess = aProcess; }
+
+ void Shutdown();
+
+ void ForcePaint(dom::TabParent* aTabParent, uint64_t aLayerObserverEpoch);
+
+ void TerminateScript();
+ 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);
+
+ MessageLoop* MonitorLoop() { return mHangMonitor->MonitorLoop(); }
+
+private:
+ bool TakeBrowserMinidump(const PluginHangData& aPhd, nsString& aCrashId);
+
+ void ForcePaintOnThread(TabId aTabId, uint64_t aLayerObserverEpoch);
+
+ void ShutdownOnThread();
+
+ const RefPtr<ProcessHangMonitor> mHangMonitor;
+
+ // This field is read-only after construction.
+ bool mReportHangs;
+
+ // 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;
+};
+
+} // namespace
+
+/* HangMonitorChild implementation */
+
+HangMonitorChild::HangMonitorChild(ProcessHangMonitor* aMonitor)
+ : mHangMonitor(aMonitor),
+ mMonitor("HangMonitorChild lock"),
+ mSentReport(false),
+ mTerminateScript(false),
+ mStartDebugger(false),
+ mFinishedStartingDebugger(false),
+ mForcePaint(false),
+ mShutdownDone(false),
+ mIPCOpen(true)
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ mContext = danger::GetJSContext();
+ mForcePaintMonitor =
+ MakeUnique<mozilla::BackgroundHangMonitor>("Gecko_Child_ForcePaint",
+ 128, /* ms timeout for microhangs */
+ 8192 /* ms timeout for permahangs */,
+ BackgroundHangMonitor::THREAD_PRIVATE);
+}
+
+HangMonitorChild::~HangMonitorChild()
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(sInstance == this);
+ mForcePaintMonitor = nullptr;
+ sInstance = nullptr;
+}
+
+void
+HangMonitorChild::InterruptCallback()
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ bool forcePaint;
+ TabId forcePaintTab;
+ uint64_t forcePaintEpoch;
+
+ {
+ MonitorAutoLock lock(mMonitor);
+ forcePaint = mForcePaint;
+ forcePaintTab = mForcePaintTab;
+ forcePaintEpoch = mForcePaintEpoch;
+
+ mForcePaint = false;
+ }
+
+ if (forcePaint) {
+ RefPtr<TabChild> tabChild = TabChild::FindTabChild(forcePaintTab);
+ if (tabChild) {
+ tabChild->ForcePaint(forcePaintEpoch);
+ }
+ }
+}
+
+void
+HangMonitorChild::Shutdown()
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ MonitorAutoLock lock(mMonitor);
+ while (!mShutdownDone) {
+ mMonitor.Wait();
+ }
+}
+
+void
+HangMonitorChild::ShutdownOnThread()
+{
+ MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
+
+ MonitorAutoLock lock(mMonitor);
+ mShutdownDone = true;
+ mMonitor.Notify();
+}
+
+void
+HangMonitorChild::ActorDestroy(ActorDestroyReason aWhy)
+{
+ MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
+
+ mIPCOpen = false;
+
+ // We use a task here to ensure that IPDL is finished with this
+ // HangMonitorChild before it gets deleted on the main thread.
+ MonitorLoop()->PostTask(NewNonOwningRunnableMethod(this, &HangMonitorChild::ShutdownOnThread));
+}
+
+bool
+HangMonitorChild::RecvTerminateScript()
+{
+ MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
+
+ MonitorAutoLock lock(mMonitor);
+ mTerminateScript = true;
+ return true;
+}
+
+bool
+HangMonitorChild::RecvBeginStartingDebugger()
+{
+ MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
+
+ MonitorAutoLock lock(mMonitor);
+ mStartDebugger = true;
+ return true;
+}
+
+bool
+HangMonitorChild::RecvEndStartingDebugger()
+{
+ MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
+
+ MonitorAutoLock lock(mMonitor);
+ mFinishedStartingDebugger = true;
+ return true;
+}
+
+bool
+HangMonitorChild::RecvForcePaint(const TabId& aTabId, const uint64_t& aLayerObserverEpoch)
+{
+ MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
+
+ mForcePaintMonitor->NotifyActivity();
+
+ {
+ MonitorAutoLock lock(mMonitor);
+ mForcePaint = true;
+ mForcePaintTab = aTabId;
+ mForcePaintEpoch = aLayerObserverEpoch;
+ }
+
+ JS_RequestInterruptCallback(mContext);
+
+ return true;
+}
+
+void
+HangMonitorChild::ClearForcePaint()
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ MOZ_RELEASE_ASSERT(XRE_IsContentProcess());
+
+ mForcePaintMonitor->NotifyWait();
+}
+
+void
+HangMonitorChild::Open(Transport* aTransport, ProcessId aPid,
+ MessageLoop* aIOLoop)
+{
+ MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
+
+ MOZ_ASSERT(!sInstance);
+ sInstance = this;
+
+ DebugOnly<bool> ok = PProcessHangMonitorChild::Open(aTransport, aPid, aIOLoop);
+ MOZ_ASSERT(ok);
+}
+
+void
+HangMonitorChild::NotifySlowScriptAsync(TabId aTabId,
+ const nsCString& aFileName,
+ unsigned aLineNo)
+{
+ if (mIPCOpen) {
+ Unused << SendHangEvidence(SlowScriptData(aTabId, aFileName, aLineNo));
+ }
+}
+
+HangMonitorChild::SlowScriptAction
+HangMonitorChild::NotifySlowScript(nsITabChild* aTabChild,
+ const char* aFileName,
+ unsigned aLineNo)
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ mSentReport = true;
+
+ {
+ MonitorAutoLock lock(mMonitor);
+
+ if (mTerminateScript) {
+ mTerminateScript = false;
+ return SlowScriptAction::Terminate;
+ }
+
+ if (mStartDebugger) {
+ mStartDebugger = false;
+ return SlowScriptAction::StartDebugger;
+ }
+ }
+
+ TabId id;
+ if (aTabChild) {
+ RefPtr<TabChild> tabChild = static_cast<TabChild*>(aTabChild);
+ id = tabChild->GetTabId();
+ }
+ nsAutoCString filename(aFileName);
+
+ MonitorLoop()->PostTask(NewNonOwningRunnableMethod
+ <TabId, nsCString, unsigned>(this,
+ &HangMonitorChild::NotifySlowScriptAsync,
+ id, filename, aLineNo));
+ 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
+ MonitorLoop()->PostTask(NewNonOwningRunnableMethod<uint32_t>(this,
+ &HangMonitorChild::NotifyPluginHangAsync,
+ aPluginId));
+}
+
+void
+HangMonitorChild::NotifyPluginHangAsync(uint32_t aPluginId)
+{
+ MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
+
+ // 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
+ MonitorLoop()->PostTask(NewNonOwningRunnableMethod(this, &HangMonitorChild::ClearHangAsync));
+
+ MonitorAutoLock lock(mMonitor);
+ mSentReport = false;
+ mTerminateScript = false;
+ mStartDebugger = false;
+ mFinishedStartingDebugger = false;
+ }
+}
+
+void
+HangMonitorChild::ClearHangAsync()
+{
+ MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
+
+ // 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")
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ mReportHangs = mozilla::Preferences::GetBool("dom.ipc.reportProcessHangs", false);
+}
+
+HangMonitorParent::~HangMonitorParent()
+{
+#ifdef MOZ_CRASHREPORTER
+ MutexAutoLock lock(mBrowserCrashDumpHashLock);
+
+ for (auto iter = mBrowserCrashDumpIds.Iter(); !iter.Done(); iter.Next()) {
+ nsString crashId = iter.UserData();
+ if (!crashId.IsEmpty()) {
+ CrashReporter::DeleteMinidumpFilesForID(crashId);
+ }
+ }
+#endif
+}
+
+void
+HangMonitorParent::Shutdown()
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ MonitorAutoLock lock(mMonitor);
+
+ if (mProcess) {
+ mProcess->Clear();
+ mProcess = nullptr;
+ }
+
+ MonitorLoop()->PostTask(NewNonOwningRunnableMethod(this,
+ &HangMonitorParent::ShutdownOnThread));
+
+ while (!mShutdownDone) {
+ mMonitor.Wait();
+ }
+}
+
+void
+HangMonitorParent::ShutdownOnThread()
+{
+ MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
+
+ // 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::ForcePaint(dom::TabParent* aTab, uint64_t aLayerObserverEpoch)
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ TabId id = aTab->GetTabId();
+ MonitorLoop()->PostTask(NewNonOwningRunnableMethod<TabId, uint64_t>(
+ this, &HangMonitorParent::ForcePaintOnThread, id, aLayerObserverEpoch));
+}
+
+void
+HangMonitorParent::ForcePaintOnThread(TabId aTabId, uint64_t aLayerObserverEpoch)
+{
+ MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
+
+ if (mIPCOpen) {
+ Unused << SendForcePaint(aTabId, aLayerObserverEpoch);
+ }
+}
+
+void
+HangMonitorParent::ActorDestroy(ActorDestroyReason aWhy)
+{
+ MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
+ mIPCOpen = false;
+}
+
+void
+HangMonitorParent::Open(Transport* aTransport, ProcessId aPid,
+ MessageLoop* aIOLoop)
+{
+ MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
+
+ DebugOnly<bool> ok = PProcessHangMonitorParent::Open(aTransport, aPid, aIOLoop);
+ MOZ_ASSERT(ok);
+}
+
+class HangObserverNotifier final : public Runnable
+{
+public:
+ HangObserverNotifier(HangMonitoredProcess* aProcess,
+ HangMonitorParent *aParent,
+ const HangData& aHangData,
+ const nsString& aBrowserDumpId,
+ bool aTakeMinidump)
+ : mProcess(aProcess),
+ mParent(aParent),
+ mHangData(aHangData),
+ mBrowserDumpId(aBrowserDumpId),
+ mTakeMinidump(aTakeMinidump)
+ {}
+
+ NS_IMETHOD
+ Run() override
+ {
+ // chrome process, main thread
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ nsString dumpId;
+ if ((mHangData.type() == HangData::TPluginHangData) && mTakeMinidump) {
+ // We've been handed a partial minidump; complete it with plugin and
+ // content process dumps.
+ const PluginHangData& phd = mHangData.get_PluginHangData();
+ plugins::TakeFullMinidump(phd.pluginId(), phd.contentProcessId(),
+ mBrowserDumpId, dumpId);
+ mParent->UpdateMinidump(phd.pluginId(), dumpId);
+ } else {
+ // We already have a full minidump; go ahead and use it.
+ dumpId = mBrowserDumpId;
+ }
+
+ mProcess->SetHangData(mHangData, dumpId);
+
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ observerService->NotifyObservers(mProcess, "process-hang-report", nullptr);
+ return NS_OK;
+ }
+
+private:
+ RefPtr<HangMonitoredProcess> mProcess;
+ HangMonitorParent* mParent;
+ HangData mHangData;
+ nsAutoString mBrowserDumpId;
+ bool mTakeMinidump;
+};
+
+// 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)
+{
+#ifdef MOZ_CRASHREPORTER
+ 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;
+ }
+ }
+ }
+#endif // MOZ_CRASHREPORTER
+
+ return false;
+}
+
+bool
+HangMonitorParent::RecvHangEvidence(const HangData& aHangData)
+{
+ // chrome process, background thread
+ MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
+
+ if (!mReportHangs) {
+ return true;
+ }
+
+#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 true;
+ }
+#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);
+
+ nsCOMPtr<nsIRunnable> notifier =
+ new HangObserverNotifier(mProcess, this, aHangData, crashId, takeMinidump);
+ NS_DispatchToMainThread(notifier);
+
+ return true;
+}
+
+class ClearHangNotifier final : public Runnable
+{
+public:
+ explicit ClearHangNotifier(HangMonitoredProcess* aProcess)
+ : mProcess(aProcess)
+ {}
+
+ NS_IMETHOD
+ Run() override
+ {
+ // chrome process, main thread
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ mProcess->ClearHang();
+
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ observerService->NotifyObservers(mProcess, "clear-hang-report", nullptr);
+ return NS_OK;
+ }
+
+private:
+ RefPtr<HangMonitoredProcess> mProcess;
+};
+
+bool
+HangMonitorParent::RecvClearHang()
+{
+ // chrome process, background thread
+ MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
+
+ if (!mReportHangs) {
+ return true;
+ }
+
+ mHangMonitor->InitiateCPOWTimeout();
+
+ MonitorAutoLock lock(mMonitor);
+
+ nsCOMPtr<nsIRunnable> notifier =
+ new ClearHangNotifier(mProcess);
+ NS_DispatchToMainThread(notifier);
+
+ return true;
+}
+
+void
+HangMonitorParent::TerminateScript()
+{
+ MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
+
+ if (mIPCOpen) {
+ Unused << SendTerminateScript();
+ }
+}
+
+void
+HangMonitorParent::BeginStartingDebugger()
+{
+ MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
+
+ if (mIPCOpen) {
+ Unused << SendBeginStartingDebugger();
+ }
+}
+
+void
+HangMonitorParent::EndStartingDebugger()
+{
+ MOZ_RELEASE_ASSERT(MessageLoop::current() == MonitorLoop());
+
+ 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);
+#ifdef MOZ_CRASHREPORTER
+ if (aRemoveFiles && !crashId.IsEmpty()) {
+ CrashReporter::DeleteMinidumpFilesForID(crashId);
+ }
+#endif
+}
+
+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::GetScriptBrowser(nsIDOMElement** 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++) {
+ TabParent* tp = TabParent::GetFrom(tabs[i]);
+ if (tp->GetTabId() == tabId) {
+ nsCOMPtr<nsIDOMElement> node = do_QueryInterface(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::GetScriptLineNo(uint32_t* aLineNo)
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ if (mHangData.type() != HangData::TSlowScriptData) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ *aLineNo = mHangData.get_SlowScriptData().lineno();
+ 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()->MonitorLoop()->PostTask(NewNonOwningRunnableMethod(mActor,
+ &HangMonitorParent::TerminateScript));
+ 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()->MonitorLoop()->PostTask(NewNonOwningRunnableMethod(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()->MonitorLoop()->PostTask(NewNonOwningRunnableMethod(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, NS_LITERAL_CSTRING("HangMonitor"),
+ mDumpId);
+
+ if (mActor) {
+ mActor->CleanupPluginHang(id, false);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+HangMonitoredProcess::IsReportForBrowser(nsIFrameLoader* aFrameLoader, bool* aResult)
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ if (!mActor) {
+ *aResult = false;
+ return NS_OK;
+ }
+
+ TabParent* tp = TabParent::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;
+}
+
+static bool
+InterruptCallback(JSContext* cx)
+{
+ if (HangMonitorChild* child = HangMonitorChild::Get()) {
+ child->InterruptCallback();
+ }
+
+ return true;
+}
+
+ProcessHangMonitor* ProcessHangMonitor::sInstance;
+
+ProcessHangMonitor::ProcessHangMonitor()
+ : mCPOWTimeout(false)
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ MOZ_COUNT_CTOR(ProcessHangMonitor);
+
+ if (XRE_IsContentProcess()) {
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ obs->AddObserver(this, "xpcom-shutdown", false);
+ }
+
+ mThread = new base::Thread("ProcessHangMonitor");
+ if (!mThread->Start()) {
+ delete mThread;
+ mThread = nullptr;
+ }
+}
+
+ProcessHangMonitor::~ProcessHangMonitor()
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ MOZ_COUNT_DTOR(ProcessHangMonitor);
+
+ MOZ_ASSERT(sInstance == this);
+ sInstance = nullptr;
+
+ delete mThread;
+}
+
+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* child = HangMonitorChild::Get()) {
+ child->Shutdown();
+ delete child;
+ }
+
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (obs) {
+ obs->RemoveObserver(this, "xpcom-shutdown");
+ }
+ }
+ return NS_OK;
+}
+
+ProcessHangMonitor::SlowScriptAction
+ProcessHangMonitor::NotifySlowScript(nsITabChild* aTabChild,
+ const char* aFileName,
+ unsigned aLineNo)
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ return HangMonitorChild::Get()->NotifySlowScript(aTabChild, aFileName, aLineNo);
+}
+
+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(MessageLoop::current() == MonitorLoop());
+ mCPOWTimeout = true;
+}
+
+void
+ProcessHangMonitor::NotifyPluginHang(uint32_t aPluginId)
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ return HangMonitorChild::Get()->NotifyPluginHang(aPluginId);
+}
+
+PProcessHangMonitorParent*
+mozilla::CreateHangMonitorParent(ContentParent* aContentParent,
+ mozilla::ipc::Transport* aTransport,
+ base::ProcessId aOtherPid)
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ ProcessHangMonitor* monitor = ProcessHangMonitor::GetOrCreate();
+ HangMonitorParent* parent = new HangMonitorParent(monitor);
+
+ HangMonitoredProcess* process = new HangMonitoredProcess(parent, aContentParent);
+ parent->SetProcess(process);
+
+ monitor->MonitorLoop()->PostTask(NewNonOwningRunnableMethod
+ <mozilla::ipc::Transport*,
+ base::ProcessId,
+ MessageLoop*>(parent,
+ &HangMonitorParent::Open,
+ aTransport, aOtherPid,
+ XRE_GetIOMessageLoop()));
+
+ return parent;
+}
+
+PProcessHangMonitorChild*
+mozilla::CreateHangMonitorChild(mozilla::ipc::Transport* aTransport,
+ base::ProcessId aOtherPid)
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ JSContext* cx = danger::GetJSContext();
+ JS_AddInterruptCallback(cx, InterruptCallback);
+
+ ProcessHangMonitor* monitor = ProcessHangMonitor::GetOrCreate();
+ HangMonitorChild* child = new HangMonitorChild(monitor);
+
+ monitor->MonitorLoop()->PostTask(NewNonOwningRunnableMethod
+ <mozilla::ipc::Transport*,
+ base::ProcessId,
+ MessageLoop*>(child,
+ &HangMonitorChild::Open,
+ aTransport, aOtherPid,
+ XRE_GetIOMessageLoop()));
+
+ return child;
+}
+
+MessageLoop*
+ProcessHangMonitor::MonitorLoop()
+{
+ return mThread->message_loop();
+}
+
+/* static */ void
+ProcessHangMonitor::AddProcess(ContentParent* aContentParent)
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+
+ if (mozilla::Preferences::GetBool("dom.ipc.processHangMonitor", false)) {
+ DebugOnly<bool> opened = PProcessHangMonitor::Open(aContentParent);
+ MOZ_ASSERT(opened);
+ }
+}
+
+/* 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::ForcePaint(PProcessHangMonitorParent* aParent,
+ dom::TabParent* aTabParent,
+ uint64_t aLayerObserverEpoch)
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ auto parent = static_cast<HangMonitorParent*>(aParent);
+ parent->ForcePaint(aTabParent, aLayerObserverEpoch);
+}
+
+/* static */ void
+ProcessHangMonitor::ClearForcePaint()
+{
+ MOZ_RELEASE_ASSERT(NS_IsMainThread());
+ MOZ_RELEASE_ASSERT(XRE_IsContentProcess());
+
+ if (HangMonitorChild* child = HangMonitorChild::Get()) {
+ child->ClearForcePaint();
+ }
+}
diff --git a/dom/ipc/ProcessHangMonitor.h b/dom/ipc/ProcessHangMonitor.h
new file mode 100644
index 000000000..5ab81c87e
--- /dev/null
+++ b/dom/ipc/ProcessHangMonitor.h
@@ -0,0 +1,82 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_ProcessHangMonitor_h
+#define mozilla_ProcessHangMonitor_h
+
+#include "mozilla/Atomics.h"
+#include "nsIObserver.h"
+
+class nsITabChild;
+
+class MessageLoop;
+
+namespace base {
+class Thread;
+} // namespace base
+
+namespace mozilla {
+
+namespace dom {
+class ContentParent;
+class TabParent;
+} // namespace dom
+
+class PProcessHangMonitorParent;
+
+class ProcessHangMonitor final
+ : public nsIObserver
+{
+ private:
+ ProcessHangMonitor();
+ virtual ~ProcessHangMonitor();
+
+ public:
+ static ProcessHangMonitor* Get() { return sInstance; }
+ static ProcessHangMonitor* GetOrCreate();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+
+ static void AddProcess(dom::ContentParent* aContentParent);
+ static void RemoveProcess(PProcessHangMonitorParent* aParent);
+
+ static void ClearHang();
+
+ static void ForcePaint(PProcessHangMonitorParent* aParent,
+ dom::TabParent* aTab,
+ uint64_t aLayerObserverEpoch);
+ static void ClearForcePaint();
+
+ enum SlowScriptAction {
+ Continue,
+ Terminate,
+ StartDebugger
+ };
+ SlowScriptAction NotifySlowScript(nsITabChild* aTabChild,
+ const char* aFileName,
+ unsigned aLineNo);
+
+ void NotifyPluginHang(uint32_t aPluginId);
+
+ bool IsDebuggerStartupComplete();
+
+ void InitiateCPOWTimeout();
+ bool ShouldTimeOutCPOWs();
+
+ MessageLoop* MonitorLoop();
+
+ private:
+ static ProcessHangMonitor* sInstance;
+
+ Atomic<bool> mCPOWTimeout;
+
+ base::Thread* mThread;
+};
+
+} // namespace mozilla
+
+#endif // mozilla_ProcessHangMonitor_h
diff --git a/dom/ipc/ProcessHangMonitorIPC.h b/dom/ipc/ProcessHangMonitorIPC.h
new file mode 100644
index 000000000..dbb9f9b76
--- /dev/null
+++ b/dom/ipc/ProcessHangMonitorIPC.h
@@ -0,0 +1,34 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_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
+
+PProcessHangMonitorParent*
+CreateHangMonitorParent(mozilla::dom::ContentParent* aContentParent,
+ mozilla::ipc::Transport* aTransport,
+ base::ProcessId aOtherProcess);
+
+PProcessHangMonitorChild*
+CreateHangMonitorChild(mozilla::ipc::Transport* aTransport,
+ base::ProcessId aOtherProcess);
+
+} // namespace mozilla
+
+#endif // mozilla_ProcessHangMonitorIPC_h
diff --git a/dom/ipc/ProcessPriorityManager.cpp b/dom/ipc/ProcessPriorityManager.cpp
new file mode 100644
index 000000000..80839796d
--- /dev/null
+++ b/dom/ipc/ProcessPriorityManager.cpp
@@ -0,0 +1,1454 @@
+/* -*- 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/TabParent.h"
+#include "mozilla/Hal.h"
+#include "mozilla/IntegerPrintfMacros.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/Services.h"
+#include "mozilla/Unused.h"
+#include "AudioChannelService.h"
+#include "mozilla/Logging.h"
+#include "nsPrintfCString.h"
+#include "nsXULAppAPI.h"
+#include "nsIFrameLoader.h"
+#include "nsIObserverService.h"
+#include "StaticPtr.h"
+#include "nsIMozBrowserFrame.h"
+#include "nsIObserver.h"
+#include "nsITimer.h"
+#include "nsIPropertyBag2.h"
+#include "nsComponentManagerUtils.h"
+#include "nsCRT.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;
+
+class ProcessLRUPool final
+{
+public:
+ /**
+ * Creates a new process LRU pool for the specified priority.
+ */
+ explicit ProcessLRUPool(ProcessPriority aPriority);
+
+ /**
+ * Used to remove a particular process priority manager from the LRU pool
+ * when the associated ContentParent is destroyed or its priority changes.
+ */
+ void Remove(ParticularProcessPriorityManager* aParticularManager);
+
+ /**
+ * Used to add a particular process priority manager into the LRU pool when
+ * the associated ContentParent's priority changes.
+ */
+ void Add(ParticularProcessPriorityManager* aParticularManager);
+
+private:
+ ProcessPriority mPriority;
+ uint32_t mLRUPoolLevels;
+ nsTArray<ParticularProcessPriorityManager*> mLRUPool;
+
+ uint32_t CalculateLRULevel(uint32_t aLRUPoolIndex);
+
+ void AdjustLRUValues(
+ nsTArray<ParticularProcessPriorityManager*>::index_type aStart,
+ bool removed);
+
+ DISALLOW_EVIL_CONSTRUCTORS(ProcessLRUPool);
+};
+
+/**
+ * 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 WakeLockObserver
+ , 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,
+ uint32_t aLRU = 0);
+
+ /**
+ * 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 = EmptyCString());
+
+ /**
+ * Does one of the child processes have priority FOREGROUND_HIGH?
+ */
+ bool ChildProcessHasHighPriority();
+
+ /**
+ * This must be called by a ParticularProcessPriorityManager when it changes
+ * its priority.
+ */
+ void NotifyProcessPriorityChanged(
+ ParticularProcessPriorityManager* aParticularManager,
+ hal::ProcessPriority aOldPriority);
+
+ /**
+ * Implements WakeLockObserver, used to monitor wake lock changes in the
+ * main process.
+ */
+ virtual void Notify(const WakeLockInformation& aInfo) override;
+
+ /**
+ * Prevents processes from changing priority until unfrozen.
+ */
+ void Freeze();
+
+ /**
+ * Allow process' priorities to change again. This will immediately adjust
+ * processes whose priority change did not happen because of the freeze.
+ */
+ void Unfreeze();
+
+ /**
+ * Call ShutDown before destroying the ProcessPriorityManager because
+ * WakeLockObserver hols a strong reference to it.
+ */
+ void ShutDown();
+
+private:
+ static bool sPrefsEnabled;
+ static bool sRemoteTabsDisabled;
+ static bool sTestMode;
+ static bool sPrefListenersRegistered;
+ static bool sInitialized;
+ static bool sFrozen;
+ static StaticRefPtr<ProcessPriorityManagerImpl> sSingleton;
+
+ static void PrefChangedCallback(const char* aPref, void* aClosure);
+
+ ProcessPriorityManagerImpl();
+ ~ProcessPriorityManagerImpl();
+ DISALLOW_EVIL_CONSTRUCTORS(ProcessPriorityManagerImpl);
+
+ void Init();
+
+ already_AddRefed<ParticularProcessPriorityManager>
+ GetParticularProcessPriorityManager(ContentParent* aContentParent);
+
+ void ObserveContentParentCreated(nsISupports* aContentParent);
+ void ObserveContentParentDestroyed(nsISupports* aSubject);
+ void ObserveScreenStateChanged(const char16_t* aData);
+
+ nsDataHashtable<nsUint64HashKey, RefPtr<ParticularProcessPriorityManager> >
+ mParticularManagers;
+
+ /** True if the main process is holding a high-priority wakelock */
+ bool mHighPriority;
+
+ /** Contains the PIDs of child processes holding high-priority wakelocks */
+ nsTHashtable<nsUint64HashKey> mHighPriorityChildIDs;
+
+ /** Contains a pseudo-LRU list of background processes */
+ ProcessLRUPool mBackgroundLRUPool;
+
+ /** Contains a pseudo-LRU list of background-perceivable processes */
+ ProcessLRUPool mBackgroundPerceivableLRUPool;
+};
+
+/**
+ * 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();
+ bool CurrentProcessIsHighPriority();
+
+private:
+ static StaticRefPtr<ProcessPriorityManagerChild> sSingleton;
+
+ ProcessPriorityManagerChild();
+ ~ProcessPriorityManagerChild() {}
+ DISALLOW_EVIL_CONSTRUCTORS(ProcessPriorityManagerChild);
+
+ 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 nsSupportsWeakReference
+{
+ ~ParticularProcessPriorityManager();
+public:
+ explicit ParticularProcessPriorityManager(ContentParent* aContentParent,
+ bool aFrozen = false);
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIOBSERVER
+ NS_DECL_NSITIMERCALLBACK
+
+ virtual void Notify(const WakeLockInformation& aInfo) override;
+ static void StaticInit();
+ void Init();
+
+ int32_t Pid() const;
+ uint64_t ChildID() const;
+ bool IsPreallocated() 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();
+
+ bool HasAppType(const char* aAppType);
+ bool IsExpectingSystemMessage();
+
+ void OnAudioChannelProcessChanged(nsISupports* aSubject);
+ void OnRemoteBrowserFrameShown(nsISupports* aSubject);
+ void OnTabParentDestroyed(nsISupports* aSubject);
+ void OnFrameloaderVisibleChanged(nsISupports* aSubject);
+ void OnActivityOpened(const char16_t* aData);
+ void OnActivityClosed(const char16_t* aData);
+
+ 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, uint32_t aLRU = 0);
+ void Freeze();
+ void Unfreeze();
+
+ void ShutDown();
+
+private:
+ static uint32_t sBackgroundPerceivableGracePeriodMS;
+ static uint32_t sBackgroundGracePeriodMS;
+
+ void FireTestOnlyObserverNotification(
+ const char* aTopic,
+ const nsACString& aData = EmptyCString());
+
+ void FireTestOnlyObserverNotification(
+ const char* aTopic,
+ const char* aData = nullptr);
+
+ ContentParent* mContentParent;
+ uint64_t mChildID;
+ ProcessPriority mPriority;
+ uint32_t mLRU;
+ bool mHoldsCPUWakeLock;
+ bool mHoldsHighPriorityWakeLock;
+ bool mIsActivityOpener;
+ bool mFrozen;
+
+ /**
+ * Used to implement NameWithComma().
+ */
+ nsAutoCString mNameWithComma;
+
+ nsCOMPtr<nsITimer> mResetPriorityTimer;
+};
+
+/* static */ bool ProcessPriorityManagerImpl::sInitialized = false;
+/* static */ bool ProcessPriorityManagerImpl::sPrefsEnabled = false;
+/* static */ bool ProcessPriorityManagerImpl::sRemoteTabsDisabled = true;
+/* static */ bool ProcessPriorityManagerImpl::sTestMode = false;
+/* static */ bool ProcessPriorityManagerImpl::sPrefListenersRegistered = false;
+/* static */ bool ProcessPriorityManagerImpl::sFrozen = false;
+/* static */ StaticRefPtr<ProcessPriorityManagerImpl>
+ ProcessPriorityManagerImpl::sSingleton;
+/* static */ uint32_t ParticularProcessPriorityManager::sBackgroundPerceivableGracePeriodMS = 0;
+/* static */ uint32_t ParticularProcessPriorityManager::sBackgroundGracePeriodMS = 0;
+
+NS_IMPL_ISUPPORTS(ProcessPriorityManagerImpl,
+ nsIObserver,
+ nsISupportsWeakReference);
+
+/* static */ void
+ProcessPriorityManagerImpl::PrefChangedCallback(const char* aPref,
+ void* aClosure)
+{
+ StaticInit();
+ if (!PrefsEnabled() && sSingleton) {
+ sSingleton->ShutDown();
+ sSingleton = nullptr;
+ sInitialized = false;
+ }
+}
+
+/* static */ bool
+ProcessPriorityManagerImpl::PrefsEnabled()
+{
+ return sPrefsEnabled && !sRemoteTabsDisabled;
+}
+
+/* static */ bool
+ProcessPriorityManagerImpl::TestMode()
+{
+ return sTestMode;
+}
+
+/* static */ void
+ProcessPriorityManagerImpl::StaticInit()
+{
+ if (sInitialized) {
+ return;
+ }
+
+ // The process priority manager is main-process only.
+ if (!XRE_IsParentProcess()) {
+ sInitialized = true;
+ return;
+ }
+
+ if (!sPrefListenersRegistered) {
+ Preferences::AddBoolVarCache(&sPrefsEnabled,
+ "dom.ipc.processPriorityManager.enabled");
+ Preferences::AddBoolVarCache(&sRemoteTabsDisabled,
+ "dom.ipc.tabs.disabled");
+ Preferences::AddBoolVarCache(&sTestMode,
+ "dom.ipc.processPriorityManager.testMode");
+ }
+
+ // 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()
+ : mHighPriority(false)
+ , mBackgroundLRUPool(PROCESS_PRIORITY_BACKGROUND)
+ , mBackgroundPerceivableLRUPool(PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE)
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+ RegisterWakeLockObserver(this);
+}
+
+ProcessPriorityManagerImpl::~ProcessPriorityManagerImpl()
+{
+ ShutDown();
+}
+
+void
+ProcessPriorityManagerImpl::ShutDown()
+{
+ UnregisterWakeLockObserver(this);
+}
+
+void
+ProcessPriorityManagerImpl::Init()
+{
+ LOG("Starting up. This is the master process.");
+
+ // The master 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_MASTER);
+
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ if (os) {
+ os->AddObserver(this, "ipc:content-created", /* ownsWeak */ true);
+ os->AddObserver(this, "ipc:content-shutdown", /* ownsWeak */ true);
+ os->AddObserver(this, "screen-state-changed", /* 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 if (topic.EqualsLiteral("screen-state-changed")) {
+ ObserveScreenStateChanged(aData);
+ } else {
+ MOZ_ASSERT(false);
+ }
+
+ return NS_OK;
+}
+
+already_AddRefed<ParticularProcessPriorityManager>
+ProcessPriorityManagerImpl::GetParticularProcessPriorityManager(
+ ContentParent* aContentParent)
+{
+ RefPtr<ParticularProcessPriorityManager> pppm;
+ uint64_t cpId = aContentParent->ChildID();
+ mParticularManagers.Get(cpId, &pppm);
+ if (!pppm) {
+ pppm = new ParticularProcessPriorityManager(aContentParent, sFrozen);
+ pppm->Init();
+ mParticularManagers.Put(cpId, pppm);
+
+ FireTestOnlyObserverNotification("process-created",
+ nsPrintfCString("%lld", cpId));
+ }
+
+ return pppm.forget();
+}
+
+void
+ProcessPriorityManagerImpl::SetProcessPriority(ContentParent* aContentParent,
+ ProcessPriority aPriority,
+ uint32_t aLRU)
+{
+ MOZ_ASSERT(aContentParent);
+ RefPtr<ParticularProcessPriorityManager> pppm =
+ GetParticularProcessPriorityManager(aContentParent);
+ if (pppm) {
+ pppm->SetPriorityNow(aPriority, aLRU);
+ }
+}
+
+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.
+ nsCOMPtr<nsIContentParent> cp = do_QueryInterface(aContentParent);
+ RefPtr<ParticularProcessPriorityManager> pppm =
+ GetParticularProcessPriorityManager(cp->AsContentParent());
+}
+
+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(NS_LITERAL_STRING("childID"), &childID);
+ NS_ENSURE_TRUE_VOID(childID != CONTENT_PROCESS_ID_UNKNOWN);
+
+ RefPtr<ParticularProcessPriorityManager> pppm;
+ mParticularManagers.Get(childID, &pppm);
+ if (pppm) {
+ // Unconditionally remove the manager from the pools
+ mBackgroundLRUPool.Remove(pppm);
+ mBackgroundPerceivableLRUPool.Remove(pppm);
+
+ pppm->ShutDown();
+
+ mParticularManagers.Remove(childID);
+
+ mHighPriorityChildIDs.RemoveEntry(childID);
+ }
+}
+
+void
+ProcessPriorityManagerImpl::ObserveScreenStateChanged(const char16_t* aData)
+{
+ if (NS_LITERAL_STRING("on").Equals(aData)) {
+ sFrozen = false;
+ for (auto iter = mParticularManagers.Iter(); !iter.Done(); iter.Next()) {
+ iter.UserData()->Unfreeze();
+ }
+ } else {
+ sFrozen = true;
+ for (auto iter = mParticularManagers.Iter(); !iter.Done(); iter.Next()) {
+ iter.UserData()->Freeze();
+ }
+ }
+}
+
+bool
+ProcessPriorityManagerImpl::ChildProcessHasHighPriority( void )
+{
+ return mHighPriorityChildIDs.Count() > 0;
+}
+
+void
+ProcessPriorityManagerImpl::NotifyProcessPriorityChanged(
+ ParticularProcessPriorityManager* aParticularManager,
+ ProcessPriority aOldPriority)
+{
+ ProcessPriority newPriority = aParticularManager->CurrentPriority();
+ bool isPreallocated = aParticularManager->IsPreallocated();
+
+ if (newPriority == PROCESS_PRIORITY_BACKGROUND &&
+ aOldPriority != PROCESS_PRIORITY_BACKGROUND &&
+ !isPreallocated) {
+ mBackgroundLRUPool.Add(aParticularManager);
+ } else if (newPriority != PROCESS_PRIORITY_BACKGROUND &&
+ aOldPriority == PROCESS_PRIORITY_BACKGROUND &&
+ !isPreallocated) {
+ mBackgroundLRUPool.Remove(aParticularManager);
+ }
+
+ if (newPriority == PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE &&
+ aOldPriority != PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE) {
+ mBackgroundPerceivableLRUPool.Add(aParticularManager);
+ } else if (newPriority != PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE &&
+ aOldPriority == PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE) {
+ mBackgroundPerceivableLRUPool.Remove(aParticularManager);
+ }
+
+ 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());
+ }
+}
+
+/* virtual */ void
+ProcessPriorityManagerImpl::Notify(const WakeLockInformation& aInfo)
+{
+ /* The main process always has an ID of 0, if it is present in the wake-lock
+ * information then we explicitly requested a high-priority wake-lock for the
+ * main process. */
+ if (aInfo.topic().EqualsLiteral("high-priority")) {
+ if (aInfo.lockingProcesses().Contains((uint64_t)0)) {
+ mHighPriority = true;
+ } else {
+ mHighPriority = false;
+ }
+
+ LOG("Got wake lock changed event. "
+ "Now mHighPriorityParent = %d\n", mHighPriority);
+ }
+}
+
+NS_IMPL_ISUPPORTS(ParticularProcessPriorityManager,
+ nsIObserver,
+ nsITimerCallback,
+ nsISupportsWeakReference);
+
+ParticularProcessPriorityManager::ParticularProcessPriorityManager(
+ ContentParent* aContentParent, bool aFrozen)
+ : mContentParent(aContentParent)
+ , mChildID(aContentParent->ChildID())
+ , mPriority(PROCESS_PRIORITY_UNKNOWN)
+ , mLRU(0)
+ , mHoldsCPUWakeLock(false)
+ , mHoldsHighPriorityWakeLock(false)
+ , mIsActivityOpener(false)
+ , mFrozen(aFrozen)
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+ LOGP("Creating ParticularProcessPriorityManager.");
+}
+
+void
+ParticularProcessPriorityManager::StaticInit()
+{
+ Preferences::AddUintVarCache(&sBackgroundPerceivableGracePeriodMS,
+ "dom.ipc.processPriorityManager.backgroundPerceivableGracePeriodMS");
+ Preferences::AddUintVarCache(&sBackgroundGracePeriodMS,
+ "dom.ipc.processPriorityManager.backgroundGracePeriodMS");
+}
+
+void
+ParticularProcessPriorityManager::Init()
+{
+ RegisterWakeLockObserver(this);
+
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ if (os) {
+ os->AddObserver(this, "audio-channel-process-changed", /* ownsWeak */ true);
+ os->AddObserver(this, "remote-browser-shown", /* ownsWeak */ true);
+ os->AddObserver(this, "ipc:browser-destroyed", /* ownsWeak */ true);
+ os->AddObserver(this, "frameloader-visible-changed", /* ownsWeak */ true);
+ os->AddObserver(this, "activity-opened", /* ownsWeak */ true);
+ os->AddObserver(this, "activity-closed", /* ownsWeak */ true);
+ }
+
+ // This process may already hold the CPU lock; for example, our parent may
+ // have acquired it on our behalf.
+ WakeLockInformation info1, info2;
+ GetWakeLockInfo(NS_LITERAL_STRING("cpu"), &info1);
+ mHoldsCPUWakeLock = info1.lockingProcesses().Contains(ChildID());
+
+ GetWakeLockInfo(NS_LITERAL_STRING("high-priority"), &info2);
+ mHoldsHighPriorityWakeLock = info2.lockingProcesses().Contains(ChildID());
+ LOGP("Done starting up. mHoldsCPUWakeLock=%d, mHoldsHighPriorityWakeLock=%d",
+ mHoldsCPUWakeLock, mHoldsHighPriorityWakeLock);
+}
+
+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;
+ }
+
+ if (dest) {
+ bool thisProcessLocks = aInfo.lockingProcesses().Contains(ChildID());
+ if (thisProcessLocks != *dest) {
+ *dest = thisProcessLocks;
+ LOGP("Got wake lock changed event. "
+ "Now mHoldsCPUWakeLock=%d, mHoldsHighPriorityWakeLock=%d",
+ mHoldsCPUWakeLock, mHoldsHighPriorityWakeLock);
+ 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("audio-channel-process-changed")) {
+ OnAudioChannelProcessChanged(aSubject);
+ } else if (topic.EqualsLiteral("remote-browser-shown")) {
+ OnRemoteBrowserFrameShown(aSubject);
+ } else if (topic.EqualsLiteral("ipc:browser-destroyed")) {
+ OnTabParentDestroyed(aSubject);
+ } else if (topic.EqualsLiteral("frameloader-visible-changed")) {
+ OnFrameloaderVisibleChanged(aSubject);
+ } else if (topic.EqualsLiteral("activity-opened")) {
+ OnActivityOpened(aData);
+ } else if (topic.EqualsLiteral("activity-closed")) {
+ OnActivityClosed(aData);
+ } 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;
+}
+
+bool
+ParticularProcessPriorityManager::IsPreallocated() const
+{
+ return mContentParent ? mContentParent->IsPreallocated() : false;
+}
+
+const nsAutoCString&
+ParticularProcessPriorityManager::NameWithComma()
+{
+ mNameWithComma.Truncate();
+ if (!mContentParent) {
+ return mNameWithComma; // empty string
+ }
+
+ nsAutoString name;
+ mContentParent->FriendlyName(name);
+ if (name.IsEmpty()) {
+ return mNameWithComma; // empty string
+ }
+
+ mNameWithComma = NS_ConvertUTF16toUTF8(name);
+ mNameWithComma.AppendLiteral(", ");
+ return mNameWithComma;
+}
+
+void
+ParticularProcessPriorityManager::OnAudioChannelProcessChanged(nsISupports* aSubject)
+{
+ nsCOMPtr<nsIPropertyBag2> props = do_QueryInterface(aSubject);
+ NS_ENSURE_TRUE_VOID(props);
+
+ uint64_t childID = CONTENT_PROCESS_ID_UNKNOWN;
+ props->GetPropertyAsUint64(NS_LITERAL_STRING("childID"), &childID);
+ if (childID == ChildID()) {
+ ResetPriority();
+ }
+}
+
+void
+ParticularProcessPriorityManager::OnRemoteBrowserFrameShown(nsISupports* aSubject)
+{
+ nsCOMPtr<nsIFrameLoader> fl = do_QueryInterface(aSubject);
+ NS_ENSURE_TRUE_VOID(fl);
+
+ TabParent* tp = TabParent::GetFrom(fl);
+ NS_ENSURE_TRUE_VOID(tp);
+
+ MOZ_ASSERT(XRE_IsParentProcess());
+ if (tp->Manager() != mContentParent) {
+ return;
+ }
+
+ // Ignore notifications that aren't from a BrowserOrApp
+ bool isMozBrowserOrApp;
+ fl->GetOwnerIsMozBrowserOrAppFrame(&isMozBrowserOrApp);
+ if (isMozBrowserOrApp) {
+ ResetPriority();
+ }
+
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ if (os) {
+ os->RemoveObserver(this, "remote-browser-shown");
+ }
+}
+
+void
+ParticularProcessPriorityManager::OnTabParentDestroyed(nsISupports* aSubject)
+{
+ nsCOMPtr<nsITabParent> tp = do_QueryInterface(aSubject);
+ NS_ENSURE_TRUE_VOID(tp);
+
+ MOZ_ASSERT(XRE_IsParentProcess());
+ if (TabParent::GetFrom(tp)->Manager() != mContentParent) {
+ return;
+ }
+
+ ResetPriority();
+}
+
+void
+ParticularProcessPriorityManager::OnFrameloaderVisibleChanged(nsISupports* aSubject)
+{
+ nsCOMPtr<nsIFrameLoader> fl = do_QueryInterface(aSubject);
+ NS_ENSURE_TRUE_VOID(fl);
+
+ if (mFrozen) {
+ return; // Ignore visibility changes when the screen is off
+ }
+
+ TabParent* tp = TabParent::GetFrom(fl);
+ if (!tp) {
+ return;
+ }
+
+ MOZ_ASSERT(XRE_IsParentProcess());
+ if (tp->Manager() != mContentParent) {
+ return;
+ }
+
+ // Most of the time when something changes in a process we call
+ // ResetPriority(), giving a grace period before downgrading its priority.
+ // But notice that here don't give a grace period: We call ResetPriorityNow()
+ // instead.
+ //
+ // We do this because we're reacting here to a setVisibility() call, which is
+ // an explicit signal from the process embedder that we should re-prioritize
+ // a process. If we gave a grace period in response to setVisibility()
+ // calls, it would be impossible for the embedder to explicitly prioritize
+ // processes and prevent e.g. the case where we switch which process is in
+ // the foreground and, during the old fg processs's grace period, it OOMs the
+ // new fg process.
+
+ ResetPriorityNow();
+}
+
+void
+ParticularProcessPriorityManager::OnActivityOpened(const char16_t* aData)
+{
+ uint64_t childID = nsCRT::atoll(NS_ConvertUTF16toUTF8(aData).get());
+
+ if (ChildID() == childID) {
+ LOGP("Marking as activity opener");
+ mIsActivityOpener = true;
+ ResetPriority();
+ }
+}
+
+void
+ParticularProcessPriorityManager::OnActivityClosed(const char16_t* aData)
+{
+ uint64_t childID = nsCRT::atoll(NS_ConvertUTF16toUTF8(aData).get());
+
+ if (ChildID() == childID) {
+ LOGP("Unmarking as activity opener");
+ mIsActivityOpener = false;
+ 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 = sBackgroundPerceivableGracePeriodMS;
+ break;
+ case BACKGROUND_GRACE_PERIOD:
+ timeout = sBackgroundGracePeriodMS;
+ break;
+ default:
+ MOZ_ASSERT(false, "Unrecognized timeout pref");
+ break;
+ }
+
+ LOGP("Scheduling reset timer to fire in %dms.", timeout);
+ mResetPriorityTimer = do_CreateInstance(NS_TIMER_CONTRACTID);
+ mResetPriorityTimer->InitWithCallback(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;
+}
+
+bool
+ParticularProcessPriorityManager::HasAppType(const char* aAppType)
+{
+ const ManagedContainer<PBrowserParent>& browsers =
+ mContentParent->ManagedPBrowserParent();
+ for (auto iter = browsers.ConstIter(); !iter.Done(); iter.Next()) {
+ nsAutoString appType;
+ TabParent::GetFrom(iter.Get()->GetKey())->GetAppType(appType);
+ if (appType.EqualsASCII(aAppType)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool
+ParticularProcessPriorityManager::IsExpectingSystemMessage()
+{
+ const ManagedContainer<PBrowserParent>& browsers =
+ mContentParent->ManagedPBrowserParent();
+ for (auto iter = browsers.ConstIter(); !iter.Done(); iter.Next()) {
+ TabParent* tp = TabParent::GetFrom(iter.Get()->GetKey());
+ nsCOMPtr<nsIMozBrowserFrame> bf = do_QueryInterface(tp->GetOwnerElement());
+ if (!bf) {
+ continue;
+ }
+ }
+
+ return false;
+}
+
+ProcessPriority
+ParticularProcessPriorityManager::CurrentPriority()
+{
+ return mPriority;
+}
+
+ProcessPriority
+ParticularProcessPriorityManager::ComputePriority()
+{
+ if ((mHoldsCPUWakeLock || mHoldsHighPriorityWakeLock) &&
+ HasAppType("critical")) {
+ return PROCESS_PRIORITY_FOREGROUND_HIGH;
+ }
+
+ bool isVisible = false;
+ const ManagedContainer<PBrowserParent>& browsers =
+ mContentParent->ManagedPBrowserParent();
+ for (auto iter = browsers.ConstIter(); !iter.Done(); iter.Next()) {
+ if (TabParent::GetFrom(iter.Get()->GetKey())->IsVisible()) {
+ isVisible = true;
+ break;
+ }
+ }
+
+ if (isVisible) {
+ return HasAppType("inputmethod") ?
+ PROCESS_PRIORITY_FOREGROUND_KEYBOARD :
+ PROCESS_PRIORITY_FOREGROUND;
+ }
+
+ if ((mHoldsCPUWakeLock || mHoldsHighPriorityWakeLock) &&
+ IsExpectingSystemMessage()) {
+ return PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE;
+ }
+
+ RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
+ if (service && service->ProcessContentOrNormalChannelIsActive(ChildID())) {
+ return PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE;
+ }
+
+ return mIsActivityOpener ? PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE
+ : PROCESS_PRIORITY_BACKGROUND;
+}
+
+void
+ParticularProcessPriorityManager::SetPriorityNow(ProcessPriority aPriority,
+ uint32_t aLRU)
+{
+ if (aPriority == PROCESS_PRIORITY_UNKNOWN) {
+ MOZ_ASSERT(false);
+ return;
+ }
+
+ if (!ProcessPriorityManagerImpl::PrefsEnabled() ||
+ !mContentParent ||
+ mFrozen ||
+ ((mPriority == aPriority) && (mLRU == aLRU))) {
+ return;
+ }
+
+ if ((mPriority == aPriority) && (mLRU != aLRU)) {
+ mLRU = aLRU;
+ hal::SetProcessPriority(Pid(), mPriority, aLRU);
+
+ nsPrintfCString processPriorityWithLRU("%s:%d",
+ ProcessPriorityToString(mPriority), aLRU);
+
+ FireTestOnlyObserverNotification("process-priority-with-LRU-set",
+ processPriorityWithLRU.get());
+ return;
+ }
+
+ LOGP("Changing priority from %s to %s.",
+ ProcessPriorityToString(mPriority),
+ ProcessPriorityToString(aPriority));
+
+ ProcessPriority oldPriority = mPriority;
+
+ mPriority = aPriority;
+ hal::SetProcessPriority(Pid(), mPriority);
+
+ if (oldPriority != mPriority) {
+ ProcessPriorityManagerImpl::GetSingleton()->
+ NotifyProcessPriorityChanged(this, oldPriority);
+
+ Unused << mContentParent->SendNotifyProcessPriorityChanged(mPriority);
+ }
+
+ FireTestOnlyObserverNotification("process-priority-set",
+ ProcessPriorityToString(mPriority));
+}
+
+void
+ParticularProcessPriorityManager::Freeze()
+{
+ mFrozen = true;
+}
+
+void
+ParticularProcessPriorityManager::Unfreeze()
+{
+ mFrozen = false;
+}
+
+void
+ParticularProcessPriorityManager::ShutDown()
+{
+ MOZ_ASSERT(mContentParent);
+
+ UnregisterWakeLockObserver(this);
+
+ if (mResetPriorityTimer) {
+ mResetPriorityTimer->Cancel();
+ mResetPriorityTimer = nullptr;
+ }
+
+ mContentParent = nullptr;
+}
+
+void
+ProcessPriorityManagerImpl::FireTestOnlyObserverNotification(
+ const char* aTopic,
+ const nsACString& aData /* = EmptyCString() */)
+{
+ 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 /* = EmptyCString() */)
+{
+ if (!ProcessPriorityManagerImpl::TestMode()) {
+ return;
+ }
+
+ nsAutoCString data(nsPrintfCString("%lld", 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_MASTER;
+ } 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(NS_LITERAL_STRING("priority"), &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;
+}
+
+bool
+ProcessPriorityManagerChild::CurrentProcessIsHighPriority()
+{
+ return mCachedPriority == PROCESS_PRIORITY_UNKNOWN ||
+ mCachedPriority >= PROCESS_PRIORITY_FOREGROUND_HIGH;
+}
+
+ProcessLRUPool::ProcessLRUPool(ProcessPriority aPriority)
+ : mPriority(aPriority)
+ , mLRUPoolLevels(1)
+{
+ // We set mLRUPoolLevels according to our pref.
+ // This value is used to set background process LRU pool
+ const char* str = ProcessPriorityToString(aPriority);
+ nsPrintfCString pref("dom.ipc.processPriorityManager.%s.LRUPoolLevels", str);
+
+ Preferences::GetUint(pref.get(), &mLRUPoolLevels);
+
+ // GonkHal defines OOM_ADJUST_MAX is 15 and b2g.js defines
+ // PROCESS_PRIORITY_BACKGROUND's oom_score_adj is 667 and oom_adj is 10.
+ // This means we can only have at most (15 -10 + 1) = 6 background LRU levels.
+ // Similarly we can have at most 4 background perceivable LRU levels. We
+ // should really be getting rid of oom_adj and just rely on oom_score_adj
+ // only which would lift this constraint.
+ MOZ_ASSERT(aPriority != PROCESS_PRIORITY_BACKGROUND || mLRUPoolLevels <= 6);
+ MOZ_ASSERT(aPriority != PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE ||
+ mLRUPoolLevels <= 4);
+
+ // LRU pool size = 2 ^ (number of background LRU pool levels) - 1
+ uint32_t LRUPoolSize = (1 << mLRUPoolLevels) - 1;
+
+ LOG("Making %s LRU pool with size(%d)", str, LRUPoolSize);
+}
+
+uint32_t
+ProcessLRUPool::CalculateLRULevel(uint32_t aLRU)
+{
+ // This is used to compute the LRU adjustment for the specified LRU position.
+ // We use power-of-two groups with increasing adjustments that look like the
+ // following:
+
+ // Priority : LRU0, LRU1
+ // Priority+1: LRU2, LRU3
+ // Priority+2: LRU4, LRU5, LRU6, LRU7
+ // Priority+3: LRU8, LRU9, LRU10, LRU11, LRU12, LRU12, LRU13, LRU14, LRU15
+ // ...
+ // Priority+L-1: 2^(number of LRU pool levels - 1)
+ // (End of buffer)
+
+ int exp;
+ Unused << frexp(static_cast<double>(aLRU), &exp);
+ uint32_t level = std::max(exp - 1, 0);
+
+ return std::min(mLRUPoolLevels - 1, level);
+}
+
+void
+ProcessLRUPool::Remove(ParticularProcessPriorityManager* aParticularManager)
+{
+ nsTArray<ParticularProcessPriorityManager*>::index_type index =
+ mLRUPool.IndexOf(aParticularManager);
+
+ if (index == nsTArray<ParticularProcessPriorityManager*>::NoIndex) {
+ return;
+ }
+
+ mLRUPool.RemoveElementAt(index);
+ AdjustLRUValues(index, /* removed */ true);
+
+ LOG("Remove ChildID(%" PRIu64 ") from %s LRU pool",
+ static_cast<uint64_t>(aParticularManager->ChildID()),
+ ProcessPriorityToString(mPriority));
+}
+
+/*
+ * Adjust the LRU values of all the processes in an LRU pool. When true the
+ * `removed` parameter indicates that the processes were shifted left because
+ * an element was removed; otherwise it means the elements were shifted right
+ * as an element was added.
+ */
+void
+ProcessLRUPool::AdjustLRUValues(
+ nsTArray<ParticularProcessPriorityManager*>::index_type aStart,
+ bool removed)
+{
+ uint32_t adj = (removed ? 2 : 1);
+
+ for (nsTArray<ParticularProcessPriorityManager*>::index_type i = aStart;
+ i < mLRUPool.Length();
+ i++) {
+ /* Check whether i is a power of two. If so, then it crossed a LRU group
+ * boundary and we need to assign its new process priority LRU. Note that
+ * depending on the direction and the bias this test will pick different
+ * elements. */
+ if (((i + adj) & (i + adj - 1)) == 0) {
+ mLRUPool[i]->SetPriorityNow(mPriority, CalculateLRULevel(i + 1));
+ }
+ }
+}
+
+void
+ProcessLRUPool::Add(ParticularProcessPriorityManager* aParticularManager)
+{
+ // Shift the list in the pool, so we have room at index 0 for the newly added
+ // manager
+ mLRUPool.InsertElementAt(0, aParticularManager);
+ AdjustLRUValues(1, /* removed */ false);
+
+ LOG("Add ChildID(%" PRIu64 ") into %s LRU pool",
+ static_cast<uint64_t>(aParticularManager->ChildID()),
+ ProcessPriorityToString(mPriority));
+}
+
+} // namespace
+
+namespace mozilla {
+
+/* static */ void
+ProcessPriorityManager::Init()
+{
+ ProcessPriorityManagerImpl::StaticInit();
+ ProcessPriorityManagerChild::StaticInit();
+ ParticularProcessPriorityManager::StaticInit();
+}
+
+/* static */ void
+ProcessPriorityManager::SetProcessPriority(ContentParent* aContentParent,
+ ProcessPriority aPriority)
+{
+ MOZ_ASSERT(aContentParent);
+
+ ProcessPriorityManagerImpl* singleton =
+ ProcessPriorityManagerImpl::GetSingleton();
+ if (singleton) {
+ singleton->SetProcessPriority(aContentParent, aPriority);
+ }
+}
+
+/* static */ bool
+ProcessPriorityManager::CurrentProcessIsForeground()
+{
+ return ProcessPriorityManagerChild::Singleton()->
+ CurrentProcessIsForeground();
+}
+
+/* static */ bool
+ProcessPriorityManager::AnyProcessHasHighPriority()
+{
+ ProcessPriorityManagerImpl* singleton =
+ ProcessPriorityManagerImpl::GetSingleton();
+
+ if (singleton) {
+ return singleton->ChildProcessHasHighPriority();
+ } else {
+ return ProcessPriorityManagerChild::Singleton()->
+ CurrentProcessIsHighPriority();
+ }
+}
+
+} // namespace mozilla
diff --git a/dom/ipc/ProcessPriorityManager.h b/dom/ipc/ProcessPriorityManager.h
new file mode 100644
index 000000000..5e02c9c32
--- /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;
+} // 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();
+
+ /**
+ * Returns true if one or more processes with FOREGROUND_HIGH priority are
+ * present, false otherwise.
+ */
+ static bool AnyProcessHasHighPriority();
+
+private:
+ ProcessPriorityManager();
+ DISALLOW_EVIL_CONSTRUCTORS(ProcessPriorityManager);
+};
+
+} // namespace mozilla
+
+#endif
diff --git a/dom/ipc/ScreenManagerParent.cpp b/dom/ipc/ScreenManagerParent.cpp
new file mode 100644
index 000000000..ddc2eb4a2
--- /dev/null
+++ b/dom/ipc/ScreenManagerParent.cpp
@@ -0,0 +1,229 @@
+/* -*- 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/TabParent.h"
+#include "mozilla/Unused.h"
+#include "nsIWidget.h"
+#include "nsServiceManagerUtils.h"
+#include "ScreenManagerParent.h"
+#include "ContentProcessManager.h"
+
+namespace mozilla {
+namespace dom {
+
+static const char *sScreenManagerContractID = "@mozilla.org/gfx/screenmanager;1";
+
+ScreenManagerParent::ScreenManagerParent(uint32_t* aNumberOfScreens,
+ float* aSystemDefaultScale,
+ bool* aSuccess)
+{
+ mScreenMgr = do_GetService(sScreenManagerContractID);
+ if (!mScreenMgr) {
+ MOZ_CRASH("Couldn't get nsIScreenManager from ScreenManagerParent.");
+ }
+
+ Unused << RecvRefresh(aNumberOfScreens, aSystemDefaultScale, aSuccess);
+}
+
+bool
+ScreenManagerParent::RecvRefresh(uint32_t* aNumberOfScreens,
+ float* aSystemDefaultScale,
+ bool* aSuccess)
+{
+ *aSuccess = false;
+
+ nsresult rv = mScreenMgr->GetNumberOfScreens(aNumberOfScreens);
+ if (NS_FAILED(rv)) {
+ return true;
+ }
+
+ rv = mScreenMgr->GetSystemDefaultScale(aSystemDefaultScale);
+ if (NS_FAILED(rv)) {
+ return true;
+ }
+
+ *aSuccess = true;
+ return true;
+}
+
+bool
+ScreenManagerParent::RecvScreenRefresh(const uint32_t& aId,
+ ScreenDetails* aRetVal,
+ bool* aSuccess)
+{
+ *aSuccess = false;
+
+ nsCOMPtr<nsIScreen> screen;
+ nsresult rv = mScreenMgr->ScreenForId(aId, getter_AddRefs(screen));
+ if (NS_FAILED(rv)) {
+ return true;
+ }
+
+ ScreenDetails details;
+ Unused << ExtractScreenDetails(screen, details);
+
+ *aRetVal = details;
+ *aSuccess = true;
+ return true;
+}
+
+bool
+ScreenManagerParent::RecvGetPrimaryScreen(ScreenDetails* aRetVal,
+ bool* aSuccess)
+{
+ *aSuccess = false;
+
+ nsCOMPtr<nsIScreen> screen;
+ nsresult rv = mScreenMgr->GetPrimaryScreen(getter_AddRefs(screen));
+
+ NS_ENSURE_SUCCESS(rv, true);
+
+ ScreenDetails details;
+ if (!ExtractScreenDetails(screen, details)) {
+ return true;
+ }
+
+ *aRetVal = details;
+ *aSuccess = true;
+ return true;
+}
+
+bool
+ScreenManagerParent::RecvScreenForRect(const int32_t& aLeft,
+ const int32_t& aTop,
+ const int32_t& aWidth,
+ const int32_t& aHeight,
+ ScreenDetails* aRetVal,
+ bool* aSuccess)
+{
+ *aSuccess = false;
+
+ nsCOMPtr<nsIScreen> screen;
+ nsresult rv = mScreenMgr->ScreenForRect(aLeft, aTop, aWidth, aHeight, getter_AddRefs(screen));
+
+ NS_ENSURE_SUCCESS(rv, true);
+
+ ScreenDetails details;
+ if (!ExtractScreenDetails(screen, details)) {
+ return true;
+ }
+
+ *aRetVal = details;
+ *aSuccess = true;
+ return true;
+}
+
+bool
+ScreenManagerParent::RecvScreenForBrowser(const TabId& aTabId,
+ ScreenDetails* aRetVal,
+ bool* aSuccess)
+{
+ *aSuccess = false;
+#ifdef MOZ_VALGRIND
+ // Zero this so that Valgrind doesn't complain when we send it to another
+ // process.
+ memset(aRetVal, 0, sizeof(ScreenDetails));
+#endif
+
+ // Find the mWidget associated with the tabparent, and then return
+ // the nsIScreen it's on.
+ ContentParent* cp = static_cast<ContentParent*>(this->Manager());
+ ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
+ RefPtr<TabParent> tabParent =
+ cpm->GetTopLevelTabParentByProcessAndTabId(cp->ChildID(), aTabId);
+ if(!tabParent){
+ return false;
+ }
+
+ nsCOMPtr<nsIWidget> widget = tabParent->GetWidget();
+
+ nsCOMPtr<nsIScreen> screen;
+ if (widget && widget->GetNativeData(NS_NATIVE_WINDOW)) {
+ mScreenMgr->ScreenForNativeWidget(widget->GetNativeData(NS_NATIVE_WINDOW),
+ getter_AddRefs(screen));
+ } else {
+ nsresult rv = mScreenMgr->GetPrimaryScreen(getter_AddRefs(screen));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return true;
+ }
+ }
+
+ NS_ENSURE_TRUE(screen, true);
+
+ ScreenDetails details;
+ if (!ExtractScreenDetails(screen, details)) {
+ return true;
+ }
+
+ *aRetVal = details;
+ *aSuccess = true;
+ return true;
+}
+
+bool
+ScreenManagerParent::ExtractScreenDetails(nsIScreen* aScreen, ScreenDetails &aDetails)
+{
+ if (!aScreen) {
+ return false;
+ }
+
+ uint32_t id;
+ nsresult rv = aScreen->GetId(&id);
+ NS_ENSURE_SUCCESS(rv, false);
+ aDetails.id() = id;
+
+ nsIntRect rect;
+ rv = aScreen->GetRect(&rect.x, &rect.y, &rect.width, &rect.height);
+ NS_ENSURE_SUCCESS(rv, false);
+ aDetails.rect() = rect;
+
+ nsIntRect rectDisplayPix;
+ rv = aScreen->GetRectDisplayPix(&rectDisplayPix.x, &rectDisplayPix.y,
+ &rectDisplayPix.width, &rectDisplayPix.height);
+ NS_ENSURE_SUCCESS(rv, false);
+ aDetails.rectDisplayPix() = rectDisplayPix;
+
+ nsIntRect availRect;
+ rv = aScreen->GetAvailRect(&availRect.x, &availRect.y, &availRect.width,
+ &availRect.height);
+ NS_ENSURE_SUCCESS(rv, false);
+ aDetails.availRect() = availRect;
+
+ nsIntRect availRectDisplayPix;
+ rv = aScreen->GetAvailRectDisplayPix(&availRectDisplayPix.x,
+ &availRectDisplayPix.y,
+ &availRectDisplayPix.width,
+ &availRectDisplayPix.height);
+ NS_ENSURE_SUCCESS(rv, false);
+ aDetails.availRectDisplayPix() = availRectDisplayPix;
+
+ int32_t pixelDepth = 0;
+ rv = aScreen->GetPixelDepth(&pixelDepth);
+ NS_ENSURE_SUCCESS(rv, false);
+ aDetails.pixelDepth() = pixelDepth;
+
+ int32_t colorDepth = 0;
+ rv = aScreen->GetColorDepth(&colorDepth);
+ NS_ENSURE_SUCCESS(rv, false);
+ aDetails.colorDepth() = colorDepth;
+
+ double contentsScaleFactor = 1.0;
+ rv = aScreen->GetContentsScaleFactor(&contentsScaleFactor);
+ NS_ENSURE_SUCCESS(rv, false);
+ aDetails.contentsScaleFactor() = contentsScaleFactor;
+
+ return true;
+}
+
+void
+ScreenManagerParent::ActorDestroy(ActorDestroyReason why)
+{
+}
+
+} // namespace dom
+} // namespace mozilla
+
diff --git a/dom/ipc/ScreenManagerParent.h b/dom/ipc/ScreenManagerParent.h
new file mode 100644
index 000000000..cd07c12b7
--- /dev/null
+++ b/dom/ipc/ScreenManagerParent.h
@@ -0,0 +1,56 @@
+/* -*- 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_ScreenManagerParent_h
+#define mozilla_dom_ScreenManagerParent_h
+
+#include "mozilla/dom/PScreenManagerParent.h"
+#include "nsIScreenManager.h"
+
+namespace mozilla {
+namespace dom {
+
+class ScreenManagerParent : public PScreenManagerParent
+{
+ public:
+ ScreenManagerParent(uint32_t* aNumberOfScreens,
+ float* aSystemDefaultScale,
+ bool* aSuccess);
+ ~ScreenManagerParent() {};
+
+ virtual bool RecvRefresh(uint32_t* aNumberOfScreens,
+ float* aSystemDefaultScale,
+ bool* aSuccess) override;
+
+ virtual bool RecvScreenRefresh(const uint32_t& aId,
+ ScreenDetails* aRetVal,
+ bool* aSuccess) override;
+
+ virtual void ActorDestroy(ActorDestroyReason aWhy) override;
+
+ virtual bool RecvGetPrimaryScreen(ScreenDetails* aRetVal,
+ bool* aSuccess) override;
+
+ virtual bool RecvScreenForRect(const int32_t& aLeft,
+ const int32_t& aTop,
+ const int32_t& aWidth,
+ const int32_t& aHeight,
+ ScreenDetails* aRetVal,
+ bool* aSuccess) override;
+
+ virtual bool RecvScreenForBrowser(const TabId& aTabId,
+ ScreenDetails* aRetVal,
+ bool* aSuccess) override;
+
+ private:
+ bool ExtractScreenDetails(nsIScreen* aScreen, ScreenDetails &aDetails);
+ nsCOMPtr<nsIScreenManager> mScreenMgr;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_ScreenManagerParent_h
diff --git a/dom/ipc/ServiceWorkerConfiguration.ipdlh b/dom/ipc/ServiceWorkerConfiguration.ipdlh
new file mode 100644
index 000000000..16e0d46ea
--- /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/StructuredCloneData.cpp b/dom/ipc/StructuredCloneData.cpp
new file mode 100644
index 000000000..98f56904f
--- /dev/null
+++ b/dom/ipc/StructuredCloneData.cpp
@@ -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/. */
+
+#include "StructuredCloneData.h"
+
+#include "nsIDOMDOMException.h"
+#include "nsIMutable.h"
+#include "nsIXPConnect.h"
+
+#include "ipc/IPCMessageUtils.h"
+#include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/BlobBinding.h"
+#include "mozilla/dom/File.h"
+#include "mozilla/dom/ToJSValue.h"
+#include "nsContentUtils.h"
+#include "nsJSEnvironment.h"
+#include "MainThreadUtils.h"
+#include "StructuredCloneTags.h"
+#include "jsapi.h"
+
+namespace mozilla {
+namespace dom {
+namespace ipc {
+
+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);
+ }
+
+ PortIdentifiers().AppendElements(aData.PortIdentifiers());
+
+ MOZ_ASSERT(BlobImpls().IsEmpty());
+ BlobImpls().AppendElements(aData.BlobImpls());
+
+ MOZ_ASSERT(GetSurfaces().IsEmpty());
+ MOZ_ASSERT(WasmModules().IsEmpty());
+
+ mInitialized = true;
+
+ return true;
+}
+
+void
+StructuredCloneData::Read(JSContext* aCx,
+ JS::MutableHandle<JS::Value> aValue,
+ ErrorResult &aRv)
+{
+ MOZ_ASSERT(mInitialized);
+
+ nsIGlobalObject *global = xpc::NativeGlobal(JS::CurrentGlobalOrNull(aCx));
+ MOZ_ASSERT(global);
+
+ ReadFromBuffer(global, aCx, Data(), aValue, aRv);
+}
+
+void
+StructuredCloneData::Write(JSContext* aCx,
+ JS::Handle<JS::Value> aValue,
+ ErrorResult &aRv)
+{
+ Write(aCx, aValue, JS::UndefinedHandleValue, aRv);
+}
+
+void
+StructuredCloneData::Write(JSContext* aCx,
+ JS::Handle<JS::Value> aValue,
+ JS::Handle<JS::Value> aTransfer,
+ ErrorResult &aRv)
+{
+ MOZ_ASSERT(!mInitialized);
+
+ StructuredCloneHolder::Write(aCx, aValue, aTransfer,
+ JS::CloneDataPolicy().denySharedArrayBuffer(), aRv);
+ if (NS_WARN_IF(aRv.Failed())) {
+ return;
+ }
+
+ JSStructuredCloneData data;
+ mBuffer->abandon();
+ mBuffer->steal(&data);
+ mBuffer = nullptr;
+ mSharedData = new SharedJSAllocatedData(Move(data));
+ mInitialized = true;
+}
+
+void
+StructuredCloneData::WriteIPCParams(IPC::Message* aMsg) const
+{
+ WriteParam(aMsg, Data());
+}
+
+bool
+StructuredCloneData::ReadIPCParams(const IPC::Message* aMsg,
+ PickleIterator* aIter)
+{
+ MOZ_ASSERT(!mInitialized);
+ JSStructuredCloneData data;
+ if (!ReadParam(aMsg, aIter, &data)) {
+ return false;
+ }
+ mSharedData = new SharedJSAllocatedData(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;
+}
+
+} // namespace ipc
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/StructuredCloneData.h b/dom/ipc/StructuredCloneData.h
new file mode 100644
index 000000000..9e427e938
--- /dev/null
+++ b/dom/ipc/StructuredCloneData.h
@@ -0,0 +1,160 @@
+/* -*- 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"
+
+namespace IPC {
+class Message;
+}
+class PickleIterator;
+
+namespace mozilla {
+namespace dom {
+namespace ipc {
+
+class SharedJSAllocatedData final
+{
+public:
+ explicit SharedJSAllocatedData(JSStructuredCloneData&& aData)
+ : mData(Move(aData))
+ { }
+
+ static already_AddRefed<SharedJSAllocatedData>
+ CreateFromExternalData(const char* aData, size_t aDataLength)
+ {
+ JSStructuredCloneData buf;
+ buf.WriteBytes(aData, aDataLength);
+ RefPtr<SharedJSAllocatedData> sharedData =
+ new SharedJSAllocatedData(Move(buf));
+ return sharedData.forget();
+ }
+
+ static already_AddRefed<SharedJSAllocatedData>
+ CreateFromExternalData(const JSStructuredCloneData& aData)
+ {
+ JSStructuredCloneData buf;
+ auto iter = aData.Iter();
+ while (!iter.Done()) {
+ buf.WriteBytes(iter.Data(), iter.RemainingInSegment());
+ iter.Advance(aData, iter.RemainingInSegment());
+ }
+ RefPtr<SharedJSAllocatedData> sharedData =
+ new SharedJSAllocatedData(Move(buf));
+ return sharedData.forget();
+ }
+
+ NS_INLINE_DECL_REFCOUNTING(SharedJSAllocatedData)
+
+ JSStructuredCloneData& Data() { return mData; }
+ size_t DataLength() const { return mData.Size(); }
+
+private:
+ ~SharedJSAllocatedData() { }
+
+ JSStructuredCloneData mData;
+};
+
+class StructuredCloneData : public StructuredCloneHolder
+{
+public:
+ StructuredCloneData()
+ : StructuredCloneHolder(StructuredCloneHolder::CloningSupported,
+ StructuredCloneHolder::TransferringSupported,
+ StructuredCloneHolder::StructuredCloneScope::DifferentProcess)
+ , mInitialized(false)
+ {}
+
+ StructuredCloneData(const StructuredCloneData&) = delete;
+
+ StructuredCloneData(StructuredCloneData&& aOther) = default;
+
+ ~StructuredCloneData()
+ {}
+
+ StructuredCloneData&
+ operator=(const StructuredCloneData& aOther) = delete;
+
+ StructuredCloneData&
+ operator=(StructuredCloneData&& aOther) = default;
+
+ const nsTArray<RefPtr<BlobImpl>>& BlobImpls() const
+ {
+ return mBlobImplArray;
+ }
+
+ nsTArray<RefPtr<BlobImpl>>& BlobImpls()
+ {
+ return mBlobImplArray;
+ }
+
+ bool Copy(const StructuredCloneData& aData);
+
+ void Read(JSContext* aCx,
+ JS::MutableHandle<JS::Value> aValue,
+ ErrorResult &aRv);
+
+ void Write(JSContext* aCx,
+ JS::Handle<JS::Value> aValue,
+ ErrorResult &aRv);
+
+ void Write(JSContext* aCx,
+ JS::Handle<JS::Value> aValue,
+ JS::Handle<JS::Value> aTransfers,
+ ErrorResult &aRv);
+
+ bool UseExternalData(const JSStructuredCloneData& aData)
+ {
+ auto iter = aData.Iter();
+ bool success = false;
+ mExternalData =
+ aData.Borrow<js::SystemAllocPolicy>(iter, aData.Size(), &success);
+ mInitialized = true;
+ return success;
+ }
+
+ bool CopyExternalData(const char* aData, size_t aDataLength);
+
+ JSStructuredCloneData& Data()
+ {
+ return mSharedData ? mSharedData->Data() : mExternalData;
+ }
+
+ const JSStructuredCloneData& Data() const
+ {
+ return mSharedData ? mSharedData->Data() : mExternalData;
+ }
+
+ size_t DataLength() const
+ {
+ return mSharedData ? mSharedData->DataLength() : mExternalData.Size();
+ }
+
+ SharedJSAllocatedData* SharedData() const
+ {
+ return mSharedData;
+ }
+
+ // For IPC serialization
+ void WriteIPCParams(IPC::Message* aMessage) const;
+ bool ReadIPCParams(const IPC::Message* aMessage, PickleIterator* aIter);
+
+private:
+ JSStructuredCloneData mExternalData;
+ RefPtr<SharedJSAllocatedData> mSharedData;
+ bool mInitialized;
+};
+
+} // namespace ipc
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_ipc_StructuredCloneData_h
diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp
new file mode 100644
index 000000000..eaf4a32ed
--- /dev/null
+++ b/dom/ipc/TabChild.cpp
@@ -0,0 +1,3371 @@
+/* -*- 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 "TabChild.h"
+
+#include "gfxPrefs.h"
+#ifdef ACCESSIBILITY
+#include "mozilla/a11y/DocAccessibleChild.h"
+#endif
+#include "Layers.h"
+#include "ContentChild.h"
+#include "TabParent.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/BrowserElementParent.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "mozilla/EventListenerManager.h"
+#include "mozilla/dom/indexedDB/PIndexedDBPermissionRequestChild.h"
+#include "mozilla/plugins/PluginWidgetChild.h"
+#include "mozilla/IMEStateManager.h"
+#include "mozilla/ipc/DocumentRendererChild.h"
+#include "mozilla/ipc/URIUtils.h"
+#include "mozilla/layers/APZChild.h"
+#include "mozilla/layers/APZCCallbackHelper.h"
+#include "mozilla/layers/APZCTreeManager.h"
+#include "mozilla/layers/APZEventState.h"
+#include "mozilla/layers/ContentProcessController.h"
+#include "mozilla/layers/CompositorBridgeChild.h"
+#include "mozilla/layers/DoubleTapToZoom.h"
+#include "mozilla/layers/ImageBridgeChild.h"
+#include "mozilla/layers/InputAPZContext.h"
+#include "mozilla/layers/ShadowLayers.h"
+#include "mozilla/layout/RenderFrameChild.h"
+#include "mozilla/layout/RenderFrameParent.h"
+#include "mozilla/LookAndFeel.h"
+#include "mozilla/MouseEvents.h"
+#include "mozilla/Move.h"
+#include "mozilla/ProcessHangMonitor.h"
+#include "mozilla/ScopeExit.h"
+#include "mozilla/Services.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/TextEvents.h"
+#include "mozilla/TouchEvents.h"
+#include "mozilla/Unused.h"
+#include "mozIApplication.h"
+#include "nsContentUtils.h"
+#include "nsCSSFrameConstructor.h"
+#include "nsDocShell.h"
+#include "nsEmbedCID.h"
+#include "nsGlobalWindow.h"
+#include <algorithm>
+#ifdef MOZ_CRASHREPORTER
+#include "nsExceptionHandler.h"
+#endif
+#include "nsFilePickerProxy.h"
+#include "mozilla/dom/Element.h"
+#include "nsGlobalWindow.h"
+#include "nsIBaseWindow.h"
+#include "nsIBrowserDOMWindow.h"
+#include "nsIDocumentInlines.h"
+#include "nsIDocShellTreeOwner.h"
+#include "nsIDOMChromeWindow.h"
+#include "nsIDOMDocument.h"
+#include "nsIDOMEvent.h"
+#include "nsIDOMWindow.h"
+#include "nsIDOMWindowUtils.h"
+#include "nsFocusManager.h"
+#include "EventStateManager.h"
+#include "nsIDocShell.h"
+#include "nsIFrame.h"
+#include "nsIURI.h"
+#include "nsIURIFixup.h"
+#include "nsCDefaultURIFixup.h"
+#include "nsIWebBrowser.h"
+#include "nsIWebBrowserFocus.h"
+#include "nsIWebBrowserSetup.h"
+#include "nsIWebProgress.h"
+#include "nsIXULRuntime.h"
+#include "nsPIDOMWindow.h"
+#include "nsPIWindowRoot.h"
+#include "nsLayoutUtils.h"
+#include "nsPrintfCString.h"
+#include "nsThreadUtils.h"
+#include "nsViewManager.h"
+#include "nsWeakReference.h"
+#include "nsWindowWatcher.h"
+#include "PermissionMessageUtils.h"
+#include "PuppetWidget.h"
+#include "StructuredCloneData.h"
+#include "nsViewportInfo.h"
+#include "nsILoadContext.h"
+#include "ipc/nsGUIEventIPC.h"
+#include "mozilla/gfx/Matrix.h"
+#include "UnitTransforms.h"
+#include "ClientLayerManager.h"
+#include "LayersLogging.h"
+#include "nsDOMClassInfoID.h"
+#include "nsColorPickerProxy.h"
+#include "nsDatePickerProxy.h"
+#include "nsContentPermissionHelper.h"
+#include "nsPresShell.h"
+#include "nsIAppsService.h"
+#include "nsNetUtil.h"
+#include "nsIPermissionManager.h"
+#include "nsIURILoader.h"
+#include "nsIScriptError.h"
+#include "mozilla/EventForwards.h"
+#include "nsDeviceContext.h"
+#include "nsSandboxFlags.h"
+#include "FrameLayerBuilder.h"
+#include "VRManagerChild.h"
+#include "nsICommandParams.h"
+#include "nsISHistory.h"
+#include "nsQueryObject.h"
+#include "GroupedSHistory.h"
+#include "nsIHttpChannel.h"
+#include "mozilla/dom/DocGroup.h"
+
+#ifdef NS_PRINTING
+#include "nsIPrintSession.h"
+#include "nsIPrintSettings.h"
+#include "nsIPrintSettingsService.h"
+#include "nsIWebBrowserPrint.h"
+#endif
+
+#define BROWSER_ELEMENT_CHILD_SCRIPT \
+ NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js")
+
+#define TABC_LOG(...)
+// #define TABC_LOG(...) printf_stderr("TABC: " __VA_ARGS__)
+
+using namespace mozilla;
+using namespace mozilla::dom;
+using namespace mozilla::dom::ipc;
+using namespace mozilla::dom::workers;
+using namespace mozilla::ipc;
+using namespace mozilla::layers;
+using namespace mozilla::layout;
+using namespace mozilla::docshell;
+using namespace mozilla::widget;
+using namespace mozilla::jsipc;
+using mozilla::layers::GeckoContentController;
+
+NS_IMPL_ISUPPORTS(ContentListener, nsIDOMEventListener)
+NS_IMPL_ISUPPORTS(TabChildSHistoryListener,
+ nsISHistoryListener,
+ nsIPartialSHistoryListener,
+ nsISupportsWeakReference)
+
+static const CSSSize kDefaultViewportSize(980, 480);
+
+static const char BEFORE_FIRST_PAINT[] = "before-first-paint";
+
+typedef nsDataHashtable<nsUint64HashKey, TabChild*> TabChildMap;
+static TabChildMap* sTabChildren;
+
+TabChildBase::TabChildBase()
+ : mTabChildGlobal(nullptr)
+{
+ mozilla::HoldJSObjects(this);
+}
+
+TabChildBase::~TabChildBase()
+{
+ mAnonymousGlobalScopes.Clear();
+ mozilla::DropJSObjects(this);
+}
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(TabChildBase)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(TabChildBase)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mTabChildGlobal)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mAnonymousGlobalScopes)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mWebBrowserChrome)
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(TabChildBase)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTabChildGlobal)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWebBrowserChrome)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(TabChildBase)
+ tmp->nsMessageManagerScriptExecutor::Trace(aCallbacks, aClosure);
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(TabChildBase)
+ NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(TabChildBase)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(TabChildBase)
+
+already_AddRefed<nsIDocument>
+TabChildBase::GetDocument() const
+{
+ nsCOMPtr<nsIDOMDocument> domDoc;
+ WebNavigation()->GetDocument(getter_AddRefs(domDoc));
+ nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
+ return doc.forget();
+}
+
+already_AddRefed<nsIPresShell>
+TabChildBase::GetPresShell() const
+{
+ nsCOMPtr<nsIPresShell> result;
+ if (nsCOMPtr<nsIDocument> doc = GetDocument()) {
+ result = doc->GetShell();
+ }
+ return result.forget();
+}
+
+void
+TabChildBase::DispatchMessageManagerMessage(const nsAString& aMessageName,
+ const nsAString& aJSONData)
+{
+ AutoSafeJSContext cx;
+ JS::Rooted<JS::Value> json(cx, JS::NullValue());
+ StructuredCloneData data;
+ if (JS_ParseJSON(cx,
+ static_cast<const char16_t*>(aJSONData.BeginReading()),
+ aJSONData.Length(),
+ &json)) {
+ ErrorResult rv;
+ data.Write(cx, json, rv);
+ if (NS_WARN_IF(rv.Failed())) {
+ rv.SuppressException();
+ return;
+ }
+ }
+
+ nsCOMPtr<nsIXPConnectJSObjectHolder> kungFuDeathGrip(GetGlobal());
+ // Let the BrowserElementScrolling helper (if it exists) for this
+ // content manipulate the frame state.
+ RefPtr<nsFrameMessageManager> mm =
+ static_cast<nsFrameMessageManager*>(mTabChildGlobal->mMessageManager.get());
+ mm->ReceiveMessage(static_cast<EventTarget*>(mTabChildGlobal), nullptr,
+ aMessageName, false, &data, nullptr, nullptr, nullptr);
+}
+
+bool
+TabChildBase::UpdateFrameHandler(const FrameMetrics& aFrameMetrics)
+{
+ MOZ_ASSERT(aFrameMetrics.GetScrollId() != FrameMetrics::NULL_SCROLL_ID);
+
+ if (aFrameMetrics.IsRootContent()) {
+ if (nsCOMPtr<nsIPresShell> shell = GetPresShell()) {
+ // Guard against stale updates (updates meant for a pres shell which
+ // has since been torn down and destroyed).
+ if (aFrameMetrics.GetPresShellId() == shell->GetPresShellId()) {
+ ProcessUpdateFrame(aFrameMetrics);
+ return true;
+ }
+ }
+ } else {
+ // aFrameMetrics.mIsRoot is false, so we are trying to update a subframe.
+ // This requires special handling.
+ FrameMetrics newSubFrameMetrics(aFrameMetrics);
+ APZCCallbackHelper::UpdateSubFrame(newSubFrameMetrics);
+ return true;
+ }
+ return true;
+}
+
+void
+TabChildBase::ProcessUpdateFrame(const FrameMetrics& aFrameMetrics)
+{
+ if (!mGlobal || !mTabChildGlobal) {
+ return;
+ }
+
+ FrameMetrics newMetrics = aFrameMetrics;
+ APZCCallbackHelper::UpdateRootFrame(newMetrics);
+}
+
+NS_IMETHODIMP
+ContentListener::HandleEvent(nsIDOMEvent* aEvent)
+{
+ RemoteDOMEvent remoteEvent;
+ remoteEvent.mEvent = do_QueryInterface(aEvent);
+ NS_ENSURE_STATE(remoteEvent.mEvent);
+ mTabChild->SendEvent(remoteEvent);
+ return NS_OK;
+}
+
+class TabChild::DelayedDeleteRunnable final
+ : public Runnable
+{
+ RefPtr<TabChild> mTabChild;
+
+public:
+ explicit DelayedDeleteRunnable(TabChild* aTabChild)
+ : mTabChild(aTabChild)
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aTabChild);
+ }
+
+private:
+ ~DelayedDeleteRunnable()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(!mTabChild);
+ }
+
+ NS_IMETHOD
+ Run() override
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(mTabChild);
+
+ // Check in case ActorDestroy was called after RecvDestroy message.
+ if (mTabChild->IPCOpen()) {
+ Unused << PBrowserChild::Send__delete__(mTabChild);
+ }
+
+ mTabChild = nullptr;
+ return NS_OK;
+ }
+};
+
+namespace {
+StaticRefPtr<TabChild> sPreallocatedTab;
+
+std::map<TabId, RefPtr<TabChild>>&
+NestedTabChildMap()
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ static std::map<TabId, RefPtr<TabChild>> sNestedTabChildMap;
+ return sNestedTabChildMap;
+}
+} // namespace
+
+already_AddRefed<TabChild>
+TabChild::FindTabChild(const TabId& aTabId)
+{
+ auto iter = NestedTabChildMap().find(aTabId);
+ if (iter == NestedTabChildMap().end()) {
+ return nullptr;
+ }
+ RefPtr<TabChild> tabChild = iter->second;
+ return tabChild.forget();
+}
+
+static void
+PreloadSlowThingsPostFork(void* aUnused)
+{
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+ observerService->NotifyObservers(nullptr, "preload-postfork", nullptr);
+
+ MOZ_ASSERT(sPreallocatedTab);
+ // Initialize initial reflow of the PresShell has to happen after fork
+ // because about:blank content viewer is created in the above observer
+ // notification.
+ nsCOMPtr<nsIDocShell> docShell =
+ do_GetInterface(sPreallocatedTab->WebNavigation());
+ if (nsIPresShell* presShell = docShell->GetPresShell()) {
+ // Initialize and do an initial reflow of the about:blank
+ // PresShell to let it preload some things for us.
+ presShell->Initialize(0, 0);
+ nsIDocument* doc = presShell->GetDocument();
+ doc->FlushPendingNotifications(Flush_Layout);
+ // ... but after it's done, make sure it doesn't do any more
+ // work.
+ presShell->MakeZombie();
+ }
+
+}
+
+static bool sPreloaded = false;
+
+/*static*/ void
+TabChild::PreloadSlowThings()
+{
+ if (sPreloaded) {
+ // If we are alredy initialized in Nuwa, don't redo preloading.
+ return;
+ }
+ sPreloaded = true;
+
+ // Pass nullptr to aManager since at this point the TabChild is
+ // not connected to any manager. Any attempt to use the TabChild
+ // in IPC will crash.
+ RefPtr<TabChild> tab(new TabChild(nullptr,
+ TabId(0),
+ TabContext(), /* chromeFlags */ 0));
+ if (!NS_SUCCEEDED(tab->Init()) ||
+ !tab->InitTabChildGlobal(DONT_LOAD_SCRIPTS)) {
+ return;
+ }
+
+ // Just load and compile these scripts, but don't run them.
+ tab->TryCacheLoadAndCompileScript(BROWSER_ELEMENT_CHILD_SCRIPT, true);
+ // Load, compile, and run these scripts.
+ tab->RecvLoadRemoteScript(
+ NS_LITERAL_STRING("chrome://global/content/preload.js"),
+ true);
+
+ sPreallocatedTab = tab;
+ ClearOnShutdown(&sPreallocatedTab);
+
+ PreloadSlowThingsPostFork(nullptr);
+}
+
+/*static*/ already_AddRefed<TabChild>
+TabChild::Create(nsIContentChild* aManager,
+ const TabId& aTabId,
+ const TabContext &aContext,
+ uint32_t aChromeFlags)
+{
+ if (sPreallocatedTab &&
+ sPreallocatedTab->mChromeFlags == aChromeFlags &&
+ aContext.IsMozBrowserOrApp()) {
+
+ RefPtr<TabChild> child = sPreallocatedTab.get();
+ sPreallocatedTab = nullptr;
+
+ MOZ_ASSERT(!child->mTriedBrowserInit);
+
+ child->mManager = aManager;
+ child->SetTabId(aTabId);
+ child->SetTabContext(aContext);
+ child->NotifyTabContextUpdated(true);
+ return child.forget();
+ }
+
+ RefPtr<TabChild> iframe = new TabChild(aManager, aTabId,
+ aContext, aChromeFlags);
+ return NS_SUCCEEDED(iframe->Init()) ? iframe.forget() : nullptr;
+}
+
+TabChild::TabChild(nsIContentChild* aManager,
+ const TabId& aTabId,
+ const TabContext& aContext,
+ uint32_t aChromeFlags)
+ : TabContext(aContext)
+ , mRemoteFrame(nullptr)
+ , mManager(aManager)
+ , mChromeFlags(aChromeFlags)
+ , mActiveSuppressDisplayport(0)
+ , mLayersId(0)
+ , mDidFakeShow(false)
+ , mNotified(false)
+ , mTriedBrowserInit(false)
+ , mOrientation(eScreenOrientation_PortraitPrimary)
+ , mIgnoreKeyPressEvent(false)
+ , mHasValidInnerSize(false)
+ , mDestroyed(false)
+ , mUniqueId(aTabId)
+ , mDPI(0)
+ , mRounding(0)
+ , mDefaultScale(0)
+ , mIsTransparent(false)
+ , mIPCOpen(false)
+ , mParentIsActive(false)
+ , mDidSetRealShowInfo(false)
+ , mDidLoadURLInit(false)
+ , mIsFreshProcess(false)
+ , mLayerObserverEpoch(0)
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+ , mNativeWindowHandle(0)
+#endif
+{
+ // In the general case having the TabParent tell us if APZ is enabled or not
+ // doesn't really work because the TabParent itself may not have a reference
+ // to the owning widget during initialization. Instead we assume that this
+ // TabChild corresponds to a widget type that would have APZ enabled, and just
+ // check the other conditions necessary for enabling APZ.
+ mAsyncPanZoomEnabled = gfxPlatform::AsyncPanZoomEnabled();
+
+ nsWeakPtr weakPtrThis(do_GetWeakReference(static_cast<nsITabChild*>(this))); // for capture by the lambda
+ mSetAllowedTouchBehaviorCallback = [weakPtrThis](uint64_t aInputBlockId,
+ const nsTArray<TouchBehaviorFlags>& aFlags)
+ {
+ if (nsCOMPtr<nsITabChild> tabChild = do_QueryReferent(weakPtrThis)) {
+ static_cast<TabChild*>(tabChild.get())->SetAllowedTouchBehavior(aInputBlockId, aFlags);
+ }
+ };
+
+ // preloaded TabChild should not be added to child map
+ if (mUniqueId) {
+ MOZ_ASSERT(NestedTabChildMap().find(mUniqueId) == NestedTabChildMap().end());
+ NestedTabChildMap()[mUniqueId] = this;
+ }
+
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+
+ if (observerService) {
+ const nsAttrValue::EnumTable* table =
+ AudioChannelService::GetAudioChannelTable();
+
+ nsAutoCString topic;
+ for (uint32_t i = 0; table[i].tag; ++i) {
+ topic.Assign("audiochannel-activity-");
+ topic.Append(table[i].tag);
+
+ observerService->AddObserver(this, topic.get(), false);
+ }
+ }
+
+ for (uint32_t idx = 0; idx < NUMBER_OF_AUDIO_CHANNELS; idx++) {
+ mAudioChannelsActive.AppendElement(false);
+ }
+}
+
+NS_IMETHODIMP
+TabChild::Observe(nsISupports *aSubject,
+ const char *aTopic,
+ const char16_t *aData)
+{
+ if (!strcmp(aTopic, BEFORE_FIRST_PAINT)) {
+ if (AsyncPanZoomEnabled()) {
+ nsCOMPtr<nsIDocument> subject(do_QueryInterface(aSubject));
+ nsCOMPtr<nsIDocument> doc(GetDocument());
+
+ if (SameCOMIdentity(subject, doc)) {
+ nsCOMPtr<nsIPresShell> shell(doc->GetShell());
+ if (shell) {
+ shell->SetIsFirstPaint(true);
+ }
+
+ APZCCallbackHelper::InitializeRootDisplayport(shell);
+ }
+ }
+ }
+
+ const nsAttrValue::EnumTable* table =
+ AudioChannelService::GetAudioChannelTable();
+
+ nsAutoCString topic;
+ int16_t audioChannel = -1;
+ for (uint32_t i = 0; table[i].tag; ++i) {
+ topic.Assign("audiochannel-activity-");
+ topic.Append(table[i].tag);
+
+ if (topic.Equals(aTopic)) {
+ audioChannel = table[i].value;
+ break;
+ }
+ }
+
+ if (audioChannel != -1 && mIPCOpen) {
+ // If the subject is not a wrapper, it is sent by the TabParent and we
+ // should ignore it.
+ nsCOMPtr<nsISupportsPRUint64> wrapper = do_QueryInterface(aSubject);
+ if (!wrapper) {
+ return NS_OK;
+ }
+
+ // We must have a window in order to compare the windowID contained into the
+ // wrapper.
+ nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation());
+ if (!window) {
+ return NS_OK;
+ }
+
+ uint64_t windowID = 0;
+ nsresult rv = wrapper->GetData(&windowID);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+
+ // In theory a tabChild should contain just 1 top window, but let's double
+ // check it comparing the windowID.
+ if (window->WindowID() != windowID) {
+ MOZ_LOG(AudioChannelService::GetAudioChannelLog(), LogLevel::Debug,
+ ("TabChild, Observe, different windowID, owner ID = %lld, "
+ "ID from wrapper = %lld", window->WindowID(), windowID));
+ return NS_OK;
+ }
+
+ nsAutoString activeStr(aData);
+ bool active = activeStr.EqualsLiteral("active");
+ if (active != mAudioChannelsActive[audioChannel]) {
+ mAudioChannelsActive[audioChannel] = active;
+ Unused << SendAudioChannelActivityNotification(audioChannel, active);
+ }
+ }
+
+ return NS_OK;
+}
+
+void
+TabChild::ContentReceivedInputBlock(const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId,
+ bool aPreventDefault) const
+{
+ if (mApzcTreeManager) {
+ mApzcTreeManager->ContentReceivedInputBlock(aInputBlockId, aPreventDefault);
+ }
+}
+
+void
+TabChild::SetTargetAPZC(uint64_t aInputBlockId,
+ const nsTArray<ScrollableLayerGuid>& aTargets) const
+{
+ if (mApzcTreeManager) {
+ mApzcTreeManager->SetTargetAPZC(aInputBlockId, aTargets);
+ }
+}
+
+void
+TabChild::SetAllowedTouchBehavior(uint64_t aInputBlockId,
+ const nsTArray<TouchBehaviorFlags>& aTargets) const
+{
+ if (mApzcTreeManager) {
+ mApzcTreeManager->SetAllowedTouchBehavior(aInputBlockId, aTargets);
+ }
+}
+
+bool
+TabChild::DoUpdateZoomConstraints(const uint32_t& aPresShellId,
+ const ViewID& aViewId,
+ const Maybe<ZoomConstraints>& aConstraints)
+{
+ if (sPreallocatedTab == this) {
+ // If we're the preallocated tab, bail out because doing IPC will crash.
+ // Once we get used for something we'll get another zoom constraints update
+ // and all will be well.
+ return true;
+ }
+
+ if (!mApzcTreeManager) {
+ return false;
+ }
+
+ ScrollableLayerGuid guid = ScrollableLayerGuid(mLayersId, aPresShellId, aViewId);
+
+ mApzcTreeManager->UpdateZoomConstraints(guid, aConstraints);
+ return true;
+}
+
+nsresult
+TabChild::Init()
+{
+ nsCOMPtr<nsIWebBrowser> webBrowser = do_CreateInstance(NS_WEBBROWSER_CONTRACTID);
+ if (!webBrowser) {
+ NS_ERROR("Couldn't create a nsWebBrowser?");
+ return NS_ERROR_FAILURE;
+ }
+
+ webBrowser->SetContainerWindow(this);
+ webBrowser->SetOriginAttributes(OriginAttributesRef());
+ mWebNav = do_QueryInterface(webBrowser);
+ NS_ASSERTION(mWebNav, "nsWebBrowser doesn't implement nsIWebNavigation?");
+
+ nsCOMPtr<nsIDocShellTreeItem> docShellItem(do_QueryInterface(WebNavigation()));
+ docShellItem->SetItemType(nsIDocShellTreeItem::typeContentWrapper);
+
+ nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(WebNavigation());
+ if (!baseWindow) {
+ NS_ERROR("mWebNav doesn't QI to nsIBaseWindow");
+ return NS_ERROR_FAILURE;
+ }
+
+ 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, 0, // no parents
+ LayoutDeviceIntRect(0, 0, 0, 0),
+ nullptr // HandleWidgetEvent
+ );
+
+ baseWindow->InitWindow(0, mPuppetWidget, 0, 0, 0, 0);
+ baseWindow->Create();
+
+ // Set the tab context attributes then pass to docShell
+ NotifyTabContextUpdated(false);
+
+ // IPC uses a WebBrowser object for which DNS prefetching is turned off
+ // by default. But here we really want it, so enable it explicitly
+ nsCOMPtr<nsIWebBrowserSetup> webBrowserSetup =
+ do_QueryInterface(baseWindow);
+ if (webBrowserSetup) {
+ webBrowserSetup->SetProperty(nsIWebBrowserSetup::SETUP_ALLOW_DNS_PREFETCH,
+ true);
+ } else {
+ NS_WARNING("baseWindow doesn't QI to nsIWebBrowserSetup, skipping "
+ "DNS prefetching enable step.");
+ }
+
+ nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
+ MOZ_ASSERT(docShell);
+
+ docShell->SetAffectPrivateSessionLifetime(
+ mChromeFlags & nsIWebBrowserChrome::CHROME_PRIVATE_LIFETIME);
+ nsCOMPtr<nsILoadContext> loadContext = do_GetInterface(WebNavigation());
+ MOZ_ASSERT(loadContext);
+ loadContext->SetPrivateBrowsing(OriginAttributesRef().mPrivateBrowsingId > 0);
+ loadContext->SetRemoteTabs(
+ mChromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW);
+
+ // 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 =
+ do_QueryInterface(window->GetChromeEventHandler());
+ docShell->SetChromeEventHandler(chromeHandler);
+
+ if (window->GetCurrentInnerWindow()) {
+ window->SetKeyboardIndicators(ShowAccelerators(), ShowFocusRings());
+ } else {
+ // Skip ShouldShowFocusRing check if no inner window is available
+ window->SetInitialKeyboardIndicators(ShowAccelerators(), ShowFocusRings());
+ }
+
+ // Set prerender flag if necessary.
+ if (mIsPrerendered) {
+ docShell->SetIsPrerendered();
+ }
+
+ nsContentUtils::SetScrollbarsVisibility(window->GetDocShell(),
+ !!(mChromeFlags & nsIWebBrowserChrome::CHROME_SCROLLBARS));
+
+ nsWeakPtr weakPtrThis = do_GetWeakReference(static_cast<nsITabChild*>(this)); // for capture by the lambda
+ ContentReceivedInputBlockCallback callback(
+ [weakPtrThis](const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId,
+ bool aPreventDefault)
+ {
+ if (nsCOMPtr<nsITabChild> tabChild = do_QueryReferent(weakPtrThis)) {
+ static_cast<TabChild*>(tabChild.get())->ContentReceivedInputBlock(aGuid, aInputBlockId, aPreventDefault);
+ }
+ });
+ mAPZEventState = new APZEventState(mPuppetWidget, Move(callback));
+
+ mIPCOpen = true;
+
+ if (GroupedSHistory::GroupedHistoryEnabled()) {
+ // Set session history listener.
+ nsCOMPtr<nsISHistory> shistory;
+ mWebNav->GetSessionHistory(getter_AddRefs(shistory));
+ if (!shistory) {
+ return NS_ERROR_FAILURE;
+ }
+ mHistoryListener = new TabChildSHistoryListener(this);
+ nsCOMPtr<nsISHistoryListener> listener(do_QueryObject(mHistoryListener));
+ shistory->AddSHistoryListener(listener);
+ nsCOMPtr<nsIPartialSHistoryListener> partialListener(do_QueryObject(mHistoryListener));
+ shistory->SetPartialSHistoryListener(partialListener);
+ }
+
+ return NS_OK;
+}
+
+void
+TabChild::NotifyTabContextUpdated(bool aIsPreallocated)
+{
+ nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
+ MOZ_ASSERT(docShell);
+
+ if (!docShell) {
+ return;
+ }
+
+ UpdateFrameType();
+
+ if (aIsPreallocated) {
+ nsDocShell::Cast(docShell)->SetOriginAttributes(OriginAttributesRef());
+ }
+
+ // Set SANDBOXED_AUXILIARY_NAVIGATION flag if this is a receiver page.
+ if (!PresentationURL().IsEmpty()) {
+ docShell->SetSandboxFlags(SANDBOXED_AUXILIARY_NAVIGATION);
+ }
+}
+
+void
+TabChild::UpdateFrameType()
+{
+ nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
+ MOZ_ASSERT(docShell);
+
+ // TODO: Bug 1252794 - remove frameType from nsIDocShell.idl
+ docShell->SetFrameType(IsMozBrowserElement() ? nsIDocShell::FRAME_TYPE_BROWSER :
+ HasOwnApp() ? nsIDocShell::FRAME_TYPE_APP :
+ nsIDocShell::FRAME_TYPE_REGULAR);
+}
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TabChild)
+ NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChrome)
+ NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChrome2)
+ NS_INTERFACE_MAP_ENTRY(nsIEmbeddingSiteWindow)
+ NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChromeFocus)
+ NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
+ NS_INTERFACE_MAP_ENTRY(nsIWindowProvider)
+ NS_INTERFACE_MAP_ENTRY(nsITabChild)
+ NS_INTERFACE_MAP_ENTRY(nsIObserver)
+ NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+ NS_INTERFACE_MAP_ENTRY(nsITooltipListener)
+NS_INTERFACE_MAP_END_INHERITING(TabChildBase)
+
+NS_IMPL_ADDREF_INHERITED(TabChild, TabChildBase);
+NS_IMPL_RELEASE_INHERITED(TabChild, TabChildBase);
+
+NS_IMETHODIMP
+TabChild::SetStatus(uint32_t aStatusType, const char16_t* aStatus)
+{
+ return SetStatusWithContext(aStatusType,
+ aStatus ? static_cast<const nsString &>(nsDependentString(aStatus))
+ : EmptyString(),
+ nullptr);
+}
+
+NS_IMETHODIMP
+TabChild::GetWebBrowser(nsIWebBrowser** aWebBrowser)
+{
+ NS_WARNING("TabChild::GetWebBrowser not supported in TabChild");
+
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TabChild::SetWebBrowser(nsIWebBrowser* aWebBrowser)
+{
+ NS_WARNING("TabChild::SetWebBrowser not supported in TabChild");
+
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TabChild::GetChromeFlags(uint32_t* aChromeFlags)
+{
+ *aChromeFlags = mChromeFlags;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+TabChild::SetChromeFlags(uint32_t aChromeFlags)
+{
+ NS_WARNING("trying to SetChromeFlags from content process?");
+
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TabChild::DestroyBrowserWindow()
+{
+ NS_WARNING("TabChild::DestroyBrowserWindow not supported in TabChild");
+
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TabChild::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));
+ 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
+TabChild::RemoteDropLinks(uint32_t aLinksCount, nsIDroppedLinkItem** aLinks)
+{
+ nsTArray<nsString> linksArray;
+ nsresult rv = NS_OK;
+ for (uint32_t i = 0; i < aLinksCount; i++) {
+ nsString tmp;
+ rv = aLinks[i]->GetUrl(tmp);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ linksArray.AppendElement(tmp);
+
+ rv = aLinks[i]->GetName(tmp);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ linksArray.AppendElement(tmp);
+
+ rv = aLinks[i]->GetType(tmp);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ linksArray.AppendElement(tmp);
+ }
+
+ bool sent = SendDropLinks(linksArray);
+
+ return sent ? NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+TabChild::SizeBrowserTo(int32_t aWidth, int32_t aHeight)
+{
+ NS_WARNING("TabChild::SizeBrowserTo not supported in TabChild");
+
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TabChild::ShowAsModal()
+{
+ NS_WARNING("TabChild::ShowAsModal not supported in TabChild");
+
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TabChild::IsWindowModal(bool* aRetVal)
+{
+ *aRetVal = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+TabChild::ExitModalEventLoop(nsresult aStatus)
+{
+ NS_WARNING("TabChild::ExitModalEventLoop not supported in TabChild");
+
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TabChild::SetStatusWithContext(uint32_t aStatusType,
+ const nsAString& aStatusText,
+ nsISupports* aStatusContext)
+{
+ // We can only send the status after the ipc machinery is set up,
+ // mRemoteFrame is a good indicator.
+ if (mRemoteFrame)
+ SendSetStatus(aStatusType, nsString(aStatusText));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+TabChild::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;
+ }
+
+ Unused << SendSetDimensions(aFlags, aX, aY, aCx, aCy);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+TabChild::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
+TabChild::SetFocus()
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TabChild::GetVisibility(bool* aVisibility)
+{
+ *aVisibility = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+TabChild::SetVisibility(bool aVisibility)
+{
+ // should the platform support this? Bug 666365
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+TabChild::GetTitle(char16_t** aTitle)
+{
+ NS_WARNING("TabChild::GetTitle not supported in TabChild");
+
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TabChild::SetTitle(const char16_t* aTitle)
+{
+ // JavaScript sends the "DOMTitleChanged" event to the parent
+ // via the message manager.
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+TabChild::GetSiteWindow(void** aSiteWindow)
+{
+ NS_WARNING("TabChild::GetSiteWindow not supported in TabChild");
+
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TabChild::Blur()
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TabChild::FocusNextElement(bool aForDocumentNavigation)
+{
+ SendMoveFocus(true, aForDocumentNavigation);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+TabChild::FocusPrevElement(bool aForDocumentNavigation)
+{
+ SendMoveFocus(false, aForDocumentNavigation);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+TabChild::GetInterface(const nsIID & aIID, void **aSink)
+{
+ if (aIID.Equals(NS_GET_IID(nsIWebBrowserChrome3))) {
+ NS_IF_ADDREF(((nsISupports *) (*aSink = mWebBrowserChrome)));
+ return NS_OK;
+ }
+
+ // XXXbz should we restrict the set of interfaces we hand out here?
+ // See bug 537429
+ return QueryInterface(aIID, aSink);
+}
+
+NS_IMETHODIMP
+TabChild::ProvideWindow(mozIDOMWindowProxy* aParent,
+ uint32_t aChromeFlags,
+ bool aCalledFromJS,
+ bool aPositionSpecified, bool aSizeSpecified,
+ nsIURI* aURI, const nsAString& aName,
+ const nsACString& aFeatures, bool aForceNoOpener,
+ bool* aWindowIsNew, mozIDOMWindowProxy** aReturn)
+{
+ *aReturn = nullptr;
+
+ // If aParent is inside an <iframe mozbrowser> or <iframe mozapp> and this
+ // isn't a request to open a modal-type window, we're going to create a new
+ // <iframe mozbrowser/mozapp> and return its window here.
+ nsCOMPtr<nsIDocShell> docshell = do_GetInterface(aParent);
+ bool iframeMoz = (docshell && docshell->GetIsInMozBrowserOrApp() &&
+ !(aChromeFlags & (nsIWebBrowserChrome::CHROME_MODAL |
+ nsIWebBrowserChrome::CHROME_OPENAS_DIALOG |
+ nsIWebBrowserChrome::CHROME_OPENAS_CHROME)));
+
+ if (!iframeMoz) {
+ int32_t openLocation =
+ nsWindowWatcher::GetWindowOpenLocation(nsPIDOMWindowOuter::From(aParent),
+ aChromeFlags, aCalledFromJS,
+ aPositionSpecified, aSizeSpecified);
+
+ // 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;
+ return browser->GetContentDOMWindow(aReturn);
+ }
+ }
+
+ // 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,
+ aParent,
+ iframeMoz,
+ aChromeFlags,
+ aCalledFromJS,
+ aPositionSpecified,
+ aSizeSpecified,
+ aURI,
+ aName,
+ aFeatures,
+ aForceNoOpener,
+ aWindowIsNew,
+ aReturn);
+}
+
+void
+TabChild::DestroyWindow()
+{
+ nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(WebNavigation());
+ if (baseWindow)
+ baseWindow->Destroy();
+
+ // NB: the order of mPuppetWidget->Destroy() and mRemoteFrame->Destroy()
+ // is important: we want to kill off remote layers before their
+ // frames
+ if (mPuppetWidget) {
+ mPuppetWidget->Destroy();
+ }
+
+ if (mRemoteFrame) {
+ mRemoteFrame->Destroy();
+ mRemoteFrame = nullptr;
+ }
+
+
+ if (mLayersId != 0) {
+ MOZ_ASSERT(sTabChildren);
+ sTabChildren->Remove(mLayersId);
+ if (!sTabChildren->Count()) {
+ delete sTabChildren;
+ sTabChildren = nullptr;
+ }
+ mLayersId = 0;
+ }
+}
+
+void
+TabChild::ActorDestroy(ActorDestroyReason why)
+{
+ mIPCOpen = false;
+
+ DestroyWindow();
+
+ if (mTabChildGlobal) {
+ // 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(mTabChildGlobal->mMessageManager);
+ if (mTabChildGlobal->mMessageManager) {
+ // The messageManager relays messages via the TabChild which
+ // no longer exists.
+ static_cast<nsFrameMessageManager*>
+ (mTabChildGlobal->mMessageManager.get())->Disconnect();
+ mTabChildGlobal->mMessageManager = nullptr;
+ }
+ }
+
+ CompositorBridgeChild* compositorChild = static_cast<CompositorBridgeChild*>(CompositorBridgeChild::Get());
+ compositorChild->CancelNotifyAfterRemotePaint(this);
+
+ if (GetTabId() != 0) {
+ NestedTabChildMap().erase(GetTabId());
+ }
+}
+
+TabChild::~TabChild()
+{
+ DestroyWindow();
+
+ nsCOMPtr<nsIWebBrowser> webBrowser = do_QueryInterface(WebNavigation());
+ if (webBrowser) {
+ webBrowser->SetContainerWindow(nullptr);
+ }
+
+ if (mHistoryListener) {
+ mHistoryListener->ClearTabChild();
+ }
+}
+
+void
+TabChild::SetProcessNameToAppName()
+{
+ nsCOMPtr<mozIApplication> app = GetOwnApp();
+ if (!app) {
+ return;
+ }
+
+ nsAutoString appName;
+ nsresult rv = app->GetName(appName);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Failed to retrieve app name");
+ return;
+ }
+
+ ContentChild::GetSingleton()->SetProcessName(appName, true);
+}
+
+bool
+TabChild::RecvLoadURL(const nsCString& aURI,
+ const ShowInfo& aInfo)
+{
+ if (!mDidLoadURLInit) {
+ mDidLoadURLInit = true;
+ if (!InitTabChildGlobal()) {
+ return false;
+ }
+
+ ApplyShowInfo(aInfo);
+
+ SetProcessNameToAppName();
+ }
+
+ nsresult rv =
+ WebNavigation()->LoadURI(NS_ConvertUTF8toUTF16(aURI).get(),
+ nsIWebNavigation::LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP |
+ nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL,
+ nullptr, nullptr, nullptr);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("WebNavigation()->LoadURI failed. Eating exception, what else can I do?");
+ }
+
+#ifdef MOZ_CRASHREPORTER
+ CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("URL"), aURI);
+#endif
+
+ return true;
+}
+
+void
+TabChild::DoFakeShow(const TextureFactoryIdentifier& aTextureFactoryIdentifier,
+ const uint64_t& aLayersId,
+ PRenderFrameChild* aRenderFrame, const ShowInfo& aShowInfo)
+{
+ RecvShow(ScreenIntSize(0, 0), aShowInfo, aTextureFactoryIdentifier,
+ aLayersId, aRenderFrame, mParentIsActive, nsSizeMode_Normal);
+ mDidFakeShow = true;
+}
+
+void
+TabChild::ApplyShowInfo(const ShowInfo& aInfo)
+{
+ if (mDidSetRealShowInfo) {
+ return;
+ }
+
+ if (!aInfo.fakeShowInfo()) {
+ // Once we've got one ShowInfo from parent, no need to update the values
+ // anymore.
+ mDidSetRealShowInfo = true;
+ }
+
+ nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
+ if (docShell) {
+ nsCOMPtr<nsIDocShellTreeItem> item = do_GetInterface(docShell);
+ if (IsMozBrowserOrApp()) {
+ // B2G allows window.name to be set by changing the name attribute on the
+ // <iframe mozbrowser> element. window.open calls cause this attribute to
+ // be set to the correct value. A normal <xul:browser> element has no such
+ // attribute. The data we get here comes from reading the attribute, so we
+ // shouldn't trust it for <xul:browser> elements.
+ item->SetName(aInfo.name());
+ }
+ docShell->SetFullscreenAllowed(aInfo.fullscreenAllowed());
+ if (aInfo.isPrivate()) {
+ nsCOMPtr<nsILoadContext> context = do_GetInterface(docShell);
+ // No need to re-set private browsing mode.
+ if (!context->UsePrivateBrowsing()) {
+ if (docShell->GetHasLoadedNonBlankURI()) {
+ nsContentUtils::ReportToConsoleNonLocalized(
+ NS_LITERAL_STRING("We should not switch to Private Browsing after loading a document."),
+ nsIScriptError::warningFlag,
+ NS_LITERAL_CSTRING("mozprivatebrowsing"),
+ nullptr);
+ } else {
+ DocShellOriginAttributes attrs(nsDocShell::Cast(docShell)->GetOriginAttributes());
+ attrs.SyncAttributesWithPrivateBrowsing(true);
+ nsDocShell::Cast(docShell)->SetOriginAttributes(attrs);
+ }
+ }
+ }
+ }
+ mDPI = aInfo.dpi();
+ mRounding = aInfo.widgetRounding();
+ mDefaultScale = aInfo.defaultScale();
+ mIsTransparent = aInfo.isTransparent();
+}
+
+bool
+TabChild::RecvShow(const ScreenIntSize& aSize,
+ const ShowInfo& aInfo,
+ const TextureFactoryIdentifier& aTextureFactoryIdentifier,
+ const uint64_t& aLayersId,
+ PRenderFrameChild* aRenderFrame,
+ const bool& aParentIsActive,
+ const nsSizeMode& aSizeMode)
+{
+ MOZ_ASSERT((!mDidFakeShow && aRenderFrame) || (mDidFakeShow && !aRenderFrame));
+
+ mPuppetWidget->SetSizeMode(aSizeMode);
+ if (mDidFakeShow) {
+ ApplyShowInfo(aInfo);
+ RecvParentActivated(aParentIsActive);
+ return true;
+ }
+
+ nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(WebNavigation());
+ if (!baseWindow) {
+ NS_ERROR("WebNavigation() doesn't QI to nsIBaseWindow");
+ return false;
+ }
+
+ if (!InitRenderingState(aTextureFactoryIdentifier, aLayersId, aRenderFrame)) {
+ // We can fail to initialize our widget if the <browser
+ // remote> has already been destroyed, and we couldn't hook
+ // into the parent-process's layer system. That's not a fatal
+ // error.
+ return true;
+ }
+
+ baseWindow->SetVisibility(true);
+
+ bool res = InitTabChildGlobal();
+ ApplyShowInfo(aInfo);
+ RecvParentActivated(aParentIsActive);
+
+ return res;
+}
+
+bool
+TabChild::RecvUpdateDimensions(const CSSRect& rect, const CSSSize& size,
+ const ScreenOrientationInternal& orientation,
+ const LayoutDeviceIntPoint& clientOffset,
+ const LayoutDeviceIntPoint& chromeDisp)
+{
+ if (!mRemoteFrame) {
+ return true;
+ }
+
+ mUnscaledOuterRect = rect;
+ mClientOffset = clientOffset;
+ mChromeDisp = chromeDisp;
+
+ mOrientation = orientation;
+ SetUnscaledInnerSize(size);
+ if (!mHasValidInnerSize && size.width != 0 && size.height != 0) {
+ mHasValidInnerSize = true;
+ }
+
+ ScreenIntSize screenSize = GetInnerSize();
+ ScreenIntRect screenRect = GetOuterRect();
+
+ // Set the size on the document viewer before we update the widget and
+ // trigger a reflow. Otherwise the MobileViewportManager reads the stale
+ // size from the content viewer 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 + clientOffset.x + chromeDisp.x,
+ screenRect.y + clientOffset.y + chromeDisp.y,
+ screenSize.width, screenSize.height, true);
+
+ return true;
+}
+
+bool
+TabChild::RecvSizeModeChanged(const nsSizeMode& aSizeMode)
+{
+ mPuppetWidget->SetSizeMode(aSizeMode);
+ if (!mPuppetWidget->IsVisible()) {
+ return true;
+ }
+ nsCOMPtr<nsIDocument> document(GetDocument());
+ nsCOMPtr<nsIPresShell> presShell = document->GetShell();
+ if (presShell) {
+ nsPresContext* presContext = presShell->GetPresContext();
+ if (presContext) {
+ presContext->SizeModeChanged(aSizeMode);
+ }
+ }
+ return true;
+}
+
+bool
+TabChild::UpdateFrame(const FrameMetrics& aFrameMetrics)
+{
+ return TabChildBase::UpdateFrameHandler(aFrameMetrics);
+}
+
+bool
+TabChild::RecvSuppressDisplayport(const bool& aEnabled)
+{
+ if (aEnabled) {
+ mActiveSuppressDisplayport++;
+ } else {
+ mActiveSuppressDisplayport--;
+ }
+
+ MOZ_ASSERT(mActiveSuppressDisplayport >= 0);
+ APZCCallbackHelper::SuppressDisplayport(aEnabled, GetPresShell());
+ return true;
+}
+
+void
+TabChild::HandleDoubleTap(const CSSPoint& aPoint, const Modifiers& aModifiers,
+ const ScrollableLayerGuid& aGuid)
+{
+ TABC_LOG("Handling double tap at %s with %p %p\n",
+ Stringify(aPoint).c_str(), mGlobal.get(), mTabChildGlobal.get());
+
+ if (!mGlobal || !mTabChildGlobal) {
+ return;
+ }
+
+ // Note: there is nothing to do with the modifiers here, as we are not
+ // synthesizing any sort of mouse event.
+ nsCOMPtr<nsIDocument> document = GetDocument();
+ 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);
+ }
+}
+
+bool
+TabChild::RecvHandleTap(const GeckoContentController::TapType& aType,
+ const LayoutDevicePoint& aPoint,
+ const Modifiers& aModifiers,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId)
+{
+ nsCOMPtr<nsIPresShell> presShell = GetPresShell();
+ if (!presShell) {
+ return true;
+ }
+ if (!presShell->GetPresContext()) {
+ return true;
+ }
+ CSSToLayoutDeviceScale scale(presShell->GetPresContext()->CSSToDevPixelScale());
+ CSSPoint point = APZCCallbackHelper::ApplyCallbackTransform(aPoint / scale, aGuid);
+
+ switch (aType) {
+ case GeckoContentController::TapType::eSingleTap:
+ if (mGlobal && mTabChildGlobal) {
+ mAPZEventState->ProcessSingleTap(point, scale, aModifiers, aGuid, 1);
+ }
+ break;
+ case GeckoContentController::TapType::eDoubleTap:
+ HandleDoubleTap(point, aModifiers, aGuid);
+ break;
+ case GeckoContentController::TapType::eSecondTap:
+ if (mGlobal && mTabChildGlobal) {
+ mAPZEventState->ProcessSingleTap(point, scale, aModifiers, aGuid, 2);
+ }
+ break;
+ case GeckoContentController::TapType::eLongTap:
+ if (mGlobal && mTabChildGlobal) {
+ mAPZEventState->ProcessLongTap(presShell, point, scale, aModifiers, aGuid,
+ aInputBlockId);
+ }
+ break;
+ case GeckoContentController::TapType::eLongTapUp:
+ if (mGlobal && mTabChildGlobal) {
+ mAPZEventState->ProcessLongTapUp(presShell, point, scale, aModifiers);
+ }
+ break;
+ case GeckoContentController::TapType::eSentinel:
+ // Should never happen, but we need to handle this case to make the compiler
+ // happy.
+ MOZ_ASSERT(false);
+ break;
+ }
+ return true;
+}
+
+bool
+TabChild::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
+TabChild::StartScrollbarDrag(const layers::AsyncDragMetrics& aDragMetrics)
+{
+ ScrollableLayerGuid guid(mLayersId, aDragMetrics.mPresShellId,
+ aDragMetrics.mViewId);
+
+ if (mApzcTreeManager) {
+ mApzcTreeManager->StartScrollbarDrag(guid, aDragMetrics);
+ }
+}
+
+void
+TabChild::ZoomToRect(const uint32_t& aPresShellId,
+ const FrameMetrics::ViewID& aViewId,
+ const CSSRect& aRect,
+ const uint32_t& aFlags)
+{
+ ScrollableLayerGuid guid(mLayersId, aPresShellId, aViewId);
+
+ if (mApzcTreeManager) {
+ mApzcTreeManager->ZoomToRect(guid, aRect, aFlags);
+ }
+}
+
+bool
+TabChild::RecvActivate()
+{
+ nsCOMPtr<nsIWebBrowserFocus> browser = do_QueryInterface(WebNavigation());
+ browser->Activate();
+ return true;
+}
+
+bool TabChild::RecvDeactivate()
+{
+ nsCOMPtr<nsIWebBrowserFocus> browser = do_QueryInterface(WebNavigation());
+ browser->Deactivate();
+ return true;
+}
+
+bool TabChild::RecvParentActivated(const bool& aActivated)
+{
+ mParentIsActive = aActivated;
+
+ nsFocusManager* fm = nsFocusManager::GetFocusManager();
+ NS_ENSURE_TRUE(fm, true);
+
+ nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation());
+ fm->ParentActivated(window, aActivated);
+ return true;
+}
+
+bool TabChild::RecvSetKeyboardIndicators(const UIStateChangeType& aShowAccelerators,
+ const UIStateChangeType& aShowFocusRings)
+{
+ nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation());
+ NS_ENSURE_TRUE(window, true);
+
+ window->SetKeyboardIndicators(aShowAccelerators, aShowFocusRings);
+ return true;
+}
+
+bool
+TabChild::RecvStopIMEStateManagement()
+{
+ IMEStateManager::StopIMEStateManagement();
+ return true;
+}
+
+bool
+TabChild::RecvMenuKeyboardListenerInstalled(const bool& aInstalled)
+{
+ IMEStateManager::OnInstalledMenuKeyboardListener(aInstalled);
+ return true;
+}
+
+bool
+TabChild::RecvNotifyAttachGroupedSessionHistory(const uint32_t& aOffset)
+{
+ // nsISHistory uses int32_t
+ if (NS_WARN_IF(aOffset > INT32_MAX)) {
+ return false;
+ }
+
+ nsCOMPtr<nsISHistory> shistory;
+ mWebNav->GetSessionHistory(getter_AddRefs(shistory));
+ NS_ENSURE_TRUE(shistory, false);
+
+ return NS_SUCCEEDED(shistory->OnAttachGroupedSessionHistory(aOffset));
+}
+
+bool
+TabChild::RecvNotifyPartialSessionHistoryActive(const uint32_t& aGlobalLength,
+ const uint32_t& aTargetLocalIndex)
+{
+ // nsISHistory uses int32_t
+ if (NS_WARN_IF(aGlobalLength > INT32_MAX || aTargetLocalIndex > INT32_MAX)) {
+ return false;
+ }
+
+ nsCOMPtr<nsISHistory> shistory;
+ mWebNav->GetSessionHistory(getter_AddRefs(shistory));
+ NS_ENSURE_TRUE(shistory, false);
+
+ return NS_SUCCEEDED(shistory->OnPartialSessionHistoryActive(aGlobalLength,
+ aTargetLocalIndex));
+}
+
+bool
+TabChild::RecvNotifyPartialSessionHistoryDeactive()
+{
+ nsCOMPtr<nsISHistory> shistory;
+ mWebNav->GetSessionHistory(getter_AddRefs(shistory));
+ NS_ENSURE_TRUE(shistory, false);
+
+ return NS_SUCCEEDED(shistory->OnPartialSessionHistoryDeactive());
+}
+
+bool
+TabChild::RecvMouseEvent(const nsString& aType,
+ const float& aX,
+ const float& aY,
+ const int32_t& aButton,
+ const int32_t& aClickCount,
+ const int32_t& aModifiers,
+ const bool& aIgnoreRootScrollFrame)
+{
+ APZCCallbackHelper::DispatchMouseEvent(GetPresShell(), aType, CSSPoint(aX, aY),
+ aButton, aClickCount, aModifiers, aIgnoreRootScrollFrame, nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN);
+ return true;
+}
+
+bool
+TabChild::RecvRealMouseMoveEvent(const WidgetMouseEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId)
+{
+ return RecvRealMouseButtonEvent(aEvent, aGuid, aInputBlockId);
+}
+
+bool
+TabChild::RecvSynthMouseMoveEvent(const WidgetMouseEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId)
+{
+ return RecvRealMouseButtonEvent(aEvent, aGuid, aInputBlockId);
+}
+
+bool
+TabChild::RecvRealMouseButtonEvent(const WidgetMouseEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId)
+{
+ // 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.
+ if (aInputBlockId && aEvent.mFlags.mHandledByAPZ) {
+ nsCOMPtr<nsIDocument> document(GetDocument());
+ APZCCallbackHelper::SendSetTargetAPZCNotification(
+ mPuppetWidget, document, aEvent, aGuid, aInputBlockId);
+ }
+
+ nsEventStatus unused;
+ InputAPZContext context(aGuid, aInputBlockId, unused);
+
+ WidgetMouseEvent localEvent(aEvent);
+ localEvent.mWidget = mPuppetWidget;
+ APZCCallbackHelper::ApplyCallbackTransform(localEvent, aGuid,
+ mPuppetWidget->GetDefaultScale());
+ APZCCallbackHelper::DispatchWidgetEvent(localEvent);
+
+ if (aInputBlockId && aEvent.mFlags.mHandledByAPZ) {
+ mAPZEventState->ProcessMouseEvent(aEvent, aGuid, aInputBlockId);
+ }
+ return true;
+}
+
+bool
+TabChild::RecvMouseWheelEvent(const WidgetWheelEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId)
+{
+ if (aInputBlockId && aEvent.mFlags.mHandledByAPZ) {
+ nsCOMPtr<nsIDocument> document(GetDocument());
+ APZCCallbackHelper::SendSetTargetAPZCNotification(
+ mPuppetWidget, document, aEvent, aGuid, aInputBlockId);
+ }
+
+ WidgetWheelEvent localEvent(aEvent);
+ localEvent.mWidget = mPuppetWidget;
+ APZCCallbackHelper::ApplyCallbackTransform(localEvent, aGuid,
+ mPuppetWidget->GetDefaultScale());
+ APZCCallbackHelper::DispatchWidgetEvent(localEvent);
+
+ if (localEvent.mCanTriggerSwipe) {
+ SendRespondStartSwipeEvent(aInputBlockId, localEvent.TriggersSwipe());
+ }
+
+ if (aInputBlockId && aEvent.mFlags.mHandledByAPZ) {
+ mAPZEventState->ProcessWheelEvent(localEvent, aGuid, aInputBlockId);
+ }
+ return true;
+}
+
+bool
+TabChild::RecvRealTouchEvent(const WidgetTouchEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId,
+ const nsEventStatus& aApzResponse)
+{
+ TABC_LOG("Receiving touch event of type %d\n", aEvent.mMessage);
+
+ WidgetTouchEvent localEvent(aEvent);
+ localEvent.mWidget = mPuppetWidget;
+
+ APZCCallbackHelper::ApplyCallbackTransform(localEvent, aGuid,
+ mPuppetWidget->GetDefaultScale());
+
+ if (localEvent.mMessage == eTouchStart && AsyncPanZoomEnabled()) {
+ nsCOMPtr<nsIDocument> document = GetDocument();
+ if (gfxPrefs::TouchActionEnabled()) {
+ APZCCallbackHelper::SendSetAllowedTouchBehaviorNotification(mPuppetWidget,
+ document, localEvent, aInputBlockId, mSetAllowedTouchBehaviorCallback);
+ }
+ APZCCallbackHelper::SendSetTargetAPZCNotification(mPuppetWidget, document,
+ localEvent, aGuid, aInputBlockId);
+ }
+
+ // Dispatch event to content (potentially a long-running operation)
+ nsEventStatus status = APZCCallbackHelper::DispatchWidgetEvent(localEvent);
+
+ if (!AsyncPanZoomEnabled()) {
+ // We shouldn't have any e10s platforms that have touch events enabled
+ // without APZ.
+ MOZ_ASSERT(false);
+ return true;
+ }
+
+ mAPZEventState->ProcessTouchEvent(localEvent, aGuid, aInputBlockId,
+ aApzResponse, status);
+ return true;
+}
+
+bool
+TabChild::RecvRealTouchMoveEvent(const WidgetTouchEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId,
+ const nsEventStatus& aApzResponse)
+{
+ return RecvRealTouchEvent(aEvent, aGuid, aInputBlockId, aApzResponse);
+}
+
+bool
+TabChild::RecvRealDragEvent(const WidgetDragEvent& aEvent,
+ const uint32_t& aDragAction,
+ const uint32_t& aDropEffect)
+{
+ WidgetDragEvent localEvent(aEvent);
+ localEvent.mWidget = mPuppetWidget;
+
+ nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
+ if (dragSession) {
+ dragSession->SetDragAction(aDragAction);
+ nsCOMPtr<nsIDOMDataTransfer> initialDataTransfer;
+ dragSession->GetDataTransfer(getter_AddRefs(initialDataTransfer));
+ 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);
+ }
+ }
+
+ APZCCallbackHelper::DispatchWidgetEvent(localEvent);
+ return true;
+}
+
+bool
+TabChild::RecvPluginEvent(const WidgetPluginEvent& aEvent)
+{
+ WidgetPluginEvent localEvent(aEvent);
+ localEvent.mWidget = mPuppetWidget;
+ nsEventStatus status = APZCCallbackHelper::DispatchWidgetEvent(localEvent);
+ if (status != nsEventStatus_eConsumeNoDefault) {
+ // If not consumed, we should call default action
+ SendDefaultProcOfPluginEvent(aEvent);
+ }
+ return true;
+}
+
+void
+TabChild::RequestNativeKeyBindings(AutoCacheNativeKeyCommands* aAutoCache,
+ const WidgetKeyboardEvent* aEvent)
+{
+ MaybeNativeKeyBinding maybeBindings;
+ if (!SendRequestNativeKeyBindings(*aEvent, &maybeBindings)) {
+ return;
+ }
+
+ if (maybeBindings.type() == MaybeNativeKeyBinding::TNativeKeyBinding) {
+ const NativeKeyBinding& bindings = maybeBindings;
+ aAutoCache->Cache(bindings.singleLineCommands(),
+ bindings.multiLineCommands(),
+ bindings.richTextCommands());
+ } else {
+ aAutoCache->CacheNoCommands();
+ }
+}
+
+bool
+TabChild::RecvNativeSynthesisResponse(const uint64_t& aObserverId,
+ const nsCString& aResponse)
+{
+ mozilla::widget::AutoObserverNotifier::NotifySavedObserver(aObserverId, aResponse.get());
+ return true;
+}
+
+bool
+TabChild::RecvRealKeyEvent(const WidgetKeyboardEvent& event,
+ const MaybeNativeKeyBinding& aBindings)
+{
+ AutoCacheNativeKeyCommands autoCache(mPuppetWidget);
+
+ if (event.mMessage == eKeyPress) {
+ // If content code called preventDefault() on a keydown event, then we don't
+ // want to process any following keypress events.
+ if (mIgnoreKeyPressEvent) {
+ return true;
+ }
+ if (aBindings.type() == MaybeNativeKeyBinding::TNativeKeyBinding) {
+ const NativeKeyBinding& bindings = aBindings;
+ autoCache.Cache(bindings.singleLineCommands(),
+ bindings.multiLineCommands(),
+ bindings.richTextCommands());
+ } else {
+ autoCache.CacheNoCommands();
+ }
+ }
+
+ WidgetKeyboardEvent localEvent(event);
+ localEvent.mWidget = mPuppetWidget;
+ nsEventStatus status = APZCCallbackHelper::DispatchWidgetEvent(localEvent);
+
+ if (event.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 mAccessKeyForwardedToChild is set, then don't resend the key event yet
+ // as RecvHandleAccessKey will do this.
+ if (localEvent.mFlags.mWantReplyFromContentProcess) {
+ SendReplyKeyEvent(localEvent);
+ }
+
+ if (localEvent.mAccessKeyForwardedToChild) {
+ SendAccessKeyNotHandled(localEvent);
+ }
+
+ if (PresShell::BeforeAfterKeyboardEventEnabled()) {
+ SendDispatchAfterKeyboardEvent(localEvent);
+ }
+
+ return true;
+}
+
+bool
+TabChild::RecvKeyEvent(const nsString& aType,
+ const int32_t& aKeyCode,
+ const int32_t& aCharCode,
+ const int32_t& aModifiers,
+ const bool& aPreventDefault)
+{
+ bool ignored = false;
+ nsContentUtils::SendKeyEvent(mPuppetWidget, aType, aKeyCode, aCharCode,
+ aModifiers, aPreventDefault, &ignored);
+ return true;
+}
+
+bool
+TabChild::RecvCompositionEvent(const WidgetCompositionEvent& event)
+{
+ WidgetCompositionEvent localEvent(event);
+ localEvent.mWidget = mPuppetWidget;
+ APZCCallbackHelper::DispatchWidgetEvent(localEvent);
+ Unused << SendOnEventNeedingAckHandled(event.mMessage);
+ return true;
+}
+
+bool
+TabChild::RecvSelectionEvent(const WidgetSelectionEvent& event)
+{
+ WidgetSelectionEvent localEvent(event);
+ localEvent.mWidget = mPuppetWidget;
+ APZCCallbackHelper::DispatchWidgetEvent(localEvent);
+ Unused << SendOnEventNeedingAckHandled(event.mMessage);
+ return true;
+}
+
+bool
+TabChild::RecvPasteTransferable(const IPCDataTransfer& aDataTransfer,
+ const bool& aIsPrivateData,
+ const IPC::Principal& aRequestingPrincipal)
+{
+ nsresult rv;
+ nsCOMPtr<nsITransferable> trans =
+ do_CreateInstance("@mozilla.org/widget/transferable;1", &rv);
+ NS_ENSURE_SUCCESS(rv, true);
+ trans->Init(nullptr);
+
+ rv = nsContentUtils::IPCTransferableToTransferable(aDataTransfer,
+ aIsPrivateData,
+ aRequestingPrincipal,
+ trans, nullptr, this);
+ NS_ENSURE_SUCCESS(rv, true);
+
+ nsCOMPtr<nsIDocShell> ourDocShell = do_GetInterface(WebNavigation());
+ if (NS_WARN_IF(!ourDocShell)) {
+ return true;
+ }
+
+ nsCOMPtr<nsICommandParams> params = do_CreateInstance("@mozilla.org/embedcomp/command-params;1", &rv);
+ NS_ENSURE_SUCCESS(rv, true);
+
+ rv = params->SetISupportsValue("transferable", trans);
+ NS_ENSURE_SUCCESS(rv, true);
+
+ ourDocShell->DoCommandWithParams("cmd_pasteTransferable", params);
+ return true;
+}
+
+
+a11y::PDocAccessibleChild*
+TabChild::AllocPDocAccessibleChild(PDocAccessibleChild*, const uint64_t&,
+ const uint32_t&, const IAccessibleHolder&)
+{
+ MOZ_ASSERT(false, "should never call this!");
+ return nullptr;
+}
+
+bool
+TabChild::DeallocPDocAccessibleChild(a11y::PDocAccessibleChild* aChild)
+{
+#ifdef ACCESSIBILITY
+ delete static_cast<mozilla::a11y::DocAccessibleChild*>(aChild);
+#endif
+ return true;
+}
+
+PDocumentRendererChild*
+TabChild::AllocPDocumentRendererChild(const nsRect& documentRect,
+ const mozilla::gfx::Matrix& transform,
+ const nsString& bgcolor,
+ const uint32_t& renderFlags,
+ const bool& flushLayout,
+ const nsIntSize& renderSize)
+{
+ return new DocumentRendererChild();
+}
+
+bool
+TabChild::DeallocPDocumentRendererChild(PDocumentRendererChild* actor)
+{
+ delete actor;
+ return true;
+}
+
+bool
+TabChild::RecvPDocumentRendererConstructor(PDocumentRendererChild* actor,
+ const nsRect& documentRect,
+ const mozilla::gfx::Matrix& transform,
+ const nsString& bgcolor,
+ const uint32_t& renderFlags,
+ const bool& flushLayout,
+ const nsIntSize& renderSize)
+{
+ DocumentRendererChild *render = static_cast<DocumentRendererChild *>(actor);
+
+ nsCOMPtr<nsIWebBrowser> browser = do_QueryInterface(WebNavigation());
+ if (!browser)
+ return true; // silently ignore
+ nsCOMPtr<mozIDOMWindowProxy> window;
+ if (NS_FAILED(browser->GetContentDOMWindow(getter_AddRefs(window))) ||
+ !window)
+ {
+ return true; // silently ignore
+ }
+
+ nsCString data;
+ bool ret = render->RenderDocument(nsPIDOMWindowOuter::From(window),
+ documentRect, transform,
+ bgcolor,
+ renderFlags, flushLayout,
+ renderSize, data);
+ if (!ret)
+ return true; // silently ignore
+
+ return PDocumentRendererChild::Send__delete__(actor, renderSize, data);
+}
+
+PColorPickerChild*
+TabChild::AllocPColorPickerChild(const nsString&, const nsString&)
+{
+ NS_RUNTIMEABORT("unused");
+ return nullptr;
+}
+
+bool
+TabChild::DeallocPColorPickerChild(PColorPickerChild* aColorPicker)
+{
+ nsColorPickerProxy* picker = static_cast<nsColorPickerProxy*>(aColorPicker);
+ NS_RELEASE(picker);
+ return true;
+}
+
+PDatePickerChild*
+TabChild::AllocPDatePickerChild(const nsString&, const nsString&)
+{
+ NS_RUNTIMEABORT("unused");
+ return nullptr;
+}
+
+bool
+TabChild::DeallocPDatePickerChild(PDatePickerChild* aDatePicker)
+{
+ nsDatePickerProxy* picker = static_cast<nsDatePickerProxy*>(aDatePicker);
+ NS_RELEASE(picker);
+ return true;
+}
+
+PFilePickerChild*
+TabChild::AllocPFilePickerChild(const nsString&, const int16_t&)
+{
+ NS_RUNTIMEABORT("unused");
+ return nullptr;
+}
+
+bool
+TabChild::DeallocPFilePickerChild(PFilePickerChild* actor)
+{
+ nsFilePickerProxy* filePicker = static_cast<nsFilePickerProxy*>(actor);
+ NS_RELEASE(filePicker);
+ return true;
+}
+
+auto
+TabChild::AllocPIndexedDBPermissionRequestChild(const Principal& aPrincipal)
+ -> PIndexedDBPermissionRequestChild*
+{
+ MOZ_CRASH("PIndexedDBPermissionRequestChild actors should always be created "
+ "manually!");
+}
+
+bool
+TabChild::DeallocPIndexedDBPermissionRequestChild(
+ PIndexedDBPermissionRequestChild* aActor)
+{
+ MOZ_ASSERT(aActor);
+ delete aActor;
+ return true;
+}
+
+bool
+TabChild::RecvActivateFrameEvent(const nsString& aType, const bool& capture)
+{
+ nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation());
+ NS_ENSURE_TRUE(window, true);
+ nsCOMPtr<EventTarget> chromeHandler =
+ do_QueryInterface(window->GetChromeEventHandler());
+ NS_ENSURE_TRUE(chromeHandler, true);
+ RefPtr<ContentListener> listener = new ContentListener(this);
+ chromeHandler->AddEventListener(aType, listener, capture);
+ return true;
+}
+
+bool
+TabChild::RecvLoadRemoteScript(const nsString& aURL, const bool& aRunInGlobalScope)
+{
+ if (!mGlobal && !InitTabChildGlobal())
+ // This can happen if we're half-destroyed. It's not a fatal
+ // error.
+ return true;
+
+ LoadScriptInternal(aURL, aRunInGlobalScope);
+ return true;
+}
+
+bool
+TabChild::RecvAsyncMessage(const nsString& aMessage,
+ InfallibleTArray<CpowEntry>&& aCpows,
+ const IPC::Principal& aPrincipal,
+ const ClonedMessageData& aData)
+{
+ if (!mTabChildGlobal) {
+ return true;
+ }
+
+ // 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(mTabChildGlobal->mMessageManager);
+ if (!mTabChildGlobal->mMessageManager) {
+ return true;
+ }
+
+ nsCOMPtr<nsIXPConnectJSObjectHolder> kungFuDeathGrip(GetGlobal());
+ StructuredCloneData data;
+ UnpackClonedMessageDataForChild(aData, data);
+ RefPtr<nsFrameMessageManager> mm =
+ static_cast<nsFrameMessageManager*>(mTabChildGlobal->mMessageManager.get());
+ CrossProcessCpowHolder cpows(Manager(), aCpows);
+ mm->ReceiveMessage(static_cast<EventTarget*>(mTabChildGlobal), nullptr,
+ aMessage, false, &data, &cpows, aPrincipal, nullptr);
+ return true;
+}
+
+bool
+TabChild::RecvSwappedWithOtherRemoteLoader(const IPCTabContext& aContext)
+{
+ nsCOMPtr<nsIDocShell> ourDocShell = do_GetInterface(WebNavigation());
+ if (NS_WARN_IF(!ourDocShell)) {
+ return true;
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> ourWindow = ourDocShell->GetWindow();
+ if (NS_WARN_IF(!ourWindow)) {
+ return true;
+ }
+
+ RefPtr<nsDocShell> docShell = static_cast<nsDocShell*>(ourDocShell.get());
+
+ nsCOMPtr<EventTarget> ourEventTarget = ourWindow->GetParentTarget();
+
+ docShell->SetInFrameSwap(true);
+
+ nsContentUtils::FirePageShowEvent(ourDocShell, ourEventTarget, false);
+ nsContentUtils::FirePageHideEvent(ourDocShell, ourEventTarget);
+
+ // 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.");
+ }
+
+ // Since mIsMozBrowserElement may change in UpdateTabContextAfterSwap, so we
+ // call UpdateFrameType here to make sure the frameType on the docshell is
+ // correct.
+ UpdateFrameType();
+
+ // Ignore previous value of mTriedBrowserInit since owner content has changed.
+ mTriedBrowserInit = true;
+ // Initialize the child side of the browser element machinery, if appropriate.
+ if (IsMozBrowserOrApp()) {
+ RecvLoadRemoteScript(BROWSER_ELEMENT_CHILD_SCRIPT, true);
+ }
+
+ nsContentUtils::FirePageShowEvent(ourDocShell, ourEventTarget, true);
+
+ docShell->SetInFrameSwap(false);
+
+ return true;
+}
+
+bool
+TabChild::RecvHandleAccessKey(const WidgetKeyboardEvent& aEvent,
+ nsTArray<uint32_t>&& aCharCodes,
+ const int32_t& aModifierMask)
+{
+ nsCOMPtr<nsIDocument> document(GetDocument());
+ nsCOMPtr<nsIPresShell> presShell = document->GetShell();
+ if (presShell) {
+ nsPresContext* pc = presShell->GetPresContext();
+ if (pc) {
+ if (!pc->EventStateManager()->
+ HandleAccessKey(&(const_cast<WidgetKeyboardEvent&>(aEvent)),
+ pc, aCharCodes,
+ aModifierMask, true)) {
+ // 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 true;
+}
+
+bool
+TabChild::RecvAudioChannelChangeNotification(const uint32_t& aAudioChannel,
+ const float& aVolume,
+ const bool& aMuted)
+{
+ nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation());
+ if (window) {
+ RefPtr<AudioChannelService> service = AudioChannelService::GetOrCreate();
+ MOZ_ASSERT(service);
+
+ service->SetAudioChannelVolume(window,
+ static_cast<AudioChannel>(aAudioChannel),
+ aVolume);
+ service->SetAudioChannelMuted(window,
+ static_cast<AudioChannel>(aAudioChannel),
+ aMuted);
+ }
+
+ return true;
+}
+
+bool
+TabChild::RecvSetUseGlobalHistory(const bool& aUse)
+{
+ nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
+ MOZ_ASSERT(docShell);
+
+ nsresult rv = docShell->SetUseGlobalHistory(aUse);
+ if (NS_FAILED(rv)) {
+ NS_WARNING("Failed to set UseGlobalHistory on TabChild docShell");
+ }
+
+ return true;
+}
+
+bool
+TabChild::RecvPrint(const uint64_t& aOuterWindowID, const PrintData& aPrintData)
+{
+#ifdef NS_PRINTING
+ nsGlobalWindow* outerWindow =
+ nsGlobalWindow::GetOuterWindowWithId(aOuterWindowID);
+ if (NS_WARN_IF(!outerWindow)) {
+ return true;
+ }
+
+ nsCOMPtr<nsIWebBrowserPrint> webBrowserPrint =
+ do_GetInterface(outerWindow->AsOuter());
+ if (NS_WARN_IF(!webBrowserPrint)) {
+ return true;
+ }
+
+ nsCOMPtr<nsIPrintSettingsService> printSettingsSvc =
+ do_GetService("@mozilla.org/gfx/printsettings-service;1");
+ if (NS_WARN_IF(!printSettingsSvc)) {
+ return true;
+ }
+
+ nsCOMPtr<nsIPrintSettings> printSettings;
+ nsresult rv =
+ printSettingsSvc->GetNewPrintSettings(getter_AddRefs(printSettings));
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return true;
+ }
+
+ nsCOMPtr<nsIPrintSession> printSession =
+ do_CreateInstance("@mozilla.org/gfx/printsession;1", &rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return true;
+ }
+
+ printSettings->SetPrintSession(printSession);
+ printSettingsSvc->DeserializeToPrintSettings(aPrintData, printSettings);
+ rv = webBrowserPrint->Print(printSettings, nullptr);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return true;
+ }
+
+#endif
+ return true;
+}
+
+bool
+TabChild::RecvUpdateNativeWindowHandle(const uintptr_t& aNewHandle)
+{
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+ mNativeWindowHandle = aNewHandle;
+ return true;
+#else
+ return false;
+#endif
+}
+
+bool
+TabChild::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();
+ }
+
+ while (mActiveSuppressDisplayport > 0) {
+ APZCCallbackHelper::SuppressDisplayport(false, nullptr);
+ mActiveSuppressDisplayport--;
+ }
+
+ if (mTabChildGlobal) {
+ // Message handlers are called from the event loop, so it better be safe to
+ // run script.
+ MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
+ mTabChildGlobal->DispatchTrustedEvent(NS_LITERAL_STRING("unload"));
+ }
+
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+
+ observerService->RemoveObserver(this, BEFORE_FIRST_PAINT);
+
+ const nsAttrValue::EnumTable* table =
+ AudioChannelService::GetAudioChannelTable();
+
+ nsAutoCString topic;
+ for (uint32_t i = 0; table[i].tag; ++i) {
+ topic.Assign("audiochannel-activity-");
+ topic.Append(table[i].tag);
+
+ observerService->RemoveObserver(this, topic.get());
+ }
+
+ // XXX what other code in ~TabChild() 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 true;
+}
+
+bool
+TabChild::RecvSetDocShellIsActive(const bool& aIsActive,
+ const bool& aPreserveLayers,
+ const uint64_t& aLayerObserverEpoch)
+{
+ // Since SetDocShellIsActive requests 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 (mLayerObserverEpoch >= aLayerObserverEpoch) {
+ return true;
+ }
+ mLayerObserverEpoch = aLayerObserverEpoch;
+
+ MOZ_ASSERT(mPuppetWidget);
+ MOZ_ASSERT(mPuppetWidget->GetLayerManager());
+ MOZ_ASSERT(mPuppetWidget->GetLayerManager()->GetBackendType() ==
+ LayersBackend::LAYERS_CLIENT);
+
+ auto clearForcePaint = 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 (aIsActive) {
+ ProcessHangMonitor::ClearForcePaint();
+ }
+ });
+
+ // We send the current layer observer epoch to the compositor so that
+ // TabParent knows whether a layer update notification corresponds to the
+ // latest SetDocShellIsActive request that was made.
+ if (ClientLayerManager* clm = mPuppetWidget->GetLayerManager()->AsClientLayerManager()) {
+ clm->SetLayerObserverEpoch(aLayerObserverEpoch);
+ }
+
+ // docshell is consider prerendered only if not active yet
+ mIsPrerendered &= !aIsActive;
+ nsCOMPtr<nsIDocShell> docShell = do_GetInterface(WebNavigation());
+ if (docShell) {
+ bool wasActive;
+ docShell->GetIsActive(&wasActive);
+ if (aIsActive && wasActive) {
+ // 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). ForcePaintNoOp does that.
+ if (IPCOpen()) {
+ Unused << SendForcePaintNoOp(aLayerObserverEpoch);
+ return true;
+ }
+ }
+
+ docShell->SetIsActive(aIsActive);
+ }
+
+ if (aIsActive) {
+ MakeVisible();
+
+ // We don't use TabChildBase::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 (nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell()) {
+ if (nsIFrame* root = presShell->FrameConstructor()->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.
+ APZCCallbackHelper::SuppressDisplayport(true, presShell);
+ if (nsContentUtils::IsSafeToRunScript()) {
+ WebWidget()->PaintNowIfNeeded();
+ } else {
+ RefPtr<nsViewManager> vm = presShell->GetViewManager();
+ if (nsView* view = vm->GetRootView()) {
+ presShell->Paint(view, view->GetBounds(),
+ nsIPresShell::PAINT_LAYERS);
+ }
+ }
+ APZCCallbackHelper::SuppressDisplayport(false, presShell);
+ }
+ } else if (!aPreserveLayers) {
+ MakeHidden();
+ }
+
+ return true;
+}
+
+bool
+TabChild::RecvNavigateByKey(const bool& aForward, const bool& aForDocumentNavigation)
+{
+ nsIFocusManager* fm = nsFocusManager::GetFocusManager();
+ if (fm) {
+ nsCOMPtr<nsIDOMElement> 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));
+ fm->MoveFocus(window, nullptr, type,
+ nsIFocusManager::FLAG_BYKEY, 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);
+ }
+
+ return true;
+}
+
+bool
+TabChild::RecvHandledWindowedPluginKeyEvent(
+ const NativeEventData& aKeyEventData,
+ const bool& aIsConsumed)
+{
+ if (NS_WARN_IF(!mPuppetWidget)) {
+ return true;
+ }
+ mPuppetWidget->HandledWindowedPluginKeyEvent(aKeyEventData, aIsConsumed);
+ return true;
+}
+
+PRenderFrameChild*
+TabChild::AllocPRenderFrameChild()
+{
+ return new RenderFrameChild();
+}
+
+bool
+TabChild::DeallocPRenderFrameChild(PRenderFrameChild* aFrame)
+{
+ delete aFrame;
+ return true;
+}
+
+bool
+TabChild::InitTabChildGlobal(FrameScriptLoading aScriptLoading)
+{
+ if (!mGlobal && !mTabChildGlobal) {
+ nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(WebNavigation());
+ NS_ENSURE_TRUE(window, false);
+ nsCOMPtr<EventTarget> chromeHandler =
+ do_QueryInterface(window->GetChromeEventHandler());
+ NS_ENSURE_TRUE(chromeHandler, false);
+
+ RefPtr<TabChildGlobal> scope = new TabChildGlobal(this);
+ mTabChildGlobal = scope;
+
+ nsISupports* scopeSupports = NS_ISUPPORTS_CAST(EventTarget*, scope);
+
+ NS_NAMED_LITERAL_CSTRING(globalId, "outOfProcessTabChildGlobal");
+ NS_ENSURE_TRUE(InitChildGlobalInternal(scopeSupports, globalId), false);
+
+ scope->Init();
+
+ nsCOMPtr<nsPIWindowRoot> root = do_QueryInterface(chromeHandler);
+ NS_ENSURE_TRUE(root, false);
+ root->SetParentTarget(scope);
+ }
+
+ if (aScriptLoading != DONT_LOAD_SCRIPTS && !mTriedBrowserInit) {
+ mTriedBrowserInit = true;
+ // Initialize the child side of the browser element machinery,
+ // if appropriate.
+ if (IsMozBrowserOrApp()) {
+ RecvLoadRemoteScript(BROWSER_ELEMENT_CHILD_SCRIPT, true);
+ }
+ }
+
+ return true;
+}
+
+bool
+TabChild::InitRenderingState(const TextureFactoryIdentifier& aTextureFactoryIdentifier,
+ const uint64_t& aLayersId,
+ PRenderFrameChild* aRenderFrame)
+{
+ mPuppetWidget->InitIMEState();
+
+ RenderFrameChild* remoteFrame = static_cast<RenderFrameChild*>(aRenderFrame);
+ if (!remoteFrame) {
+ NS_WARNING("failed to construct RenderFrame");
+ return false;
+ }
+
+ MOZ_ASSERT(aLayersId != 0);
+ mTextureFactoryIdentifier = aTextureFactoryIdentifier;
+
+ // Pushing layers transactions directly to a separate
+ // compositor context.
+ PCompositorBridgeChild* compositorChild = CompositorBridgeChild::Get();
+ if (!compositorChild) {
+ NS_WARNING("failed to get CompositorBridgeChild instance");
+ PRenderFrameChild::Send__delete__(remoteFrame);
+ return false;
+ }
+
+ ShadowLayerForwarder* lf =
+ mPuppetWidget->GetLayerManager(
+ nullptr, mTextureFactoryIdentifier.mParentBackend)
+ ->AsShadowForwarder();
+ // As long as we are creating a ClientLayerManager for the puppet widget,
+ // lf must be non-null here.
+ MOZ_ASSERT(lf);
+
+ if (lf) {
+ nsTArray<LayersBackend> backends;
+ backends.AppendElement(mTextureFactoryIdentifier.mParentBackend);
+ bool success;
+ PLayerTransactionChild* shadowManager =
+ compositorChild->SendPLayerTransactionConstructor(backends,
+ aLayersId, &mTextureFactoryIdentifier, &success);
+ if (!success) {
+ NS_WARNING("failed to properly allocate layer transaction");
+ PRenderFrameChild::Send__delete__(remoteFrame);
+ return false;
+ }
+
+ if (!shadowManager) {
+ NS_WARNING("failed to construct LayersChild");
+ // This results in |remoteFrame| being deleted.
+ PRenderFrameChild::Send__delete__(remoteFrame);
+ return false;
+ }
+
+ lf->SetShadowManager(shadowManager);
+ lf->IdentifyTextureHost(mTextureFactoryIdentifier);
+ ImageBridgeChild::IdentifyCompositorTextureHost(mTextureFactoryIdentifier);
+ gfx::VRManagerChild::IdentifyTextureHost(mTextureFactoryIdentifier);
+ }
+
+ mRemoteFrame = remoteFrame;
+ if (aLayersId != 0) {
+ if (!sTabChildren) {
+ sTabChildren = new TabChildMap;
+ }
+ MOZ_ASSERT(!sTabChildren->Get(aLayersId));
+ sTabChildren->Put(aLayersId, this);
+ mLayersId = aLayersId;
+ }
+
+ mApzcTreeManager = CompositorBridgeChild::Get()->GetAPZCTreeManager(mLayersId);
+
+ nsCOMPtr<nsIObserverService> observerService =
+ mozilla::services::GetObserverService();
+
+ if (observerService) {
+ observerService->AddObserver(this,
+ BEFORE_FIRST_PAINT,
+ false);
+ }
+ return true;
+}
+
+void
+TabChild::GetDPI(float* aDPI)
+{
+ *aDPI = -1.0;
+ if (!mRemoteFrame) {
+ return;
+ }
+
+ if (mDPI > 0) {
+ *aDPI = mDPI;
+ return;
+ }
+
+ // Fallback to a sync call if needed.
+ SendGetDPI(aDPI);
+}
+
+void
+TabChild::GetDefaultScale(double* aScale)
+{
+ *aScale = -1.0;
+ if (!mRemoteFrame) {
+ return;
+ }
+
+ if (mDefaultScale > 0) {
+ *aScale = mDefaultScale;
+ return;
+ }
+
+ // Fallback to a sync call if needed.
+ SendGetDefaultScale(aScale);
+}
+
+void
+TabChild::GetWidgetRounding(int32_t* aRounding)
+{
+ *aRounding = 1;
+ if (!mRemoteFrame) {
+ return;
+ }
+ if (mRounding > 0) {
+ *aRounding = mRounding;
+ return;
+ }
+
+ // Fallback to a sync call if needed.
+ SendGetWidgetRounding(aRounding);
+}
+
+void
+TabChild::GetMaxTouchPoints(uint32_t* aTouchPoints)
+{
+ // Fallback to a sync call.
+ SendGetMaxTouchPoints(aTouchPoints);
+}
+
+void
+TabChild::NotifyPainted()
+{
+ if (!mNotified) {
+ mRemoteFrame->SendNotifyCompositorTransaction();
+ mNotified = true;
+ }
+}
+
+void
+TabChild::MakeVisible()
+{
+ if (mPuppetWidget && mPuppetWidget->IsVisible()) {
+ return;
+ }
+
+ if (mPuppetWidget) {
+ mPuppetWidget->Show(true);
+ }
+}
+
+void
+TabChild::MakeHidden()
+{
+ if (mPuppetWidget && !mPuppetWidget->IsVisible()) {
+ return;
+ }
+
+ CompositorBridgeChild* compositor = CompositorBridgeChild::Get();
+
+ // Clear cached resources directly. This avoids one extra IPC
+ // round-trip from CompositorBridgeChild to CompositorBridgeParent.
+ compositor->RecvClearCachedResources(mLayersId);
+
+ if (mPuppetWidget) {
+ mPuppetWidget->Show(false);
+ }
+}
+
+NS_IMETHODIMP
+TabChild::GetMessageManager(nsIContentFrameMessageManager** aResult)
+{
+ if (mTabChildGlobal) {
+ NS_ADDREF(*aResult = mTabChildGlobal);
+ return NS_OK;
+ }
+ *aResult = nullptr;
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+TabChild::GetWebBrowserChrome(nsIWebBrowserChrome3** aWebBrowserChrome)
+{
+ NS_IF_ADDREF(*aWebBrowserChrome = mWebBrowserChrome);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+TabChild::SetWebBrowserChrome(nsIWebBrowserChrome3* aWebBrowserChrome)
+{
+ mWebBrowserChrome = aWebBrowserChrome;
+ return NS_OK;
+}
+
+void
+TabChild::SendRequestFocus(bool aCanFocus)
+{
+ PBrowserChild::SendRequestFocus(aCanFocus);
+}
+
+void
+TabChild::SendGetTabCount(uint32_t* tabCount)
+{
+ PBrowserChild::SendGetTabCount(tabCount);
+}
+
+void
+TabChild::EnableDisableCommands(const nsAString& aAction,
+ nsTArray<nsCString>& aEnabledCommands,
+ nsTArray<nsCString>& aDisabledCommands)
+{
+ PBrowserChild::SendEnableDisableCommands(PromiseFlatString(aAction),
+ aEnabledCommands, aDisabledCommands);
+}
+
+NS_IMETHODIMP
+TabChild::GetTabId(uint64_t* aId)
+{
+ *aId = GetTabId();
+ return NS_OK;
+}
+
+void
+TabChild::SetTabId(const TabId& aTabId)
+{
+ MOZ_ASSERT(mUniqueId == 0);
+
+ mUniqueId = aTabId;
+ NestedTabChildMap()[mUniqueId] = this;
+}
+
+bool
+TabChild::DoSendBlockingMessage(JSContext* aCx,
+ const nsAString& aMessage,
+ StructuredCloneData& aData,
+ JS::Handle<JSObject *> aCpows,
+ nsIPrincipal* aPrincipal,
+ nsTArray<StructuredCloneData>* aRetVal,
+ bool aIsSync)
+{
+ ClonedMessageData data;
+ if (!BuildClonedMessageDataForChild(Manager(), aData, data)) {
+ return false;
+ }
+ InfallibleTArray<CpowEntry> cpows;
+ if (aCpows && !Manager()->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) {
+ return false;
+ }
+ if (aIsSync) {
+ return SendSyncMessage(PromiseFlatString(aMessage), data, cpows,
+ Principal(aPrincipal), aRetVal);
+ }
+
+ return SendRpcMessage(PromiseFlatString(aMessage), data, cpows,
+ Principal(aPrincipal), aRetVal);
+}
+
+nsresult
+TabChild::DoSendAsyncMessage(JSContext* aCx,
+ const nsAString& aMessage,
+ StructuredCloneData& aData,
+ JS::Handle<JSObject *> aCpows,
+ nsIPrincipal* aPrincipal)
+{
+ ClonedMessageData data;
+ if (!BuildClonedMessageDataForChild(Manager(), aData, data)) {
+ return NS_ERROR_DOM_DATA_CLONE_ERR;
+ }
+ InfallibleTArray<CpowEntry> cpows;
+ if (aCpows && !Manager()->GetCPOWManager()->Wrap(aCx, aCpows, &cpows)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ if (!SendAsyncMessage(PromiseFlatString(aMessage), cpows,
+ Principal(aPrincipal), data)) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ return NS_OK;
+}
+
+/* static */ nsTArray<RefPtr<TabChild>>
+TabChild::GetAll()
+{
+ nsTArray<RefPtr<TabChild>> list;
+ if (!sTabChildren) {
+ return list;
+ }
+
+ for (auto iter = sTabChildren->Iter(); !iter.Done(); iter.Next()) {
+ list.AppendElement(iter.Data());
+ }
+
+ return list;
+}
+
+TabChild*
+TabChild::GetFrom(nsIPresShell* aPresShell)
+{
+ nsIDocument* doc = aPresShell->GetDocument();
+ if (!doc) {
+ return nullptr;
+ }
+ nsCOMPtr<nsIDocShell> docShell(doc->GetDocShell());
+ return GetFrom(docShell);
+}
+
+TabChild*
+TabChild::GetFrom(uint64_t aLayersId)
+{
+ if (!sTabChildren) {
+ return nullptr;
+ }
+ return sTabChildren->Get(aLayersId);
+}
+
+void
+TabChild::DidComposite(uint64_t aTransactionId,
+ const TimeStamp& aCompositeStart,
+ const TimeStamp& aCompositeEnd)
+{
+ MOZ_ASSERT(mPuppetWidget);
+ MOZ_ASSERT(mPuppetWidget->GetLayerManager());
+ MOZ_ASSERT(mPuppetWidget->GetLayerManager()->GetBackendType() ==
+ LayersBackend::LAYERS_CLIENT);
+
+ RefPtr<ClientLayerManager> manager = mPuppetWidget->GetLayerManager()->AsClientLayerManager();
+
+ manager->DidComposite(aTransactionId, aCompositeStart, aCompositeEnd);
+}
+
+void
+TabChild::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
+TabChild::ClearCachedResources()
+{
+ MOZ_ASSERT(mPuppetWidget);
+ MOZ_ASSERT(mPuppetWidget->GetLayerManager());
+ MOZ_ASSERT(mPuppetWidget->GetLayerManager()->GetBackendType() ==
+ LayersBackend::LAYERS_CLIENT);
+
+ ClientLayerManager *manager = mPuppetWidget->GetLayerManager()->AsClientLayerManager();
+ manager->ClearCachedResources();
+}
+
+void
+TabChild::InvalidateLayers()
+{
+ MOZ_ASSERT(mPuppetWidget);
+ MOZ_ASSERT(mPuppetWidget->GetLayerManager());
+ MOZ_ASSERT(mPuppetWidget->GetLayerManager()->GetBackendType() ==
+ LayersBackend::LAYERS_CLIENT);
+
+ RefPtr<LayerManager> lm = mPuppetWidget->GetLayerManager();
+ FrameLayerBuilder::InvalidateAllLayers(lm);
+}
+
+void
+TabChild::ReinitRendering()
+{
+ MOZ_ASSERT(mLayersId);
+
+ // 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 TabParent before TabChild is given rendering
+ // information.
+ //
+ // In this case, we will send a sync message to our TabParent, 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.
+ SendEnsureLayersConnected();
+
+ RefPtr<CompositorBridgeChild> cb = CompositorBridgeChild::Get();
+
+ bool success;
+ nsTArray<LayersBackend> ignored;
+ PLayerTransactionChild* shadowManager =
+ cb->SendPLayerTransactionConstructor(ignored, LayersId(), &mTextureFactoryIdentifier, &success);
+ if (!success) {
+ NS_WARNING("failed to re-allocate layer transaction");
+ return;
+ }
+
+ if (!shadowManager) {
+ NS_WARNING("failed to re-construct LayersChild");
+ return;
+ }
+
+ RefPtr<LayerManager> lm = mPuppetWidget->RecreateLayerManager(shadowManager);
+ ShadowLayerForwarder* lf = lm->AsShadowForwarder();
+ lf->IdentifyTextureHost(mTextureFactoryIdentifier);
+
+ mApzcTreeManager = CompositorBridgeChild::Get()->GetAPZCTreeManager(mLayersId);
+ if (mApzcTreeManager) {
+ APZChild* apz = ContentProcessController::Create(mUniqueId);
+ CompositorBridgeChild::Get()->SendPAPZConstructor(apz, mLayersId);
+ }
+
+ nsCOMPtr<nsIDocument> doc(GetDocument());
+ doc->NotifyLayerManagerRecreated();
+}
+
+void
+TabChild::CompositorUpdated(const TextureFactoryIdentifier& aNewIdentifier)
+{
+ gfxPlatform::GetPlatform()->CompositorUpdated();
+
+ RefPtr<LayerManager> lm = mPuppetWidget->GetLayerManager();
+ ClientLayerManager* clm = lm->AsClientLayerManager();
+ MOZ_ASSERT(clm);
+
+ mTextureFactoryIdentifier = aNewIdentifier;
+ clm->UpdateTextureFactoryIdentifier(aNewIdentifier);
+ FrameLayerBuilder::InvalidateAllLayers(clm);
+}
+
+NS_IMETHODIMP
+TabChild::OnShowTooltip(int32_t aXCoords, int32_t aYCoords, const char16_t *aTipText,
+ const char16_t *aTipDir)
+{
+ nsString str(aTipText);
+ nsString dir(aTipDir);
+ SendShowTooltip(aXCoords, aYCoords, str, dir);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+TabChild::OnHideTooltip()
+{
+ SendHideTooltip();
+ return NS_OK;
+}
+
+bool
+TabChild::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
+ // RenderFrameParent.
+ compositor->RequestNotifyAfterRemotePaint(this);
+ return true;
+}
+
+bool
+TabChild::RecvUIResolutionChanged(const float& aDpi,
+ const int32_t& aRounding,
+ const double& aScale)
+{
+ ScreenIntSize oldScreenSize = GetInnerSize();
+ mDPI = 0;
+ mRounding = 0;
+ mDefaultScale = 0;
+ static_cast<PuppetWidget*>(mPuppetWidget.get())->UpdateBackingScaleCache(aDpi, aRounding, aScale);
+ nsCOMPtr<nsIDocument> document(GetDocument());
+ nsCOMPtr<nsIPresShell> presShell = document->GetShell();
+ if (presShell) {
+ RefPtr<nsPresContext> presContext = presShell->GetPresContext();
+ if (presContext) {
+ presContext->UIResolutionChangedSync();
+ }
+ }
+
+ ScreenIntSize screenSize = GetInnerSize();
+ if (mHasValidInnerSize && oldScreenSize != screenSize) {
+ ScreenIntRect screenRect = GetOuterRect();
+ mPuppetWidget->Resize(screenRect.x + mClientOffset.x + mChromeDisp.x,
+ screenRect.y + mClientOffset.y + mChromeDisp.y,
+ screenSize.width, screenSize.height, true);
+
+ nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(WebNavigation());
+ baseWin->SetPositionAndSize(0, 0, screenSize.width, screenSize.height,
+ nsIBaseWindow::eRepaint);
+ }
+
+ return true;
+}
+
+bool
+TabChild::RecvThemeChanged(nsTArray<LookAndFeelInt>&& aLookAndFeelIntCache)
+{
+ LookAndFeel::SetIntCache(aLookAndFeelIntCache);
+ nsCOMPtr<nsIDocument> document(GetDocument());
+ nsCOMPtr<nsIPresShell> presShell = document->GetShell();
+ if (presShell) {
+ RefPtr<nsPresContext> presContext = presShell->GetPresContext();
+ if (presContext) {
+ presContext->ThemeChanged();
+ }
+ }
+ return true;
+}
+
+bool
+TabChild::RecvSetFreshProcess()
+{
+ mIsFreshProcess = true;
+ return true;
+}
+
+mozilla::plugins::PPluginWidgetChild*
+TabChild::AllocPPluginWidgetChild()
+{
+ return new mozilla::plugins::PluginWidgetChild();
+}
+
+bool
+TabChild::DeallocPPluginWidgetChild(mozilla::plugins::PPluginWidgetChild* aActor)
+{
+ delete aActor;
+ return true;
+}
+
+nsresult
+TabChild::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.mUnicode = false;
+ 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;
+}
+
+ScreenIntSize
+TabChild::GetInnerSize()
+{
+ LayoutDeviceIntSize innerSize =
+ RoundedToInt(mUnscaledInnerSize * mPuppetWidget->GetDefaultScale());
+ return ViewAs<ScreenPixel>(innerSize, PixelCastJustification::LayoutDeviceIsScreenForTabDims);
+};
+
+ScreenIntRect
+TabChild::GetOuterRect()
+{
+ LayoutDeviceIntRect outerRect =
+ RoundedToInt(mUnscaledOuterRect * mPuppetWidget->GetDefaultScale());
+ return ViewAs<ScreenPixel>(outerRect, PixelCastJustification::LayoutDeviceIsScreenForTabDims);
+}
+
+void
+TabChild::ForcePaint(uint64_t aLayerObserverEpoch)
+{
+ if (!IPCOpen()) {
+ // Don't bother doing anything now. Better to wait until we receive the
+ // message on the PContent channel.
+ return;
+ }
+
+ nsAutoScriptBlocker scriptBlocker;
+ RecvSetDocShellIsActive(true, false, aLayerObserverEpoch);
+}
+
+/*******************************************************************************
+ * nsISHistoryListener
+ ******************************************************************************/
+
+NS_IMETHODIMP
+TabChildSHistoryListener::OnHistoryNewEntry(nsIURI *aNewURI, int32_t aOldIndex)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TabChildSHistoryListener::OnHistoryGoBack(nsIURI *aBackURI, bool *_retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TabChildSHistoryListener::OnHistoryGoForward(nsIURI *aForwardURI, bool *_retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TabChildSHistoryListener::OnHistoryReload(nsIURI *aReloadURI, uint32_t aReloadFlags, bool *_retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TabChildSHistoryListener::OnHistoryGotoIndex(int32_t aIndex, nsIURI *aGotoURI, bool *_retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TabChildSHistoryListener::OnHistoryPurge(int32_t aNumEntries, bool *_retval)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TabChildSHistoryListener::OnHistoryReplaceEntry(int32_t aIndex)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+TabChildSHistoryListener::OnLengthChange(int32_t aCount)
+{
+ RefPtr<TabChild> tabChild(mTabChild);
+ if (!tabChild) {
+ return NS_ERROR_FAILURE;
+ }
+
+ if (aCount < 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return tabChild->SendNotifySessionHistoryChange(aCount) ?
+ NS_OK : NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+TabChildSHistoryListener::OnRequestCrossBrowserNavigation(uint32_t aIndex)
+{
+ RefPtr<TabChild> tabChild(mTabChild);
+ if (!tabChild) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return tabChild->SendRequestCrossBrowserNavigation(aIndex) ?
+ NS_OK : NS_ERROR_FAILURE;
+}
+
+TabChildGlobal::TabChildGlobal(TabChildBase* aTabChild)
+: mTabChild(aTabChild)
+{
+ SetIsNotDOMBinding();
+}
+
+TabChildGlobal::~TabChildGlobal()
+{
+}
+
+void
+TabChildGlobal::Init()
+{
+ NS_ASSERTION(!mMessageManager, "Re-initializing?!?");
+ mMessageManager = new nsFrameMessageManager(mTabChild,
+ nullptr,
+ MM_CHILD);
+}
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(TabChildGlobal)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(TabChildGlobal,
+ DOMEventTargetHelper)
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessageManager);
+ NS_IMPL_CYCLE_COLLECTION_UNLINK(mTabChild);
+ tmp->UnlinkHostObjectURIs();
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(TabChildGlobal,
+ DOMEventTargetHelper)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessageManager)
+ NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTabChild)
+ tmp->TraverseHostObjectURIs(cb);
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(TabChildGlobal)
+ NS_INTERFACE_MAP_ENTRY(nsIMessageListenerManager)
+ NS_INTERFACE_MAP_ENTRY(nsIMessageSender)
+ NS_INTERFACE_MAP_ENTRY(nsISyncMessageSender)
+ NS_INTERFACE_MAP_ENTRY(nsIContentFrameMessageManager)
+ NS_INTERFACE_MAP_ENTRY(nsIScriptObjectPrincipal)
+ NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
+ NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+ NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(ContentFrameMessageManager)
+NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
+
+NS_IMPL_ADDREF_INHERITED(TabChildGlobal, DOMEventTargetHelper)
+NS_IMPL_RELEASE_INHERITED(TabChildGlobal, DOMEventTargetHelper)
+
+// This method isn't automatically forwarded safely because it's notxpcom, so
+// the IDL binding doesn't know what value to return.
+NS_IMETHODIMP_(bool)
+TabChildGlobal::MarkForCC()
+{
+ if (mTabChild) {
+ mTabChild->MarkScopesForCC();
+ }
+ EventListenerManager* elm = GetExistingListenerManager();
+ if (elm) {
+ elm->MarkForCC();
+ }
+ return mMessageManager ? mMessageManager->MarkForCC() : false;
+}
+
+NS_IMETHODIMP
+TabChildGlobal::GetContent(mozIDOMWindowProxy** aContent)
+{
+ *aContent = nullptr;
+ if (!mTabChild)
+ return NS_ERROR_NULL_POINTER;
+ nsCOMPtr<nsPIDOMWindowOuter> window = do_GetInterface(mTabChild->WebNavigation());
+ window.forget(aContent);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+TabChildGlobal::GetDocShell(nsIDocShell** aDocShell)
+{
+ *aDocShell = nullptr;
+ if (!mTabChild)
+ return NS_ERROR_NULL_POINTER;
+ nsCOMPtr<nsIDocShell> docShell = do_GetInterface(mTabChild->WebNavigation());
+ docShell.swap(*aDocShell);
+ return NS_OK;
+}
+
+nsIPrincipal*
+TabChildGlobal::GetPrincipal()
+{
+ if (!mTabChild)
+ return nullptr;
+ return mTabChild->GetPrincipal();
+}
+
+JSObject*
+TabChildGlobal::GetGlobalJSObject()
+{
+ NS_ENSURE_TRUE(mTabChild, nullptr);
+ nsCOMPtr<nsIXPConnectJSObjectHolder> ref = mTabChild->GetGlobal();
+ NS_ENSURE_TRUE(ref, nullptr);
+ return ref->GetJSObject();
+}
diff --git a/dom/ipc/TabChild.h b/dom/ipc/TabChild.h
new file mode 100644
index 000000000..b23c7c19e
--- /dev/null
+++ b/dom/ipc/TabChild.h
@@ -0,0 +1,818 @@
+/* -*- 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_TabChild_h
+#define mozilla_dom_TabChild_h
+
+#include "mozilla/dom/PBrowserChild.h"
+#include "nsIWebNavigation.h"
+#include "nsCOMPtr.h"
+#include "nsAutoPtr.h"
+#include "nsIWebBrowserChrome2.h"
+#include "nsIEmbeddingSiteWindow.h"
+#include "nsIWebBrowserChromeFocus.h"
+#include "nsIDOMEventListener.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIWindowProvider.h"
+#include "nsIDOMWindow.h"
+#include "nsIDocShell.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsFrameMessageManager.h"
+#include "nsIPresShell.h"
+#include "nsIScriptObjectPrincipal.h"
+#include "nsWeakReference.h"
+#include "nsITabChild.h"
+#include "nsITooltipListener.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/dom/TabContext.h"
+#include "mozilla/DOMEventTargetHelper.h"
+#include "mozilla/EventDispatcher.h"
+#include "mozilla/EventForwards.h"
+#include "mozilla/layers/CompositorTypes.h"
+#include "mozilla/layers/APZCCallbackHelper.h"
+#include "nsIWebBrowserChrome3.h"
+#include "mozilla/dom/ipc/IdType.h"
+#include "AudioChannelService.h"
+#include "PuppetWidget.h"
+#include "mozilla/layers/GeckoContentController.h"
+#include "nsISHistoryListener.h"
+#include "nsIPartialSHistoryListener.h"
+
+class nsIDOMWindowUtils;
+class nsIHttpChannel;
+
+namespace mozilla {
+namespace layout {
+class RenderFrameChild;
+} // namespace layout
+
+namespace layers {
+class APZChild;
+class APZEventState;
+class AsyncDragMetrics;
+class IAPZCTreeManager;
+class ImageCompositeNotification;
+} // namespace layers
+
+namespace widget {
+struct AutoCacheNativeKeyCommands;
+} // namespace widget
+
+namespace plugins {
+class PluginWidgetChild;
+} // namespace plugins
+
+namespace dom {
+
+class TabChild;
+class ClonedMessageData;
+class TabChildBase;
+
+class TabChildGlobal : public DOMEventTargetHelper,
+ public nsIContentFrameMessageManager,
+ public nsIScriptObjectPrincipal,
+ public nsIGlobalObject,
+ public nsSupportsWeakReference
+{
+public:
+ explicit TabChildGlobal(TabChildBase* aTabChild);
+ void Init();
+ NS_DECL_ISUPPORTS_INHERITED
+ NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(TabChildGlobal, DOMEventTargetHelper)
+ NS_FORWARD_SAFE_NSIMESSAGELISTENERMANAGER(mMessageManager)
+ NS_FORWARD_SAFE_NSIMESSAGESENDER(mMessageManager)
+ NS_FORWARD_SAFE_NSIMESSAGEMANAGERGLOBAL(mMessageManager)
+ NS_IMETHOD SendSyncMessage(const nsAString& aMessageName,
+ JS::Handle<JS::Value> aObject,
+ JS::Handle<JS::Value> aRemote,
+ nsIPrincipal* aPrincipal,
+ JSContext* aCx,
+ uint8_t aArgc,
+ JS::MutableHandle<JS::Value> aRetval) override
+ {
+ return mMessageManager
+ ? mMessageManager->SendSyncMessage(aMessageName, aObject, aRemote,
+ aPrincipal, aCx, aArgc, aRetval)
+ : NS_ERROR_NULL_POINTER;
+ }
+ NS_IMETHOD SendRpcMessage(const nsAString& aMessageName,
+ JS::Handle<JS::Value> aObject,
+ JS::Handle<JS::Value> aRemote,
+ nsIPrincipal* aPrincipal,
+ JSContext* aCx,
+ uint8_t aArgc,
+ JS::MutableHandle<JS::Value> aRetval) override
+ {
+ return mMessageManager
+ ? mMessageManager->SendRpcMessage(aMessageName, aObject, aRemote,
+ aPrincipal, aCx, aArgc, aRetval)
+ : NS_ERROR_NULL_POINTER;
+ }
+ NS_IMETHOD GetContent(mozIDOMWindowProxy** aContent) override;
+ NS_IMETHOD GetDocShell(nsIDocShell** aDocShell) override;
+
+ nsresult AddEventListener(const nsAString& aType,
+ nsIDOMEventListener* aListener,
+ bool aUseCapture)
+ {
+ // By default add listeners only for trusted events!
+ return DOMEventTargetHelper::AddEventListener(aType, aListener,
+ aUseCapture, false, 2);
+ }
+ using DOMEventTargetHelper::AddEventListener;
+ NS_IMETHOD AddEventListener(const nsAString& aType,
+ nsIDOMEventListener* aListener,
+ bool aUseCapture, bool aWantsUntrusted,
+ uint8_t optional_argc) override
+ {
+ return DOMEventTargetHelper::AddEventListener(aType, aListener,
+ aUseCapture,
+ aWantsUntrusted,
+ optional_argc);
+ }
+
+ nsresult
+ PreHandleEvent(EventChainPreVisitor& aVisitor) override
+ {
+ aVisitor.mForceContentDispatch = true;
+ return NS_OK;
+ }
+
+ virtual nsIPrincipal* GetPrincipal() override;
+ virtual JSObject* GetGlobalJSObject() override;
+
+ virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override
+ {
+ MOZ_CRASH("TabChildGlobal doesn't use DOM bindings!");
+ }
+
+ nsCOMPtr<nsIContentFrameMessageManager> mMessageManager;
+ RefPtr<TabChildBase> mTabChild;
+
+protected:
+ ~TabChildGlobal();
+};
+
+class ContentListener final : public nsIDOMEventListener
+{
+public:
+ explicit ContentListener(TabChild* aTabChild) : mTabChild(aTabChild) {}
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIDOMEVENTLISTENER
+protected:
+ ~ContentListener() {}
+ TabChild* mTabChild;
+};
+
+/**
+ * Listens on session history change, and sends NotifySessionHistoryChange to
+ * parent process.
+ */
+class TabChildSHistoryListener final : public nsISHistoryListener,
+ public nsIPartialSHistoryListener,
+ public nsSupportsWeakReference
+{
+public:
+ explicit TabChildSHistoryListener(TabChild* aTabChild) : mTabChild(aTabChild) {}
+ void ClearTabChild() { mTabChild = nullptr; }
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSISHISTORYLISTENER
+ NS_DECL_NSIPARTIALSHISTORYLISTENER
+
+private:
+ ~TabChildSHistoryListener() {}
+ TabChild* mTabChild;
+};
+
+// This is base clase which helps to share Viewport and touch related
+// functionality between b2g/android FF/embedlite clients implementation.
+// It make sense to place in this class all helper functions, and functionality
+// which could be shared between Cross-process/Cross-thread implmentations.
+class TabChildBase : public nsISupports,
+ public nsMessageManagerScriptExecutor,
+ public ipc::MessageManagerCallback
+{
+protected:
+ typedef mozilla::widget::PuppetWidget PuppetWidget;
+
+public:
+ TabChildBase();
+
+ NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+ NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(TabChildBase)
+
+ virtual nsIWebNavigation* WebNavigation() const = 0;
+ virtual PuppetWidget* WebWidget() = 0;
+ nsIPrincipal* GetPrincipal() { return mPrincipal; }
+ virtual bool DoUpdateZoomConstraints(const uint32_t& aPresShellId,
+ const mozilla::layers::FrameMetrics::ViewID& aViewId,
+ const Maybe<mozilla::layers::ZoomConstraints>& aConstraints) = 0;
+
+ virtual ScreenIntSize GetInnerSize() = 0;
+
+ // Get the Document for the top-level window in this tab.
+ already_AddRefed<nsIDocument> GetDocument() const;
+
+ // Get the pres-shell of the document for the top-level window in this tab.
+ already_AddRefed<nsIPresShell> GetPresShell() const;
+
+protected:
+ virtual ~TabChildBase();
+
+ // Wraps up a JSON object as a structured clone and sends it to the browser
+ // chrome script.
+ //
+ // XXX/bug 780335: Do the work the browser chrome script does in C++ instead
+ // so we don't need things like this.
+ void DispatchMessageManagerMessage(const nsAString& aMessageName,
+ const nsAString& aJSONData);
+
+ void ProcessUpdateFrame(const mozilla::layers::FrameMetrics& aFrameMetrics);
+
+ bool UpdateFrameHandler(const mozilla::layers::FrameMetrics& aFrameMetrics);
+
+protected:
+ RefPtr<TabChildGlobal> mTabChildGlobal;
+ nsCOMPtr<nsIWebBrowserChrome3> mWebBrowserChrome;
+};
+
+class TabChild final : public TabChildBase,
+ public PBrowserChild,
+ public nsIWebBrowserChrome2,
+ public nsIEmbeddingSiteWindow,
+ public nsIWebBrowserChromeFocus,
+ public nsIInterfaceRequestor,
+ public nsIWindowProvider,
+ public nsSupportsWeakReference,
+ public nsITabChild,
+ public nsIObserver,
+ public TabContext,
+ public nsITooltipListener,
+ public mozilla::ipc::IShmemAllocator
+{
+ typedef mozilla::dom::ClonedMessageData ClonedMessageData;
+ typedef mozilla::layout::RenderFrameChild RenderFrameChild;
+ typedef mozilla::layers::APZEventState APZEventState;
+ typedef mozilla::layers::SetAllowedTouchBehaviorCallback SetAllowedTouchBehaviorCallback;
+
+public:
+ /**
+ * Find TabChild of aTabId in the same content process of the
+ * caller.
+ */
+ static already_AddRefed<TabChild> FindTabChild(const TabId& aTabId);
+
+ // Return a list of all active TabChildren.
+ static nsTArray<RefPtr<TabChild>> GetAll();
+
+public:
+ /**
+ * Create a new TabChild object.
+ */
+ TabChild(nsIContentChild* aManager,
+ const TabId& aTabId,
+ const TabContext& aContext,
+ uint32_t aChromeFlags);
+
+ nsresult Init();
+
+ /**
+ * This is expected to be called off the critical path to content
+ * startup. This is an opportunity to load things that are slow
+ * on the critical path.
+ */
+ static void PreloadSlowThings();
+
+ /** Return a TabChild with the given attributes. */
+ static already_AddRefed<TabChild>
+ Create(nsIContentChild* aManager, const TabId& aTabId,
+ const TabContext& aContext, uint32_t aChromeFlags);
+
+ // 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_ISUPPORTS_INHERITED
+ NS_DECL_NSIWEBBROWSERCHROME
+ NS_DECL_NSIWEBBROWSERCHROME2
+ NS_DECL_NSIEMBEDDINGSITEWINDOW
+ NS_DECL_NSIWEBBROWSERCHROMEFOCUS
+ NS_DECL_NSIINTERFACEREQUESTOR
+ NS_DECL_NSIWINDOWPROVIDER
+ NS_DECL_NSITABCHILD
+ NS_DECL_NSIOBSERVER
+ NS_DECL_NSITOOLTIPLISTENER
+
+ FORWARD_SHMEM_ALLOCATOR_TO(PBrowserChild)
+
+ /**
+ * MessageManagerCallback methods that we override.
+ */
+ virtual bool DoSendBlockingMessage(JSContext* aCx,
+ const nsAString& aMessage,
+ StructuredCloneData& aData,
+ JS::Handle<JSObject *> aCpows,
+ nsIPrincipal* aPrincipal,
+ nsTArray<StructuredCloneData>* aRetVal,
+ bool aIsSync) override;
+
+ virtual nsresult DoSendAsyncMessage(JSContext* aCx,
+ const nsAString& aMessage,
+ StructuredCloneData& aData,
+ JS::Handle<JSObject *> aCpows,
+ nsIPrincipal* aPrincipal) override;
+
+ virtual bool
+ DoUpdateZoomConstraints(const uint32_t& aPresShellId,
+ const ViewID& aViewId,
+ const Maybe<ZoomConstraints>& aConstraints) override;
+
+ virtual bool RecvLoadURL(const nsCString& aURI,
+ const ShowInfo& aInfo) override;
+ virtual bool
+ RecvShow(const ScreenIntSize& aSize,
+ const ShowInfo& aInfo,
+ const TextureFactoryIdentifier& aTextureFactoryIdentifier,
+ const uint64_t& aLayersId,
+ PRenderFrameChild* aRenderFrame,
+ const bool& aParentIsActive,
+ const nsSizeMode& aSizeMode) override;
+
+ virtual bool
+ RecvUpdateDimensions(const CSSRect& aRect,
+ const CSSSize& aSize,
+ const ScreenOrientationInternal& aOrientation,
+ const LayoutDeviceIntPoint& aClientOffset,
+ const LayoutDeviceIntPoint& aChromeDisp) override;
+ virtual bool
+ RecvSizeModeChanged(const nsSizeMode& aSizeMode) override;
+
+ virtual bool RecvActivate() override;
+
+ virtual bool RecvDeactivate() override;
+
+ virtual bool RecvMouseEvent(const nsString& aType,
+ const float& aX,
+ const float& aY,
+ const int32_t& aButton,
+ const int32_t& aClickCount,
+ const int32_t& aModifiers,
+ const bool& aIgnoreRootScrollFrame) override;
+
+ virtual bool RecvRealMouseMoveEvent(const mozilla::WidgetMouseEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId) override;
+
+ virtual bool RecvSynthMouseMoveEvent(const mozilla::WidgetMouseEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId) override;
+
+ virtual bool RecvRealMouseButtonEvent(const mozilla::WidgetMouseEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId) override;
+
+ virtual bool RecvRealDragEvent(const WidgetDragEvent& aEvent,
+ const uint32_t& aDragAction,
+ const uint32_t& aDropEffect) override;
+
+ virtual bool
+ RecvRealKeyEvent(const mozilla::WidgetKeyboardEvent& aEvent,
+ const MaybeNativeKeyBinding& aBindings) override;
+
+ virtual bool RecvMouseWheelEvent(const mozilla::WidgetWheelEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId) override;
+
+ virtual bool RecvRealTouchEvent(const WidgetTouchEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId,
+ const nsEventStatus& aApzResponse) override;
+
+ virtual bool
+ RecvRealTouchMoveEvent(const WidgetTouchEvent& aEvent,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId,
+ const nsEventStatus& aApzResponse) override;
+
+ virtual bool RecvKeyEvent(const nsString& aType,
+ const int32_t& aKeyCode,
+ const int32_t& aCharCode,
+ const int32_t& aModifiers,
+ const bool& aPreventDefault) override;
+
+ virtual bool RecvNativeSynthesisResponse(const uint64_t& aObserverId,
+ const nsCString& aResponse) override;
+
+ virtual bool RecvPluginEvent(const WidgetPluginEvent& aEvent) override;
+
+ virtual bool
+ RecvCompositionEvent(const mozilla::WidgetCompositionEvent& aEvent) override;
+
+ virtual bool
+ RecvSelectionEvent(const mozilla::WidgetSelectionEvent& aEvent) override;
+
+ virtual bool
+ RecvPasteTransferable(const IPCDataTransfer& aDataTransfer,
+ const bool& aIsPrivateData,
+ const IPC::Principal& aRequestingPrincipal) override;
+
+ virtual bool
+ RecvActivateFrameEvent(const nsString& aType, const bool& aCapture) override;
+
+ virtual bool RecvLoadRemoteScript(const nsString& aURL,
+ const bool& aRunInGlobalScope) override;
+
+ virtual bool RecvAsyncMessage(const nsString& aMessage,
+ InfallibleTArray<CpowEntry>&& aCpows,
+ const IPC::Principal& aPrincipal,
+ const ClonedMessageData& aData) override;
+
+ virtual bool
+ RecvSwappedWithOtherRemoteLoader(const IPCTabContext& aContext) override;
+
+ virtual PDocAccessibleChild*
+ AllocPDocAccessibleChild(PDocAccessibleChild*, const uint64_t&,
+ const uint32_t&, const IAccessibleHolder&) override;
+
+ virtual bool DeallocPDocAccessibleChild(PDocAccessibleChild*) override;
+
+ virtual PDocumentRendererChild*
+ AllocPDocumentRendererChild(const nsRect& aDocumentRect,
+ const gfx::Matrix& aTransform,
+ const nsString& aBggcolor,
+ const uint32_t& aRenderFlags,
+ const bool& aFlushLayout,
+ const nsIntSize& arenderSize) override;
+
+ virtual bool
+ DeallocPDocumentRendererChild(PDocumentRendererChild* aCctor) override;
+
+ virtual bool
+ RecvPDocumentRendererConstructor(PDocumentRendererChild* aActor,
+ const nsRect& aDocumentRect,
+ const gfx::Matrix& aTransform,
+ const nsString& aBgcolor,
+ const uint32_t& aRenderFlags,
+ const bool& aFlushLayout,
+ const nsIntSize& aRenderSize) override;
+
+
+ virtual PColorPickerChild*
+ AllocPColorPickerChild(const nsString& aTitle,
+ const nsString& aInitialColor) override;
+
+ virtual bool DeallocPColorPickerChild(PColorPickerChild* aActor) override;
+
+ virtual PDatePickerChild*
+ AllocPDatePickerChild(const nsString& title, const nsString& initialDate) override;
+ virtual bool DeallocPDatePickerChild(PDatePickerChild* actor) override;
+
+ virtual PFilePickerChild*
+ AllocPFilePickerChild(const nsString& aTitle, const int16_t& aMode) override;
+
+ virtual bool
+ DeallocPFilePickerChild(PFilePickerChild* aActor) override;
+
+ virtual PIndexedDBPermissionRequestChild*
+ AllocPIndexedDBPermissionRequestChild(const Principal& aPrincipal) override;
+
+ virtual bool
+ DeallocPIndexedDBPermissionRequestChild(PIndexedDBPermissionRequestChild* aActor) override;
+
+ virtual nsIWebNavigation* WebNavigation() const override
+ {
+ return mWebNav;
+ }
+
+ virtual PuppetWidget* WebWidget() override { return mPuppetWidget; }
+
+ /** Return the DPI of the widget this TabChild draws to. */
+ void GetDPI(float* aDPI);
+
+ void GetDefaultScale(double *aScale);
+
+ void GetWidgetRounding(int32_t* aRounding);
+
+ bool IsTransparent() const { return mIsTransparent; }
+
+ void GetMaxTouchPoints(uint32_t* aTouchPoints);
+
+ ScreenOrientationInternal GetOrientation() const { return mOrientation; }
+
+ void SetBackgroundColor(const nscolor& aColor);
+
+ void NotifyPainted();
+
+ void RequestNativeKeyBindings(mozilla::widget::AutoCacheNativeKeyCommands* aAutoCache,
+ const WidgetKeyboardEvent* aEvent);
+
+ /**
+ * Signal to this TabChild that it should be made visible:
+ * activated widget, retained layer tree, etc. (Respectively,
+ * made not visible.)
+ */
+ void MakeVisible();
+ void MakeHidden();
+
+ nsIContentChild* Manager() const { return mManager; }
+
+ static inline TabChild*
+ GetFrom(nsIDocShell* aDocShell)
+ {
+ if (!aDocShell) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsITabChild> tc = aDocShell->GetTabChild();
+ return static_cast<TabChild*>(tc.get());
+ }
+
+ static inline TabChild*
+ GetFrom(mozIDOMWindow* aWindow)
+ {
+ nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(aWindow);
+ nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(webNav);
+ return GetFrom(docShell);
+ }
+
+ static inline TabChild*
+ GetFrom(mozIDOMWindowProxy* aWindow)
+ {
+ nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(aWindow);
+ nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(webNav);
+ return GetFrom(docShell);
+ }
+
+ static TabChild* GetFrom(nsIPresShell* aPresShell);
+ static TabChild* GetFrom(uint64_t aLayersId);
+
+ uint64_t LayersId() { return mLayersId; }
+
+ void DidComposite(uint64_t aTransactionId,
+ const TimeStamp& aCompositeStart,
+ const TimeStamp& aCompositeEnd);
+
+ void DidRequestComposite(const TimeStamp& aCompositeReqStart,
+ const TimeStamp& aCompositeReqEnd);
+
+ void ClearCachedResources();
+ void InvalidateLayers();
+ void ReinitRendering();
+ void CompositorUpdated(const TextureFactoryIdentifier& aNewIdentifier);
+
+ static inline TabChild* GetFrom(nsIDOMWindow* aWindow)
+ {
+ nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(aWindow);
+ nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(webNav);
+ return GetFrom(docShell);
+ }
+
+ virtual bool RecvUIResolutionChanged(const float& aDpi,
+ const int32_t& aRounding,
+ const double& aScale) override;
+
+ virtual bool
+ RecvThemeChanged(nsTArray<LookAndFeelInt>&& aLookAndFeelIntCache) override;
+
+ virtual bool RecvHandleAccessKey(const WidgetKeyboardEvent& aEvent,
+ nsTArray<uint32_t>&& aCharCodes,
+ const int32_t& aModifierMask) override;
+
+ virtual bool RecvAudioChannelChangeNotification(const uint32_t& aAudioChannel,
+ const float& aVolume,
+ const bool& aMuted) override;
+
+ virtual bool RecvSetUseGlobalHistory(const bool& aUse) override;
+
+ virtual bool RecvHandledWindowedPluginKeyEvent(
+ const mozilla::NativeEventData& aKeyEventData,
+ const bool& aIsConsumed) override;
+
+ virtual bool RecvPrint(const uint64_t& aOuterWindowID,
+ const PrintData& aPrintData) override;
+
+ virtual bool RecvUpdateNativeWindowHandle(const uintptr_t& aNewHandle) override;
+
+ /**
+ * Native widget remoting protocol for use with windowed plugins with e10s.
+ */
+ PPluginWidgetChild* AllocPPluginWidgetChild() override;
+
+ bool DeallocPPluginWidgetChild(PPluginWidgetChild* aActor) override;
+
+ nsresult CreatePluginWidget(nsIWidget* aParent, nsIWidget** aOut);
+
+ LayoutDeviceIntPoint GetClientOffset() const { return mClientOffset; }
+ LayoutDeviceIntPoint GetChromeDisplacement() const { return mChromeDisp; };
+
+ bool IPCOpen() const { return mIPCOpen; }
+
+ bool ParentIsActive() const
+ {
+ return mParentIsActive;
+ }
+
+ bool AsyncPanZoomEnabled() const { return mAsyncPanZoomEnabled; }
+
+ virtual ScreenIntSize GetInnerSize() override;
+
+ // Call RecvShow(nsIntSize(0, 0)) and block future calls to RecvShow().
+ void DoFakeShow(const TextureFactoryIdentifier& aTextureFactoryIdentifier,
+ const uint64_t& aLayersId,
+ PRenderFrameChild* aRenderFrame,
+ const ShowInfo& aShowInfo);
+
+ void ContentReceivedInputBlock(const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId,
+ bool aPreventDefault) const;
+ void SetTargetAPZC(uint64_t aInputBlockId,
+ const nsTArray<ScrollableLayerGuid>& aTargets) const;
+ bool RecvHandleTap(const layers::GeckoContentController::TapType& aType,
+ const LayoutDevicePoint& aPoint,
+ const Modifiers& aModifiers,
+ const ScrollableLayerGuid& aGuid,
+ const uint64_t& aInputBlockId) override;
+ void SetAllowedTouchBehavior(uint64_t aInputBlockId,
+ const nsTArray<TouchBehaviorFlags>& aFlags) const;
+
+ bool UpdateFrame(const FrameMetrics& aFrameMetrics);
+ 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 FrameMetrics::ViewID& aViewId,
+ const CSSRect& aRect,
+ const uint32_t& aFlags);
+
+ // Request that the docshell be marked as active.
+ void ForcePaint(uint64_t aLayerObserverEpoch);
+
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+ uintptr_t GetNativeWindowHandle() const { return mNativeWindowHandle; }
+#endif
+
+ bool TakeIsFreshProcess()
+ {
+ bool wasFreshProcess = mIsFreshProcess;
+ mIsFreshProcess = false;
+ return wasFreshProcess;
+ }
+
+protected:
+ virtual ~TabChild();
+
+ virtual PRenderFrameChild* AllocPRenderFrameChild() override;
+
+ virtual bool DeallocPRenderFrameChild(PRenderFrameChild* aFrame) override;
+
+ virtual bool RecvDestroy() override;
+
+ virtual bool RecvSetDocShellIsActive(const bool& aIsActive,
+ const bool& aIsHidden,
+ const uint64_t& aLayerObserverEpoch) override;
+
+ virtual bool RecvNavigateByKey(const bool& aForward,
+ const bool& aForDocumentNavigation) override;
+
+ virtual bool RecvRequestNotifyAfterRemotePaint() override;
+
+ virtual bool RecvSuppressDisplayport(const bool& aEnabled) override;
+
+ virtual bool RecvParentActivated(const bool& aActivated) override;
+
+ virtual bool RecvSetKeyboardIndicators(const UIStateChangeType& aShowAccelerators,
+ const UIStateChangeType& aShowFocusRings) override;
+
+ virtual bool RecvStopIMEStateManagement() override;
+
+ virtual bool RecvMenuKeyboardListenerInstalled(
+ const bool& aInstalled) override;
+
+ virtual bool RecvNotifyAttachGroupedSessionHistory(const uint32_t& aOffset) override;
+
+ virtual bool RecvNotifyPartialSessionHistoryActive(const uint32_t& aGlobalLength,
+ const uint32_t& aTargetLocalIndex) override;
+
+ virtual bool RecvNotifyPartialSessionHistoryDeactive() override;
+
+ virtual bool RecvSetFreshProcess() override;
+
+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().
+ //
+ // @param aIsPreallocated true if this is called for Preallocated Tab.
+ void NotifyTabContextUpdated(bool aIsPreallocated);
+
+ // Update the frameType on our docshell.
+ void UpdateFrameType();
+
+ void ActorDestroy(ActorDestroyReason why) override;
+
+ enum FrameScriptLoading { DONT_LOAD_SCRIPTS, DEFAULT_LOAD_SCRIPTS };
+
+ bool InitTabChildGlobal(FrameScriptLoading aScriptLoading = DEFAULT_LOAD_SCRIPTS);
+
+ bool InitRenderingState(const TextureFactoryIdentifier& aTextureFactoryIdentifier,
+ const uint64_t& aLayersId,
+ PRenderFrameChild* aRenderFrame);
+
+ void DestroyWindow();
+
+ void SetProcessNameToAppName();
+
+ void ApplyShowInfo(const ShowInfo& aInfo);
+
+ bool HasValidInnerSize();
+
+ void SetTabId(const TabId& aTabId);
+
+ ScreenIntRect GetOuterRect();
+
+ void SetUnscaledInnerSize(const CSSSize& aSize)
+ {
+ mUnscaledInnerSize = aSize;
+ }
+
+ class DelayedDeleteRunnable;
+
+ TextureFactoryIdentifier mTextureFactoryIdentifier;
+ nsCOMPtr<nsIWebNavigation> mWebNav;
+ RefPtr<PuppetWidget> mPuppetWidget;
+ nsCOMPtr<nsIURI> mLastURI;
+ RenderFrameChild* mRemoteFrame;
+ RefPtr<nsIContentChild> mManager;
+ RefPtr<TabChildSHistoryListener> mHistoryListener;
+ uint32_t mChromeFlags;
+ int32_t mActiveSuppressDisplayport;
+ uint64_t mLayersId;
+ CSSRect mUnscaledOuterRect;
+ nscolor mLastBackgroundColor;
+ bool mDidFakeShow;
+ bool mNotified;
+ bool mTriedBrowserInit;
+ ScreenOrientationInternal mOrientation;
+
+ 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)
+ LayoutDeviceIntPoint mChromeDisp;
+ TabId mUniqueId;
+
+ friend class ContentChild;
+ float mDPI;
+ int32_t mRounding;
+ double mDefaultScale;
+
+ bool mIsTransparent;
+
+ bool mIPCOpen;
+ bool mParentIsActive;
+ bool mAsyncPanZoomEnabled;
+ CSSSize mUnscaledInnerSize;
+ bool mDidSetRealShowInfo;
+ bool mDidLoadURLInit;
+ bool mIsFreshProcess;
+
+ AutoTArray<bool, NUMBER_OF_AUDIO_CHANNELS> mAudioChannelsActive;
+
+ RefPtr<layers::IAPZCTreeManager> mApzcTreeManager;
+ // APZChild clears this pointer from its destructor, so it shouldn't be a
+ // dangling pointer.
+ layers::APZChild* mAPZChild;
+
+ // The most recently seen layer observer epoch in RecvSetDocShellIsActive.
+ uint64_t mLayerObserverEpoch;
+
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+ // The handle associated with the native window that contains this tab
+ uintptr_t mNativeWindowHandle;
+#endif // defined(XP_WIN)
+
+ DISALLOW_EVIL_CONSTRUCTORS(TabChild);
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_dom_TabChild_h
diff --git a/dom/ipc/TabContext.cpp b/dom/ipc/TabContext.cpp
new file mode 100644
index 000000000..b36dbc5eb
--- /dev/null
+++ b/dom/ipc/TabContext.cpp
@@ -0,0 +1,434 @@
+/* -*- 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/TabParent.h"
+#include "mozilla/dom/TabChild.h"
+#include "nsIAppsService.h"
+#include "nsIScriptSecurityManager.h"
+#include "nsServiceManagerUtils.h"
+
+#define NO_APP_ID (nsIScriptSecurityManager::NO_APP_ID)
+
+using namespace mozilla::dom::ipc;
+using namespace mozilla::layout;
+
+namespace mozilla {
+namespace dom {
+
+TabContext::TabContext()
+ : mIsPrerendered(false)
+ , mInitialized(false)
+ , mIsMozBrowserElement(false)
+ , mContainingAppId(NO_APP_ID)
+ , mShowAccelerators(UIStateChangeType_NoChange)
+ , mShowFocusRings(UIStateChangeType_NoChange)
+{
+}
+
+bool
+TabContext::IsMozBrowserElement() const
+{
+ return mIsMozBrowserElement;
+}
+
+bool
+TabContext::IsIsolatedMozBrowserElement() const
+{
+ return mOriginAttributes.mInIsolatedMozBrowser;
+}
+
+bool
+TabContext::IsMozBrowserOrApp() const
+{
+ return HasOwnApp() || IsMozBrowserElement();
+}
+
+uint32_t
+TabContext::OwnAppId() const
+{
+ return mOriginAttributes.mAppId;
+}
+
+already_AddRefed<mozIApplication>
+TabContext::GetOwnApp() const
+{
+ nsCOMPtr<mozIApplication> ownApp = mOwnApp;
+ return ownApp.forget();
+}
+
+bool
+TabContext::HasOwnApp() const
+{
+ nsCOMPtr<mozIApplication> ownApp = GetOwnApp();
+ return !!ownApp;
+}
+
+uint32_t
+TabContext::BrowserOwnerAppId() const
+{
+ if (IsMozBrowserElement()) {
+ return mContainingAppId;
+ }
+ return NO_APP_ID;
+}
+
+already_AddRefed<mozIApplication>
+TabContext::GetBrowserOwnerApp() const
+{
+ nsCOMPtr<mozIApplication> ownerApp;
+ if (IsMozBrowserElement()) {
+ ownerApp = mContainingApp;
+ }
+ return ownerApp.forget();
+}
+
+bool
+TabContext::HasBrowserOwnerApp() const
+{
+ nsCOMPtr<mozIApplication> ownerApp = GetBrowserOwnerApp();
+ return !!ownerApp;
+}
+
+uint32_t
+TabContext::AppOwnerAppId() const
+{
+ if (HasOwnApp()) {
+ return mContainingAppId;
+ }
+ return NO_APP_ID;
+}
+
+already_AddRefed<mozIApplication>
+TabContext::GetAppOwnerApp() const
+{
+ nsCOMPtr<mozIApplication> ownerApp;
+ if (HasOwnApp()) {
+ ownerApp = mContainingApp;
+ }
+ return ownerApp.forget();
+}
+
+bool
+TabContext::HasAppOwnerApp() const
+{
+ nsCOMPtr<mozIApplication> ownerApp = GetAppOwnerApp();
+ return !!ownerApp;
+}
+
+uint32_t
+TabContext::OwnOrContainingAppId() const
+{
+ if (HasOwnApp()) {
+ return mOriginAttributes.mAppId;
+ }
+
+ return mContainingAppId;
+}
+
+already_AddRefed<mozIApplication>
+TabContext::GetOwnOrContainingApp() const
+{
+ nsCOMPtr<mozIApplication> ownOrContainingApp;
+ if (HasOwnApp()) {
+ ownOrContainingApp = mOwnApp;
+ } else {
+ ownOrContainingApp = mContainingApp;
+ }
+
+ return ownOrContainingApp.forget();
+}
+
+bool
+TabContext::HasOwnOrContainingApp() const
+{
+ nsCOMPtr<mozIApplication> ownOrContainingApp = GetOwnOrContainingApp();
+ return !!ownOrContainingApp;
+}
+
+bool
+TabContext::SetTabContext(const TabContext& aContext)
+{
+ NS_ENSURE_FALSE(mInitialized, false);
+
+ *this = aContext;
+ mInitialized = true;
+
+ return true;
+}
+
+void
+TabContext::SetPrivateBrowsingAttributes(bool aIsPrivateBrowsing)
+{
+ mOriginAttributes.SyncAttributesWithPrivateBrowsing(aIsPrivateBrowsing);
+}
+
+bool
+TabContext::UpdateTabContextAfterSwap(const TabContext& aContext)
+{
+ // This is only used after already initialized.
+ MOZ_ASSERT(mInitialized);
+
+ // The only permissable change is to `mIsMozBrowserElement`. All other fields
+ // must match for the change to be accepted.
+ if (aContext.OwnAppId() != OwnAppId() ||
+ aContext.mContainingAppId != mContainingAppId ||
+ aContext.mOriginAttributes != mOriginAttributes) {
+ return false;
+ }
+
+ mIsMozBrowserElement = aContext.mIsMozBrowserElement;
+ return true;
+}
+
+const DocShellOriginAttributes&
+TabContext::OriginAttributesRef() const
+{
+ return mOriginAttributes;
+}
+
+const nsAString&
+TabContext::PresentationURL() const
+{
+ return mPresentationURL;
+}
+
+UIStateChangeType
+TabContext::ShowAccelerators() const
+{
+ return mShowAccelerators;
+}
+
+UIStateChangeType
+TabContext::ShowFocusRings() const
+{
+ return mShowFocusRings;
+}
+
+bool
+TabContext::SetTabContext(bool aIsMozBrowserElement,
+ bool aIsPrerendered,
+ mozIApplication* aOwnApp,
+ mozIApplication* aAppFrameOwnerApp,
+ UIStateChangeType aShowAccelerators,
+ UIStateChangeType aShowFocusRings,
+ const DocShellOriginAttributes& aOriginAttributes,
+ const nsAString& aPresentationURL)
+{
+ NS_ENSURE_FALSE(mInitialized, false);
+
+ // Get ids for both apps and only write to our member variables after we've
+ // verified that this worked.
+ uint32_t ownAppId = NO_APP_ID;
+ if (aOwnApp) {
+ nsresult rv = aOwnApp->GetLocalId(&ownAppId);
+ NS_ENSURE_SUCCESS(rv, false);
+ NS_ENSURE_TRUE(ownAppId != NO_APP_ID, false);
+ }
+
+ uint32_t containingAppId = NO_APP_ID;
+ if (aAppFrameOwnerApp) {
+ nsresult rv = aAppFrameOwnerApp->GetLocalId(&containingAppId);
+ NS_ENSURE_SUCCESS(rv, false);
+ NS_ENSURE_TRUE(containingAppId != NO_APP_ID, false);
+ }
+
+ // Veryify that app id matches mAppId passed in originAttributes
+ MOZ_RELEASE_ASSERT((aOwnApp && aOriginAttributes.mAppId == ownAppId) ||
+ (aAppFrameOwnerApp && aOriginAttributes.mAppId == containingAppId) ||
+ aOriginAttributes.mAppId == NO_APP_ID);
+
+ mInitialized = true;
+ mIsMozBrowserElement = aIsMozBrowserElement;
+ mIsPrerendered = aIsPrerendered;
+ mOriginAttributes = aOriginAttributes;
+ mContainingAppId = containingAppId;
+ mOwnApp = aOwnApp;
+ mContainingApp = aAppFrameOwnerApp;
+ mPresentationURL = aPresentationURL;
+ mShowAccelerators = aShowAccelerators;
+ mShowFocusRings = aShowFocusRings;
+ return true;
+}
+
+IPCTabContext
+TabContext::AsIPCTabContext() const
+{
+ return IPCTabContext(FrameIPCTabContext(mOriginAttributes,
+ mContainingAppId,
+ mIsMozBrowserElement,
+ mIsPrerendered,
+ mPresentationURL,
+ mShowAccelerators,
+ mShowFocusRings));
+}
+
+static already_AddRefed<mozIApplication>
+GetAppForId(uint32_t aAppId)
+{
+ nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
+ NS_ENSURE_TRUE(appsService, nullptr);
+
+ nsCOMPtr<mozIApplication> app;
+ appsService->GetAppByLocalId(aAppId, getter_AddRefs(app));
+
+ return app.forget();
+}
+
+MaybeInvalidTabContext::MaybeInvalidTabContext(const IPCTabContext& aParams)
+ : mInvalidReason(nullptr)
+{
+ bool isMozBrowserElement = false;
+ bool isPrerendered = false;
+ uint32_t containingAppId = NO_APP_ID;
+ DocShellOriginAttributes originAttributes;
+ nsAutoString presentationURL;
+ UIStateChangeType showAccelerators = UIStateChangeType_NoChange;
+ UIStateChangeType showFocusRings = UIStateChangeType_NoChange;
+
+ switch(aParams.type()) {
+ case IPCTabContext::TPopupIPCTabContext: {
+ const PopupIPCTabContext &ipcContext = aParams.get_PopupIPCTabContext();
+
+ TabContext *context;
+ if (ipcContext.opener().type() == PBrowserOrId::TPBrowserParent) {
+ context = TabParent::GetFrom(ipcContext.opener().get_PBrowserParent());
+ if (!context) {
+ mInvalidReason = "Child is-browser process tried to "
+ "open a null tab.";
+ return;
+ }
+ if (context->IsMozBrowserElement() &&
+ !ipcContext.isMozBrowserElement()) {
+ // If the TabParent corresponds to a browser element, then it can only
+ // open other browser elements, for security reasons. We should have
+ // checked this before calling the TabContext constructor, so this is
+ // a fatal error.
+ mInvalidReason = "Child is-browser process tried to "
+ "open a non-browser tab.";
+ return;
+ }
+ } else if (ipcContext.opener().type() == PBrowserOrId::TPBrowserChild) {
+ context = static_cast<TabChild*>(ipcContext.opener().get_PBrowserChild());
+ } else if (ipcContext.opener().type() == PBrowserOrId::TTabId) {
+ // We should never get here because this PopupIPCTabContext is only
+ // used for allocating a new tab id, not for allocating a PBrowser.
+ mInvalidReason = "Child process tried to open an tab without the opener information.";
+ return;
+ } else {
+ // This should be unreachable because PopupIPCTabContext::opener is not a
+ // nullable field.
+ mInvalidReason = "PopupIPCTabContext::opener was null (?!).";
+ return;
+ }
+
+ // Browser elements can't nest other browser elements. So if
+ // our opener is browser element, we must be a new DOM window
+ // opened by it. In that case we inherit our containing app ID
+ // (if any).
+ //
+ // Otherwise, we're a new app window and we inherit from our
+ // opener app.
+ isMozBrowserElement = ipcContext.isMozBrowserElement();
+ originAttributes = context->mOriginAttributes;
+ if (isMozBrowserElement) {
+ containingAppId = context->OwnOrContainingAppId();
+ } else {
+ containingAppId = context->mContainingAppId;
+ }
+ break;
+ }
+ case IPCTabContext::TFrameIPCTabContext: {
+ const FrameIPCTabContext &ipcContext =
+ aParams.get_FrameIPCTabContext();
+
+ isMozBrowserElement = ipcContext.isMozBrowserElement();
+ isPrerendered = ipcContext.isPrerendered();
+ containingAppId = ipcContext.frameOwnerAppId();
+ presentationURL = ipcContext.presentationURL();
+ showAccelerators = ipcContext.showAccelerators();
+ showFocusRings = ipcContext.showFocusRings();
+ originAttributes = ipcContext.originAttributes();
+ break;
+ }
+ case IPCTabContext::TUnsafeIPCTabContext: {
+ // XXXcatalinb: This used *only* by ServiceWorkerClients::OpenWindow.
+ // It is meant as a temporary solution until service workers can
+ // provide a TabChild equivalent. Don't allow this on b2g since
+ // it might be used to escalate privileges.
+#ifdef MOZ_B2G
+ mInvalidReason = "ServiceWorkerClients::OpenWindow is not supported.";
+ return;
+#endif
+ if (!Preferences::GetBool("dom.serviceWorkers.enabled", false)) {
+ mInvalidReason = "ServiceWorkers should be enabled.";
+ return;
+ }
+
+ containingAppId = NO_APP_ID;
+ break;
+ }
+ default: {
+ MOZ_CRASH();
+ }
+ }
+
+ nsCOMPtr<mozIApplication> ownApp;
+ if (!isMozBrowserElement) {
+ // mAppId corresponds to OwnOrContainingAppId; if isMozBrowserElement is
+ // false then it's ownApp otherwise it's containingApp
+ ownApp = GetAppForId(originAttributes.mAppId);
+ if ((ownApp == nullptr) != (originAttributes.mAppId == NO_APP_ID)) {
+ mInvalidReason = "Got an ownAppId that didn't correspond to an app.";
+ return;
+ }
+ }
+
+ nsCOMPtr<mozIApplication> containingApp = GetAppForId(containingAppId);
+ if ((containingApp == nullptr) != (containingAppId == NO_APP_ID)) {
+ mInvalidReason = "Got a containingAppId that didn't correspond to an app.";
+ return;
+ }
+
+ bool rv;
+ rv = mTabContext.SetTabContext(isMozBrowserElement,
+ isPrerendered,
+ ownApp,
+ containingApp,
+ showAccelerators,
+ showFocusRings,
+ originAttributes,
+ presentationURL);
+ 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 dom
+} // namespace mozilla
diff --git a/dom/ipc/TabContext.h b/dom/ipc/TabContext.h
new file mode 100644
index 000000000..48974cad1
--- /dev/null
+++ b/dom/ipc/TabContext.h
@@ -0,0 +1,337 @@
+/* -*- 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 "mozIApplication.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 that may be a mozbrowser
+ * or mozapp. You can ask whether a TabContext corresponds to a mozbrowser or
+ * mozapp, get the app that contains the browser, and so on.
+ *
+ * TabParent and TabChild 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 or
+ * AppFrameIPCTabContext from this TabContext's information.
+ */
+ IPCTabContext AsIPCTabContext() const;
+
+ /**
+ * Does this TabContext correspond to a mozbrowser?
+ *
+ * <iframe mozbrowser mozapp> and <xul:browser> are not considered to be
+ * mozbrowser elements.
+ *
+ * If IsMozBrowserElement() is true, HasOwnApp() and HasAppOwnerApp() are
+ * guaranteed to be false.
+ *
+ * If IsMozBrowserElement() is false, HasBrowserOwnerApp() is guaranteed to be
+ * false.
+ */
+ bool IsMozBrowserElement() const;
+
+ /**
+ * Does this TabContext correspond to an isolated mozbrowser?
+ *
+ * <iframe mozbrowser mozapp> and <xul:browser> are not considered to be
+ * mozbrowser elements. <iframe mozbrowser noisolation> does not count as
+ * isolated since isolation is disabled. Isolation can only be disabled by
+ * chrome pages.
+ */
+ bool IsIsolatedMozBrowserElement() const;
+
+ /**
+ * Does this TabContext correspond to a mozbrowser or mozapp? This is
+ * equivalent to IsMozBrowserElement() || HasOwnApp(). Returns false for
+ * <xul:browser>, which is neither a mozbrowser nor a mozapp.
+ */
+ bool IsMozBrowserOrApp() const;
+
+ /**
+ * OwnAppId() returns the id of the app which directly corresponds to this
+ * context's frame. GetOwnApp() returns the corresponding app object, and
+ * HasOwnApp() returns true iff GetOwnApp() would return a non-null value.
+ *
+ * If HasOwnApp() is true, IsMozBrowserElement() is guaranteed to be
+ * false.
+ */
+ uint32_t OwnAppId() const;
+ already_AddRefed<mozIApplication> GetOwnApp() const;
+ bool HasOwnApp() const;
+
+ /**
+ * BrowserOwnerAppId() gets the ID of the app which contains this browser
+ * frame. If this is not a mozbrowser frame (if !IsMozBrowserElement()), then
+ * BrowserOwnerAppId() is guaranteed to return NO_APP_ID.
+ *
+ * Even if we are a browser frame, BrowserOwnerAppId() may still return
+ * NO_APP_ID, if this browser frame is not contained inside an app.
+ */
+ uint32_t BrowserOwnerAppId() const;
+ already_AddRefed<mozIApplication> GetBrowserOwnerApp() const;
+ bool HasBrowserOwnerApp() const;
+
+ /**
+ * AppOwnerAppId() gets the ID of the app which contains this app frame. If
+ * this is not an app frame (i.e., if !HasOwnApp()), then AppOwnerAppId() is
+ * guaranteed to return NO_APP_ID.
+ *
+ * Even if we are an app frame, AppOwnerAppId() may still return NO_APP_ID, if
+ * this app frame is not contained inside an app.
+ */
+ uint32_t AppOwnerAppId() const;
+ already_AddRefed<mozIApplication> GetAppOwnerApp() const;
+ bool HasAppOwnerApp() const;
+
+ /**
+ * OwnOrContainingAppId() gets the ID of this frame, if HasOwnApp(). If this
+ * frame does not have its own app, it gets the ID of the app which contains
+ * this frame (i.e., the result of {Browser,App}OwnerAppId(), as applicable).
+ */
+ uint32_t OwnOrContainingAppId() const;
+ already_AddRefed<mozIApplication> GetOwnOrContainingApp() const;
+ bool HasOwnOrContainingApp() const;
+
+ /**
+ * OriginAttributesRef() returns the DocShellOriginAttributes of this frame to
+ * the caller. This is used to store any attribute associated with the frame's
+ * docshell, such as the AppId.
+ */
+ const DocShellOriginAttributes& OriginAttributesRef() const;
+
+ /**
+ * Returns the presentation URL associated with the tab if this tab is
+ * created for presented content
+ */
+ const nsAString& PresentationURL() const;
+
+ UIStateChangeType ShowAccelerators() const;
+ UIStateChangeType ShowFocusRings() const;
+
+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);
+
+ /**
+ * Set the tab context's origin attributes to a private browsing value.
+ */
+ void SetPrivateBrowsingAttributes(bool aIsPrivateBrowsing);
+
+ /**
+ * Set the TabContext for this frame. This can either be:
+ * - an app frame (with the given own app) inside the given owner app. Either
+ * apps can be null.
+ * - a browser frame inside the given owner app (which may be null).
+ * - a non-browser, non-app frame. Both own app and owner app should be null.
+ */
+ bool SetTabContext(bool aIsMozBrowserElement,
+ bool aIsPrerendered,
+ mozIApplication* aOwnApp,
+ mozIApplication* aAppFrameOwnerApp,
+ UIStateChangeType aShowAccelerators,
+ UIStateChangeType aShowFocusRings,
+ const DocShellOriginAttributes& aOriginAttributes,
+ const nsAString& aPresentationURL);
+
+ /**
+ * 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 `mIsMozBrowserElement` to be
+ * changed. If any other fields have changed, the update is ignored and
+ * returns false.
+ */
+ bool UpdateTabContextAfterSwap(const TabContext& aContext);
+
+ /**
+ * Whether this TabContext is in prerender mode.
+ */
+ bool mIsPrerendered;
+
+private:
+ /**
+ * Has this TabContext been initialized? If so, mutator methods will fail.
+ */
+ bool mInitialized;
+
+ /**
+ * Whether this TabContext corresponds to a mozbrowser.
+ *
+ * <iframe mozbrowser mozapp> and <xul:browser> are not considered to be
+ * mozbrowser elements.
+ */
+ bool mIsMozBrowserElement;
+
+ /**
+ * This TabContext's own app. If this is non-null, then this
+ * TabContext corresponds to an app, and mIsMozBrowserElement must be false.
+ */
+ nsCOMPtr<mozIApplication> mOwnApp;
+
+ /**
+ * This TabContext's containing app. If mIsMozBrowserElement, this
+ * corresponds to the app which contains the browser frame; otherwise, this
+ * corresponds to the app which contains the app frame.
+ */
+ nsCOMPtr<mozIApplication> mContainingApp;
+
+ /*
+ * Cache of mContainingApp->GetLocalId().
+ */
+ uint32_t mContainingAppId;
+
+ /**
+ * DocShellOriginAttributes of the top level tab docShell
+ */
+ DocShellOriginAttributes mOriginAttributes;
+
+ /**
+ * The requested presentation URL.
+ */
+ nsString mPresentationURL;
+
+ /**
+ * Keyboard indicator state (focus rings, accelerators).
+ */
+ UIStateChangeType mShowAccelerators;
+ UIStateChangeType mShowFocusRings;
+};
+
+/**
+ * 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(bool aIsMozBrowserElement,
+ bool aIsPrerendered,
+ mozIApplication* aOwnApp,
+ mozIApplication* aAppFrameOwnerApp,
+ UIStateChangeType aShowAccelerators,
+ UIStateChangeType aShowFocusRings,
+ const DocShellOriginAttributes& aOriginAttributes,
+ const nsAString& aPresentationURL = EmptyString())
+ {
+ return TabContext::SetTabContext(aIsMozBrowserElement,
+ aIsPrerendered,
+ aOwnApp,
+ aAppFrameOwnerApp,
+ aShowAccelerators,
+ aShowFocusRings,
+ aOriginAttributes,
+ aPresentationURL);
+ }
+};
+
+/**
+ * MaybeInvalidTabContext is a simple class that lets you transform an
+ * IPCTabContext into a TabContext.
+ *
+ * The issue is that an IPCTabContext is not necessarily valid; for example, it
+ * might specify an app-id which doesn't exist. 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/TabMessageUtils.cpp b/dom/ipc/TabMessageUtils.cpp
new file mode 100644
index 000000000..5085a7e95
--- /dev/null
+++ b/dom/ipc/TabMessageUtils.cpp
@@ -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/. */
+
+#include "mozilla/EventDispatcher.h"
+#include "mozilla/dom/Event.h"
+#include "mozilla/dom/TabMessageUtils.h"
+#include "nsCOMPtr.h"
+
+namespace mozilla {
+namespace 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 dom
+} // namespace mozilla
diff --git a/dom/ipc/TabMessageUtils.h b/dom/ipc/TabMessageUtils.h
new file mode 100644
index 000000000..cdb76099d
--- /dev/null
+++ b/dom/ipc/TabMessageUtils.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 TABMESSAGE_UTILS_H
+#define TABMESSAGE_UTILS_H
+
+#include "ipc/IPCMessageUtils.h"
+#include "mozilla/dom/AudioChannelBinding.h"
+#include "nsIDOMEvent.h"
+#include "nsPIDOMWindow.h"
+#include "nsCOMPtr.h"
+
+#ifdef MOZ_CRASHREPORTER
+#include "nsExceptionHandler.h"
+#endif
+
+namespace mozilla {
+namespace dom {
+struct RemoteDOMEvent
+{
+ // Make sure to set the owner after deserializing.
+ nsCOMPtr<nsIDOMEvent> mEvent;
+};
+
+bool ReadRemoteEvent(const IPC::Message* aMsg, PickleIterator* aIter,
+ mozilla::dom::RemoteDOMEvent* aResult);
+
+#ifdef MOZ_CRASHREPORTER
+typedef CrashReporter::ThreadId NativeThreadId;
+#else
+// unused in this case
+typedef int32_t NativeThreadId;
+#endif
+
+} // namespace dom
+} // namespace mozilla
+
+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<mozilla::dom::AudioChannel>
+{
+ typedef mozilla::dom::AudioChannel paramType;
+
+ static bool IsLegalValue(const paramType &aValue)
+ {
+ return aValue <= mozilla::dom::AudioChannel::Publicnotification;
+ }
+
+ static void Write(Message* aMsg, const paramType& aValue)
+ {
+ MOZ_ASSERT(IsLegalValue(aValue));
+ WriteParam(aMsg, (uint32_t)aValue);
+ }
+
+ static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
+ {
+ uint32_t value;
+ if(!ReadParam(aMsg, aIter, &value) ||
+ !IsLegalValue(paramType(value))) {
+ return false;
+ }
+ *aResult = paramType(value);
+ return true;
+ }
+
+ static void Log(const paramType& aParam, std::wstring* aLog)
+ {}
+};
+
+template <>
+struct ParamTraits<nsEventStatus>
+ : public ContiguousEnumSerializer<nsEventStatus,
+ nsEventStatus_eIgnore,
+ nsEventStatus_eSentinel>
+{};
+
+template<>
+struct ParamTraits<nsSizeMode>
+ : public ContiguousEnumSerializer<nsSizeMode,
+ nsSizeMode_Normal,
+ nsSizeMode_Invalid>
+{};
+
+template<>
+struct ParamTraits<UIStateChangeType>
+ : public ContiguousEnumSerializer<UIStateChangeType,
+ UIStateChangeType_NoChange,
+ UIStateChangeType_Invalid>
+{ };
+
+} // namespace IPC
+
+#endif // TABMESSAGE_UTILS_H
diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp
new file mode 100644
index 000000000..0df4c1253
--- /dev/null
+++ b/dom/ipc/TabParent.cpp
@@ -0,0 +1,3306 @@
+/* -*- 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 "TabParent.h"
+
+#include "AudioChannelService.h"
+#include "AppProcessChecker.h"
+#include "mozIApplication.h"
+#ifdef ACCESSIBILITY
+#include "mozilla/a11y/DocAccessibleParent.h"
+#include "nsAccessibilityService.h"
+#endif
+#include "mozilla/BrowserElementParent.h"
+#include "mozilla/dom/ContentBridgeParent.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/DataTransfer.h"
+#include "mozilla/dom/Event.h"
+#include "mozilla/dom/indexedDB/ActorsParent.h"
+#include "mozilla/plugins/PluginWidgetParent.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/DocumentRendererParent.h"
+#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
+#include "mozilla/layers/AsyncDragMetrics.h"
+#include "mozilla/layers/InputAPZContext.h"
+#include "mozilla/layout/RenderFrameParent.h"
+#include "mozilla/LookAndFeel.h"
+#include "mozilla/MouseEvents.h"
+#include "mozilla/net/NeckoChild.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/TextEvents.h"
+#include "mozilla/TouchEvents.h"
+#include "mozilla/UniquePtr.h"
+#include "mozilla/Unused.h"
+#include "BlobParent.h"
+#include "nsCOMPtr.h"
+#include "nsContentAreaDragDrop.h"
+#include "nsContentUtils.h"
+#include "nsDebug.h"
+#include "nsFocusManager.h"
+#include "nsFrameLoader.h"
+#include "nsIBaseWindow.h"
+#include "nsIBrowser.h"
+#include "nsIContent.h"
+#include "nsIDocShell.h"
+#include "nsIDocShellTreeOwner.h"
+#include "nsIDOMElement.h"
+#include "nsIDOMEvent.h"
+#include "nsIDOMWindow.h"
+#include "nsIDOMWindowUtils.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsILoadInfo.h"
+#include "nsPrincipal.h"
+#include "nsIPromptFactory.h"
+#include "nsIURI.h"
+#include "nsIWindowWatcher.h"
+#include "nsIWebBrowserChrome.h"
+#include "nsIXULBrowserWindow.h"
+#include "nsIXULWindow.h"
+#include "nsIRemoteBrowser.h"
+#include "nsViewManager.h"
+#include "nsVariant.h"
+#include "nsIWidget.h"
+#ifndef XP_WIN
+#include "nsJARProtocolHandler.h"
+#endif
+#include "nsPIDOMWindow.h"
+#include "nsPresShell.h"
+#include "nsPrintfCString.h"
+#include "nsServiceManagerUtils.h"
+#include "nsThreadUtils.h"
+#include "PermissionMessageUtils.h"
+#include "StructuredCloneData.h"
+#include "ColorPickerParent.h"
+#include "DatePickerParent.h"
+#include "FilePickerParent.h"
+#include "TabChild.h"
+#include "LoadContext.h"
+#include "nsNetCID.h"
+#include "nsIAuthInformation.h"
+#include "nsIAuthPromptCallback.h"
+#include "nsAuthInformationHolder.h"
+#include "nsICancelable.h"
+#include "gfxPrefs.h"
+#include "nsILoginManagerPrompter.h"
+#include "nsPIWindowRoot.h"
+#include "nsIAuthPrompt2.h"
+#include "gfxDrawable.h"
+#include "ImageOps.h"
+#include "UnitTransforms.h"
+#include <algorithm>
+#include "mozilla/WebBrowserPersistDocumentParent.h"
+#include "nsIGroupedSHistory.h"
+#include "PartialSHistory.h"
+
+#if defined(XP_WIN) && defined(ACCESSIBILITY)
+#include "mozilla/a11y/AccessibleWrap.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::jsipc;
+using namespace mozilla::gfx;
+
+using mozilla::Unused;
+
+// The flags passed by the webProgress notifications are 16 bits shifted
+// from the ones registered by webProgressListeners.
+#define NOTIFY_FLAG_SHIFT 16
+
+namespace mozilla {
+namespace dom {
+
+TabParent::LayerToTabParentTable* TabParent::sLayerToTabParentTable = nullptr;
+
+NS_IMPL_ISUPPORTS(TabParent,
+ nsITabParent,
+ nsIAuthPromptProvider,
+ nsISecureBrowserUI,
+ nsISupportsWeakReference,
+ nsIWebBrowserPersistable)
+
+TabParent::TabParent(nsIContentParent* aManager,
+ const TabId& aTabId,
+ const TabContext& aContext,
+ uint32_t aChromeFlags)
+ : TabContext(aContext)
+ , mFrameElement(nullptr)
+ , mRect(0, 0, 0, 0)
+ , mDimensions(0, 0)
+ , mOrientation(0)
+ , mDPI(0)
+ , mRounding(0)
+ , mDefaultScale(0)
+ , mUpdatedDimensions(false)
+ , mSizeMode(nsSizeMode_Normal)
+ , mManager(aManager)
+ , mDocShellIsActive(false)
+ , mMarkedDestroying(false)
+ , mIsDestroyed(false)
+ , mChromeFlags(aChromeFlags)
+ , mDragValid(false)
+ , mInitedByParent(false)
+ , mTabId(aTabId)
+ , mCreatingWindow(false)
+ , mCursor(nsCursor(-1))
+ , mTabSetsCursor(false)
+ , mHasContentOpener(false)
+#ifdef DEBUG
+ , mActiveSupressDisplayportCount(0)
+#endif
+ , mLayerTreeEpoch(0)
+ , mPreserveLayers(false)
+{
+ MOZ_ASSERT(aManager);
+}
+
+TabParent::~TabParent()
+{
+}
+
+TabParent*
+TabParent::GetTabParentFromLayersId(uint64_t aLayersId)
+{
+ if (!sLayerToTabParentTable) {
+ return nullptr;
+ }
+ return sLayerToTabParentTable->Get(aLayersId);
+}
+
+void
+TabParent::AddTabParentToTable(uint64_t aLayersId, TabParent* aTabParent)
+{
+ if (!sLayerToTabParentTable) {
+ sLayerToTabParentTable = new LayerToTabParentTable();
+ }
+ sLayerToTabParentTable->Put(aLayersId, aTabParent);
+}
+
+void
+TabParent::RemoveTabParentFromTable(uint64_t aLayersId)
+{
+ if (!sLayerToTabParentTable) {
+ return;
+ }
+ sLayerToTabParentTable->Remove(aLayersId);
+ if (sLayerToTabParentTable->Count() == 0) {
+ delete sLayerToTabParentTable;
+ sLayerToTabParentTable = nullptr;
+ }
+}
+
+void
+TabParent::CacheFrameLoader(nsFrameLoader* aFrameLoader)
+{
+ mFrameLoader = aFrameLoader;
+}
+
+/**
+ * Will return nullptr if there is no outer window available for the
+ * document hosting the owner element of this TabParent. Also will return
+ * nullptr if that outer window is in the process of closing.
+ */
+already_AddRefed<nsPIDOMWindowOuter>
+TabParent::GetParentWindowOuter()
+{
+ nsCOMPtr<nsIContent> frame = do_QueryInterface(GetOwnerElement());
+ if (!frame) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> parent = frame->OwnerDoc()->GetWindow();
+ if (!parent || parent->Closed()) {
+ return nullptr;
+ }
+
+ return parent.forget();
+}
+
+void
+TabParent::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 (curTopLevelWin && !isSameTopLevelWin) {
+ curTopLevelWin->RemoveBrowser(this);
+ }
+
+ // Update to the new content, and register to listen for events from it.
+ mFrameElement = aElement;
+
+ if (newTopLevelWin && !isSameTopLevelWin) {
+ newTopLevelWin->AddBrowser(this);
+ }
+
+ if (mFrameElement) {
+ bool useGlobalHistory =
+ !mFrameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::disableglobalhistory);
+ Unused << SendSetUseGlobalHistory(useGlobalHistory);
+ }
+
+#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);
+ }
+#endif
+
+ AddWindowListeners();
+ TryCacheDPIAndScale();
+}
+
+void
+TabParent::AddWindowListeners()
+{
+ if (mFrameElement && mFrameElement->OwnerDoc()) {
+ if (nsCOMPtr<nsPIDOMWindowOuter> window = mFrameElement->OwnerDoc()->GetWindow()) {
+ nsCOMPtr<EventTarget> eventTarget = window->GetTopWindowRoot();
+ if (eventTarget) {
+ eventTarget->AddEventListener(NS_LITERAL_STRING("MozUpdateWindowPos"),
+ this, false, false);
+ }
+ }
+ if (nsIPresShell* shell = mFrameElement->OwnerDoc()->GetShell()) {
+ mPresShellWithRefreshListener = shell;
+ shell->AddPostRefreshObserver(this);
+ }
+
+ RefPtr<AudioChannelService> acs = AudioChannelService::GetOrCreate();
+ if (acs) {
+ acs->RegisterTabParent(this);
+ }
+ }
+}
+
+void
+TabParent::RemoveWindowListeners()
+{
+ if (mFrameElement && mFrameElement->OwnerDoc()->GetWindow()) {
+ nsCOMPtr<nsPIDOMWindowOuter> window = mFrameElement->OwnerDoc()->GetWindow();
+ nsCOMPtr<EventTarget> eventTarget = window->GetTopWindowRoot();
+ if (eventTarget) {
+ eventTarget->RemoveEventListener(NS_LITERAL_STRING("MozUpdateWindowPos"),
+ this, false);
+ }
+ }
+ if (mPresShellWithRefreshListener) {
+ mPresShellWithRefreshListener->RemovePostRefreshObserver(this);
+ mPresShellWithRefreshListener = nullptr;
+ }
+
+ RefPtr<AudioChannelService> acs = AudioChannelService::GetOrCreate();
+ if (acs) {
+ acs->UnregisterTabParent(this);
+ }
+}
+
+void
+TabParent::DidRefresh()
+{
+ if (mChromeOffset != -GetChildProcessOffset()) {
+ UpdatePosition();
+ }
+}
+
+void
+TabParent::GetAppType(nsAString& aOut)
+{
+ aOut.Truncate();
+ nsCOMPtr<Element> elem = do_QueryInterface(mFrameElement);
+ if (!elem) {
+ return;
+ }
+
+ elem->GetAttr(kNameSpaceID_None, nsGkAtoms::mozapptype, aOut);
+}
+
+bool
+TabParent::IsVisible() const
+{
+ RefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
+ if (!frameLoader) {
+ return false;
+ }
+
+ return frameLoader->GetVisible();
+}
+
+void
+TabParent::DestroyInternal()
+{
+ IMEStateManager::OnTabParentDestroying(this);
+
+ RemoveWindowListeners();
+
+ // 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();
+
+ if (RenderFrameParent* frame = GetRenderFrame()) {
+ RemoveTabParentFromTable(frame->GetLayersId());
+ frame->Destroy();
+ }
+
+ // 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();
+ }
+}
+
+void
+TabParent::Destroy()
+{
+ // Aggressively release the window to avoid leaking the world in shutdown
+ // corner cases.
+ mBrowserDOMWindow = nullptr;
+
+ if (mIsDestroyed) {
+ return;
+ }
+
+ DestroyInternal();
+
+ mIsDestroyed = true;
+
+ if (XRE_IsParentProcess()) {
+ ContentParent::NotifyTabDestroying(this->GetTabId(), Manager()->AsContentParent()->ChildID());
+ } else {
+ ContentParent::NotifyTabDestroying(this->GetTabId(), Manager()->ChildID());
+ }
+
+ mMarkedDestroying = true;
+}
+
+bool
+TabParent::RecvEnsureLayersConnected()
+{
+ if (RenderFrameParent* frame = GetRenderFrame()) {
+ frame->EnsureLayersConnected();
+ }
+ return true;
+}
+
+bool
+TabParent::Recv__delete__()
+{
+ if (XRE_IsParentProcess()) {
+ ContentParent::DeallocateTabId(mTabId,
+ Manager()->AsContentParent()->ChildID(),
+ mMarkedDestroying);
+ }
+ else {
+ Manager()->AsContentBridgeParent()->NotifyTabDestroyed();
+ ContentParent::DeallocateTabId(mTabId,
+ Manager()->ChildID(),
+ mMarkedDestroying);
+ }
+
+ return true;
+}
+
+void
+TabParent::ActorDestroy(ActorDestroyReason why)
+{
+ // Even though TabParent::Destroy calls this, we need to do it here too in
+ // case of a crash.
+ IMEStateManager::OnTabParentDestroying(this);
+
+ // Prevent executing ContentParent::NotifyTabDestroying in
+ // TabParent::Destroy() called by frameLoader->DestroyComplete() below
+ // when tab crashes in contentprocess because ContentParent::ActorDestroy()
+ // in main process will be triggered before this function
+ // and remove the process information that
+ // ContentParent::NotifyTabDestroying need from mContentParentMap.
+
+ // When tab crashes in content process,
+ // there is no need to call ContentParent::NotifyTabDestroying
+ // because the jobs in ContentParent::NotifyTabDestroying
+ // will be done by ContentParent::ActorDestroy.
+ if (XRE_IsContentProcess() && why == AbnormalShutdown && !mIsDestroyed) {
+ DestroyInternal();
+ mIsDestroyed = true;
+ }
+
+ RefPtr<nsFrameLoader> frameLoader = GetFrameLoader(true);
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ if (frameLoader) {
+ nsCOMPtr<Element> frameElement(mFrameElement);
+ ReceiveMessage(CHILD_PROCESS_SHUTDOWN_MESSAGE, false, nullptr, nullptr,
+ nullptr);
+ frameLoader->DestroyComplete();
+
+ if (why == AbnormalShutdown && os) {
+ os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, frameLoader),
+ "oop-frameloader-crashed", nullptr);
+ nsCOMPtr<nsIFrameLoaderOwner> owner = do_QueryInterface(frameElement);
+ if (owner) {
+ RefPtr<nsFrameLoader> currentFrameLoader = owner->GetFrameLoader();
+ // It's possible that the frameloader owner has already moved on
+ // and created a new frameloader. If so, we don't fire the event,
+ // since the frameloader owner has clearly moved on.
+ if (currentFrameLoader == frameLoader) {
+ nsContentUtils::DispatchTrustedEvent(frameElement->OwnerDoc(), frameElement,
+ NS_LITERAL_STRING("oop-browser-crashed"),
+ true, true);
+
+ }
+ }
+ }
+
+ mFrameLoader = nullptr;
+ }
+
+ if (os) {
+ os->NotifyObservers(NS_ISUPPORTS_CAST(nsITabParent*, this), "ipc:browser-destroyed", nullptr);
+ }
+}
+
+bool
+TabParent::RecvMoveFocus(const bool& aForward, const bool& aForDocumentNavigation)
+{
+ nsCOMPtr<nsIFocusManager> fm = do_GetService(FOCUSMANAGER_CONTRACTID);
+ if (fm) {
+ nsCOMPtr<nsIDOMElement> 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));
+ nsCOMPtr<nsIDOMElement> frame = do_QueryInterface(mFrameElement);
+ fm->MoveFocus(nullptr, frame, type, nsIFocusManager::FLAG_BYKEY,
+ getter_AddRefs(dummy));
+ }
+ return true;
+}
+
+bool
+TabParent::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, true);
+
+ nsCOMPtr<nsIDocShell> docShell = mFrameElement->OwnerDoc()->GetDocShell();
+ NS_ENSURE_TRUE(docShell, true);
+
+ nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
+ nsresult rv = docShell->GetTreeOwner(getter_AddRefs(treeOwner));
+ NS_ENSURE_SUCCESS(rv, true);
+
+ 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<nsIXULWindow> xulWin(do_GetInterface(treeOwner));
+ NS_ENSURE_TRUE(xulWin, true);
+ xulWin->SizeShellToWithLimit(width, height, aShellItemWidth, aShellItemHeight);
+
+ return true;
+}
+
+bool
+TabParent::RecvDropLinks(nsTArray<nsString>&& aLinks)
+{
+ nsCOMPtr<nsIBrowser> browser = do_QueryInterface(mFrameElement);
+ if (browser) {
+ UniquePtr<const char16_t*[]> links;
+ links = MakeUnique<const char16_t*[]>(aLinks.Length());
+ for (uint32_t i = 0; i < aLinks.Length(); i++) {
+ links[i] = aLinks[i].get();
+ }
+ browser->DropLinks(aLinks.Length(), links.get());
+ }
+ return true;
+}
+
+bool
+TabParent::RecvEvent(const RemoteDOMEvent& aEvent)
+{
+ nsCOMPtr<nsIDOMEvent> event = do_QueryInterface(aEvent.mEvent);
+ NS_ENSURE_TRUE(event, true);
+
+ nsCOMPtr<mozilla::dom::EventTarget> target = do_QueryInterface(mFrameElement);
+ NS_ENSURE_TRUE(target, true);
+
+ event->SetOwner(target);
+
+ bool dummy;
+ target->DispatchEvent(event, &dummy);
+ return true;
+}
+
+TabParent* TabParent::sNextTabParent;
+
+/* static */ TabParent*
+TabParent::GetNextTabParent()
+{
+ TabParent* result = sNextTabParent;
+ sNextTabParent = nullptr;
+ return result;
+}
+
+bool
+TabParent::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
+TabParent::LoadURL(nsIURI* aURI)
+{
+ MOZ_ASSERT(aURI);
+
+ if (mIsDestroyed) {
+ return;
+ }
+
+ nsCString spec;
+ aURI->GetSpec(spec);
+
+ if (mCreatingWindow) {
+ // Don't send the message if the child wants to load its own URL.
+ MOZ_ASSERT(mDelayedURL.IsEmpty());
+ mDelayedURL = spec;
+ return;
+ }
+
+ Unused << SendLoadURL(spec, GetShowInfo());
+}
+
+void
+TabParent::Show(const ScreenIntSize& size, bool aParentIsActive)
+{
+ mDimensions = size;
+ if (mIsDestroyed) {
+ return;
+ }
+
+ TextureFactoryIdentifier textureFactoryIdentifier;
+ uint64_t layersId = 0;
+ bool success = false;
+ RenderFrameParent* renderFrame = nullptr;
+ if (IsInitedByParent()) {
+ // If TabParent is initialized by parent side then the RenderFrame must also
+ // be created here. If TabParent is initialized by child side,
+ // child side will create RenderFrame.
+ MOZ_ASSERT(!GetRenderFrame());
+ RefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
+ if (frameLoader) {
+ renderFrame = new RenderFrameParent(frameLoader, &success);
+ MOZ_ASSERT(success);
+ layersId = renderFrame->GetLayersId();
+ renderFrame->GetTextureFactoryIdentifier(&textureFactoryIdentifier);
+ AddTabParentToTable(layersId, this);
+ Unused << SendPRenderFrameConstructor(renderFrame);
+ }
+ } else {
+ // Otherwise, the child should have constructed the RenderFrame,
+ // and we should already know about it.
+ MOZ_ASSERT(GetRenderFrame());
+ }
+
+ nsCOMPtr<nsISupports> container = mFrameElement->OwnerDoc()->GetContainer();
+ nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(container);
+ nsCOMPtr<nsIWidget> mainWidget;
+ baseWindow->GetMainWidget(getter_AddRefs(mainWidget));
+ mSizeMode = mainWidget ? mainWidget->SizeMode() : nsSizeMode_Normal;
+
+ Unused << SendShow(size, GetShowInfo(), textureFactoryIdentifier,
+ layersId, renderFrame, aParentIsActive, mSizeMode);
+}
+
+bool
+TabParent::RecvSetDimensions(const uint32_t& aFlags,
+ const int32_t& aX, const int32_t& aY,
+ const int32_t& aCx, const int32_t& aCy)
+{
+ MOZ_ASSERT(!(aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_SIZE_INNER),
+ "We should never see DIM_FLAGS_SIZE_INNER here!");
+
+ NS_ENSURE_TRUE(mFrameElement, true);
+ nsCOMPtr<nsIDocShell> docShell = mFrameElement->OwnerDoc()->GetDocShell();
+ NS_ENSURE_TRUE(docShell, true);
+ nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
+ docShell->GetTreeOwner(getter_AddRefs(treeOwner));
+ nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = do_QueryInterface(treeOwner);
+ NS_ENSURE_TRUE(treeOwnerAsWin, true);
+
+ // We only care about the parameters that actually changed, see more
+ // details in TabChild::SetDimensions.
+ int32_t unused;
+ int32_t x = aX;
+ if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_X) {
+ treeOwnerAsWin->GetPosition(&x, &unused);
+ }
+
+ int32_t y = aY;
+ if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_Y) {
+ treeOwnerAsWin->GetPosition(&unused, &y);
+ }
+
+ int32_t cx = aCx;
+ if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_CX) {
+ treeOwnerAsWin->GetSize(&cx, &unused);
+ }
+
+ int32_t cy = aCy;
+ if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_IGNORE_CY) {
+ treeOwnerAsWin->GetSize(&unused, &cy);
+ }
+
+ if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_POSITION &&
+ aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_SIZE_OUTER) {
+ treeOwnerAsWin->SetPositionAndSize(x, y, cx, cy,
+ nsIBaseWindow::eRepaint);
+ return true;
+ }
+
+ if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_POSITION) {
+ treeOwnerAsWin->SetPosition(x, y);
+ mUpdatedDimensions = false;
+ UpdatePosition();
+ return true;
+ }
+
+ if (aFlags & nsIEmbeddingSiteWindow::DIM_FLAGS_SIZE_OUTER) {
+ treeOwnerAsWin->SetSize(cx, cy, true);
+ return true;
+ }
+
+ MOZ_ASSERT(false, "Unknown flags!");
+ return false;
+}
+
+nsresult
+TabParent::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
+TabParent::UpdateDimensions(const nsIntRect& rect, const ScreenIntSize& size)
+{
+ if (mIsDestroyed) {
+ return;
+ }
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (!widget) {
+ NS_WARNING("No widget found in TabParent::UpdateDimensions");
+ return;
+ }
+
+ hal::ScreenConfiguration config;
+ hal::GetCurrentScreenConfiguration(&config);
+ ScreenOrientationInternal orientation = config.orientation();
+ LayoutDeviceIntPoint clientOffset = widget->GetClientOffset();
+ LayoutDeviceIntPoint chromeOffset = -GetChildProcessOffset();
+
+ 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;
+
+ 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;
+ Unused << SendUpdateDimensions(unscaledRect, unscaledSize,
+ orientation, clientOffset, chromeOffset);
+ }
+}
+
+void
+TabParent::SizeModeChanged(const nsSizeMode& aSizeMode)
+{
+ if (!mIsDestroyed && aSizeMode != mSizeMode) {
+ mSizeMode = aSizeMode;
+ Unused << SendSizeModeChanged(aSizeMode);
+ }
+}
+
+void
+TabParent::UIResolutionChanged()
+{
+ 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
+TabParent::ThemeChanged()
+{
+ if (!mIsDestroyed) {
+ // The theme has changed, and any cached values we had sent down
+ // to the child have been invalidated. When this method is called,
+ // LookAndFeel should have the up-to-date values, which we now
+ // send down to the child. We do this for every remote tab for now,
+ // but bug 1156934 has been filed to do it once per content process.
+ Unused << SendThemeChanged(LookAndFeel::GetIntCache());
+ }
+}
+
+void
+TabParent::HandleAccessKey(const WidgetKeyboardEvent& aEvent,
+ nsTArray<uint32_t>& aCharCodes,
+ const int32_t& aModifierMask)
+{
+ if (!mIsDestroyed) {
+ Unused << SendHandleAccessKey(aEvent, aCharCodes, aModifierMask);
+ }
+}
+
+void
+TabParent::Activate()
+{
+ if (!mIsDestroyed) {
+ Unused << SendActivate();
+ }
+}
+
+void
+TabParent::Deactivate()
+{
+ if (!mIsDestroyed) {
+ Unused << SendDeactivate();
+ }
+}
+
+NS_IMETHODIMP
+TabParent::Init(mozIDOMWindowProxy *window)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+TabParent::GetState(uint32_t *aState)
+{
+ NS_ENSURE_ARG(aState);
+ NS_WARNING("SecurityState not valid here");
+ *aState = 0;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+TabParent::SetDocShell(nsIDocShell *aDocShell)
+{
+ NS_ENSURE_ARG(aDocShell);
+ NS_WARNING("No mDocShell member in TabParent so there is no docShell to set");
+ return NS_OK;
+}
+
+ a11y::PDocAccessibleParent*
+TabParent::AllocPDocAccessibleParent(PDocAccessibleParent* aParent,
+ const uint64_t&, const uint32_t&,
+ const IAccessibleHolder&)
+{
+#ifdef ACCESSIBILITY
+ return new a11y::DocAccessibleParent();
+#else
+ return nullptr;
+#endif
+}
+
+bool
+TabParent::DeallocPDocAccessibleParent(PDocAccessibleParent* aParent)
+{
+#ifdef ACCESSIBILITY
+ delete static_cast<a11y::DocAccessibleParent*>(aParent);
+#endif
+ return true;
+}
+
+bool
+TabParent::RecvPDocAccessibleConstructor(PDocAccessibleParent* aDoc,
+ PDocAccessibleParent* aParentDoc,
+ const uint64_t& aParentID,
+ const uint32_t& aMsaaID,
+ const IAccessibleHolder& aDocCOMProxy)
+{
+#ifdef ACCESSIBILITY
+ auto doc = static_cast<a11y::DocAccessibleParent*>(aDoc);
+ 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 false;
+ }
+
+ auto parentDoc = static_cast<a11y::DocAccessibleParent*>(aParentDoc);
+ bool added = parentDoc->AddChildDoc(doc, aParentID);
+#ifdef XP_WIN
+ MOZ_ASSERT(aDocCOMProxy.IsNull());
+ if (added) {
+ a11y::WrapperFor(doc)->SetID(aMsaaID);
+ }
+#endif
+ return added;
+ } 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 false;
+ }
+
+ doc->SetTopLevel();
+ a11y::DocManager::RemoteDocAdded(doc);
+#ifdef XP_WIN
+ a11y::WrapperFor(doc)->SetID(aMsaaID);
+ MOZ_ASSERT(!aDocCOMProxy.IsNull());
+ RefPtr<IAccessible> proxy(aDocCOMProxy.Get());
+ doc->SetCOMProxy(proxy);
+#endif
+ }
+#endif
+ return true;
+}
+
+a11y::DocAccessibleParent*
+TabParent::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());
+ if (!doc->ParentDoc()) {
+ 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;
+}
+
+PDocumentRendererParent*
+TabParent::AllocPDocumentRendererParent(const nsRect& documentRect,
+ const gfx::Matrix& transform,
+ const nsString& bgcolor,
+ const uint32_t& renderFlags,
+ const bool& flushLayout,
+ const nsIntSize& renderSize)
+{
+ return new DocumentRendererParent();
+}
+
+bool
+TabParent::DeallocPDocumentRendererParent(PDocumentRendererParent* actor)
+{
+ delete actor;
+ return true;
+}
+
+PFilePickerParent*
+TabParent::AllocPFilePickerParent(const nsString& aTitle, const int16_t& aMode)
+{
+ return new FilePickerParent(aTitle, aMode);
+}
+
+bool
+TabParent::DeallocPFilePickerParent(PFilePickerParent* actor)
+{
+ delete actor;
+ return true;
+}
+
+auto
+TabParent::AllocPIndexedDBPermissionRequestParent(const Principal& aPrincipal)
+ -> PIndexedDBPermissionRequestParent*
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ nsCOMPtr<nsIPrincipal> principal(aPrincipal);
+ if (!principal) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIContentParent> manager = Manager();
+ if (manager->IsContentParent()) {
+ if (NS_WARN_IF(!AssertAppPrincipal(manager->AsContentParent(),
+ principal))) {
+ return nullptr;
+ }
+ } else {
+ MOZ_CRASH("Figure out security checks for bridged content!");
+ }
+
+ if (NS_WARN_IF(!mFrameElement)) {
+ return nullptr;
+ }
+
+ return
+ mozilla::dom::indexedDB::AllocPIndexedDBPermissionRequestParent(mFrameElement,
+ principal);
+}
+
+bool
+TabParent::RecvPIndexedDBPermissionRequestConstructor(
+ PIndexedDBPermissionRequestParent* aActor,
+ const Principal& aPrincipal)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aActor);
+
+ return
+ mozilla::dom::indexedDB::RecvPIndexedDBPermissionRequestConstructor(aActor);
+}
+
+bool
+TabParent::DeallocPIndexedDBPermissionRequestParent(
+ PIndexedDBPermissionRequestParent* aActor)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aActor);
+
+ return
+ mozilla::dom::indexedDB::DeallocPIndexedDBPermissionRequestParent(aActor);
+}
+
+void
+TabParent::SendMouseEvent(const nsAString& aType, float aX, float aY,
+ int32_t aButton, int32_t aClickCount,
+ int32_t aModifiers, bool aIgnoreRootScrollFrame)
+{
+ if (!mIsDestroyed) {
+ Unused << PBrowserParent::SendMouseEvent(nsString(aType), aX, aY,
+ aButton, aClickCount,
+ aModifiers, aIgnoreRootScrollFrame);
+ }
+}
+
+void
+TabParent::SendKeyEvent(const nsAString& aType,
+ int32_t aKeyCode,
+ int32_t aCharCode,
+ int32_t aModifiers,
+ bool aPreventDefault)
+{
+ if (!mIsDestroyed) {
+ Unused << PBrowserParent::SendKeyEvent(nsString(aType), aKeyCode, aCharCode,
+ aModifiers, aPreventDefault);
+ }
+}
+
+bool TabParent::SendRealMouseEvent(WidgetMouseEvent& event)
+{
+ if (mIsDestroyed) {
+ return false;
+ }
+ event.mRefPoint += GetChildProcessOffset();
+
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (widget) {
+ // When we mouseenter the tab, the tab's cursor should
+ // become the current cursor. When we mouseexit, we stop.
+ if (eMouseEnterIntoWidget == event.mMessage) {
+ mTabSetsCursor = true;
+ if (mCustomCursor) {
+ widget->SetCursor(mCustomCursor, mCustomCursorHotspotX, mCustomCursorHotspotY);
+ } else if (mCursor != nsCursor(-1)) {
+ widget->SetCursor(mCursor);
+ }
+ } else if (eMouseExitFromWidget == event.mMessage) {
+ mTabSetsCursor = false;
+ }
+ }
+
+ ScrollableLayerGuid guid;
+ uint64_t blockId;
+ ApzAwareEventRoutingToChild(&guid, &blockId, nullptr);
+
+ if (eMouseMove == event.mMessage) {
+ if (event.mReason == WidgetMouseEvent::eSynthesized) {
+ return SendSynthMouseMoveEvent(event, guid, blockId);
+ } else {
+ return SendRealMouseMoveEvent(event, guid, blockId);
+ }
+ }
+
+ return SendRealMouseButtonEvent(event, guid, blockId);
+}
+
+LayoutDeviceToCSSScale
+TabParent::GetLayoutDeviceToCSSScale()
+{
+ nsCOMPtr<nsIContent> content = do_QueryInterface(mFrameElement);
+ nsIDocument* doc = (content ? content->OwnerDoc() : nullptr);
+ nsIPresShell* shell = (doc ? doc->GetShell() : nullptr);
+ nsPresContext* ctx = (shell ? shell->GetPresContext() : nullptr);
+ return LayoutDeviceToCSSScale(ctx
+ ? (float)ctx->AppUnitsPerDevPixel() / nsPresContext::AppUnitsPerCSSPixel()
+ : 0.0f);
+}
+
+bool
+TabParent::SendRealDragEvent(WidgetDragEvent& event, uint32_t aDragAction,
+ uint32_t aDropEffect)
+{
+ if (mIsDestroyed) {
+ return false;
+ }
+ event.mRefPoint += GetChildProcessOffset();
+ return PBrowserParent::SendRealDragEvent(event, aDragAction, aDropEffect);
+}
+
+LayoutDevicePoint TabParent::AdjustTapToChildWidget(const LayoutDevicePoint& aPoint)
+{
+ return aPoint + LayoutDevicePoint(GetChildProcessOffset());
+}
+
+bool TabParent::SendMouseWheelEvent(WidgetWheelEvent& event)
+{
+ if (mIsDestroyed) {
+ return false;
+ }
+
+ ScrollableLayerGuid guid;
+ uint64_t blockId;
+ ApzAwareEventRoutingToChild(&guid, &blockId, nullptr);
+ event.mRefPoint += GetChildProcessOffset();
+ return PBrowserParent::SendMouseWheelEvent(event, guid, blockId);
+}
+
+bool TabParent::RecvDispatchWheelEvent(const mozilla::WidgetWheelEvent& aEvent)
+{
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (!widget) {
+ return true;
+ }
+
+ WidgetWheelEvent localEvent(aEvent);
+ localEvent.mWidget = widget;
+ localEvent.mRefPoint -= GetChildProcessOffset();
+
+ widget->DispatchInputEvent(&localEvent);
+ return true;
+}
+
+bool
+TabParent::RecvDispatchMouseEvent(const mozilla::WidgetMouseEvent& aEvent)
+{
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (!widget) {
+ return true;
+ }
+
+ WidgetMouseEvent localEvent(aEvent);
+ localEvent.mWidget = widget;
+ localEvent.mRefPoint -= GetChildProcessOffset();
+
+ widget->DispatchInputEvent(&localEvent);
+ return true;
+}
+
+bool
+TabParent::RecvDispatchKeyboardEvent(const mozilla::WidgetKeyboardEvent& aEvent)
+{
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (!widget) {
+ return true;
+ }
+
+ WidgetKeyboardEvent localEvent(aEvent);
+ localEvent.mWidget = widget;
+ localEvent.mRefPoint -= GetChildProcessOffset();
+
+ widget->DispatchInputEvent(&localEvent);
+ return true;
+}
+
+static void
+DoCommandCallback(mozilla::Command aCommand, void* aData)
+{
+ static_cast<InfallibleTArray<mozilla::CommandInt>*>(aData)->AppendElement(aCommand);
+}
+
+bool
+TabParent::RecvRequestNativeKeyBindings(const WidgetKeyboardEvent& aEvent,
+ MaybeNativeKeyBinding* aBindings)
+{
+ AutoTArray<mozilla::CommandInt, 4> singleLine;
+ AutoTArray<mozilla::CommandInt, 4> multiLine;
+ AutoTArray<mozilla::CommandInt, 4> richText;
+
+ *aBindings = mozilla::void_t();
+
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (!widget) {
+ return true;
+ }
+
+ WidgetKeyboardEvent localEvent(aEvent);
+
+ if (NS_FAILED(widget->AttachNativeKeyEvent(localEvent))) {
+ return true;
+ }
+
+ widget->ExecuteNativeKeyBinding(nsIWidget::NativeKeyBindingsForSingleLineEditor,
+ localEvent, DoCommandCallback, &singleLine);
+ widget->ExecuteNativeKeyBinding(nsIWidget::NativeKeyBindingsForMultiLineEditor,
+ localEvent, DoCommandCallback, &multiLine);
+ widget->ExecuteNativeKeyBinding(nsIWidget::NativeKeyBindingsForRichTextEditor,
+ localEvent, DoCommandCallback, &richText);
+
+ if (!singleLine.IsEmpty() || !multiLine.IsEmpty() || !richText.IsEmpty()) {
+ *aBindings = NativeKeyBinding(singleLine, multiLine, richText);
+ }
+
+ return true;
+}
+
+class SynthesizedEventObserver : public nsIObserver
+{
+ NS_DECL_ISUPPORTS
+
+public:
+ SynthesizedEventObserver(TabParent* aTabParent, const uint64_t& aObserverId)
+ : mTabParent(aTabParent)
+ , mObserverId(aObserverId)
+ {
+ MOZ_ASSERT(mTabParent);
+ }
+
+ NS_IMETHOD Observe(nsISupports* aSubject,
+ const char* aTopic,
+ const char16_t* aData) override
+ {
+ if (!mTabParent || !mObserverId) {
+ // We already sent the notification, or we don't actually need to
+ // send any notification at all.
+ return NS_OK;
+ }
+
+ if (!mTabParent->SendNativeSynthesisResponse(mObserverId, nsCString(aTopic))) {
+ NS_WARNING("Unable to send native event synthesization response!");
+ }
+ // Null out tabparent to indicate we already sent the response
+ mTabParent = nullptr;
+ return NS_OK;
+ }
+
+private:
+ virtual ~SynthesizedEventObserver() { }
+
+ RefPtr<TabParent> mTabParent;
+ uint64_t mObserverId;
+};
+
+NS_IMPL_ISUPPORTS(SynthesizedEventObserver, nsIObserver)
+
+class MOZ_STACK_CLASS AutoSynthesizedEventResponder
+{
+public:
+ AutoSynthesizedEventResponder(TabParent* aTabParent,
+ const uint64_t& aObserverId,
+ const char* aTopic)
+ : mObserver(new SynthesizedEventObserver(aTabParent, 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;
+};
+
+bool
+TabParent::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 true;
+}
+
+bool
+TabParent::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 true;
+}
+
+bool
+TabParent::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 true;
+}
+
+bool
+TabParent::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 true;
+}
+
+bool
+TabParent::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 true;
+}
+
+bool
+TabParent::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 true;
+}
+
+bool
+TabParent::RecvClearNativeTouchSequence(const uint64_t& aObserverId)
+{
+ AutoSynthesizedEventResponder responder(this, aObserverId, "cleartouch");
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (widget) {
+ widget->ClearNativeTouchSequence(responder.GetObserver());
+ }
+ return true;
+}
+
+bool TabParent::SendRealKeyEvent(WidgetKeyboardEvent& event)
+{
+ if (mIsDestroyed) {
+ return false;
+ }
+ event.mRefPoint += GetChildProcessOffset();
+
+ MaybeNativeKeyBinding bindings;
+ bindings = void_t();
+ if (event.mMessage == eKeyPress) {
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+
+ AutoTArray<mozilla::CommandInt, 4> singleLine;
+ AutoTArray<mozilla::CommandInt, 4> multiLine;
+ AutoTArray<mozilla::CommandInt, 4> richText;
+
+ widget->ExecuteNativeKeyBinding(nsIWidget::NativeKeyBindingsForSingleLineEditor,
+ event, DoCommandCallback, &singleLine);
+ widget->ExecuteNativeKeyBinding(nsIWidget::NativeKeyBindingsForMultiLineEditor,
+ event, DoCommandCallback, &multiLine);
+ widget->ExecuteNativeKeyBinding(nsIWidget::NativeKeyBindingsForRichTextEditor,
+ event, DoCommandCallback, &richText);
+
+ if (!singleLine.IsEmpty() || !multiLine.IsEmpty() || !richText.IsEmpty()) {
+ bindings = NativeKeyBinding(singleLine, multiLine, richText);
+ }
+ }
+
+ return PBrowserParent::SendRealKeyEvent(event, bindings);
+}
+
+bool TabParent::SendRealTouchEvent(WidgetTouchEvent& event)
+{
+ if (mIsDestroyed) {
+ return false;
+ }
+
+ // 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 (event.mMessage == eTouchEnd || event.mMessage == eTouchCancel) {
+ for (int i = event.mTouches.Length() - 1; i >= 0; i--) {
+ if (!event.mTouches[i]->mChanged) {
+ event.mTouches.RemoveElementAt(i);
+ }
+ }
+ }
+
+ ScrollableLayerGuid guid;
+ uint64_t blockId;
+ nsEventStatus apzResponse;
+ ApzAwareEventRoutingToChild(&guid, &blockId, &apzResponse);
+
+ if (mIsDestroyed) {
+ return false;
+ }
+
+ LayoutDeviceIntPoint offset = GetChildProcessOffset();
+ for (uint32_t i = 0; i < event.mTouches.Length(); i++) {
+ event.mTouches[i]->mRefPoint += offset;
+ }
+
+ return (event.mMessage == eTouchMove) ?
+ PBrowserParent::SendRealTouchMoveEvent(event, guid, blockId, apzResponse) :
+ PBrowserParent::SendRealTouchEvent(event, guid, blockId, apzResponse);
+}
+
+bool
+TabParent::SendHandleTap(TapType aType,
+ const LayoutDevicePoint& aPoint,
+ Modifiers aModifiers,
+ const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId)
+{
+ if (mIsDestroyed) {
+ return false;
+ }
+ if ((aType == TapType::eSingleTap || aType == TapType::eSecondTap) &&
+ GetRenderFrame()) {
+ GetRenderFrame()->TakeFocusForClickFromTap();
+ }
+ LayoutDeviceIntPoint offset = GetChildProcessOffset();
+ return PBrowserParent::SendHandleTap(aType, aPoint + offset, aModifiers, aGuid,
+ aInputBlockId);
+}
+
+bool
+TabParent::RecvSyncMessage(const nsString& aMessage,
+ const ClonedMessageData& aData,
+ InfallibleTArray<CpowEntry>&& aCpows,
+ const IPC::Principal& aPrincipal,
+ nsTArray<StructuredCloneData>* aRetVal)
+{
+ // FIXME Permission check for TabParent in Content process
+ nsIPrincipal* principal = aPrincipal;
+ if (Manager()->IsContentParent()) {
+ ContentParent* parent = Manager()->AsContentParent();
+ if (!ContentParent::IgnoreIPCPrincipal() &&
+ parent && principal && !AssertAppPrincipal(parent, principal)) {
+ return false;
+ }
+ }
+
+ StructuredCloneData data;
+ ipc::UnpackClonedMessageDataForParent(aData, data);
+
+ CrossProcessCpowHolder cpows(Manager(), aCpows);
+ return ReceiveMessage(aMessage, true, &data, &cpows, aPrincipal, aRetVal);
+}
+
+bool
+TabParent::RecvRpcMessage(const nsString& aMessage,
+ const ClonedMessageData& aData,
+ InfallibleTArray<CpowEntry>&& aCpows,
+ const IPC::Principal& aPrincipal,
+ nsTArray<StructuredCloneData>* aRetVal)
+{
+ // FIXME Permission check for TabParent in Content process
+ nsIPrincipal* principal = aPrincipal;
+ if (Manager()->IsContentParent()) {
+ ContentParent* parent = Manager()->AsContentParent();
+ if (!ContentParent::IgnoreIPCPrincipal() &&
+ parent && principal && !AssertAppPrincipal(parent, principal)) {
+ return false;
+ }
+ }
+
+ StructuredCloneData data;
+ ipc::UnpackClonedMessageDataForParent(aData, data);
+
+ CrossProcessCpowHolder cpows(Manager(), aCpows);
+ return ReceiveMessage(aMessage, true, &data, &cpows, aPrincipal, aRetVal);
+}
+
+bool
+TabParent::RecvAsyncMessage(const nsString& aMessage,
+ InfallibleTArray<CpowEntry>&& aCpows,
+ const IPC::Principal& aPrincipal,
+ const ClonedMessageData& aData)
+{
+ // FIXME Permission check for TabParent in Content process
+ nsIPrincipal* principal = aPrincipal;
+ if (Manager()->IsContentParent()) {
+ ContentParent* parent = Manager()->AsContentParent();
+ if (!ContentParent::IgnoreIPCPrincipal() &&
+ parent && principal && !AssertAppPrincipal(parent, principal)) {
+ return false;
+ }
+ }
+
+ StructuredCloneData data;
+ ipc::UnpackClonedMessageDataForParent(aData, data);
+
+ CrossProcessCpowHolder cpows(Manager(), aCpows);
+ return ReceiveMessage(aMessage, false, &data, &cpows, aPrincipal, nullptr);
+}
+
+bool
+TabParent::RecvSetCursor(const uint32_t& aCursor, const bool& aForce)
+{
+ mCursor = static_cast<nsCursor>(aCursor);
+ mCustomCursor = nullptr;
+
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (widget) {
+ if (aForce) {
+ widget->ClearCachedCursor();
+ }
+ if (mTabSetsCursor) {
+ widget->SetCursor(mCursor);
+ }
+ }
+ return true;
+}
+
+bool
+TabParent::RecvSetCustomCursor(const nsCString& aCursorData,
+ const uint32_t& aWidth,
+ const uint32_t& aHeight,
+ const uint32_t& aStride,
+ const uint8_t& aFormat,
+ const uint32_t& aHotspotX,
+ const uint32_t& aHotspotY,
+ const bool& aForce)
+{
+ mCursor = nsCursor(-1);
+
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (widget) {
+ if (aForce) {
+ widget->ClearCachedCursor();
+ }
+
+ if (mTabSetsCursor) {
+ const gfx::IntSize size(aWidth, aHeight);
+
+ RefPtr<gfx::DataSourceSurface> customCursor =
+ gfx::CreateDataSourceSurfaceFromData(size,
+ static_cast<gfx::SurfaceFormat>(aFormat),
+ reinterpret_cast<const uint8_t*>(aCursorData.BeginReading()),
+ aStride);
+
+ RefPtr<gfxDrawable> drawable = new gfxSurfaceDrawable(customCursor, size);
+ nsCOMPtr<imgIContainer> cursorImage(image::ImageOps::CreateFromDrawable(drawable));
+ widget->SetCursor(cursorImage, aHotspotX, aHotspotY);
+ mCustomCursor = cursorImage;
+ mCustomCursorHotspotX = aHotspotX;
+ mCustomCursorHotspotY = aHotspotY;
+ }
+ }
+
+ return true;
+}
+
+nsIXULBrowserWindow*
+TabParent::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<nsIXULWindow> window = do_GetInterface(treeOwner);
+ if (!window) {
+ return nullptr;
+ }
+
+ nsCOMPtr<nsIXULBrowserWindow> xulBrowserWindow;
+ window->GetXULBrowserWindow(getter_AddRefs(xulBrowserWindow));
+ return xulBrowserWindow;
+}
+
+bool
+TabParent::RecvSetStatus(const uint32_t& aType, const nsString& aStatus)
+{
+ nsCOMPtr<nsIXULBrowserWindow> xulBrowserWindow = GetXULBrowserWindow();
+ if (!xulBrowserWindow) {
+ return true;
+ }
+
+ switch (aType) {
+ case nsIWebBrowserChrome::STATUS_SCRIPT:
+ xulBrowserWindow->SetJSStatus(aStatus);
+ break;
+ case nsIWebBrowserChrome::STATUS_LINK:
+ xulBrowserWindow->SetOverLink(aStatus, nullptr);
+ break;
+ }
+ return true;
+}
+
+bool
+TabParent::RecvShowTooltip(const uint32_t& aX, const uint32_t& aY, const nsString& aTooltip,
+ const nsString& aDirection)
+{
+ nsCOMPtr<nsIXULBrowserWindow> xulBrowserWindow = GetXULBrowserWindow();
+ if (!xulBrowserWindow) {
+ return true;
+ }
+
+ xulBrowserWindow->ShowTooltip(aX, aY, aTooltip, aDirection);
+ return true;
+}
+
+bool
+TabParent::RecvHideTooltip()
+{
+ nsCOMPtr<nsIXULBrowserWindow> xulBrowserWindow = GetXULBrowserWindow();
+ if (!xulBrowserWindow) {
+ return true;
+ }
+
+ xulBrowserWindow->HideTooltip();
+ return true;
+}
+
+bool
+TabParent::RecvNotifyIMEFocus(const ContentCache& aContentCache,
+ const IMENotification& aIMENotification,
+ nsIMEUpdatePreference* aPreference)
+{
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (!widget) {
+ *aPreference = nsIMEUpdatePreference();
+ return true;
+ }
+
+ mContentCache.AssignContent(aContentCache, widget, &aIMENotification);
+ IMEStateManager::NotifyIME(aIMENotification, widget, true);
+
+ if (aIMENotification.mMessage == NOTIFY_IME_OF_FOCUS) {
+ *aPreference = widget->GetIMEUpdatePreference();
+ }
+ return true;
+}
+
+bool
+TabParent::RecvNotifyIMETextChange(const ContentCache& aContentCache,
+ const IMENotification& aIMENotification)
+{
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (!widget)
+ return true;
+
+#ifdef DEBUG
+ nsIMEUpdatePreference updatePreference = widget->GetIMEUpdatePreference();
+ NS_ASSERTION(updatePreference.WantTextChange(),
+ "Don't call Send/RecvNotifyIMETextChange without NOTIFY_TEXT_CHANGE");
+#endif
+
+ mContentCache.AssignContent(aContentCache, widget, &aIMENotification);
+ mContentCache.MaybeNotifyIME(widget, aIMENotification);
+ return true;
+}
+
+bool
+TabParent::RecvNotifyIMECompositionUpdate(
+ const ContentCache& aContentCache,
+ const IMENotification& aIMENotification)
+{
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (!widget) {
+ return true;
+ }
+
+ mContentCache.AssignContent(aContentCache, widget, &aIMENotification);
+ mContentCache.MaybeNotifyIME(widget, aIMENotification);
+ return true;
+}
+
+bool
+TabParent::RecvNotifyIMESelection(const ContentCache& aContentCache,
+ const IMENotification& aIMENotification)
+{
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (!widget)
+ return true;
+
+ mContentCache.AssignContent(aContentCache, widget, &aIMENotification);
+ mContentCache.MaybeNotifyIME(widget, aIMENotification);
+ return true;
+}
+
+bool
+TabParent::RecvUpdateContentCache(const ContentCache& aContentCache)
+{
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (!widget) {
+ return true;
+ }
+
+ mContentCache.AssignContent(aContentCache, widget);
+ return true;
+}
+
+bool
+TabParent::RecvNotifyIMEMouseButtonEvent(
+ const IMENotification& aIMENotification,
+ bool* aConsumedByIME)
+{
+
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (!widget) {
+ *aConsumedByIME = false;
+ return true;
+ }
+ nsresult rv = IMEStateManager::NotifyIME(aIMENotification, widget, true);
+ *aConsumedByIME = rv == NS_SUCCESS_EVENT_CONSUMED;
+ return true;
+}
+
+bool
+TabParent::RecvNotifyIMEPositionChange(const ContentCache& aContentCache,
+ const IMENotification& aIMENotification)
+{
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (!widget) {
+ return true;
+ }
+
+ mContentCache.AssignContent(aContentCache, widget, &aIMENotification);
+ mContentCache.MaybeNotifyIME(widget, aIMENotification);
+ return true;
+}
+
+bool
+TabParent::RecvOnEventNeedingAckHandled(const EventMessage& aMessage)
+{
+ // This is called when the child process receives WidgetCompositionEvent or
+ // WidgetSelectionEvent.
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (!widget) {
+ return true;
+ }
+
+ // While calling OnEventNeedingAckHandled(), TabParent *might* be destroyed
+ // since it may send notifications to IME.
+ RefPtr<TabParent> kungFuDeathGrip(this);
+ mContentCache.OnEventNeedingAckHandled(widget, aMessage);
+ return true;
+}
+
+void
+TabParent::HandledWindowedPluginKeyEvent(const NativeEventData& aKeyEventData,
+ bool aIsConsumed)
+{
+ DebugOnly<bool> ok =
+ SendHandledWindowedPluginKeyEvent(aKeyEventData, aIsConsumed);
+ NS_WARNING_ASSERTION(ok, "SendHandledWindowedPluginKeyEvent failed");
+}
+
+bool
+TabParent::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 true;
+ }
+ 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 true;
+ }
+
+ // 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 true;
+ }
+
+ // 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 true;
+}
+
+bool
+TabParent::RecvRequestFocus(const bool& aCanRaise)
+{
+ nsCOMPtr<nsIFocusManager> fm = nsFocusManager::GetFocusManager();
+ if (!fm) {
+ return true;
+ }
+
+ nsCOMPtr<nsIContent> content = do_QueryInterface(mFrameElement);
+ if (!content || !content->OwnerDoc()) {
+ return true;
+ }
+
+ uint32_t flags = nsIFocusManager::FLAG_NOSCROLL;
+ if (aCanRaise)
+ flags |= nsIFocusManager::FLAG_RAISE;
+
+ nsCOMPtr<nsIDOMElement> node = do_QueryInterface(mFrameElement);
+ fm->SetFocus(node, flags);
+ return true;
+}
+
+bool
+TabParent::RecvEnableDisableCommands(const nsString& aAction,
+ nsTArray<nsCString>&& aEnabledCommands,
+ nsTArray<nsCString>&& aDisabledCommands)
+{
+ nsCOMPtr<nsIRemoteBrowser> remoteBrowser = do_QueryInterface(mFrameElement);
+ if (remoteBrowser) {
+ UniquePtr<const char*[]> enabledCommands, disabledCommands;
+
+ if (aEnabledCommands.Length()) {
+ enabledCommands = MakeUnique<const char*[]>(aEnabledCommands.Length());
+ for (uint32_t c = 0; c < aEnabledCommands.Length(); c++) {
+ enabledCommands[c] = aEnabledCommands[c].get();
+ }
+ }
+
+ if (aDisabledCommands.Length()) {
+ disabledCommands = MakeUnique<const char*[]>(aDisabledCommands.Length());
+ for (uint32_t c = 0; c < aDisabledCommands.Length(); c++) {
+ disabledCommands[c] = aDisabledCommands[c].get();
+ }
+ }
+
+ remoteBrowser->EnableDisableCommands(aAction,
+ aEnabledCommands.Length(), enabledCommands.get(),
+ aDisabledCommands.Length(), disabledCommands.get());
+ }
+
+ return true;
+}
+
+NS_IMETHODIMP
+TabParent::GetChildProcessOffset(int32_t* aOutCssX, int32_t* aOutCssY)
+{
+ NS_ENSURE_ARG(aOutCssX);
+ NS_ENSURE_ARG(aOutCssY);
+ CSSPoint offset = LayoutDevicePoint(GetChildProcessOffset())
+ * GetLayoutDeviceToCSSScale();
+ *aOutCssX = offset.x;
+ *aOutCssY = offset.y;
+ return NS_OK;
+}
+
+LayoutDeviceIntPoint
+TabParent::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;
+ }
+
+ // Find out how far we're offset from the nearest widget.
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (!widget) {
+ return offset;
+ }
+ nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(widget,
+ LayoutDeviceIntPoint(0, 0),
+ targetFrame);
+
+ return LayoutDeviceIntPoint::FromAppUnitsToNearest(
+ pt, targetFrame->PresContext()->AppUnitsPerDevPixel());
+}
+
+bool
+TabParent::RecvReplyKeyEvent(const WidgetKeyboardEvent& event)
+{
+ NS_ENSURE_TRUE(mFrameElement, true);
+
+ WidgetKeyboardEvent localEvent(event);
+ // Mark the event as not to be dispatched to remote process again.
+ localEvent.StopCrossProcessForwarding();
+
+ // Here we convert the WidgetEvent that we received to an nsIDOMEvent
+ // to be able to dispatch it to the <browser> element as the target element.
+ nsIDocument* doc = mFrameElement->OwnerDoc();
+ nsIPresShell* presShell = doc->GetShell();
+ NS_ENSURE_TRUE(presShell, true);
+ nsPresContext* presContext = presShell->GetPresContext();
+ NS_ENSURE_TRUE(presContext, true);
+
+ AutoHandlingUserInputStatePusher userInpStatePusher(localEvent.IsTrusted(),
+ &localEvent, doc);
+
+ EventDispatcher::Dispatch(mFrameElement, presContext, &localEvent);
+ return true;
+}
+
+bool
+TabParent::RecvDispatchAfterKeyboardEvent(const WidgetKeyboardEvent& aEvent)
+{
+ NS_ENSURE_TRUE(mFrameElement, true);
+
+ WidgetKeyboardEvent localEvent(aEvent);
+ localEvent.mWidget = GetWidget();
+
+ nsIDocument* doc = mFrameElement->OwnerDoc();
+ nsCOMPtr<nsIPresShell> presShell = doc->GetShell();
+ NS_ENSURE_TRUE(presShell, true);
+
+ if (mFrameElement &&
+ PresShell::BeforeAfterKeyboardEventEnabled() &&
+ localEvent.mMessage != eKeyPress) {
+ presShell->DispatchAfterKeyboardEvent(mFrameElement, localEvent,
+ aEvent.DefaultPrevented());
+ }
+
+ return true;
+}
+
+bool
+TabParent::RecvAccessKeyNotHandled(const WidgetKeyboardEvent& aEvent)
+{
+ NS_ENSURE_TRUE(mFrameElement, true);
+
+ WidgetKeyboardEvent localEvent(aEvent);
+ localEvent.mMessage = eAccessKeyNotFound;
+ localEvent.mAccessKeyForwardedToChild = false;
+
+ // Here we convert the WidgetEvent that we received to an nsIDOMEvent
+ // to be able to dispatch it to the <browser> element as the target element.
+ nsIDocument* doc = mFrameElement->OwnerDoc();
+ nsIPresShell* presShell = doc->GetShell();
+ NS_ENSURE_TRUE(presShell, true);
+
+ if (presShell->CanDispatchEvent()) {
+ nsPresContext* presContext = presShell->GetPresContext();
+ NS_ENSURE_TRUE(presContext, true);
+
+ EventDispatcher::Dispatch(mFrameElement, presContext, &localEvent);
+ }
+
+ return true;
+}
+
+bool
+TabParent::HandleQueryContentEvent(WidgetQueryContentEvent& aEvent)
+{
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (!widget) {
+ return true;
+ }
+ if (NS_WARN_IF(!mContentCache.HandleQueryContentEvent(aEvent, widget)) ||
+ NS_WARN_IF(!aEvent.mSucceeded)) {
+ return true;
+ }
+ switch (aEvent.mMessage) {
+ case eQueryTextRect:
+ case eQueryCaretRect:
+ case eQueryEditorRect:
+ aEvent.mReply.mRect -= GetChildProcessOffset();
+ break;
+ default:
+ break;
+ }
+ return true;
+}
+
+bool
+TabParent::SendCompositionEvent(WidgetCompositionEvent& event)
+{
+ if (mIsDestroyed) {
+ return false;
+ }
+
+ if (!mContentCache.OnCompositionEvent(event)) {
+ return true;
+ }
+ return PBrowserParent::SendCompositionEvent(event);
+}
+
+bool
+TabParent::SendSelectionEvent(WidgetSelectionEvent& event)
+{
+ if (mIsDestroyed) {
+ return false;
+ }
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (!widget) {
+ return true;
+ }
+ mContentCache.OnSelectionEvent(event);
+ if (NS_WARN_IF(!PBrowserParent::SendSelectionEvent(event))) {
+ return false;
+ }
+ event.mSucceeded = true;
+ return true;
+}
+
+bool
+TabParent::SendPasteTransferable(const IPCDataTransfer& aDataTransfer,
+ const bool& aIsPrivateData,
+ const IPC::Principal& aRequestingPrincipal)
+{
+ return PBrowserParent::SendPasteTransferable(aDataTransfer,
+ aIsPrivateData,
+ aRequestingPrincipal);
+}
+
+/*static*/ TabParent*
+TabParent::GetFrom(nsFrameLoader* aFrameLoader)
+{
+ if (!aFrameLoader) {
+ return nullptr;
+ }
+ PBrowserParent* remoteBrowser = aFrameLoader->GetRemoteBrowser();
+ return static_cast<TabParent*>(remoteBrowser);
+}
+
+/*static*/ TabParent*
+TabParent::GetFrom(nsIFrameLoader* aFrameLoader)
+{
+ if (!aFrameLoader)
+ return nullptr;
+ return GetFrom(static_cast<nsFrameLoader*>(aFrameLoader));
+}
+
+/*static*/ TabParent*
+TabParent::GetFrom(nsITabParent* aTabParent)
+{
+ return static_cast<TabParent*>(aTabParent);
+}
+
+/*static*/ TabParent*
+TabParent::GetFrom(PBrowserParent* aTabParent)
+{
+ return static_cast<TabParent*>(aTabParent);
+}
+
+/*static*/ TabParent*
+TabParent::GetFrom(nsIContent* aContent)
+{
+ nsCOMPtr<nsIFrameLoaderOwner> loaderOwner = do_QueryInterface(aContent);
+ if (!loaderOwner) {
+ return nullptr;
+ }
+ RefPtr<nsFrameLoader> frameLoader = loaderOwner->GetFrameLoader();
+ return GetFrom(frameLoader);
+}
+
+/*static*/ TabId
+TabParent::GetTabIdFrom(nsIDocShell *docShell)
+{
+ nsCOMPtr<nsITabChild> tabChild(TabChild::GetFrom(docShell));
+ if (tabChild) {
+ return static_cast<TabChild*>(tabChild.get())->GetTabId();
+ }
+ return TabId(0);
+}
+
+RenderFrameParent*
+TabParent::GetRenderFrame()
+{
+ PRenderFrameParent* p = LoneManagedOrNullAsserts(ManagedPRenderFrameParent());
+ RenderFrameParent* frame = static_cast<RenderFrameParent*>(p);
+
+ return frame;
+}
+
+bool
+TabParent::RecvRequestIMEToCommitComposition(const bool& aCancel,
+ bool* aIsCommitted,
+ nsString* aCommittedString)
+{
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (!widget) {
+ *aIsCommitted = false;
+ return true;
+ }
+
+ *aIsCommitted =
+ mContentCache.RequestIMEToCommitComposition(widget, aCancel,
+ *aCommittedString);
+ return true;
+}
+
+bool
+TabParent::RecvStartPluginIME(const WidgetKeyboardEvent& aKeyboardEvent,
+ const int32_t& aPanelX, const int32_t& aPanelY,
+ nsString* aCommitted)
+{
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (!widget) {
+ return true;
+ }
+ widget->StartPluginIME(aKeyboardEvent,
+ (int32_t&)aPanelX,
+ (int32_t&)aPanelY,
+ *aCommitted);
+ return true;
+}
+
+bool
+TabParent::RecvSetPluginFocused(const bool& aFocused)
+{
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (!widget) {
+ return true;
+ }
+ widget->SetPluginFocused((bool&)aFocused);
+ return true;
+}
+
+ bool
+TabParent::RecvSetCandidateWindowForPlugin(
+ const CandidateWindowPosition& aPosition)
+{
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (!widget) {
+ return true;
+ }
+
+ widget->SetCandidateWindowForPlugin(aPosition);
+ return true;
+}
+
+bool
+TabParent::RecvDefaultProcOfPluginEvent(const WidgetPluginEvent& aEvent)
+{
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (!widget) {
+ return true;
+ }
+
+ widget->DefaultProcOfPluginEvent(aEvent);
+ return true;
+}
+
+bool
+TabParent::RecvGetInputContext(int32_t* aIMEEnabled,
+ int32_t* aIMEOpen)
+{
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (!widget) {
+ *aIMEEnabled = IMEState::DISABLED;
+ *aIMEOpen = IMEState::OPEN_STATE_NOT_SUPPORTED;
+ return true;
+ }
+
+ InputContext context = widget->GetInputContext();
+ *aIMEEnabled = static_cast<int32_t>(context.mIMEState.mEnabled);
+ *aIMEOpen = static_cast<int32_t>(context.mIMEState.mOpen);
+ return true;
+}
+
+bool
+TabParent::RecvSetInputContext(const int32_t& aIMEEnabled,
+ const int32_t& aIMEOpen,
+ const nsString& aType,
+ const nsString& aInputmode,
+ const nsString& aActionHint,
+ const int32_t& aCause,
+ const int32_t& aFocusChange)
+{
+ InputContext context;
+ context.mIMEState.mEnabled = static_cast<IMEState::Enabled>(aIMEEnabled);
+ context.mIMEState.mOpen = static_cast<IMEState::Open>(aIMEOpen);
+ context.mHTMLInputType.Assign(aType);
+ context.mHTMLInputInputmode.Assign(aInputmode);
+ context.mActionHint.Assign(aActionHint);
+ context.mOrigin = InputContext::ORIGIN_CONTENT;
+
+ InputContextAction action(
+ static_cast<InputContextAction::Cause>(aCause),
+ static_cast<InputContextAction::FocusChange>(aFocusChange));
+
+ IMEStateManager::SetInputContextForChildProcess(this, context, action);
+
+ return true;
+}
+
+bool
+TabParent::RecvIsParentWindowMainWidgetVisible(bool* aIsVisible)
+{
+ nsCOMPtr<nsIContent> frame = do_QueryInterface(mFrameElement);
+ if (!frame)
+ return true;
+ nsCOMPtr<nsIDOMWindowUtils> windowUtils =
+ do_QueryInterface(frame->OwnerDoc()->GetWindow());
+ nsresult rv = windowUtils->GetIsParentWindowMainWidgetVisible(aIsVisible);
+ return NS_SUCCEEDED(rv);
+}
+
+bool
+TabParent::RecvGetDPI(float* aValue)
+{
+ TryCacheDPIAndScale();
+
+ MOZ_ASSERT(mDPI > 0,
+ "Must not ask for DPI before OwnerElement is received!");
+ *aValue = mDPI;
+ return true;
+}
+
+bool
+TabParent::RecvGetDefaultScale(double* aValue)
+{
+ TryCacheDPIAndScale();
+
+ MOZ_ASSERT(mDefaultScale.scale > 0,
+ "Must not ask for scale before OwnerElement is received!");
+ *aValue = mDefaultScale.scale;
+ return true;
+}
+
+bool
+TabParent::RecvGetWidgetRounding(int32_t* aValue)
+{
+ TryCacheDPIAndScale();
+
+ MOZ_ASSERT(mRounding > 0,
+ "Must not ask for rounding before OwnerElement is received!");
+ *aValue = mRounding;
+ return true;
+}
+
+bool
+TabParent::RecvGetMaxTouchPoints(uint32_t* aTouchPoints)
+{
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (widget) {
+ *aTouchPoints = widget->GetMaxTouchPoints();
+ } else {
+ *aTouchPoints = 0;
+ }
+ return true;
+}
+
+already_AddRefed<nsIWidget>
+TabParent::GetTopLevelWidget()
+{
+ nsCOMPtr<nsIContent> content = do_QueryInterface(mFrameElement);
+ if (content) {
+ nsIPresShell* shell = content->OwnerDoc()->GetShell();
+ if (shell) {
+ nsViewManager* vm = shell->GetViewManager();
+ nsCOMPtr<nsIWidget> widget;
+ vm->GetRootWidget(getter_AddRefs(widget));
+ return widget.forget();
+ }
+ }
+ return nullptr;
+}
+
+bool
+TabParent::RecvGetWidgetNativeData(WindowsHandle* aValue)
+{
+ *aValue = 0;
+ nsCOMPtr<nsIWidget> widget = GetTopLevelWidget();
+ if (widget) {
+ *aValue = reinterpret_cast<WindowsHandle>(
+ widget->GetNativeData(NS_NATIVE_SHAREABLE_WINDOW));
+ }
+ return true;
+}
+
+bool
+TabParent::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 true;
+#else
+ NS_NOTREACHED(
+ "TabParent::RecvSetNativeChildOfShareableWindow not implemented!");
+ return false;
+#endif
+}
+
+bool
+TabParent::RecvDispatchFocusToTopLevelWindow()
+{
+ nsCOMPtr<nsIWidget> widget = GetTopLevelWidget();
+ if (widget) {
+ widget->SetFocus(false);
+ }
+ return true;
+}
+
+bool
+TabParent::ReceiveMessage(const nsString& aMessage,
+ bool aSync,
+ StructuredCloneData* aData,
+ CpowHolder* aCpows,
+ nsIPrincipal* aPrincipal,
+ nsTArray<StructuredCloneData>* aRetVal)
+{
+ RefPtr<nsFrameLoader> frameLoader = GetFrameLoader(true);
+ if (frameLoader && frameLoader->GetFrameMessageManager()) {
+ RefPtr<nsFrameMessageManager> manager =
+ frameLoader->GetFrameMessageManager();
+
+ manager->ReceiveMessage(mFrameElement,
+ frameLoader,
+ aMessage,
+ aSync,
+ aData,
+ aCpows,
+ aPrincipal,
+ aRetVal);
+ }
+ return true;
+}
+
+// nsIAuthPromptProvider
+
+// This method is largely copied from nsDocShell::GetAuthPrompt
+NS_IMETHODIMP
+TabParent::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;
+ nsCOMPtr<nsIContent> frame = do_QueryInterface(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<nsILoginManagerPrompter> prompter = do_QueryInterface(prompt);
+ if (prompter) {
+ nsCOMPtr<nsIDOMElement> browser = do_QueryInterface(mFrameElement);
+ prompter->SetBrowser(browser);
+ }
+
+ *aResult = prompt.forget().take();
+ return NS_OK;
+}
+
+PColorPickerParent*
+TabParent::AllocPColorPickerParent(const nsString& aTitle,
+ const nsString& aInitialColor)
+{
+ return new ColorPickerParent(aTitle, aInitialColor);
+}
+
+bool
+TabParent::DeallocPColorPickerParent(PColorPickerParent* actor)
+{
+ delete actor;
+ return true;
+}
+
+PDatePickerParent*
+TabParent::AllocPDatePickerParent(const nsString& aTitle,
+ const nsString& aInitialDate)
+{
+ return new DatePickerParent(aTitle, aInitialDate);
+}
+
+bool
+TabParent::DeallocPDatePickerParent(PDatePickerParent* actor)
+{
+ delete actor;
+ return true;
+}
+
+PRenderFrameParent*
+TabParent::AllocPRenderFrameParent()
+{
+ MOZ_ASSERT(ManagedPRenderFrameParent().IsEmpty());
+ RefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
+ uint64_t layersId = 0;
+ bool success = false;
+
+ PRenderFrameParent* renderFrame =
+ new RenderFrameParent(frameLoader, &success);
+ if (success) {
+ RenderFrameParent* rfp = static_cast<RenderFrameParent*>(renderFrame);
+ layersId = rfp->GetLayersId();
+ AddTabParentToTable(layersId, this);
+ }
+ return renderFrame;
+}
+
+bool
+TabParent::DeallocPRenderFrameParent(PRenderFrameParent* aFrame)
+{
+ delete aFrame;
+ return true;
+}
+
+bool
+TabParent::SetRenderFrame(PRenderFrameParent* aRFParent)
+{
+ if (IsInitedByParent()) {
+ return false;
+ }
+
+ RefPtr<nsFrameLoader> frameLoader = GetFrameLoader();
+
+ if (!frameLoader) {
+ return false;
+ }
+
+ RenderFrameParent* renderFrame = static_cast<RenderFrameParent*>(aRFParent);
+ bool success = renderFrame->Init(frameLoader);
+ if (!success) {
+ return false;
+ }
+
+ uint64_t layersId = renderFrame->GetLayersId();
+ AddTabParentToTable(layersId, this);
+
+ return true;
+}
+
+bool
+TabParent::GetRenderFrameInfo(TextureFactoryIdentifier* aTextureFactoryIdentifier,
+ uint64_t* aLayersId)
+{
+ RenderFrameParent* rfp = GetRenderFrame();
+ if (!rfp) {
+ return false;
+ }
+
+ *aLayersId = rfp->GetLayersId();
+ rfp->GetTextureFactoryIdentifier(aTextureFactoryIdentifier);
+ return true;
+}
+
+bool
+TabParent::RecvAudioChannelActivityNotification(const uint32_t& aAudioChannel,
+ const bool& aActive)
+{
+ if (aAudioChannel >= NUMBER_OF_AUDIO_CHANNELS) {
+ return false;
+ }
+
+ nsCOMPtr<nsIObserverService> os = services::GetObserverService();
+ if (os) {
+ nsAutoCString topic;
+ topic.Assign("audiochannel-activity-");
+ topic.Append(AudioChannelService::GetAudioChannelTable()[aAudioChannel].tag);
+
+ os->NotifyObservers(NS_ISUPPORTS_CAST(nsITabParent*, this),
+ topic.get(),
+ aActive ? u"active" : u"inactive");
+ }
+
+ return true;
+}
+
+already_AddRefed<nsFrameLoader>
+TabParent::GetFrameLoader(bool aUseCachedFrameLoaderAfterDestroy) const
+{
+ if (mIsDestroyed && !aUseCachedFrameLoaderAfterDestroy) {
+ return nullptr;
+ }
+
+ if (mFrameLoader) {
+ RefPtr<nsFrameLoader> fl = mFrameLoader;
+ return fl.forget();
+ }
+ nsCOMPtr<nsIFrameLoaderOwner> frameLoaderOwner = do_QueryInterface(mFrameElement);
+ return frameLoaderOwner ? frameLoaderOwner->GetFrameLoader() : nullptr;
+}
+
+void
+TabParent::TryCacheDPIAndScale()
+{
+ if (mDPI > 0) {
+ return;
+ }
+
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+
+ if (widget) {
+ mDPI = widget->GetDPI();
+ mRounding = widget->RoundsWidgetCoordinatesTo();
+ mDefaultScale = widget->GetDefaultScale();
+ }
+}
+
+already_AddRefed<nsIWidget>
+TabParent::GetWidget() const
+{
+ if (!mFrameElement) {
+ return nullptr;
+ }
+ nsCOMPtr<nsIWidget> widget = nsContentUtils::WidgetForDocument(mFrameElement->OwnerDoc());
+ return widget.forget();
+}
+
+void
+TabParent::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 renderframe. In such cases the main-thread hit-
+ // testing code "wins" so we need to update the guid to reflect this.
+ if (RenderFrameParent* rfp = GetRenderFrame()) {
+ if (aOutTargetGuid->mLayersId != rfp->GetLayersId()) {
+ *aOutTargetGuid = ScrollableLayerGuid(rfp->GetLayersId(), 0, FrameMetrics::NULL_SCROLL_ID);
+ }
+ }
+ }
+ if (aOutInputBlockId) {
+ *aOutInputBlockId = InputAPZContext::GetInputBlockId();
+ }
+ if (aOutApzResponse) {
+ *aOutApzResponse = InputAPZContext::GetApzResponse();
+ }
+ } else {
+ if (aOutInputBlockId) {
+ *aOutInputBlockId = 0;
+ }
+ if (aOutApzResponse) {
+ *aOutApzResponse = nsEventStatus_eIgnore;
+ }
+ }
+}
+
+bool
+TabParent::RecvBrowserFrameOpenWindow(PBrowserParent* aOpener,
+ PRenderFrameParent* aRenderFrame,
+ const nsString& aURL,
+ const nsString& aName,
+ const nsString& aFeatures,
+ bool* aOutWindowOpened,
+ TextureFactoryIdentifier* aTextureFactoryIdentifier,
+ uint64_t* aLayersId)
+{
+ BrowserElementParent::OpenWindowResult opened =
+ BrowserElementParent::OpenWindowOOP(TabParent::GetFrom(aOpener),
+ this, aRenderFrame, aURL, aName, aFeatures,
+ aTextureFactoryIdentifier, aLayersId);
+ *aOutWindowOpened = (opened == BrowserElementParent::OPEN_WINDOW_ADDED);
+ if (!*aOutWindowOpened) {
+ Destroy();
+ }
+ return true;
+}
+
+bool
+TabParent::RecvRespondStartSwipeEvent(const uint64_t& aInputBlockId,
+ const bool& aStartSwipe)
+{
+ if (nsCOMPtr<nsIWidget> widget = GetWidget()) {
+ widget->ReportSwipeStarted(aInputBlockId, aStartSwipe);
+ }
+ return true;
+}
+
+already_AddRefed<nsILoadContext>
+TabParent::GetLoadContext()
+{
+ nsCOMPtr<nsILoadContext> loadContext;
+ if (mLoadContext) {
+ loadContext = mLoadContext;
+ } else {
+ bool isPrivate = mChromeFlags & nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW;
+ SetPrivateBrowsingAttributes(isPrivate);
+ loadContext = new LoadContext(GetOwnerElement(),
+ true /* aIsContent */,
+ isPrivate,
+ mChromeFlags & nsIWebBrowserChrome::CHROME_REMOTE_WINDOW,
+ OriginAttributesRef());
+ mLoadContext = loadContext;
+ }
+ return loadContext.forget();
+}
+
+NS_IMETHODIMP
+TabParent::GetUseAsyncPanZoom(bool* useAsyncPanZoom)
+{
+ *useAsyncPanZoom = AsyncPanZoomEnabled();
+ return NS_OK;
+}
+
+// defined in nsITabParent
+NS_IMETHODIMP
+TabParent::SetDocShellIsActive(bool isActive)
+{
+ // Increment the epoch so that layer tree updates from previous
+ // SetDocShellIsActive requests are ignored.
+ mLayerTreeEpoch++;
+
+ // docshell is consider prerendered only if not active yet
+ mIsPrerendered &= !isActive;
+ mDocShellIsActive = isActive;
+ Unused << SendSetDocShellIsActive(isActive, mPreserveLayers, mLayerTreeEpoch);
+
+ // Ask the child to repaint using the PHangMonitor channel/thread (which may
+ // be less congested).
+ if (isActive) {
+ ContentParent* cp = Manager()->AsContentParent();
+ cp->ForceTabPaint(this, mLayerTreeEpoch);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+TabParent::GetDocShellIsActive(bool* aIsActive)
+{
+ *aIsActive = mDocShellIsActive;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+TabParent::GetIsPrerendered(bool* aIsPrerendered)
+{
+ *aIsPrerendered = mIsPrerendered;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+TabParent::PreserveLayers(bool aPreserveLayers)
+{
+ mPreserveLayers = aPreserveLayers;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+TabParent::SuppressDisplayport(bool aEnabled)
+{
+ if (IsDestroyed()) {
+ return NS_OK;
+ }
+
+#ifdef DEBUG
+ if (aEnabled) {
+ mActiveSupressDisplayportCount++;
+ } else {
+ mActiveSupressDisplayportCount--;
+ }
+ MOZ_ASSERT(mActiveSupressDisplayportCount >= 0);
+#endif
+
+ Unused << SendSuppressDisplayport(aEnabled);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+TabParent::GetTabId(uint64_t* aId)
+{
+ *aId = GetTabId();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+TabParent::GetOsPid(int32_t* aId)
+{
+ *aId = Manager()->Pid();
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+TabParent::GetHasContentOpener(bool* aResult)
+{
+ *aResult = mHasContentOpener;
+ return NS_OK;
+}
+
+void
+TabParent::SetHasContentOpener(bool aHasContentOpener)
+{
+ mHasContentOpener = aHasContentOpener;
+}
+
+NS_IMETHODIMP
+TabParent::NavigateByKey(bool aForward, bool aForDocumentNavigation)
+{
+ Unused << SendNavigateByKey(aForward, aForDocumentNavigation);
+ return NS_OK;
+}
+
+class LayerTreeUpdateRunnable final
+ : public mozilla::Runnable
+{
+ uint64_t mLayersId;
+ uint64_t mEpoch;
+ bool mActive;
+
+public:
+ explicit LayerTreeUpdateRunnable(uint64_t aLayersId, uint64_t aEpoch, bool aActive)
+ : mLayersId(aLayersId), mEpoch(aEpoch), mActive(aActive)
+ {
+ MOZ_ASSERT(!NS_IsMainThread());
+ }
+
+private:
+ NS_IMETHOD Run() override {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (RefPtr<TabParent> tabParent = TabParent::GetTabParentFromLayersId(mLayersId)) {
+ tabParent->LayerTreeUpdate(mEpoch, mActive);
+ }
+ return NS_OK;
+ }
+};
+
+void
+TabParent::LayerTreeUpdate(uint64_t aEpoch, bool aActive)
+{
+ // 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;
+ }
+
+ nsCOMPtr<mozilla::dom::EventTarget> target = do_QueryInterface(mFrameElement);
+ if (!target) {
+ NS_WARNING("Could not locate target for layer tree message.");
+ return;
+ }
+
+ RefPtr<Event> event = NS_NewDOMEvent(mFrameElement, nullptr, nullptr);
+ if (aActive) {
+ event->InitEvent(NS_LITERAL_STRING("MozLayerTreeReady"), true, false);
+ } else {
+ event->InitEvent(NS_LITERAL_STRING("MozLayerTreeCleared"), true, false);
+ }
+ event->SetTrusted(true);
+ event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
+ bool dummy;
+ mFrameElement->DispatchEvent(event, &dummy);
+}
+
+bool
+TabParent::RecvForcePaintNoOp(const uint64_t& aLayerObserverEpoch)
+{
+ // We sent a ForcePaint 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(aLayerObserverEpoch, true);
+ return true;
+}
+
+bool
+TabParent::RecvRemotePaintIsReady()
+{
+ nsCOMPtr<mozilla::dom::EventTarget> target = do_QueryInterface(mFrameElement);
+ if (!target) {
+ NS_WARNING("Could not locate target for MozAfterRemotePaint message.");
+ return true;
+ }
+
+ RefPtr<Event> event = NS_NewDOMEvent(mFrameElement, nullptr, nullptr);
+ event->InitEvent(NS_LITERAL_STRING("MozAfterRemotePaint"), false, false);
+ event->SetTrusted(true);
+ event->WidgetEventPtr()->mFlags.mOnlyChromeDispatch = true;
+ bool dummy;
+ mFrameElement->DispatchEvent(event, &dummy);
+ return true;
+}
+
+mozilla::plugins::PPluginWidgetParent*
+TabParent::AllocPPluginWidgetParent()
+{
+ return new mozilla::plugins::PluginWidgetParent();
+}
+
+bool
+TabParent::DeallocPPluginWidgetParent(mozilla::plugins::PPluginWidgetParent* aActor)
+{
+ delete aActor;
+ return true;
+}
+
+nsresult
+TabParent::HandleEvent(nsIDOMEvent* aEvent)
+{
+ nsAutoString eventType;
+ aEvent->GetType(eventType);
+
+ if (eventType.EqualsLiteral("MozUpdateWindowPos") && !mIsDestroyed) {
+ // This event is sent when the widget moved. Therefore we only update
+ // the position.
+ 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 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 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 Open2(nsIInputStream**) NO_IMPL
+ NS_IMETHOD AsyncOpen(nsIStreamListener*, nsISupports*) NO_IMPL
+ NS_IMETHOD AsyncOpen2(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(nsIDOMElement** aElement) override
+ {
+ nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(mElement);
+ elem.forget(aElement);
+ return NS_OK;
+ }
+ NS_IMETHOD GetNestedFrameId(uint64_t*) NO_IMPL
+ 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 GetIsInIsolatedMozBrowserElement(bool*) NO_IMPL
+ NS_IMETHOD GetAppId(uint32_t*) NO_IMPL
+ NS_IMETHOD GetOriginAttributes(JS::MutableHandleValue) NO_IMPL
+ NS_IMETHOD GetUseRemoteTabs(bool*) NO_IMPL
+ NS_IMETHOD SetRemoteTabs(bool) NO_IMPL
+ NS_IMETHOD IsTrackingProtectionOn(bool*) NO_IMPL
+#undef NO_IMPL
+
+protected:
+ ~FakeChannel() {}
+
+ nsCOMPtr<nsIURI> mUri;
+ uint64_t mCallbackId;
+ nsCOMPtr<Element> mElement;
+ nsCOMPtr<nsILoadInfo> mLoadInfo;
+};
+
+NS_IMPL_ISUPPORTS(FakeChannel, nsIChannel, nsIAuthPromptCallback,
+ nsIRequest, nsIInterfaceRequestor, nsILoadContext);
+
+bool
+TabParent::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,
+ EmptyCString());
+
+ uint32_t level = nsIAuthPrompt2::LEVEL_NONE;
+ nsCOMPtr<nsICancelable> dummy;
+ nsresult rv =
+ authPrompt->AsyncPromptAuth(channel, channel, nullptr,
+ level, holder, getter_AddRefs(dummy));
+
+ return rv == NS_OK;
+}
+
+bool
+TabParent::RecvInvokeDragSession(nsTArray<IPCDataTransfer>&& aTransfers,
+ const uint32_t& aAction,
+ const OptionalShmem& aVisualDnDData,
+ const uint32_t& aStride, const uint8_t& aFormat,
+ const LayoutDeviceIntRect& aDragRect)
+{
+ mInitialDataTransferItems.Clear();
+ nsIPresShell* shell = mFrameElement->OwnerDoc()->GetShell();
+ if (!shell) {
+ if (Manager()->IsContentParent()) {
+ Unused << Manager()->AsContentParent()->SendEndDragSession(true, true,
+ LayoutDeviceIntPoint());
+ }
+ return true;
+ }
+
+ EventStateManager* esm = shell->GetPresContext()->EventStateManager();
+ for (uint32_t i = 0; i < aTransfers.Length(); ++i) {
+ mInitialDataTransferItems.AppendElement(mozilla::Move(aTransfers[i].items()));
+ }
+ if (Manager()->IsContentParent()) {
+ nsCOMPtr<nsIDragService> dragService =
+ do_GetService("@mozilla.org/widget/dragservice;1");
+ if (dragService) {
+ dragService->MaybeAddChildProcess(Manager()->AsContentParent());
+ }
+ }
+
+ if (aVisualDnDData.type() == OptionalShmem::Tvoid_t ||
+ !aVisualDnDData.get_Shmem().IsReadable() ||
+ aVisualDnDData.get_Shmem().Size<char>() < aDragRect.height * aStride) {
+ mDnDVisualization = nullptr;
+ } else {
+ mDnDVisualization =
+ gfx::CreateDataSourceSurfaceFromData(gfx::IntSize(aDragRect.width, aDragRect.height),
+ static_cast<gfx::SurfaceFormat>(aFormat),
+ aVisualDnDData.get_Shmem().get<uint8_t>(),
+ aStride);
+ }
+
+ mDragValid = true;
+ mDragRect = aDragRect;
+
+ esm->BeginTrackingRemoteDragGesture(mFrameElement);
+
+ if (aVisualDnDData.type() == OptionalShmem::TShmem) {
+ Unused << DeallocShmem(aVisualDnDData);
+ }
+
+ return true;
+}
+
+void
+TabParent::AddInitialDnDDataTo(DataTransfer* aDataTransfer)
+{
+ for (uint32_t i = 0; i < mInitialDataTransferItems.Length(); ++i) {
+ nsTArray<IPCDataTransferItem>& itemArray = mInitialDataTransferItems[i];
+ for (auto& item : itemArray) {
+ RefPtr<nsVariantCC> variant = new nsVariantCC();
+ // Special case kFilePromiseMime so that we get the right
+ // nsIFlavorDataProvider for it.
+ if (item.flavor().EqualsLiteral(kFilePromiseMime)) {
+ RefPtr<nsISupports> flavorDataProvider =
+ new nsContentAreaDragDropDataProvider();
+ variant->SetAsISupports(flavorDataProvider);
+ } else if (item.data().type() == IPCDataTransferData::TnsString) {
+ variant->SetAsAString(item.data().get_nsString());
+ } else if (item.data().type() == IPCDataTransferData::TPBlobParent) {
+ auto* parent = static_cast<BlobParent*>(item.data().get_PBlobParent());
+ RefPtr<BlobImpl> impl = parent->GetBlobImpl();
+ variant->SetAsISupports(impl);
+ } else if (item.data().type() == IPCDataTransferData::TShmem) {
+ if (nsContentUtils::IsFlavorImage(item.flavor())) {
+ // An image! Get the imgIContainer for it and set it in the variant.
+ nsCOMPtr<imgIContainer> imageContainer;
+ nsresult rv =
+ nsContentUtils::DataTransferItemToImage(item,
+ getter_AddRefs(imageContainer));
+ if (NS_FAILED(rv)) {
+ continue;
+ }
+ variant->SetAsISupports(imageContainer);
+ } else {
+ Shmem data = item.data().get_Shmem();
+ variant->SetAsACString(nsDependentCString(data.get<char>(), data.Size<char>()));
+ }
+
+ mozilla::Unused << DeallocShmem(item.data().get_Shmem());
+ }
+
+ // Using system principal here, since once the data is on parent process
+ // side, it can be handled as being from browser chrome or OS.
+
+ // We set aHidden to false, as we don't need to worry about hiding data
+ // from content in the parent process where there is no content.
+ // XXX: Nested Content Processes may change this
+ aDataTransfer->SetDataWithPrincipalFromOtherProcess(NS_ConvertUTF8toUTF16(item.flavor()),
+ variant, i,
+ nsContentUtils::GetSystemPrincipal(),
+ /* aHidden = */ false);
+ }
+ }
+ mInitialDataTransferItems.Clear();
+}
+
+bool
+TabParent::TakeDragVisualization(RefPtr<mozilla::gfx::SourceSurface>& aSurface,
+ LayoutDeviceIntRect* aDragRect)
+{
+ if (!mDragValid)
+ return false;
+
+ aSurface = mDnDVisualization.forget();
+ *aDragRect = mDragRect;
+ mDragValid = false;
+ return true;
+}
+
+bool
+TabParent::AsyncPanZoomEnabled() const
+{
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ return widget && widget->AsyncPanZoomEnabled();
+}
+
+NS_IMETHODIMP
+TabParent::StartPersistence(uint64_t aOuterWindowID,
+ nsIWebBrowserPersistDocumentReceiver* aRecv)
+{
+ nsCOMPtr<nsIContentParent> manager = Manager();
+ if (!manager->IsContentParent()) {
+ return NS_ERROR_UNEXPECTED;
+ }
+ auto* actor = new WebBrowserPersistDocumentParent();
+ actor->SetOnReady(aRecv);
+ return manager->AsContentParent()
+ ->SendPWebBrowserPersistDocumentConstructor(actor, this, aOuterWindowID)
+ ? NS_OK : NS_ERROR_FAILURE;
+ // (The actor will be destroyed on constructor failure.)
+}
+
+ShowInfo
+TabParent::GetShowInfo()
+{
+ TryCacheDPIAndScale();
+ if (mFrameElement) {
+ nsAutoString name;
+ mFrameElement->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name);
+ bool allowFullscreen =
+ mFrameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::allowfullscreen) ||
+ mFrameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::mozallowfullscreen);
+ bool isPrivate = mFrameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::mozprivatebrowsing);
+ bool isTransparent =
+ nsContentUtils::IsChromeDoc(mFrameElement->OwnerDoc()) &&
+ mFrameElement->HasAttr(kNameSpaceID_None, nsGkAtoms::transparent);
+ return ShowInfo(name, allowFullscreen, isPrivate, false,
+ isTransparent, mDPI, mRounding, mDefaultScale.scale);
+ }
+
+ return ShowInfo(EmptyString(), false, false, false,
+ false, mDPI, mRounding, mDefaultScale.scale);
+}
+
+void
+TabParent::AudioChannelChangeNotification(nsPIDOMWindowOuter* aWindow,
+ AudioChannel aAudioChannel,
+ float aVolume,
+ bool aMuted)
+{
+ if (!mFrameElement || !mFrameElement->OwnerDoc()) {
+ return;
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> window = mFrameElement->OwnerDoc()->GetWindow();
+ while (window) {
+ if (window == aWindow) {
+ Unused << SendAudioChannelChangeNotification(static_cast<uint32_t>(aAudioChannel),
+ aVolume, aMuted);
+ break;
+ }
+
+ nsCOMPtr<nsPIDOMWindowOuter> win = window->GetScriptableParentOrNull();
+ if (!win) {
+ break;
+ }
+
+ window = win;
+ }
+}
+
+bool
+TabParent::RecvGetTabCount(uint32_t* aValue)
+{
+ *aValue = 0;
+
+ nsCOMPtr<nsIXULBrowserWindow> xulBrowserWindow = GetXULBrowserWindow();
+ NS_ENSURE_TRUE(xulBrowserWindow, true);
+
+ uint32_t tabCount;
+ nsresult rv = xulBrowserWindow->GetTabCount(&tabCount);
+ NS_ENSURE_SUCCESS(rv, true);
+
+ *aValue = tabCount;
+ return true;
+}
+
+bool
+TabParent::RecvLookUpDictionary(const nsString& aText,
+ nsTArray<FontRange>&& aFontRangeArray,
+ const bool& aIsVertical,
+ const LayoutDeviceIntPoint& aPoint)
+{
+ nsCOMPtr<nsIWidget> widget = GetWidget();
+ if (!widget) {
+ return true;
+ }
+
+ widget->LookUpDictionary(aText, aFontRangeArray, aIsVertical,
+ aPoint - GetChildProcessOffset());
+ return true;
+}
+
+bool
+TabParent::RecvNotifySessionHistoryChange(const uint32_t& aCount)
+{
+ RefPtr<nsFrameLoader> frameLoader(GetFrameLoader());
+ if (!frameLoader) {
+ // FrameLoader can be nullptr if the it is destroying.
+ // In this case session history change can simply be ignored.
+ return true;
+ }
+
+ nsCOMPtr<nsIPartialSHistory> partialHistory;
+ frameLoader->GetPartialSessionHistory(getter_AddRefs(partialHistory));
+ if (!partialHistory) {
+ // PartialSHistory is not enabled
+ return true;
+ }
+
+ partialHistory->OnSessionHistoryChange(aCount);
+ return true;
+}
+
+bool
+TabParent::RecvRequestCrossBrowserNavigation(const uint32_t& aGlobalIndex)
+{
+ RefPtr<nsFrameLoader> frameLoader(GetFrameLoader());
+ if (!frameLoader) {
+ // FrameLoader can be nullptr if the it is destroying.
+ // In this case we can ignore the request.
+ return true;
+ }
+
+ return NS_SUCCEEDED(frameLoader->RequestGroupedHistoryNavigation(aGlobalIndex));
+}
+
+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;
+}
+
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h
new file mode 100644
index 000000000..43afb0538
--- /dev/null
+++ b/dom/ipc/TabParent.h
@@ -0,0 +1,813 @@
+/* -*- 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_tabs_TabParent_h
+#define mozilla_tabs_TabParent_h
+
+#include "js/TypeDecls.h"
+#include "mozilla/ContentCache.h"
+#include "mozilla/dom/AudioChannelBinding.h"
+#include "mozilla/dom/ipc/IdType.h"
+#include "mozilla/dom/PBrowserParent.h"
+#include "mozilla/dom/PContent.h"
+#include "mozilla/dom/PFilePickerParent.h"
+#include "mozilla/dom/TabContext.h"
+#include "mozilla/EventForwards.h"
+#include "mozilla/dom/File.h"
+#include "mozilla/layers/CompositorBridgeParent.h"
+#include "mozilla/RefPtr.h"
+#include "mozilla/Move.h"
+#include "nsCOMPtr.h"
+#include "nsIAuthPromptProvider.h"
+#include "nsIBrowserDOMWindow.h"
+#include "nsIDOMEventListener.h"
+#include "nsIKeyEventInPluginCallback.h"
+#include "nsISecureBrowserUI.h"
+#include "nsITabParent.h"
+#include "nsIWebBrowserPersistable.h"
+#include "nsIXULBrowserWindow.h"
+#include "nsRefreshDriver.h"
+#include "nsWeakReference.h"
+#include "Units.h"
+#include "nsIWidget.h"
+#include "nsIPartialSHistory.h"
+
+class nsFrameLoader;
+class nsIFrameLoader;
+class nsIContent;
+class nsIPrincipal;
+class nsIURI;
+class nsILoadContext;
+class nsIDocShell;
+
+namespace mozilla {
+
+namespace a11y {
+class DocAccessibleParent;
+}
+
+namespace jsipc {
+class CpowHolder;
+} // namespace jsipc
+
+namespace layers {
+struct TextureFactoryIdentifier;
+} // namespace layers
+
+namespace layout {
+class RenderFrameParent;
+} // namespace layout
+
+namespace widget {
+struct IMENotification;
+} // namespace widget
+
+namespace gfx {
+class SourceSurface;
+class DataSourceSurface;
+} // namespace gfx
+
+namespace dom {
+
+class ClonedMessageData;
+class nsIContentParent;
+class Element;
+class DataTransfer;
+
+namespace ipc {
+class StructuredCloneData;
+} // ipc namespace
+
+class TabParent final : public PBrowserParent
+ , public nsIDOMEventListener
+ , public nsITabParent
+ , public nsIAuthPromptProvider
+ , public nsISecureBrowserUI
+ , public nsIKeyEventInPluginCallback
+ , public nsSupportsWeakReference
+ , public TabContext
+ , public nsAPostRefreshObserver
+ , public nsIWebBrowserPersistable
+{
+ typedef mozilla::dom::ClonedMessageData ClonedMessageData;
+
+ virtual ~TabParent();
+
+public:
+ // Helper class for ContentParent::RecvCreateWindow.
+ struct AutoUseNewTab;
+
+ // nsITabParent
+ NS_DECL_NSITABPARENT
+ // nsIDOMEventListener interfaces
+ NS_DECL_NSIDOMEVENTLISTENER
+
+ TabParent(nsIContentParent* aManager,
+ const TabId& aTabId,
+ const TabContext& aContext,
+ uint32_t aChromeFlags);
+
+ Element* GetOwnerElement() const { return mFrameElement; }
+ already_AddRefed<nsPIDOMWindowOuter> GetParentWindowOuter();
+
+ void SetOwnerElement(Element* aElement);
+
+ void CacheFrameLoader(nsFrameLoader* aFrameLoader);
+
+ /**
+ * Get the mozapptype attribute from this TabParent's owner DOM element.
+ */
+ void GetAppType(nsAString& aOut);
+
+ /**
+ * Returns true iff this TabParent's nsIFrameLoader is visible.
+ *
+ * The frameloader's visibility can be independent of e.g. its docshell's
+ * visibility.
+ */
+ bool IsVisible() const;
+
+ nsIBrowserDOMWindow *GetBrowserDOMWindow() const { return mBrowserDOMWindow; }
+
+ void SetBrowserDOMWindow(nsIBrowserDOMWindow* aBrowserDOMWindow)
+ {
+ mBrowserDOMWindow = aBrowserDOMWindow;
+ }
+
+ void SetHasContentOpener(bool aHasContentOpener);
+
+ void SwapFrameScriptsFrom(nsTArray<FrameScriptInfo>& aFrameScripts)
+ {
+ aFrameScripts.SwapElements(mDelayedFrameScripts);
+ }
+
+ already_AddRefed<nsILoadContext> GetLoadContext();
+
+ already_AddRefed<nsIWidget> GetTopLevelWidget();
+
+ nsIXULBrowserWindow* GetXULBrowserWindow();
+
+ void Destroy();
+
+ void RemoveWindowListeners();
+
+ void AddWindowListeners();
+
+ void DidRefresh() override;
+
+ virtual bool RecvMoveFocus(const bool& aForward,
+ const bool& aForDocumentNavigation) override;
+
+ virtual bool RecvSizeShellTo(const uint32_t& aFlags,
+ const int32_t& aWidth,
+ const int32_t& aHeight,
+ const int32_t& aShellItemWidth,
+ const int32_t& aShellItemHeight) override;
+
+ virtual bool RecvDropLinks(nsTArray<nsString>&& aLinks) override;
+
+ virtual bool RecvEvent(const RemoteDOMEvent& aEvent) override;
+
+ virtual bool RecvReplyKeyEvent(const WidgetKeyboardEvent& aEvent) override;
+
+ virtual bool
+ RecvDispatchAfterKeyboardEvent(const WidgetKeyboardEvent& aEvent) override;
+
+ virtual bool
+ RecvAccessKeyNotHandled(const WidgetKeyboardEvent& aEvent) override;
+
+ virtual bool RecvBrowserFrameOpenWindow(PBrowserParent* aOpener,
+ PRenderFrameParent* aRenderFrame,
+ const nsString& aURL,
+ const nsString& aName,
+ const nsString& aFeatures,
+ bool* aOutWindowOpened,
+ TextureFactoryIdentifier* aTextureFactoryIdentifier,
+ uint64_t* aLayersId) override;
+
+ virtual bool
+ RecvSyncMessage(const nsString& aMessage,
+ const ClonedMessageData& aData,
+ InfallibleTArray<CpowEntry>&& aCpows,
+ const IPC::Principal& aPrincipal,
+ nsTArray<ipc::StructuredCloneData>* aRetVal) override;
+
+ virtual bool
+ RecvRpcMessage(const nsString& aMessage,
+ const ClonedMessageData& aData,
+ InfallibleTArray<CpowEntry>&& aCpows,
+ const IPC::Principal& aPrincipal,
+ nsTArray<ipc::StructuredCloneData>* aRetVal) override;
+
+ virtual bool RecvAsyncMessage(const nsString& aMessage,
+ InfallibleTArray<CpowEntry>&& aCpows,
+ const IPC::Principal& aPrincipal,
+ const ClonedMessageData& aData) override;
+
+ virtual bool
+ RecvNotifyIMEFocus(const ContentCache& aContentCache,
+ const widget::IMENotification& aEventMessage,
+ nsIMEUpdatePreference* aPreference) override;
+
+ virtual bool
+ RecvNotifyIMETextChange(const ContentCache& aContentCache,
+ const widget::IMENotification& aEventMessage) override;
+
+ virtual bool
+ RecvNotifyIMECompositionUpdate(const ContentCache& aContentCache,
+ const widget::IMENotification& aEventMessage) override;
+
+ virtual bool
+ RecvNotifyIMESelection(const ContentCache& aContentCache,
+ const widget::IMENotification& aEventMessage) override;
+
+ virtual bool
+ RecvUpdateContentCache(const ContentCache& aContentCache) override;
+
+ virtual bool
+ RecvNotifyIMEMouseButtonEvent(const widget::IMENotification& aEventMessage,
+ bool* aConsumedByIME) override;
+
+ virtual bool
+ RecvNotifyIMEPositionChange(const ContentCache& aContentCache,
+ const widget::IMENotification& aEventMessage) override;
+
+ virtual bool
+ RecvOnEventNeedingAckHandled(const EventMessage& aMessage) override;
+
+ virtual bool
+ RecvRequestIMEToCommitComposition(const bool& aCancel,
+ bool* aIsCommitted,
+ nsString* aCommittedString) override;
+
+ virtual bool
+ RecvStartPluginIME(const WidgetKeyboardEvent& aKeyboardEvent,
+ const int32_t& aPanelX,
+ const int32_t& aPanelY,
+ nsString* aCommitted) override;
+
+ virtual bool RecvSetPluginFocused(const bool& aFocused) override;
+
+ virtual bool RecvSetCandidateWindowForPlugin(
+ const widget::CandidateWindowPosition& aPosition) override;
+
+ virtual bool
+ RecvDefaultProcOfPluginEvent(const WidgetPluginEvent& aEvent) override;
+
+ virtual bool RecvGetInputContext(int32_t* aIMEEnabled,
+ int32_t* aIMEOpen) override;
+
+ virtual bool RecvSetInputContext(const int32_t& aIMEEnabled,
+ const int32_t& aIMEOpen,
+ const nsString& aType,
+ const nsString& aInputmode,
+ const nsString& aActionHint,
+ const int32_t& aCause,
+ const int32_t& aFocusChange) override;
+
+
+ // See nsIKeyEventInPluginCallback
+ virtual void HandledWindowedPluginKeyEvent(
+ const NativeEventData& aKeyEventData,
+ bool aIsConsumed) override;
+
+ virtual bool RecvOnWindowedPluginKeyEvent(
+ const NativeEventData& aKeyEventData) override;
+
+ virtual bool RecvRequestFocus(const bool& aCanRaise) override;
+
+ virtual bool RecvLookUpDictionary(
+ const nsString& aText,
+ nsTArray<mozilla::FontRange>&& aFontRangeArray,
+ const bool& aIsVertical,
+ const LayoutDeviceIntPoint& aPoint) override;
+
+ virtual bool
+ RecvEnableDisableCommands(const nsString& aAction,
+ nsTArray<nsCString>&& aEnabledCommands,
+ nsTArray<nsCString>&& aDisabledCommands) override;
+
+ virtual bool
+ RecvSetCursor(const uint32_t& aValue, const bool& aForce) override;
+
+ virtual bool RecvSetCustomCursor(const nsCString& aUri,
+ const uint32_t& aWidth,
+ const uint32_t& aHeight,
+ const uint32_t& aStride,
+ const uint8_t& aFormat,
+ const uint32_t& aHotspotX,
+ const uint32_t& aHotspotY,
+ const bool& aForce) override;
+
+ virtual bool RecvSetStatus(const uint32_t& aType,
+ const nsString& aStatus) override;
+
+ virtual bool RecvIsParentWindowMainWidgetVisible(bool* aIsVisible) override;
+
+ virtual bool RecvShowTooltip(const uint32_t& aX,
+ const uint32_t& aY,
+ const nsString& aTooltip,
+ const nsString& aDirection) override;
+
+ virtual bool RecvHideTooltip() override;
+
+ virtual bool RecvGetDPI(float* aValue) override;
+
+ virtual bool RecvGetDefaultScale(double* aValue) override;
+
+ virtual bool RecvGetWidgetRounding(int32_t* aValue) override;
+
+ virtual bool RecvGetMaxTouchPoints(uint32_t* aTouchPoints) override;
+
+ virtual bool RecvGetWidgetNativeData(WindowsHandle* aValue) override;
+
+ virtual bool RecvSetNativeChildOfShareableWindow(const uintptr_t& childWindow) override;
+
+ virtual bool RecvDispatchFocusToTopLevelWindow() override;
+
+ virtual bool RecvRespondStartSwipeEvent(const uint64_t& aInputBlockId,
+ const bool& aStartSwipe) override;
+
+ virtual bool
+ RecvDispatchWheelEvent(const mozilla::WidgetWheelEvent& aEvent) override;
+
+ virtual bool
+ RecvDispatchMouseEvent(const mozilla::WidgetMouseEvent& aEvent) override;
+
+ virtual bool
+ RecvDispatchKeyboardEvent(const mozilla::WidgetKeyboardEvent& aEvent) override;
+
+ virtual PColorPickerParent*
+ AllocPColorPickerParent(const nsString& aTitle,
+ const nsString& aInitialColor) override;
+
+ virtual bool
+ DeallocPColorPickerParent(PColorPickerParent* aColorPicker) override;
+
+ virtual PDatePickerParent*
+ AllocPDatePickerParent(const nsString& aTitle, const nsString& aInitialDate) override;
+ virtual bool DeallocPDatePickerParent(PDatePickerParent* aDatePicker) override;
+
+ virtual PDocAccessibleParent*
+ AllocPDocAccessibleParent(PDocAccessibleParent*, const uint64_t&,
+ const uint32_t&, const IAccessibleHolder&) override;
+
+ virtual bool DeallocPDocAccessibleParent(PDocAccessibleParent*) override;
+
+ virtual bool
+ RecvPDocAccessibleConstructor(PDocAccessibleParent* aDoc,
+ PDocAccessibleParent* aParentDoc,
+ const uint64_t& aParentID,
+ const uint32_t& aMsaaID,
+ const IAccessibleHolder& aDocCOMProxy) override;
+
+ /**
+ * Return the top level doc accessible parent for this tab.
+ */
+ a11y::DocAccessibleParent* GetTopLevelDocAccessible() const;
+
+ void LoadURL(nsIURI* aURI);
+
+ // XXX/cjones: it's not clear what we gain by hiding these
+ // message-sending functions under a layer of indirection and
+ // eating the return values
+ void Show(const ScreenIntSize& aSize, bool aParentIsActive);
+
+ void UpdateDimensions(const nsIntRect& aRect, const ScreenIntSize& aSize);
+
+ void SizeModeChanged(const nsSizeMode& aSizeMode);
+
+ void UIResolutionChanged();
+
+ void ThemeChanged();
+
+ void HandleAccessKey(const WidgetKeyboardEvent& aEvent,
+ nsTArray<uint32_t>& aCharCodes,
+ const int32_t& aModifierMask);
+
+ void Activate();
+
+ void Deactivate();
+
+ bool MapEventCoordinatesForChildProcess(mozilla::WidgetEvent* aEvent);
+
+ void MapEventCoordinatesForChildProcess(const LayoutDeviceIntPoint& aOffset,
+ mozilla::WidgetEvent* aEvent);
+
+ LayoutDeviceToCSSScale GetLayoutDeviceToCSSScale();
+
+ virtual bool
+ RecvRequestNativeKeyBindings(const mozilla::WidgetKeyboardEvent& aEvent,
+ MaybeNativeKeyBinding* aBindings) override;
+
+ virtual bool
+ RecvSynthesizeNativeKeyEvent(const int32_t& aNativeKeyboardLayout,
+ const int32_t& aNativeKeyCode,
+ const uint32_t& aModifierFlags,
+ const nsString& aCharacters,
+ const nsString& aUnmodifiedCharacters,
+ const uint64_t& aObserverId) override;
+
+ virtual bool
+ RecvSynthesizeNativeMouseEvent(const LayoutDeviceIntPoint& aPoint,
+ const uint32_t& aNativeMessage,
+ const uint32_t& aModifierFlags,
+ const uint64_t& aObserverId) override;
+
+ virtual bool
+ RecvSynthesizeNativeMouseMove(const LayoutDeviceIntPoint& aPoint,
+ const uint64_t& aObserverId) override;
+
+ virtual bool
+ 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) override;
+
+ virtual bool
+ RecvSynthesizeNativeTouchPoint(const uint32_t& aPointerId,
+ const TouchPointerState& aPointerState,
+ const LayoutDeviceIntPoint& aPoint,
+ const double& aPointerPressure,
+ const uint32_t& aPointerOrientation,
+ const uint64_t& aObserverId) override;
+
+ virtual bool
+ RecvSynthesizeNativeTouchTap(const LayoutDeviceIntPoint& aPoint,
+ const bool& aLongTap,
+ const uint64_t& aObserverId) override;
+
+ virtual bool
+ RecvClearNativeTouchSequence(const uint64_t& aObserverId) override;
+
+ void SendMouseEvent(const nsAString& aType, float aX, float aY,
+ int32_t aButton, int32_t aClickCount,
+ int32_t aModifiers, bool aIgnoreRootScrollFrame);
+
+ void SendKeyEvent(const nsAString& aType, int32_t aKeyCode,
+ int32_t aCharCode, int32_t aModifiers,
+ bool aPreventDefault);
+
+ bool SendRealMouseEvent(mozilla::WidgetMouseEvent& event);
+
+ bool SendRealDragEvent(mozilla::WidgetDragEvent& aEvent,
+ uint32_t aDragAction,
+ uint32_t aDropEffect);
+
+ bool SendMouseWheelEvent(mozilla::WidgetWheelEvent& event);
+
+ bool SendRealKeyEvent(mozilla::WidgetKeyboardEvent& event);
+
+ bool SendRealTouchEvent(WidgetTouchEvent& event);
+
+ bool SendHandleTap(TapType aType,
+ const LayoutDevicePoint& aPoint,
+ Modifiers aModifiers,
+ const ScrollableLayerGuid& aGuid,
+ uint64_t aInputBlockId);
+
+ virtual PDocumentRendererParent*
+ AllocPDocumentRendererParent(const nsRect& documentRect,
+ const gfx::Matrix& transform,
+ const nsString& bgcolor,
+ const uint32_t& renderFlags,
+ const bool& flushLayout,
+ const nsIntSize& renderSize) override;
+
+ virtual bool
+ DeallocPDocumentRendererParent(PDocumentRendererParent* actor) override;
+
+ virtual PFilePickerParent*
+ AllocPFilePickerParent(const nsString& aTitle,
+ const int16_t& aMode) override;
+
+ virtual bool DeallocPFilePickerParent(PFilePickerParent* actor) override;
+
+ virtual PIndexedDBPermissionRequestParent*
+ AllocPIndexedDBPermissionRequestParent(const Principal& aPrincipal) override;
+
+ virtual bool
+ RecvPIndexedDBPermissionRequestConstructor(
+ PIndexedDBPermissionRequestParent* aActor,
+ const Principal& aPrincipal)
+ override;
+
+ virtual bool
+ DeallocPIndexedDBPermissionRequestParent(
+ PIndexedDBPermissionRequestParent* aActor)
+ override;
+
+ bool GetGlobalJSObject(JSContext* cx, JSObject** globalp);
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIAUTHPROMPTPROVIDER
+ NS_DECL_NSISECUREBROWSERUI
+ NS_DECL_NSIWEBBROWSERPERSISTABLE
+
+ bool HandleQueryContentEvent(mozilla::WidgetQueryContentEvent& aEvent);
+
+ bool SendCompositionEvent(mozilla::WidgetCompositionEvent& event);
+
+ bool SendSelectionEvent(mozilla::WidgetSelectionEvent& event);
+
+ bool SendPasteTransferable(const IPCDataTransfer& aDataTransfer,
+ const bool& aIsPrivateData,
+ const IPC::Principal& aRequestingPrincipal);
+
+ static TabParent* GetFrom(nsFrameLoader* aFrameLoader);
+
+ static TabParent* GetFrom(nsIFrameLoader* aFrameLoader);
+
+ static TabParent* GetFrom(nsITabParent* aTabParent);
+
+ static TabParent* GetFrom(PBrowserParent* aTabParent);
+
+ static TabParent* GetFrom(nsIContent* aContent);
+
+ static TabId GetTabIdFrom(nsIDocShell* docshell);
+
+ nsIContentParent* Manager() const { return mManager; }
+
+ /**
+ * 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; }
+
+ already_AddRefed<nsIWidget> GetWidget() const;
+
+ const TabId GetTabId() const
+ {
+ return mTabId;
+ }
+
+ LayoutDeviceIntPoint GetChildProcessOffset();
+ LayoutDevicePoint AdjustTapToChildWidget(const LayoutDevicePoint& aPoint);
+
+ /**
+ * Native widget remoting protocol for use with windowed plugins with e10s.
+ */
+ virtual PPluginWidgetParent* AllocPPluginWidgetParent() override;
+
+ virtual bool
+ DeallocPPluginWidgetParent(PPluginWidgetParent* aActor) override;
+
+ void SetInitedByParent() { mInitedByParent = true; }
+
+ bool IsInitedByParent() const { return mInitedByParent; }
+
+ static TabParent* GetNextTabParent();
+
+ bool SendLoadRemoteScript(const nsString& aURL,
+ const bool& aRunInGlobalScope);
+
+ void LayerTreeUpdate(uint64_t aEpoch, bool aActive);
+
+ virtual bool
+ RecvInvokeDragSession(nsTArray<IPCDataTransfer>&& aTransfers,
+ const uint32_t& aAction,
+ const OptionalShmem& aVisualDnDData,
+ const uint32_t& aStride, const uint8_t& aFormat,
+ const LayoutDeviceIntRect& aDragRect) override;
+
+ void AddInitialDnDDataTo(DataTransfer* aDataTransfer);
+
+ bool TakeDragVisualization(RefPtr<mozilla::gfx::SourceSurface>& aSurface,
+ LayoutDeviceIntRect* aDragRect);
+
+ layout::RenderFrameParent* GetRenderFrame();
+
+ void AudioChannelChangeNotification(nsPIDOMWindowOuter* aWindow,
+ AudioChannel aAudioChannel,
+ float aVolume,
+ bool aMuted);
+ bool SetRenderFrame(PRenderFrameParent* aRFParent);
+ bool GetRenderFrameInfo(TextureFactoryIdentifier* aTextureFactoryIdentifier,
+ uint64_t* aLayersId);
+
+ bool RecvEnsureLayersConnected() override;
+
+protected:
+ bool ReceiveMessage(const nsString& aMessage,
+ bool aSync,
+ ipc::StructuredCloneData* aData,
+ mozilla::jsipc::CpowHolder* aCpows,
+ nsIPrincipal* aPrincipal,
+ nsTArray<ipc::StructuredCloneData>* aJSONRetVal = nullptr);
+
+ virtual bool RecvAsyncAuthPrompt(const nsCString& aUri,
+ const nsString& aRealm,
+ const uint64_t& aCallbackId) override;
+
+ virtual bool Recv__delete__() override;
+
+ virtual void ActorDestroy(ActorDestroyReason why) override;
+
+ Element* mFrameElement;
+ nsCOMPtr<nsIBrowserDOMWindow> mBrowserDOMWindow;
+
+ virtual PRenderFrameParent* AllocPRenderFrameParent() override;
+
+ virtual bool DeallocPRenderFrameParent(PRenderFrameParent* aFrame) override;
+
+ virtual bool RecvRemotePaintIsReady() override;
+
+ virtual bool RecvForcePaintNoOp(const uint64_t& aLayerObserverEpoch) override;
+
+ virtual bool RecvSetDimensions(const uint32_t& aFlags,
+ const int32_t& aX, const int32_t& aY,
+ const int32_t& aCx, const int32_t& aCy) override;
+
+ virtual bool RecvGetTabCount(uint32_t* aValue) override;
+
+ virtual bool RecvAudioChannelActivityNotification(const uint32_t& aAudioChannel,
+ const bool& aActive) override;
+
+ virtual bool RecvNotifySessionHistoryChange(const uint32_t& aCount) override;
+
+ virtual bool RecvRequestCrossBrowserNavigation(const uint32_t& aGlobalIndex) override;
+
+ ContentCacheInParent mContentCache;
+
+ nsIntRect mRect;
+ ScreenIntSize mDimensions;
+ ScreenOrientationInternal mOrientation;
+ float mDPI;
+ int32_t mRounding;
+ CSSToLayoutDeviceScale mDefaultScale;
+ bool mUpdatedDimensions;
+ nsSizeMode mSizeMode;
+ LayoutDeviceIntPoint mClientOffset;
+ LayoutDeviceIntPoint mChromeOffset;
+
+private:
+ void DestroyInternal();
+
+ already_AddRefed<nsFrameLoader>
+ GetFrameLoader(bool aUseCachedFrameLoaderAfterDestroy = false) const;
+
+ RefPtr<nsIContentParent> mManager;
+ void TryCacheDPIAndScale();
+
+ nsresult UpdatePosition();
+
+ bool AsyncPanZoomEnabled() const;
+
+ // Cached value indicating the docshell active state of the remote browser.
+ bool mDocShellIsActive;
+
+ // 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 true, we've initiated normal shutdown and notified our managing PContent.
+ bool mMarkedDestroying;
+ // When true, the TabParent is invalid and we should not send IPC messages anymore.
+ bool mIsDestroyed;
+
+ uint32_t mChromeFlags;
+
+ nsTArray<nsTArray<IPCDataTransferItem>> mInitialDataTransferItems;
+
+ RefPtr<gfx::DataSourceSurface> mDnDVisualization;
+ bool mDragValid;
+ LayoutDeviceIntRect mDragRect;
+
+ // When true, the TabParent is initialized without child side's request.
+ // When false, the TabParent is initialized by window.open() from child side.
+ bool mInitedByParent;
+
+ nsCOMPtr<nsILoadContext> mLoadContext;
+
+ // 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;
+
+ TabId mTabId;
+
+ // When loading a new tab or window via window.open, the child process sends
+ // a new PBrowser to use. We store that tab in sNextTabParent and then
+ // proceed through the browser's normal paths to create a new
+ // window/tab. When it comes time to create a new TabParent, we instead use
+ // sNextTabParent.
+ static TabParent* sNextTabParent;
+
+ // When loading a new tab or window via window.open, the child is
+ // responsible for loading the URL it wants into the new TabChild. 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 and the URL is stored in
+ // mSkippedURL.
+ bool mCreatingWindow;
+ nsCString mDelayedURL;
+
+ // 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 TabChild loads them immediately.
+ nsTArray<FrameScriptInfo> mDelayedFrameScripts;
+
+ // Cached cursor setting from TabChild. When the cursor is over the tab,
+ // it should take this appearance.
+ nsCursor mCursor;
+ nsCOMPtr<imgIContainer> mCustomCursor;
+ uint32_t mCustomCursorHotspotX, mCustomCursorHotspotY;
+
+ // True if the cursor changes from the TabChild should change the widget
+ // cursor. This happens whenever the cursor is in the tab's region.
+ bool mTabSetsCursor;
+
+ RefPtr<nsIPresShell> mPresShellWithRefreshListener;
+
+ bool mHasContentOpener;
+
+#ifdef DEBUG
+ int32_t mActiveSupressDisplayportCount;
+#endif
+
+ ShowInfo GetShowInfo();
+
+private:
+ // This is used when APZ needs to find the TabParent associated with a layer
+ // to dispatch events.
+ typedef nsDataHashtable<nsUint64HashKey, TabParent*> LayerToTabParentTable;
+ static LayerToTabParentTable* sLayerToTabParentTable;
+
+ static void AddTabParentToTable(uint64_t aLayersId, TabParent* aTabParent);
+
+ static void RemoveTabParentFromTable(uint64_t aLayersId);
+
+ uint64_t mLayerTreeEpoch;
+
+ // If this flag is set, then the tab's layers will be preserved even when
+ // the tab's docshell is inactive.
+ bool mPreserveLayers;
+
+public:
+ static TabParent* GetTabParentFromLayersId(uint64_t aLayersId);
+};
+
+struct MOZ_STACK_CLASS TabParent::AutoUseNewTab final
+{
+public:
+ AutoUseNewTab(TabParent* aNewTab, bool* aWindowIsNew, nsCString* aURLToLoad)
+ : mNewTab(aNewTab), mWindowIsNew(aWindowIsNew), mURLToLoad(aURLToLoad)
+ {
+ MOZ_ASSERT(!TabParent::sNextTabParent);
+ MOZ_ASSERT(!aNewTab->mCreatingWindow);
+
+ TabParent::sNextTabParent = aNewTab;
+ aNewTab->mCreatingWindow = true;
+ aNewTab->mDelayedURL.Truncate();
+ }
+
+ ~AutoUseNewTab()
+ {
+ mNewTab->mCreatingWindow = false;
+ *mURLToLoad = mNewTab->mDelayedURL;
+
+ if (TabParent::sNextTabParent) {
+ MOZ_ASSERT(TabParent::sNextTabParent == mNewTab);
+ TabParent::sNextTabParent = nullptr;
+ *mWindowIsNew = false;
+ }
+ }
+
+private:
+ TabParent* mNewTab;
+ bool* mWindowIsNew;
+ nsCString* mURLToLoad;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_tabs_TabParent_h
diff --git a/dom/ipc/extensions.js b/dom/ipc/extensions.js
new file mode 100644
index 000000000..f6549bb3d
--- /dev/null
+++ b/dom/ipc/extensions.js
@@ -0,0 +1,17 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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";
+
+function debug(msg) {
+ // dump("extensions - " + msg + "\n");
+}
+
+debug("loaded");
+
+ExtensionContent.init(this);
+
+addEventListener("unload", () => {
+ ExtensionContent.uninit(this);
+});
diff --git a/dom/ipc/jar.mn b/dom/ipc/jar.mn
new file mode 100644
index 000000000..86920f074
--- /dev/null
+++ b/dom/ipc/jar.mn
@@ -0,0 +1,13 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.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.xul (test.xul)
+ 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)
+ content/global/BrowserElementCopyPaste.js (../browser-element/BrowserElementCopyPaste.js)
+ content/global/extensions.js (extensions.js)
+ content/global/manifestMessages.js (manifestMessages.js)
+ content/global/preload.js (preload.js)
diff --git a/dom/ipc/manifestMessages.js b/dom/ipc/manifestMessages.js
new file mode 100644
index 000000000..c6bb40494
--- /dev/null
+++ b/dom/ipc/manifestMessages.js
@@ -0,0 +1,108 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.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
+ */
+/*globals Task, ManifestObtainer, ManifestFinder, content, sendAsyncMessage, addMessageListener, Components*/
+"use strict";
+const {
+ utils: Cu,
+} = Components;
+Cu.import("resource://gre/modules/ManifestObtainer.jsm");
+Cu.import("resource://gre/modules/ManifestFinder.jsm");
+Cu.import("resource://gre/modules/Task.jsm");
+
+const MessageHandler = {
+ registerListeners() {
+ addMessageListener(
+ "DOM:WebManifest:hasManifestLink",
+ this.hasManifestLink.bind(this)
+ );
+ addMessageListener(
+ "DOM:ManifestObtainer:Obtain",
+ this.obtainManifest.bind(this)
+ );
+ addMessageListener(
+ "DOM:Manifest:FireAppInstalledEvent",
+ this.fireAppInstalledEvent.bind(this)
+ );
+ },
+
+ /**
+ * Check if the content document includes a link to a web manifest.
+ * @param {Object} aMsg The IPC message, which is destructured to just
+ * get the id.
+ */
+ hasManifestLink({data: {id}}) {
+ const response = makeMsgResponse(id);
+ response.result = ManifestFinder.contentHasManifestLink(content);
+ response.success = true;
+ sendAsyncMessage("DOM:WebManifest:hasManifestLink", response);
+ },
+
+ /**
+ * Asynchronously obtains a web manifest from content by using the
+ * ManifestObtainer and messages back the result.
+ * @param {Object} aMsg The IPC message, which is destructured to just
+ * get the id.
+ */
+ obtainManifest: Task.async(function* ({data: {id}}) {
+ const response = makeMsgResponse(id);
+ try {
+ response.result = yield ManifestObtainer.contentObtainManifest(content);
+ response.success = true;
+ } catch (err) {
+ response.result = serializeError(err);
+ }
+ sendAsyncMessage("DOM:ManifestObtainer:Obtain", response);
+ }),
+
+ fireAppInstalledEvent({data: {id}}){
+ const ev = new Event("appinstalled");
+ const response = makeMsgResponse(id);
+ if (!content || content.top !== content) {
+ const msg = "Can only dispatch install event on top-level browsing contexts.";
+ response.result = serializeError(new Error(msg));
+ } else {
+ response.success = true;
+ content.dispatchEvent(ev);
+ }
+ sendAsyncMessage("DOM:Manifest:FireAppInstalledEvent", 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(aId) {
+ return {
+ id: aId,
+ success: false,
+ result: undefined
+ };
+ }
+
+MessageHandler.registerListeners();
diff --git a/dom/ipc/moz.build b/dom/ipc/moz.build
new file mode 100644
index 000000000..153bd3aae
--- /dev/null
+++ b/dom/ipc/moz.build
@@ -0,0 +1,179 @@
+# -*- 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/.
+
+XPIDL_SOURCES += [
+ 'nsIHangReport.idl',
+]
+
+XPIDL_MODULE = 'dom'
+
+EXPORTS.mozilla.dom.ipc += [
+ 'BlobChild.h',
+ 'BlobParent.h',
+ 'IdType.h',
+ 'nsIRemoteBlob.h',
+ 'StructuredCloneData.h',
+]
+
+EXPORTS.mozilla.dom += [
+ 'ContentBridgeChild.h',
+ 'ContentBridgeParent.h',
+ 'ContentChild.h',
+ 'ContentParent.h',
+ 'ContentProcess.h',
+ 'ContentProcessManager.h',
+ 'CPOWManagerGetter.h',
+ 'CrashReporterChild.h',
+ 'CrashReporterParent.h',
+ 'FilePickerParent.h',
+ 'nsIContentChild.h',
+ 'nsIContentParent.h',
+ 'PermissionMessageUtils.h',
+ 'TabChild.h',
+ 'TabContext.h',
+ 'TabMessageUtils.h',
+ 'TabParent.h',
+]
+
+EXPORTS.mozilla += [
+ 'AppProcessChecker.h',
+ 'PreallocatedProcessManager.h',
+ 'ProcessHangMonitor.h',
+ 'ProcessHangMonitorIPC.h',
+ 'ProcessPriorityManager.h',
+]
+
+UNIFIED_SOURCES += [
+ 'AppProcessChecker.cpp',
+ 'ColorPickerParent.cpp',
+ 'ContentBridgeChild.cpp',
+ 'ContentBridgeParent.cpp',
+ 'ContentParent.cpp',
+ 'ContentProcess.cpp',
+ 'ContentProcessManager.cpp',
+ 'CrashReporterParent.cpp',
+ 'DatePickerParent.cpp',
+ 'FilePickerParent.cpp',
+ 'nsIContentChild.cpp',
+ 'nsIContentParent.cpp',
+ 'PermissionMessageUtils.cpp',
+ 'PreallocatedProcessManager.cpp',
+ 'ProcessPriorityManager.cpp',
+ 'ScreenManagerParent.cpp',
+ 'StructuredCloneData.cpp',
+ 'TabChild.cpp',
+ 'TabContext.cpp',
+ 'TabMessageUtils.cpp',
+ 'TabParent.cpp',
+]
+
+# Blob.cpp cannot be compiled in unified mode because it triggers a fatal gcc warning.
+# CrashReporterChild.cpp cannot be compiled in unified mode because of name clashes
+# in OS X headers.
+# ContentChild.cpp cannot be compiled in unified mode on linux due to Time conflict
+SOURCES += [
+ 'Blob.cpp',
+ 'ContentChild.cpp',
+ 'CrashReporterChild.cpp',
+ 'ProcessHangMonitor.cpp',
+]
+
+IPDL_SOURCES += [
+ 'BlobTypes.ipdlh',
+ 'DOMTypes.ipdlh',
+ 'PBlob.ipdl',
+ 'PBlobStream.ipdl',
+ 'PBrowser.ipdl',
+ 'PBrowserOrId.ipdlh',
+ 'PColorPicker.ipdl',
+ 'PContent.ipdl',
+ 'PContentBridge.ipdl',
+ 'PContentPermission.ipdlh',
+ 'PContentPermissionRequest.ipdl',
+ 'PCrashReporter.ipdl',
+ 'PCycleCollectWithLogs.ipdl',
+ 'PDatePicker.ipdl',
+ 'PDocumentRenderer.ipdl',
+ 'PFilePicker.ipdl',
+ 'PMemoryReportRequest.ipdl',
+ 'PPluginWidget.ipdl',
+ 'PProcessHangMonitor.ipdl',
+ 'PScreenManager.ipdl',
+ 'PTabContext.ipdlh',
+ 'ServiceWorkerConfiguration.ipdlh',
+]
+
+include('/ipc/chromium/chromium-config.mozbuild')
+
+FINAL_LIBRARY = 'xul'
+
+if CONFIG['MOZ_SANDBOX'] and CONFIG['OS_TARGET'] == 'Darwin':
+ USE_LIBS += [
+ 'mozsandbox',
+ ]
+
+if CONFIG['MOZ_CONTENT_SANDBOX'] and CONFIG['OS_ARCH'] == 'Linux':
+ USE_LIBS += [
+ 'mozsandbox',
+ ]
+
+LOCAL_INCLUDES += [
+ '/caps',
+ '/chrome',
+ '/docshell/base',
+ '/dom/base',
+ '/dom/events',
+ '/dom/filesystem',
+ '/dom/geolocation',
+ '/dom/media/webspeech/synth/ipc',
+ '/dom/security',
+ '/dom/storage',
+ '/dom/workers',
+ '/embedding/components/printingui/ipc',
+ '/extensions/cookie',
+ '/extensions/spellcheck/src',
+ '/gfx/2d',
+ '/hal/sandbox',
+ '/layout/base',
+ '/media/webrtc',
+ '/netwerk/base',
+ '/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']
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('android', 'gtk2', 'gonk'):
+ DEFINES['MOZ_ENABLE_FREETYPE'] = True
+
+if CONFIG['MOZ_TOOLKIT_SEARCH']:
+ DEFINES['MOZ_TOOLKIT_SEARCH'] = True
+
+JAR_MANIFESTS += ['jar.mn']
+
+BROWSER_CHROME_MANIFESTS += ['tests/browser.ini']
+MOCHITEST_CHROME_MANIFESTS += ['tests/chrome.ini']
+MOCHITEST_MANIFESTS += ['tests/mochitest.ini']
+
+CXXFLAGS += CONFIG['TK_CFLAGS']
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']
diff --git a/dom/ipc/nsIContentChild.cpp b/dom/ipc/nsIContentChild.cpp
new file mode 100644
index 000000000..c5f748fe1
--- /dev/null
+++ b/dom/ipc/nsIContentChild.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 "nsIContentChild.h"
+
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/dom/DOMTypes.h"
+#include "mozilla/dom/File.h"
+#include "mozilla/dom/PermissionMessageUtils.h"
+#include "mozilla/dom/TabChild.h"
+#include "mozilla/dom/ipc/BlobChild.h"
+#include "mozilla/dom/ipc/StructuredCloneData.h"
+#include "mozilla/ipc/FileDescriptorSetChild.h"
+#include "mozilla/ipc/InputStreamUtils.h"
+#include "mozilla/ipc/SendStream.h"
+
+#include "nsPrintfCString.h"
+#include "xpcpublic.h"
+
+using namespace mozilla::ipc;
+using namespace mozilla::jsipc;
+
+namespace mozilla {
+namespace dom {
+
+PJavaScriptChild*
+nsIContentChild::AllocPJavaScriptChild()
+{
+ return NewJavaScriptChild();
+}
+
+bool
+nsIContentChild::DeallocPJavaScriptChild(PJavaScriptChild* aChild)
+{
+ ReleaseJavaScriptChild(aChild);
+ return true;
+}
+
+PBrowserChild*
+nsIContentChild::AllocPBrowserChild(const TabId& aTabId,
+ const IPCTabContext& aContext,
+ const uint32_t& aChromeFlags,
+ const ContentParentId& aCpID,
+ const bool& aIsForApp,
+ const bool& aIsForBrowser)
+{
+ // 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<TabChild> child =
+ TabChild::Create(this, aTabId, tc.GetTabContext(), aChromeFlags);
+
+ // The ref here is released in DeallocPBrowserChild.
+ return child.forget().take();
+}
+
+bool
+nsIContentChild::DeallocPBrowserChild(PBrowserChild* aIframe)
+{
+ TabChild* child = static_cast<TabChild*>(aIframe);
+ NS_RELEASE(child);
+ return true;
+}
+
+PBlobChild*
+nsIContentChild::AllocPBlobChild(const BlobConstructorParams& aParams)
+{
+ return BlobChild::Create(this, aParams);
+}
+
+bool
+nsIContentChild::DeallocPBlobChild(PBlobChild* aActor)
+{
+ BlobChild::Destroy(aActor);
+ return true;
+}
+
+BlobChild*
+nsIContentChild::GetOrCreateActorForBlob(Blob* aBlob)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aBlob);
+
+ RefPtr<BlobImpl> blobImpl = aBlob->Impl();
+ MOZ_ASSERT(blobImpl);
+
+ return GetOrCreateActorForBlobImpl(blobImpl);
+}
+
+BlobChild*
+nsIContentChild::GetOrCreateActorForBlobImpl(BlobImpl* aImpl)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aImpl);
+
+ BlobChild* actor = BlobChild::GetOrCreate(this, aImpl);
+ NS_ENSURE_TRUE(actor, nullptr);
+
+ return actor;
+}
+
+PSendStreamChild*
+nsIContentChild::AllocPSendStreamChild()
+{
+ MOZ_CRASH("PSendStreamChild actors should be manually constructed!");
+}
+
+bool
+nsIContentChild::DeallocPSendStreamChild(PSendStreamChild* aActor)
+{
+ delete aActor;
+ return true;
+}
+
+PFileDescriptorSetChild*
+nsIContentChild::AllocPFileDescriptorSetChild(const FileDescriptor& aFD)
+{
+ return new FileDescriptorSetChild(aFD);
+}
+
+bool
+nsIContentChild::DeallocPFileDescriptorSetChild(PFileDescriptorSetChild* aActor)
+{
+ delete static_cast<FileDescriptorSetChild*>(aActor);
+ return true;
+}
+
+bool
+nsIContentChild::RecvAsyncMessage(const nsString& aMsg,
+ InfallibleTArray<CpowEntry>&& aCpows,
+ const IPC::Principal& aPrincipal,
+ const ClonedMessageData& aData)
+{
+ RefPtr<nsFrameMessageManager> cpm = nsFrameMessageManager::GetChildProcessManager();
+ if (cpm) {
+ ipc::StructuredCloneData data;
+ ipc::UnpackClonedMessageDataForChild(aData, data);
+
+ CrossProcessCpowHolder cpows(this, aCpows);
+ cpm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(cpm.get()), nullptr,
+ aMsg, false, &data, &cpows, aPrincipal, nullptr);
+ }
+ return true;
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/nsIContentChild.h b/dom/ipc/nsIContentChild.h
new file mode 100644
index 000000000..211144e9a
--- /dev/null
+++ b/dom/ipc/nsIContentChild.h
@@ -0,0 +1,118 @@
+/* -*- 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_nsIContentChild_h
+#define mozilla_dom_nsIContentChild_h
+
+#include "mozilla/dom/ipc/IdType.h"
+
+#include "nsISupports.h"
+#include "nsTArrayForwardDeclare.h"
+#include "mozilla/dom/CPOWManagerGetter.h"
+#include "mozilla/ipc/Shmem.h"
+#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
+
+#define NS_ICONTENTCHILD_IID \
+ { 0x4eed2e73, 0x94ba, 0x48a8, \
+ { 0xa2, 0xd1, 0xa5, 0xed, 0x86, 0xd7, 0xbb, 0xe4 } }
+
+class nsString;
+
+namespace IPC {
+class Principal;
+} // namespace IPC
+
+namespace mozilla {
+namespace ipc {
+class FileDescriptor;
+class PFileDescriptorSetChild;
+class PSendStreamChild;
+class Shmem;
+} // namespace ipc
+
+namespace jsipc {
+class PJavaScriptChild;
+class CpowEntry;
+} // namespace jsipc
+
+namespace dom {
+
+class Blob;
+class BlobChild;
+class BlobImpl;
+class BlobConstructorParams;
+class ClonedMessageData;
+class IPCTabContext;
+class PBlobChild;
+class PBrowserChild;
+
+class nsIContentChild : public nsISupports
+ , public CPOWManagerGetter
+ , public mozilla::ipc::IShmemAllocator
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_ICONTENTCHILD_IID)
+
+ BlobChild* GetOrCreateActorForBlob(Blob* aBlob);
+ BlobChild* GetOrCreateActorForBlobImpl(BlobImpl* aImpl);
+
+ virtual PBlobChild*
+ SendPBlobConstructor(PBlobChild* aActor,
+ const BlobConstructorParams& aParams) = 0;
+
+ virtual bool
+ SendPBrowserConstructor(PBrowserChild* aActor,
+ const TabId& aTabId,
+ const IPCTabContext& aContext,
+ const uint32_t& aChromeFlags,
+ const ContentParentId& aCpID,
+ const bool& aIsForApp,
+ const bool& aIsForBrowser) = 0;
+
+ virtual mozilla::ipc::PFileDescriptorSetChild*
+ SendPFileDescriptorSetConstructor(const mozilla::ipc::FileDescriptor&) = 0;
+
+ virtual mozilla::ipc::PSendStreamChild*
+ SendPSendStreamConstructor(mozilla::ipc::PSendStreamChild*) = 0;
+
+protected:
+ virtual jsipc::PJavaScriptChild* AllocPJavaScriptChild();
+ virtual bool DeallocPJavaScriptChild(jsipc::PJavaScriptChild*);
+
+ virtual PBrowserChild* AllocPBrowserChild(const TabId& aTabId,
+ const IPCTabContext& aContext,
+ const uint32_t& aChromeFlags,
+ const ContentParentId& aCpId,
+ const bool& aIsForApp,
+ const bool& aIsForBrowser);
+ virtual bool DeallocPBrowserChild(PBrowserChild*);
+
+ virtual PBlobChild* AllocPBlobChild(const BlobConstructorParams& aParams);
+
+ virtual bool DeallocPBlobChild(PBlobChild* aActor);
+
+ virtual mozilla::ipc::PSendStreamChild* AllocPSendStreamChild();
+
+ virtual bool DeallocPSendStreamChild(mozilla::ipc::PSendStreamChild* aActor);
+
+ virtual mozilla::ipc::PFileDescriptorSetChild*
+ AllocPFileDescriptorSetChild(const mozilla::ipc::FileDescriptor& aFD);
+
+ virtual bool
+ DeallocPFileDescriptorSetChild(mozilla::ipc::PFileDescriptorSetChild* aActor);
+
+ virtual bool RecvAsyncMessage(const nsString& aMsg,
+ InfallibleTArray<jsipc::CpowEntry>&& aCpows,
+ const IPC::Principal& aPrincipal,
+ const ClonedMessageData& aData);
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIContentChild, NS_ICONTENTCHILD_IID)
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_nsIContentChild_h */
diff --git a/dom/ipc/nsIContentParent.cpp b/dom/ipc/nsIContentParent.cpp
new file mode 100644
index 000000000..0ef1abdf7
--- /dev/null
+++ b/dom/ipc/nsIContentParent.cpp
@@ -0,0 +1,328 @@
+/* -*- 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 "nsIContentParent.h"
+
+#include "mozilla/AppProcessChecker.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/dom/File.h"
+#include "mozilla/dom/ContentParent.h"
+#include "mozilla/dom/ContentBridgeParent.h"
+#include "mozilla/dom/PTabContext.h"
+#include "mozilla/dom/PermissionMessageUtils.h"
+#include "mozilla/dom/TabParent.h"
+#include "mozilla/dom/ipc/BlobParent.h"
+#include "mozilla/dom/ipc/StructuredCloneData.h"
+#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
+#include "mozilla/ipc/FileDescriptorSetParent.h"
+#include "mozilla/ipc/PFileDescriptorSetParent.h"
+#include "mozilla/ipc/SendStreamAlloc.h"
+#include "mozilla/Unused.h"
+
+#include "nsFrameMessageManager.h"
+#include "nsIWebBrowserChrome.h"
+#include "nsPrintfCString.h"
+#include "xpcpublic.h"
+
+using namespace mozilla::jsipc;
+
+// 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
+
+namespace mozilla {
+namespace dom {
+
+nsIContentParent::nsIContentParent()
+{
+ mMessageManager = nsFrameMessageManager::NewProcessMessageManager(true);
+}
+
+ContentParent*
+nsIContentParent::AsContentParent()
+{
+ MOZ_ASSERT(IsContentParent());
+ return static_cast<ContentParent*>(this);
+}
+
+ContentBridgeParent*
+nsIContentParent::AsContentBridgeParent()
+{
+ MOZ_ASSERT(IsContentBridgeParent());
+ return static_cast<ContentBridgeParent*>(this);
+}
+
+PJavaScriptParent*
+nsIContentParent::AllocPJavaScriptParent()
+{
+ return NewJavaScriptParent();
+}
+
+bool
+nsIContentParent::DeallocPJavaScriptParent(PJavaScriptParent* aParent)
+{
+ ReleaseJavaScriptParent(aParent);
+ return true;
+}
+
+bool
+nsIContentParent::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 &&
+ aContext.type() != IPCTabContext::TUnsafeIPCTabContext) {
+ ASSERT_UNLESS_FUZZING("Unexpected IPCTabContext type. Aborting AllocPBrowserParent.");
+ return false;
+ }
+
+ if (aContext.type() == IPCTabContext::TPopupIPCTabContext) {
+ const PopupIPCTabContext& popupContext = aContext.get_PopupIPCTabContext();
+ if (popupContext.opener().type() != PBrowserOrId::TPBrowserParent) {
+ ASSERT_UNLESS_FUZZING("Unexpected PopupIPCTabContext type. Aborting AllocPBrowserParent.");
+ return false;
+ }
+
+ auto opener = TabParent::GetFrom(popupContext.opener().get_PBrowserParent());
+ if (!opener) {
+ ASSERT_UNLESS_FUZZING("Got null opener from child; aborting AllocPBrowserParent.");
+ return false;
+ }
+
+ // Popup windows of isMozBrowserElement frames must be isMozBrowserElement if
+ // the parent isMozBrowserElement. Allocating a !isMozBrowserElement frame with
+ // same app ID would allow the content to access data it's not supposed to.
+ if (!popupContext.isMozBrowserElement() && opener->IsMozBrowserElement()) {
+ ASSERT_UNLESS_FUZZING("Child trying to escalate privileges! 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;
+}
+
+PBrowserParent*
+nsIContentParent::AllocPBrowserParent(const TabId& aTabId,
+ const IPCTabContext& aContext,
+ const uint32_t& aChromeFlags,
+ const ContentParentId& aCpId,
+ const bool& aIsForApp,
+ const bool& aIsForBrowser)
+{
+ Unused << aCpId;
+ Unused << aIsForApp;
+ Unused << aIsForBrowser;
+
+ if (!CanOpenBrowser(aContext)) {
+ return nullptr;
+ }
+
+ uint32_t chromeFlags = aChromeFlags;
+ if (aContext.type() == IPCTabContext::TPopupIPCTabContext) {
+ // CanOpenBrowser has ensured that the IPCTabContext is of
+ // type PopupIPCTabContext, and that the opener TabParent is
+ // reachable.
+ const PopupIPCTabContext& popupContext = aContext.get_PopupIPCTabContext();
+ auto opener = TabParent::GetFrom(popupContext.opener().get_PBrowserParent());
+ // We must ensure that the private browsing and remoteness flags
+ // match those of the opener.
+ nsCOMPtr<nsILoadContext> loadContext = opener->GetLoadContext();
+ if (!loadContext) {
+ return nullptr;
+ }
+
+ bool isPrivate;
+ loadContext->GetUsePrivateBrowsing(&isPrivate);
+ if (isPrivate) {
+ chromeFlags |= nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW;
+ }
+ }
+
+ // And because we're allocating a remote browser, of course the
+ // window is remote.
+ chromeFlags |= nsIWebBrowserChrome::CHROME_REMOTE_WINDOW;
+
+ MaybeInvalidTabContext tc(aContext);
+ MOZ_ASSERT(tc.IsValid());
+ TabParent* parent = new TabParent(this, aTabId, tc.GetTabContext(), chromeFlags);
+
+ // We release this ref in DeallocPBrowserParent()
+ NS_ADDREF(parent);
+ return parent;
+}
+
+bool
+nsIContentParent::DeallocPBrowserParent(PBrowserParent* aFrame)
+{
+ TabParent* parent = TabParent::GetFrom(aFrame);
+ NS_RELEASE(parent);
+ return true;
+}
+
+PBlobParent*
+nsIContentParent::AllocPBlobParent(const BlobConstructorParams& aParams)
+{
+ return BlobParent::Create(this, aParams);
+}
+
+bool
+nsIContentParent::DeallocPBlobParent(PBlobParent* aActor)
+{
+ BlobParent::Destroy(aActor);
+ return true;
+}
+
+BlobParent*
+nsIContentParent::GetOrCreateActorForBlob(Blob* aBlob)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aBlob);
+
+ RefPtr<BlobImpl> blobImpl = aBlob->Impl();
+ MOZ_ASSERT(blobImpl);
+
+ return GetOrCreateActorForBlobImpl(blobImpl);
+}
+
+BlobParent*
+nsIContentParent::GetOrCreateActorForBlobImpl(BlobImpl* aImpl)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+ MOZ_ASSERT(aImpl);
+
+ BlobParent* actor = BlobParent::GetOrCreate(this, aImpl);
+ NS_ENSURE_TRUE(actor, nullptr);
+
+ return actor;
+}
+
+bool
+nsIContentParent::RecvSyncMessage(const nsString& aMsg,
+ const ClonedMessageData& aData,
+ InfallibleTArray<CpowEntry>&& aCpows,
+ const IPC::Principal& aPrincipal,
+ nsTArray<ipc::StructuredCloneData>* aRetvals)
+{
+ // FIXME Permission check in Content process
+ nsIPrincipal* principal = aPrincipal;
+ if (IsContentParent()) {
+ ContentParent* parent = AsContentParent();
+ if (!ContentParent::IgnoreIPCPrincipal() &&
+ parent && principal && !AssertAppPrincipal(parent, principal)) {
+ return false;
+ }
+ }
+
+ RefPtr<nsFrameMessageManager> ppm = mMessageManager;
+ if (ppm) {
+ ipc::StructuredCloneData data;
+ ipc::UnpackClonedMessageDataForParent(aData, data);
+
+ CrossProcessCpowHolder cpows(this, aCpows);
+ ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()), nullptr,
+ aMsg, true, &data, &cpows, aPrincipal, aRetvals);
+ }
+ return true;
+}
+
+bool
+nsIContentParent::RecvRpcMessage(const nsString& aMsg,
+ const ClonedMessageData& aData,
+ InfallibleTArray<CpowEntry>&& aCpows,
+ const IPC::Principal& aPrincipal,
+ nsTArray<ipc::StructuredCloneData>* aRetvals)
+{
+ // FIXME Permission check in Content process
+ nsIPrincipal* principal = aPrincipal;
+ if (IsContentParent()) {
+ ContentParent* parent = AsContentParent();
+ if (!ContentParent::IgnoreIPCPrincipal() &&
+ parent && principal && !AssertAppPrincipal(parent, principal)) {
+ return false;
+ }
+ }
+
+ RefPtr<nsFrameMessageManager> ppm = mMessageManager;
+ if (ppm) {
+ ipc::StructuredCloneData data;
+ ipc::UnpackClonedMessageDataForParent(aData, data);
+
+ CrossProcessCpowHolder cpows(this, aCpows);
+ ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()), nullptr,
+ aMsg, true, &data, &cpows, aPrincipal, aRetvals);
+ }
+ return true;
+}
+
+PFileDescriptorSetParent*
+nsIContentParent::AllocPFileDescriptorSetParent(const FileDescriptor& aFD)
+{
+ return new FileDescriptorSetParent(aFD);
+}
+
+bool
+nsIContentParent::DeallocPFileDescriptorSetParent(PFileDescriptorSetParent* aActor)
+{
+ delete static_cast<FileDescriptorSetParent*>(aActor);
+ return true;
+}
+
+PSendStreamParent*
+nsIContentParent::AllocPSendStreamParent()
+{
+ return mozilla::ipc::AllocPSendStreamParent();
+}
+
+bool
+nsIContentParent::DeallocPSendStreamParent(PSendStreamParent* aActor)
+{
+ delete aActor;
+ return true;
+}
+
+bool
+nsIContentParent::RecvAsyncMessage(const nsString& aMsg,
+ InfallibleTArray<CpowEntry>&& aCpows,
+ const IPC::Principal& aPrincipal,
+ const ClonedMessageData& aData)
+{
+ // FIXME Permission check in Content process
+ nsIPrincipal* principal = aPrincipal;
+ if (IsContentParent()) {
+ ContentParent* parent = AsContentParent();
+ if (!ContentParent::IgnoreIPCPrincipal() &&
+ parent && principal && !AssertAppPrincipal(parent, principal)) {
+ return false;
+ }
+ }
+
+ RefPtr<nsFrameMessageManager> ppm = mMessageManager;
+ if (ppm) {
+ ipc::StructuredCloneData data;
+ ipc::UnpackClonedMessageDataForParent(aData, data);
+
+ CrossProcessCpowHolder cpows(this, aCpows);
+ ppm->ReceiveMessage(static_cast<nsIContentFrameMessageManager*>(ppm.get()), nullptr,
+ aMsg, false, &data, &cpows, aPrincipal, nullptr);
+ }
+ return true;
+}
+
+} // namespace dom
+} // namespace mozilla
diff --git a/dom/ipc/nsIContentParent.h b/dom/ipc/nsIContentParent.h
new file mode 100644
index 000000000..7ba015264
--- /dev/null
+++ b/dom/ipc/nsIContentParent.h
@@ -0,0 +1,145 @@
+/* -*- 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_nsIContentParent_h
+#define mozilla_dom_nsIContentParent_h
+
+#include "mozilla/Attributes.h"
+#include "mozilla/dom/ipc/IdType.h"
+#include "mozilla/ipc/ProtocolUtils.h"
+
+#include "nsFrameMessageManager.h"
+#include "nsISupports.h"
+#include "mozilla/dom/CPOWManagerGetter.h"
+
+#define NS_ICONTENTPARENT_IID \
+ { 0xeeec9ebf, 0x8ecf, 0x4e38, \
+ { 0x81, 0xda, 0xb7, 0x34, 0x13, 0x7e, 0xac, 0xf3 } }
+
+namespace IPC {
+class Principal;
+} // namespace IPC
+
+namespace mozilla {
+
+namespace jsipc {
+class PJavaScriptParent;
+class CpowEntry;
+} // namespace jsipc
+
+namespace ipc {
+class PFileDescriptorSetParent;
+class PSendStreamParent;
+}
+
+namespace dom {
+
+class Blob;
+class BlobConstructorParams;
+class BlobImpl;
+class BlobParent;
+class ContentParent;
+class ContentBridgeParent;
+class IPCTabContext;
+class PBlobParent;
+class PBrowserParent;
+
+class nsIContentParent : public nsISupports
+ , public mozilla::dom::ipc::MessageManagerCallback
+ , public CPOWManagerGetter
+ , public mozilla::ipc::IShmemAllocator
+{
+public:
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_ICONTENTPARENT_IID)
+
+ nsIContentParent();
+
+ BlobParent* GetOrCreateActorForBlob(Blob* aBlob);
+ BlobParent* GetOrCreateActorForBlobImpl(BlobImpl* aImpl);
+
+ virtual ContentParentId ChildID() const = 0;
+ virtual bool IsForApp() const = 0;
+ virtual bool IsForBrowser() const = 0;
+
+ MOZ_MUST_USE virtual PBlobParent*
+ SendPBlobConstructor(PBlobParent* aActor,
+ const BlobConstructorParams& aParams) = 0;
+
+ MOZ_MUST_USE virtual PBrowserParent*
+ SendPBrowserConstructor(PBrowserParent* actor,
+ const TabId& aTabId,
+ const IPCTabContext& context,
+ const uint32_t& chromeFlags,
+ const ContentParentId& aCpId,
+ const bool& aIsForApp,
+ const bool& aIsForBrowser) = 0;
+
+ virtual bool IsContentParent() const { return false; }
+
+ ContentParent* AsContentParent();
+
+ virtual bool IsContentBridgeParent() const { return false; }
+
+ ContentBridgeParent* AsContentBridgeParent();
+
+ nsFrameMessageManager* GetMessageManager() const { return mMessageManager; }
+
+ virtual int32_t Pid() const = 0;
+
+protected: // methods
+ bool CanOpenBrowser(const IPCTabContext& aContext);
+
+protected: // IPDL methods
+ virtual mozilla::jsipc::PJavaScriptParent* AllocPJavaScriptParent();
+ virtual bool DeallocPJavaScriptParent(mozilla::jsipc::PJavaScriptParent*);
+
+ virtual PBrowserParent* AllocPBrowserParent(const TabId& aTabId,
+ const IPCTabContext& aContext,
+ const uint32_t& aChromeFlags,
+ const ContentParentId& aCpId,
+ const bool& aIsForApp,
+ const bool& aIsForBrowser);
+ virtual bool DeallocPBrowserParent(PBrowserParent* frame);
+
+ virtual PBlobParent* AllocPBlobParent(const BlobConstructorParams& aParams);
+
+ virtual bool DeallocPBlobParent(PBlobParent* aActor);
+
+ virtual mozilla::ipc::PFileDescriptorSetParent*
+ AllocPFileDescriptorSetParent(const mozilla::ipc::FileDescriptor& aFD);
+
+ virtual bool
+ DeallocPFileDescriptorSetParent(mozilla::ipc::PFileDescriptorSetParent* aActor);
+
+ virtual mozilla::ipc::PSendStreamParent* AllocPSendStreamParent();
+
+ virtual bool DeallocPSendStreamParent(mozilla::ipc::PSendStreamParent* aActor);
+
+ virtual bool RecvSyncMessage(const nsString& aMsg,
+ const ClonedMessageData& aData,
+ InfallibleTArray<jsipc::CpowEntry>&& aCpows,
+ const IPC::Principal& aPrincipal,
+ nsTArray<ipc::StructuredCloneData>* aRetvals);
+ virtual bool RecvRpcMessage(const nsString& aMsg,
+ const ClonedMessageData& aData,
+ InfallibleTArray<jsipc::CpowEntry>&& aCpows,
+ const IPC::Principal& aPrincipal,
+ nsTArray<ipc::StructuredCloneData>* aRetvals);
+ virtual bool RecvAsyncMessage(const nsString& aMsg,
+ InfallibleTArray<jsipc::CpowEntry>&& aCpows,
+ const IPC::Principal& aPrincipal,
+ const ClonedMessageData& aData);
+
+protected: // members
+ RefPtr<nsFrameMessageManager> mMessageManager;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIContentParent, NS_ICONTENTPARENT_IID)
+
+} // namespace dom
+} // namespace mozilla
+
+#endif /* mozilla_dom_nsIContentParent_h */
diff --git a/dom/ipc/nsIHangReport.idl b/dom/ipc/nsIHangReport.idl
new file mode 100644
index 000000000..9ccbbbbad
--- /dev/null
+++ b/dom/ipc/nsIHangReport.idl
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsISupports.idl"
+
+interface nsIDOMElement;
+interface nsIFrameLoader;
+
+/**
+ * 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 nsIDOMElement scriptBrowser;
+ readonly attribute ACString scriptFileName;
+ readonly attribute unsigned long scriptLineNo;
+
+ // For PLUGIN_HANGs, this field contains information about the plugin.
+ // Only valid for PLUGIN_HANG reports.
+ readonly attribute ACString pluginName;
+
+ // 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 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 nsIFrameLoader aFrameLoader);
+};
diff --git a/dom/ipc/nsIRemoteBlob.h b/dom/ipc/nsIRemoteBlob.h
new file mode 100644
index 000000000..55ff0352a
--- /dev/null
+++ b/dom/ipc/nsIRemoteBlob.h
@@ -0,0 +1,45 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_ipc_nsIRemoteBlob_h
+#define mozilla_dom_ipc_nsIRemoteBlob_h
+
+#include "nsISupports.h"
+
+#ifndef NS_NO_VTABLE
+#define NS_NO_VTABLE
+#endif
+
+#define NS_IREMOTEBLOB_IID \
+ {0x0b8b0091, 0xb315, 0x48a2, {0x90, 0xf1, 0x60, 0x0e, 0x78, 0x35, 0xf7, 0x2d}}
+
+namespace mozilla {
+namespace dom {
+
+class BlobChild;
+class BlobParent;
+
+} // namespace dom
+} // namespace mozilla
+
+class NS_NO_VTABLE nsIRemoteBlob : public nsISupports
+{
+public:
+ typedef mozilla::dom::BlobChild BlobChild;
+ typedef mozilla::dom::BlobParent BlobParent;
+
+ NS_DECLARE_STATIC_IID_ACCESSOR(NS_IREMOTEBLOB_IID)
+
+ virtual BlobChild*
+ GetBlobChild() = 0;
+
+ virtual BlobParent*
+ GetBlobParent() = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsIRemoteBlob, NS_IREMOTEBLOB_IID)
+
+#endif // mozilla_dom_ipc_nsIRemoteBlob_h
diff --git a/dom/ipc/preload.js b/dom/ipc/preload.js
new file mode 100644
index 000000000..10fd15f4a
--- /dev/null
+++ b/dom/ipc/preload.js
@@ -0,0 +1,130 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// Preload some things, in an attempt to make app startup faster.
+//
+// This script is run when the preallocated process starts. It is injected as
+// a frame script.
+
+var BrowserElementIsPreloaded = true;
+
+var DoPreloadPostfork = function(aCallback) {
+ Services.obs.addObserver({
+ _callback: aCallback,
+
+ observe: function() {
+ this._callback();
+ Services.obs.removeObserver(this, "preload-postfork");
+ }
+ }, "preload-postfork", false);
+};
+
+(function (global) {
+ "use strict";
+
+ let Cu = Components.utils;
+ let Cc = Components.classes;
+ let Ci = Components.interfaces;
+
+ Cu.import("resource://gre/modules/AppsUtils.jsm");
+ Cu.import("resource://gre/modules/BrowserElementPromptService.jsm");
+ Cu.import("resource://gre/modules/DOMRequestHelper.jsm");
+ Cu.import("resource://gre/modules/FileUtils.jsm");
+ Cu.import("resource://gre/modules/Geometry.jsm");
+ Cu.import("resource://gre/modules/IndexedDBHelper.jsm");
+ Cu.import("resource://gre/modules/NetUtil.jsm");
+ Cu.import("resource://gre/modules/Services.jsm");
+ Cu.import("resource://gre/modules/SettingsDB.jsm");
+ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+ Cc["@mozilla.org/appshell/appShellService;1"].getService(Ci["nsIAppShellService"]);
+ Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci["nsIWindowMediator"]);
+ Cc["@mozilla.org/base/telemetry;1"].getService(Ci["nsITelemetry"]);
+ Cc["@mozilla.org/categorymanager;1"].getService(Ci["nsICategoryManager"]);
+ Cc["@mozilla.org/childprocessmessagemanager;1"].getService(Ci["nsIMessageSender"]);
+ Cc["@mozilla.org/consoleservice;1"].getService(Ci["nsIConsoleService"]);
+ Cc["@mozilla.org/docshell/urifixup;1"].getService(Ci["nsIURIFixup"]);
+ Cc["@mozilla.org/dom/dom-request-service;1"].getService(Ci["nsIDOMRequestService"]);
+ Cc["@mozilla.org/embedcomp/prompt-service;1"].getService(Ci["nsIPromptService"]);
+ Cc["@mozilla.org/embedcomp/window-watcher;1"].getService(Ci["nsIWindowWatcher"]);
+ Cc["@mozilla.org/eventlistenerservice;1"].getService(Ci["nsIEventListenerService"]);
+ Cc["@mozilla.org/focus-manager;1"].getService(Ci["nsIFocusManager"]);
+ Cc["@mozilla.org/intl/nslocaleservice;1"].getService(Ci["nsILocaleService"]);
+ Cc["@mozilla.org/intl/stringbundle;1"].getService(Ci["nsIStringBundleService"]);
+ Cc["@mozilla.org/layout/content-policy;1"].getService(Ci["nsIContentPolicy"]);
+ Cc["@mozilla.org/message-loop;1"].getService(Ci["nsIMessageLoop"]);
+ Cc["@mozilla.org/moz/jssubscript-loader;1"].getService(Ci["mozIJSSubScriptLoader"]);
+ Cc["@mozilla.org/network/application-cache-service;1"].getService(Ci["nsIApplicationCacheService"]);
+ Cc["@mozilla.org/network/dns-service;1"].getService(Ci["nsIDNSService"]);
+ Cc["@mozilla.org/network/effective-tld-service;1"].getService(Ci["nsIEffectiveTLDService"]);
+ Cc["@mozilla.org/network/idn-service;1"].getService(Ci["nsIIDNService"]);
+ Cc["@mozilla.org/network/io-service;1"].getService(Ci["nsIIOService2"]);
+ Cc["@mozilla.org/network/mime-hdrparam;1"].getService(Ci["nsIMIMEHeaderParam"]);
+ Cc["@mozilla.org/network/socket-transport-service;1"].getService(Ci["nsISocketTransportService"]);
+ Cc["@mozilla.org/network/stream-transport-service;1"].getService(Ci["nsIStreamTransportService"]);
+ Cc["@mozilla.org/network/url-parser;1?auth=maybe"].getService(Ci["nsIURLParser"]);
+ Cc["@mozilla.org/network/url-parser;1?auth=no"].getService(Ci["nsIURLParser"]);
+ Cc["@mozilla.org/network/url-parser;1?auth=yes"].getService(Ci["nsIURLParser"]);
+ Cc["@mozilla.org/observer-service;1"].getService(Ci["nsIObserverService"]);
+ Cc["@mozilla.org/preferences-service;1"].getService(Ci["nsIPrefBranch"]);
+ Cc["@mozilla.org/scriptsecuritymanager;1"].getService(Ci["nsIScriptSecurityManager"]);
+ Cc["@mozilla.org/storage/service;1"].getService(Ci["mozIStorageService"]);
+ Cc["@mozilla.org/system-info;1"].getService(Ci["nsIPropertyBag2"]);
+ Cc["@mozilla.org/thread-manager;1"].getService(Ci["nsIThreadManager"]);
+ Cc["@mozilla.org/toolkit/app-startup;1"].getService(Ci["nsIAppStartup"]);
+ Cc["@mozilla.org/uriloader;1"].getService(Ci["nsIURILoader"]);
+ Cc["@mozilla.org/cspcontext;1"].createInstance(Ci["nsIContentSecurityPolicy"]);
+ Cc["@mozilla.org/settingsManager;1"].createInstance(Ci["nsISupports"]);
+
+ /* Applications Specific Helper */
+ try {
+ if (Services.prefs.getBoolPref("dom.sysmsg.enabled")) {
+ Cc["@mozilla.org/system-message-manager;1"].getService(Ci["nsIDOMNavigatorSystemMessages"]);
+ }
+ } catch(e) {
+ }
+
+ try {
+ if (Services.prefs.getBoolPref("dom.mozInputMethod.enabled")) {
+ Services.scriptloader.loadSubScript("chrome://global/content/forms.js", global);
+ }
+ } catch (e) {
+ }
+
+ Services.scriptloader.loadSubScript("chrome://global/content/BrowserElementCopyPaste.js", global);
+ Services.scriptloader.loadSubScript("chrome://global/content/BrowserElementChildPreload.js", global);
+
+ Services.io.getProtocolHandler("app");
+ Services.io.getProtocolHandler("default");
+
+ // Register an observer for topic "preload_postfork" after we fork a content
+ // process.
+ DoPreloadPostfork(function () {
+ // Load AppsServiceChild.jsm after fork since it sends an async message to
+ // the chrome process in its init() function.
+ Cu.import("resource://gre/modules/AppsServiceChild.jsm");
+
+ // Load nsIAppsService after fork since its implementation loads
+ // AppsServiceChild.jsm
+ Cc["@mozilla.org/AppsService;1"].getService(Ci["nsIAppsService"]);
+
+ // Load nsICookieService after fork since it sends an IPC constructor
+ // message to the chrome process.
+ Cc["@mozilla.org/cookieService;1"].getService(Ci["nsICookieService"]);
+
+ // Load nsIPermissionManager after fork since it sends a message to the
+ // chrome process to read permissions.
+ Cc["@mozilla.org/permissionmanager;1"].getService(Ci["nsIPermissionManager"]);
+
+ // Load nsIProtocolProxyService after fork since it asynchronously accesses
+ // the "Proxy Resolution" thread after it's frozen.
+ Cc["@mozilla.org/network/protocol-proxy-service;1"].getService(Ci["nsIProtocolProxyService"]);
+
+ // Call docShell.createAboutBlankContentViewer() after fork since it has IPC
+ // activity in the PCompositor protocol.
+ docShell.createAboutBlankContentViewer(null);
+ docShell.isActive = false;
+ });
+})(this);
+
diff --git a/dom/ipc/remote-test.js b/dom/ipc/remote-test.js
new file mode 100644
index 000000000..dd21415cd
--- /dev/null
+++ b/dom/ipc/remote-test.js
@@ -0,0 +1,54 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+dump("Loading remote script!\n");
+dump(content + "\n");
+
+var cpm = Components.classes["@mozilla.org/childprocessmessagemanager;1"]
+ .getService(Components.interfaces.nsISyncMessageSender);
+cpm.addMessageListener("cpm-async",
+ function(m) {
+ cpm.sendSyncMessage("ppm-sync");
+ dump(content.document.documentElement);
+ cpm.sendAsyncMessage("ppm-async");
+ });
+
+var Cc = Components.classes;
+var Ci = Components.interfaces;
+var dshell = content.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShellTreeItem)
+ .rootTreeItem
+ .QueryInterface(Ci.nsIDocShell);
+
+
+addEventListener("click",
+ function(e) {
+ dump(e.target + "\n");
+ if (e.target instanceof Components.interfaces.nsIDOMHTMLAnchorElement &&
+ dshell == docShell) {
+ var retval = docShell.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
+ getInterface(Components.interfaces.nsIContentFrameMessageManager).
+ 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) {
+ while (sendSyncMessage("speed-test")[0].message != "done");
+ });
+
+addMessageListener("async-echo", function(m) {
+ sendAsyncMessage(m.name);
+});
diff --git a/dom/ipc/test.xul b/dom/ipc/test.xul
new file mode 100644
index 000000000..13425779c
--- /dev/null
+++ b/dom/ipc/test.xul
@@ -0,0 +1,274 @@
+<?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"
+ 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 &lt; 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 &lt; 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.QueryInterface(Components.interfaces.nsIFrameLoaderOwner).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').QueryInterface(Components.interfaces.nsIFrameLoaderOwner).frameLoader;
+ var x = parseInt(Math.random() * 100);
+ var y = parseInt(Math.random() * 100);
+ frameLoader.sendCrossProcessMouseEvent("mousedown", x, y, 0, 1, 0, false);
+ frameLoader.sendCrossProcessMouseEvent("mouseup", x, y, 0, 1, 0, false);
+ }
+
+ function keyPress() {
+ // 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').QueryInterface(Components.interfaces.nsIFrameLoaderOwner).frameLoader;
+
+ var keyCode = Components.interfaces.nsIDOMKeyEvent.DOM_VK_A;
+ frameLoader.sendCrossProcessKeyEvent("keydown", keyCode, 0, 0);
+ frameLoader.sendCrossProcessKeyEvent("keypress", keyCode, 0, 0);
+ frameLoader.sendCrossProcessKeyEvent("keyup", keyCode, 0, 0);
+ }
+
+ function openWindow() {
+ window.open('chrome://global/content/test-ipc.xul', '_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(Components.interfaces.nsIMessageBroadcaster);
+ var gm = Components.classes["@mozilla.org/globalmessagemanager;1"]
+ .getService(Components.interfaces.nsIMessageBroadcaster);
+ var cpm = Components.classes["@mozilla.org/childprocessmessagemanager;1"]
+ .getService(Components.interfaces.nsISyncMessageSender);
+
+ 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") &amp;&amp;
+ (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") &amp;&amp;
+ 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 &amp;&amp; hasSync &amp;&amp; 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);
+ }
+
+ var Ci = Components.interfaces;
+ var Cu = Components.utils;
+ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+ </script>
+
+ <toolbar id="controls">
+ <toolbarbutton label="Back"/>
+ <toolbarbutton label="Forward"/>
+ <textbox onchange="loadURL(this.value)" flex="1" id="URL"/>
+ </toolbar>
+ <toolbar>
+ <toolbarbutton onclick="restart()" label="Recover"/>
+ <toolbarbutton onclick="randomClick()" label="random click"/>
+ <toolbarbutton onclick="keyPress()" label="key press"/>
+ <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:"/>
+ <textbox flex="1" id="script"/><button
+ label="send" oncommand="document.getElementById('page')
+ .QueryInterface(Components.interfaces.nsIFrameLoaderOwner)
+ .frameLoader.messageManager
+ .loadFrameScript(this.previousSibling.value, false);"/>
+ </toolbar>
+ <toolbar>
+ <label value="Eval script in chrome context"/>
+ <textbox flex="1"/><button label="run" oncommand="eval(this.previousSibling.value);"/>
+ </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/blob_verify.sjs b/dom/ipc/tests/blob_verify.sjs
new file mode 100644
index 000000000..62b82359e
--- /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, bodyBytes.length);
+ response.finish();
+}
diff --git a/dom/ipc/tests/browser.ini b/dom/ipc/tests/browser.ini
new file mode 100644
index 000000000..f3d8ce140
--- /dev/null
+++ b/dom/ipc/tests/browser.ini
@@ -0,0 +1,6 @@
+[DEFAULT]
+support-files =
+ file_disableScript.html
+ file_domainPolicy_base.html
+
+[browser_domainPolicy.js]
diff --git a/dom/ipc/tests/browser_domainPolicy.js b/dom/ipc/tests/browser_domainPolicy.js
new file mode 100644
index 000000000..df06b8bc0
--- /dev/null
+++ b/dom/ipc/tests/browser_domainPolicy.js
@@ -0,0 +1,240 @@
+var policy; // To make sure we never leave up an activated domain policy after a failed test, let's make this global.
+function activateDomainPolicy() {
+ const ssm = Services.scriptSecurityManager;
+ policy = ssm.activateDomainPolicy();
+}
+
+function deactivateDomainPolicy() {
+ if (policy) {
+ policy.deactivate();
+ policy = null;
+ }
+}
+
+function* test_domainPolicy() {
+
+ XPCOMUtils.defineLazyModuleGetter(this, "Promise", "resource://gre/modules/Promise.jsm");
+ let deferred = Promise.defer();
+ let currentTask = deferred.promise;
+ SpecialPowers.pushPrefEnv(
+ {set: [["dom.ipc.browser_frames.oop_by_default", false],
+ ["browser.pagethumbnails.capturing_disabled", false],
+ ["dom.mozBrowserFramesEnabled", false]]},
+ () => { return deferred.resolve()});
+ yield currentTask;
+
+ // Create tab
+ let tab;
+
+ // Init test
+ function initProcess() {
+ tab = gBrowser.addTab();
+ gBrowser.selectedTab = tab;
+
+ let initPromise = ContentTask.spawn(tab.linkedBrowser, null, function() {
+ Cu.import("resource://gre/modules/PromiseUtils.jsm");
+ function loadBase() {
+ let deferred = PromiseUtils.defer();
+ let listener = (event) => {
+ removeEventListener("DOMDocElementInserted", listener, true);
+ let listener2 = (event) => {
+ content.removeEventListener('load', listener2);
+ deferred.resolve();
+ }
+ content.addEventListener('load', listener2);
+ };
+ addEventListener("DOMDocElementInserted", listener, true);
+ return deferred.promise;
+ }
+
+ return loadBase();
+ });
+ tab.linkedBrowser.loadURI("http://mochi.test:8888/browser/dom/ipc/tests/file_domainPolicy_base.html");
+ return initPromise;
+ }
+
+ // We use ContentTask for the tests, but we also want to pass some data and some helper functions too.
+ // To do that, we serialize an input object via JSON |ipcArgs| and some shared helper functions |initUtils|
+ // and eval them in the content process.
+ var ipcArgs = {};
+ function initUtils(obj) {
+ obj.checkScriptEnabled = function(win, expectEnabled) {
+ win.wrappedJSObject.gFiredOnclick = false;
+ win.document.body.dispatchEvent(new win.Event('click'));
+ return { passed: win.wrappedJSObject.gFiredOnclick == expectEnabled,
+ msg: `Checking script-enabled for ${win.name} (${win.location})`};
+ }
+
+ obj.navigateFrame = function(ifr, src) {
+ let deferred = PromiseUtils.defer();
+ function onload() {
+ ifr.removeEventListener('load', onload);
+ deferred.resolve();
+ }
+ ifr.addEventListener('load', onload, false);
+ ifr.setAttribute('src', src);
+ return deferred.promise;
+ }
+ };
+
+ function runTest(test) {
+ return ContentTask.spawn(tab.linkedBrowser,
+ 'ipcArgs = ' + JSON.stringify(ipcArgs) + '; (' + initUtils.toSource() + ')(utils)', test);
+ }
+
+ function checkAndCleanup(result) {
+ result = [].concat(result);
+ for (var i in result)
+ ok(result[i].passed, result[i].msg);
+ gBrowser.removeTab(tab);
+ deactivateDomainPolicy();
+ ipcArgs = {};
+ }
+
+ function testDomain(domain) {
+ ipcArgs.domain = domain;
+ return (aUtils) => {
+ Cu.import("resource://gre/modules/PromiseUtils.jsm");
+ var ipcArgs;
+ var utils = {};
+ eval(aUtils);
+
+ let path = '/browser/dom/ipc/tests/file_disableScript.html';
+ let deferred = PromiseUtils.defer();
+ var rootFrame = content.document.getElementById('root');
+ utils.navigateFrame(rootFrame, ipcArgs.domain + path).then(() => {
+ deferred.resolve(utils.checkScriptEnabled(rootFrame.contentWindow, false));
+ });
+ return deferred.promise;
+ }
+ }
+
+ info("Testing simple blacklist policy");
+
+ info("Creating child process first, activating domainPolicy after");
+ currentTask = initProcess();
+ yield currentTask;
+ activateDomainPolicy();
+ var bl = policy.blacklist;
+ bl.add(Services.io.newURI('http://example.com', null, null));
+ currentTask = runTest(testDomain("http://example.com"));
+ checkAndCleanup(yield currentTask);
+
+ info("Activating domainPolicy first, creating child process after");
+ activateDomainPolicy();
+ var bl = policy.blacklist;
+ bl.add(Services.io.newURI('http://example.com', null, null));
+ currentTask = initProcess();
+ yield currentTask;
+ currentTask = runTest(testDomain("http://example.com"));
+ checkAndCleanup(yield currentTask);
+
+ function testList(expectEnabled, list) {
+ ipcArgs.expectEnabled = expectEnabled;
+ ipcArgs.list = list;
+ return (aUtils) => {
+ Cu.import("resource://gre/modules/PromiseUtils.jsm");
+ var ipcArgs;
+ var utils = {};
+ eval(aUtils);
+
+ var results = [];
+ var testListInternal = function(expectEnabled, list, idx) {
+ idx = idx || 0;
+ let deferred = PromiseUtils.defer();
+ let path = '/browser/dom/ipc/tests/file_disableScript.html';
+ let target = list[idx] + path;
+ var rootFrame = content.document.getElementById('root');
+ utils.navigateFrame(rootFrame, target).then(function() {
+ results.push(utils.checkScriptEnabled(rootFrame.contentWindow, expectEnabled));
+ if (idx == list.length - 1)
+ deferred.resolve(results);
+ else
+ testListInternal(expectEnabled, list, idx + 1).then(function(retArg) { deferred.resolve(retArg); });
+ });
+ return deferred.promise;
+ }
+ return testListInternal(ipcArgs.expectEnabled, ipcArgs.list);
+ }
+ }
+
+ let testPolicy = {
+ 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'],
+ };
+
+ function activate(isBlack, exceptions, superExceptions) {
+ activateDomainPolicy();
+ let set = isBlack ? policy.blacklist : policy.whitelist;
+ let superSet = isBlack ? policy.superBlacklist : policy.superWhitelist;
+ for (var e of exceptions)
+ set.add(makeURI(e));
+ for (var e of superExceptions)
+ superSet.add(makeURI(e));
+ };
+
+ info("Testing Blacklist-style Domain Policy");
+ info("Activating domainPolicy first, creating child process after");
+ activate(true, testPolicy.exceptions, testPolicy.superExceptions);
+ currentTask = initProcess();
+ yield currentTask;
+ let results = [];
+ currentTask = runTest(testList(true, testPolicy.notExempt));
+ results = results.concat(yield currentTask);
+ currentTask = runTest(testList(false, testPolicy.exempt));
+ results = results.concat(yield currentTask);
+ checkAndCleanup(results);
+
+ info("Creating child process first, activating domainPolicy after");
+ currentTask = initProcess();
+ yield currentTask;
+ activate(true, testPolicy.exceptions, testPolicy.superExceptions);
+ results = [];
+ currentTask = runTest(testList(true, testPolicy.notExempt));
+ results = results.concat(yield currentTask);
+ currentTask = runTest(testList(false, testPolicy.exempt));
+ results = results.concat(yield currentTask);
+ checkAndCleanup(results);
+
+ info("Testing Whitelist-style Domain Policy");
+ deferred = Promise.defer();
+ currentTask = deferred.promise;
+ SpecialPowers.pushPrefEnv({set:[["javascript.enabled", false]]}, () => { return deferred.resolve()});
+ yield currentTask;
+
+ info("Activating domainPolicy first, creating child process after");
+ activate(false, testPolicy.exceptions, testPolicy.superExceptions);
+ currentTask = initProcess();
+ yield currentTask;
+ results = [];
+ currentTask = runTest(testList(false, testPolicy.notExempt));
+ results = results.concat(yield currentTask);
+ currentTask = runTest(testList(true, testPolicy.exempt));
+ results = results.concat(yield currentTask);
+ checkAndCleanup(results);
+
+ info("Creating child process first, activating domainPolicy after");
+ currentTask = initProcess();
+ yield currentTask;
+ activate(false, testPolicy.exceptions, testPolicy.superExceptions);
+ results = [];
+ currentTask = runTest(testList(false, testPolicy.notExempt));
+ results = results.concat(yield currentTask);
+ currentTask = runTest(testList(true, testPolicy.exempt));
+ results = results.concat(yield currentTask);
+ checkAndCleanup(results);
+ finish();
+}
+
+
+add_task(test_domainPolicy);
+
+registerCleanupFunction(()=>{
+ deactivateDomainPolicy();
+}) \ No newline at end of file
diff --git a/dom/ipc/tests/chrome.ini b/dom/ipc/tests/chrome.ini
new file mode 100644
index 000000000..b6e3d4801
--- /dev/null
+++ b/dom/ipc/tests/chrome.ini
@@ -0,0 +1,8 @@
+[DEFAULT]
+skip-if = os == 'android'
+support-files =
+ process_error.xul
+ process_error_contentscript.js
+
+[test_process_error.xul]
+skip-if = !crashreporter
diff --git a/dom/ipc/tests/file_bug1086684.html b/dom/ipc/tests/file_bug1086684.html
new file mode 100644
index 000000000..95db6f387
--- /dev/null
+++ b/dom/ipc/tests/file_bug1086684.html
@@ -0,0 +1,16 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1086684
+-->
+<head>
+ <title>Test for Bug 1086684</title>
+ <meta charset="UTF-8">
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1086684">Mozilla Bug 1086684</a>
+<div id="content">
+ <input type="file" id="f">
+</div>
+</body>
+</html>
diff --git a/dom/ipc/tests/file_disableScript.html b/dom/ipc/tests/file_disableScript.html
new file mode 100644
index 000000000..f4888cd58
--- /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 000000000..6e3ec7aec
--- /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/mochitest.ini b/dom/ipc/tests/mochitest.ini
new file mode 100644
index 000000000..cf2a20d5b
--- /dev/null
+++ b/dom/ipc/tests/mochitest.ini
@@ -0,0 +1,22 @@
+[DEFAULT]
+support-files =
+ file_bug1086684.html
+
+[test_blob_sliced_from_child_process.html]
+# This test is only supposed to run in the main process.
+skip-if = e10s
+[test_blob_sliced_from_parent_process.html]
+# This test is only supposed to run in the main process.
+skip-if = e10s
+[test_bug1086684.html]
+# This test is only supposed to run in the main process
+skip-if = e10s || toolkit == 'android'
+[test_cpow_cookies.html]
+[test_child_docshell.html]
+skip-if = toolkit == 'cocoa' # disabled due to hangs, see changeset 6852e7c47edf
+[test_CrashService_crash.html]
+skip-if = !(crashreporter && !e10s && (toolkit == 'gtk2' || toolkit == 'gtk3' || toolkit == 'cocoa' || toolkit == 'windows'))
+[test_temporaryfile_stream.html]
+support-files =
+ blob_verify.sjs
+ !/dom/canvas/test/captureStream_common.js
diff --git a/dom/ipc/tests/process_error.xul b/dom/ipc/tests/process_error.xul
new file mode 100644
index 000000000..62d3d9724
--- /dev/null
+++ b/dom/ipc/tests/process_error.xul
@@ -0,0 +1,58 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin/" 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[
+ Components.utils.import("resource://gre/modules/Services.jsm");
+
+ const ok = window.opener.wrappedJSObject.ok;
+ const is = window.opener.wrappedJSObject.is;
+ const done = window.opener.wrappedJSObject.done;
+ const SimpleTest = window.opener.wrappedJSObject.SimpleTest;
+
+ function getMinidumpDirectory() {
+ var dir = Services.dirsvc.get('ProfD', Components.interfaces.nsIFile);
+ dir.append("minidumps");
+ return dir;
+ }
+
+ function removeFile(directory, filename) {
+ var file = directory.clone();
+ file.append(filename);
+ if (file.exists()) {
+ file.remove(false);
+ }
+ }
+
+ function crashObserver(subject, topic, data) {
+ is(topic, 'ipc:content-shutdown', 'Received correct observer topic.');
+ ok(subject instanceof Components.interfaces.nsIPropertyBag2,
+ 'Subject implements nsIPropertyBag2.');
+
+ var dumpID;
+ if ('nsICrashReporter' in Components.interfaces) {
+ dumpID = subject.getPropertyAsAString('dumpID');
+ ok(dumpID, "dumpID is present and not an empty string");
+ }
+
+ if (dumpID) {
+ var minidumpDirectory = getMinidumpDirectory();
+ removeFile(minidumpDirectory, dumpID + '.dmp');
+ removeFile(minidumpDirectory, dumpID + '.extra');
+ }
+
+ Services.obs.removeObserver(crashObserver, 'ipc:content-shutdown');
+ done();
+ }
+ Services.obs.addObserver(crashObserver, 'ipc:content-shutdown', false);
+
+ document.getElementById('thebrowser')
+ .QueryInterface(Components.interfaces.nsIFrameLoaderOwner)
+ .frameLoader.messageManager
+ .loadFrameScript('chrome://mochitests/content/chrome/dom/ipc/tests/process_error_contentscript.js', true);
+ ]]></script>
+
+</window>
diff --git a/dom/ipc/tests/process_error_contentscript.js b/dom/ipc/tests/process_error_contentscript.js
new file mode 100644
index 000000000..789b8a37f
--- /dev/null
+++ b/dom/ipc/tests/process_error_contentscript.js
@@ -0,0 +1,7 @@
+Components.utils.import("resource://gre/modules/ctypes.jsm");
+
+privateNoteIntentionalCrash();
+
+var zero = new ctypes.intptr_t(8);
+var badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));
+var crash = badptr.contents;
diff --git a/dom/ipc/tests/test_CrashService_crash.html b/dom/ipc/tests/test_CrashService_crash.html
new file mode 100644
index 000000000..a1d9b938d
--- /dev/null
+++ b/dom/ipc/tests/test_CrashService_crash.html
@@ -0,0 +1,95 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+Ensures that content crashes are reported to the crash service
+(nsICrashService and CrashManager.jsm).
+-->
+<head>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+
+<script type="application/javascript;version=1.7">
+"use strict";
+
+SimpleTest.waitForExplicitFinish();
+SimpleTest.requestFlakyTimeout("untriaged");
+SpecialPowers.addPermission("browser", true, document);
+SpecialPowers.pushPrefEnv({'set':[
+ ["dom.mozBrowserFramesEnabled", true],
+ ["network.disable.ipc.security", true],
+ ["dom.ipc.tabs.disabled", false]
+]}, function () {
+
+ var iframe = document.createElementNS('http://www.w3.org/1999/xhtml', 'iframe');
+ iframe.setAttribute("remote", "true");
+ SpecialPowers.wrap(iframe).mozbrowser = true;
+ document.documentElement.appendChild(iframe);
+
+ SimpleTest.expectChildProcessCrash();
+
+ var crashMan =
+ SpecialPowers.Cu.import("resource://gre/modules/Services.jsm").
+ Services.crashmanager;
+
+ // First, clear the crash record store.
+ info("Waiting for pruneOldCrashes");
+ var future = new Date(Date.now() + 1000 * 60 * 60 * 24);
+ crashMan.pruneOldCrashes(future).then(function () {
+
+ var crashDateMS = Date.now();
+
+ // Inject a frame script that crashes the content process.
+ var mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
+ mm.loadFrameScript('data:,new ' + function ContentScriptScope() {
+ let Cu = Components.utils;
+ Cu.import("resource://gre/modules/ctypes.jsm");
+ let crash = function() {
+ let zero = new ctypes.intptr_t(8);
+ let badptr = ctypes.cast(zero, ctypes.PointerType(ctypes.int32_t));
+ badptr.contents;
+ };
+ privateNoteIntentionalCrash();
+ crash();
+ }, false);
+
+ // Finally, poll for the new crash record.
+ function tryGetCrash() {
+ info("Waiting for getCrashes");
+ crashMan.getCrashes().then(SpecialPowers.wrapCallback(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);
+ SimpleTest.finish();
+ }
+ else {
+ setTimeout(tryGetCrash, 1000);
+ }
+ }), function (err) {
+ ok(false, "Error getting crashes: " + err);
+ SimpleTest.finish();
+ });
+ }
+ setTimeout(tryGetCrash, 1000);
+
+ }, function () {
+ ok(false, "pruneOldCrashes error");
+ SimpleTest.finish();
+ });
+});
+
+</script>
+</body>
+</html>
diff --git a/dom/ipc/tests/test_blob_sliced_from_child_process.html b/dom/ipc/tests/test_blob_sliced_from_child_process.html
new file mode 100644
index 000000000..0dec1d84c
--- /dev/null
+++ b/dom/ipc/tests/test_blob_sliced_from_child_process.html
@@ -0,0 +1,185 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Test for slicing blobs that originated in a child process</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js">
+ </script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ </head>
+ <body onload="setup();">
+ <script type="application/javascript;version=1.7">
+"use strict";
+
+function childFrameScript() {
+ "use strict";
+
+ Components.utils.importGlobalProperties(["Blob"]);
+
+ const messageName = "test:blob-slice-test";
+ const blobData = ["So", " ", "many", " ", "blobs!"];
+ const blobText = blobData.join("");
+ const blobType = "text/plain";
+
+ let blob = new Blob(blobData, { type: blobType });
+
+ let sliceText = blobData[2][1] + blobData[2][2];
+ let sliceIndex = blobText.indexOf(sliceText);
+
+ 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: blob });
+ sendAsyncMessage(messageName, { slice: slice });
+}
+
+function parentFrameScript(mm) {
+ 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 finishedTestingBlob = false;
+ let finishedTestingSlice = false;
+
+ mm.addMessageListener(messageName, function(message) {
+ if ("blob" in message.data) {
+ is(receivedBlob, false, "Have not yet received Blob");
+ is(receivedSlice, false, "Have not yet received Slice");
+ is(finishedTestingBlob, false, "Have not yet finished testing Blob");
+ is(finishedTestingSlice, false, "Have not yet finished testing Slice");
+
+ receivedBlob = true;
+
+ let blob = message.data.blob;
+
+ ok(blob instanceof Blob, "Received a Blob");
+ is(blob.size, blobText.length, "Blob has correct size");
+ is(blob.type, blobType, "Blob has correct type");
+
+ let slice = blob.slice(blobText.length -
+ blobData[blobData.length - 1].length,
+ blob.size,
+ blobType);
+
+ ok(slice instanceof Blob, "Slice returned a Blob");
+ is(slice.size,
+ blobData[blobData.length - 1].length,
+ "Slice has correct size");
+ is(slice.type, blobType, "Slice has correct type");
+
+ let reader = new FileReader();
+ reader.onload = function() {
+ is(reader.result,
+ blobData[blobData.length - 1],
+ "Slice has correct data");
+
+ finishedTestingBlob = true;
+
+ if (finishedTestingSlice) {
+ SimpleTest.finish();
+ }
+ };
+ reader.readAsText(slice);
+
+ return;
+ }
+
+ if ("slice" in message.data) {
+ is(receivedBlob, true, "Already received Blob");
+ is(receivedSlice, false, "Have not yet received Slice");
+ is(finishedTestingSlice, false, "Have not yet finished testing Slice");
+
+ receivedSlice = true;
+
+ let slice = message.data.slice;
+
+ ok(slice instanceof Blob, "Received a Blob for slice");
+ is(slice.size, sliceText.length, "Slice has correct size");
+ is(slice.type, blobType, "Slice has correct type");
+
+ let reader = new FileReader();
+ reader.onload = function() {
+ is(reader.result, sliceText, "Slice has correct data");
+
+ let slice2 = slice.slice(1, 2, blobType);
+
+ ok(slice2 instanceof Blob, "Slice returned a Blob");
+ is(slice2.size, 1, "Slice has correct size");
+ is(slice2.type, blobType, "Slice has correct type");
+
+ let reader2 = new FileReader();
+ reader2.onload = function() {
+ is(reader2.result, sliceText[1], "Slice has correct data");
+
+ finishedTestingSlice = true;
+
+ if (finishedTestingBlob) {
+ SimpleTest.finish();
+ }
+ };
+ reader2.readAsText(slice2);
+ };
+ reader.readAsText(slice);
+
+ return;
+ }
+
+ ok(false, "Received a bad message: " + JSON.stringify(message.data));
+ });
+
+ mm.loadFrameScript("data:,(" + childFrameScript.toString() + ")();",
+ false);
+}
+
+function setup() {
+ info("Got load event");
+
+ SpecialPowers.pushPrefEnv(
+ { set: [ ["dom.ipc.browser_frames.oop_by_default", true],
+ ["dom.mozBrowserFramesEnabled", true],
+ ["network.disable.ipc.security", true],
+ ["browser.pagethumbnails.capturing_disabled", true] ] },
+ function() {
+ info("Prefs set");
+
+ SpecialPowers.pushPermissions(
+ [ { type: "browser", allow: true, context: document } ],
+ function() {
+ info("Permissions set");
+
+ let iframe = document.createElement("iframe");
+ SpecialPowers.wrap(iframe).mozbrowser = true;
+ iframe.id = "iframe";
+ iframe.src =
+ "data:text/html,<!DOCTYPE HTML><html><body></body></html>";
+
+ iframe.addEventListener("mozbrowserloadend", function() {
+ info("Starting tests");
+
+ let mm = SpecialPowers.getBrowserFrameMessageManager(iframe)
+ parentFrameScript(mm);
+ });
+
+ document.body.appendChild(iframe);
+ }
+ );
+ }
+ );
+}
+
+SimpleTest.waitForExplicitFinish();
+ </script>
+ </body>
+</html>
diff --git a/dom/ipc/tests/test_blob_sliced_from_parent_process.html b/dom/ipc/tests/test_blob_sliced_from_parent_process.html
new file mode 100644
index 000000000..7d8a1665f
--- /dev/null
+++ b/dom/ipc/tests/test_blob_sliced_from_parent_process.html
@@ -0,0 +1,213 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>Test for slicing blobs that originated in a parent process</title>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js">
+ </script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+ </head>
+ <body onload="setup();">
+ <script type="application/javascript;version=1.7">
+"use strict";
+
+function childFrameScript() {
+ "use strict";
+
+ const { classes: Cc, interfaces: Ci } = Components;
+
+ 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: msg });
+ }
+
+ function ok(condition, name, diag) {
+ sendAsyncMessage(messageName,
+ { op: "ok",
+ condition: condition,
+ name: name,
+ diag: 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: result });
+ }
+
+ function grabAndContinue(arg) {
+ testGenerator.send(arg);
+ }
+
+ function testSteps() {
+ addMessageListener(messageName, grabAndContinue);
+ let message = yield undefined;
+
+ let blob = message.data;
+
+ ok(blob instanceof Ci.nsIDOMBlob, "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(slice instanceof Ci.nsIDOMBlob, "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(slice instanceof Ci.nsIDOMBlob, "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);
+
+ yield undefined;
+ }
+
+ let testGenerator = testSteps();
+ testGenerator.next();
+}
+
+function parentFrameScript(mm) {
+ const messageName = "test:blob-slice-test";
+ const blobData = ["So", " ", "many", " ", "blobs!"];
+ const blobText = blobData.join("");
+ const blobType = "text/plain";
+
+ const sliceText = "an";
+
+ function grabAndContinue(arg) {
+ testGenerator.send(arg);
+ }
+
+ function testSteps() {
+ let slice = yield undefined;
+
+ ok(slice instanceof Blob, "Received a Blob");
+ is(slice.size, sliceText.length, "Slice has correct size");
+ is(slice.type, blobType, "Slice has correct type");
+
+ let reader = new FileReader();
+ reader.onload = grabAndContinue;
+ reader.readAsText(slice);
+ yield undefined;
+
+ is(reader.result, sliceText, "Slice has correct data");
+ SimpleTest.finish();
+
+ yield undefined;
+ }
+
+ let testGenerator = testSteps();
+ testGenerator.next();
+
+ 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.send(data.result);
+ break;
+ }
+
+ default: {
+ ok(false, "Unknown op: " + data.op);
+ SimpleTest.finish();
+ }
+ }
+ });
+
+ mm.loadFrameScript("data:,(" + childFrameScript.toString() + ")();",
+ false);
+
+ let blob = new Blob(blobData, { type: blobType });
+ mm.sendAsyncMessage(messageName, blob);
+}
+
+function setup() {
+ info("Got load event");
+
+ SpecialPowers.pushPrefEnv(
+ { set: [ ["dom.ipc.browser_frames.oop_by_default", true],
+ ["dom.mozBrowserFramesEnabled", true],
+ ["network.disable.ipc.security", true],
+ ["browser.pagethumbnails.capturing_disabled", true] ] },
+ function() {
+ info("Prefs set");
+
+ SpecialPowers.pushPermissions(
+ [ { type: "browser", allow: true, context: document } ],
+ function() {
+ info("Permissions set");
+
+ let iframe = document.createElement("iframe");
+ SpecialPowers.wrap(iframe).mozbrowser = true;
+ iframe.id = "iframe";
+ iframe.src =
+ "data:text/html,<!DOCTYPE HTML><html><body></body></html>";
+
+ iframe.addEventListener("mozbrowserloadend", function() {
+ info("Starting tests");
+
+ let mm = SpecialPowers.getBrowserFrameMessageManager(iframe)
+ parentFrameScript(mm);
+ });
+
+ document.body.appendChild(iframe);
+ }
+ );
+ }
+ );
+}
+
+SimpleTest.waitForExplicitFinish();
+ </script>
+ </body>
+</html>
diff --git a/dom/ipc/tests/test_bug1086684.html b/dom/ipc/tests/test_bug1086684.html
new file mode 100644
index 000000000..962826ec6
--- /dev/null
+++ b/dom/ipc/tests/test_bug1086684.html
@@ -0,0 +1,107 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for recursive CPOW-getting-cookie bug</title>
+ <script type="application/javascript"
+ src="/tests/SimpleTest/SimpleTest.js">
+ </script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+
+ <script type="application/javascript;version=1.8">
+ "use strict";
+
+ SimpleTest.waitForExplicitFinish();
+
+ const childFrameURL =
+ "http://mochi.test:8888/tests/dom/ipc/tests/file_bug1086684.html";
+
+ function childFrameScript() {
+ "use strict";
+
+ let { MockFilePicker } =
+ Components.utils.import("chrome://specialpowers/content/MockFilePicker.jsm", {});
+
+ function parentReady(message) {
+ MockFilePicker.init(content);
+ MockFilePicker.returnFiles = [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);
+ });
+ }
+
+ 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.
+ is(value, "", "got the right answer and didn't crash");
+
+ SimpleTest.finish();
+ }
+
+ function runTests() {
+ info("Browser prefs set.");
+
+ let iframe = document.createElement("iframe");
+ SpecialPowers.wrap(iframe).mozbrowser = true;
+ iframe.id = "iframe";
+ iframe.src = childFrameURL;
+
+ iframe.addEventListener("mozbrowserloadend", function() {
+ info("Got iframe load event.");
+ let mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
+ mm.loadFrameScript("data:,(" + encodeURI(childFrameScript.toString()) + ")();",
+ false);
+
+ test = testStructure(mm);
+ test.next();
+ });
+
+ document.body.appendChild(iframe);
+ }
+
+ addEventListener("load", function() {
+ info("Got load event.");
+
+ SpecialPowers.addPermission("browser", true, document);
+ SpecialPowers.pushPrefEnv({
+ "set": [
+ ["dom.ipc.browser_frames.oop_by_default", true],
+ ["dom.mozBrowserFramesEnabled", true],
+ ["network.disable.ipc.security", true],
+ ["browser.pagethumbnails.capturing_disabled", true]
+ ]
+ }, runTests);
+ });
+ </script>
+</body>
+</html>
diff --git a/dom/ipc/tests/test_child_docshell.html b/dom/ipc/tests/test_child_docshell.html
new file mode 100644
index 000000000..5855569e8
--- /dev/null
+++ b/dom/ipc/tests/test_child_docshell.html
@@ -0,0 +1,86 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+-->
+<head>
+ <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+
+<script type="application/javascript;version=1.7">
+"use strict";
+
+SimpleTest.waitForExplicitFinish();
+SpecialPowers.addPermission("browser", true, document);
+SpecialPowers.pushPrefEnv({'set':[
+ ["dom.mozBrowserFramesEnabled", true],
+ ["network.disable.ipc.security", true],
+ ["dom.ipc.tabs.disabled", false]
+]}, function () {
+
+ var iframe = document.createElementNS('http://www.w3.org/1999/xhtml', 'iframe');
+ iframe.setAttribute("remote", "true");
+ SpecialPowers.wrap(iframe).mozbrowser = true;
+ document.documentElement.appendChild(iframe);
+
+ var mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
+ mm.addMessageListener("chromeEventHandler", function (msg) {
+ msg = SpecialPowers.wrap(msg);
+ var result = msg.json;
+ is(result.processType, SpecialPowers.Ci.nsIXULRuntime.PROCESS_TYPE_CONTENT,
+ "The frame script is running in a real distinct child process");
+ ok(result.hasCorrectInterface,
+ "docshell.chromeEventHandler has nsIDOMEventTarget interface");
+ });
+
+
+ mm.addMessageListener("DOMWindowCreatedReceived", function (msg) {
+ msg = SpecialPowers.wrap(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");
+ SimpleTest.finish();
+ });
+ // Inject a frame script in the child process:
+ mm.loadFrameScript('data:,new ' + function ContentScriptScope() {
+ var processType = Components.classes["@mozilla.org/xre/runtime;1"]
+ .getService(Components.interfaces.nsIXULRuntime)
+ .processType;
+ var chromeEventHandler = docShell.chromeEventHandler;
+ sendAsyncMessage("chromeEventHandler", {
+ processType: Services.appinfo.processType,
+ hasCorrectInterface: chromeEventHandler instanceof Components.interfaces.nsIDOMEventTarget
+ });
+
+ /*
+ 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.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShell);
+ sendAsyncMessage("DOMWindowCreatedReceived", {
+ stableChromeEventHandler: chromeEventHandler === docShell.chromeEventHandler,
+ iframeHasNewDocShell: new_docShell !== docShell,
+ iframeHasSameChromeEventHandler: new_docShell.chromeEventHandler === chromeEventHandler
+ });
+ });
+
+ let i = content.document.createElement("iframe");
+ i.setAttribute("src", "data:text/html,foo");
+ content.document.documentElement.appendChild(i);
+ }, false);
+});
+
+</script>
+</body>
+</html>
diff --git a/dom/ipc/tests/test_cpow_cookies.html b/dom/ipc/tests/test_cpow_cookies.html
new file mode 100644
index 000000000..1e55d3878
--- /dev/null
+++ b/dom/ipc/tests/test_cpow_cookies.html
@@ -0,0 +1,90 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+ <title>Test for recursive CPOW-getting-cookie bug</title>
+ <script type="application/javascript"
+ src="/tests/SimpleTest/SimpleTest.js">
+ </script>
+ <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+
+ <script type="application/javascript;version=1.8">
+ "use strict";
+
+ SimpleTest.waitForExplicitFinish();
+
+ const childFrameURL =
+ "data:text/html,<!DOCTYPE HTML><html><body></body></html>";
+
+ function childFrameScript() {
+ "use strict";
+
+ const Ci = Components.interfaces;
+
+ function test1(message) {
+ // NB: This is a no-op because we're a data: document with a null
+ // principal.
+ content.document.cookie = "a=b";
+ message.target.sendAsyncMessage("testCPOWCookies:test1Finished", { pass: true });
+ }
+
+ addMessageListener("testCPOWCookies:test1", function(message) {
+ test1(message);
+ });
+ }
+
+ let test;
+ function* testStructure(mm) {
+ let lastResult;
+
+ function testDone(msg) {
+ test.next(msg.data);
+ }
+
+ mm.addMessageListener("testCPOWCookies:test1Finished", testDone);
+
+ mm.sendAsyncMessage("testCPOWCookies:test1", {});
+ lastResult = yield;
+ ok(lastResult.pass, "got the right answer and didn't crash");
+
+ SimpleTest.finish();
+ }
+
+ function runTests() {
+ info("Browser prefs set.");
+
+ let iframe = document.createElement("iframe");
+ SpecialPowers.wrap(iframe).mozbrowser = true;
+ iframe.id = "iframe";
+ iframe.src = childFrameURL;
+
+ iframe.addEventListener("mozbrowserloadend", function() {
+ info("Got iframe load event.");
+ let mm = SpecialPowers.getBrowserFrameMessageManager(iframe);
+ mm.loadFrameScript("data:,(" + encodeURI(childFrameScript.toString()) + ")();",
+ false);
+
+ test = testStructure(mm);
+ test.next();
+ });
+
+ document.body.appendChild(iframe);
+ }
+
+ addEventListener("load", function() {
+ info("Got load event.");
+
+ SpecialPowers.addPermission("browser", true, document);
+ SpecialPowers.pushPrefEnv({
+ "set": [
+ ["dom.ipc.browser_frames.oop_by_default", true],
+ ["dom.mozBrowserFramesEnabled", true],
+ ["network.disable.ipc.security", true],
+ ["browser.pagethumbnails.capturing_disabled", true]
+ ]
+ }, runTests);
+ });
+ </script>
+</body>
+</html>
diff --git a/dom/ipc/tests/test_process_error.xul b/dom/ipc/tests/test_process_error.xul
new file mode 100644
index 000000000..72352fc88
--- /dev/null
+++ b/dom/ipc/tests/test_process_error.xul
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin/" 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 type="application/javascript"
+ src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
+
+ <script>
+ SimpleTest.waitForExplicitFinish();
+
+ var w = window.open('process_error.xul', '_blank', 'chrome,resizable=yes,width=400,height=600');
+
+ 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_temporaryfile_stream.html b/dom/ipc/tests/test_temporaryfile_stream.html
new file mode 100644
index 000000000..e05accbdf
--- /dev/null
+++ b/dom/ipc/tests/test_temporaryfile_stream.html
@@ -0,0 +1,80 @@
+<!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);
+
+ var helper = new CaptureStreamTestHelper2D(100, 100);
+ helper.drawColor(canvas, helper.red);
+
+ var stream = canvas.captureStream(0);
+
+ var blob;
+
+ 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.waitForPixelColor(video, helper.red, 128, "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>